2026-03-18 22:50:23 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Tests\Twig;
|
|
|
|
|
|
|
|
|
|
use App\Twig\ViteAssetExtension;
|
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
|
use Psr\Cache\CacheItemInterface;
|
|
|
|
|
use Psr\Cache\CacheItemPoolInterface;
|
|
|
|
|
|
|
|
|
|
class ViteAssetExtensionTest extends TestCase
|
|
|
|
|
{
|
|
|
|
|
private CacheItemPoolInterface $cache;
|
|
|
|
|
|
|
|
|
|
protected function setUp(): void
|
|
|
|
|
{
|
|
|
|
|
$this->cache = $this->createMock(CacheItemPoolInterface::class);
|
|
|
|
|
$_ENV['VITE_LOAD'] = '1';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function createExtension(string $manifestPath = '/tmp/nonexistent'): ViteAssetExtension
|
|
|
|
|
{
|
|
|
|
|
return new ViteAssetExtension($manifestPath, $this->cache);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 23:14:04 +01:00
|
|
|
private function mockCacheItem(bool $isHit, mixed $value = null): CacheItemInterface
|
|
|
|
|
{
|
|
|
|
|
$item = $this->createMock(CacheItemInterface::class);
|
|
|
|
|
$item->method('isHit')->willReturn($isHit);
|
|
|
|
|
$item->method('get')->willReturn($value);
|
|
|
|
|
$item->method('set')->willReturnSelf();
|
|
|
|
|
$item->method('expiresAfter')->willReturnSelf();
|
|
|
|
|
|
|
|
|
|
return $item;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 22:50:23 +01:00
|
|
|
public function testGetFunctionsReturnsExpectedNames(): void
|
|
|
|
|
{
|
|
|
|
|
$extension = $this->createExtension();
|
|
|
|
|
$functions = $extension->getFunctions();
|
|
|
|
|
|
|
|
|
|
$names = array_map(fn ($f) => $f->getName(), $functions);
|
|
|
|
|
|
|
|
|
|
self::assertContains('vite_asset', $names);
|
|
|
|
|
self::assertContains('isMobile', $names);
|
|
|
|
|
self::assertContains('vite_favicons', $names);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAssetDevReturnsScriptTags(): void
|
|
|
|
|
{
|
|
|
|
|
$_ENV['VITE_LOAD'] = '0';
|
|
|
|
|
$extension = $this->createExtension();
|
|
|
|
|
|
|
|
|
|
$html = $extension->asset('app.js', []);
|
|
|
|
|
|
|
|
|
|
self::assertStringContainsString('localhost:5173', $html);
|
|
|
|
|
self::assertStringContainsString('app.js', $html);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAssetProdUsesManifest(): void
|
|
|
|
|
{
|
|
|
|
|
$manifest = [
|
|
|
|
|
'app.js' => [
|
|
|
|
|
'file' => 'assets/app.abc123.js',
|
|
|
|
|
'css' => ['assets/app.def456.css'],
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
|
2026-03-18 23:14:04 +01:00
|
|
|
$item = $this->mockCacheItem(true, $manifest);
|
2026-03-18 22:50:23 +01:00
|
|
|
$this->cache->method('getItem')->willReturn($item);
|
|
|
|
|
|
|
|
|
|
$extension = $this->createExtension();
|
|
|
|
|
$html = $extension->assetProd('app.js');
|
|
|
|
|
|
|
|
|
|
self::assertStringContainsString('assets/app.abc123.js', $html);
|
|
|
|
|
self::assertStringContainsString('assets/app.def456.css', $html);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAssetProdHandlesMissingManifest(): void
|
|
|
|
|
{
|
2026-03-18 23:14:04 +01:00
|
|
|
$item = $this->mockCacheItem(false);
|
2026-03-18 22:50:23 +01:00
|
|
|
$this->cache->method('getItem')->willReturn($item);
|
|
|
|
|
|
|
|
|
|
$extension = $this->createExtension('/tmp/nonexistent_manifest.json');
|
|
|
|
|
$html = $extension->assetProd('missing.js');
|
|
|
|
|
|
|
|
|
|
self::assertStringContainsString('script', $html);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 23:14:04 +01:00
|
|
|
public function testAssetProdLoadsManifestFromFile(): void
|
|
|
|
|
{
|
|
|
|
|
$manifest = ['app.js' => ['file' => 'assets/app.xyz.js', 'css' => []]];
|
|
|
|
|
$tmpFile = tempnam(sys_get_temp_dir(), 'manifest');
|
|
|
|
|
file_put_contents($tmpFile, json_encode($manifest));
|
|
|
|
|
|
|
|
|
|
$item = $this->mockCacheItem(false);
|
|
|
|
|
$this->cache->method('getItem')->willReturn($item);
|
|
|
|
|
$this->cache->method('save')->willReturn(true);
|
|
|
|
|
|
|
|
|
|
$extension = $this->createExtension($tmpFile);
|
|
|
|
|
$html = $extension->assetProd('app.js');
|
|
|
|
|
|
|
|
|
|
self::assertStringContainsString('assets/app.xyz.js', $html);
|
|
|
|
|
unlink($tmpFile);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 22:50:23 +01:00
|
|
|
public function testFaviconsDevReturnsFaviconLink(): void
|
|
|
|
|
{
|
|
|
|
|
$_ENV['VITE_LOAD'] = '0';
|
|
|
|
|
$extension = $this->createExtension();
|
|
|
|
|
|
|
|
|
|
$html = $extension->favicons();
|
|
|
|
|
|
|
|
|
|
self::assertStringContainsString('favicon.ico', $html);
|
|
|
|
|
}
|
2026-03-18 23:14:04 +01:00
|
|
|
|
|
|
|
|
public function testFaviconsProdReturnsIconLinks(): void
|
|
|
|
|
{
|
|
|
|
|
$manifest = [
|
|
|
|
|
'favicon.png' => ['file' => 'assets/favicon.abc.png'],
|
|
|
|
|
'app.js' => ['file' => 'assets/app.js'],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$item = $this->mockCacheItem(true, $manifest);
|
|
|
|
|
$this->cache->method('getItem')->willReturn($item);
|
|
|
|
|
|
|
|
|
|
$extension = $this->createExtension();
|
|
|
|
|
$html = $extension->favicons();
|
|
|
|
|
|
|
|
|
|
self::assertStringContainsString('assets/favicon.abc.png', $html);
|
|
|
|
|
self::assertStringNotContainsString('app.js', $html);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testIsMobileReturnsBool(): void
|
|
|
|
|
{
|
|
|
|
|
$extension = $this->createExtension();
|
|
|
|
|
|
|
|
|
|
self::assertIsBool($extension->isMobile());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetNonceReturnsEmptyWithoutCspListener(): void
|
|
|
|
|
{
|
|
|
|
|
$extension = $this->createExtension();
|
|
|
|
|
$html = $extension->assetDev('app.js', []);
|
|
|
|
|
|
|
|
|
|
self::assertStringContainsString('nonce=""', $html);
|
|
|
|
|
}
|
2026-03-18 22:50:23 +01:00
|
|
|
}
|