cache = $this->createMock(CacheItemPoolInterface::class); $_ENV['VITE_LOAD'] = '1'; } private function createExtension(string $manifestPath = '/tmp/nonexistent'): ViteAssetExtension { return new ViteAssetExtension($manifestPath, $this->cache); } 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; } 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'], ], ]; $item = $this->mockCacheItem(true, $manifest); $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 { $item = $this->mockCacheItem(false); $this->cache->method('getItem')->willReturn($item); $extension = $this->createExtension('/tmp/nonexistent_manifest.json'); $html = $extension->assetProd('missing.js'); self::assertStringContainsString('script', $html); } 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); } public function testFaviconsDevReturnsFaviconLink(): void { $_ENV['VITE_LOAD'] = '0'; $extension = $this->createExtension(); $html = $extension->favicons(); self::assertStringContainsString('favicon.ico', $html); } 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); } }