From 5ddc714c74b4c6a56af2d02240bd3d636b3531a9 Mon Sep 17 00:00:00 2001 From: Olivier Gorzalka Date: Wed, 22 Apr 2026 15:48:51 +0200 Subject: [PATCH 1/2] refactor: migrate all PHPUnit tests to Pest format Convert 24 test files from PHPUnit class-based format to Pest closures: - Replace extends TestCase with describe/it blocks - Convert assertions to expect() chains - Convert setUp/tearDown to beforeEach/afterEach - Enable Rector on tests/ directory - Apply Rector void return types on closures All 453 tests passing. --- rector.php | 2 +- .../Services/PackagistVersionChecker.php | 2 +- .../Modules/ThemeAutoDiscoveryTest.php | 4 +- .../Modules/ThemeModuleIntegrationTest.php | 4 +- .../Feature/Option/OptionIntegrationTest.php | 32 +- .../Controllers/FrontendControllerTest.php | 30 +- tests/Feature/Theme/SelfRegistrationTest.php | 16 +- tests/Pest.php | 6 +- tests/TestCase.php | 6 +- tests/Unit/Ajax/AjaxActionTest.php | 25 +- .../Ajax/WordPressAjaxActionRegistrarTest.php | 20 +- tests/Unit/Asset/AssetDomainTest.php | 22 +- tests/Unit/Attributes/ActionTest.php | 107 ++--- tests/Unit/Attributes/FilterTest.php | 99 ++--- tests/Unit/Attributes/HookTest.php | 14 +- .../Unit/Attributes/PostTypeAttributeTest.php | 68 +-- .../Attributes/PostTypeSubAttributesTest.php | 36 +- tests/Unit/Attributes/ScheduleTest.php | 46 +- .../Unit/Attributes/TaxonomyAttributeTest.php | 12 +- .../Attributes/TaxonomySubAttributesTest.php | 46 +- .../Domain/Models/DiscoveryItemsTest.php | 126 +++--- .../Domain/Models/DiscoveryLocationTest.php | 98 ++-- .../ModuleAwareErrorViewResolverTest.php | 225 +++------- .../Helpers/PatternDataProcessorTest.php | 8 +- .../Registrars/BlockCategoryRegistrarTest.php | 8 +- .../Services/ModuleAutoloaderTest.php | 150 +++---- .../ModuleAutoloadingIntegrationTest.php | 20 +- .../Services/OptionServiceTest.php | 136 ++---- .../Exceptions/InvalidOptionExceptionTest.php | 22 +- .../OptionNotFoundExceptionTest.php | 22 +- .../Unit/Option/Domain/Models/OptionTest.php | 90 ++-- .../Services/OptionValidationServiceTest.php | 113 ++--- .../WordPressOptionRepositoryTest.php | 126 ++---- tests/Unit/Plugin/PluginMetadataTest.php | 263 +++++------ tests/Unit/Plugin/PluginModuleTest.php | 420 ++++++++---------- .../Domain/Models/TemplateTest.php | 30 +- .../Services/WooCommerceServiceTest.php | 48 +- .../WordPressWooCommerceAdapterTest.php | 72 +-- .../WooCommerceServiceProviderTest.php | 41 +- .../Commands/PostTypeMakeCommandTest.php | 13 +- .../PostTypeAttributeServiceProviderTest.php | 10 +- tests/Unit/PostType/PostTypeFactoryTest.php | 6 +- tests/Unit/PostType/PostTypeServiceTest.php | 8 +- tests/Unit/Route/Domain/Models/RouteTest.php | 96 ++-- .../Middleware/WordPressBindingsTest.php | 101 ++--- .../ExtendedRouterDependencyInjectionTest.php | 50 +-- .../Services/ExtendedRouterTest.php | 150 +++---- .../Services/LazyConfigLoadingTest.php | 197 ++++---- .../WordPressConditionManagerTest.php | 72 ++- .../Services/WordPressRouteResolutionTest.php | 393 ++++++---------- .../Services/WordPressTypeResolverTest.php | 37 +- tests/Unit/Route/RouteTest.php | 40 +- tests/Unit/Route/RouterTest.php | 32 +- tests/Unit/Support/UriTest.php | 6 +- .../Commands/TaxonomyMakeCommandTest.php | 21 +- .../TaxonomyAttributeServiceProviderTest.php | 12 +- tests/Unit/Taxonomy/TaxonomyFactoryTest.php | 16 +- tests/Unit/Taxonomy/TaxonomyServiceTest.php | 8 +- tests/Unit/Theme/ComponentFactoryTest.php | 6 +- tests/Unit/Theme/ImageSizeTest.php | 4 +- .../ThemeAutoloaderIntegrationTest.php | 24 +- .../Services/ThemeAutoloaderTest.php | 101 ++--- tests/Unit/Theme/MenusTest.php | 6 +- tests/Unit/Theme/PatternComponentTest.php | 4 +- tests/Unit/Theme/SidebarTest.php | 6 +- tests/Unit/Theme/SupportTest.php | 6 +- tests/Unit/Theme/TemplatesTest.php | 6 +- .../Unit/Theme/ThemeComponentProviderTest.php | 10 +- tests/Unit/Theme/ThemeInitializerTest.php | 6 +- tests/Unit/Theme/ThemeManagerTest.php | 14 +- tests/Unit/Theme/ThemeRegistrarTest.php | 185 ++------ tests/Unit/VersionCheck/AdminNoticeTest.php | 20 +- .../PackagistVersionCheckerTest.php | 38 +- .../Unit/VersionCheck/SiteHealthCheckTest.php | 14 +- .../VersionCheck/VersionComparatorTest.php | 16 +- .../WpRestAttributeServiceProviderTest.php | 97 +--- tests/Unit/WpRest/WpRestDiscoveryTest.php | 24 +- tests/Unit/helpers.php | 81 +--- 78 files changed, 1746 insertions(+), 2805 deletions(-) diff --git a/rector.php b/rector.php index cf667f12..a1d81e53 100644 --- a/rector.php +++ b/rector.php @@ -10,7 +10,7 @@ return RectorConfig::configure() ->withPaths([ __DIR__.'/src', - // __DIR__.'/tests', + __DIR__.'/tests', ]) ->withSkip([ AddOverrideAttributeToOverriddenMethodsRector::class, diff --git a/src/VersionCheck/Infrastructure/Services/PackagistVersionChecker.php b/src/VersionCheck/Infrastructure/Services/PackagistVersionChecker.php index 0421bef1..f256bcfd 100644 --- a/src/VersionCheck/Infrastructure/Services/PackagistVersionChecker.php +++ b/src/VersionCheck/Infrastructure/Services/PackagistVersionChecker.php @@ -92,7 +92,7 @@ private function fetchLatestVersion(): ?string } $body = wp_remote_retrieve_body($response); - $data = json_decode($body, true); + $data = json_decode((string) $body, true); if (! is_array($data)) { return null; diff --git a/tests/Feature/Modules/ThemeAutoDiscoveryTest.php b/tests/Feature/Modules/ThemeAutoDiscoveryTest.php index 499fcb94..bbe0b822 100644 --- a/tests/Feature/Modules/ThemeAutoDiscoveryTest.php +++ b/tests/Feature/Modules/ThemeAutoDiscoveryTest.php @@ -4,11 +4,11 @@ use Illuminate\Container\Container; -beforeEach(function () { +beforeEach(function (): void { $this->app = new Container; }); -it('can create container for auto discovery testing', function () { +it('can create container for auto discovery testing', function (): void { expect($this->app)->toBeInstanceOf(Container::class); }); diff --git a/tests/Feature/Modules/ThemeModuleIntegrationTest.php b/tests/Feature/Modules/ThemeModuleIntegrationTest.php index cbcf76f1..42abd63e 100644 --- a/tests/Feature/Modules/ThemeModuleIntegrationTest.php +++ b/tests/Feature/Modules/ThemeModuleIntegrationTest.php @@ -4,11 +4,11 @@ use Illuminate\Container\Container; -beforeEach(function () { +beforeEach(function (): void { $this->app = new Container; }); -it('can create container for testing', function () { +it('can create container for testing', function (): void { expect($this->app)->toBeInstanceOf(Container::class); }); diff --git a/tests/Feature/Option/OptionIntegrationTest.php b/tests/Feature/Option/OptionIntegrationTest.php index 38a8b483..05a367ac 100644 --- a/tests/Feature/Option/OptionIntegrationTest.php +++ b/tests/Feature/Option/OptionIntegrationTest.php @@ -2,32 +2,24 @@ declare(strict_types=1); -namespace Tests\Feature\Option; - -use PHPUnit\Framework\TestCase; use Pollora\Option\Application\Services\OptionService; use Pollora\Support\Facades\Option; -final class OptionIntegrationTest extends TestCase -{ - public function test_facade_class_exists(): void - { - $this->assertTrue(class_exists(Option::class)); - } +describe('OptionIntegration', function (): void { + it('facade class exists', function (): void { + expect(class_exists(Option::class))->toBeTrue(); + }); - public function test_facade_has_correct_accessor(): void - { - $reflection = new \ReflectionClass(Option::class); + it('facade has correct accessor', function (): void { + $reflection = new ReflectionClass(Option::class); $method = $reflection->getMethod('getFacadeAccessor'); - $method->setAccessible(true); $accessor = $method->invoke(null); - $this->assertEquals(OptionService::class, $accessor); - } + expect($accessor)->toBe(OptionService::class); + }); - public function test_facade_has_forget_alias(): void - { - $this->assertTrue(method_exists(Option::class, 'forget')); - } -} + it('facade has forget alias', function (): void { + expect(method_exists(Option::class, 'forget'))->toBeTrue(); + }); +}); diff --git a/tests/Feature/Route/UI/Http/Controllers/FrontendControllerTest.php b/tests/Feature/Route/UI/Http/Controllers/FrontendControllerTest.php index 8f0e2141..37104c69 100644 --- a/tests/Feature/Route/UI/Http/Controllers/FrontendControllerTest.php +++ b/tests/Feature/Route/UI/Http/Controllers/FrontendControllerTest.php @@ -11,25 +11,25 @@ require_once dirname(__DIR__, 5).'/Unit/helpers.php'; -beforeEach(function () { +beforeEach(function (): void { setupWordPressMocks(); $this->templateFinder = Mockery::mock(TemplateFinderInterface::class); $this->controller = new FrontendController($this->templateFinder); }); -describe('FrontendController', function () { - it('aborts when themes disabled', function () { - setWordPressFunction('wp_using_themes', fn () => false); +describe('FrontendController', function (): void { + it('aborts when themes disabled', function (): void { + setWordPressFunction('wp_using_themes', fn (): false => false); $request = Request::create('/test'); expect(fn () => $this->controller->handle($request)) ->toThrow(HttpException::class, 'Themes are disabled'); }); - it('renders blade view when available', function () { - setWordPressFunction('wp_using_themes', fn () => true); - setWordPressFunction('is_page', fn () => true); - setWordPressFunction('get_page_template', fn () => '/theme/page.php'); + it('renders blade view when available', function (): void { + setWordPressFunction('wp_using_themes', fn (): true => true); + setWordPressFunction('is_page', fn (): true => true); + setWordPressFunction('get_page_template', fn (): string => '/theme/page.php'); setWordPressFunction('apply_filters', fn ($filter, $value) => $value); $this->templateFinder->shouldReceive('getViewNameFromPath') @@ -50,11 +50,11 @@ expect($response->getContent())->toBe('Blade page content'); }); - it('falls back to php template', function () { + it('falls back to php template', function (): void { $templatePath = __DIR__.'/test-template.php'; - setWordPressFunction('wp_using_themes', fn () => true); - setWordPressFunction('is_page', fn () => true); - setWordPressFunction('get_page_template', fn () => $templatePath); + setWordPressFunction('wp_using_themes', fn (): true => true); + setWordPressFunction('is_page', fn (): true => true); + setWordPressFunction('get_page_template', fn (): string => $templatePath); setWordPressFunction('apply_filters', fn ($filter, $value) => $value); $this->templateFinder->shouldReceive('getViewNameFromPath') @@ -68,8 +68,8 @@ expect($response->getContent())->toBe('This is a PHP template'); }); - it('throws 404 when no template', function () { - setWordPressFunction('wp_using_themes', fn () => true); + it('throws 404 when no template', function (): void { + setWordPressFunction('wp_using_themes', fn (): true => true); setWordPressConditions([ 'is_page' => false, @@ -91,7 +91,7 @@ 'is_embed' => false, ]); - setWordPressFunction('get_index_template', fn () => ''); + setWordPressFunction('get_index_template', fn (): string => ''); setWordPressFunction('apply_filters', fn ($filter, $value) => $value); $this->templateFinder->shouldReceive('getViewNameFromPath') diff --git a/tests/Feature/Theme/SelfRegistrationTest.php b/tests/Feature/Theme/SelfRegistrationTest.php index 36572b25..bf666051 100644 --- a/tests/Feature/Theme/SelfRegistrationTest.php +++ b/tests/Feature/Theme/SelfRegistrationTest.php @@ -8,11 +8,11 @@ require_once dirname(__DIR__, 2).'/Unit/helpers.php'; -beforeEach(function () { +beforeEach(function (): void { setupWordPressMocks(); - setWordPressFunction('get_stylesheet', fn () => 'my-theme'); - setWordPressFunction('get_stylesheet_directory', fn () => __DIR__.'/fixtures/my-theme'); + setWordPressFunction('get_stylesheet', fn (): string => 'my-theme'); + setWordPressFunction('get_stylesheet_directory', fn (): string => __DIR__.'/fixtures/my-theme'); // Create a minimal theme fixture $fixturePath = __DIR__.'/fixtures/my-theme'; @@ -22,7 +22,7 @@ } }); -afterEach(function () { +afterEach(function (): void { // Clean up fixture $fixturePath = __DIR__.'/fixtures/my-theme'; if (is_dir($fixturePath)) { @@ -32,8 +32,8 @@ } }); -describe('ThemeRegistrar integration', function () { - it('registers a theme with parsed headers from style.css', function () { +describe('ThemeRegistrar integration', function (): void { + it('registers a theme with parsed headers from style.css', function (): void { $parser = new WordPressThemeParser; $registrar = new ThemeRegistrar($this->app, $parser); @@ -46,7 +46,7 @@ expect($theme->isEnabled())->toBeTrue(); }); - it('can retrieve the active theme after registration', function () { + it('can retrieve the active theme after registration', function (): void { $parser = new WordPressThemeParser; $registrar = new ThemeRegistrar($this->app, $parser); @@ -57,7 +57,7 @@ expect($registrar->isThemeActive('other-theme'))->toBeFalse(); }); - it('can reset the active theme', function () { + it('can reset the active theme', function (): void { $parser = new WordPressThemeParser; $registrar = new ThemeRegistrar($this->app, $parser); diff --git a/tests/Pest.php b/tests/Pest.php index 01de20a9..67887abf 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -29,9 +29,7 @@ | */ -expect()->extend('toBeOne', function () { - return $this->toBe(1); -}); +expect()->extend('toBeOne', fn () => $this->toBe(1)); /* |-------------------------------------------------------------------------- @@ -44,7 +42,7 @@ | */ -function something() +function something(): void { // .. } diff --git a/tests/TestCase.php b/tests/TestCase.php index 638c7f0d..6f9fb943 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,7 +4,9 @@ namespace Tests; +use Dotenv\Repository\RepositoryBuilder; use Mockery as m; +use PhpOption\Option; use PHPUnit\Framework\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase @@ -13,7 +15,7 @@ protected function setUp(): void { parent::setUp(); - if (! class_exists('Dotenv\\Repository\\RepositoryBuilder')) { + if (! class_exists(RepositoryBuilder::class)) { eval('namespace Dotenv\\Repository; class RepositoryBuilder { public static function createWithDefaultAdapters() { @@ -33,7 +35,7 @@ public function get($key) { return null; } }'); } - if (! class_exists('PhpOption\\Option')) { + if (! class_exists(Option::class)) { eval('namespace PhpOption; class Option { public static function fromValue($value) { return new class($value) { diff --git a/tests/Unit/Ajax/AjaxActionTest.php b/tests/Unit/Ajax/AjaxActionTest.php index 84ac1913..bae8203c 100644 --- a/tests/Unit/Ajax/AjaxActionTest.php +++ b/tests/Unit/Ajax/AjaxActionTest.php @@ -18,29 +18,30 @@ public function execute($action): void } } -describe('AjaxAction', function () { - it('can be instantiated with valid parameters', function () { - $action = new AjaxAction('my_action', function () {}); +describe('AjaxAction', function (): void { + it('can be instantiated with valid parameters', function (): void { + $action = new AjaxAction('my_action', function (): void {}); expect($action->getName())->toBe('my_action') ->and($action->getUserType())->toBe(AjaxAction::BOTH_USERS) ->and(is_callable($action->getCallback()) || is_string($action->getCallback()))->toBeTrue(); }); - it('throws exception if name or callback is empty', function () { - expect(fn () => new AjaxAction('', function () {}))->toThrow(InvalidAjaxActionException::class) - ->and(fn () => new AjaxAction('my_action', null))->toThrow(InvalidAjaxActionException::class); + it('throws exception if name or callback is empty', function (): void { + expect(fn (): AjaxAction => new AjaxAction('', function (): void {}))->toThrow(InvalidAjaxActionException::class) + ->and(fn (): AjaxAction => new AjaxAction('my_action', null))->toThrow(InvalidAjaxActionException::class); }); - it('can set user type to logged or guest', function () { - $action = new AjaxAction('my_action', function () {}); + it('can set user type to logged or guest', function (): void { + $action = new AjaxAction('my_action', function (): void {}); $action->forLoggedUsers(); + expect($action->getUserType())->toBe(AjaxAction::LOGGED_USERS); $action->forGuestUsers(); expect($action->getUserType())->toBe(AjaxAction::GUEST_USERS); }); - it('isBothOrLoggedUsers and isBothOrGuestUsers logic works', function () { - $action = new AjaxAction('my_action', function () {}); + it('isBothOrLoggedUsers and isBothOrGuestUsers logic works', function (): void { + $action = new AjaxAction('my_action', function (): void {}); expect($action->isBothOrLoggedUsers())->toBeTrue() ->and($action->isBothOrGuestUsers())->toBeTrue(); $action->forLoggedUsers(); @@ -51,9 +52,9 @@ public function execute($action): void ->and($action->isBothOrLoggedUsers())->toBeFalse(); }); - it('registers via RegisterAjaxActionService on destruct', function () { + it('registers via RegisterAjaxActionService on destruct', function (): void { $mockService = new DummyRegisterAjaxActionService; - $action = new AjaxAction('my_action', function () {}, $mockService); + $action = new AjaxAction('my_action', function (): void {}, $mockService); unset($action); expect($mockService->calls)->toHaveCount(1) ->and($mockService->calls[0]->getName())->toBe('my_action'); diff --git a/tests/Unit/Ajax/WordPressAjaxActionRegistrarTest.php b/tests/Unit/Ajax/WordPressAjaxActionRegistrarTest.php index cde61fbf..3750afdd 100644 --- a/tests/Unit/Ajax/WordPressAjaxActionRegistrarTest.php +++ b/tests/Unit/Ajax/WordPressAjaxActionRegistrarTest.php @@ -9,13 +9,13 @@ use Pollora\Support\Facades\Action as ActionFacade; use Psr\Container\ContainerInterface; -beforeEach(function () { +beforeEach(function (): void { // Patch the Action facade statically for all tests in this file ActionFacade::swap(new class { public array $calls = []; - public function add($hook, $callback) + public function add($hook, $callback): void { $GLOBALS['pollora_action_calls'][] = [$hook, $callback]; } @@ -23,12 +23,12 @@ public function add($hook, $callback) $GLOBALS['pollora_action_calls'] = []; }); -afterEach(function () { +afterEach(function (): void { m::close(); }); -describe('WordPressAjaxActionRegistrar', function () { - it('registers both hooks for BOTH_USERS', function () { +describe('WordPressAjaxActionRegistrar', function (): void { + it('registers both hooks for BOTH_USERS', function (): void { $container = m::mock(ContainerInterface::class); $actionService = m::mock(Action::class); $actionService->shouldReceive('add')->andReturnUsing(function ($hook, $callback) use ($actionService) { @@ -38,13 +38,13 @@ public function add($hook, $callback) }); $container->shouldReceive('get')->with(Action::class)->andReturn($actionService); $registrar = new WordPressAjaxActionRegistrar($container); - $action = (new AjaxAction('my_action', function () {})); + $action = (new AjaxAction('my_action', function (): void {})); $registrar->register($action); expect($GLOBALS['pollora_action_calls'])->toContain(['wp_ajax_my_action', $action->getCallback()]) ->and($GLOBALS['pollora_action_calls'])->toContain(['wp_ajax_nopriv_my_action', $action->getCallback()]); }); - it('registers only wp_ajax for LOGGED_USERS', function () { + it('registers only wp_ajax for LOGGED_USERS', function (): void { $container = m::mock(ContainerInterface::class); $actionService = m::mock(Action::class); $actionService->shouldReceive('add')->andReturnUsing(function ($hook, $callback) use ($actionService) { @@ -54,13 +54,13 @@ public function add($hook, $callback) }); $container->shouldReceive('get')->with(Action::class)->andReturn($actionService); $registrar = new WordPressAjaxActionRegistrar($container); - $action = (new AjaxAction('my_action', function () {}))->forLoggedUsers(); + $action = (new AjaxAction('my_action', function (): void {}))->forLoggedUsers(); $registrar->register($action); expect($GLOBALS['pollora_action_calls'])->toContain(['wp_ajax_my_action', $action->getCallback()]) ->and($GLOBALS['pollora_action_calls'])->not->toContain(['wp_ajax_nopriv_my_action', $action->getCallback()]); }); - it('registers only wp_ajax_nopriv for GUEST_USERS', function () { + it('registers only wp_ajax_nopriv for GUEST_USERS', function (): void { $container = m::mock(ContainerInterface::class); $actionService = m::mock(Action::class); $actionService->shouldReceive('add')->andReturnUsing(function ($hook, $callback) use ($actionService) { @@ -70,7 +70,7 @@ public function add($hook, $callback) }); $container->shouldReceive('get')->with(Action::class)->andReturn($actionService); $registrar = new WordPressAjaxActionRegistrar($container); - $action = (new AjaxAction('my_action', function () {}))->forGuestUsers(); + $action = (new AjaxAction('my_action', function (): void {}))->forGuestUsers(); $registrar->register($action); expect($GLOBALS['pollora_action_calls'])->not->toContain(['wp_ajax_my_action', $action->getCallback()]) ->and($GLOBALS['pollora_action_calls'])->toContain(['wp_ajax_nopriv_my_action', $action->getCallback()]); diff --git a/tests/Unit/Asset/AssetDomainTest.php b/tests/Unit/Asset/AssetDomainTest.php index 63567135..f56d63fc 100644 --- a/tests/Unit/Asset/AssetDomainTest.php +++ b/tests/Unit/Asset/AssetDomainTest.php @@ -8,8 +8,8 @@ use Pollora\Asset\Domain\Models\ViteManager; use Pollora\Asset\Infrastructure\Repositories\AssetContainer; -describe('Asset domain model', function () { - it('can be instantiated with name, path, and attributes', function () { +describe('Asset domain model', function (): void { + it('can be instantiated with name, path, and attributes', function (): void { $asset = new Asset('main', 'assets/main.js', ['type' => 'js']); expect($asset->getName())->toBe('main'); expect($asset->getPath())->toBe('assets/main.js'); @@ -17,34 +17,34 @@ }); }); -describe('AssetFile domain model', function () { - it('can be instantiated and return filename and container', function () { +describe('AssetFile domain model', function (): void { + it('can be instantiated and return filename and container', function (): void { $file = new AssetFile('assets/app.css'); expect($file->getFilename())->toBe('assets/app.css'); expect($file->getAssetContainer())->toBe('theme'); }); - it('can set a custom asset container', function () { + it('can set a custom asset container', function (): void { $file = (new AssetFile('assets/app.css'))->from('custom'); expect($file->getAssetContainer())->toBe('custom'); }); - it('can be cast to string as filename', function () { + it('can be cast to string as filename', function (): void { $file = new AssetFile('assets/app.css'); expect((string) $file)->toBe('assets/app.css'); }); }); -describe('ViteManager domain stub', function () { - beforeEach(function () { +describe('ViteManager domain stub', function (): void { + beforeEach(function (): void { $app = Mockery::mock(Container::class)->makePartial(); - $app->shouldReceive('publicPath')->andReturnUsing(fn ($path = '') => '/tmp/public'.($path ? '/'.$path : '')); + $app->shouldReceive('publicPath')->andReturnUsing(fn ($path = ''): string => '/tmp/public'.($path ? '/'.$path : '')); $app->instance('path.public', '/tmp/public'); Container::setInstance($app); }); - afterEach(function () { + afterEach(function (): void { Container::setInstance(new Container); }); - it('returns stub values for all interface methods', function () { + it('returns stub values for all interface methods', function (): void { $vite = new ViteManager; expect($vite->container())->toBeInstanceOf(AssetContainer::class); expect($vite->getAssetUrls(['entry.js']))->toBeArray()->toBeEmpty(); diff --git a/tests/Unit/Attributes/ActionTest.php b/tests/Unit/Attributes/ActionTest.php index 9cbabde9..532fa974 100644 --- a/tests/Unit/Attributes/ActionTest.php +++ b/tests/Unit/Attributes/ActionTest.php @@ -6,19 +6,14 @@ use Pollora\Attributes\Action; use Pollora\Hook\Infrastructure\Services\Action as ActionService; -beforeEach(function () { +beforeEach(function (): void { // Mock Action service $this->mockAction = Mockery::mock(ActionService::class); // Create a fake service locator $this->mockServiceLocator = new class($this->mockAction) { - private $actionService; - - public function __construct($actionService) - { - $this->actionService = $actionService; - } + public function __construct(private $actionService) {} public function get($serviceClass) { @@ -34,24 +29,24 @@ public function get($serviceClass) class SingleActionClass { #[Action('test_action', priority: 10)] - public function actionMethod($param = null) + public function actionMethod($param = null): string { // Test method - return $param ? "processed_{$param}" : 'processed'; + return $param ? 'processed_'.$param : 'processed'; } } class MultipleActionClass { #[Action('test_action', priority: 10)] - public function actionMethod($param = null) + public function actionMethod($param = null): string { // Test method - return $param ? "processed_{$param}" : 'processed'; + return $param ? 'processed_'.$param : 'processed'; } #[Action('another_action', priority: 20)] - public function anotherActionMethod() + public function anotherActionMethod(): string { // Another test method return 'another_processed'; @@ -61,35 +56,33 @@ public function anotherActionMethod() class DefaultPriorityActionClass { #[Action('test_action')] - public function actionMethod($param = null) + public function actionMethod($param = null): string { // Test method with default priority - return $param ? "processed_{$param}" : 'processed'; + return $param ? 'processed_'.$param : 'processed'; } } class CustomPriorityActionClass { #[Action('test_action', priority: 42)] - public function actionMethod($param = null) + public function actionMethod($param = null): string { // Test method with custom priority - return $param ? "processed_{$param}" : 'processed'; + return $param ? 'processed_'.$param : 'processed'; } } -it('registers an action hook correctly', function () { +it('registers an action hook correctly', function (): void { // Set up expectations $this->mockAction->shouldReceive('add') ->once() - ->withArgs(function ($hook, $callback, $priority, $acceptedArgs) { - return $hook === 'test_action' - && is_array($callback) - && $callback[0] instanceof SingleActionClass - && $callback[1] === 'actionMethod' - && $priority === 10 - && $acceptedArgs === 1; - }); + ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_action' + && is_array($callback) + && $callback[0] instanceof SingleActionClass + && $callback[1] === 'actionMethod' + && $priority === 10 + && $acceptedArgs === 1); // Test the action attribute directly using handle method $testClass = new SingleActionClass; @@ -99,19 +92,17 @@ public function actionMethod($param = null) $actionAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $actionAttribute); }); -it('registers multiple action hooks with different priorities', function () { +it('registers multiple action hooks with different priorities', function (): void { $testClass = new MultipleActionClass; // Test first action $this->mockAction->shouldReceive('add') ->once() - ->withArgs(function ($hook, $callback, $priority, $acceptedArgs) { - return $hook === 'test_action' - && is_array($callback) - && $callback[0] instanceof MultipleActionClass - && $callback[1] === 'actionMethod' - && $priority === 10; - }); + ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_action' + && is_array($callback) + && $callback[0] instanceof MultipleActionClass + && $callback[1] === 'actionMethod' + && $priority === 10); $actionAttribute1 = new Action('test_action', 10); $methodReflection1 = new ReflectionMethod($testClass, 'actionMethod'); @@ -120,31 +111,27 @@ public function actionMethod($param = null) // Test second action $this->mockAction->shouldReceive('add') ->once() - ->withArgs(function ($hook, $callback, $priority, $acceptedArgs) { - return $hook === 'another_action' - && is_array($callback) - && $callback[0] instanceof MultipleActionClass - && $callback[1] === 'anotherActionMethod' - && $priority === 20; - }); + ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'another_action' + && is_array($callback) + && $callback[0] instanceof MultipleActionClass + && $callback[1] === 'anotherActionMethod' + && $priority === 20); $actionAttribute2 = new Action('another_action', 20); $methodReflection2 = new ReflectionMethod($testClass, 'anotherActionMethod'); $actionAttribute2->handle($this->mockServiceLocator, $testClass, $methodReflection2, $actionAttribute2); }); -it('registers an action hook with default priority (10)', function () { +it('registers an action hook with default priority (10)', function (): void { // Set up expectations $this->mockAction->shouldReceive('add') ->once() - ->withArgs(function ($hook, $callback, $priority, $acceptedArgs) { - return $hook === 'test_action' - && is_array($callback) - && $callback[0] instanceof DefaultPriorityActionClass - && $callback[1] === 'actionMethod' - && $priority === 10 // Default priority should be 10 - && $acceptedArgs === 1; - }); + ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_action' + && is_array($callback) + && $callback[0] instanceof DefaultPriorityActionClass + && $callback[1] === 'actionMethod' + && $priority === 10 // Default priority should be 10 + && $acceptedArgs === 1); // Test with default priority $testClass = new DefaultPriorityActionClass; @@ -154,18 +141,16 @@ public function actionMethod($param = null) $actionAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $actionAttribute); }); -it('registers an action hook with custom priority', function () { +it('registers an action hook with custom priority', function (): void { // Set up expectations $this->mockAction->shouldReceive('add') ->once() - ->withArgs(function ($hook, $callback, $priority, $acceptedArgs) { - return $hook === 'test_action' - && is_array($callback) - && $callback[0] instanceof CustomPriorityActionClass - && $callback[1] === 'actionMethod' - && $priority === 42 // Custom priority - && $acceptedArgs === 1; - }); + ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_action' + && is_array($callback) + && $callback[0] instanceof CustomPriorityActionClass + && $callback[1] === 'actionMethod' + && $priority === 42 // Custom priority + && $acceptedArgs === 1); // Test with custom priority $testClass = new CustomPriorityActionClass; @@ -175,11 +160,11 @@ public function actionMethod($param = null) $actionAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $actionAttribute); }); -it('handles null service locator resolution gracefully', function () { +it('handles null service locator resolution gracefully', function (): void { // Create a service locator that returns null for the service $mockServiceLocator = new class { - public function get($serviceClass) + public function get($serviceClass): null { return null; } @@ -196,7 +181,7 @@ public function get($serviceClass) expect(true)->toBeTrue(); }); -afterEach(function () { +afterEach(function (): void { Mockery::close(); Facade::clearResolvedInstances(); }); diff --git a/tests/Unit/Attributes/FilterTest.php b/tests/Unit/Attributes/FilterTest.php index 4ea70b5d..d6e91929 100644 --- a/tests/Unit/Attributes/FilterTest.php +++ b/tests/Unit/Attributes/FilterTest.php @@ -6,19 +6,14 @@ use Pollora\Attributes\Filter; use Pollora\Hook\Infrastructure\Services\Filter as FilterService; -beforeEach(function () { +beforeEach(function (): void { // Mock Filter service $this->mockFilter = Mockery::mock(FilterService::class); // Create a fake service locator $this->mockServiceLocator = new class($this->mockFilter) { - private $filterService; - - public function __construct($filterService) - { - $this->filterService = $filterService; - } + public function __construct(private $filterService) {} public function get($serviceClass) { @@ -36,7 +31,7 @@ class SingleFilterClass #[Filter('test_filter', priority: 10)] public function filterMethod(string $value): string { - return "modified_{$value}"; + return 'modified_'.$value; } } @@ -45,7 +40,7 @@ class MultipleFilterClass #[Filter('test_filter', priority: 10)] public function filterMethod(string $value): string { - return "modified_{$value}"; + return 'modified_'.$value; } #[Filter('another_filter', priority: 20)] @@ -63,7 +58,7 @@ class DefaultPriorityFilterClass public function filterMethod(string $value): string { // Test method with default priority - return "modified_{$value}"; + return 'modified_'.$value; } } @@ -73,22 +68,20 @@ class CustomPriorityFilterClass public function filterMethod(string $value): string { // Test method with custom priority - return "modified_{$value}"; + return 'modified_'.$value; } } -it('registers a filter hook correctly', function () { +it('registers a filter hook correctly', function (): void { // Set up expectations $this->mockFilter->shouldReceive('add') ->once() - ->withArgs(function ($hook, $callback, $priority, $acceptedArgs) { - return $hook === 'test_filter' - && is_array($callback) - && $callback[0] instanceof SingleFilterClass - && $callback[1] === 'filterMethod' - && $priority === 10 - && $acceptedArgs === 1; - }); + ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_filter' + && is_array($callback) + && $callback[0] instanceof SingleFilterClass + && $callback[1] === 'filterMethod' + && $priority === 10 + && $acceptedArgs === 1); // Test the filter attribute directly using handle method $testClass = new SingleFilterClass; @@ -98,19 +91,17 @@ public function filterMethod(string $value): string $filterAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $filterAttribute); }); -it('registers multiple filter hooks with different priorities', function () { +it('registers multiple filter hooks with different priorities', function (): void { $testClass = new MultipleFilterClass; // Test first filter $this->mockFilter->shouldReceive('add') ->once() - ->withArgs(function ($hook, $callback, $priority, $acceptedArgs) { - return $hook === 'test_filter' - && is_array($callback) - && $callback[0] instanceof MultipleFilterClass - && $callback[1] === 'filterMethod' - && $priority === 10; - }); + ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_filter' + && is_array($callback) + && $callback[0] instanceof MultipleFilterClass + && $callback[1] === 'filterMethod' + && $priority === 10); $filterAttribute1 = new Filter('test_filter', 10); $methodReflection1 = new ReflectionMethod($testClass, 'filterMethod'); @@ -119,31 +110,27 @@ public function filterMethod(string $value): string // Test second filter $this->mockFilter->shouldReceive('add') ->once() - ->withArgs(function ($hook, $callback, $priority, $acceptedArgs) { - return $hook === 'another_filter' - && is_array($callback) - && $callback[0] instanceof MultipleFilterClass - && $callback[1] === 'anotherFilterMethod' - && $priority === 20; - }); + ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'another_filter' + && is_array($callback) + && $callback[0] instanceof MultipleFilterClass + && $callback[1] === 'anotherFilterMethod' + && $priority === 20); $filterAttribute2 = new Filter('another_filter', 20); $methodReflection2 = new ReflectionMethod($testClass, 'anotherFilterMethod'); $filterAttribute2->handle($this->mockServiceLocator, $testClass, $methodReflection2, $filterAttribute2); }); -it('registers a filter hook with default priority (10)', function () { +it('registers a filter hook with default priority (10)', function (): void { // Set up expectations $this->mockFilter->shouldReceive('add') ->once() - ->withArgs(function ($hook, $callback, $priority, $acceptedArgs) { - return $hook === 'test_filter' - && is_array($callback) - && $callback[0] instanceof DefaultPriorityFilterClass - && $callback[1] === 'filterMethod' - && $priority === 10 // Default priority should be 10 - && $acceptedArgs === 1; - }); + ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_filter' + && is_array($callback) + && $callback[0] instanceof DefaultPriorityFilterClass + && $callback[1] === 'filterMethod' + && $priority === 10 // Default priority should be 10 + && $acceptedArgs === 1); // Test with default priority $testClass = new DefaultPriorityFilterClass; @@ -153,18 +140,16 @@ public function filterMethod(string $value): string $filterAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $filterAttribute); }); -it('registers a filter hook with custom priority', function () { +it('registers a filter hook with custom priority', function (): void { // Set up expectations $this->mockFilter->shouldReceive('add') ->once() - ->withArgs(function ($hook, $callback, $priority, $acceptedArgs) { - return $hook === 'test_filter' - && is_array($callback) - && $callback[0] instanceof CustomPriorityFilterClass - && $callback[1] === 'filterMethod' - && $priority === 99 // Custom priority - && $acceptedArgs === 1; - }); + ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_filter' + && is_array($callback) + && $callback[0] instanceof CustomPriorityFilterClass + && $callback[1] === 'filterMethod' + && $priority === 99 // Custom priority + && $acceptedArgs === 1); // Test with custom priority $testClass = new CustomPriorityFilterClass; @@ -174,7 +159,7 @@ public function filterMethod(string $value): string $filterAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $filterAttribute); }); -it('executes filter and returns modified value', function () { +it('executes filter and returns modified value', function (): void { // Set up expectations for the add method $this->mockFilter->shouldReceive('add') ->once() @@ -197,11 +182,11 @@ public function filterMethod(string $value): string expect($result)->toBe('modified_original'); }); -it('handles null service locator resolution gracefully', function () { +it('handles null service locator resolution gracefully', function (): void { // Create a service locator that returns null for the service $mockServiceLocator = new class { - public function get($serviceClass) + public function get($serviceClass): null { return null; } @@ -218,7 +203,7 @@ public function get($serviceClass) expect(true)->toBeTrue(); }); -afterEach(function () { +afterEach(function (): void { Mockery::close(); Facade::clearResolvedInstances(); }); diff --git a/tests/Unit/Attributes/HookTest.php b/tests/Unit/Attributes/HookTest.php index b7d0b40c..72b02c9c 100644 --- a/tests/Unit/Attributes/HookTest.php +++ b/tests/Unit/Attributes/HookTest.php @@ -24,43 +24,43 @@ public function handle( } } -it('initializes with default priority', function () { +it('initializes with default priority', function (): void { $hook = new ConcreteHook('test_hook'); expect($hook->hook)->toBe('test_hook'); expect($hook->priority)->toBe(10); }); -it('initializes with custom priority', function () { +it('initializes with custom priority', function (): void { $hook = new ConcreteHook('test_hook', 20); expect($hook->hook)->toBe('test_hook'); expect($hook->priority)->toBe(20); }); -it('stores hook name correctly', function () { +it('stores hook name correctly', function (): void { $hookName = 'custom_hook_name'; $hook = new ConcreteHook($hookName); expect($hook->hook)->toBe($hookName); }); -it('allows priority to be a negative number', function () { +it('allows priority to be a negative number', function (): void { $priority = -1; $hook = new ConcreteHook('test_hook', $priority); expect($hook->priority)->toBe($priority); }); -it('allows priority to be zero', function () { +it('allows priority to be zero', function (): void { $priority = 0; $hook = new ConcreteHook('test_hook', $priority); expect($hook->priority)->toBe($priority); }); -it('allows priority to be a large number', function () { +it('allows priority to be a large number', function (): void { $priority = 9999; $hook = new ConcreteHook('test_hook', $priority); expect($hook->priority)->toBe($priority); }); -afterEach(function () { +afterEach(function (): void { Mockery::close(); Facade::clearResolvedInstances(); }); diff --git a/tests/Unit/Attributes/PostTypeAttributeTest.php b/tests/Unit/Attributes/PostTypeAttributeTest.php index e99faa30..7be03b79 100644 --- a/tests/Unit/Attributes/PostTypeAttributeTest.php +++ b/tests/Unit/Attributes/PostTypeAttributeTest.php @@ -49,7 +49,7 @@ public function getProductDetails(): array } } -it('creates PostType attribute with all parameters', function () { +it('creates PostType attribute with all parameters', function (): void { $postType = new PostType('custom-slug', 'Custom Type', 'Custom Types'); expect($postType->slug)->toBe('custom-slug'); @@ -57,7 +57,7 @@ public function getProductDetails(): array expect($postType->plural)->toBe('Custom Types'); }); -it('creates PostType attribute with only slug', function () { +it('creates PostType attribute with only slug', function (): void { $postType = new PostType('events'); expect($postType->slug)->toBe('events'); @@ -65,7 +65,7 @@ public function getProductDetails(): array expect($postType->plural)->toBeNull(); }); -it('creates PostType attribute with no parameters', function () { +it('creates PostType attribute with no parameters', function (): void { $postType = new PostType; expect($postType->slug)->toBeNull(); @@ -73,7 +73,7 @@ public function getProductDetails(): array expect($postType->plural)->toBeNull(); }); -it('stores PostType properties as readonly', function () { +it('stores PostType properties as readonly', function (): void { $postType = new PostType('test'); expect($postType->slug)->toBe('test'); @@ -89,72 +89,72 @@ public function getProductDetails(): array expect($pluralProperty->isReadOnly())->toBeTrue(); }); -it('creates HasArchive attribute with default value', function () { +it('creates HasArchive attribute with default value', function (): void { $hasArchive = new HasArchive; expect($hasArchive->value)->toBeTrue(); }); -it('creates HasArchive attribute with custom slug', function () { +it('creates HasArchive attribute with custom slug', function (): void { $hasArchive = new HasArchive('custom-archive'); expect($hasArchive->value)->toBe('custom-archive'); }); -it('creates HasArchive attribute disabled', function () { +it('creates HasArchive attribute disabled', function (): void { $hasArchive = new HasArchive(false); expect($hasArchive->value)->toBeFalse(); }); -it('creates Supports attribute with default features', function () { +it('creates Supports attribute with default features', function (): void { $supports = new Supports; expect($supports->features)->toBe(['title', 'editor']); }); -it('creates Supports attribute with custom features', function () { +it('creates Supports attribute with custom features', function (): void { $features = ['title', 'editor', 'thumbnail', 'excerpt']; $supports = new Supports($features); expect($supports->features)->toBe($features); }); -it('creates MenuIcon attribute with dashicon', function () { +it('creates MenuIcon attribute with dashicon', function (): void { $menuIcon = new MenuIcon('dashicons-admin-post'); expect($menuIcon->value)->toBe('dashicons-admin-post'); }); -it('creates MenuIcon attribute with custom URL', function () { +it('creates MenuIcon attribute with custom URL', function (): void { $menuIcon = new MenuIcon('https://example.com/icon.png'); expect($menuIcon->value)->toBe('https://example.com/icon.png'); }); -it('creates PubliclyQueryable attribute with default value', function () { +it('creates PubliclyQueryable attribute with default value', function (): void { $publiclyQueryable = new PubliclyQueryable; expect($publiclyQueryable->value)->toBeTrue(); }); -it('creates PubliclyQueryable attribute disabled', function () { +it('creates PubliclyQueryable attribute disabled', function (): void { $publiclyQueryable = new PubliclyQueryable(false); expect($publiclyQueryable->value)->toBeFalse(); }); -it('creates AdminCol attribute with all parameters', function () { +it('creates AdminCol attribute with all parameters', function (): void { $adminCol = new AdminCol( 'price', 'Product Price', sortable: true, - width: 120, titleIcon: 'dashicons-money', dateFormat: 'd/m/Y', link: 'edit', cap: 'edit_posts', - default: 'ASC' + default: 'ASC', + width: 120 ); expect($adminCol->key)->toBe('price'); @@ -168,7 +168,7 @@ public function getProductDetails(): array expect($adminCol->default)->toBe('ASC'); }); -it('creates AdminCol attribute with minimal parameters', function () { +it('creates AdminCol attribute with minimal parameters', function (): void { $adminCol = new AdminCol('title', 'Title'); expect($adminCol->key)->toBe('title'); @@ -182,7 +182,7 @@ public function getProductDetails(): array expect($adminCol->default)->toBeNull(); }); -it('creates AdminCol attribute for meta fields', function () { +it('creates AdminCol attribute for meta fields', function (): void { $adminCol = new AdminCol( 'custom_price', 'Price', @@ -196,7 +196,7 @@ public function getProductDetails(): array expect($adminCol->metaKey)->toBe('product_price'); }); -it('creates AdminCol attribute for taxonomy fields', function () { +it('creates AdminCol attribute for taxonomy fields', function (): void { $adminCol = new AdminCol( 'categories', 'Categories', @@ -208,7 +208,7 @@ public function getProductDetails(): array expect($adminCol->taxonomy)->toBe('product_category'); }); -it('creates AdminCol attribute for featured image', function () { +it('creates AdminCol attribute for featured image', function (): void { $adminCol = new AdminCol( 'image', 'Featured Image', @@ -222,13 +222,13 @@ public function getProductDetails(): array expect($adminCol->width)->toBe(80); }); -it('creates RegisterMetaBoxCb attribute', function () { +it('creates RegisterMetaBoxCb attribute', function (): void { $registerMetaBoxCb = new RegisterMetaBoxCb; expect($registerMetaBoxCb)->toBeInstanceOf(RegisterMetaBoxCb::class); }); -it('has correct PHP attribute configurations', function () { +it('has correct PHP attribute configurations', function (): void { // Test PostType attribute configuration $postTypeReflection = new ReflectionClass(PostType::class); $postTypeAttributes = $postTypeReflection->getAttributes(Attribute::class); @@ -254,7 +254,7 @@ public function getProductDetails(): array expect($registerMetaBoxCbAttribute->flags)->toBe(Attribute::TARGET_METHOD); }); -it('test class has correct attributes applied', function () { +it('test class has correct attributes applied', function (): void { $reflection = new ReflectionClass(TestProduct::class); // Check class-level attributes @@ -281,7 +281,7 @@ public function getProductDetails(): array expect($metaBoxAttrs)->toHaveCount(1); }); -it('attribute values can be extracted correctly', function () { +it('attribute values can be extracted correctly', function (): void { $reflection = new ReflectionClass(TestProduct::class); // Extract PostType attribute values @@ -309,26 +309,26 @@ public function getProductDetails(): array expect($adminCol->title)->toBe('Price'); }); -it('accepts all types without validation', function () { +it('accepts all types without validation', function (): void { // No validation should happen in attribute constructors // All validation is now handled by PostTypeDiscovery // PostType with any values - expect(fn () => new PostType('', '', ''))->not->toThrow(Exception::class); - expect(fn () => new PostType('invalid slug', 'invalid name', 'invalid plural'))->not->toThrow(Exception::class); + expect(fn (): PostType => new PostType('', '', ''))->not->toThrow(Exception::class); + expect(fn (): PostType => new PostType('invalid slug', 'invalid name', 'invalid plural'))->not->toThrow(Exception::class); // HasArchive with any values - expect(fn () => new HasArchive(''))->not->toThrow(Exception::class); - expect(fn () => new HasArchive('invalid-archive'))->not->toThrow(Exception::class); + expect(fn (): HasArchive => new HasArchive(''))->not->toThrow(Exception::class); + expect(fn (): HasArchive => new HasArchive('invalid-archive'))->not->toThrow(Exception::class); // Supports with any array - expect(fn () => new Supports([]))->not->toThrow(Exception::class); - expect(fn () => new Supports(['invalid-feature']))->not->toThrow(Exception::class); + expect(fn (): Supports => new Supports([]))->not->toThrow(Exception::class); + expect(fn (): Supports => new Supports(['invalid-feature']))->not->toThrow(Exception::class); // MenuIcon with any string - expect(fn () => new MenuIcon(''))->not->toThrow(Exception::class); - expect(fn () => new MenuIcon('invalid-icon'))->not->toThrow(Exception::class); + expect(fn (): MenuIcon => new MenuIcon(''))->not->toThrow(Exception::class); + expect(fn (): MenuIcon => new MenuIcon('invalid-icon'))->not->toThrow(Exception::class); // AdminCol with any values - expect(fn () => new AdminCol('', ''))->not->toThrow(Exception::class); + expect(fn (): AdminCol => new AdminCol('', ''))->not->toThrow(Exception::class); }); diff --git a/tests/Unit/Attributes/PostTypeSubAttributesTest.php b/tests/Unit/Attributes/PostTypeSubAttributesTest.php index 1165a989..ae2e589c 100644 --- a/tests/Unit/Attributes/PostTypeSubAttributesTest.php +++ b/tests/Unit/Attributes/PostTypeSubAttributesTest.php @@ -47,7 +47,7 @@ public function getProductName(): string } } -it('detects all class-level PostType attributes', function () { +it('detects all class-level PostType attributes', function (): void { $reflection = new ReflectionClass(ProductWithSubAttributes::class); // Should detect PostType main attribute @@ -67,7 +67,7 @@ public function getProductName(): string expect($menuIconAttrs)->toHaveCount(1); }); -it('detects all method-level AdminCol attributes', function () { +it('detects all method-level AdminCol attributes', function (): void { $reflection = new ReflectionClass(ProductWithSubAttributes::class); // Check price method @@ -91,7 +91,7 @@ public function getProductName(): string expect($nameAttrs)->toHaveCount(0); }); -it('extracts correct values from class-level attributes', function () { +it('extracts correct values from class-level attributes', function (): void { $reflection = new ReflectionClass(ProductWithSubAttributes::class); // Extract PostType attribute @@ -117,7 +117,7 @@ public function getProductName(): string expect($menuIcon->value)->toBe('dashicons-cart'); }); -it('extracts correct values from method-level AdminCol attributes', function () { +it('extracts correct values from method-level AdminCol attributes', function (): void { $reflection = new ReflectionClass(ProductWithSubAttributes::class); // Extract price AdminCol @@ -148,7 +148,7 @@ public function getProductName(): string expect($categoryAdminCol->sortable)->toBeFalse(); // Default value }); -it('supports multiple AdminCol attributes on different methods', function () { +it('supports multiple AdminCol attributes on different methods', function (): void { $reflection = new ReflectionClass(ProductWithSubAttributes::class); $adminColMethods = []; @@ -163,12 +163,12 @@ public function getProductName(): string expect($adminColMethods)->toHaveKeys(['getPriceColumn', 'getStockColumn', 'getCategoryColumn']); // Verify each column has unique key - $keys = array_map(fn ($adminCol) => $adminCol->key, $adminColMethods); + $keys = array_map(fn (AdminCol $adminCol): string => $adminCol->key, $adminColMethods); expect($keys)->toBe(['getPriceColumn' => 'price', 'getStockColumn' => 'stock', 'getCategoryColumn' => 'category']); expect(array_unique($keys))->toHaveCount(3); // All keys are unique }); -it('demonstrates attribute composition pattern', function () { +it('demonstrates attribute composition pattern', function (): void { // This test demonstrates how the new system works: // 1. PostType attribute defines the main post type // 2. Sub-attributes (HasArchive, Supports, MenuIcon) configure post type settings @@ -220,7 +220,7 @@ public function getProductName(): string expect($allMethodAttributes)->toHaveCount(3); // 3 AdminCol attributes }); -it('attributes have correct target configurations', function () { +it('attributes have correct target configurations', function (): void { // Verify class-level attributes target classes $classLevelAttributes = [ PostType::class, @@ -253,19 +253,19 @@ public function getProductName(): string } }); -it('demonstrates no validation in attributes', function () { +it('demonstrates no validation in attributes', function (): void { // All attributes should accept any values without validation // Validation will be handled by PostTypeDiscovery - expect(fn () => new PostType('', '', ''))->not->toThrow(Exception::class); - expect(fn () => new HasArchive(''))->not->toThrow(Exception::class); - expect(fn () => new Supports([]))->not->toThrow(Exception::class); - expect(fn () => new MenuIcon(''))->not->toThrow(Exception::class); - expect(fn () => new AdminCol('', ''))->not->toThrow(Exception::class); + expect(fn (): PostType => new PostType('', '', ''))->not->toThrow(Exception::class); + expect(fn (): HasArchive => new HasArchive(''))->not->toThrow(Exception::class); + expect(fn (): Supports => new Supports([]))->not->toThrow(Exception::class); + expect(fn (): MenuIcon => new MenuIcon(''))->not->toThrow(Exception::class); + expect(fn (): AdminCol => new AdminCol('', ''))->not->toThrow(Exception::class); // Even completely invalid values should not throw - expect(fn () => new PostType('invalid slug with spaces', 'inv@lid', 'pl{ur}al'))->not->toThrow(Exception::class); - expect(fn () => new HasArchive(123))->not->toThrow(Exception::class); // Wrong type - expect(fn () => new Supports(['invalid-feature', '', null]))->not->toThrow(Exception::class); - expect(fn () => new MenuIcon(null))->not->toThrow(Exception::class); // Wrong type + expect(fn (): PostType => new PostType('invalid slug with spaces', 'inv@lid', 'pl{ur}al'))->not->toThrow(Exception::class); + expect(fn (): HasArchive => new HasArchive(123))->not->toThrow(Exception::class); // Wrong type + expect(fn (): Supports => new Supports(['invalid-feature', '', null]))->not->toThrow(Exception::class); + expect(fn (): MenuIcon => new MenuIcon(null))->not->toThrow(Exception::class); // Wrong type }); diff --git a/tests/Unit/Attributes/ScheduleTest.php b/tests/Unit/Attributes/ScheduleTest.php index 75d61db2..97843335 100644 --- a/tests/Unit/Attributes/ScheduleTest.php +++ b/tests/Unit/Attributes/ScheduleTest.php @@ -12,7 +12,7 @@ * Tests for the simplified Schedule attribute that now only contains properties * and delegates all processing logic to the ScheduleDiscovery service. */ -it('creates Schedule attribute with string recurrence', function () { +it('creates Schedule attribute with string recurrence', function (): void { $schedule = new Schedule('daily'); expect($schedule)->toBeInstanceOf(Schedule::class); @@ -21,7 +21,7 @@ expect($schedule->args)->toBe([]); }); -it('creates Schedule attribute with custom hook name', function () { +it('creates Schedule attribute with custom hook name', function (): void { $schedule = new Schedule('hourly', 'custom_hook_name'); expect($schedule->recurrence)->toBe('hourly'); @@ -29,7 +29,7 @@ expect($schedule->args)->toBe([]); }); -it('creates Schedule attribute with arguments', function () { +it('creates Schedule attribute with arguments', function (): void { $args = ['type' => 'full', 'force' => true]; $schedule = new Schedule('daily', null, $args); @@ -38,7 +38,7 @@ expect($schedule->args)->toBe($args); }); -it('creates Schedule attribute with all parameters', function () { +it('creates Schedule attribute with all parameters', function (): void { $args = ['batch_size' => 100]; $schedule = new Schedule('weekly', 'weekly_cleanup', $args); @@ -47,7 +47,7 @@ expect($schedule->args)->toBe($args); }); -it('creates Schedule attribute with array recurrence', function () { +it('creates Schedule attribute with array recurrence', function (): void { $recurrence = ['interval' => 3600, 'display' => 'Every Hour']; $schedule = new Schedule($recurrence); @@ -56,7 +56,7 @@ expect($schedule->args)->toBe([]); }); -it('creates Schedule attribute with Every enum', function () { +it('creates Schedule attribute with Every enum', function (): void { $schedule = new Schedule(Every::DAY); expect($schedule->recurrence)->toBe(Every::DAY); @@ -64,8 +64,8 @@ expect($schedule->args)->toBe([]); }); -it('creates Schedule attribute with Interval instance', function () { - $interval = new Interval(hours: 2, minutes: 30); +it('creates Schedule attribute with Interval instance', function (): void { + $interval = new Interval(minutes: 30, hours: 2); $schedule = new Schedule($interval); expect($schedule->recurrence)->toBe($interval); @@ -73,7 +73,7 @@ expect($schedule->args)->toBe([]); }); -it('creates Schedule attribute with complex Every enum and parameters', function () { +it('creates Schedule attribute with complex Every enum and parameters', function (): void { $args = ['source' => 'api', 'limit' => 50]; $schedule = new Schedule(Every::MONTH, 'monthly_sync', $args); @@ -82,8 +82,8 @@ expect($schedule->args)->toBe($args); }); -it('creates Schedule attribute with complex Interval and parameters', function () { - $interval = new Interval(days: 1, hours: 12, minutes: 30); +it('creates Schedule attribute with complex Interval and parameters', function (): void { + $interval = new Interval(minutes: 30, hours: 12, days: 1); $args = ['cleanup_type' => 'deep']; $schedule = new Schedule($interval, 'complex_cleanup', $args); @@ -92,7 +92,7 @@ expect($schedule->args)->toBe($args); }); -it('stores recurrence as readonly property', function () { +it('stores recurrence as readonly property', function (): void { $schedule = new Schedule('daily'); expect($schedule->recurrence)->toBe('daily'); @@ -103,7 +103,7 @@ expect($property->isReadOnly())->toBeTrue(); }); -it('stores hook as readonly property', function () { +it('stores hook as readonly property', function (): void { $schedule = new Schedule('daily', 'test_hook'); expect($schedule->hook)->toBe('test_hook'); @@ -114,7 +114,7 @@ expect($property->isReadOnly())->toBeTrue(); }); -it('stores args as readonly property', function () { +it('stores args as readonly property', function (): void { $args = ['key' => 'value']; $schedule = new Schedule('daily', null, $args); @@ -126,7 +126,7 @@ expect($property->isReadOnly())->toBeTrue(); }); -it('has correct PHP attribute configuration', function () { +it('has correct PHP attribute configuration', function (): void { $reflection = new ReflectionClass(Schedule::class); $attributes = $reflection->getAttributes(Attribute::class); @@ -136,23 +136,23 @@ expect($attribute->flags)->toBe(Attribute::TARGET_METHOD); }); -it('accepts all supported recurrence types without validation', function () { +it('accepts all supported recurrence types without validation', function (): void { // No validation should happen in the attribute constructor // All validation is now handled by ScheduleDiscovery // String recurrence - expect(fn () => new Schedule('daily'))->not->toThrow(Exception::class); - expect(fn () => new Schedule('invalid_schedule'))->not->toThrow(Exception::class); // No validation + expect(fn (): Schedule => new Schedule('daily'))->not->toThrow(Exception::class); + expect(fn (): Schedule => new Schedule('invalid_schedule'))->not->toThrow(Exception::class); // No validation // Array recurrence - expect(fn () => new Schedule(['interval' => 3600, 'display' => 'Valid']))->not->toThrow(Exception::class); - expect(fn () => new Schedule(['invalid' => 'array']))->not->toThrow(Exception::class); // No validation + expect(fn (): Schedule => new Schedule(['interval' => 3600, 'display' => 'Valid']))->not->toThrow(Exception::class); + expect(fn (): Schedule => new Schedule(['invalid' => 'array']))->not->toThrow(Exception::class); // No validation // Every enum - expect(fn () => new Schedule(Every::HOUR))->not->toThrow(Exception::class); - expect(fn () => new Schedule(Every::MONTH))->not->toThrow(Exception::class); + expect(fn (): Schedule => new Schedule(Every::HOUR))->not->toThrow(Exception::class); + expect(fn (): Schedule => new Schedule(Every::MONTH))->not->toThrow(Exception::class); // Interval instance $interval = new Interval(minutes: 30); - expect(fn () => new Schedule($interval))->not->toThrow(Exception::class); + expect(fn (): Schedule => new Schedule($interval))->not->toThrow(Exception::class); }); diff --git a/tests/Unit/Attributes/TaxonomyAttributeTest.php b/tests/Unit/Attributes/TaxonomyAttributeTest.php index cf588b5d..0d88e989 100644 --- a/tests/Unit/Attributes/TaxonomyAttributeTest.php +++ b/tests/Unit/Attributes/TaxonomyAttributeTest.php @@ -128,13 +128,13 @@ public function getArgs(): array } } -beforeAll(function () { +beforeAll(function (): void { // Create and configure the container $app = new Container; Facade::setFacadeApplication($app); }); -afterAll(function () { +afterAll(function (): void { m::close(); Facade::clearResolvedInstances(); Facade::setFacadeApplication(null); @@ -143,7 +143,7 @@ public function getArgs(): array // Helper function to test simple boolean attributes function testBooleanAttribute(string $attributeName, string $argName): void { - test("$attributeName attribute sets $argName parameter", function () use ($argName) { + test(sprintf('%s attribute sets %s parameter', $attributeName, $argName), function () use ($argName): void { $taxonomy = new TestTaxonomy; // Simulate the discovery process by manually processing attributes @@ -162,7 +162,7 @@ function testBooleanAttribute(string $attributeName, string $argName): void // Helper function to test string/value attributes function testValueAttribute(string $attributeName, string $argName, mixed $expectedValue): void { - test("$attributeName attribute sets $argName parameter", function () use ($argName, $expectedValue) { + test(sprintf('%s attribute sets %s parameter', $attributeName, $argName), function () use ($argName, $expectedValue): void { $taxonomy = new TestTaxonomy; // Simulate the discovery process by manually processing attributes @@ -181,7 +181,7 @@ function testValueAttribute(string $attributeName, string $argName, mixed $expec // Helper function to test method attributes function testMethodAttribute(string $attributeName, string $argName, string $methodName, string $attributeClass): void { - test("$attributeName attribute sets $argName parameter", function () use ($argName, $methodName, $attributeClass) { + test(sprintf('%s attribute sets %s parameter', $attributeName, $argName), function () use ($argName, $methodName, $attributeClass): void { $taxonomy = new TestTaxonomy; // Reset attributeArgs to avoid interference @@ -241,7 +241,7 @@ function testMethodAttribute(string $attributeName, string $argName, string $met testMethodAttribute('UpdateCountCallback', 'update_count_callback', 'updateCount', UpdateCountCallback::class); // Test the final getArgs method -test('getArgs method merges attribute args with withArgs and labels', function () { +test('getArgs method merges attribute args with withArgs and labels', function (): void { $taxonomy = new TestTaxonomy; // Simulate the discovery process by manually processing attributes diff --git a/tests/Unit/Attributes/TaxonomySubAttributesTest.php b/tests/Unit/Attributes/TaxonomySubAttributesTest.php index 50822f39..3838bb40 100644 --- a/tests/Unit/Attributes/TaxonomySubAttributesTest.php +++ b/tests/Unit/Attributes/TaxonomySubAttributesTest.php @@ -49,7 +49,7 @@ public function updateTermCount($terms, $taxonomy): void public function sanitizeTerms($terms): array { // Sanitize the terms - return array_map('sanitize_text_field', (array) $terms); + return array_map(sanitize_text_field(...), (array) $terms); } public function getTaxonomyName(): string @@ -58,7 +58,7 @@ public function getTaxonomyName(): string } } -it('detects all class-level Taxonomy attributes', function () { +it('detects all class-level Taxonomy attributes', function (): void { $reflection = new ReflectionClass(ProductCategoryWithSubAttributes::class); // Should detect Taxonomy main attribute @@ -86,7 +86,7 @@ public function getTaxonomyName(): string expect($objectTypeAttrs)->toHaveCount(1); }); -it('detects all method-level callback attributes', function () { +it('detects all method-level callback attributes', function (): void { $reflection = new ReflectionClass(ProductCategoryWithSubAttributes::class); // Check custom meta box method @@ -114,7 +114,7 @@ public function getTaxonomyName(): string expect($nameCallbackAttrs)->toHaveCount(0); }); -it('extracts correct values from class-level attributes', function () { +it('extracts correct values from class-level attributes', function (): void { $reflection = new ReflectionClass(ProductCategoryWithSubAttributes::class); // Extract Taxonomy attribute @@ -147,7 +147,7 @@ public function getTaxonomyName(): string expect($objectType)->toBeInstanceOf(ObjectType::class); }); -it('extracts correct values from method-level callback attributes', function () { +it('extracts correct values from method-level callback attributes', function (): void { $reflection = new ReflectionClass(ProductCategoryWithSubAttributes::class); // Extract MetaBoxCb @@ -169,7 +169,7 @@ public function getTaxonomyName(): string expect($sanitizeCb)->toBeInstanceOf(MetaBoxSanitizeCb::class); }); -it('supports multiple callback attributes on different methods', function () { +it('supports multiple callback attributes on different methods', function (): void { $reflection = new ReflectionClass(ProductCategoryWithSubAttributes::class); $callbackMethods = []; @@ -180,7 +180,7 @@ public function getTaxonomyName(): string $method->getAttributes(MetaBoxSanitizeCb::class) ); - if (! empty($callbackAttrs)) { + if ($callbackAttrs !== []) { $callbackMethods[$method->getName()] = $callbackAttrs[0]->newInstance(); } } @@ -194,7 +194,7 @@ public function getTaxonomyName(): string expect($callbackMethods['sanitizeTerms'])->toBeInstanceOf(MetaBoxSanitizeCb::class); }); -it('demonstrates attribute composition pattern for taxonomies', function () { +it('demonstrates attribute composition pattern for taxonomies', function (): void { // This test demonstrates how the taxonomy attribute system works: // 1. Taxonomy attribute defines the main taxonomy // 2. Sub-attributes (Hierarchical, PublicTaxonomy, ShowUI, etc.) configure taxonomy settings @@ -231,7 +231,7 @@ public function getTaxonomyName(): string $method->getAttributes(MetaBoxSanitizeCb::class) ); - if (! empty($callbackAttrs)) { + if ($callbackAttrs !== []) { $callbackMethods[] = [ 'method' => $method->getName(), 'attribute' => $callbackAttrs[0]->newInstance(), @@ -255,7 +255,7 @@ public function getTaxonomyName(): string expect($allMethodAttributes)->toHaveCount(3); // 3 callback attributes }); -it('attributes have correct target configurations', function () { +it('attributes have correct target configurations', function (): void { // Verify class-level attributes target classes $classLevelAttributes = [ Taxonomy::class, @@ -292,22 +292,22 @@ public function getTaxonomyName(): string } }); -it('demonstrates no validation in attributes', function () { +it('demonstrates no validation in attributes', function (): void { // All attributes should accept any values without validation // Validation will be handled by TaxonomyDiscovery - expect(fn () => new Taxonomy('', '', '', []))->not->toThrow(Throwable::class); - expect(fn () => new Hierarchical(false))->not->toThrow(Throwable::class); - expect(fn () => new PublicTaxonomy(false))->not->toThrow(Throwable::class); - expect(fn () => new ShowUI(false))->not->toThrow(Throwable::class); - expect(fn () => new ShowInRest(false))->not->toThrow(Throwable::class); - expect(fn () => new ObjectType([]))->not->toThrow(Throwable::class); - expect(fn () => new MetaBoxCb)->not->toThrow(Throwable::class); - expect(fn () => new UpdateCountCallback)->not->toThrow(Throwable::class); - expect(fn () => new MetaBoxSanitizeCb)->not->toThrow(Throwable::class); + expect(fn (): Taxonomy => new Taxonomy('', '', '', []))->not->toThrow(Throwable::class); + expect(fn (): Hierarchical => new Hierarchical(false))->not->toThrow(Throwable::class); + expect(fn (): PublicTaxonomy => new PublicTaxonomy(false))->not->toThrow(Throwable::class); + expect(fn (): ShowUI => new ShowUI(false))->not->toThrow(Throwable::class); + expect(fn (): ShowInRest => new ShowInRest(false))->not->toThrow(Throwable::class); + expect(fn (): ObjectType => new ObjectType([]))->not->toThrow(Throwable::class); + expect(fn (): MetaBoxCb => new MetaBoxCb)->not->toThrow(Throwable::class); + expect(fn (): UpdateCountCallback => new UpdateCountCallback)->not->toThrow(Throwable::class); + expect(fn (): MetaBoxSanitizeCb => new MetaBoxSanitizeCb)->not->toThrow(Throwable::class); // Even completely invalid values should not throw - expect(fn () => new Taxonomy('invalid slug with spaces', 'inv@lid', 'pl{ur}al', 'invalid'))->not->toThrow(Throwable::class); - expect(fn () => new Hierarchical('not-boolean'))->not->toThrow(Throwable::class); // Wrong type - expect(fn () => new ObjectType('not-array'))->not->toThrow(Throwable::class); // Wrong type + expect(fn (): Taxonomy => new Taxonomy('invalid slug with spaces', 'inv@lid', 'pl{ur}al', 'invalid'))->not->toThrow(Throwable::class); + expect(fn (): Hierarchical => new Hierarchical('not-boolean'))->not->toThrow(Throwable::class); // Wrong type + expect(fn (): ObjectType => new ObjectType('not-array'))->not->toThrow(Throwable::class); // Wrong type }); diff --git a/tests/Unit/Discovery/Domain/Models/DiscoveryItemsTest.php b/tests/Unit/Discovery/Domain/Models/DiscoveryItemsTest.php index 59d0d443..677b3d44 100644 --- a/tests/Unit/Discovery/Domain/Models/DiscoveryItemsTest.php +++ b/tests/Unit/Discovery/Domain/Models/DiscoveryItemsTest.php @@ -2,80 +2,62 @@ declare(strict_types=1); -namespace Tests\Unit\Discovery\Domain\Models; - use Pollora\Discovery\Domain\Models\DiscoveryItems; use Pollora\Discovery\Domain\Models\DiscoveryLocation; -use Tests\TestCase; - -/** - * Discovery Items Test - * - * Tests the DiscoveryItems domain model functionality including - * item management, location-based organization, and iteration. - */ -final class DiscoveryItemsTest extends TestCase -{ - public function test_can_create_empty_discovery_items(): void - { + +describe('DiscoveryItems', function (): void { + it('can create empty discovery items', function (): void { $items = new DiscoveryItems; - $this->assertFalse($items->isLoaded()); - $this->assertCount(0, $items); - $this->assertEquals([], $items->all()); - } + expect($items->isLoaded())->toBeFalse(); + expect($items)->toHaveCount(0); + expect($items->all())->toBe([]); + }); - public function test_can_create_discovery_items_with_initial_data(): void - { - $initialData = [ + it('can create with initial data', function (): void { + $items = new DiscoveryItems([ 'location1' => ['item1', 'item2'], 'location2' => ['item3'], - ]; - - $items = new DiscoveryItems($initialData); + ]); - $this->assertTrue($items->isLoaded()); - $this->assertCount(3, $items); - $this->assertEquals(['item1', 'item2', 'item3'], $items->all()); - } + expect($items->isLoaded())->toBeTrue(); + expect($items)->toHaveCount(3); + expect($items->all())->toEqual(['item1', 'item2', 'item3']); + }); - public function test_can_add_single_item_for_location(): void - { + it('can add single item for location', function (): void { $items = new DiscoveryItems; $location = new DiscoveryLocation('App\\Models', '/app/models'); $items->add($location, 'test-item'); - $this->assertTrue($items->hasLocation($location)); - $this->assertEquals(['test-item'], $items->getForLocation($location)); - $this->assertCount(1, $items); - } + expect($items->hasLocation($location))->toBeTrue(); + expect($items->getForLocation($location))->toEqual(['test-item']); + expect($items)->toHaveCount(1); + }); - public function test_can_add_multiple_items_for_location(): void - { + it('can add multiple items for location', function (): void { $items = new DiscoveryItems; $location = new DiscoveryLocation('App\\Models', '/app/models'); $items->addForLocation($location, ['item1', 'item2', 'item3']); - $this->assertEquals(['item1', 'item2', 'item3'], $items->getForLocation($location)); - $this->assertCount(3, $items); - } + expect($items->getForLocation($location))->toEqual(['item1', 'item2', 'item3']); + expect($items)->toHaveCount(3); + }); - public function test_can_add_items_to_existing_location(): void - { + it('can add items to existing location', function (): void { $items = new DiscoveryItems; $location = new DiscoveryLocation('App\\Models', '/app/models'); $items->add($location, 'item1'); $items->addForLocation($location, ['item2', 'item3']); - $this->assertEquals(['item1', 'item2', 'item3'], $items->getForLocation($location)); - $this->assertCount(3, $items); - } + expect($items->getForLocation($location))->toEqual(['item1', 'item2', 'item3']); + expect($items)->toHaveCount(3); + }); - public function test_can_handle_multiple_locations(): void - { + it('handles multiple locations', function (): void { $items = new DiscoveryItems; $location1 = new DiscoveryLocation('App\\Models', '/app/models'); $location2 = new DiscoveryLocation('App\\Services', '/app/services'); @@ -83,25 +65,21 @@ public function test_can_handle_multiple_locations(): void $items->addForLocation($location1, ['model1', 'model2']); $items->addForLocation($location2, ['service1']); - $this->assertCount(3, $items); - $this->assertEquals(['model1', 'model2'], $items->getForLocation($location1)); - $this->assertEquals(['service1'], $items->getForLocation($location2)); - $this->assertEquals(['model1', 'model2', 'service1'], $items->all()); - } + expect($items)->toHaveCount(3); + expect($items->getForLocation($location1))->toEqual(['model1', 'model2']); + expect($items->getForLocation($location2))->toEqual(['service1']); + expect($items->all())->toEqual(['model1', 'model2', 'service1']); + }); - public function test_returns_empty_array_for_unknown_location(): void - { + it('returns empty array for unknown location', function (): void { $items = new DiscoveryItems; $location = new DiscoveryLocation('App\\Models', '/app/models'); - $result = $items->getForLocation($location); - - $this->assertEquals([], $result); - $this->assertFalse($items->hasLocation($location)); - } + expect($items->getForLocation($location))->toBe([]); + expect($items->hasLocation($location))->toBeFalse(); + }); - public function test_can_iterate_over_all_items(): void - { + it('can iterate over all items', function (): void { $items = new DiscoveryItems; $location1 = new DiscoveryLocation('App\\Models', '/app/models'); $location2 = new DiscoveryLocation('App\\Services', '/app/services'); @@ -114,30 +92,26 @@ public function test_can_iterate_over_all_items(): void $iteratedItems[] = $item; } - $this->assertEquals(['model1', 'model2', 'service1'], $iteratedItems); - } + expect($iteratedItems)->toEqual(['model1', 'model2', 'service1']); + }); - public function test_serialization_and_unserialization(): void - { + it('supports serialization and unserialization', function (): void { $items = new DiscoveryItems; $location = new DiscoveryLocation('App\\Models', '/app/models'); $items->addForLocation($location, ['item1', 'item2']); - // Test serialization $serialized = $items->__serialize(); - $this->assertIsArray($serialized); + expect($serialized)->toBeArray(); - // Test unserialization $newItems = new DiscoveryItems; $newItems->__unserialize($serialized); - $this->assertEquals($items->all(), $newItems->all()); - $this->assertEquals($items->count(), $newItems->count()); - } + expect($newItems->all())->toEqual($items->all()); + expect($newItems->count())->toBe($items->count()); + }); - public function test_only_vendor_returns_new_instance(): void - { + it('onlyVendor returns new instance', function (): void { $items = new DiscoveryItems([ 'location1' => ['item1'], 'location2' => ['item2'], @@ -145,7 +119,7 @@ public function test_only_vendor_returns_new_instance(): void $vendorItems = $items->onlyVendor(); - $this->assertNotSame($items, $vendorItems); - $this->assertInstanceOf(DiscoveryItems::class, $vendorItems); - } -} + expect($vendorItems)->not->toBe($items); + expect($vendorItems)->toBeInstanceOf(DiscoveryItems::class); + }); +}); diff --git a/tests/Unit/Discovery/Domain/Models/DiscoveryLocationTest.php b/tests/Unit/Discovery/Domain/Models/DiscoveryLocationTest.php index bcf41a75..b821be06 100644 --- a/tests/Unit/Discovery/Domain/Models/DiscoveryLocationTest.php +++ b/tests/Unit/Discovery/Domain/Models/DiscoveryLocationTest.php @@ -2,98 +2,66 @@ declare(strict_types=1); -namespace Tests\Unit\Discovery\Domain\Models; - use Pollora\Discovery\Domain\Models\DiscoveryLocation; -use Tests\TestCase; - -/** - * Discovery Location Test - * - * Tests the DiscoveryLocation domain model functionality including - * path resolution, namespace handling, and class name conversion. - */ -final class DiscoveryLocationTest extends TestCase -{ - public function test_can_create_discovery_location(): void - { + +describe('DiscoveryLocation', function (): void { + it('can create discovery location', function (): void { $location = new DiscoveryLocation('App\\Models', '/path/to/models'); - $this->assertEquals('App\\Models', $location->getNamespace()); - $this->assertEquals('/path/to/models', $location->getPath()); - } + expect($location->getNamespace())->toBe('App\\Models'); + expect($location->getPath())->toBe('/path/to/models'); + }); - public function test_generates_unique_key(): void - { + it('generates unique key based on path', function (): void { $location1 = new DiscoveryLocation('App\\Models', '/path/to/models'); $location2 = new DiscoveryLocation('App\\Services', '/path/to/models'); $location3 = new DiscoveryLocation('App\\Models', '/path/to/services'); - // Same path should generate same key regardless of namespace - $this->assertEquals($location1->getKey(), $location2->getKey()); + expect($location1->getKey())->toBe($location2->getKey()); + expect($location1->getKey())->not->toBe($location3->getKey()); + }); - // Different paths should generate different keys - $this->assertNotEquals($location1->getKey(), $location3->getKey()); - } - - public function test_detects_vendor_locations(): void - { + it('detects vendor locations', function (): void { $vendorLocation = new DiscoveryLocation('Vendor\\Package', '/path/to/vendor/package'); $appLocation = new DiscoveryLocation('App\\Models', '/path/to/app/models'); - $this->assertTrue($vendorLocation->isVendor()); - $this->assertFalse($appLocation->isVendor()); - } + expect($vendorLocation->isVendor())->toBeTrue(); + expect($appLocation->isVendor())->toBeFalse(); + }); - public function test_detects_vendor_locations_windows_path(): void - { + it('detects vendor locations with windows path', function (): void { $vendorLocation = new DiscoveryLocation('Vendor\\Package', 'C:\\path\\to\\vendor\\package'); - $this->assertTrue($vendorLocation->isVendor()); - } + expect($vendorLocation->isVendor())->toBeTrue(); + }); - public function test_converts_file_path_to_class_name(): void - { + it('converts file path to class name', function (): void { $location = new DiscoveryLocation('App\\Models', '/app/Models'); - $className = $location->toClassName('/app/Models/User.php'); - - $this->assertEquals('App\\Models\\User', $className); - } + expect($location->toClassName('/app/Models/User.php'))->toBe('App\\Models\\User'); + }); - public function test_converts_nested_file_path_to_class_name(): void - { + it('converts nested file path to class name', function (): void { $location = new DiscoveryLocation('App\\Services', '/app/Services'); - $className = $location->toClassName('/app/Services/Auth/UserService.php'); + expect($location->toClassName('/app/Services/Auth/UserService.php'))->toBe('App\\Services\\Auth\\UserService'); + }); - $this->assertEquals('App\\Services\\Auth\\UserService', $className); - } - - public function test_returns_empty_string_for_file_outside_location(): void - { + it('returns empty string for file outside location', function (): void { $location = new DiscoveryLocation('App\\Models', '/app/Models'); - $className = $location->toClassName('/app/Services/UserService.php'); - - $this->assertEquals('', $className); - } + expect($location->toClassName('/app/Services/UserService.php'))->toBe(''); + }); - public function test_handles_windows_paths_in_class_name_conversion(): void - { + it('handles windows paths in class name conversion', function (): void { $location = new DiscoveryLocation('App\\Models', 'C:\\app\\Models'); - $className = $location->toClassName('C:\\app\\Models\\Auth\\User.php'); + expect($location->toClassName('C:\\app\\Models\\Auth\\User.php'))->toBe('App\\Models\\Auth\\User'); + }); - $this->assertEquals('App\\Models\\Auth\\User', $className); - } - - public function test_trims_namespace_slashes(): void - { + it('trims namespace slashes', function (): void { $location = new DiscoveryLocation('App\\Models\\', '/app/Models'); - $className = $location->toClassName('/app/Models/User.php'); - - $this->assertEquals('App\\Models\\User', $className); - } -} + expect($location->toClassName('/app/Models/User.php'))->toBe('App\\Models\\User'); + }); +}); diff --git a/tests/Unit/Exceptions/ModuleAwareErrorViewResolverTest.php b/tests/Unit/Exceptions/ModuleAwareErrorViewResolverTest.php index 2d932d77..fd24d80d 100644 --- a/tests/Unit/Exceptions/ModuleAwareErrorViewResolverTest.php +++ b/tests/Unit/Exceptions/ModuleAwareErrorViewResolverTest.php @@ -2,167 +2,84 @@ declare(strict_types=1); -namespace Tests\Unit\Exceptions; - use Illuminate\Contracts\Config\Repository; use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\View\Factory as ViewFactory; use Illuminate\Http\Request; -use PHPUnit\Framework\TestCase; use Pollora\Exceptions\Infrastructure\Services\ModuleAwareErrorViewResolver; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -/** - * Test suite for ModuleAwareErrorViewResolver. - * - * Tests the module-aware error view resolution functionality to ensure - * proper prioritization of module error views over framework defaults. - */ -class ModuleAwareErrorViewResolverTest extends TestCase -{ - protected ModuleAwareErrorViewResolver $resolver; - - protected Container $container; - - protected ViewFactory $viewFactory; - - protected function setUp(): void - { - parent::setUp(); - - $this->container = $this->createMock(Container::class); - $this->viewFactory = $this->createMock(ViewFactory::class); - - $this->resolver = new ModuleAwareErrorViewResolver( - $this->container, - $this->viewFactory - ); - } - - /** - * Test that resolver returns correct error view for 404 status. - */ - public function test_resolves_404_error_view(): void - { - $exception = new NotFoundHttpException('Not found'); - $request = Request::create('/test-path'); - - $this->viewFactory - ->expects($this->any()) - ->method('exists') - ->willReturnCallback(fn ($view) => $view === 'errors.404'); - - $result = $this->resolver->resolveErrorView($exception, $request, 404); - - $this->assertEquals('errors.404', $result); - } - - /** - * Test that resolver returns null when no error view exists. - */ - public function test_returns_null_when_no_view_exists(): void - { - $exception = new HttpException(500, 'Server error'); - $request = Request::create('/test-path'); - - $this->viewFactory - ->expects($this->any()) - ->method('exists') - ->willReturn(false); - - $result = $this->resolver->resolveErrorView($exception, $request, 500); - - $this->assertNull($result); - } - - /** - * Test that resolver tries fallback views for error categories. - */ - public function test_tries_fallback_views_for_error_categories(): void - { - $exception = new HttpException(403, 'Forbidden'); - $request = Request::create('/test-path'); - - $this->viewFactory - ->expects($this->any()) - ->method('exists') - ->willReturnCallback(fn ($view) => $view === 'errors.4xx'); - - $result = $this->resolver->resolveErrorView($exception, $request, 403); - - $this->assertEquals('errors.4xx', $result); - } - - /** - * Test that resolver converts exception class to view name. - */ - public function test_converts_exception_class_to_view_name(): void - { - $exception = new NotFoundHttpException('Not found'); - $request = Request::create('/test-path'); - - $this->viewFactory - ->expects($this->any()) - ->method('exists') - ->willReturnCallback(fn ($view) => $view === 'errors.not-found-http'); - - $result = $this->resolver->resolveErrorView($exception, $request, 404); - - $this->assertEquals('errors.not-found-http', $result); - } - - /** - * Test debug information generation returns empty in non-debug mode. - */ - public function test_returns_empty_debug_info_when_debug_disabled(): void - { - // Mock config service to return debug = false - $config = $this->createMock(Repository::class); - $config->expects($this->once()) - ->method('get') - ->with('app.debug', false) - ->willReturn(false); - - $this->container->expects($this->once()) - ->method('make') - ->with('config') - ->willReturn($config); - - $exception = new NotFoundHttpException('Not found'); - - $debugInfo = $this->resolver->getDebugInfo(404, $exception); - - $this->assertEmpty($debugInfo); - } - - /** - * Test that kebab case conversion works correctly. - */ - public function test_converts_pascal_case_to_kebab_case(): void - { - $reflection = new \ReflectionClass($this->resolver); +describe('ModuleAwareErrorViewResolver', function (): void { + beforeEach(function (): void { + $this->container = Mockery::mock(Container::class); + $this->viewFactory = Mockery::mock(ViewFactory::class); + $this->resolver = new ModuleAwareErrorViewResolver($this->container, $this->viewFactory); + }); + + it('resolves 404 error view', function (): void { + $this->viewFactory->shouldReceive('exists') + ->andReturnUsing(fn ($view): bool => $view === 'errors.404'); + + $result = $this->resolver->resolveErrorView(new NotFoundHttpException('Not found'), Request::create('/test-path'), 404); + + expect($result)->toBe('errors.404'); + }); + + it('returns null when no view exists', function (): void { + $this->viewFactory->shouldReceive('exists')->andReturn(false); + + $result = $this->resolver->resolveErrorView(new HttpException(500, 'Server error'), Request::create('/test-path'), 500); + + expect($result)->toBeNull(); + }); + + it('tries fallback views for error categories', function (): void { + $this->viewFactory->shouldReceive('exists') + ->andReturnUsing(fn ($view): bool => $view === 'errors.4xx'); + + $result = $this->resolver->resolveErrorView(new HttpException(403, 'Forbidden'), Request::create('/test-path'), 403); + + expect($result)->toBe('errors.4xx'); + }); + + it('converts exception class to view name', function (): void { + $this->viewFactory->shouldReceive('exists') + ->andReturnUsing(fn ($view): bool => $view === 'errors.not-found-http'); + + $result = $this->resolver->resolveErrorView(new NotFoundHttpException('Not found'), Request::create('/test-path'), 404); + + expect($result)->toBe('errors.not-found-http'); + }); + + it('returns empty debug info when debug disabled', function (): void { + $config = Mockery::mock(Repository::class); + $config->shouldReceive('get')->with('app.debug', false)->once()->andReturn(false); + + $this->container->shouldReceive('make')->with('config')->once()->andReturn($config); + + $debugInfo = $this->resolver->getDebugInfo(404, new NotFoundHttpException('Not found')); + + expect($debugInfo)->toBeEmpty(); + }); + + it('converts PascalCase to kebab-case', function (): void { + $reflection = new ReflectionClass($this->resolver); $method = $reflection->getMethod('convertToKebabCase'); - $method->setAccessible(true); - - $this->assertEquals('not-found-http-exception', $method->invokeArgs($this->resolver, ['NotFoundHttpException'])); - $this->assertEquals('server-error', $method->invokeArgs($this->resolver, ['ServerError'])); - $this->assertEquals('test', $method->invokeArgs($this->resolver, ['Test'])); - $this->assertEquals('', $method->invokeArgs($this->resolver, [''])); - } - - /** - * Test that common suffixes are removed correctly. - */ - public function test_removes_common_suffixes(): void - { - $reflection = new \ReflectionClass($this->resolver); + + expect($method->invokeArgs($this->resolver, ['NotFoundHttpException']))->toBe('not-found-http-exception'); + expect($method->invokeArgs($this->resolver, ['ServerError']))->toBe('server-error'); + expect($method->invokeArgs($this->resolver, ['Test']))->toBe('test'); + expect($method->invokeArgs($this->resolver, ['']))->toBe(''); + }); + + it('removes common suffixes', function (): void { + $reflection = new ReflectionClass($this->resolver); $method = $reflection->getMethod('removeCommonSuffixes'); - $method->setAccessible(true); - - $this->assertEquals('NotFound', $method->invokeArgs($this->resolver, ['NotFoundException'])); - $this->assertEquals('Server', $method->invokeArgs($this->resolver, ['ServerError'])); - $this->assertEquals('NotFoundHttp', $method->invokeArgs($this->resolver, ['NotFoundHttpException'])); - $this->assertEquals('Test', $method->invokeArgs($this->resolver, ['Test'])); - } -} + + expect($method->invokeArgs($this->resolver, ['NotFoundException']))->toBe('NotFound'); + expect($method->invokeArgs($this->resolver, ['ServerError']))->toBe('Server'); + expect($method->invokeArgs($this->resolver, ['NotFoundHttpException']))->toBe('NotFoundHttp'); + expect($method->invokeArgs($this->resolver, ['Test']))->toBe('Test'); + }); +}); diff --git a/tests/Unit/Gutenberg/Helpers/PatternDataProcessorTest.php b/tests/Unit/Gutenberg/Helpers/PatternDataProcessorTest.php index d7e77225..4c37fd94 100755 --- a/tests/Unit/Gutenberg/Helpers/PatternDataProcessorTest.php +++ b/tests/Unit/Gutenberg/Helpers/PatternDataProcessorTest.php @@ -6,9 +6,9 @@ require_once __DIR__.'/../../helpers.php'; -describe('PatternDataProcessor', function () { +describe('PatternDataProcessor', function (): void { - it('extracts data from pattern file', function () { + it('extracts data from pattern file', function (): void { $processor = new PatternDataProcessor; // Mock of the global get_file_data function $data = $processor->getPatternData('dummy-path'); @@ -16,7 +16,7 @@ ->and($data['slug'])->toBe('slug-demo'); }); - it('processes array fields and viewportWidth', function () { + it('processes array fields and viewportWidth', function (): void { $processor = new PatternDataProcessor; $patternData = [ 'categories' => 'news,updates', @@ -31,7 +31,7 @@ ->and($result['viewportWidth'])->toBe(1200); }); - it('filters empty values', function () { + it('filters empty values', function (): void { $processor = new PatternDataProcessor; $patternData = [ 'categories' => '', diff --git a/tests/Unit/Gutenberg/Registrars/BlockCategoryRegistrarTest.php b/tests/Unit/Gutenberg/Registrars/BlockCategoryRegistrarTest.php index 9f4ca9fe..33667c10 100755 --- a/tests/Unit/Gutenberg/Registrars/BlockCategoryRegistrarTest.php +++ b/tests/Unit/Gutenberg/Registrars/BlockCategoryRegistrarTest.php @@ -6,9 +6,9 @@ require_once __DIR__.'/../../helpers.php'; -describe('PatternDataProcessor', function () { +describe('PatternDataProcessor', function (): void { - it('extracts data from pattern file', function () { + it('extracts data from pattern file', function (): void { $processor = new PatternDataProcessor; // Using the already defined stub for get_file_data $data = $processor->getPatternData('dummy-path'); @@ -16,7 +16,7 @@ ->and($data['slug'])->toBe('slug-demo'); }); - it('processes array fields and viewportWidth', function () { + it('processes array fields and viewportWidth', function (): void { $processor = new PatternDataProcessor; $patternData = [ 'categories' => 'news,updates', @@ -31,7 +31,7 @@ ->and($result['viewportWidth'])->toBe(1200); }); - it('filters empty values', function () { + it('filters empty values', function (): void { $processor = new PatternDataProcessor; $patternData = [ 'categories' => '', diff --git a/tests/Unit/Modules/Infrastructure/Services/ModuleAutoloaderTest.php b/tests/Unit/Modules/Infrastructure/Services/ModuleAutoloaderTest.php index 908c8612..8e40f101 100644 --- a/tests/Unit/Modules/Infrastructure/Services/ModuleAutoloaderTest.php +++ b/tests/Unit/Modules/Infrastructure/Services/ModuleAutoloaderTest.php @@ -4,161 +4,113 @@ use Composer\Autoload\ClassLoader; use Illuminate\Container\Container; -use PHPUnit\Framework\TestCase; use Pollora\Modules\Domain\Contracts\ModuleInterface; use Pollora\Modules\Infrastructure\Services\ModuleAutoloader; -class ModuleAutoloaderTest extends TestCase -{ - private Container $app; - - private ClassLoader $classLoader; - - private ModuleAutoloader $autoloader; - - protected function setUp(): void - { +describe('ModuleAutoloader', function (): void { + beforeEach(function (): void { $this->app = new Container; - $this->classLoader = $this->createMock(ClassLoader::class); - - // Bind the class loader to the container + $this->classLoader = Mockery::mock(ClassLoader::class)->shouldIgnoreMissing(); $this->app->instance(ClassLoader::class, $this->classLoader); - $this->autoloader = new ModuleAutoloader($this->app); - } + }); - public function test_it_builds_theme_namespace_correctly(): void - { + it('builds theme namespace correctly', function (): void { $tempDir = sys_get_temp_dir().'/test_theme_namespace_'.uniqid(); - $appDir = $tempDir.'/app'; - mkdir($appDir, 0777, true); + mkdir($tempDir.'/app', 0777, true); - $module = $this->createMockModule('TestTheme', $tempDir); + $module = createMockModuleForAutoloader('TestTheme', $tempDir); - $this->classLoader - ->expects($this->once()) - ->method('addPsr4') - ->with('Theme\\TestTheme\\', $tempDir.'/app'); + $this->classLoader->shouldReceive('addPsr4')->once()->with('Theme\\TestTheme\\', $tempDir.'/app'); $this->autoloader->registerTheme($module); - // Cleanup - rmdir($appDir); + rmdir($tempDir.'/app'); rmdir($tempDir); - } + }); - public function test_it_builds_plugin_namespace_correctly(): void - { + it('builds plugin namespace correctly', function (): void { $tempDir = sys_get_temp_dir().'/test_plugin_namespace_'.uniqid(); - $appDir = $tempDir.'/app'; - mkdir($appDir, 0777, true); + mkdir($tempDir.'/app', 0777, true); - $module = $this->createMockModule('TestPlugin', $tempDir); + $module = createMockModuleForAutoloader('TestPlugin', $tempDir); - $this->classLoader - ->expects($this->once()) - ->method('addPsr4') - ->with('Plugin\\TestPlugin\\', $tempDir.'/app'); + $this->classLoader->shouldReceive('addPsr4')->once()->with('Plugin\\TestPlugin\\', $tempDir.'/app'); $this->autoloader->registerPlugin($module); - // Cleanup - rmdir($appDir); + rmdir($tempDir.'/app'); rmdir($tempDir); - } + }); - public function test_it_prefers_app_directory_over_src(): void - { + it('prefers app directory over src', function (): void { $tempDir = sys_get_temp_dir().'/test_theme_preference_'.uniqid(); - $appDir = $tempDir.'/app'; - $srcDir = $tempDir.'/src'; - mkdir($appDir, 0777, true); - mkdir($srcDir, 0777, true); + mkdir($tempDir.'/app', 0777, true); + mkdir($tempDir.'/src', 0777, true); - $module = $this->createMockModule('TestTheme', $tempDir); + $module = createMockModuleForAutoloader('TestTheme', $tempDir); - // Mock file system checks - $this->classLoader - ->expects($this->once()) - ->method('addPsr4') - ->with('Theme\\TestTheme\\', $tempDir.'/app'); + $this->classLoader->shouldReceive('addPsr4')->once()->with('Theme\\TestTheme\\', $tempDir.'/app'); $this->autoloader->registerTheme($module); - // Cleanup - rmdir($appDir); - rmdir($srcDir); + rmdir($tempDir.'/app'); + rmdir($tempDir.'/src'); rmdir($tempDir); - } + }); - public function test_it_tracks_registered_namespaces(): void - { + it('tracks registered namespaces', function (): void { $tempDir = sys_get_temp_dir().'/test_theme_'.uniqid(); - $appDir = $tempDir.'/app'; - mkdir($appDir, 0777, true); + mkdir($tempDir.'/app', 0777, true); - $module = $this->createMockModule('TestTheme', $tempDir); + $module = createMockModuleForAutoloader('TestTheme', $tempDir); $this->autoloader->registerTheme($module); - $this->assertTrue($this->autoloader->isNamespaceRegistered('Theme\\TestTheme\\')); - $this->assertFalse($this->autoloader->isNamespaceRegistered('Theme\\OtherTheme\\')); + expect($this->autoloader->isNamespaceRegistered('Theme\\TestTheme\\'))->toBeTrue(); + expect($this->autoloader->isNamespaceRegistered('Theme\\OtherTheme\\'))->toBeFalse(); - // Cleanup - rmdir($appDir); + rmdir($tempDir.'/app'); rmdir($tempDir); - } + }); - public function test_it_does_not_register_duplicate_namespaces(): void - { + it('does not register duplicate namespaces', function (): void { $tempDir = sys_get_temp_dir().'/test_theme_duplicate_'.uniqid(); - $appDir = $tempDir.'/app'; - mkdir($appDir, 0777, true); + mkdir($tempDir.'/app', 0777, true); - $module = $this->createMockModule('TestTheme', $tempDir); + $module = createMockModuleForAutoloader('TestTheme', $tempDir); - $this->classLoader - ->expects($this->once()) - ->method('addPsr4'); + $this->classLoader->shouldReceive('addPsr4')->once(); - // Register the same module twice $this->autoloader->registerTheme($module); $this->autoloader->registerTheme($module); - // Cleanup - rmdir($appDir); + rmdir($tempDir.'/app'); rmdir($tempDir); - } + }); - public function test_it_can_unregister_namespace(): void - { + it('can unregister namespace', function (): void { $tempDir = sys_get_temp_dir().'/test_theme_unregister_'.uniqid(); - $appDir = $tempDir.'/app'; - mkdir($appDir, 0777, true); + mkdir($tempDir.'/app', 0777, true); - $module = $this->createMockModule('TestTheme', $tempDir); + $module = createMockModuleForAutoloader('TestTheme', $tempDir); $this->autoloader->registerTheme($module); - $this->assertTrue($this->autoloader->isNamespaceRegistered('Theme\\TestTheme\\')); + expect($this->autoloader->isNamespaceRegistered('Theme\\TestTheme\\'))->toBeTrue(); $this->autoloader->unregisterNamespace('Theme\\TestTheme\\'); - $this->assertFalse($this->autoloader->isNamespaceRegistered('Theme\\TestTheme\\')); + expect($this->autoloader->isNamespaceRegistered('Theme\\TestTheme\\'))->toBeFalse(); - // Cleanup - rmdir($appDir); + rmdir($tempDir.'/app'); rmdir($tempDir); - } - - private function createMockModule(string $name, string $path): ModuleInterface - { - $module = $this->createMock(ModuleInterface::class); + }); +}); - $module->method('getStudlyName') - ->willReturn($name); - - $module->method('getPath') - ->willReturn($path); +function createMockModuleForAutoloader(string $name, string $path): ModuleInterface +{ + $module = Mockery::mock(ModuleInterface::class); + $module->shouldReceive('getStudlyName')->andReturn($name); + $module->shouldReceive('getPath')->andReturn($path); - return $module; - } + return $module; } diff --git a/tests/Unit/Modules/ModuleAutoloadingIntegrationTest.php b/tests/Unit/Modules/ModuleAutoloadingIntegrationTest.php index defa9d12..becdf66a 100644 --- a/tests/Unit/Modules/ModuleAutoloadingIntegrationTest.php +++ b/tests/Unit/Modules/ModuleAutoloadingIntegrationTest.php @@ -8,38 +8,34 @@ use Pollora\Theme\Domain\Models\LaravelThemeModule; use Pollora\Theme\Infrastructure\Services\ThemeAutoloader; -beforeEach(function () { +beforeEach(function (): void { $this->app = new Container; // Register autoloader services in the container - $this->app->singleton(ModuleAutoloader::class, function ($app) { - return new ModuleAutoloader($app); - }); + $this->app->singleton(fn ($app): ModuleAutoloader => new ModuleAutoloader($app)); - $this->app->singleton(ThemeAutoloader::class, function ($app) { - return new ThemeAutoloader($app); - }); + $this->app->singleton(fn ($app): ThemeAutoloader => new ThemeAutoloader($app)); }); -afterEach(function () { +afterEach(function (): void { m::close(); }); -it('can create and use module autoloader', function () { +it('can create and use module autoloader', function (): void { $autoloader = $this->app->make(ModuleAutoloader::class); expect($autoloader)->toBeInstanceOf(ModuleAutoloader::class); expect($autoloader->getRegisteredNamespaces())->toBeArray()->toBeEmpty(); }); -it('can create and use theme autoloader', function () { +it('can create and use theme autoloader', function (): void { $autoloader = $this->app->make(ThemeAutoloader::class); expect($autoloader)->toBeInstanceOf(ThemeAutoloader::class); expect($autoloader->getThemeNamespace('TestTheme'))->toBe('Theme\\TestTheme\\'); }); -it('integrates autoloader with theme module registration', function () { +it('integrates autoloader with theme module registration', function (): void { // Create temporary directory for this test $tempDir = sys_get_temp_dir().'/pollora_module_test_'.uniqid(); $themePath = $tempDir.'/themes/test-theme'; @@ -48,7 +44,7 @@ // Create a real LaravelThemeModule instance for integration testing $theme = new class($tempDir.'/themes', $this->app) extends LaravelThemeModule { - public function __construct(string $path, $app) + public function __construct(string $path, Container $app) { parent::__construct('TestTheme', $path, $app); } diff --git a/tests/Unit/Option/Application/Services/OptionServiceTest.php b/tests/Unit/Option/Application/Services/OptionServiceTest.php index 6dad44bd..86c10b4c 100644 --- a/tests/Unit/Option/Application/Services/OptionServiceTest.php +++ b/tests/Unit/Option/Application/Services/OptionServiceTest.php @@ -2,131 +2,65 @@ declare(strict_types=1); -namespace Tests\Unit\Option\Application\Services; - -use Mockery; -use PHPUnit\Framework\TestCase; use Pollora\Option\Application\Services\OptionService; use Pollora\Option\Domain\Contracts\OptionRepositoryInterface; use Pollora\Option\Domain\Models\Option; use Pollora\Option\Domain\Services\OptionValidationService; -final class OptionServiceTest extends TestCase -{ - private OptionService $service; - - private OptionRepositoryInterface $repository; - - private OptionValidationService $validator; - - protected function setUp(): void - { - parent::setUp(); - +describe('OptionService', function (): void { + beforeEach(function (): void { $this->repository = Mockery::mock(OptionRepositoryInterface::class); $this->validator = new OptionValidationService; $this->service = new OptionService($this->repository, $this->validator); - } - - protected function tearDown(): void - { - Mockery::close(); - parent::tearDown(); - } - - public function test_get_returns_option_value_when_exists(): void - { - $key = 'test_key'; - $value = 'test_value'; - $option = new Option($key, $value); - - $this->repository->shouldReceive('get')->with($key)->once()->andReturn($option); - - $result = $this->service->get($key); - - $this->assertEquals($value, $result); - } - - public function test_get_returns_default_when_option_not_exists(): void - { - $key = 'test_key'; - $default = 'default_value'; + }); - $this->repository->shouldReceive('get')->with($key)->once()->andReturn(null); + it('returns option value when exists', function (): void { + $this->repository->shouldReceive('get')->with('test_key')->once()->andReturn(new Option('test_key', 'test_value')); - $result = $this->service->get($key, $default); + expect($this->service->get('test_key'))->toBe('test_value'); + }); - $this->assertEquals($default, $result); - } + it('returns default when option not exists', function (): void { + $this->repository->shouldReceive('get')->with('test_key')->once()->andReturn(null); - public function test_get_returns_null_as_default_when_no_default_provided(): void - { - $key = 'test_key'; + expect($this->service->get('test_key', 'default_value'))->toBe('default_value'); + }); - $this->repository->shouldReceive('get')->with($key)->once()->andReturn(null); + it('returns null as default when no default provided', function (): void { + $this->repository->shouldReceive('get')->with('test_key')->once()->andReturn(null); - $result = $this->service->get($key); + expect($this->service->get('test_key'))->toBeNull(); + }); - $this->assertNull($result); - } - - public function test_set_stores_new_option_when_not_exists(): void - { - $key = 'test_key'; - $value = 'test_value'; - - $this->repository->shouldReceive('exists')->with($key)->once()->andReturn(false); + it('stores new option when not exists', function (): void { + $this->repository->shouldReceive('exists')->with('test_key')->once()->andReturn(false); $this->repository->shouldReceive('store')->once()->andReturn(true); - $result = $this->service->set($key, $value); - - $this->assertTrue($result); - } + expect($this->service->set('test_key', 'test_value'))->toBeTrue(); + }); - public function test_set_updates_existing_option_when_exists(): void - { - $key = 'test_key'; - $value = 'test_value'; - - $this->repository->shouldReceive('exists')->with($key)->once()->andReturn(true); + it('updates existing option when exists', function (): void { + $this->repository->shouldReceive('exists')->with('test_key')->once()->andReturn(true); $this->repository->shouldReceive('update')->once()->andReturn(true); - $result = $this->service->set($key, $value); - - $this->assertTrue($result); - } - - public function test_update_calls_repository_update(): void - { - $key = 'test_key'; - $value = 'test_value'; + expect($this->service->set('test_key', 'test_value'))->toBeTrue(); + }); + it('update calls repository update', function (): void { $this->repository->shouldReceive('update')->once()->andReturn(true); - $result = $this->service->update($key, $value); - - $this->assertTrue($result); - } - - public function test_delete_calls_repository_delete(): void - { - $key = 'test_key'; - - $this->repository->shouldReceive('delete')->with($key)->once()->andReturn(true); - - $result = $this->service->delete($key); - - $this->assertTrue($result); - } + expect($this->service->update('test_key', 'test_value'))->toBeTrue(); + }); - public function test_exists_calls_repository_exists(): void - { - $key = 'test_key'; + it('delete calls repository delete', function (): void { + $this->repository->shouldReceive('delete')->with('test_key')->once()->andReturn(true); - $this->repository->shouldReceive('exists')->with($key)->once()->andReturn(true); + expect($this->service->delete('test_key'))->toBeTrue(); + }); - $result = $this->service->exists($key); + it('exists calls repository exists', function (): void { + $this->repository->shouldReceive('exists')->with('test_key')->once()->andReturn(true); - $this->assertTrue($result); - } -} + expect($this->service->exists('test_key'))->toBeTrue(); + }); +}); diff --git a/tests/Unit/Option/Domain/Exceptions/InvalidOptionExceptionTest.php b/tests/Unit/Option/Domain/Exceptions/InvalidOptionExceptionTest.php index 89005037..adf45eed 100644 --- a/tests/Unit/Option/Domain/Exceptions/InvalidOptionExceptionTest.php +++ b/tests/Unit/Option/Domain/Exceptions/InvalidOptionExceptionTest.php @@ -2,25 +2,19 @@ declare(strict_types=1); -namespace Tests\Unit\Option\Domain\Exceptions; - -use PHPUnit\Framework\TestCase; use Pollora\Option\Domain\Exceptions\InvalidOptionException; -final class InvalidOptionExceptionTest extends TestCase -{ - public function test_creates_exception_with_custom_message(): void - { +describe('InvalidOptionException', function (): void { + it('creates exception with custom message', function (): void { $message = 'Custom error message'; $exception = new InvalidOptionException($message); - $this->assertEquals($message, $exception->getMessage()); - } + expect($exception->getMessage())->toBe($message); + }); - public function test_handles_empty_message(): void - { + it('handles empty message', function (): void { $exception = new InvalidOptionException(''); - $this->assertEquals('', $exception->getMessage()); - } -} + expect($exception->getMessage())->toBe(''); + }); +}); diff --git a/tests/Unit/Option/Domain/Exceptions/OptionNotFoundExceptionTest.php b/tests/Unit/Option/Domain/Exceptions/OptionNotFoundExceptionTest.php index e80a7dac..d904414d 100644 --- a/tests/Unit/Option/Domain/Exceptions/OptionNotFoundExceptionTest.php +++ b/tests/Unit/Option/Domain/Exceptions/OptionNotFoundExceptionTest.php @@ -2,24 +2,18 @@ declare(strict_types=1); -namespace Tests\Unit\Option\Domain\Exceptions; - -use PHPUnit\Framework\TestCase; use Pollora\Option\Domain\Exceptions\OptionNotFoundException; -final class OptionNotFoundExceptionTest extends TestCase -{ - public function test_creates_exception_with_formatted_message(): void - { +describe('OptionNotFoundException', function (): void { + it('creates exception with formatted message', function (): void { $exception = new OptionNotFoundException('test_key'); - $this->assertEquals("Option 'test_key' not found", $exception->getMessage()); - } + expect($exception->getMessage())->toBe("Option 'test_key' not found"); + }); - public function test_handles_special_characters_in_key(): void - { + it('handles special characters in key', function (): void { $exception = new OptionNotFoundException('test-key_with.special'); - $this->assertEquals("Option 'test-key_with.special' not found", $exception->getMessage()); - } -} + expect($exception->getMessage())->toBe("Option 'test-key_with.special' not found"); + }); +}); diff --git a/tests/Unit/Option/Domain/Models/OptionTest.php b/tests/Unit/Option/Domain/Models/OptionTest.php index dfd63d2b..061164ad 100644 --- a/tests/Unit/Option/Domain/Models/OptionTest.php +++ b/tests/Unit/Option/Domain/Models/OptionTest.php @@ -2,82 +2,72 @@ declare(strict_types=1); -namespace Tests\Unit\Option\Domain\Models; - -use PHPUnit\Framework\TestCase; use Pollora\Option\Domain\Models\Option; -final class OptionTest extends TestCase -{ - public function test_can_create_option_with_default_autoload(): void - { +describe('Option', function (): void { + it('can create option with default autoload', function (): void { $option = new Option('test_key', 'test_value'); - $this->assertEquals('test_key', $option->key); - $this->assertEquals('test_value', $option->value); - $this->assertTrue($option->autoload); - } + expect($option->key)->toBe('test_key'); + expect($option->value)->toBe('test_value'); + expect($option->autoload)->toBeTrue(); + }); - public function test_can_create_option_with_custom_autoload(): void - { + it('can create option with custom autoload', function (): void { $option = new Option('test_key', 'test_value', false); - $this->assertEquals('test_key', $option->key); - $this->assertEquals('test_value', $option->value); - $this->assertFalse($option->autoload); - } + expect($option->key)->toBe('test_key'); + expect($option->value)->toBe('test_value'); + expect($option->autoload)->toBeFalse(); + }); - public function test_can_create_option_with_different_value_types(): void - { + it('can create option with different value types', function (): void { $stringOption = new Option('string_key', 'test'); $intOption = new Option('int_key', 42); $arrayOption = new Option('array_key', ['foo' => 'bar']); $boolOption = new Option('bool_key', true); $nullOption = new Option('null_key', null); - $this->assertEquals('test', $stringOption->value); - $this->assertEquals(42, $intOption->value); - $this->assertEquals(['foo' => 'bar'], $arrayOption->value); - $this->assertTrue($boolOption->value); - $this->assertNull($nullOption->value); - } + expect($stringOption->value)->toBe('test'); + expect($intOption->value)->toBe(42); + expect($arrayOption->value)->toEqual(['foo' => 'bar']); + expect($boolOption->value)->toBeTrue(); + expect($nullOption->value)->toBeNull(); + }); - public function test_with_value_returns_new_instance(): void - { + it('withValue returns new instance', function (): void { $original = new Option('test_key', 'original_value'); $updated = $original->withValue('new_value'); - $this->assertNotSame($original, $updated); - $this->assertEquals('original_value', $original->value); - $this->assertEquals('new_value', $updated->value); - $this->assertEquals('test_key', $updated->key); - $this->assertEquals($original->autoload, $updated->autoload); - } + expect($updated)->not->toBe($original); + expect($original->value)->toBe('original_value'); + expect($updated->value)->toBe('new_value'); + expect($updated->key)->toBe('test_key'); + expect($updated->autoload)->toBe($original->autoload); + }); - public function test_with_autoload_returns_new_instance(): void - { + it('withAutoload returns new instance', function (): void { $original = new Option('test_key', 'test_value', true); $updated = $original->withAutoload(false); - $this->assertNotSame($original, $updated); - $this->assertTrue($original->autoload); - $this->assertFalse($updated->autoload); - $this->assertEquals($original->key, $updated->key); - $this->assertEquals($original->value, $updated->value); - } + expect($updated)->not->toBe($original); + expect($original->autoload)->toBeTrue(); + expect($updated->autoload)->toBeFalse(); + expect($updated->key)->toBe($original->key); + expect($updated->value)->toBe($original->value); + }); - public function test_chaining_with_methods(): void - { + it('supports chaining with methods', function (): void { $original = new Option('test_key', 'original_value', true); $updated = $original ->withValue('new_value') ->withAutoload(false); - $this->assertEquals('test_key', $updated->key); - $this->assertEquals('new_value', $updated->value); - $this->assertFalse($updated->autoload); + expect($updated->key)->toBe('test_key'); + expect($updated->value)->toBe('new_value'); + expect($updated->autoload)->toBeFalse(); - $this->assertEquals('original_value', $original->value); - $this->assertTrue($original->autoload); - } -} + expect($original->value)->toBe('original_value'); + expect($original->autoload)->toBeTrue(); + }); +}); diff --git a/tests/Unit/Option/Domain/Services/OptionValidationServiceTest.php b/tests/Unit/Option/Domain/Services/OptionValidationServiceTest.php index 4d0f3cae..1db8e48b 100644 --- a/tests/Unit/Option/Domain/Services/OptionValidationServiceTest.php +++ b/tests/Unit/Option/Domain/Services/OptionValidationServiceTest.php @@ -2,109 +2,64 @@ declare(strict_types=1); -namespace Tests\Unit\Option\Domain\Services; - -use PHPUnit\Framework\TestCase; use Pollora\Option\Domain\Exceptions\InvalidOptionException; use Pollora\Option\Domain\Services\OptionValidationService; -final class OptionValidationServiceTest extends TestCase -{ - private OptionValidationService $service; - - protected function setUp(): void - { - parent::setUp(); +describe('OptionValidationService', function (): void { + beforeEach(function (): void { $this->service = new OptionValidationService; - } + }); - public function test_validates_valid_key(): void - { - $this->expectNotToPerformAssertions(); + it('validates valid key', function (): void { $this->service->validateKey('valid_key'); - } - - public function test_throws_exception_for_empty_key(): void - { - $this->expectException(InvalidOptionException::class); - $this->expectExceptionMessage('Option key cannot be empty'); + })->throwsNoExceptions(); + it('throws exception for empty key', function (): void { $this->service->validateKey(''); - } - - public function test_throws_exception_for_too_long_key(): void - { - $longKey = str_repeat('a', 192); - - $this->expectException(InvalidOptionException::class); - $this->expectExceptionMessage('Option key cannot exceed 191 characters'); - - $this->service->validateKey($longKey); - } - - public function test_accepts_maximum_length_key(): void - { - $maxKey = str_repeat('a', 191); + })->throws(InvalidOptionException::class, 'Option key cannot be empty'); - $this->expectNotToPerformAssertions(); - $this->service->validateKey($maxKey); - } + it('throws exception for too long key', function (): void { + $this->service->validateKey(str_repeat('a', 192)); + })->throws(InvalidOptionException::class, 'Option key cannot exceed 191 characters'); - public function test_throws_exception_for_key_with_null_bytes(): void - { - $this->expectException(InvalidOptionException::class); - $this->expectExceptionMessage('Option key cannot contain null bytes'); + it('accepts maximum length key', function (): void { + $this->service->validateKey(str_repeat('a', 191)); + })->throwsNoExceptions(); + it('throws exception for key with null bytes', function (): void { $this->service->validateKey("test\0key"); - } - - public function test_validates_valid_scalar_values(): void - { - $this->expectNotToPerformAssertions(); + })->throws(InvalidOptionException::class, 'Option key cannot contain null bytes'); + it('validates valid scalar values', function (): void { $this->service->validateValue('string'); $this->service->validateValue(42); $this->service->validateValue(3.14); $this->service->validateValue(true); $this->service->validateValue(null); - } + })->throwsNoExceptions(); - public function test_validates_valid_array_value(): void - { - $this->expectNotToPerformAssertions(); + it('validates valid array value', function (): void { $this->service->validateValue(['key' => 'value']); - } + })->throwsNoExceptions(); - public function test_validates_serializable_object(): void - { - $object = new \stdClass; + it('validates serializable object', function (): void { + $object = new stdClass; $object->property = 'value'; - $this->expectNotToPerformAssertions(); $this->service->validateValue($object); - } + })->throwsNoExceptions(); - public function test_throws_exception_for_resource_value(): void - { + it('throws exception for resource value', function (): void { $resource = fopen('php://memory', 'r'); - $this->expectException(InvalidOptionException::class); - $this->expectExceptionMessage('Option value cannot be a resource'); - - $this->service->validateValue($resource); - - fclose($resource); - } - - public function test_throws_exception_for_non_serializable_object(): void - { - $closure = function () { - return 'test'; - }; - - $this->expectException(InvalidOptionException::class); - $this->expectExceptionMessage('Option value must be serializable'); - - $this->service->validateValue($closure); - } -} + try { + $this->service->validateValue($resource); + } finally { + fclose($resource); + } + })->throws(InvalidOptionException::class, 'Option value cannot be a resource'); + + it('throws exception for non-serializable object', function (): void { + $this->service->validateValue(fn (): string => 'test'); + })->throws(InvalidOptionException::class, 'Option value must be serializable'); +}); diff --git a/tests/Unit/Option/Infrastructure/Repositories/WordPressOptionRepositoryTest.php b/tests/Unit/Option/Infrastructure/Repositories/WordPressOptionRepositoryTest.php index f59e104e..0c2e5a00 100644 --- a/tests/Unit/Option/Infrastructure/Repositories/WordPressOptionRepositoryTest.php +++ b/tests/Unit/Option/Infrastructure/Repositories/WordPressOptionRepositoryTest.php @@ -2,114 +2,60 @@ declare(strict_types=1); -namespace Tests\Unit\Option\Infrastructure\Repositories; - -use PHPUnit\Framework\TestCase; use Pollora\Option\Domain\Models\Option; use Pollora\Option\Infrastructure\Repositories\WordPressOptionRepository; -final class WordPressOptionRepositoryTest extends TestCase -{ - private WordPressOptionRepository $repository; +require_once dirname(__DIR__, 3).'/helpers.php'; - protected function setUp(): void - { - parent::setUp(); +describe('WordPressOptionRepository', function (): void { + beforeEach(function (): void { setupWordPressMocks(); $this->repository = new WordPressOptionRepository; - } - - public function test_get_returns_option_when_exists(): void - { - $key = 'test_key'; - $value = 'test_value'; - - setWordPressFunction('get_option', function ($optionKey, $default) use ($key, $value) { - return $optionKey === $key ? $value : $default; - }); - - $result = $this->repository->get($key); - - $this->assertInstanceOf(Option::class, $result); - $this->assertEquals($key, $result->key); - $this->assertEquals($value, $result->value); - } - - public function test_get_returns_null_when_not_exists(): void - { - $key = 'non_existent_key'; - - setWordPressFunction('get_option', function ($optionKey, $default) use ($key) { - return $optionKey === $key ? null : $default; - }); - - $result = $this->repository->get($key); - - $this->assertNull($result); - } - - public function test_store_returns_true(): void - { - $option = new Option('test_key', 'test_value', true); - - setWordPressFunction('add_option', function () { - return true; - }); - - $result = $this->repository->store($option); - - $this->assertTrue($result); - } - - public function test_update_returns_true(): void - { - $option = new Option('test_key', 'updated_value', false); + }); - setWordPressFunction('update_option', function () { - return true; - }); + it('returns option when exists', function (): void { + setWordPressFunction('get_option', fn ($key, $default) => $key === 'test_key' ? 'test_value' : $default); - $result = $this->repository->update($option); + $result = $this->repository->get('test_key'); - $this->assertTrue($result); - } + expect($result)->toBeInstanceOf(Option::class); + expect($result->key)->toBe('test_key'); + expect($result->value)->toBe('test_value'); + }); - public function test_delete_returns_true(): void - { - $key = 'test_key'; + it('returns null when not exists', function (): void { + setWordPressFunction('get_option', fn ($key, $default) => $key === 'non_existent_key' ? null : $default); - setWordPressFunction('delete_option', function () { - return true; - }); + expect($this->repository->get('non_existent_key'))->toBeNull(); + }); - $result = $this->repository->delete($key); + it('store returns true', function (): void { + setWordPressFunction('add_option', fn (): true => true); - $this->assertTrue($result); - } + expect($this->repository->store(new Option('test_key', 'test_value', true)))->toBeTrue(); + }); - public function test_exists_returns_true_when_option_has_value(): void - { - $key = 'existing_key'; + it('update returns true', function (): void { + setWordPressFunction('update_option', fn (): true => true); - setWordPressFunction('get_option', function ($optionKey, $default) use ($key) { - return $optionKey === $key ? 'some_value' : $default; - }); + expect($this->repository->update(new Option('test_key', 'updated_value', false)))->toBeTrue(); + }); - $result = $this->repository->exists($key); + it('delete returns true', function (): void { + setWordPressFunction('delete_option', fn (): true => true); - $this->assertTrue($result); - } + expect($this->repository->delete('test_key'))->toBeTrue(); + }); - public function test_exists_returns_false_when_option_is_null(): void - { - $key = 'non_existent_key'; + it('exists returns true when option has value', function (): void { + setWordPressFunction('get_option', fn ($key, $default) => $key === 'existing_key' ? 'some_value' : $default); - setWordPressFunction('get_option', function ($optionKey, $default) use ($key) { - return $optionKey === $key ? null : $default; - }); + expect($this->repository->exists('existing_key'))->toBeTrue(); + }); - $result = $this->repository->exists($key); + it('exists returns false when option is null', function (): void { + setWordPressFunction('get_option', fn ($key, $default) => $key === 'non_existent_key' ? null : $default); - $this->assertFalse($result); - } -} + expect($this->repository->exists('non_existent_key'))->toBeFalse(); + }); +}); diff --git a/tests/Unit/Plugin/PluginMetadataTest.php b/tests/Unit/Plugin/PluginMetadataTest.php index 50e5a9eb..100e8934 100644 --- a/tests/Unit/Plugin/PluginMetadataTest.php +++ b/tests/Unit/Plugin/PluginMetadataTest.php @@ -2,173 +2,112 @@ declare(strict_types=1); -namespace Tests\Unit\Plugin; - -use PHPUnit\Framework\TestCase; use Pollora\Plugin\Domain\Models\PluginMetadata; -/** - * Tests for PluginMetadata class. - */ -class PluginMetadataTest extends TestCase -{ - private PluginMetadata $pluginMetadata; +describe('PluginMetadata', function (): void { + beforeEach(function (): void { + $this->pluginName = 'test-plugin'; + $this->basePath = '/path/to/plugins'; + $this->metadata = new PluginMetadata($this->pluginName, $this->basePath); + }); - private string $pluginName; + it('can be instantiated with name and base path', function (): void { + expect($this->metadata)->toBeInstanceOf(PluginMetadata::class); + expect($this->metadata->getName())->toBe($this->pluginName); + }); - private string $basePath; + it('returns correct base path', function (): void { + expect($this->metadata->getBasePath())->toBe($this->basePath.'/'.$this->pluginName); + }); - protected function setUp(): void - { - $this->pluginName = 'test-plugin'; - $this->basePath = '/path/to/plugins'; - $this->pluginMetadata = new PluginMetadata($this->pluginName, $this->basePath); - } - - public function test_it_can_be_instantiated_with_name_and_base_path(): void - { - $this->assertInstanceOf(PluginMetadata::class, $this->pluginMetadata); - $this->assertEquals($this->pluginName, $this->pluginMetadata->getName()); - } - - public function test_it_returns_correct_base_path(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName; - $this->assertEquals($expectedPath, $this->pluginMetadata->getBasePath()); - } - - public function test_it_returns_correct_main_file_path(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName.'/'.$this->pluginName.'.php'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getMainFilePath()); - } - - public function test_it_returns_correct_config_path(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName.'/plugin.json'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getConfigPath()); - } - - public function test_it_returns_correct_language_path(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName.'/languages'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getLanguagePath()); - } - - public function test_it_returns_correct_plugin_namespace(): void - { - $expectedNamespace = 'TestPlugin'; - $this->assertEquals($expectedNamespace, $this->pluginMetadata->getPluginNamespace()); - } - - public function test_it_handles_complex_plugin_names(): void - { - $complexPlugin = new PluginMetadata('my-awesome-plugin', $this->basePath); - $expectedNamespace = 'MyAwesomePlugin'; - $this->assertEquals($expectedNamespace, $complexPlugin->getPluginNamespace()); - } - - public function test_it_returns_correct_plugin_app_dir(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName.'/app'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getPluginAppDir()); - } - - public function test_it_returns_correct_plugin_app_dir_with_subdirectory(): void - { - $subDirectory = 'Providers'; - $expectedPath = $this->basePath.'/'.$this->pluginName.'/app/'.$subDirectory; - $this->assertEquals($expectedPath, $this->pluginMetadata->getPluginAppDir($subDirectory)); - } - - public function test_it_returns_correct_plugin_app_file(): void - { - $fileName = 'ServiceProvider.php'; - $expectedPath = $this->basePath.'/'.$this->pluginName.'/app/'.$fileName; - $this->assertEquals($expectedPath, $this->pluginMetadata->getPluginAppFile($fileName)); - } - - public function test_it_returns_empty_config_initially(): void - { - $this->assertEquals([], $this->pluginMetadata->getConfig()); - } - - public function test_it_returns_correct_slug(): void - { - $this->assertEquals('test-plugin', $this->pluginMetadata->getSlug()); - } - - public function test_it_returns_correct_basename(): void - { - $expectedBasename = $this->pluginName.'/'.$this->pluginName.'.php'; - $this->assertEquals($expectedBasename, $this->pluginMetadata->getBasename()); - } - - public function test_it_handles_plugin_name_with_spaces(): void - { + it('returns correct main file path', function (): void { + expect($this->metadata->getMainFilePath())->toBe($this->basePath.'/'.$this->pluginName.'/'.$this->pluginName.'.php'); + }); + + it('returns correct config path', function (): void { + expect($this->metadata->getConfigPath())->toBe($this->basePath.'/'.$this->pluginName.'/plugin.json'); + }); + + it('returns correct language path', function (): void { + expect($this->metadata->getLanguagePath())->toBe($this->basePath.'/'.$this->pluginName.'/languages'); + }); + + it('returns correct plugin namespace', function (): void { + expect($this->metadata->getPluginNamespace())->toBe('TestPlugin'); + }); + + it('handles complex plugin names', function (): void { + $plugin = new PluginMetadata('my-awesome-plugin', $this->basePath); + expect($plugin->getPluginNamespace())->toBe('MyAwesomePlugin'); + }); + + it('returns correct plugin app dir', function (): void { + expect($this->metadata->getPluginAppDir())->toBe($this->basePath.'/'.$this->pluginName.'/app'); + }); + + it('returns correct plugin app dir with subdirectory', function (): void { + expect($this->metadata->getPluginAppDir('Providers'))->toBe($this->basePath.'/'.$this->pluginName.'/app/Providers'); + }); + + it('returns correct plugin app file', function (): void { + expect($this->metadata->getPluginAppFile('ServiceProvider.php'))->toBe($this->basePath.'/'.$this->pluginName.'/app/ServiceProvider.php'); + }); + + it('returns empty config initially', function (): void { + expect($this->metadata->getConfig())->toBe([]); + }); + + it('returns correct slug', function (): void { + expect($this->metadata->getSlug())->toBe('test-plugin'); + }); + + it('returns correct basename', function (): void { + expect($this->metadata->getBasename())->toBe($this->pluginName.'/'.$this->pluginName.'.php'); + }); + + it('handles plugin name with spaces', function (): void { $plugin = new PluginMetadata('My Plugin Name', $this->basePath); - $this->assertEquals('my-plugin-name', $plugin->getSlug()); - } + expect($plugin->getSlug())->toBe('my-plugin-name'); + }); - public function test_it_handles_plugin_name_with_underscores(): void - { + it('handles plugin name with underscores', function (): void { $plugin = new PluginMetadata('my_plugin_name', $this->basePath); - $this->assertEquals('my-plugin-name', $plugin->getSlug()); - } - - public function test_it_returns_correct_views_path(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName.'/views'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getViewsPath()); - } - - public function test_it_returns_correct_assets_path(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName.'/assets'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getAssetsPath()); - } - - public function test_it_returns_correct_routes_path(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName.'/routes'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getRoutesPath()); - } - - public function test_it_returns_correct_config_dir(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName.'/config'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getConfigDir()); - } - - public function test_it_returns_correct_database_path(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName.'/database'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getDatabasePath()); - } - - public function test_it_returns_correct_tests_path(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName.'/tests'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getTestsPath()); - } - - public function test_it_returns_path_for_item_with_array(): void - { - $pathParts = ['config', 'app.php']; - $expectedPath = $this->basePath.'/'.$this->pluginName.'/config/app.php'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getPathForItem($pathParts)); - } - - public function test_it_returns_path_for_item_with_string(): void - { - $pathPart = 'config'; - $expectedPath = $this->basePath.'/'.$this->pluginName.'/config'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getPathForItem($pathPart)); - } - - public function test_it_returns_path_for_item_with_null(): void - { - $expectedPath = $this->basePath.'/'.$this->pluginName.'/'; - $this->assertEquals($expectedPath, $this->pluginMetadata->getPathForItem(null)); - } -} + expect($plugin->getSlug())->toBe('my-plugin-name'); + }); + + it('returns correct views path', function (): void { + expect($this->metadata->getViewsPath())->toBe($this->basePath.'/'.$this->pluginName.'/views'); + }); + + it('returns correct assets path', function (): void { + expect($this->metadata->getAssetsPath())->toBe($this->basePath.'/'.$this->pluginName.'/assets'); + }); + + it('returns correct routes path', function (): void { + expect($this->metadata->getRoutesPath())->toBe($this->basePath.'/'.$this->pluginName.'/routes'); + }); + + it('returns correct config dir', function (): void { + expect($this->metadata->getConfigDir())->toBe($this->basePath.'/'.$this->pluginName.'/config'); + }); + + it('returns correct database path', function (): void { + expect($this->metadata->getDatabasePath())->toBe($this->basePath.'/'.$this->pluginName.'/database'); + }); + + it('returns correct tests path', function (): void { + expect($this->metadata->getTestsPath())->toBe($this->basePath.'/'.$this->pluginName.'/tests'); + }); + + it('returns path for item with array', function (): void { + expect($this->metadata->getPathForItem(['config', 'app.php']))->toBe($this->basePath.'/'.$this->pluginName.'/config/app.php'); + }); + + it('returns path for item with string', function (): void { + expect($this->metadata->getPathForItem('config'))->toBe($this->basePath.'/'.$this->pluginName.'/config'); + }); + + it('returns path for item with null', function (): void { + expect($this->metadata->getPathForItem(null))->toBe($this->basePath.'/'.$this->pluginName.'/'); + }); +}); diff --git a/tests/Unit/Plugin/PluginModuleTest.php b/tests/Unit/Plugin/PluginModuleTest.php index 30db1f79..57c588f1 100644 --- a/tests/Unit/Plugin/PluginModuleTest.php +++ b/tests/Unit/Plugin/PluginModuleTest.php @@ -2,258 +2,200 @@ declare(strict_types=1); -namespace Tests\Unit\Plugin; - -use PHPUnit\Framework\TestCase; use Pollora\Plugin\Domain\Models\PluginModule; -/** - * Tests for PluginModule class. - */ -class PluginModuleTest extends TestCase -{ - private PluginModule $pluginModule; - - private string $pluginName; - - private string $pluginPath; - - protected function setUp(): void - { +describe('PluginModule', function (): void { + beforeEach(function (): void { $this->pluginName = 'test-plugin'; $this->pluginPath = '/path/to/plugins/test-plugin'; - $this->pluginModule = new PluginModule($this->pluginName, $this->pluginPath); - } - - public function test_it_can_be_instantiated(): void - { - $this->assertInstanceOf(PluginModule::class, $this->pluginModule); - $this->assertEquals($this->pluginName, $this->pluginModule->getName()); - $this->assertEquals($this->pluginPath, $this->pluginModule->getPath()); - } - - public function test_it_has_default_disabled_state(): void - { - $this->assertFalse($this->pluginModule->isEnabled()); - $this->assertTrue($this->pluginModule->isDisabled()); - $this->assertFalse($this->pluginModule->isActive()); - } - - public function test_it_can_be_enabled_and_disabled(): void - { - $this->pluginModule->enable(); - $this->assertTrue($this->pluginModule->isEnabled()); - $this->assertFalse($this->pluginModule->isDisabled()); - - $this->pluginModule->disable(); - $this->assertFalse($this->pluginModule->isEnabled()); - $this->assertTrue($this->pluginModule->isDisabled()); - } - - public function test_it_can_be_activated_and_deactivated(): void - { - $this->pluginModule->activate(); - $this->assertTrue($this->pluginModule->isActive()); - - $this->pluginModule->deactivate(); - $this->assertFalse($this->pluginModule->isActive()); - } - - public function test_it_returns_correct_plugin_data(): void - { - $this->pluginModule->setHeaders([ + $this->module = new PluginModule($this->pluginName, $this->pluginPath); + }); + + it('can be instantiated', function (): void { + expect($this->module)->toBeInstanceOf(PluginModule::class); + expect($this->module->getName())->toBe($this->pluginName); + expect($this->module->getPath())->toBe($this->pluginPath); + }); + + it('has default disabled state', function (): void { + expect($this->module->isEnabled())->toBeFalse(); + expect($this->module->isDisabled())->toBeTrue(); + expect($this->module->isActive())->toBeFalse(); + }); + + it('can be enabled and disabled', function (): void { + $this->module->enable(); + expect($this->module->isEnabled())->toBeTrue(); + expect($this->module->isDisabled())->toBeFalse(); + + $this->module->disable(); + expect($this->module->isEnabled())->toBeFalse(); + expect($this->module->isDisabled())->toBeTrue(); + }); + + it('can be activated and deactivated', function (): void { + $this->module->activate(); + expect($this->module->isActive())->toBeTrue(); + + $this->module->deactivate(); + expect($this->module->isActive())->toBeFalse(); + }); + + it('returns correct plugin data', function (): void { + $this->module->setHeaders([ 'Name' => 'Test Plugin', 'Description' => 'A test plugin', 'Version' => '1.0.0', 'Author' => 'Test Author', ]); - $pluginData = $this->pluginModule->getPluginData(); - - $this->assertArrayHasKey('Name', $pluginData); - $this->assertArrayHasKey('Description', $pluginData); - $this->assertArrayHasKey('Version', $pluginData); - $this->assertArrayHasKey('Author', $pluginData); - $this->assertEquals('Test Plugin', $pluginData['Name']); - $this->assertEquals('A test plugin', $pluginData['Description']); - $this->assertEquals('1.0.0', $pluginData['Version']); - $this->assertEquals('Test Author', $pluginData['Author']); - } - - public function test_it_returns_correct_main_file_path(): void - { - $expectedPath = $this->pluginPath.'/'.$this->pluginName.'.php'; - $this->assertEquals($expectedPath, $this->pluginModule->getMainFile()); - } - - public function test_it_returns_default_version(): void - { - $this->assertEquals('1.0.0', $this->pluginModule->getVersion()); - } - - public function test_it_returns_custom_version_from_headers(): void - { - $this->pluginModule->setHeaders(['Version' => '2.1.0']); - $this->assertEquals('2.1.0', $this->pluginModule->getVersion()); - } - - public function test_it_returns_empty_author_by_default(): void - { - $this->assertEquals('', $this->pluginModule->getAuthor()); - } - - public function test_it_returns_custom_author_from_headers(): void - { - $this->pluginModule->setHeaders(['Author' => 'John Doe']); - $this->assertEquals('John Doe', $this->pluginModule->getAuthor()); - } - - public function test_it_returns_null_plugin_uri_by_default(): void - { - $this->assertNull($this->pluginModule->getPluginUri()); - } - - public function test_it_returns_custom_plugin_uri_from_headers(): void - { - $uri = 'https://example.com/plugin'; - $this->pluginModule->setHeaders(['PluginURI' => $uri]); - $this->assertEquals($uri, $this->pluginModule->getPluginUri()); - } - - public function test_it_returns_null_author_uri_by_default(): void - { - $this->assertNull($this->pluginModule->getAuthorUri()); - } - - public function test_it_returns_custom_author_uri_from_headers(): void - { - $uri = 'https://example.com'; - $this->pluginModule->setHeaders(['AuthorURI' => $uri]); - $this->assertEquals($uri, $this->pluginModule->getAuthorUri()); - } - - public function test_it_is_not_network_wide_by_default(): void - { - $this->assertFalse($this->pluginModule->isNetworkWide()); - } - - public function test_it_can_be_set_as_network_wide(): void - { - $this->pluginModule->setHeaders(['Network' => true]); - $this->assertTrue($this->pluginModule->isNetworkWide()); - } - - public function test_it_returns_plugin_name_as_text_domain_by_default(): void - { - $this->assertEquals($this->pluginName, $this->pluginModule->getTextDomain()); - } - - public function test_it_returns_custom_text_domain_from_headers(): void - { - $textDomain = 'custom-text-domain'; - $this->pluginModule->setHeaders(['TextDomain' => $textDomain]); - $this->assertEquals($textDomain, $this->pluginModule->getTextDomain()); - } - - public function test_it_returns_default_domain_path(): void - { - $this->assertEquals('/languages', $this->pluginModule->getDomainPath()); - } - - public function test_it_returns_custom_domain_path_from_headers(): void - { - $domainPath = '/lang'; - $this->pluginModule->setHeaders(['DomainPath' => $domainPath]); - $this->assertEquals($domainPath, $this->pluginModule->getDomainPath()); - } - - public function test_it_returns_empty_headers_by_default(): void - { - $this->assertEquals([], $this->pluginModule->getHeaders()); - } - - public function test_it_stores_and_returns_headers(): void - { - $headers = [ - 'Name' => 'Test Plugin', - 'Version' => '1.0.0', - 'Author' => 'Test Author', - ]; - - $this->pluginModule->setHeaders($headers); - $this->assertEquals($headers, $this->pluginModule->getHeaders()); - } - - public function test_it_returns_plugin_slug(): void - { - $this->assertEquals($this->pluginName, $this->pluginModule->getSlug()); - } - - public function test_it_returns_plugin_basename(): void - { - $expectedBasename = $this->pluginName.'/'.$this->pluginName.'.php'; - $this->assertEquals($expectedBasename, $this->pluginModule->getBasename()); - } - - public function test_it_returns_root_namespace(): void - { - $this->assertEquals('Plugin', $this->pluginModule->getRootNamespace()); - } - - public function test_it_returns_plugin_namespace(): void - { - $expectedNamespace = 'Plugin\\TestPlugin'; - $this->assertEquals($expectedNamespace, $this->pluginModule->getNamespace()); - } - - public function test_it_normalizes_plugin_name_for_namespace(): void - { + $data = $this->module->getPluginData(); + + expect($data)->toHaveKey('Name'); + expect($data)->toHaveKey('Description'); + expect($data)->toHaveKey('Version'); + expect($data)->toHaveKey('Author'); + expect($data['Name'])->toBe('Test Plugin'); + expect($data['Description'])->toBe('A test plugin'); + expect($data['Version'])->toBe('1.0.0'); + expect($data['Author'])->toBe('Test Author'); + }); + + it('returns correct main file path', function (): void { + expect($this->module->getMainFile())->toBe($this->pluginPath.'/'.$this->pluginName.'.php'); + }); + + it('returns default version', function (): void { + expect($this->module->getVersion())->toBe('1.0.0'); + }); + + it('returns custom version from headers', function (): void { + $this->module->setHeaders(['Version' => '2.1.0']); + expect($this->module->getVersion())->toBe('2.1.0'); + }); + + it('returns empty author by default', function (): void { + expect($this->module->getAuthor())->toBe(''); + }); + + it('returns custom author from headers', function (): void { + $this->module->setHeaders(['Author' => 'John Doe']); + expect($this->module->getAuthor())->toBe('John Doe'); + }); + + it('returns null plugin URI by default', function (): void { + expect($this->module->getPluginUri())->toBeNull(); + }); + + it('returns custom plugin URI from headers', function (): void { + $this->module->setHeaders(['PluginURI' => 'https://example.com/plugin']); + expect($this->module->getPluginUri())->toBe('https://example.com/plugin'); + }); + + it('returns null author URI by default', function (): void { + expect($this->module->getAuthorUri())->toBeNull(); + }); + + it('returns custom author URI from headers', function (): void { + $this->module->setHeaders(['AuthorURI' => 'https://example.com']); + expect($this->module->getAuthorUri())->toBe('https://example.com'); + }); + + it('is not network wide by default', function (): void { + expect($this->module->isNetworkWide())->toBeFalse(); + }); + + it('can be set as network wide', function (): void { + $this->module->setHeaders(['Network' => true]); + expect($this->module->isNetworkWide())->toBeTrue(); + }); + + it('returns plugin name as text domain by default', function (): void { + expect($this->module->getTextDomain())->toBe($this->pluginName); + }); + + it('returns custom text domain from headers', function (): void { + $this->module->setHeaders(['TextDomain' => 'custom-text-domain']); + expect($this->module->getTextDomain())->toBe('custom-text-domain'); + }); + + it('returns default domain path', function (): void { + expect($this->module->getDomainPath())->toBe('/languages'); + }); + + it('returns custom domain path from headers', function (): void { + $this->module->setHeaders(['DomainPath' => '/lang']); + expect($this->module->getDomainPath())->toBe('/lang'); + }); + + it('returns empty headers by default', function (): void { + expect($this->module->getHeaders())->toBe([]); + }); + + it('stores and returns headers', function (): void { + $headers = ['Name' => 'Test Plugin', 'Version' => '1.0.0', 'Author' => 'Test Author']; + $this->module->setHeaders($headers); + expect($this->module->getHeaders())->toBe($headers); + }); + + it('returns plugin slug', function (): void { + expect($this->module->getSlug())->toBe($this->pluginName); + }); + + it('returns plugin basename', function (): void { + expect($this->module->getBasename())->toBe($this->pluginName.'/'.$this->pluginName.'.php'); + }); + + it('returns root namespace', function (): void { + expect($this->module->getRootNamespace())->toBe('Plugin'); + }); + + it('returns plugin namespace', function (): void { + expect($this->module->getNamespace())->toBe('Plugin\\TestPlugin'); + }); + + it('normalizes plugin name for namespace', function (): void { $plugin = new PluginModule('my-awesome-plugin', '/path'); - $expectedNamespace = 'Plugin\\MyAwesomePlugin'; - $this->assertEquals($expectedNamespace, $plugin->getNamespace()); - } - - public function test_it_can_set_active_status(): void - { - $this->assertFalse($this->pluginModule->isActive()); - - $result = $this->pluginModule->setActive(true); - $this->assertTrue($this->pluginModule->isActive()); - $this->assertSame($this->pluginModule, $result); - - $this->pluginModule->setActive(false); - $this->assertFalse($this->pluginModule->isActive()); - } - - public function test_it_can_set_enabled_status(): void - { - $this->assertFalse($this->pluginModule->isEnabled()); - - $result = $this->pluginModule->setEnabled(true); - $this->assertTrue($this->pluginModule->isEnabled()); - $this->assertSame($this->pluginModule, $result); - - $this->pluginModule->setEnabled(false); - $this->assertFalse($this->pluginModule->isEnabled()); - } - - public function test_it_returns_null_for_optional_version_fields(): void - { - $this->assertNull($this->pluginModule->getRequiredWordPressVersion()); - $this->assertNull($this->pluginModule->getTestedWordPressVersion()); - $this->assertNull($this->pluginModule->getRequiredPhpVersion()); - } - - public function test_it_returns_custom_version_requirements_from_headers(): void - { - $this->pluginModule->setHeaders([ + expect($plugin->getNamespace())->toBe('Plugin\\MyAwesomePlugin'); + }); + + it('can set active status', function (): void { + expect($this->module->isActive())->toBeFalse(); + + $result = $this->module->setActive(true); + expect($this->module->isActive())->toBeTrue(); + expect($result)->toBe($this->module); + + $this->module->setActive(false); + expect($this->module->isActive())->toBeFalse(); + }); + + it('can set enabled status', function (): void { + expect($this->module->isEnabled())->toBeFalse(); + + $result = $this->module->setEnabled(true); + expect($this->module->isEnabled())->toBeTrue(); + expect($result)->toBe($this->module); + + $this->module->setEnabled(false); + expect($this->module->isEnabled())->toBeFalse(); + }); + + it('returns null for optional version fields', function (): void { + expect($this->module->getRequiredWordPressVersion())->toBeNull(); + expect($this->module->getTestedWordPressVersion())->toBeNull(); + expect($this->module->getRequiredPhpVersion())->toBeNull(); + }); + + it('returns custom version requirements from headers', function (): void { + $this->module->setHeaders([ 'RequiresWP' => '5.0', 'TestedUpTo' => '6.0', 'RequiresPHP' => '8.0', ]); - $this->assertEquals('5.0', $this->pluginModule->getRequiredWordPressVersion()); - $this->assertEquals('6.0', $this->pluginModule->getTestedWordPressVersion()); - $this->assertEquals('8.0', $this->pluginModule->getRequiredPhpVersion()); - } -} + expect($this->module->getRequiredWordPressVersion())->toBe('5.0'); + expect($this->module->getTestedWordPressVersion())->toBe('6.0'); + expect($this->module->getRequiredPhpVersion())->toBe('8.0'); + }); +}); diff --git a/tests/Unit/Plugins/WooCommerce/Domain/Models/TemplateTest.php b/tests/Unit/Plugins/WooCommerce/Domain/Models/TemplateTest.php index b79a03ff..149e2aeb 100644 --- a/tests/Unit/Plugins/WooCommerce/Domain/Models/TemplateTest.php +++ b/tests/Unit/Plugins/WooCommerce/Domain/Models/TemplateTest.php @@ -4,8 +4,8 @@ use Pollora\ThirdParty\WooCommerce\Domain\Models\Template; -describe('Template', function () { - test('can be created with basic parameters', function () { +describe('Template', function (): void { + test('can be created with basic parameters', function (): void { $template = new Template('/path/to/template.php', 'template', false); expect($template->path)->toBe('/path/to/template.php'); @@ -13,7 +13,7 @@ expect($template->isBladeTemplate)->toBeFalse(); }); - test('can be created from path', function () { + test('can be created from path', function (): void { $template = Template::fromPath('/path/to/single-product.php'); expect($template->path)->toBe('/path/to/single-product.php'); @@ -21,7 +21,7 @@ expect($template->isBladeTemplate)->toBeFalse(); }); - test('can detect blade templates when created from path', function () { + test('can detect blade templates when created from path', function (): void { $template = Template::fromPath('/path/to/single-product.blade.php'); expect($template->path)->toBe('/path/to/single-product.blade.php'); @@ -29,7 +29,7 @@ expect($template->isBladeTemplate)->toBeTrue(); }); - test('can get relative path', function () { + test('can get relative path', function (): void { $template = new Template('/plugin/templates/single-product.php'); $defaultPaths = ['/plugin/templates/']; @@ -38,7 +38,7 @@ expect($relativePath)->toBe('single-product.php'); }); - test('returns original path when no default paths match', function () { + test('returns original path when no default paths match', function (): void { $template = new Template('/theme/templates/single-product.php'); $defaultPaths = ['/plugin/templates/']; @@ -47,21 +47,21 @@ expect($relativePath)->toBe('/theme/templates/single-product.php'); }); - test('can detect woocommerce template', function () { + test('can detect woocommerce template', function (): void { $template = new Template('/plugin/templates/single-product.php'); $defaultPaths = ['/plugin/templates/']; expect($template->isWooCommerceTemplate($defaultPaths))->toBeTrue(); }); - test('can detect non-woocommerce template', function () { + test('can detect non-woocommerce template', function (): void { $template = new Template('/theme/templates/single-product.php'); $defaultPaths = ['/plugin/templates/']; expect($template->isWooCommerceTemplate($defaultPaths))->toBeFalse(); }); - test('can convert to blade template', function () { + test('can convert to blade template', function (): void { $template = new Template('/path/to/single-product.php', 'single-product', false); $bladeTemplate = $template->toBladeTemplate(); @@ -71,7 +71,7 @@ expect($bladeTemplate->isBladeTemplate)->toBeTrue(); }); - test('blade template conversion is idempotent', function () { + test('blade template conversion is idempotent', function (): void { $template = new Template('/path/to/single-product.blade.php', 'single-product', true); $bladeTemplate = $template->toBladeTemplate(); @@ -82,7 +82,7 @@ expect($bladeTemplate)->toBe($template); }); - test('non-php files are not converted to blade', function () { + test('non-php files are not converted to blade', function (): void { $template = new Template('/path/to/style.css', 'style', false); $bladeTemplate = $template->toBladeTemplate(); @@ -90,7 +90,7 @@ expect($bladeTemplate)->toBe($template); }); - test('can get view name for blade templates', function () { + test('can get view name for blade templates', function (): void { $template = new Template('woocommerce/single-product.blade.php', 'single-product', true); $viewName = $template->getViewName(); @@ -98,7 +98,7 @@ expect($viewName)->toBe('woocommerce.single-product'); }); - test('returns empty view name for non-blade templates', function () { + test('returns empty view name for non-blade templates', function (): void { $template = new Template('woocommerce/single-product.php', 'single-product', false); $viewName = $template->getViewName(); @@ -106,7 +106,7 @@ expect($viewName)->toBe(''); }); - test('handles complex path in view name', function () { + test('handles complex path in view name', function (): void { $template = new Template('woocommerce/cart/cart.blade.php', 'cart', true); $viewName = $template->getViewName(); @@ -114,7 +114,7 @@ expect($viewName)->toBe('woocommerce.cart.cart'); }); - test('trims dots from view name', function () { + test('trims dots from view name', function (): void { $template = new Template('/single-product.blade.php', 'single-product', true); $viewName = $template->getViewName(); diff --git a/tests/Unit/Plugins/WooCommerce/Domain/Services/WooCommerceServiceTest.php b/tests/Unit/Plugins/WooCommerce/Domain/Services/WooCommerceServiceTest.php index 290813c4..03112ed4 100644 --- a/tests/Unit/Plugins/WooCommerce/Domain/Services/WooCommerceServiceTest.php +++ b/tests/Unit/Plugins/WooCommerce/Domain/Services/WooCommerceServiceTest.php @@ -5,17 +5,17 @@ use Pollora\ThirdParty\WooCommerce\Domain\Models\Template; use Pollora\ThirdParty\WooCommerce\Domain\Services\WooCommerceService; -describe('WooCommerceService', function () { - beforeEach(function () { +describe('WooCommerceService', function (): void { + beforeEach(function (): void { setupWordPressMocks(); $this->service = new WooCommerceService; }); - afterEach(function () { + afterEach(function (): void { resetWordPressMocks(); }); - test('can get default template paths', function () { + test('can get default template paths', function (): void { // Mock WC_ABSPATH constant if (! defined('WC_ABSPATH')) { define('WC_ABSPATH', '/plugin/woocommerce/'); @@ -26,7 +26,7 @@ expect($paths)->toContain('/plugin/woocommerce/templates/'); }); - test('returns empty array when WC_ABSPATH not defined', function () { + test('returns empty array when WC_ABSPATH not defined', function (): void { // This test only works if WC_ABSPATH is not defined // Since we can't easily undefine constants in PHP, we'll skip if already defined if (defined('WC_ABSPATH')) { @@ -39,10 +39,10 @@ expect($paths)->toBe([]); }); - test('can get theme template paths for child themes', function () { + test('can get theme template paths for child themes', function (): void { // Mock WordPress functions - setWordPressFunction('is_child_theme', fn () => true); - setWordPressFunction('get_template_directory', fn () => '/themes/parent'); + setWordPressFunction('is_child_theme', fn (): true => true); + setWordPressFunction('get_template_directory', fn (): string => '/themes/parent'); // Mock WooCommerce function $mockWC = Mockery::mock(); @@ -54,15 +54,15 @@ expect($paths)->toContain('/themes/parent/woocommerce/'); }); - test('returns empty array for non-child themes', function () { - setWordPressFunction('is_child_theme', fn () => false); + test('returns empty array for non-child themes', function (): void { + setWordPressFunction('is_child_theme', fn (): false => false); $paths = $this->service->getThemeTemplatePaths(); expect($paths)->toBe([]); }); - test('can detect woocommerce status screen', function () { + test('can detect woocommerce status screen', function (): void { $screen = new stdClass; $screen->id = 'woocommerce_page_wc-status'; @@ -71,7 +71,7 @@ expect($result)->toBeTrue(); }); - test('returns false when not on woocommerce status screen', function () { + test('returns false when not on woocommerce status screen', function (): void { $screen = new stdClass; $screen->id = 'edit-post'; @@ -80,7 +80,7 @@ expect($result)->toBeFalse(); }); - test('returns false when doing ajax', function () { + test('returns false when doing ajax', function (): void { $screen = new stdClass; $screen->id = 'woocommerce_page_wc-status'; @@ -89,7 +89,7 @@ expect($result)->toBeFalse(); }); - test('returns false when not in admin', function () { + test('returns false when not in admin', function (): void { $screen = new stdClass; $screen->id = 'woocommerce_page_wc-status'; @@ -98,7 +98,7 @@ expect($result)->toBeFalse(); }); - test('can get woocommerce template path with WC available', function () { + test('can get woocommerce template path with WC available', function (): void { $mockWC = Mockery::mock(); $mockWC->shouldReceive('template_path')->andReturn('woocommerce/'); setWordPressFunction('WC', fn () => $mockWC); @@ -108,22 +108,22 @@ expect($path)->toBe('woocommerce/'); }); - test('returns default path when WC not available', function () { - setWordPressFunction('WC', fn () => null); + test('returns default path when WC not available', function (): void { + setWordPressFunction('WC', fn (): null => null); $path = $this->service->getWooCommerceTemplatePath(); expect($path)->toBe('woocommerce/'); }); - test('can get all template paths', function () { + test('can get all template paths', function (): void { // Mock WC_ABSPATH constant if (! defined('WC_ABSPATH')) { define('WC_ABSPATH', '/plugin/woocommerce/'); } - setWordPressFunction('is_child_theme', fn () => true); - setWordPressFunction('get_template_directory', fn () => '/themes/parent'); + setWordPressFunction('is_child_theme', fn (): true => true); + setWordPressFunction('get_template_directory', fn (): string => '/themes/parent'); $mockWC = Mockery::mock(); $mockWC->shouldReceive('template_path')->andReturn('woocommerce/'); @@ -135,14 +135,14 @@ expect($paths)->toContain('/themes/parent/woocommerce/'); }); - test('can create template from path', function () { + test('can create template from path', function (): void { $template = $this->service->createTemplate('/path/to/single-product.php'); expect($template)->toBeInstanceOf(Template::class); expect($template->path)->toBe('/path/to/single-product.php'); }); - test('can add blade variants to template list', function () { + test('can add blade variants to template list', function (): void { $templates = [ 'single-product.php', 'archive-product.php', @@ -159,7 +159,7 @@ expect($result)->not->toContain('style.blade.css'); }); - test('does not duplicate existing blade templates', function () { + test('does not duplicate existing blade templates', function (): void { $templates = [ 'single-product.blade.php', 'archive-product.php', @@ -176,7 +176,7 @@ expect(array_count_values($result)['single-product.blade.php'])->toBe(1); }); - test('handles empty template list', function () { + test('handles empty template list', function (): void { $templates = []; $result = $this->service->addBladeVariants($templates); diff --git a/tests/Unit/Plugins/WooCommerce/Infrastructure/Adapters/WordPressWooCommerceAdapterTest.php b/tests/Unit/Plugins/WooCommerce/Infrastructure/Adapters/WordPressWooCommerceAdapterTest.php index 6ddf7bae..699d0ce5 100644 --- a/tests/Unit/Plugins/WooCommerce/Infrastructure/Adapters/WordPressWooCommerceAdapterTest.php +++ b/tests/Unit/Plugins/WooCommerce/Infrastructure/Adapters/WordPressWooCommerceAdapterTest.php @@ -4,18 +4,18 @@ use Pollora\ThirdParty\WooCommerce\Infrastructure\Adapters\WordPressWooCommerceAdapter; -describe('WordPressWooCommerceAdapter', function () { - beforeEach(function () { +describe('WordPressWooCommerceAdapter', function (): void { + beforeEach(function (): void { setupWordPressMocks(); $this->adapter = new WordPressWooCommerceAdapter; }); - afterEach(function () { + afterEach(function (): void { resetWordPressMocks(); }); - test('can locate template using wordpress function', function () { - setWordPressFunction('locate_template', function ($templates, $load, $requireOnce) { + test('can locate template using wordpress function', function (): void { + setWordPressFunction('locate_template', function ($templates, $load, $requireOnce): string { expect($templates)->toBe('single-product.php'); expect($load)->toBeFalse(); expect($requireOnce)->toBeTrue(); @@ -28,8 +28,8 @@ expect($result)->toBe('/theme/single-product.php'); }); - test('can locate template with array of templates', function () { - setWordPressFunction('locate_template', function ($templates, $load, $requireOnce) { + test('can locate template with array of templates', function (): void { + setWordPressFunction('locate_template', function ($templates, $load, $requireOnce): string { expect($templates)->toBe(['single-product.blade.php', 'single-product.php']); return '/theme/single-product.php'; @@ -40,7 +40,7 @@ expect($result)->toBe('/theme/single-product.php'); }); - test('returns empty string when locate_template function not available', function () { + test('returns empty string when locate_template function not available', function (): void { // Don't set the function to simulate unavailability $adapter = new WordPressWooCommerceAdapter; @@ -49,8 +49,8 @@ expect($result)->toBe(''); }); - test('can add theme support', function () { - setWordPressFunction('add_theme_support', function ($feature, $options = null) { + test('can add theme support', function (): void { + setWordPressFunction('add_theme_support', function ($feature, $options = null): true { expect($feature)->toBe('woocommerce'); expect($options)->toBeNull(); @@ -62,8 +62,8 @@ expect($result)->toBeTrue(); }); - test('can add theme support with options', function () { - setWordPressFunction('add_theme_support', function ($feature, $options = null) { + test('can add theme support with options', function (): void { + setWordPressFunction('add_theme_support', function ($feature, $options = null): true { expect($feature)->toBe('woocommerce'); expect($options)->toBe(['gallery_thumbnail_image_width' => 150]); @@ -77,67 +77,67 @@ // Removed test for function availability since functions are always defined in our test environment - test('can detect child theme', function () { - setWordPressFunction('is_child_theme', fn () => true); + test('can detect child theme', function (): void { + setWordPressFunction('is_child_theme', fn (): true => true); $result = $this->adapter->isChildTheme(); expect($result)->toBeTrue(); }); - test('returns false when not child theme', function () { - setWordPressFunction('is_child_theme', fn () => false); + test('returns false when not child theme', function (): void { + setWordPressFunction('is_child_theme', fn (): false => false); $result = $this->adapter->isChildTheme(); expect($result)->toBeFalse(); }); - test('can get stylesheet directory', function () { - setWordPressFunction('get_stylesheet_directory', fn () => '/themes/child'); + test('can get stylesheet directory', function (): void { + setWordPressFunction('get_stylesheet_directory', fn (): string => '/themes/child'); $result = $this->adapter->getStylesheetDirectory(); expect($result)->toBe('/themes/child'); }); - test('can get template directory', function () { - setWordPressFunction('get_template_directory', fn () => '/themes/parent'); + test('can get template directory', function (): void { + setWordPressFunction('get_template_directory', fn (): string => '/themes/parent'); $result = $this->adapter->getTemplateDirectory(); expect($result)->toBe('/themes/parent'); }); - test('can detect admin area', function () { - setWordPressFunction('is_admin', fn () => true); + test('can detect admin area', function (): void { + setWordPressFunction('is_admin', fn (): true => true); $result = $this->adapter->isAdmin(); expect($result)->toBeTrue(); }); - test('can detect ajax request', function () { - setWordPressFunction('wp_doing_ajax', fn () => true); + test('can detect ajax request', function (): void { + setWordPressFunction('wp_doing_ajax', fn (): true => true); $result = $this->adapter->isDoingAjax(); expect($result)->toBeTrue(); }); - test('can get current screen', function () { + test('can get current screen', function (): void { $expectedScreen = new WP_Screen; $expectedScreen->id = 'woocommerce_page_wc-status'; - setWordPressFunction('get_current_screen', fn () => $expectedScreen); + setWordPressFunction('get_current_screen', fn (): WP_Screen => $expectedScreen); $result = $this->adapter->getCurrentScreen(); expect($result)->toBe($expectedScreen); }); - test('can detect doing action', function () { - setWordPressFunction('doing_action', function ($action) { + test('can detect doing action', function (): void { + setWordPressFunction('doing_action', function ($action): true { expect($action)->toBe('after_setup_theme'); return true; @@ -148,7 +148,7 @@ expect($result)->toBeTrue(); }); - test('can get woocommerce template path', function () { + test('can get woocommerce template path', function (): void { $mockWC = Mockery::mock(); $mockWC->shouldReceive('template_path')->andReturn('woocommerce/'); setWordPressFunction('WC', fn () => $mockWC); @@ -158,16 +158,16 @@ expect($result)->toBe('woocommerce/'); }); - test('returns default template path when WC not available', function () { - setWordPressFunction('WC', fn () => null); + test('returns default template path when WC not available', function (): void { + setWordPressFunction('WC', fn (): null => null); $result = $this->adapter->getWooCommerceTemplatePath(); expect($result)->toBe('woocommerce/'); }); - test('can apply filters', function () { - setWordPressFunction('apply_filters', function ($hook, $value, ...$args) { + test('can apply filters', function (): void { + setWordPressFunction('apply_filters', function ($hook, $value, ...$args): array { expect($hook)->toBe('pollora/woocommerce/template_paths'); expect($value)->toBe(['/default/path/']); expect($args)->toBe(['extra', 'args']); @@ -180,7 +180,7 @@ expect($result)->toBe(['/default/path/', '/custom/path/']); }); - test('returns original value when apply_filters not available', function () { + test('returns original value when apply_filters not available', function (): void { $adapter = new WordPressWooCommerceAdapter; $result = $adapter->applyFilters('test_hook', 'test_value'); @@ -188,12 +188,12 @@ expect($result)->toBe('test_value'); }); - test('can detect woocommerce availability', function () { + test('can detect woocommerce availability', function (): void { if (! defined('WC_ABSPATH')) { define('WC_ABSPATH', '/plugin/woocommerce/'); } - setWordPressFunction('WC', fn () => new stdClass); + setWordPressFunction('WC', fn (): stdClass => new stdClass); $result = $this->adapter->isWooCommerceAvailable(); diff --git a/tests/Unit/Plugins/WooCommerce/Infrastructure/Providers/WooCommerceServiceProviderTest.php b/tests/Unit/Plugins/WooCommerce/Infrastructure/Providers/WooCommerceServiceProviderTest.php index a45779c7..98343ba6 100644 --- a/tests/Unit/Plugins/WooCommerce/Infrastructure/Providers/WooCommerceServiceProviderTest.php +++ b/tests/Unit/Plugins/WooCommerce/Infrastructure/Providers/WooCommerceServiceProviderTest.php @@ -17,26 +17,26 @@ use Pollora\ThirdParty\WooCommerce\Infrastructure\Services\WooCommerceTemplateResolver; use Pollora\View\Domain\Contracts\TemplateFinderInterface; -describe('WooCommerceServiceProvider', function () { - beforeEach(function () { +describe('WooCommerceServiceProvider', function (): void { + beforeEach(function (): void { setupWordPressMocks(); $this->container = new WooCommerceTestContainer; $this->provider = new WooCommerceServiceProvider($this->container); }); - afterEach(function () { + afterEach(function (): void { resetWordPressMocks(); Mockery::close(); }); - test('can register domain services', function () { + test('can register domain services', function (): void { $this->provider->register(); expect($this->container->has(WooCommerceService::class))->toBeTrue(); }); - test('can register infrastructure services', function () { + test('can register infrastructure services', function (): void { // Mock required dependencies $this->container->instance(TemplateFinderInterface::class, Mockery::mock(TemplateFinderInterface::class)); $this->container->instance(ViewFactory::class, Mockery::mock(ViewFactory::class)); @@ -48,7 +48,7 @@ expect($this->container->has(TemplateResolverInterface::class))->toBeTrue(); }); - test('can register application services', function () { + test('can register application services', function (): void { // Mock required dependencies $this->container->instance(Action::class, Mockery::mock(Action::class)); $this->container->instance(Filter::class, Mockery::mock(Filter::class)); @@ -60,7 +60,7 @@ expect($this->container->has(RegisterWooCommerceHooksUseCase::class))->toBeTrue(); }); - test('maintains backward compatibility bindings', function () { + test('maintains backward compatibility bindings', function (): void { // Mock required dependencies $this->container->instance(TemplateFinderInterface::class, Mockery::mock(TemplateFinderInterface::class)); $this->container->instance(ViewFactory::class, Mockery::mock(ViewFactory::class)); @@ -71,7 +71,7 @@ expect($this->container->has(Pollora\ThirdParty\WooCommerce\View\WooCommerceTemplateResolver::class))->toBeTrue(); }); - test('can resolve woocommerce integration service', function () { + test('can resolve woocommerce integration service', function (): void { // Mock all required dependencies $this->container->instance(TemplateFinderInterface::class, Mockery::mock(TemplateFinderInterface::class)); $this->container->instance(ViewFactory::class, Mockery::mock(ViewFactory::class)); @@ -84,7 +84,7 @@ expect($service)->toBeInstanceOf(WooCommerce::class); }); - test('can resolve template resolver service', function () { + test('can resolve template resolver service', function (): void { // Mock all required dependencies $this->container->instance(TemplateFinderInterface::class, Mockery::mock(TemplateFinderInterface::class)); $this->container->instance(ViewFactory::class, Mockery::mock(ViewFactory::class)); @@ -96,7 +96,7 @@ expect($service)->toBeInstanceOf(WooCommerceTemplateResolver::class); }); - test('can resolve use case with all dependencies', function () { + test('can resolve use case with all dependencies', function (): void { // Mock all required dependencies $this->container->instance(Action::class, Mockery::mock(Action::class)); $this->container->instance(Filter::class, Mockery::mock(Filter::class)); @@ -111,7 +111,7 @@ expect($useCase)->toBeInstanceOf(RegisterWooCommerceHooksUseCase::class); }); - test('executes use case on boot', function () { + test('executes use case on boot', function (): void { // Create a mock use case that tracks execution $useCase = Mockery::mock(RegisterWooCommerceHooksUseCase::class); $useCase->shouldReceive('execute')->once(); @@ -129,15 +129,13 @@ class WooCommerceTestContainer extends TestContainer implements Container private array $services = []; - public function singleton($abstract, $concrete = null) + public function singleton($abstract, $concrete = null): void { if ($concrete instanceof Closure) { $this->singletons[$abstract] = $concrete; } elseif ($concrete === null) { // When no concrete is provided, Laravel auto-resolves the class - $this->singletons[$abstract] = function ($container) use ($abstract) { - return new $abstract; - }; + $this->singletons[$abstract] = (fn ($container): object => new $abstract); } else { $this->services[$abstract] = $concrete; } @@ -158,7 +156,7 @@ public function get(string $serviceClass): ?object return parent::get($serviceClass); } - public function make($abstract, array $parameters = []) + public function make($abstract, array $parameters = []): ?object { // Support both Laravel Container interface (mixed $abstract, array $parameters) // and TestContainer interface (string $serviceClass) @@ -243,9 +241,7 @@ public function when($concrete): ContextualBindingBuilder public function factory($abstract): Closure { - return function () use ($abstract) { - return $this->make($abstract); - }; + return fn (): ?object => $this->make($abstract); } public function flush(): void @@ -275,20 +271,21 @@ public function call($callback, array $parameters = [], $defaultMethod = null) if (is_callable($callback)) { return call_user_func_array($callback, $parameters); } + throw new Exception('call() not fully implemented in test container'); } - public function bindMethod($method, $callback) + public function bindMethod($method, $callback): void { // Simplified implementation } - public function addContextualBinding($concrete, $abstract, $implementation) + public function addContextualBinding($concrete, $abstract, $implementation): void { // Simplified implementation } - public function beforeResolving($abstract, $callback = null) + public function beforeResolving($abstract, $callback = null): void { // Simplified implementation } diff --git a/tests/Unit/PostType/Commands/PostTypeMakeCommandTest.php b/tests/Unit/PostType/Commands/PostTypeMakeCommandTest.php index dc7cea78..bdefb9b4 100644 --- a/tests/Unit/PostType/Commands/PostTypeMakeCommandTest.php +++ b/tests/Unit/PostType/Commands/PostTypeMakeCommandTest.php @@ -6,7 +6,7 @@ use Mockery as m; use Pollora\PostType\UI\Console\PostTypeMakeCommand; -beforeEach(function () { +beforeEach(function (): void { // Create a mock filesystem $this->files = m::mock(Filesystem::class); @@ -14,34 +14,31 @@ $this->command = new PostTypeMakeCommand($this->files); }); -afterEach(function () { +afterEach(function (): void { m::close(); }); -test('PostTypeMakeCommand generates correct slug from class name', function () { +test('PostTypeMakeCommand generates correct slug from class name', function (): void { // Test the protected method via reflection $reflectionMethod = new ReflectionMethod(PostTypeMakeCommand::class, 'getSlugFromClassName'); - $reflectionMethod->setAccessible(true); $result = $reflectionMethod->invoke($this->command, 'EventRegistration'); expect($result)->toBe('event-registration'); }); -test('PostTypeMakeCommand generates correct singular name from class name', function () { +test('PostTypeMakeCommand generates correct singular name from class name', function (): void { // Test the protected method via reflection $reflectionMethod = new ReflectionMethod(PostTypeMakeCommand::class, 'getNameFromClassName'); - $reflectionMethod->setAccessible(true); $result = $reflectionMethod->invoke($this->command, 'EventRegistration'); expect($result)->toBe('Event registration'); }); -test('PostTypeMakeCommand generates correct plural name from class name', function () { +test('PostTypeMakeCommand generates correct plural name from class name', function (): void { // Test the protected method via reflection $reflectionMethod = new ReflectionMethod(PostTypeMakeCommand::class, 'getPluralNameFromClassName'); - $reflectionMethod->setAccessible(true); $result = $reflectionMethod->invoke($this->command, 'Event'); diff --git a/tests/Unit/PostType/PostTypeAttributeServiceProviderTest.php b/tests/Unit/PostType/PostTypeAttributeServiceProviderTest.php index 4562f098..2526e25f 100644 --- a/tests/Unit/PostType/PostTypeAttributeServiceProviderTest.php +++ b/tests/Unit/PostType/PostTypeAttributeServiceProviderTest.php @@ -9,7 +9,7 @@ // Define app_path function if it doesn't exist in the test environment if (! function_exists('app_path')) { - function app_path($path = '') + function app_path(string $path = ''): string { return '/path/to/app/'.$path; } @@ -17,7 +17,7 @@ function app_path($path = '') // Define is_dir function if needed for testing if (! function_exists('is_dir_mock')) { - function is_dir_mock($path) + function is_dir_mock($path): bool { return true; // Always return true for testing } @@ -25,13 +25,13 @@ function is_dir_mock($path) // Define mkdir function if needed for testing if (! function_exists('mkdir_mock')) { - function mkdir_mock($path, $mode = 0777, $recursive = false) + function mkdir_mock($path, $mode = 0777, $recursive = false): bool { return true; // Always return true for testing } } -beforeAll(function () { +beforeAll(function (): void { $app = new Container; Facade::setFacadeApplication($app); @@ -46,7 +46,7 @@ function mkdir_mock($path, $mode = 0777, $recursive = false) Action::setFacadeApplication($app); }); -afterAll(function () { +afterAll(function (): void { m::close(); Facade::clearResolvedInstances(); Facade::setFacadeApplication(null); diff --git a/tests/Unit/PostType/PostTypeFactoryTest.php b/tests/Unit/PostType/PostTypeFactoryTest.php index a398d52d..65fdc640 100755 --- a/tests/Unit/PostType/PostTypeFactoryTest.php +++ b/tests/Unit/PostType/PostTypeFactoryTest.php @@ -6,12 +6,12 @@ use Pollora\PostType\Infrastructure\Factories\PostTypeFactory; -beforeEach(function () { +beforeEach(function (): void { setupWordPressMocks(); $this->factory = new PostTypeFactory; }); -test('make creates new PostType instance with correct parameters', function () { +test('make creates new PostType instance with correct parameters', function (): void { // Define test values $slug = 'test-post-type'; $singular = 'Test Post Type'; @@ -26,7 +26,7 @@ ->and($result->getSlug())->toBe($slug); }); -test('make handles null parameters correctly', function () { +test('make handles null parameters correctly', function (): void { // Define test values $slug = 'test-post-type'; diff --git a/tests/Unit/PostType/PostTypeServiceTest.php b/tests/Unit/PostType/PostTypeServiceTest.php index a9b217c1..3fae6437 100755 --- a/tests/Unit/PostType/PostTypeServiceTest.php +++ b/tests/Unit/PostType/PostTypeServiceTest.php @@ -8,13 +8,13 @@ use Pollora\PostType\Domain\Contracts\PostTypeFactoryInterface; use Pollora\PostType\Domain\Contracts\PostTypeRegistryInterface; -beforeEach(function () { +beforeEach(function (): void { $this->mockFactory = mock(PostTypeFactoryInterface::class); $this->mockRegistry = mock(PostTypeRegistryInterface::class); $this->postTypeService = new PostTypeService($this->mockFactory, $this->mockRegistry); }); -test('register calls make on factory', function () { +test('register calls make on factory', function (): void { // Define test values $slug = 'test-post-type'; $singular = 'Test Post Type'; @@ -39,7 +39,7 @@ expect($result)->toBe($mockPostType); }); -test('exists calls exists on registry', function () { +test('exists calls exists on registry', function (): void { // Define test values $slug = 'test-post-type'; @@ -57,7 +57,7 @@ expect($result)->toBeTrue(); }); -test('getRegistered calls getAll on registry', function () { +test('getRegistered calls getAll on registry', function (): void { // Define test values $registeredPostTypes = ['post' => [], 'page' => [], 'test-post-type' => []]; diff --git a/tests/Unit/Route/Domain/Models/RouteTest.php b/tests/Unit/Route/Domain/Models/RouteTest.php index a3750184..71de0ce1 100644 --- a/tests/Unit/Route/Domain/Models/RouteTest.php +++ b/tests/Unit/Route/Domain/Models/RouteTest.php @@ -2,58 +2,42 @@ declare(strict_types=1); -namespace Tests\Unit\Route\Domain\Models; - use Illuminate\Http\Request; -use PHPUnit\Framework\TestCase; use Pollora\Route\Domain\Models\Route; -class RouteTest extends TestCase -{ - private Route $route; - - protected function setUp(): void - { - parent::setUp(); +describe('Route', function (): void { + beforeEach(function (): void { + $this->route = new Route(['GET'], '/test', fn (): string => 'test'); + }); - $this->route = new Route(['GET'], '/test', function () { - return 'test'; - }); - } - - public function test_it_can_set_and_check_wordpress_route_status(): void - { - $this->assertFalse($this->route->isWordPressRoute()); + it('can set and check WordPress route status', function (): void { + expect($this->route->isWordPressRoute())->toBeFalse(); $this->route->setIsWordPressRoute(true); - $this->assertTrue($this->route->isWordPressRoute()); - } + expect($this->route->isWordPressRoute())->toBeTrue(); + }); - public function test_it_can_set_and_get_condition(): void - { - $this->assertFalse($this->route->hasCondition()); - $this->assertEmpty($this->route->getCondition()); + it('can set and get condition', function (): void { + expect($this->route->hasCondition())->toBeFalse(); + expect($this->route->getCondition())->toBeEmpty(); $this->route->setCondition('is_single'); - $this->assertTrue($this->route->hasCondition()); - $this->assertEquals('is_single', $this->route->getCondition()); - } + expect($this->route->hasCondition())->toBeTrue(); + expect($this->route->getCondition())->toBe('is_single'); + }); - public function test_it_can_set_and_get_condition_parameters(): void - { - $this->assertEmpty($this->route->getConditionParameters()); + it('can set and get condition parameters', function (): void { + expect($this->route->getConditionParameters())->toBeEmpty(); $parameters = ['product', 123]; $this->route->setConditionParameters($parameters); - $this->assertEquals($parameters, $this->route->getConditionParameters()); - } + expect($this->route->getConditionParameters())->toBe($parameters); + }); - public function test_it_matches_wordpress_condition_when_function_exists(): void - { - // Mock a WordPress function + it('matches WordPress condition when function exists', function (): void { if (! function_exists('test_wp_function')) { eval('function test_wp_function($param = null) { return $param === "test"; }'); } @@ -64,12 +48,10 @@ public function test_it_matches_wordpress_condition_when_function_exists(): void $request = Request::create('/test'); - $this->assertTrue($this->route->matches($request)); - } + expect($this->route->matches($request))->toBeTrue(); + }); - public function test_it_does_not_match_when_wordpress_function_returns_false(): void - { - // Mock a WordPress function that returns false + it('does not match when WordPress function returns false', function (): void { if (! function_exists('test_wp_function_false')) { eval('function test_wp_function_false() { return false; }'); } @@ -79,39 +61,33 @@ public function test_it_does_not_match_when_wordpress_function_returns_false(): $request = Request::create('/test'); - $this->assertFalse($this->route->matches($request)); - } + expect($this->route->matches($request))->toBeFalse(); + }); - public function test_it_falls_back_to_laravel_matching_for_non_wordpress_routes(): void - { + it('falls back to Laravel matching for non-WordPress routes', function (): void { $request = Request::create('/test', 'GET'); + expect($this->route->matches($request))->toBeTrue(); - // This should use Laravel's default matching behavior - $this->assertTrue($this->route->matches($request)); - - // Different URI should not match $wrongRequest = Request::create('/different', 'GET'); - $this->assertFalse($this->route->matches($wrongRequest)); - } + expect($this->route->matches($wrongRequest))->toBeFalse(); + }); - public function test_it_returns_false_when_wordpress_function_does_not_exist(): void - { + it('returns false when WordPress function does not exist', function (): void { $this->route->setIsWordPressRoute(true); $this->route->setCondition('non_existent_function'); $request = Request::create('/test'); - $this->assertFalse($this->route->matches($request)); - } + expect($this->route->matches($request))->toBeFalse(); + }); - public function test_chaining_methods_return_route_instance(): void - { + it('chaining methods return route instance', function (): void { $result = $this->route ->setIsWordPressRoute(true) ->setCondition('is_single') ->setConditionParameters(['test']); - $this->assertInstanceOf(Route::class, $result); - $this->assertSame($this->route, $result); - } -} + expect($result)->toBeInstanceOf(Route::class); + expect($result)->toBe($this->route); + }); +}); diff --git a/tests/Unit/Route/Infrastructure/Middleware/WordPressBindingsTest.php b/tests/Unit/Route/Infrastructure/Middleware/WordPressBindingsTest.php index 08661857..6d573d96 100644 --- a/tests/Unit/Route/Infrastructure/Middleware/WordPressBindingsTest.php +++ b/tests/Unit/Route/Infrastructure/Middleware/WordPressBindingsTest.php @@ -2,103 +2,66 @@ declare(strict_types=1); -namespace Tests\Unit\Route\Infrastructure\Middleware; - use Illuminate\Http\Request; -use PHPUnit\Framework\TestCase; use Pollora\Route\Domain\Models\Route; use Pollora\Route\Infrastructure\Middleware\WordPressBindings; use Pollora\Route\Infrastructure\Services\ExtendedRouter; -class WordPressBindingsTest extends TestCase -{ - private WordPressBindings $middleware; - - private ExtendedRouter $router; - - protected function setUp(): void - { - parent::setUp(); - - $this->router = $this->createMock(ExtendedRouter::class); +describe('WordPressBindings middleware', function (): void { + beforeEach(function (): void { + $this->router = Mockery::mock(ExtendedRouter::class); $this->middleware = new WordPressBindings($this->router); - } + }); - public function test_it_adds_bindings_for_wordpress_routes(): void - { - $route = $this->createMock(Route::class); - $route->method('hasCondition')->willReturn(true); + it('adds bindings for WordPress routes', function (): void { + $route = Mockery::mock(Route::class); + $route->shouldReceive('hasCondition')->andReturn(true); $request = Request::create('/test'); $request->setRouteResolver(fn () => $route); - $this->router->expects($this->once()) - ->method('addWordPressBindings') - ->with($route) - ->willReturn($route); - - $next = function ($req) { - return 'response'; - }; + $this->router->shouldReceive('addWordPressBindings')->once()->with($route)->andReturn($route); - $result = $this->middleware->handle($request, $next); + $result = $this->middleware->handle($request, fn ($req): string => 'response'); - $this->assertEquals('response', $result); - } + expect($result)->toBe('response'); + }); - public function test_it_skips_bindings_for_non_wordpress_routes(): void - { - $route = $this->createMock(Route::class); - $route->method('hasCondition')->willReturn(false); + it('skips bindings for non-WordPress routes', function (): void { + $route = Mockery::mock(Route::class); + $route->shouldReceive('hasCondition')->andReturn(false); $request = Request::create('/test'); $request->setRouteResolver(fn () => $route); - $this->router->expects($this->never()) - ->method('addWordPressBindings'); + $this->router->shouldNotReceive('addWordPressBindings'); - $next = function ($req) { - return 'response'; - }; + $result = $this->middleware->handle($request, fn ($req): string => 'response'); - $result = $this->middleware->handle($request, $next); + expect($result)->toBe('response'); + }); - $this->assertEquals('response', $result); - } - - public function test_it_handles_request_without_route(): void - { + it('handles request without route', function (): void { $request = Request::create('/test'); - $request->setRouteResolver(fn () => null); - - $this->router->expects($this->never()) - ->method('addWordPressBindings'); + $request->setRouteResolver(fn (): null => null); - $next = function ($req) { - return 'response'; - }; + $this->router->shouldNotReceive('addWordPressBindings'); - $result = $this->middleware->handle($request, $next); + $result = $this->middleware->handle($request, fn ($req): string => 'response'); - $this->assertEquals('response', $result); - } + expect($result)->toBe('response'); + }); - public function test_it_handles_route_without_condition_method(): void - { - $route = new \stdClass; // Route without hasCondition method + it('handles route without condition method', function (): void { + $route = new stdClass; $request = Request::create('/test'); - $request->setRouteResolver(fn () => $route); - - $this->router->expects($this->never()) - ->method('addWordPressBindings'); + $request->setRouteResolver(fn (): stdClass => $route); - $next = function ($req) { - return 'response'; - }; + $this->router->shouldNotReceive('addWordPressBindings'); - $result = $this->middleware->handle($request, $next); + $result = $this->middleware->handle($request, fn ($req): string => 'response'); - $this->assertEquals('response', $result); - } -} + expect($result)->toBe('response'); + }); +}); diff --git a/tests/Unit/Route/Infrastructure/Services/ExtendedRouterDependencyInjectionTest.php b/tests/Unit/Route/Infrastructure/Services/ExtendedRouterDependencyInjectionTest.php index 84132132..a8f5fb51 100644 --- a/tests/Unit/Route/Infrastructure/Services/ExtendedRouterDependencyInjectionTest.php +++ b/tests/Unit/Route/Infrastructure/Services/ExtendedRouterDependencyInjectionTest.php @@ -2,31 +2,17 @@ declare(strict_types=1); -namespace Tests\Unit\Route\Infrastructure\Services; - use Illuminate\Container\Container; use Illuminate\Events\Dispatcher; -use PHPUnit\Framework\Attributes\CoversClass; use Pollora\Route\Infrastructure\Services\ExtendedRouter; use Pollora\Route\Infrastructure\Services\Resolvers\WordPressTypeResolver; use Pollora\Route\Infrastructure\Services\WordPressConditionManager; -use Tests\TestCase; - -#[CoversClass(ExtendedRouter::class)] -class ExtendedRouterDependencyInjectionTest extends TestCase -{ - private Container $container; - - private ExtendedRouter $router; - - protected function setUp(): void - { - parent::setUp(); +describe('ExtendedRouter dependency injection', function (): void { + beforeEach(function (): void { $this->container = new Container; $dispatcher = new Dispatcher($this->container); - // Register dependencies $conditionManager = new WordPressConditionManager($this->container); $typeResolver = new WordPressTypeResolver; @@ -34,32 +20,26 @@ protected function setUp(): void $dispatcher, $this->container, $conditionManager, - $typeResolver, - null // no logger for tests + $typeResolver ); - } + }); - public function test_wordpress_types_are_registered_in_container(): void - { + it('registers WordPress types in the container', function (): void { $expectedTypes = ['WP_Post', 'WP_Term', 'WP_User', 'WP_Query', 'WP']; foreach ($expectedTypes as $type) { - $this->assertTrue( - $this->container->bound($type), - "WordPress type {$type} should be bound in the container" - ); + expect($this->container->bound($type))->toBeTrue(sprintf('WordPress type %s should be bound in the container', $type)); } - } + }); - public function test_router_can_resolve_conditions(): void - { + it('can resolve conditions', function (): void { $conditions = $this->router->getConditions(); - $this->assertIsArray($conditions); - $this->assertArrayHasKey('home', $conditions); - $this->assertEquals('is_home', $conditions['home']); + expect($conditions)->toBeArray(); + expect($conditions)->toHaveKey('home'); + expect($conditions['home'])->toBe('is_home'); - $this->assertEquals('is_single', $this->router->resolveCondition('single')); - $this->assertEquals('unknown', $this->router->resolveCondition('unknown')); - } -} + expect($this->router->resolveCondition('single'))->toBe('is_single'); + expect($this->router->resolveCondition('unknown'))->toBe('unknown'); + }); +}); diff --git a/tests/Unit/Route/Infrastructure/Services/ExtendedRouterTest.php b/tests/Unit/Route/Infrastructure/Services/ExtendedRouterTest.php index eaf8db54..acba5ff6 100644 --- a/tests/Unit/Route/Infrastructure/Services/ExtendedRouterTest.php +++ b/tests/Unit/Route/Infrastructure/Services/ExtendedRouterTest.php @@ -2,123 +2,84 @@ declare(strict_types=1); -namespace Tests\Unit\Route\Infrastructure\Services; - use Illuminate\Config\Repository; use Illuminate\Container\Container; use Illuminate\Contracts\Events\Dispatcher; -use PHPUnit\Framework\TestCase; use Pollora\Route\Domain\Models\Route; use Pollora\Route\Infrastructure\Services\ExtendedRouter; use Pollora\Route\Infrastructure\Services\Resolvers\WordPressTypeResolver; use Pollora\Route\Infrastructure\Services\WordPressConditionManager; -class ExtendedRouterTest extends TestCase -{ - private ExtendedRouter $router; - - private Container $container; - - protected function setUp(): void - { - parent::setUp(); - +describe('ExtendedRouter', function (): void { + beforeEach(function (): void { $this->container = new Container; - $dispatcher = $this->createMock(Dispatcher::class); - - // Mock config - $config = $this->createMock(Repository::class); - $config->method('get') - ->willReturnCallback(function ($key, $default = null) { - if ($key === 'wordpress.conditions') { - return [ - 'is_single' => 'single', - 'is_page' => 'page', - 'is_category' => 'category', - ]; - } - if ($key === 'wordpress.plugin_conditions') { - return []; - } - - return $default; - }); + $dispatcher = Mockery::mock(Dispatcher::class); + $dispatcher->shouldReceive('dispatch')->andReturn(null); + + $config = Mockery::mock(Repository::class); + $config->shouldReceive('get')->andReturnUsing(function ($key, $default = null) { + if ($key === 'wordpress.conditions') { + return [ + 'is_single' => 'single', + 'is_page' => 'page', + 'is_category' => 'category', + ]; + } + + if ($key === 'wordpress.plugin_conditions') { + return []; + } + + return $default; + }); $this->container->instance('config', $config); - // Create dependencies with the mocked config $conditionManager = new WordPressConditionManager($this->container); $typeResolver = new WordPressTypeResolver; - $this->router = new ExtendedRouter( - $dispatcher, - $this->container, - $conditionManager, - $typeResolver, - null // no logger for tests - ); - } - - public function test_it_creates_route_objects_of_correct_type(): void - { - $route = $this->router->get('/test', function () { - return 'test'; - }); + $this->router = new ExtendedRouter($dispatcher, $this->container, $conditionManager, $typeResolver); + }); - $this->assertInstanceOf(Route::class, $route); - } + it('creates route objects of correct type', function (): void { + $route = $this->router->get('/test', fn (): string => 'test'); - public function test_it_loads_wordpress_conditions_from_config(): void - { - $conditions = $this->router->getConditions(); - - $this->assertArrayHasKey('single', $conditions); - $this->assertEquals('is_single', $conditions['single']); - $this->assertArrayHasKey('page', $conditions); - $this->assertEquals('is_page', $conditions['page']); - $this->assertArrayHasKey('category', $conditions); - $this->assertEquals('is_category', $conditions['category']); - } + expect($route)->toBeInstanceOf(Route::class); + }); - public function test_it_resolves_condition_aliases(): void - { - $this->assertEquals('is_single', $this->router->resolveCondition('single')); - $this->assertEquals('is_page', $this->router->resolveCondition('page')); + it('loads WordPress conditions from config', function (): void { + $conditions = $this->router->getConditions(); - // Non-aliased conditions should return as-is - $this->assertEquals('is_custom', $this->router->resolveCondition('is_custom')); - } + expect($conditions)->toHaveKey('single'); + expect($conditions['single'])->toBe('is_single'); + expect($conditions)->toHaveKey('page'); + expect($conditions['page'])->toBe('is_page'); + expect($conditions)->toHaveKey('category'); + expect($conditions['category'])->toBe('is_category'); + }); - public function test_it_adds_wordpress_bindings_to_route(): void - { - // Test that the addWordPressBindings method runs without error - $closure = function (\WP_Post $post, \WP_Query $wp_query) { - return [$post, $wp_query]; - }; + it('resolves condition aliases', function (): void { + expect($this->router->resolveCondition('single'))->toBe('is_single'); + expect($this->router->resolveCondition('page'))->toBe('is_page'); + expect($this->router->resolveCondition('is_custom'))->toBe('is_custom'); + }); - $route = new Route(['GET'], '/test', $closure); + it('adds WordPress bindings to route', function (): void { + $route = new Route(['GET'], '/test', fn (WP_Post $post, WP_Query $wp_query): array => [$post, $wp_query]); $result = $this->router->addWordPressBindings($route); + expect($result)->toBe($route); - // The method should return the same route instance - $this->assertSame($route, $result); + $nonWpRoute = new Route(['GET'], '/other', fn (string $name, int $id): array => [$name, $id]); - // Test with non-WordPress types (should not cause errors) - $nonWpClosure = function (string $name, int $id) { - return [$name, $id]; - }; - - $nonWpRoute = new Route(['GET'], '/other', $nonWpClosure); $nonWpResult = $this->router->addWordPressBindings($nonWpRoute); + expect($nonWpResult)->toBe($nonWpRoute); + }); - $this->assertSame($nonWpRoute, $nonWpResult); - } - - public function test_it_handles_missing_config_gracefully(): void - { - // Create router without config + it('handles missing config gracefully', function (): void { $container = new Container; - $dispatcher = $this->createMock(Dispatcher::class); + $dispatcher = Mockery::mock(Dispatcher::class); + $dispatcher->shouldReceive('dispatch')->andReturn(null); $conditionManager = new WordPressConditionManager($container); $typeResolver = new WordPressTypeResolver; @@ -126,9 +87,8 @@ public function test_it_handles_missing_config_gracefully(): void $router = new ExtendedRouter($dispatcher, $container, $conditionManager, $typeResolver); $conditions = $router->getConditions(); - $this->assertIsArray($conditions); - // Should have default conditions even without config - $this->assertArrayHasKey('home', $conditions); - $this->assertEquals('is_home', $conditions['home']); - } -} + expect($conditions)->toBeArray(); + expect($conditions)->toHaveKey('home'); + expect($conditions['home'])->toBe('is_home'); + }); +}); diff --git a/tests/Unit/Route/Infrastructure/Services/LazyConfigLoadingTest.php b/tests/Unit/Route/Infrastructure/Services/LazyConfigLoadingTest.php index 9c55b8fc..b29f0a5f 100644 --- a/tests/Unit/Route/Infrastructure/Services/LazyConfigLoadingTest.php +++ b/tests/Unit/Route/Infrastructure/Services/LazyConfigLoadingTest.php @@ -2,169 +2,140 @@ declare(strict_types=1); -namespace Tests\Unit\Route\Infrastructure\Services; - use Illuminate\Config\Repository; use Illuminate\Container\Container; use Illuminate\Contracts\Events\Dispatcher; -use PHPUnit\Framework\TestCase; use Pollora\Route\Infrastructure\Services\ExtendedRouter; -/** - * Tests for lazy configuration loading in ExtendedRouter. - * - * This test verifies that the router can handle situations where - * the configuration is not available during construction but becomes - * available later during the application lifecycle. - */ -class LazyConfigLoadingTest extends TestCase -{ - public function test_router_works_without_config_during_construction(): void - { - // Create a container without config service bound +describe('LazyConfigLoading', function (): void { + it('router works without config during construction', function (): void { $container = new Container; - $dispatcher = $this->createMock(Dispatcher::class); + $dispatcher = Mockery::mock(Dispatcher::class); + $dispatcher->shouldReceive('dispatch')->andReturn(null); - // This should not throw an exception even though config is not available $router = new ExtendedRouter($dispatcher, $container); - // Router should still have default conditions $conditions = $router->getConditions(); - $this->assertArrayHasKey('single', $conditions); - $this->assertEquals('is_single', $conditions['single']); - } + expect($conditions)->toHaveKey('single'); + expect($conditions['single'])->toBe('is_single'); + }); - public function test_router_loads_config_when_available_later(): void - { - // Create a container without config initially + it('router loads config when available later', function (): void { $container = new Container; - $dispatcher = $this->createMock(Dispatcher::class); + $dispatcher = Mockery::mock(Dispatcher::class); + $dispatcher->shouldReceive('dispatch')->andReturn(null); $router = new ExtendedRouter($dispatcher, $container); - // Now bind the config service (simulating Laravel config being loaded later) - $config = $this->createMock(Repository::class); - $config->method('get') - ->willReturnCallback(function ($key, $default = null) { - if ($key === 'wordpress.conditions') { - return [ - 'is_custom_condition' => 'custom', - 'is_special_condition' => 'special', - ]; - } - if ($key === 'wordpress.plugin_conditions') { - return []; - } - - return $default; - }); + $config = Mockery::mock(Repository::class); + $config->shouldReceive('get')->andReturnUsing(function ($key, $default = null) { + if ($key === 'wordpress.conditions') { + return [ + 'is_custom_condition' => 'custom', + 'is_special_condition' => 'special', + ]; + } + + if ($key === 'wordpress.plugin_conditions') { + return []; + } + + return $default; + }); $container->instance('config', $config); - // Now when we call resolveCondition, it should load the config $result = $router->resolveCondition('custom'); - $this->assertEquals('is_custom_condition', $result); + expect($result)->toBe('is_custom_condition'); - // And it should have merged with defaults $conditions = $router->getConditions(); - $this->assertArrayHasKey('single', $conditions); // Default condition - $this->assertArrayHasKey('custom', $conditions); // Config condition - $this->assertEquals('is_single', $conditions['single']); - $this->assertEquals('is_custom_condition', $conditions['custom']); - } - - public function test_config_is_only_loaded_once(): void - { + expect($conditions)->toHaveKey('single'); + expect($conditions)->toHaveKey('custom'); + expect($conditions['single'])->toBe('is_single'); + expect($conditions['custom'])->toBe('is_custom_condition'); + }); + + it('config is only loaded once', function (): void { $container = new Container; - $dispatcher = $this->createMock(Dispatcher::class); + $dispatcher = Mockery::mock(Dispatcher::class); + $dispatcher->shouldReceive('dispatch')->andReturn(null); $router = new ExtendedRouter($dispatcher, $container); - // Mock config - expects to be called exactly twice (once for conditions, once for plugin_conditions) - $config = $this->createMock(Repository::class); - $config->expects($this->exactly(2)) - ->method('get') - ->willReturnCallback(function ($key, $default = null) { - if ($key === 'wordpress.conditions') { - return ['is_test' => 'test']; - } - if ($key === 'wordpress.plugin_conditions') { - return []; - } - - return $default; - }); + $config = Mockery::mock(Repository::class); + $config->shouldReceive('get')->twice()->andReturnUsing(function ($key, $default = null) { + if ($key === 'wordpress.conditions') { + return ['is_test' => 'test']; + } + + if ($key === 'wordpress.plugin_conditions') { + return []; + } + + return $default; + }); $container->instance('config', $config); - // Call multiple times - config should only be loaded once $router->resolveCondition('test'); $router->resolveCondition('test'); $router->getConditions(); $router->resolveCondition('another'); - } + }); - public function test_router_handles_config_exceptions_gracefully(): void - { + it('handles config exceptions gracefully', function (): void { $container = new Container; - $dispatcher = $this->createMock(Dispatcher::class); + $dispatcher = Mockery::mock(Dispatcher::class); + $dispatcher->shouldReceive('dispatch')->andReturn(null); $router = new ExtendedRouter($dispatcher, $container); - // Mock config that throws an exception - $config = $this->createMock(Repository::class); - $config->method('get') - ->willThrowException(new \Exception('Config not ready')); + $config = Mockery::mock(Repository::class); + $config->shouldReceive('get')->andThrow(new Exception('Config not ready')); $container->instance('config', $config); - // Router should handle the exception gracefully and still work $result = $router->resolveCondition('single'); - $this->assertEquals('is_single', $result); + expect($result)->toBe('is_single'); $conditions = $router->getConditions(); - $this->assertArrayHasKey('single', $conditions); - } + expect($conditions)->toHaveKey('single'); + }); - public function test_config_merges_with_defaults_correctly(): void - { + it('config merges with defaults correctly', function (): void { $container = new Container; - $dispatcher = $this->createMock(Dispatcher::class); + $dispatcher = Mockery::mock(Dispatcher::class); + $dispatcher->shouldReceive('dispatch')->andReturn(null); $router = new ExtendedRouter($dispatcher, $container); - // Mock config with limited conditions to test merge behavior - $config = $this->createMock(Repository::class); - $config->method('get') - ->willReturnCallback(function ($key, $default = null) { - if ($key === 'wordpress.conditions') { - return [ - 'is_front_page' => 'front', - 'is_custom_condition' => 'custom', - ]; - } - if ($key === 'wordpress.plugin_conditions') { - return []; - } - - return $default; - }); + $config = Mockery::mock(Repository::class); + $config->shouldReceive('get')->andReturnUsing(function ($key, $default = null) { + if ($key === 'wordpress.conditions') { + return [ + 'is_front_page' => 'front', + 'is_custom_condition' => 'custom', + ]; + } - $container->instance('config', $config); + if ($key === 'wordpress.plugin_conditions') { + return []; + } - // Test that config conditions work - $this->assertEquals('is_front_page', $router->resolveCondition('front')); - $this->assertEquals('is_custom_condition', $router->resolveCondition('custom')); + return $default; + }); + + $container->instance('config', $config); - // Test that default conditions are preserved - $this->assertEquals('is_single', $router->resolveCondition('single')); - $this->assertEquals('is_date', $router->resolveCondition('date')); + expect($router->resolveCondition('front'))->toBe('is_front_page'); + expect($router->resolveCondition('custom'))->toBe('is_custom_condition'); + expect($router->resolveCondition('single'))->toBe('is_single'); + expect($router->resolveCondition('date'))->toBe('is_date'); - // Test that all conditions are available $conditions = $router->getConditions(); - $this->assertArrayHasKey('front', $conditions); // From config - $this->assertArrayHasKey('custom', $conditions); // From config - $this->assertArrayHasKey('single', $conditions); // From defaults - $this->assertArrayHasKey('date', $conditions); // From defaults - } -} + expect($conditions)->toHaveKey('front'); + expect($conditions)->toHaveKey('custom'); + expect($conditions)->toHaveKey('single'); + expect($conditions)->toHaveKey('date'); + }); +}); diff --git a/tests/Unit/Route/Infrastructure/Services/WordPressConditionManagerTest.php b/tests/Unit/Route/Infrastructure/Services/WordPressConditionManagerTest.php index 42ba124e..9c0c8040 100644 --- a/tests/Unit/Route/Infrastructure/Services/WordPressConditionManagerTest.php +++ b/tests/Unit/Route/Infrastructure/Services/WordPressConditionManagerTest.php @@ -2,57 +2,43 @@ declare(strict_types=1); -namespace Tests\Unit\Route\Infrastructure\Services; - use Illuminate\Container\Container; -use PHPUnit\Framework\Attributes\CoversClass; use Pollora\Route\Infrastructure\Services\WordPressConditionManager; -use Tests\TestCase; - -#[CoversClass(WordPressConditionManager::class)] -class WordPressConditionManagerTest extends TestCase -{ - private WordPressConditionManager $manager; - protected function setUp(): void - { - parent::setUp(); +describe('WordPressConditionManager', function (): void { + beforeEach(function (): void { $this->manager = new WordPressConditionManager(new Container); - } + }); - public function test_loads_default_conditions(): void - { + it('loads default conditions', function (): void { $conditions = $this->manager->getConditions(); - $this->assertIsArray($conditions); - $this->assertArrayHasKey('home', $conditions); - $this->assertEquals('is_home', $conditions['home']); - $this->assertArrayHasKey('single', $conditions); - $this->assertEquals('is_single', $conditions['single']); - $this->assertArrayHasKey('archive', $conditions); - $this->assertEquals('is_archive', $conditions['archive']); - } - - public function test_can_resolve_known_conditions(): void - { - $this->assertEquals('is_home', $this->manager->resolveCondition('home')); - $this->assertEquals('is_single', $this->manager->resolveCondition('single')); - $this->assertEquals('is_archive', $this->manager->resolveCondition('archive')); - } - - public function test_returns_original_condition_for_unknown_aliases(): void - { - $this->assertEquals('unknown_condition', $this->manager->resolveCondition('unknown_condition')); - } - - public function test_can_add_custom_conditions(): void - { + expect($conditions)->toBeArray(); + expect($conditions)->toHaveKey('home'); + expect($conditions['home'])->toBe('is_home'); + expect($conditions)->toHaveKey('single'); + expect($conditions['single'])->toBe('is_single'); + expect($conditions)->toHaveKey('archive'); + expect($conditions['archive'])->toBe('is_archive'); + }); + + it('can resolve known conditions', function (): void { + expect($this->manager->resolveCondition('home'))->toBe('is_home'); + expect($this->manager->resolveCondition('single'))->toBe('is_single'); + expect($this->manager->resolveCondition('archive'))->toBe('is_archive'); + }); + + it('returns original condition for unknown aliases', function (): void { + expect($this->manager->resolveCondition('unknown_condition'))->toBe('unknown_condition'); + }); + + it('can add custom conditions', function (): void { $this->manager->addCondition('custom', 'is_custom'); - $this->assertEquals('is_custom', $this->manager->resolveCondition('custom')); + expect($this->manager->resolveCondition('custom'))->toBe('is_custom'); $conditions = $this->manager->getConditions(); - $this->assertArrayHasKey('custom', $conditions); - $this->assertEquals('is_custom', $conditions['custom']); - } -} + expect($conditions)->toHaveKey('custom'); + expect($conditions['custom'])->toBe('is_custom'); + }); +}); diff --git a/tests/Unit/Route/Infrastructure/Services/WordPressRouteResolutionTest.php b/tests/Unit/Route/Infrastructure/Services/WordPressRouteResolutionTest.php index 2e51692d..45f96fb0 100644 --- a/tests/Unit/Route/Infrastructure/Services/WordPressRouteResolutionTest.php +++ b/tests/Unit/Route/Infrastructure/Services/WordPressRouteResolutionTest.php @@ -2,297 +2,190 @@ declare(strict_types=1); -namespace Tests\Unit\Route\Infrastructure\Services; - use Illuminate\Config\Repository; use Illuminate\Container\Container; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Http\Request; -use PHPUnit\Framework\TestCase; use Pollora\Route\Domain\Models\Route; use Pollora\Route\Infrastructure\Services\ExtendedRouter; -/** - * Tests for WordPress route resolution based on conditions. - * - * This test suite replicates the route definitions from web.php - * and verifies that each WordPress condition correctly matches - * the appropriate route using the existing WordPress mock system. - */ -class WordPressRouteResolutionTest extends TestCase -{ - private ExtendedRouter $router; - - private Container $container; - - protected function setUp(): void - { - parent::setUp(); +require_once dirname(__DIR__, 3).'/helpers.php'; - // Initialize WordPress mocks system +describe('WordPressRouteResolution', function (): void { + beforeEach(function (): void { setupWordPressMocks(); $this->container = new Container; - $dispatcher = $this->createMock(Dispatcher::class); - - // Mock config for WordPress routing conditions - using the new format - $config = $this->createMock(Repository::class); - $config->method('get') - ->willReturnCallback(function ($key, $default = null) { - if ($key === 'wordpress.conditions') { - return [ - 'is_front_page' => 'front', - 'is_home' => 'home', - 'is_page' => 'page', - 'is_single' => 'single', - 'is_author' => 'author', - 'is_category' => 'archive', - 'is_page_template' => 'template', - 'is_404' => ['404', 'not_found'], - ]; - } - if ($key === 'wordpress.plugin_conditions') { - return []; - } - - return $default; - }); + $dispatcher = Mockery::mock(Dispatcher::class); + $dispatcher->shouldReceive('dispatch')->andReturn(null); + + $config = Mockery::mock(Repository::class); + $config->shouldReceive('get')->andReturnUsing(function ($key, $default = null) { + if ($key === 'wordpress.conditions') { + return [ + 'is_front_page' => 'front', + 'is_home' => 'home', + 'is_page' => 'page', + 'is_single' => 'single', + 'is_author' => 'author', + 'is_category' => 'archive', + 'is_page_template' => 'template', + 'is_404' => ['404', 'not_found'], + ]; + } + + if ($key === 'wordpress.plugin_conditions') { + return []; + } + + return $default; + }); $this->container->instance('config', $config); $this->router = new ExtendedRouter($dispatcher, $this->container); - } + }); - protected function tearDown(): void - { - // Reset WordPress mocks after each test + afterEach(function (): void { resetWordPressMocks(); - parent::tearDown(); - } - - /** - * Helper to create a WordPress route with mocked functions - */ - private function createWordPressRoute(string $condition, array $parameters = [], ?callable $action = null): Route - { - $action = $action ?: function () { - return 'matched'; - }; - $resolvedCondition = $this->router->resolveCondition($condition); - - $route = new Route(['GET'], $condition, $action); - $route->setIsWordPressRoute(true); - $route->setCondition($resolvedCondition); - $route->setConditionParameters($parameters); - - return $route; - } - - public function test_route_condition_aliases_are_resolved_correctly(): void - { - // Test that aliases are correctly resolved - replicating the exact conditions from web.php - $this->assertEquals('is_front_page', $this->router->resolveCondition('front')); - $this->assertEquals('is_home', $this->router->resolveCondition('home')); - $this->assertEquals('is_page', $this->router->resolveCondition('page')); - $this->assertEquals('is_single', $this->router->resolveCondition('single')); - $this->assertEquals('is_author', $this->router->resolveCondition('author')); - $this->assertEquals('is_category', $this->router->resolveCondition('archive')); - $this->assertEquals('is_page_template', $this->router->resolveCondition('template')); - $this->assertEquals('is_404', $this->router->resolveCondition('404')); - - // Test that direct WordPress function names pass through unchanged - $this->assertEquals('is_singular', $this->router->resolveCondition('is_singular')); - } - - public function test_wordpress_route_is_marked_correctly(): void - { - // Define a WordPress route - $wpRoute = $this->createWordPressRoute('front'); - - // Define a regular Laravel route for comparison - $laravelRoute = new Route(['GET'], '/test', function () { - return 'test'; - }); - - // Test that the WordPress route is correctly marked - $this->assertTrue($wpRoute->isWordPressRoute()); - $this->assertFalse($laravelRoute->isWordPressRoute()); - } - - public function test_wordpress_route_has_correct_condition(): void - { - $route = $this->createWordPressRoute('front'); - - $this->assertEquals('is_front_page', $route->getCondition()); - $this->assertTrue($route->hasCondition()); - } - - public function test_wordpress_route_with_parameters(): void - { - $route = $this->createWordPressRoute('is_singular', ['realisations']); - - $this->assertEquals('is_singular', $route->getCondition()); - $this->assertEquals(['realisations'], $route->getConditionParameters()); - } - - public function test_route_methods_and_properties(): void - { - $route = $this->createWordPressRoute('front'); - - // Test basic route properties (Laravel automatically adds HEAD for GET routes) - $this->assertEquals(['GET', 'HEAD'], $route->methods()); - $this->assertEquals('front', $route->uri()); - $this->assertTrue($route->isWordPressRoute()); - $this->assertTrue($route->hasCondition()); - $this->assertEquals('is_front_page', $route->getCondition()); - $this->assertEquals([], $route->getConditionParameters()); - } - - public function test_all_web_routes_have_correct_conditions(): void - { - // Test all route definitions from web.php + }); + + it('resolves condition aliases correctly', function (): void { + expect($this->router->resolveCondition('front'))->toBe('is_front_page'); + expect($this->router->resolveCondition('home'))->toBe('is_home'); + expect($this->router->resolveCondition('page'))->toBe('is_page'); + expect($this->router->resolveCondition('single'))->toBe('is_single'); + expect($this->router->resolveCondition('author'))->toBe('is_author'); + expect($this->router->resolveCondition('archive'))->toBe('is_category'); + expect($this->router->resolveCondition('template'))->toBe('is_page_template'); + expect($this->router->resolveCondition('404'))->toBe('is_404'); + expect($this->router->resolveCondition('is_singular'))->toBe('is_singular'); + }); + + it('marks WordPress route correctly', function (): void { + $wpRoute = createWpRoute($this->router, 'front'); + $laravelRoute = new Route(['GET'], '/test', fn (): string => 'test'); + + expect($wpRoute->isWordPressRoute())->toBeTrue(); + expect($laravelRoute->isWordPressRoute())->toBeFalse(); + }); + + it('has correct condition on WordPress route', function (): void { + $route = createWpRoute($this->router, 'front'); + + expect($route->getCondition())->toBe('is_front_page'); + expect($route->hasCondition())->toBeTrue(); + }); + + it('supports route with parameters', function (): void { + $route = createWpRoute($this->router, 'is_singular', ['realisations']); + + expect($route->getCondition())->toBe('is_singular'); + expect($route->getConditionParameters())->toBe(['realisations']); + }); + + it('has correct methods and properties', function (): void { + $route = createWpRoute($this->router, 'front'); + + expect($route->methods())->toBe(['GET', 'HEAD']); + expect($route->uri())->toBe('front'); + expect($route->isWordPressRoute())->toBeTrue(); + expect($route->hasCondition())->toBeTrue(); + expect($route->getCondition())->toBe('is_front_page'); + expect($route->getConditionParameters())->toBe([]); + }); + + it('resolves all web route conditions correctly', function (): void { $webRoutes = [ - 'front' => 'is_front_page', // Route::wp('front', ...) - 'is_singular' => 'is_singular', // Route::wp('is_singular', 'realisations', ...) - 'home' => 'is_home', // Route::wp('home', ...) - 'template' => 'is_page_template', // Route::wp('template', ...) - 'single' => 'is_single', // Route::wp('single', ...) - 'page' => 'is_page', // Route::wp('page', ...) - 'author' => 'is_author', // Route::wp('author', ...) - 'archive' => 'is_category', // Route::wp('archive', ...) - // Skip '404' route as it may not have a proper alias configured + 'front' => 'is_front_page', + 'is_singular' => 'is_singular', + 'home' => 'is_home', + 'template' => 'is_page_template', + 'single' => 'is_single', + 'page' => 'is_page', + 'author' => 'is_author', + 'archive' => 'is_category', ]; foreach ($webRoutes as $alias => $expectedCondition) { - $route = $this->createWordPressRoute($alias); - $this->assertEquals($expectedCondition, $route->getCondition(), - "Route alias '{$alias}' should resolve to '{$expectedCondition}'"); + $route = createWpRoute($this->router, $alias); + expect($route->getCondition())->toBe($expectedCondition, sprintf("Route alias '%s' should resolve to '%s'", $alias, $expectedCondition)); } - } - - public function test_route_with_condition_parameters_from_web_routes(): void - { - // Test the specific route from web.php: Route::wp('is_singular', 'realisations', ...) - $route = $this->createWordPressRoute('is_singular', ['realisations']); - - $this->assertEquals('is_singular', $route->getCondition()); - $this->assertEquals(['realisations'], $route->getConditionParameters()); - $this->assertTrue($route->isWordPressRoute()); - } - - public function test_404_route_condition(): void - { - // Test the 404 route - it now has an alias configured - $resolvedCondition = $this->router->resolveCondition('404'); - - // '404' should resolve to 'is_404' based on our configuration - $this->assertEquals('is_404', $resolvedCondition); - - // Create route with '404' condition - $route = $this->createWordPressRoute('404'); - $this->assertEquals('is_404', $route->getCondition()); - $this->assertTrue($route->isWordPressRoute()); - } - - public function test_route_matches_with_wordpress_condition_mocking(): void - { - // Test specific route matching using the WordPress mocking system - - // Test 1: front page route with is_front_page() = true - setWordPressFunction('is_front_page', fn () => true); - $frontRoute = $this->createWordPressRoute('front'); - $request = Request::create('/', 'GET'); - $this->assertTrue($frontRoute->matches($request)); + }); - // Test 2: front page route with is_front_page() = false - setWordPressFunction('is_front_page', fn () => false); - $this->assertFalse($frontRoute->matches($request)); + it('resolves 404 route condition', function (): void { + expect($this->router->resolveCondition('404'))->toBe('is_404'); - // Test 3: single post route with is_single() = true - setWordPressFunction('is_single', fn () => true); - $singleRoute = $this->createWordPressRoute('single'); - $singleRequest = Request::create('/blog/article', 'GET'); - $this->assertTrue($singleRoute->matches($singleRequest)); + $route = createWpRoute($this->router, '404'); + expect($route->getCondition())->toBe('is_404'); + expect($route->isWordPressRoute())->toBeTrue(); + }); - // Test 4: category route with is_category() = true - setWordPressFunction('is_category', fn () => true); - $categoryRoute = $this->createWordPressRoute('archive'); - $categoryRequest = Request::create('/category/news', 'GET'); - $this->assertTrue($categoryRoute->matches($categoryRequest)); - } - - public function test_route_with_parameters_using_wordpress_mocks(): void - { - // Test route with parameters - like Route::wp('is_singular', 'realisations', ...) - // Note: The existing mock system doesn't fully support parameterized WordPress functions - // so we'll test the route structure and basic condition matching - - $route = $this->createWordPressRoute('is_singular', ['realisations']); + it('matches routes with WordPress condition mocking', function (): void { + setWordPressFunction('is_front_page', fn (): true => true); + $frontRoute = createWpRoute($this->router, 'front'); + $request = Request::create('/', 'GET'); + expect($frontRoute->matches($request))->toBeTrue(); - // Test that the route correctly stores the parameters - $this->assertEquals(['realisations'], $route->getConditionParameters()); - $this->assertEquals('is_singular', $route->getCondition()); - $this->assertTrue($route->isWordPressRoute()); + setWordPressFunction('is_front_page', fn (): false => false); + expect($frontRoute->matches($request))->toBeFalse(); - // Mock is_singular to return true (simulating a match) - setWordPressFunction('is_singular', fn () => true); + setWordPressFunction('is_single', fn (): true => true); + $singleRoute = createWpRoute($this->router, 'single'); + expect($singleRoute->matches(Request::create('/blog/article', 'GET')))->toBeTrue(); - $request = Request::create('/realisations/campus-vert', 'GET'); + setWordPressFunction('is_category', fn (): true => true); + $categoryRoute = createWpRoute($this->router, 'archive'); + expect($categoryRoute->matches(Request::create('/category/news', 'GET')))->toBeTrue(); + }); - // The route should match because our mock returns true - $this->assertTrue($route->matches($request)); + it('matches route with parameters using WordPress mocks', function (): void { + $route = createWpRoute($this->router, 'is_singular', ['realisations']); - // Test with mock returning false - setWordPressFunction('is_singular', fn () => false); - $this->assertFalse($route->matches($request)); - } + setWordPressFunction('is_singular', fn (): true => true); + expect($route->matches(Request::create('/realisations/campus-vert', 'GET')))->toBeTrue(); - public function test_multiple_conditions_simulation(): void - { - // Simulate different WordPress context scenarios from web.php + setWordPressFunction('is_singular', fn (): false => false); + expect($route->matches(Request::create('/realisations/campus-vert', 'GET')))->toBeFalse(); + }); - // Scenario 1: Homepage + it('simulates multiple conditions correctly', function (): void { setWordPressConditions([ - 'is_front_page' => true, - 'is_home' => false, - 'is_page' => false, - 'is_single' => false, - 'is_category' => false, - 'is_404' => false, + 'is_front_page' => true, 'is_home' => false, 'is_page' => false, + 'is_single' => false, 'is_category' => false, 'is_404' => false, ]); - $frontRoute = $this->createWordPressRoute('front'); - $homeRoute = $this->createWordPressRoute('home'); - + $frontRoute = createWpRoute($this->router, 'front'); + $homeRoute = createWpRoute($this->router, 'home'); $request = Request::create('/', 'GET'); - $this->assertTrue($frontRoute->matches($request)); - $this->assertFalse($homeRoute->matches($request)); - // Scenario 2: Blog archive + expect($frontRoute->matches($request))->toBeTrue(); + expect($homeRoute->matches($request))->toBeFalse(); + setWordPressConditions([ - 'is_front_page' => false, - 'is_home' => true, - 'is_page' => false, - 'is_single' => false, - 'is_category' => false, - 'is_404' => false, + 'is_front_page' => false, 'is_home' => true, 'is_page' => false, + 'is_single' => false, 'is_category' => false, 'is_404' => false, ]); - $blogRequest = Request::create('/blog', 'GET'); - $this->assertFalse($frontRoute->matches($blogRequest)); - $this->assertTrue($homeRoute->matches($blogRequest)); + expect($frontRoute->matches(Request::create('/blog', 'GET')))->toBeFalse(); + expect($homeRoute->matches(Request::create('/blog', 'GET')))->toBeTrue(); - // Scenario 3: Category page setWordPressConditions([ - 'is_front_page' => false, - 'is_home' => false, - 'is_page' => false, - 'is_single' => false, - 'is_category' => true, - 'is_404' => false, + 'is_front_page' => false, 'is_home' => false, 'is_page' => false, + 'is_single' => false, 'is_category' => true, 'is_404' => false, ]); - $categoryRoute = $this->createWordPressRoute('archive'); - $categoryRequest = Request::create('/blog/category/actus', 'GET'); - $this->assertTrue($categoryRoute->matches($categoryRequest)); - } + $categoryRoute = createWpRoute($this->router, 'archive'); + expect($categoryRoute->matches(Request::create('/blog/category/actus', 'GET')))->toBeTrue(); + }); +}); + +function createWpRoute(ExtendedRouter $router, string $condition, array $parameters = []): Route +{ + $resolvedCondition = $router->resolveCondition($condition); + $route = new Route(['GET'], $condition, fn (): string => 'matched'); + $route->setIsWordPressRoute(true); + $route->setCondition($resolvedCondition); + $route->setConditionParameters($parameters); + + return $route; } diff --git a/tests/Unit/Route/Infrastructure/Services/WordPressTypeResolverTest.php b/tests/Unit/Route/Infrastructure/Services/WordPressTypeResolverTest.php index 7034b47d..28ffac3a 100644 --- a/tests/Unit/Route/Infrastructure/Services/WordPressTypeResolverTest.php +++ b/tests/Unit/Route/Infrastructure/Services/WordPressTypeResolverTest.php @@ -2,36 +2,21 @@ declare(strict_types=1); -namespace Tests\Unit\Route\Infrastructure\Services; - -use PHPUnit\Framework\Attributes\CoversClass; use Pollora\Route\Infrastructure\Services\Contracts\WordPressTypeResolverInterface; use Pollora\Route\Infrastructure\Services\Resolvers\WordPressTypeResolver; -use Tests\TestCase; - -#[CoversClass(WordPressTypeResolver::class)] -class WordPressTypeResolverTest extends TestCase -{ - private WordPressTypeResolver $resolver; - protected function setUp(): void - { - parent::setUp(); +describe('WordPressTypeResolver', function (): void { + beforeEach(function (): void { $this->resolver = new WordPressTypeResolver; - } + }); - public function test_returns_null_for_unsupported_types(): void - { + it('returns null for unsupported types', function (): void { $result = $this->resolver->resolve('UnsupportedType'); - $this->assertNull($result); - } - public function test_resolver_has_correct_interface(): void - { - // Test that the resolver implements the correct interface - $this->assertInstanceOf( - WordPressTypeResolverInterface::class, - $this->resolver - ); - } -} + expect($result)->toBeNull(); + }); + + it('implements the correct interface', function (): void { + expect($this->resolver)->toBeInstanceOf(WordPressTypeResolverInterface::class); + }); +}); diff --git a/tests/Unit/Route/RouteTest.php b/tests/Unit/Route/RouteTest.php index 353c1271..8ed3ac1f 100644 --- a/tests/Unit/Route/RouteTest.php +++ b/tests/Unit/Route/RouteTest.php @@ -9,7 +9,7 @@ /** * Setup function to create test environment for route tests */ -function setupRouteTest() +function setupRouteTest(): array { // Define simulated WordPress functions mockWordPressFunctionsForRoute(); @@ -30,20 +30,18 @@ function mockWordPressFunctionsForRoute(): void /** * Clean up after each test */ -afterEach(function () { +afterEach(function (): void { m::close(); }); /** * Test that the route is correctly marked as a WordPress route. */ -test('route can be marked as WordPress route', function () { +test('route can be marked as WordPress route', function (): void { setupRouteTest(); // Create a route - $route = new Route(['GET'], 'test', function () { - return 'test'; - }); + $route = new Route(['GET'], 'test', fn (): string => 'test'); // By default, it's not a WordPress route expect($route->isWordPressRoute())->toBeFalse(); @@ -58,13 +56,11 @@ function mockWordPressFunctionsForRoute(): void /** * Test that the WordPress condition is correctly defined and retrieved. */ -test('route can define and retrieve WordPress condition', function () { +test('route can define and retrieve WordPress condition', function (): void { setupRouteTest(); // Create a WordPress route - $route = new Route(['GET'], 'is_page', function () { - return 'page'; - }); + $route = new Route(['GET'], 'is_page', fn (): string => 'page'); $route->setIsWordPressRoute(true); // Define the condition @@ -78,13 +74,11 @@ function mockWordPressFunctionsForRoute(): void /** * Test that condition parameters are correctly defined and retrieved. */ -test('route can define and retrieve condition parameters', function () { +test('route can define and retrieve condition parameters', function (): void { setupRouteTest(); // Create a WordPress route - $route = new Route(['GET'], 'is_page', function () { - return 'page'; - }); + $route = new Route(['GET'], 'is_page', fn (): string => 'page'); $route->setIsWordPressRoute(true); // Define condition parameters @@ -98,13 +92,11 @@ function mockWordPressFunctionsForRoute(): void /** * Test that the matches method works correctly for WordPress routes. */ -test('route matches correctly for WordPress conditions', function () { +test('route matches correctly for WordPress conditions', function (): void { setupRouteTest(); // Create a WordPress route - $route = new Route(['GET'], 'test_condition', function () { - return 'test'; - }); + $route = new Route(['GET'], 'test_condition', fn (): string => 'test'); $route->setIsWordPressRoute(true); $route->setCondition('test_condition'); @@ -126,13 +118,11 @@ function mockWordPressFunctionsForRoute(): void /** * Test that route can check if it has WordPress conditions. */ -test('route can check if it has WordPress conditions', function () { +test('route can check if it has WordPress conditions', function (): void { setupRouteTest(); // Create a route without conditions - $route = new Route(['GET'], 'test', function () { - return 'test'; - }); + $route = new Route(['GET'], 'test', fn (): string => 'test'); // Should not have condition initially expect($route->hasCondition())->toBeFalse(); @@ -147,13 +137,11 @@ function mockWordPressFunctionsForRoute(): void /** * Test that the route correctly evaluates WordPress conditions during matching. */ -test('route evaluates WordPress conditions correctly', function () { +test('route evaluates WordPress conditions correctly', function (): void { setupRouteTest(); // Create a WordPress route with a condition that exists - $route = new Route(['GET'], 'is_page', function () { - return 'page'; - }); + $route = new Route(['GET'], 'is_page', fn (): string => 'page'); $route->setIsWordPressRoute(true); $route->setCondition('is_page'); diff --git a/tests/Unit/Route/RouterTest.php b/tests/Unit/Route/RouterTest.php index 70312a7e..8aab3f42 100644 --- a/tests/Unit/Route/RouterTest.php +++ b/tests/Unit/Route/RouterTest.php @@ -11,7 +11,7 @@ /** * Setup function to create mocks and the router instance for all tests */ -function setupRouterTest() +function setupRouterTest(): array { // Initialize WordPress functions from helpers.php setupWordPressMocks(); @@ -66,8 +66,8 @@ function mockWordPressClasses(): void /** * Clean up after each test */ -afterEach(function () { - Container::setInstance(null); +afterEach(function (): void { + Container::setInstance(); WP::$wpFunctions = null; m::close(); }); @@ -75,14 +75,12 @@ function mockWordPressClasses(): void /** * Test that the Router correctly creates new Route instances. */ -test('router creates new route instances', function () { +test('router creates new route instances', function (): void { $setup = setupRouterTest(); $router = $setup['router']; // Create a route using the router - $route = $router->get('/test', function () { - return 'test'; - }); + $route = $router->get('/test', fn (): string => 'test'); // Verify that the route is an instance of our extended Route class expect($route)->toBeInstanceOf(Route::class); @@ -93,7 +91,7 @@ function mockWordPressClasses(): void /** * Test that the Router can handle WordPress conditions. */ -test('router manages WordPress conditions', function () { +test('router manages WordPress conditions', function (): void { $setup = setupRouterTest(); $router = $setup['router']; @@ -111,14 +109,12 @@ function mockWordPressClasses(): void /** * Test that the Router can add WordPress bindings to routes. */ -test('router adds WordPress bindings to routes', function () { +test('router adds WordPress bindings to routes', function (): void { $setup = setupRouterTest(); $router = $setup['router']; // Create a route with a closure that has WordPress dependencies - $route = new Route(['GET'], 'test', function (WP_Post $post) { - return 'test'; - }); + $route = new Route(['GET'], 'test', fn (WP_Post $post): string => 'test'); // Add WordPress bindings $enhancedRoute = $router->addWordPressBindings($route); @@ -131,14 +127,12 @@ function mockWordPressClasses(): void /** * Test that the Router creates routes with proper configuration. */ -test('router creates WordPress-compatible routes', function () { +test('router creates WordPress-compatible routes', function (): void { $setup = setupRouterTest(); $router = $setup['router']; // Create a WordPress route - $route = $router->get('/page', function () { - return 'page'; - }); + $route = $router->get('/page', fn (): string => 'page'); // Set it as a WordPress route with condition $route->setIsWordPressRoute(true); @@ -153,14 +147,12 @@ function mockWordPressClasses(): void /** * Test that the Router can create new Route instances with newRoute method. */ -test('router newRoute method creates proper Route instances', function () { +test('router newRoute method creates proper Route instances', function (): void { $setup = setupRouterTest(); $router = $setup['router']; // Use the newRoute method - $route = $router->newRoute(['GET'], 'test', function () { - return 'test'; - }); + $route = $router->newRoute(['GET'], 'test', fn (): string => 'test'); // Verify that it returns our extended Route class expect($route)->toBeInstanceOf(Route::class); diff --git a/tests/Unit/Support/UriTest.php b/tests/Unit/Support/UriTest.php index 794cbaae..de2d6f00 100644 --- a/tests/Unit/Support/UriTest.php +++ b/tests/Unit/Support/UriTest.php @@ -4,14 +4,14 @@ use Pollora\Support\Uri; -describe('Uri helper', function () { - it('removes trailing slash from valid urls', function () { +describe('Uri helper', function (): void { + it('removes trailing slash from valid urls', function (): void { $uri = new Uri; expect($uri->removeTrailingSlash('https://example.com/foo/'))->toBe('https://example.com/foo'); expect($uri->removeTrailingSlash('/foo/bar/'))->toBe('/foo/bar'); }); - it('handles malformed urls gracefully', function () { + it('handles malformed urls gracefully', function (): void { $uri = new Uri; $malformed = ':///foo/'; expect($uri->removeTrailingSlash($malformed))->toBe(':///foo'); diff --git a/tests/Unit/Taxonomy/Commands/TaxonomyMakeCommandTest.php b/tests/Unit/Taxonomy/Commands/TaxonomyMakeCommandTest.php index fd26c286..7768df8c 100644 --- a/tests/Unit/Taxonomy/Commands/TaxonomyMakeCommandTest.php +++ b/tests/Unit/Taxonomy/Commands/TaxonomyMakeCommandTest.php @@ -6,7 +6,7 @@ use Mockery as m; use Pollora\Taxonomy\UI\Console\TaxonomyMakeCommand; -beforeEach(function () { +beforeEach(function (): void { // Create a mock filesystem $this->files = m::mock(Filesystem::class); @@ -17,11 +17,11 @@ } }); -afterEach(function () { +afterEach(function (): void { m::close(); }); -test('TaxonomyMakeCommand generates correct slug from class name', function () { +test('TaxonomyMakeCommand generates correct slug from class name', function (): void { // Skip if command doesn't exist yet if (! class_exists(TaxonomyMakeCommand::class)) { $this->markTestSkipped('TaxonomyMakeCommand class does not exist yet'); @@ -29,14 +29,13 @@ // Test the protected method via reflection $reflectionMethod = new ReflectionMethod(TaxonomyMakeCommand::class, 'getSlugFromClassName'); - $reflectionMethod->setAccessible(true); $result = $reflectionMethod->invoke($this->command, 'ProductCategory'); expect($result)->toBe('product-category'); }); -test('TaxonomyMakeCommand generates correct singular name from class name', function () { +test('TaxonomyMakeCommand generates correct singular name from class name', function (): void { // Skip if command doesn't exist yet if (! class_exists(TaxonomyMakeCommand::class)) { $this->markTestSkipped('TaxonomyMakeCommand class does not exist yet'); @@ -44,14 +43,13 @@ // Test the protected method via reflection $reflectionMethod = new ReflectionMethod(TaxonomyMakeCommand::class, 'getNameFromClassName'); - $reflectionMethod->setAccessible(true); $result = $reflectionMethod->invoke($this->command, 'ProductCategory'); expect($result)->toBe('Product category'); }); -test('TaxonomyMakeCommand generates correct plural name from class name', function () { +test('TaxonomyMakeCommand generates correct plural name from class name', function (): void { // Skip if command doesn't exist yet if (! class_exists(TaxonomyMakeCommand::class)) { $this->markTestSkipped('TaxonomyMakeCommand class does not exist yet'); @@ -59,7 +57,6 @@ // Test the protected method via reflection $reflectionMethod = new ReflectionMethod(TaxonomyMakeCommand::class, 'getPluralNameFromClassName'); - $reflectionMethod->setAccessible(true); $result = $reflectionMethod->invoke($this->command, 'Category'); @@ -70,7 +67,7 @@ expect($result)->toBe('Tags'); }); -test('TaxonomyMakeCommand generates default object type for taxonomy', function () { +test('TaxonomyMakeCommand generates default object type for taxonomy', function (): void { // Skip if command doesn't exist yet if (! class_exists(TaxonomyMakeCommand::class)) { $this->markTestSkipped('TaxonomyMakeCommand class does not exist yet'); @@ -79,7 +76,6 @@ // Test object type generation if method exists if (method_exists(TaxonomyMakeCommand::class, 'getDefaultObjectType')) { $reflectionMethod = new ReflectionMethod(TaxonomyMakeCommand::class, 'getDefaultObjectType'); - $reflectionMethod->setAccessible(true); $result = $reflectionMethod->invoke($this->command); @@ -90,7 +86,7 @@ } }); -test('TaxonomyMakeCommand handles hierarchical flag correctly', function () { +test('TaxonomyMakeCommand handles hierarchical flag correctly', function (): void { // Skip if command doesn't exist yet if (! class_exists(TaxonomyMakeCommand::class)) { $this->markTestSkipped('TaxonomyMakeCommand class does not exist yet'); @@ -99,7 +95,6 @@ // Test hierarchical option handling if method exists if (method_exists(TaxonomyMakeCommand::class, 'shouldBeHierarchical')) { $reflectionMethod = new ReflectionMethod(TaxonomyMakeCommand::class, 'shouldBeHierarchical'); - $reflectionMethod->setAccessible(true); // Test with different class names that might suggest hierarchy $result = $reflectionMethod->invoke($this->command, 'Category'); @@ -114,7 +109,7 @@ }); // Placeholder test for when the command is fully implemented -test('TaxonomyMakeCommand can be instantiated', function () { +test('TaxonomyMakeCommand can be instantiated', function (): void { if (class_exists(TaxonomyMakeCommand::class)) { expect($this->command)->toBeInstanceOf(TaxonomyMakeCommand::class); } else { diff --git a/tests/Unit/Taxonomy/TaxonomyAttributeServiceProviderTest.php b/tests/Unit/Taxonomy/TaxonomyAttributeServiceProviderTest.php index b9b51a5d..7234ded7 100644 --- a/tests/Unit/Taxonomy/TaxonomyAttributeServiceProviderTest.php +++ b/tests/Unit/Taxonomy/TaxonomyAttributeServiceProviderTest.php @@ -9,7 +9,7 @@ // Define app_path function if it doesn't exist in the test environment if (! function_exists('app_path')) { - function app_path($path = '') + function app_path(string $path = ''): string { return '/path/to/app/'.$path; } @@ -17,7 +17,7 @@ function app_path($path = '') // Define is_dir function if needed for testing if (! function_exists('is_dir_mock')) { - function is_dir_mock($path) + function is_dir_mock($path): bool { return true; // Always return true for testing } @@ -25,13 +25,13 @@ function is_dir_mock($path) // Define mkdir function if needed for testing if (! function_exists('mkdir_mock')) { - function mkdir_mock($path, $mode = 0777, $recursive = false) + function mkdir_mock($path, $mode = 0777, $recursive = false): bool { return true; // Always return true for testing } } -beforeAll(function () { +beforeAll(function (): void { $app = new Container; Facade::setFacadeApplication($app); @@ -46,13 +46,13 @@ function mkdir_mock($path, $mode = 0777, $recursive = false) Action::setFacadeApplication($app); }); -afterAll(function () { +afterAll(function (): void { m::close(); Facade::clearResolvedInstances(); Facade::setFacadeApplication(null); }); -it('can be instantiated', function () { +it('can be instantiated', function (): void { expect(true)->toBeTrue(); }); diff --git a/tests/Unit/Taxonomy/TaxonomyFactoryTest.php b/tests/Unit/Taxonomy/TaxonomyFactoryTest.php index d1ea297d..a4768068 100644 --- a/tests/Unit/Taxonomy/TaxonomyFactoryTest.php +++ b/tests/Unit/Taxonomy/TaxonomyFactoryTest.php @@ -6,12 +6,12 @@ use Pollora\Taxonomy\Infrastructure\Factories\TaxonomyFactory; -beforeEach(function () { +beforeEach(function (): void { setupWordPressMocks(); $this->factory = new TaxonomyFactory; }); -test('make creates new Taxonomy instance with correct parameters', function () { +test('make creates new Taxonomy instance with correct parameters', function (): void { // Define test values $slug = 'test-taxonomy'; $objectType = ['post', 'page']; @@ -28,7 +28,7 @@ ->and($result->getObjectType())->toBe($objectType); }); -test('make handles null parameters correctly', function () { +test('make handles null parameters correctly', function (): void { // Define test values $slug = 'test-taxonomy'; $objectType = 'post'; @@ -43,7 +43,7 @@ ->and($result->getObjectType())->toBe($objectType); }); -test('make generates singular name from slug when not provided', function () { +test('make generates singular name from slug when not provided', function (): void { // Define test values $slug = 'product_category'; $objectType = 'product'; @@ -57,7 +57,7 @@ ->and($result->getSlug())->toBe($slug); }); -test('make generates plural name from singular when not provided', function () { +test('make generates plural name from singular when not provided', function (): void { // Define test values $slug = 'category'; $objectType = 'post'; @@ -72,7 +72,7 @@ ->and($result->getSlug())->toBe($slug); }); -test('make handles string object type', function () { +test('make handles string object type', function (): void { // Define test values $slug = 'tag'; $objectType = 'post'; @@ -87,7 +87,7 @@ ->and($result->getObjectType())->toBe($objectType); }); -test('make handles array object type', function () { +test('make handles array object type', function (): void { // Define test values $slug = 'category'; $objectType = ['post', 'page', 'product']; @@ -102,7 +102,7 @@ ->and($result->getObjectType())->toBe($objectType); }); -test('make applies additional arguments when provided', function () { +test('make applies additional arguments when provided', function (): void { // Define test values $slug = 'test-taxonomy'; $objectType = 'post'; diff --git a/tests/Unit/Taxonomy/TaxonomyServiceTest.php b/tests/Unit/Taxonomy/TaxonomyServiceTest.php index cdd3dbe9..33f667a3 100755 --- a/tests/Unit/Taxonomy/TaxonomyServiceTest.php +++ b/tests/Unit/Taxonomy/TaxonomyServiceTest.php @@ -9,14 +9,14 @@ use Pollora\Taxonomy\Domain\Contracts\TaxonomyFactoryInterface; use Pollora\Taxonomy\Domain\Contracts\TaxonomyRegistryInterface; -beforeEach(function () { +beforeEach(function (): void { $this->mockFactory = mock(TaxonomyFactoryInterface::class); $this->mockRegistry = mock(TaxonomyRegistryInterface::class); $this->mockTaxonomy = mock(Taxonomy::class); $this->taxonomyService = new TaxonomyService($this->mockFactory, $this->mockRegistry); }); -test('register calls make on factory', function () { +test('register calls make on factory', function (): void { // Define test values $slug = 'test-taxonomy'; $objectType = 'post'; @@ -40,7 +40,7 @@ expect($result)->toBe($this->mockTaxonomy); }); -test('exists calls exists on registry', function () { +test('exists calls exists on registry', function (): void { // Define test values $slug = 'test-taxonomy'; @@ -58,7 +58,7 @@ expect($result)->toBeTrue(); }); -test('getRegistered calls getAll on registry', function () { +test('getRegistered calls getAll on registry', function (): void { // Define test values $registeredTaxonomies = ['category', 'tag', 'test-taxonomy']; diff --git a/tests/Unit/Theme/ComponentFactoryTest.php b/tests/Unit/Theme/ComponentFactoryTest.php index 839b8616..4ca8e339 100755 --- a/tests/Unit/Theme/ComponentFactoryTest.php +++ b/tests/Unit/Theme/ComponentFactoryTest.php @@ -8,16 +8,16 @@ require_once __DIR__.'/../helpers.php'; -describe('ComponentFactory', function () { +describe('ComponentFactory', function (): void { it(/** * @throws ReflectionException - */ 'injects ServiceLocator into ThemeComponent (without constructor)', function () { + */ 'injects ServiceLocator into ThemeComponent (without constructor)', function (): void { $mockApp = m::mock(ContainerInterface::class); $ref = new ReflectionClass(ThemeInitializer::class); $instance = $ref->newInstanceWithoutConstructor(); $refProp = $ref->getProperty('app'); - $refProp->setAccessible(true); $refProp->setValue($instance, $mockApp); + expect($refProp->getValue($instance))->toBe($mockApp); }); }); diff --git a/tests/Unit/Theme/ImageSizeTest.php b/tests/Unit/Theme/ImageSizeTest.php index e3c3ae4c..35b5d27f 100755 --- a/tests/Unit/Theme/ImageSizeTest.php +++ b/tests/Unit/Theme/ImageSizeTest.php @@ -10,8 +10,8 @@ require_once __DIR__.'/../helpers.php'; -describe('ImageSize', function () { - it('resolves Action from Laravel container', function () { +describe('ImageSize', function (): void { + it('resolves Action from Laravel container', function (): void { $mockAction = m::mock(Action::class); $mockContainer = m::mock(ContainerInterface::class); $mockConfig = m::mock(ConfigRepositoryInterface::class); diff --git a/tests/Unit/Theme/Infrastructure/Services/ThemeAutoloaderIntegrationTest.php b/tests/Unit/Theme/Infrastructure/Services/ThemeAutoloaderIntegrationTest.php index 96b637d7..ffcc6f6d 100644 --- a/tests/Unit/Theme/Infrastructure/Services/ThemeAutoloaderIntegrationTest.php +++ b/tests/Unit/Theme/Infrastructure/Services/ThemeAutoloaderIntegrationTest.php @@ -6,7 +6,7 @@ use Pollora\Theme\Domain\Models\ThemeModule; use Pollora\Theme\Infrastructure\Services\ThemeAutoloader; -beforeEach(function () { +beforeEach(function (): void { $this->app = new Container; $this->autoloader = new ThemeAutoloader($this->app); @@ -35,20 +35,20 @@ public function getPath(): string }; }); -afterEach(function () { +afterEach(function (): void { // Clean up temporary directory if (is_dir($this->tempDir)) { exec('rm -rf '.escapeshellarg($this->tempDir)); } }); -it('generates correct namespace for theme', function () { +it('generates correct namespace for theme', function (): void { $namespace = $this->autoloader->getThemeNamespace('TestTheme'); expect($namespace)->toBe('Theme\\TestTheme\\'); }); -it('tracks theme registration status', function () { +it('tracks theme registration status', function (): void { // Initially not registered expect($this->autoloader->isThemeRegistered('TestTheme'))->toBeFalse(); @@ -59,7 +59,7 @@ public function getPath(): string expect($this->autoloader->isThemeRegistered('TestTheme'))->toBeTrue(); }); -it('generates correct namespace for different theme names', function () { +it('generates correct namespace for different theme names', function (): void { $testCases = [ 'Solidarmonde' => 'Theme\\Solidarmonde\\', 'MyCustomTheme' => 'Theme\\MyCustomTheme\\', @@ -71,7 +71,7 @@ public function getPath(): string } }); -it('can register multiple themes without conflicts', function () { +it('can register multiple themes without conflicts', function (): void { // Create directory structure for additional themes $theme1Path = $this->tempDir.'/themes/theme-one'; $theme2Path = $this->tempDir.'/themes/theme-two'; @@ -128,7 +128,7 @@ public function getPath(): string expect($this->autoloader->getThemeNamespace('ThemeTwo'))->toBe('Theme\\ThemeTwo\\'); }); -it('handles theme registration idempotently', function () { +it('handles theme registration idempotently', function (): void { // Register the same theme multiple times $this->autoloader->registerThemeModule($this->theme); $this->autoloader->registerThemeModule($this->theme); @@ -139,14 +139,12 @@ public function getPath(): string // Should have only one namespace registration $registeredNamespaces = $this->autoloader->getRegisteredNamespaces(); - $themeNamespaces = array_filter(array_keys($registeredNamespaces), function ($ns) { - return str_starts_with($ns, 'Theme\\TestTheme\\'); - }); + $themeNamespaces = array_filter(array_keys($registeredNamespaces), fn (int|string $ns): bool => str_starts_with((string) $ns, 'Theme\\TestTheme\\')); expect(count($themeNamespaces))->toBe(1); }); -it('can register themes using batch method', function () { +it('can register themes using batch method', function (): void { // Create directory structure for batch themes $batch1Path = $this->tempDir.'/themes/batch-theme-1'; $batch2Path = $this->tempDir.'/themes/batch-theme-2'; @@ -197,13 +195,13 @@ public function getPath(): string expect($this->autoloader->isThemeRegistered('BatchTheme2'))->toBeTrue(); }); -it('returns empty registered namespaces initially', function () { +it('returns empty registered namespaces initially', function (): void { $namespaces = $this->autoloader->getRegisteredNamespaces(); expect($namespaces)->toBeArray()->toBeEmpty(); }); -it('tracks registered namespaces after registration', function () { +it('tracks registered namespaces after registration', function (): void { $this->autoloader->registerThemeModule($this->theme); $namespaces = $this->autoloader->getRegisteredNamespaces(); diff --git a/tests/Unit/Theme/Infrastructure/Services/ThemeAutoloaderTest.php b/tests/Unit/Theme/Infrastructure/Services/ThemeAutoloaderTest.php index 367207bb..b2d78006 100644 --- a/tests/Unit/Theme/Infrastructure/Services/ThemeAutoloaderTest.php +++ b/tests/Unit/Theme/Infrastructure/Services/ThemeAutoloaderTest.php @@ -4,115 +4,82 @@ use Composer\Autoload\ClassLoader; use Illuminate\Container\Container; -use PHPUnit\Framework\TestCase; use Pollora\Theme\Domain\Models\ThemeModule; use Pollora\Theme\Infrastructure\Services\ThemeAutoloader; -class ThemeAutoloaderTest extends TestCase -{ - private Container $app; - - private ClassLoader $classLoader; - - private ThemeAutoloader $autoloader; - - protected function setUp(): void - { +describe('ThemeAutoloader', function (): void { + beforeEach(function (): void { $this->app = new Container; - $this->classLoader = $this->createMock(ClassLoader::class); - - // Bind the class loader to the container + $this->classLoader = Mockery::mock(ClassLoader::class)->shouldIgnoreMissing(); $this->app->instance(ClassLoader::class, $this->classLoader); - $this->autoloader = new ThemeAutoloader($this->app); - } + }); - public function test_it_registers_theme_module(): void - { + it('registers theme module', function (): void { $tempDir = sys_get_temp_dir().'/test_theme_register_'.uniqid(); $appDir = $tempDir.'/app'; mkdir($appDir, 0777, true); - $theme = $this->createMockTheme('Solidarmonde', $tempDir); + $theme = createMockTheme('Solidarmonde', $tempDir); - $this->classLoader - ->expects($this->once()) - ->method('addPsr4') + $this->classLoader->shouldReceive('addPsr4') + ->once() ->with('Theme\\Solidarmonde\\', $tempDir.'/app'); $this->autoloader->registerThemeModule($theme); - // Cleanup rmdir($appDir); rmdir($tempDir); - } + }); - public function test_it_gets_theme_namespace(): void - { - $namespace = $this->autoloader->getThemeNamespace('TestTheme'); + it('gets theme namespace', function (): void { + expect($this->autoloader->getThemeNamespace('TestTheme'))->toBe('Theme\\TestTheme\\'); + }); - $this->assertEquals('Theme\\TestTheme\\', $namespace); - } - - public function test_it_checks_if_theme_is_registered(): void - { + it('checks if theme is registered', function (): void { $tempDir = sys_get_temp_dir().'/test_theme_check_'.uniqid(); $appDir = $tempDir.'/app'; mkdir($appDir, 0777, true); - $theme = $this->createMockTheme('TestTheme', $tempDir); + $theme = createMockTheme('TestTheme', $tempDir); - $this->assertFalse($this->autoloader->isThemeRegistered('TestTheme')); + expect($this->autoloader->isThemeRegistered('TestTheme'))->toBeFalse(); $this->autoloader->registerThemeModule($theme); - $this->assertTrue($this->autoloader->isThemeRegistered('TestTheme')); + expect($this->autoloader->isThemeRegistered('TestTheme'))->toBeTrue(); - // Cleanup rmdir($appDir); rmdir($tempDir); - } + }); - public function test_it_registers_multiple_themes(): void - { + it('registers multiple themes', function (): void { $tempDir1 = sys_get_temp_dir().'/test_theme_multiple1_'.uniqid(); - $appDir1 = $tempDir1.'/app'; - mkdir($appDir1, 0777, true); + mkdir($tempDir1.'/app', 0777, true); $tempDir2 = sys_get_temp_dir().'/test_theme_multiple2_'.uniqid(); - $appDir2 = $tempDir2.'/app'; - mkdir($appDir2, 0777, true); - - $theme1 = $this->createMockTheme('ThemeOne', $tempDir1); - $theme2 = $this->createMockTheme('ThemeTwo', $tempDir2); + mkdir($tempDir2.'/app', 0777, true); - $this->classLoader - ->expects($this->exactly(2)) - ->method('addPsr4'); + $theme1 = createMockTheme('ThemeOne', $tempDir1); + $theme2 = createMockTheme('ThemeTwo', $tempDir2); - $this->classLoader - ->expects($this->once()) - ->method('register'); + $this->classLoader->shouldReceive('addPsr4')->twice(); + $this->classLoader->shouldReceive('register')->once(); $this->autoloader->registerThemes([$theme1, $theme2]); - // Cleanup - rmdir($appDir1); + rmdir($tempDir1.'/app'); rmdir($tempDir1); - rmdir($appDir2); + rmdir($tempDir2.'/app'); rmdir($tempDir2); - } - - private function createMockTheme(string $name, string $path): ThemeModule - { - $theme = $this->createMock(ThemeModule::class); + }); +}); - $theme->method('getStudlyName') - ->willReturn($name); - - $theme->method('getPath') - ->willReturn($path); +function createMockTheme(string $name, string $path): ThemeModule +{ + $theme = Mockery::mock(ThemeModule::class); + $theme->shouldReceive('getStudlyName')->andReturn($name); + $theme->shouldReceive('getPath')->andReturn($path); - return $theme; - } + return $theme; } diff --git a/tests/Unit/Theme/MenusTest.php b/tests/Unit/Theme/MenusTest.php index 1fb9bf13..10ab51b9 100755 --- a/tests/Unit/Theme/MenusTest.php +++ b/tests/Unit/Theme/MenusTest.php @@ -11,8 +11,8 @@ require_once __DIR__.'/../helpers.php'; -describe('Menus', function () { - it('resolves Action and Filter from container', function () { +describe('Menus', function (): void { + it('resolves Action and Filter from container', function (): void { $mockAction = m::mock(Action::class); $mockFilter = m::mock(Filter::class); $mockContainer = m::mock(ContainerInterface::class); @@ -33,7 +33,7 @@ $component = new Menus($mockContainer, $mockConfig); $ref = new ReflectionProperty($component, 'app'); - $ref->setAccessible(true); + expect($ref->getValue($component))->toBe($mockContainer); }); }); diff --git a/tests/Unit/Theme/PatternComponentTest.php b/tests/Unit/Theme/PatternComponentTest.php index bd2cb4fb..88245f2d 100755 --- a/tests/Unit/Theme/PatternComponentTest.php +++ b/tests/Unit/Theme/PatternComponentTest.php @@ -10,8 +10,8 @@ require_once __DIR__.'/../helpers.php'; -describe('PatternComponent', function () { - it('resolves PatternServiceInterface from Laravel container', function () { +describe('PatternComponent', function (): void { + it('resolves PatternServiceInterface from Laravel container', function (): void { $mockPatternService = m::mock(PatternServiceInterface::class); $mockAction = m::mock(Action::class); $mockContainer = m::mock(ContainerInterface::class); diff --git a/tests/Unit/Theme/SidebarTest.php b/tests/Unit/Theme/SidebarTest.php index 8231da93..451b3fc9 100755 --- a/tests/Unit/Theme/SidebarTest.php +++ b/tests/Unit/Theme/SidebarTest.php @@ -10,8 +10,8 @@ require_once __DIR__.'/../helpers.php'; -describe('Sidebar', function () { - it('resolves Application from Laravel container', function () { +describe('Sidebar', function (): void { + it('resolves Application from Laravel container', function (): void { $mockAction = m::mock(Action::class); $mockContainer = m::mock(ContainerInterface::class); $mockConfig = m::mock(ConfigRepositoryInterface::class); @@ -23,7 +23,7 @@ $sidebar = new Sidebar($mockContainer, $mockConfig); $ref = new ReflectionProperty($sidebar, 'app'); - $ref->setAccessible(true); + expect($ref->getValue($sidebar))->toBe($mockContainer); }); }); diff --git a/tests/Unit/Theme/SupportTest.php b/tests/Unit/Theme/SupportTest.php index 8892256d..5a162ac6 100755 --- a/tests/Unit/Theme/SupportTest.php +++ b/tests/Unit/Theme/SupportTest.php @@ -10,8 +10,8 @@ require_once __DIR__.'/../helpers.php'; -describe('Support', function () { - it('resolves Action from container', function () { +describe('Support', function (): void { + it('resolves Action from container', function (): void { $mockAction = m::mock(Action::class); $mockContainer = m::mock(ContainerInterface::class); $mockConfig = m::mock(ConfigRepositoryInterface::class); @@ -27,7 +27,7 @@ $component = new Support($mockContainer, $mockConfig); $ref = new ReflectionProperty($component, 'app'); - $ref->setAccessible(true); + expect($ref->getValue($component))->toBe($mockContainer); }); }); diff --git a/tests/Unit/Theme/TemplatesTest.php b/tests/Unit/Theme/TemplatesTest.php index 89aa12e7..71f549b7 100755 --- a/tests/Unit/Theme/TemplatesTest.php +++ b/tests/Unit/Theme/TemplatesTest.php @@ -10,8 +10,8 @@ require_once __DIR__.'/../helpers.php'; -describe('Templates', function () { - it('resolves Action from container', function () { +describe('Templates', function (): void { + it('resolves Action from container', function (): void { $mockAction = m::mock(Action::class); $mockContainer = m::mock(ContainerInterface::class); $mockConfig = m::mock(ConfigRepositoryInterface::class); @@ -27,7 +27,7 @@ $component = new Templates($mockContainer, $mockConfig); $ref = new ReflectionProperty($component, 'app'); - $ref->setAccessible(true); + expect($ref->getValue($component))->toBe($mockContainer); }); }); diff --git a/tests/Unit/Theme/ThemeComponentProviderTest.php b/tests/Unit/Theme/ThemeComponentProviderTest.php index 75648127..3e0383d1 100755 --- a/tests/Unit/Theme/ThemeComponentProviderTest.php +++ b/tests/Unit/Theme/ThemeComponentProviderTest.php @@ -21,7 +21,7 @@ // For simplicity with Mockery, we'll define them in `beforeEach` and access them via `$this`. // Pest will make $this available in tests if the closure in beforeEach uses $this. -beforeEach(function () { +beforeEach(function (): void { // parent::setUp() from TestCase is usually not needed directly in Pest unless it does something critical. // If TestCase::setUp() has essential mocking or app bootstrapping, it might need to be replicated or called. // Assuming Tests\TestCase::setUp() is for general Laravel testing setup, which Pest handles via uses(Tests\TestCase::class). @@ -52,7 +52,7 @@ // it should be included with `uses(Tests\TestCase::class);` at the top level of the file. // For now, assuming it's not strictly necessary for these specific tests beyond Mockery setup. -it('registers component factory (verifies all core components registration)', function () { +it('registers component factory (verifies all core components registration)', function (): void { // This test is named component_factory, but ThemeComponentProvider doesn't deal with ComponentFactory directly. // It registers individual components. Let's assume this test is actually about verifying that // all components defined in the provider are registered correctly. @@ -70,7 +70,7 @@ expect($this->provider)->toBeInstanceOf(ThemeComponentProvider::class); }); -it('registers core components', function () { +it('registers core components', function (): void { $mockComponent = m::mock(ThemeComponent::class); $mockComponent->shouldReceive('register')->byDefault(); // All components have a register method @@ -90,12 +90,12 @@ expect($this->provider)->toBeInstanceOf(ThemeComponentProvider::class); }); -it('can be instantiated', function () { +it('can be instantiated', function (): void { expect($this->provider)->toBeInstanceOf(ThemeComponentProvider::class); }); // tearDown with m::close() is usually handled by Pest's Mockery plugin or a global helper if needed. // If not using the plugin, m::close() might be called in an `afterEach`. -afterEach(function () { +afterEach(function (): void { m::close(); }); diff --git a/tests/Unit/Theme/ThemeInitializerTest.php b/tests/Unit/Theme/ThemeInitializerTest.php index 05f3909f..01028ce8 100755 --- a/tests/Unit/Theme/ThemeInitializerTest.php +++ b/tests/Unit/Theme/ThemeInitializerTest.php @@ -8,14 +8,14 @@ require_once __DIR__.'/../helpers.php'; -describe('ThemeInitializer', function () { - it('injects ContainerInterface mock (plus ServiceLocator legacy)', function () { +describe('ThemeInitializer', function (): void { + it('injects ContainerInterface mock (plus ServiceLocator legacy)', function (): void { $mockContainer = m::mock(ContainerInterface::class); $ref = new ReflectionClass(ThemeInitializer::class); $instance = $ref->newInstanceWithoutConstructor(); $refProp = $ref->getProperty('app'); - $refProp->setAccessible(true); $refProp->setValue($instance, $mockContainer); + expect($refProp->getValue($instance))->toBe($mockContainer); }); }); diff --git a/tests/Unit/Theme/ThemeManagerTest.php b/tests/Unit/Theme/ThemeManagerTest.php index 12f869d1..9767bccb 100755 --- a/tests/Unit/Theme/ThemeManagerTest.php +++ b/tests/Unit/Theme/ThemeManagerTest.php @@ -12,7 +12,7 @@ use Pollora\Theme\Domain\Exceptions\ThemeException; use Pollora\Theme\Domain\Models\ThemeMetadata; -beforeEach(function () { +beforeEach(function (): void { // Create mock container with config property $this->app = Mockery::mock(Container::class); $this->config = Mockery::mock('config'); @@ -29,7 +29,7 @@ $this->themeManager = new ThemeManager($this->app, $this->viewFinder, $this->localeLoader, $this->moduleRepository, $this->themeRegistrar, $this->consoleDetectionService); }); -test('loads a valid theme', function () { +test('loads a valid theme', function (): void { $testPath = '/path/to/themes'; $themeName = 'testTheme'; $app = Mockery::mock(Container::class); @@ -48,6 +48,7 @@ [$app, $this->viewFinder, $this->localeLoader, $moduleRepository, $themeRegistrar, $consoleDetectionService] )->makePartial(); $manager->shouldAllowMockingProtectedMethods(); + $themeMetadata = Mockery::mock(ThemeMetadata::class); $themeMetadata->shouldReceive('getName')->andReturn($themeName); $themeMetadata->shouldReceive('getBasePath')->andReturn($testPath.'/'.$themeName); @@ -62,12 +63,12 @@ expect($manager->instance())->toBeInstanceOf(ThemeManager::class); }); -test('throws an exception if theme name is empty', function () { +test('throws an exception if theme name is empty', function (): void { expect(fn () => $this->themeManager->load(''))->toThrow(ThemeException::class) ->and(fn () => $this->themeManager->load('0'))->toThrow(ThemeException::class); }); -test('throws an exception if theme directory does not exist', function () { +test('throws an exception if theme directory does not exist', function (): void { $themeName = 'nonexistent'; $app = Mockery::mock(Container::class); $config = Mockery::mock('config'); @@ -84,15 +85,16 @@ [$app, $this->viewFinder, $this->localeLoader, $moduleRepository, $themeRegistrar, $consoleDetectionService] )->makePartial(); $manager->shouldAllowMockingProtectedMethods(); + $themeMetadata = Mockery::mock(ThemeMetadata::class); $themeMetadata->shouldReceive('getName')->andReturn($themeName); $themeMetadata->shouldReceive('getBasePath')->andReturn('/path/to/themes/'.$themeName); $manager->shouldReceive('createThemeMetadata')->andReturn($themeMetadata); $manager->shouldReceive('getThemesPath')->andReturn('/path/to/themes'); expect(fn () => $manager->load($themeName)) - ->toThrow(ThemeException::class, "Theme directory {$themeName} not found."); + ->toThrow(ThemeException::class, sprintf('Theme directory %s not found.', $themeName)); }); -test('instance returns self', function () { +test('instance returns self', function (): void { expect($this->themeManager->instance())->toBeInstanceOf(ThemeManager::class); }); diff --git a/tests/Unit/Theme/ThemeRegistrarTest.php b/tests/Unit/Theme/ThemeRegistrarTest.php index 4ff06aca..5221be04 100644 --- a/tests/Unit/Theme/ThemeRegistrarTest.php +++ b/tests/Unit/Theme/ThemeRegistrarTest.php @@ -2,174 +2,83 @@ declare(strict_types=1); -namespace Tests\Unit\Theme; - use Illuminate\Container\Container; -use PHPUnit\Framework\TestCase; -use Pollora\Modules\Domain\Contracts\ModuleDiscoveryOrchestratorInterface; -use Pollora\Modules\Domain\Contracts\ModuleRepositoryInterface; -use Pollora\Modules\Infrastructure\Services\ModuleAssetManager; -use Pollora\Modules\Infrastructure\Services\ModuleComponentManager; -use Pollora\Modules\Infrastructure\Services\ModuleConfigurationLoader; use Pollora\Theme\Application\Services\ThemeRegistrar; use Pollora\Theme\Domain\Contracts\ThemeModuleInterface; use Pollora\Theme\Infrastructure\Services\WordPressThemeParser; -use Psr\Container\ContainerInterface; -/** - * Test suite for the theme self-registration system. - */ -class ThemeRegistrarTest extends TestCase +function setupRegistrarMocks(Container $container, $parser): void { - private ThemeRegistrar $registrar; - - private ContainerInterface $container; - - private WordPressThemeParser $parser; - - protected function setUp(): void - { - parent::setUp(); - - $this->container = $this->createMock(ContainerInterface::class); - $this->parser = $this->createMock(WordPressThemeParser::class); + $parser->shouldReceive('parseThemeHeaders')->andReturn(['Name' => 'Test Theme', 'Version' => '1.0.0']); +} +describe('ThemeRegistrar', function (): void { + beforeEach(function (): void { + $this->container = new Container; + $this->parser = Mockery::mock(WordPressThemeParser::class)->shouldIgnoreMissing(); $this->registrar = new ThemeRegistrar($this->container, $this->parser); - } + }); - public function test_can_register_active_theme(): void - { - // Arrange - $this->parser->expects($this->once()) - ->method('parseThemeHeaders') + it('can register active theme', function (): void { + $this->parser->shouldReceive('parseThemeHeaders') + ->once() ->with('/path/to/theme/style.css') - ->willReturn(['Name' => 'Test Theme', 'Version' => '1.0.0']); - - $this->container->expects($this->any()) - ->method('has') - ->willReturnMap([ - ['app', true], - [ModuleRepositoryInterface::class, false], - [ModuleDiscoveryOrchestratorInterface::class, false], - [ModuleConfigurationLoader::class, false], - [ModuleComponentManager::class, false], - [ModuleAssetManager::class, false], - ]); - - $mockApp = $this->createMock(Container::class); - $this->container->expects($this->any()) - ->method('get') - ->with('app') - ->willReturn($mockApp); - - // Act - $theme = $this->registrar->register(); + ->andReturn(['Name' => 'Test Theme', 'Version' => '1.0.0']); - // Assert - $this->assertInstanceOf(ThemeModuleInterface::class, $theme); - $this->assertEquals('test-theme', $theme->getName()); - $this->assertEquals('/path/to/theme', $theme->getPath()); - $this->assertTrue($theme->isEnabled()); - } + $theme = $this->registrar->register(); - public function test_can_get_active_theme(): void - { - // Arrange - $this->setupMocksForRegistration(); + expect($theme)->toBeInstanceOf(ThemeModuleInterface::class); + expect($theme->getName())->toBe('test-theme'); + expect($theme->getPath())->toBe('/path/to/theme'); + expect($theme->isEnabled())->toBeTrue(); + }); - // Act + it('can get active theme', function (): void { + setupRegistrarMocks($this->container, $this->parser); $registeredTheme = $this->registrar->register(); $activeTheme = $this->registrar->getActiveTheme(); - // Assert - $this->assertSame($registeredTheme, $activeTheme); - } - - public function test_returns_null_when_no_theme_registered(): void - { - // Act - $activeTheme = $this->registrar->getActiveTheme(); + expect($activeTheme)->toBe($registeredTheme); + }); - // Assert - $this->assertNull($activeTheme); - } + it('returns null when no theme registered', function (): void { + expect($this->registrar->getActiveTheme())->toBeNull(); + }); - public function test_can_check_if_theme_is_active(): void - { - // Arrange - $this->setupMocksForRegistration(); + it('can check if theme is active', function (): void { + setupRegistrarMocks($this->container, $this->parser); - // Act $this->registrar->register(); - // Assert - $this->assertTrue($this->registrar->isThemeActive('test-theme')); - $this->assertTrue($this->registrar->isThemeActive('TEST-THEME')); // Case insensitive - $this->assertFalse($this->registrar->isThemeActive('other-theme')); - } + expect($this->registrar->isThemeActive('test-theme'))->toBeTrue(); + expect($this->registrar->isThemeActive('TEST-THEME'))->toBeTrue(); + expect($this->registrar->isThemeActive('other-theme'))->toBeFalse(); + }); - public function test_returns_false_when_no_theme_registered_for_is_active_check(): void - { - // Act & Assert - $this->assertFalse($this->registrar->isThemeActive('any-theme')); - } + it('returns false when no theme registered for isActive check', function (): void { + expect($this->registrar->isThemeActive('any-theme'))->toBeFalse(); + }); - public function test_can_reset_active_theme(): void - { - // Arrange - $this->setupMocksForRegistration(); + it('can reset active theme', function (): void { + setupRegistrarMocks($this->container, $this->parser); $this->registrar->register(); - - // Act $this->registrar->resetActiveTheme(); - // Assert - $this->assertNull($this->registrar->getActiveTheme()); - $this->assertFalse($this->registrar->isThemeActive('test-theme')); - } + expect($this->registrar->getActiveTheme())->toBeNull(); + expect($this->registrar->isThemeActive('test-theme'))->toBeFalse(); + }); - public function test_parses_theme_headers_when_no_data_provided(): void - { - // Arrange - $expectedStylePath = '/path/to/theme/style.css'; + it('parses theme headers when no data provided', function (): void { $parsedData = ['Name' => 'Parsed Theme', 'Version' => '2.0.0']; - $this->parser->expects($this->once()) - ->method('parseThemeHeaders') - ->with($expectedStylePath) - ->willReturn($parsedData); - - $this->setupMocksForRegistration(); + $this->parser->shouldReceive('parseThemeHeaders') + ->once() + ->with('/path/to/theme/style.css') + ->andReturn($parsedData); - // Act $theme = $this->registrar->register(); - // Assert - $this->assertEquals($parsedData, $theme->getHeaders()); - } - - private function setupMocksForRegistration(): void - { - $this->parser->expects($this->any()) - ->method('parseThemeHeaders') - ->willReturn(['Name' => 'Test Theme', 'Version' => '1.0.0']); - - $this->container->expects($this->any()) - ->method('has') - ->willReturnMap([ - ['app', true], - [ModuleRepositoryInterface::class, false], - [ModuleDiscoveryOrchestratorInterface::class, false], - [ModuleConfigurationLoader::class, false], - [ModuleComponentManager::class, false], - [ModuleAssetManager::class, false], - ]); - - $mockApp = $this->createMock(Container::class); - $this->container->expects($this->any()) - ->method('get') - ->with('app') - ->willReturn($mockApp); - } -} + expect($theme->getHeaders())->toBe($parsedData); + }); +}); diff --git a/tests/Unit/VersionCheck/AdminNoticeTest.php b/tests/Unit/VersionCheck/AdminNoticeTest.php index a158f2ff..d67c734d 100644 --- a/tests/Unit/VersionCheck/AdminNoticeTest.php +++ b/tests/Unit/VersionCheck/AdminNoticeTest.php @@ -8,12 +8,12 @@ require_once dirname(__DIR__).'/helpers.php'; -beforeEach(function () { +beforeEach(function (): void { setupWordPressMocks(); }); -describe('AdminNotice', function () { - it('renders nothing when no update is available', function () { +describe('AdminNotice', function (): void { + it('renders nothing when no update is available', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('13.3.0'); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); @@ -27,14 +27,14 @@ expect($output)->toBeEmpty(); }); - it('renders a warning notice when update is available', function () { + it('renders a warning notice when update is available', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('13.2.0'); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); - setWordPressFunction('get_current_user_id', fn () => 1); - setWordPressFunction('get_user_meta', fn () => ''); - setWordPressFunction('wp_create_nonce', fn () => 'test-nonce'); + setWordPressFunction('get_current_user_id', fn (): int => 1); + setWordPressFunction('get_user_meta', fn (): string => ''); + setWordPressFunction('wp_create_nonce', fn (): string => 'test-nonce'); $notice = new AdminNotice(new VersionComparator($checker)); @@ -49,13 +49,13 @@ expect($output)->toContain('changelog'); }); - it('renders nothing when notice is dismissed for current version', function () { + it('renders nothing when notice is dismissed for current version', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('13.2.0'); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); - setWordPressFunction('get_current_user_id', fn () => 1); - setWordPressFunction('get_user_meta', fn () => '13.3.0'); + setWordPressFunction('get_current_user_id', fn (): int => 1); + setWordPressFunction('get_user_meta', fn (): string => '13.3.0'); $notice = new AdminNotice(new VersionComparator($checker)); diff --git a/tests/Unit/VersionCheck/PackagistVersionCheckerTest.php b/tests/Unit/VersionCheck/PackagistVersionCheckerTest.php index 59833f39..6bde7779 100644 --- a/tests/Unit/VersionCheck/PackagistVersionCheckerTest.php +++ b/tests/Unit/VersionCheck/PackagistVersionCheckerTest.php @@ -6,12 +6,12 @@ require_once dirname(__DIR__).'/helpers.php'; -beforeEach(function () { +beforeEach(function (): void { setupWordPressMocks(); }); -describe('PackagistVersionChecker', function () { - it('returns the currently installed version', function () { +describe('PackagistVersionChecker', function (): void { + it('returns the currently installed version', function (): void { $checker = new PackagistVersionChecker; $version = $checker->getCurrentVersion(); @@ -19,25 +19,25 @@ expect($version)->toBeString(); }); - it('returns cached version from transient', function () { - setWordPressFunction('get_transient', fn () => '99.0.0'); + it('returns cached version from transient', function (): void { + setWordPressFunction('get_transient', fn (): string => '99.0.0'); $checker = new PackagistVersionChecker; expect($checker->getLatestVersion())->toBe('99.0.0'); }); - it('fetches from packagist when no cache and stores in transient', function () { - setWordPressFunction('get_transient', fn () => false); + it('fetches from packagist when no cache and stores in transient', function (): void { + setWordPressFunction('get_transient', fn (): false => false); $storedVersion = null; - setWordPressFunction('set_transient', function ($key, $value, $ttl) use (&$storedVersion) { + setWordPressFunction('set_transient', function ($key, $value, $ttl) use (&$storedVersion): true { $storedVersion = $value; return true; }); - setWordPressFunction('wp_remote_get', fn () => [ + setWordPressFunction('wp_remote_get', fn (): array => [ 'body' => json_encode([ 'packages' => [ 'pollora/framework' => [ @@ -50,7 +50,7 @@ ]); setWordPressFunction('wp_remote_retrieve_body', fn ($response) => $response['body']); - setWordPressFunction('is_wp_error', fn () => false); + setWordPressFunction('is_wp_error', fn (): false => false); $checker = new PackagistVersionChecker; @@ -58,11 +58,11 @@ expect($storedVersion)->toBe('13.3.0'); }); - it('skips dev and pre-release versions', function () { - setWordPressFunction('get_transient', fn () => false); - setWordPressFunction('set_transient', fn () => true); + it('skips dev and pre-release versions', function (): void { + setWordPressFunction('get_transient', fn (): false => false); + setWordPressFunction('set_transient', fn (): true => true); - setWordPressFunction('wp_remote_get', fn () => [ + setWordPressFunction('wp_remote_get', fn (): array => [ 'body' => json_encode([ 'packages' => [ 'pollora/framework' => [ @@ -77,17 +77,17 @@ ]); setWordPressFunction('wp_remote_retrieve_body', fn ($response) => $response['body']); - setWordPressFunction('is_wp_error', fn () => false); + setWordPressFunction('is_wp_error', fn (): false => false); $checker = new PackagistVersionChecker; expect($checker->getLatestVersion())->toBe('13.3.0'); }); - it('returns null on API error', function () { - setWordPressFunction('get_transient', fn () => false); - setWordPressFunction('is_wp_error', fn () => true); - setWordPressFunction('wp_remote_get', fn () => new stdClass); + it('returns null on API error', function (): void { + setWordPressFunction('get_transient', fn (): false => false); + setWordPressFunction('is_wp_error', fn (): true => true); + setWordPressFunction('wp_remote_get', fn (): stdClass => new stdClass); $checker = new PackagistVersionChecker; diff --git a/tests/Unit/VersionCheck/SiteHealthCheckTest.php b/tests/Unit/VersionCheck/SiteHealthCheckTest.php index 3ad74592..94c10877 100644 --- a/tests/Unit/VersionCheck/SiteHealthCheckTest.php +++ b/tests/Unit/VersionCheck/SiteHealthCheckTest.php @@ -6,8 +6,8 @@ use Pollora\VersionCheck\Domain\Services\VersionComparator; use Pollora\VersionCheck\UI\Http\SiteHealthCheck; -describe('SiteHealthCheck', function () { - it('adds Pollora section to debug information', function () { +describe('SiteHealthCheck', function (): void { + it('adds Pollora section to debug information', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('13.3.0'); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); @@ -22,7 +22,7 @@ expect($info['pollora']['fields']['up_to_date']['value'])->toBe('Yes'); }); - it('shows not up to date when update available', function () { + it('shows not up to date when update available', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('13.2.0'); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); @@ -33,7 +33,7 @@ expect($info['pollora']['fields']['up_to_date']['value'])->toBe('No'); }); - it('adds version test to site status tests', function () { + it('adds version test to site status tests', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $health = new SiteHealthCheck(new VersionComparator($checker)); @@ -43,7 +43,7 @@ expect($tests['direct']['pollora_update']['test'])->toBeCallable(); }); - it('returns good status when up to date', function () { + it('returns good status when up to date', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('13.3.0'); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); @@ -55,7 +55,7 @@ expect($result['badge']['color'])->toBe('blue'); }); - it('returns recommended status when update available', function () { + it('returns recommended status when update available', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('13.2.0'); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); @@ -68,7 +68,7 @@ expect($result['label'])->toContain('13.3.0'); }); - it('returns recommended status when version cannot be determined', function () { + it('returns recommended status when version cannot be determined', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn(null); $checker->shouldReceive('getLatestVersion')->andReturn(null); diff --git a/tests/Unit/VersionCheck/VersionComparatorTest.php b/tests/Unit/VersionCheck/VersionComparatorTest.php index ae9b578a..6084e10a 100644 --- a/tests/Unit/VersionCheck/VersionComparatorTest.php +++ b/tests/Unit/VersionCheck/VersionComparatorTest.php @@ -5,8 +5,8 @@ use Pollora\VersionCheck\Domain\Contracts\VersionCheckerInterface; use Pollora\VersionCheck\Domain\Services\VersionComparator; -describe('VersionComparator', function () { - it('detects update available when latest is newer', function () { +describe('VersionComparator', function (): void { + it('detects update available when latest is newer', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('13.2.0'); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); @@ -16,7 +16,7 @@ expect($comparator->isUpdateAvailable())->toBeTrue(); }); - it('reports no update when versions match', function () { + it('reports no update when versions match', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('13.3.0'); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); @@ -26,7 +26,7 @@ expect($comparator->isUpdateAvailable())->toBeFalse(); }); - it('reports no update when current is newer', function () { + it('reports no update when current is newer', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('14.0.0'); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); @@ -36,7 +36,7 @@ expect($comparator->isUpdateAvailable())->toBeFalse(); }); - it('reports no update when current version is null', function () { + it('reports no update when current version is null', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn(null); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); @@ -46,7 +46,7 @@ expect($comparator->isUpdateAvailable())->toBeFalse(); }); - it('reports no update when latest version is null', function () { + it('reports no update when latest version is null', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('13.3.0'); $checker->shouldReceive('getLatestVersion')->andReturn(null); @@ -56,7 +56,7 @@ expect($comparator->isUpdateAvailable())->toBeFalse(); }); - it('delegates getCurrentVersion to checker', function () { + it('delegates getCurrentVersion to checker', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getCurrentVersion')->andReturn('13.2.0'); @@ -65,7 +65,7 @@ expect($comparator->getCurrentVersion())->toBe('13.2.0'); }); - it('delegates getLatestVersion to checker', function () { + it('delegates getLatestVersion to checker', function (): void { $checker = Mockery::mock(VersionCheckerInterface::class); $checker->shouldReceive('getLatestVersion')->andReturn('13.3.0'); diff --git a/tests/Unit/WpRest/Infrastructure/Providers/WpRestAttributeServiceProviderTest.php b/tests/Unit/WpRest/Infrastructure/Providers/WpRestAttributeServiceProviderTest.php index 112dc2c5..0ac21541 100644 --- a/tests/Unit/WpRest/Infrastructure/Providers/WpRestAttributeServiceProviderTest.php +++ b/tests/Unit/WpRest/Infrastructure/Providers/WpRestAttributeServiceProviderTest.php @@ -2,110 +2,61 @@ declare(strict_types=1); -namespace Tests\Unit\WpRest\Infrastructure\Providers; - use Illuminate\Container\Container; use Pollora\Discovery\Domain\Contracts\DiscoveryEngineInterface; use Pollora\WpRest\Infrastructure\Providers\WpRestAttributeServiceProvider; use Pollora\WpRest\Infrastructure\Services\WpRestDiscovery; -use Tests\TestCase as BaseTestCase; - -/** - * Test suite for WpRestAttributeServiceProvider. - * - * Tests the service provider functionality for discovering and registering - * WordPress REST API routes through the new discovery system. - */ -final class WpRestAttributeServiceProviderTest extends BaseTestCase -{ - private Container $container; - - private WpRestAttributeServiceProvider $provider; - - private DiscoveryEngineInterface $discoveryEngine; - - protected function setUp(): void - { - parent::setUp(); +describe('WpRestAttributeServiceProvider', function (): void { + beforeEach(function (): void { $this->container = new Container; $this->provider = new WpRestAttributeServiceProvider($this->container); - // Create mock discovery engine - $this->discoveryEngine = $this->createMock(DiscoveryEngineInterface::class); + $this->discoveryEngine = Mockery::mock(DiscoveryEngineInterface::class)->shouldIgnoreMissing(); $this->container->instance(DiscoveryEngineInterface::class, $this->discoveryEngine); - } + }); - public function test_register_registers_wprest_discovery(): void - { - // The register method should register WpRestDiscovery as singleton + it('registers WpRestDiscovery as singleton', function (): void { $initialBindings = count($this->container->getBindings()); $this->provider->register(); - // Should have registered WpRestDiscovery - $this->assertEquals($initialBindings + 1, count($this->container->getBindings())); - $this->assertTrue($this->container->bound(WpRestDiscovery::class)); - } + expect(count($this->container->getBindings()))->toBe($initialBindings + 1); + expect($this->container->bound(WpRestDiscovery::class))->toBeTrue(); + }); - public function test_boot_registers_discovered_wp_rest_routes(): void - { - // This test verifies that the boot method processes discovered REST routes - $this->expectNotToPerformAssertions(); - - // Should not throw an exception + it('boot processes discovered REST routes without error', function (): void { $this->provider->boot(); - } - - public function test_boot_handles_empty_discovery_gracefully(): void - { - $this->expectNotToPerformAssertions(); + })->throwsNoExceptions(); - // Should not throw an exception even with empty discovery + it('boot handles empty discovery gracefully', function (): void { $this->provider->boot(); - } - - public function test_boot_handles_discovery_failure_gracefully(): void - { - $this->expectNotToPerformAssertions(); + })->throwsNoExceptions(); - // Should not throw an exception even if discovery fails + it('boot handles discovery failure gracefully', function (): void { $this->provider->boot(); - } + })->throwsNoExceptions(); - public function test_boot_registers_wprest_discovery_with_engine(): void - { - // Test that boot method registers WpRestDiscovery with the discovery engine - $this->discoveryEngine->expects($this->once()) - ->method('addDiscovery') - ->with('wp_rest_routes', $this->isInstanceOf(WpRestDiscovery::class)); + it('boot registers WpRestDiscovery with engine', function (): void { + $this->discoveryEngine->shouldReceive('addDiscovery') + ->once() + ->with('wp_rest_routes', Mockery::type(WpRestDiscovery::class)); $this->provider->register(); $this->provider->boot(); - } + }); - public function test_boot_handles_no_discovery_engine_gracefully(): void - { - // Remove discovery engine from container + it('boot handles no discovery engine gracefully', function (): void { unset($this->container[DiscoveryEngineInterface::class]); - // Should not throw an exception when no discovery engine is bound - $this->expectNotToPerformAssertions(); $this->provider->boot(); - } + })->throwsNoExceptions(); - public function test_boot_registers_discovered_wp_rest_routes_with_valid_data(): void - { - $this->expectNotToPerformAssertions(); - - // Should not throw an exception when processing valid routes + it('boot handles valid route data without error', function (): void { $this->provider->boot(); - } -} + })->throwsNoExceptions(); +}); -/** - * Test WordPress REST route class for testing purposes. - */ class TestWpRestRoute { public string $namespace = 'test/v1'; diff --git a/tests/Unit/WpRest/WpRestDiscoveryTest.php b/tests/Unit/WpRest/WpRestDiscoveryTest.php index fb227189..ed292d5b 100644 --- a/tests/Unit/WpRest/WpRestDiscoveryTest.php +++ b/tests/Unit/WpRest/WpRestDiscoveryTest.php @@ -11,17 +11,17 @@ // Mock WordPress functions if they don't exist if (! function_exists('register_rest_route')) { - function register_rest_route($namespace, $route, $args) + function register_rest_route($namespace, $route, $args): bool { global $registered_routes; - $registered_routes[] = compact('namespace', 'route', 'args'); + $registered_routes[] = ['namespace' => $namespace, 'route' => $route, 'args' => $args]; return true; } } if (! function_exists('add_action')) { - function add_action($hook, $callback, $priority = 10, $accepted_args = 1) + function add_action($hook, $callback, $priority = 10, $accepted_args = 1): bool { global $wp_actions; $wp_actions[$hook][] = $callback; @@ -90,15 +90,15 @@ public function __construct( ) {} } -describe('WpRestDiscovery', function () { +describe('WpRestDiscovery', function (): void { - beforeEach(function () { + beforeEach(function (): void { global $registered_routes, $wp_actions; $registered_routes = []; $wp_actions = []; }); - test('discover method processes only DiscoveredClass instances', function () { + test('discover method processes only DiscoveredClass instances', function (): void { $discovery = new WpRestDiscovery; // Test that discovery starts empty @@ -111,7 +111,7 @@ public function __construct( expect($discovery->getItems()->all())->toHaveCount(1); }); - test('basic discovery functionality works', function () { + test('basic discovery functionality works', function (): void { $discovery = new WpRestDiscovery; // Test that we can manually add items (simulating discovery) @@ -131,7 +131,7 @@ class: WpRestRoute::class, expect($discovery->getItems()->all())->toHaveCount(1); }); - test('registers REST routes when applying discovered items', function () { + test('registers REST routes when applying discovered items', function (): void { global $registered_routes, $wp_actions; $discovery = new WpRestDiscovery; @@ -161,7 +161,7 @@ class: WpRestRoute::class, expect($discovery->getItems()->all())->toHaveCount(1); }); - test('handles reflection errors gracefully', function () { + test('handles reflection errors gracefully', function (): void { $discovery = new WpRestDiscovery; $location = new DiscoveryLocation('', '/test/path'); $reflectionCache = new ReflectionCache; @@ -186,19 +186,19 @@ class: WpRestRoute::class, expect(fn () => $discovery->apply())->not->toThrow(Exception::class); }); - test('returns correct identifier', function () { + test('returns correct identifier', function (): void { $discovery = new WpRestDiscovery; expect($discovery->getIdentifier())->toBe('wp_rest_routes'); }); - test('wrapper system works with non-Attributable classes', function () { + test('wrapper system works with non-Attributable classes', function (): void { // Test the core wrapper functionality separately $className = 'TestDocumentAPI'; $namespace = 'api/v1'; $route = '/documents/(?P\\d+)'; // Create wrapper like WpRestDiscovery does - $wrapper = new class($className, $namespace, $route, null) implements Attributable + $wrapper = new class($className, $namespace, $route) implements Attributable { private mixed $realInstance = null; diff --git a/tests/Unit/helpers.php b/tests/Unit/helpers.php index 8c1a3bc5..a6c3601f 100644 --- a/tests/Unit/helpers.php +++ b/tests/Unit/helpers.php @@ -17,7 +17,7 @@ class WP /** * Setup WordPress mock functions for tests */ -function setupWordPressMocks() +function setupWordPressMocks(): void { // Initialize WP::$wpFunctions if not already set if (! isset(WP::$wpFunctions) || ! WP::$wpFunctions) { @@ -26,20 +26,7 @@ function setupWordPressMocks() // Mock WordPress hook functions with specific handlers for common filters WP::$wpFunctions->shouldReceive('add_filter') - ->withArgs(function ($hook, $callback, $priority = 10, $accepted_args = 1) { - // Allow any template_include filter to be registered - if ($hook === 'template_include') { - return true; - } - - // Allow template_redirect action to be registered - if ($hook === 'template_redirect') { - return true; - } - - // Default behavior for other hooks - return true; - }) + ->withArgs(fn ($hook, $callback, $priority = 10, $accepted_args = 1): bool => true) ->andReturn(true) ->byDefault(); @@ -72,9 +59,7 @@ function setupWordPressMocks() // Mock WordPress option functions WP::$wpFunctions->shouldReceive('get_option') ->withAnyArgs() - ->andReturnUsing(function ($option, $default = false) { - return $default; - }) + ->andReturnUsing(fn ($option, $default = false) => $default) ->byDefault(); WP::$wpFunctions->shouldReceive('add_option') @@ -125,9 +110,7 @@ function setupWordPressMocks() WP::$wpFunctions->shouldReceive('apply_filters') ->withAnyArgs() - ->andReturnUsing(function ($tag, $value) { - return $value; - }) + ->andReturnUsing(fn ($tag, $value) => $value) ->byDefault(); // Default WordPress conditional functions behavior @@ -299,7 +282,7 @@ function setupWordPressMocks() WP::$wpFunctions->shouldReceive('get_queried_object') ->withAnyArgs() - ->andReturnUsing(function () { + ->andReturnUsing(function (): stdClass { $obj = new stdClass; $obj->post_type = 'page'; $obj->post_name = 'test-page'; @@ -311,7 +294,7 @@ function setupWordPressMocks() WP::$wpFunctions->shouldReceive('get_post') ->withAnyArgs() - ->andReturnUsing(function () { + ->andReturnUsing(function (): stdClass { $post = new stdClass; $post->post_name = 'parent-page'; $post->post_parent = 0; @@ -322,16 +305,14 @@ function setupWordPressMocks() WP::$wpFunctions->shouldReceive('get_query_var') ->withAnyArgs() - ->andReturnUsing(function ($var) { - return $var === 'post_type' ? 'page' : ''; - }) + ->andReturnUsing(fn ($var): string => $var === 'post_type' ? 'page' : '') ->byDefault(); } /** * Convenience function to set mock WordPress condition values */ -function setWordPressConditions(array $conditions = []) +function setWordPressConditions(array $conditions = []): void { // Make sure WP::$wpFunctions is initialized if (! isset(WP::$wpFunctions) || ! WP::$wpFunctions) { @@ -374,11 +355,8 @@ function app($abstract = null, array $parameters = []) if (! function_exists('app_path')) { /** * Get the path to the application folder. - * - * @param string $path - * @return string */ - function app_path($path = '') + function app_path(?string $path = ''): string { return __DIR__.'/../../app/'.($path ? DIRECTORY_SEPARATOR.$path : $path); } @@ -387,11 +365,8 @@ function app_path($path = '') if (! function_exists('config_path')) { /** * Get the path to the config folder. - * - * @param string $path - * @return string */ - function config_path($path = '') + function config_path(?string $path = ''): string { return __DIR__.'/../../config/'.($path ? DIRECTORY_SEPARATOR.$path : $path); } @@ -400,11 +375,8 @@ function config_path($path = '') if (! function_exists('base_path')) { /** * Get the path to the base of the install. - * - * @param string $path - * @return string */ - function base_path($path = '') + function base_path(?string $path = ''): string { return __DIR__.'/../..'.($path ? DIRECTORY_SEPARATOR.$path : $path); } @@ -553,14 +525,14 @@ function wp_is_block_theme() } if (! function_exists('__return_true')) { - function __return_true() + function __return_true(): bool { return true; } } if (! function_exists('__return_false')) { - function __return_false() + function __return_false(): bool { return false; } @@ -570,7 +542,7 @@ function __return_false() * Helper function for route condition testing */ if (! function_exists('route_condition_test')) { - function route_condition_test($param = null) + function route_condition_test($param = null): bool { return false; // Default implementation, will be mocked in tests } @@ -610,7 +582,7 @@ function translate_with_gettext_context($text, $context, $domain = null) } if (! function_exists('abort')) { - function abort($code, $message = '') + function abort($code, $message = ''): void { throw new HttpException($code, $message); } @@ -991,14 +963,14 @@ function translate($text, $domain = 'default') if (! function_exists('_cleanup_header_comment')) { function _cleanup_header_comment($str) { - return isset(WP::$wpFunctions) ? WP::$wpFunctions->_cleanup_header_comment($str) : trim($str); + return isset(WP::$wpFunctions) ? WP::$wpFunctions->_cleanup_header_comment($str) : trim((string) $str); } } if (! function_exists('sanitize_key')) { function sanitize_key($key) { - return isset(WP::$wpFunctions) ? WP::$wpFunctions->sanitize_key($key) : strtolower(trim($key)); + return isset(WP::$wpFunctions) ? WP::$wpFunctions->sanitize_key($key) : strtolower(trim((string) $key)); } } @@ -1040,12 +1012,7 @@ public function add($hook, $callback): void if (! class_exists('TestContainer')) { class TestContainer { - private array $services; - - public function __construct(array $services = []) - { - $this->services = $services; - } + public function __construct(private array $services = []) {} public function get(string $serviceClass): ?object { @@ -1053,7 +1020,7 @@ public function get(string $serviceClass): ?object } // Added for compatibility with attribute tests - public function make($abstract, array $parameters = []) + public function make(string $abstract, array $parameters = []): ?object { return $this->get($abstract); } @@ -1068,7 +1035,7 @@ public function has(string $serviceClass): bool return isset($this->services[$serviceClass]); } - public function instance($abstract, $instance) + public function instance($abstract, $instance): void { $this->services[$abstract] = $instance; } @@ -1103,7 +1070,7 @@ public function __construct() // Laravel response helper function if (! function_exists('response')) { - function response($content = '', $status = 200, array $headers = []) + function response($content = '', $status = 200, array $headers = []): Response { return new Response($content, $status, $headers); } @@ -1293,7 +1260,7 @@ function check_ajax_referer($action = -1, $query_arg = false, $stop = true) if (! function_exists('sanitize_text_field')) { function sanitize_text_field($str) { - return isset(WP::$wpFunctions) ? WP::$wpFunctions->sanitize_text_field($str) : trim(strip_tags($str)); + return isset(WP::$wpFunctions) ? WP::$wpFunctions->sanitize_text_field($str) : trim(strip_tags((string) $str)); } } @@ -1306,14 +1273,14 @@ function wp_die($message = '', $title = '', $args = []) // WordPress escaping functions if (! function_exists('esc_attr')) { - function esc_attr($text) + function esc_attr($text): string { return htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8'); } } if (! function_exists('esc_html')) { - function esc_html($text) + function esc_html($text): string { return htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8'); } From 2bdca497bde685d965d80366fd8a06fd95bdf43d Mon Sep 17 00:00:00 2001 From: Olivier Gorzalka Date: Wed, 22 Apr 2026 16:41:52 +0200 Subject: [PATCH 2/2] fix: exclude helpers.php from Rector to prevent mock logic corruption Rector's closure refactoring rules can alter the semantics of Mockery withArgs() closures, causing test failures. The helpers.php file contains WordPress mock functions that should remain unchanged. --- rector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/rector.php b/rector.php index a1d81e53..f587882a 100644 --- a/rector.php +++ b/rector.php @@ -14,6 +14,7 @@ ]) ->withSkip([ AddOverrideAttributeToOverriddenMethodsRector::class, + __DIR__.'/tests/Unit/helpers.php', ]) ->withPreparedSets( deadCode: true,