From dc177dcbbef7fd855b5b3f61ee7387a73c66c9db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brian=20Sch=C3=A4ffner?= Date: Thu, 1 Oct 2026 17:21:41 +0200 Subject: [PATCH 1/2] test: declare data providers with PHPUnit attributes --- tests/phpunit/Unit/Asset/ScriptTest.php | 6 +++--- tests/phpunit/Unit/Asset/StyleTest.php | 2 +- tests/phpunit/Unit/AssetFactoryTest.php | 14 ++++---------- .../Unit/Loader/AbstractWebpackLoaderTest.php | 3 +-- .../Unit/Loader/WebpackManifestLoaderTest.php | 4 ++-- tests/phpunit/Unit/Util/AssetHookResolverTest.php | 2 +- 6 files changed, 12 insertions(+), 19 deletions(-) diff --git a/tests/phpunit/Unit/Asset/ScriptTest.php b/tests/phpunit/Unit/Asset/ScriptTest.php index 8facbbb..736efc4 100644 --- a/tests/phpunit/Unit/Asset/ScriptTest.php +++ b/tests/phpunit/Unit/Asset/ScriptTest.php @@ -56,8 +56,8 @@ public function testWithTranslation(): void /** * @test - * @dataProvider provideLocalized */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideLocalized')] public function testWithLocalize(string $objectName, $objectValue, $expected): void { $script = new Script('handle', 'script.js'); @@ -229,8 +229,8 @@ static function (): string { /** * @test - * @dataProvider provideAssetsFile */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideAssetsFile')] public function testDependencyExtractionPlugin( string $scriptFile, string $depsFileName, @@ -366,8 +366,8 @@ public function testDependencyExtractionPluginWithDependencies(): void /** * @test - * @dataProvider provideVersions */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideVersions')] public function testDependencyExtractionPluginWithVersion( ?string $withVersion, string $dependencyExtractionPluginVersion, diff --git a/tests/phpunit/Unit/Asset/StyleTest.php b/tests/phpunit/Unit/Asset/StyleTest.php index 5ae0917..188d53b 100644 --- a/tests/phpunit/Unit/Asset/StyleTest.php +++ b/tests/phpunit/Unit/Asset/StyleTest.php @@ -86,7 +86,7 @@ public function testStyleLoadingModeCanReturnToBlocking(): void static::assertSame([], $testee->filters()); } - /** @dataProvider provideCssVars */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideCssVars')] public function testWithCssVars(string $element, array $cssVars, array $expected): void { $testee = new Style('handle', 'foo.css'); diff --git a/tests/phpunit/Unit/AssetFactoryTest.php b/tests/phpunit/Unit/AssetFactoryTest.php index df6a8d1..e92c727 100644 --- a/tests/phpunit/Unit/AssetFactoryTest.php +++ b/tests/phpunit/Unit/AssetFactoryTest.php @@ -72,9 +72,8 @@ public function testCreateLocation(): void /** * @test - * - * @dataProvider provideDependencies */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideDependencies')] public function testDependencies($input, array $expected): void { $asset = AssetFactory::create( @@ -252,9 +251,8 @@ public function testCreateMultipleLocations(): void /** * @test - * - * @dataProvider provideInvalidConfig */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideInvalidConfig')] public function testInvalidConfig(array $config, string $expectedExceptionType): void { \Brain\Monkey\Functions\when('esc_html')->returnArg(); @@ -414,9 +412,7 @@ public static function provideInvalidConfig(): \Generator ]; } - /** - * @dataProvider provideConfigWithTranslation - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideConfigWithTranslation')] public function testCreateWithTranslation(array $config, array $expected): void { /** @var Script $asset */ @@ -474,9 +470,7 @@ public static function provideConfigWithTranslation(): array ]; } - /** - * @dataProvider provideConfigWithLocalize - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideConfigWithLocalize')] public function testCreateWithLocalize(array $config, array $expected): void { /** @var Script $asset */ diff --git a/tests/phpunit/Unit/Loader/AbstractWebpackLoaderTest.php b/tests/phpunit/Unit/Loader/AbstractWebpackLoaderTest.php index 060c675..d420ada 100644 --- a/tests/phpunit/Unit/Loader/AbstractWebpackLoaderTest.php +++ b/tests/phpunit/Unit/Loader/AbstractWebpackLoaderTest.php @@ -81,9 +81,8 @@ public function load($filePath): array /** * @test - * - * @dataProvider provideAssetLocations */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideAssetLocations')] public function testResolveLocations(string $inputFile, int $expectedLocation): void { $loader = new class extends AbstractWebpackLoader { diff --git a/tests/phpunit/Unit/Loader/WebpackManifestLoaderTest.php b/tests/phpunit/Unit/Loader/WebpackManifestLoaderTest.php index d915734..bd5d05c 100644 --- a/tests/phpunit/Unit/Loader/WebpackManifestLoaderTest.php +++ b/tests/phpunit/Unit/Loader/WebpackManifestLoaderTest.php @@ -25,8 +25,8 @@ public function setUp(): void /** * @test - * @dataProvider provideManifest */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideManifest')] public function testLoadFromManifest( string $json, string $expectedHandle, @@ -130,8 +130,8 @@ public function testLoadFromManifestNotSupportedTypes(): void /** * @test - * @dataProvider provideManifestWithAlternativeUrl */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideManifestWithAlternativeUrl')] public function testLoadFromManifestWithAlternativeUrl( string $json, string $alternativeUrl, diff --git a/tests/phpunit/Unit/Util/AssetHookResolverTest.php b/tests/phpunit/Unit/Util/AssetHookResolverTest.php index 4679661..580a79c 100644 --- a/tests/phpunit/Unit/Util/AssetHookResolverTest.php +++ b/tests/phpunit/Unit/Util/AssetHookResolverTest.php @@ -67,8 +67,8 @@ public function testResolveBackend(): void /** * @test - * @dataProvider provideLastHook */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideLastHook')] public function testResolveLastHook(ContextStub $context, $expected): void { $hookResolver = new AssetHookResolver($context); From 03b91644211b271aa28395212ca4dc0d8dcecc4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brian=20Sch=C3=A4ffner?= Date: Thu, 1 Oct 2026 17:21:41 +0200 Subject: [PATCH 2/2] feat: expose public Encore manifest array loader --- docs/loaders.md | 21 ++++++++++++ src/Loader/EncoreEntrypointsLoader.php | 14 ++++++++ .../Loader/EncoreEntrypointsLoaderTest.php | 34 +++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/docs/loaders.md b/docs/loaders.md index db5d1ff..9429358 100644 --- a/docs/loaders.md +++ b/docs/loaders.md @@ -62,6 +62,27 @@ $assets = (new EncoreEntrypointsLoader()) Chunks from an entrypoint are registered in the correct order with dependencies. +Call `loadFromArray()` when the application has already decoded and validated +the manifest, for example after filtering invalid optional entrypoints: + +```php +$assets = (new EncoreEntrypointsLoader()) + ->withDirectoryUrl('https://example.com/theme/build/') + ->loadFromArray([ + 'entrypoints' => [ + 'theme' => ['css' => ['css/app.css'], 'js' => ['js/app.js']], + ], + ], __DIR__ . '/build/entrypoints.json'); +``` + +The second argument supplies the manifest path used to resolve asset paths. It +does not trigger a file read and the manifest need not exist. The caller must +validate the input shape and apply any application-specific path restrictions. +The loader preserves the file loader's handles, chunk dependencies, URL mapping +and version-discovery settings. Existing file-access and inline-content policies +still apply when the assets are consumed. Consumers do not need to subclass the +loader or call its protected parsing method. + ## Array Loader ```php diff --git a/src/Loader/EncoreEntrypointsLoader.php b/src/Loader/EncoreEntrypointsLoader.php index 33ae5af..ca59eb3 100644 --- a/src/Loader/EncoreEntrypointsLoader.php +++ b/src/Loader/EncoreEntrypointsLoader.php @@ -13,6 +13,20 @@ */ class EncoreEntrypointsLoader extends AbstractWebpackLoader implements LoaderInterface { + /** + * Load an already decoded and validated Encore manifest without reading it again. + * The resource path anchors relative asset paths; the manifest need not exist. + * File access and inline-content policies still apply when assets are consumed. + * + * @param array{entrypoints?: array, js?: list}>} $data + * @return array + */ + #[\NoDiscard] + public function loadFromArray(array $data, string $resource): array + { + return $this->parseData($data, $resource); + } + /** * {@inheritDoc} */ diff --git a/tests/phpunit/Unit/Loader/EncoreEntrypointsLoaderTest.php b/tests/phpunit/Unit/Loader/EncoreEntrypointsLoaderTest.php index 176150b..7343026 100644 --- a/tests/phpunit/Unit/Loader/EncoreEntrypointsLoaderTest.php +++ b/tests/phpunit/Unit/Loader/EncoreEntrypointsLoaderTest.php @@ -80,6 +80,40 @@ public function testLoadWithDependencies(): void static::assertSame(['theme', 'theme-1'], $asset->dependencies()); } + public function testLoadFromArrayPreservesPathsAndChunkDependenciesWithoutReadingManifest(): void + { + $loader = (new EncoreEntrypointsLoader())->withDirectoryUrl('https://example.com/build/'); + $data = [ + 'entrypoints' => [ + 'theme' => [ + 'css' => ['css/theme.css'], + 'js' => ['js/runtime.js', 'js/theme.js'], + ], + ], + ]; + $assets = $loader->loadFromArray($data, '/theme/build/missing-entrypoints.json'); + + static::assertCount(3, $assets); + static::assertInstanceOf(Style::class, $assets[0]); + static::assertInstanceOf(Script::class, $assets[1]); + static::assertSame('https://example.com/build/css/theme.css', $assets[0]->url()); + static::assertSame('/theme/build/css/theme.css', $assets[0]->filePath()); + static::assertSame('/theme/build/js/theme.js', $assets[2]->filePath()); + static::assertSame('https://example.com/build/js/theme.js', $assets[2]->url()); + static::assertSame('theme-1', $assets[2]->handle()); + static::assertSame(['theme'], $assets[2]->dependencies()); + static::assertSame([], $loader->loadFromArray([], '/theme/build/entrypoints.json')); + } + + public function testLoadFromArrayMatchesFileLoader(): void + { + $data = ['entrypoints' => ['theme' => ['js' => ['./theme.js']]]]; + $file = $this->mockEntrypointsFile($data); + $loader = new EncoreEntrypointsLoader(); + + static::assertEquals($loader->load($file), $loader->loadFromArray($data, $file)); + } + private function mockEntrypointsFile(array $json): string { return vfsStream::newFile('entrypoints.json')