diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 190b4da..c0fbc53 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -15,9 +15,10 @@ jobs: - ubuntu-latest php: - - "8.1" - "8.2" - "8.3" + - "8.4" + - "8.5" steps: - name: Checkout diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml deleted file mode 100644 index 1a7aa24..0000000 --- a/.github/workflows/docs-build.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: docs-build - -on: - release: - types: [published] - workflow_dispatch: - -jobs: - build-deploy: - runs-on: ubuntu-latest - steps: - - name: Build Docs - uses: dotkernel/documentation-theme/github-actions/docs@main - env: - DEPLOY_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000..9976515 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,50 @@ +on: + - push + +name: Run PHPStan checks + +jobs: + mutation: + name: PHPStan ${{ matrix.php }}-${{ matrix.os }} + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: + - ubuntu-latest + + php: + - "8.2" + - "8.3" + - "8.4" + - "8.5" + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: pcov + ini-values: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On + tools: composer:v2, cs2pr + + - name: Determine composer cache directory + run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV + + - name: Cache dependencies installed with composer + uses: actions/cache@v4 + with: + path: ${{ env.COMPOSER_CACHE_DIR }} + key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: | + php${{ matrix.php }}-composer- + + - name: Install dependencies with composer + run: composer install --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + - name: Run static analysis with PHPStan + run: vendor/bin/phpstan analyse diff --git a/README.md b/README.md index 8911399..29800d9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # dot-navigation ![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/dot-navigation) -![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-navigation/3.5.1) +![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-navigation/3.6.0) [![GitHub issues](https://img.shields.io/github/issues/dotkernel/dot-navigation)](https://github.com/dotkernel/dot-navigation/issues) [![GitHub forks](https://img.shields.io/github/forks/dotkernel/dot-navigation)](https://github.com/dotkernel/dot-navigation/network) @@ -10,21 +10,21 @@ [![Build Static](https://github.com/dotkernel/dot-navigation/actions/workflows/static-analysis.yml/badge.svg?branch=3.0)](https://github.com/dotkernel/dot-navigation/actions/workflows/static-analysis.yml) [![codecov](https://codecov.io/gh/dotkernel/dot-navigation/graph/badge.svg?token=AI9WFYDDX9)](https://codecov.io/gh/dotkernel/dot-navigation) +[![PHPStan](https://github.com/dotkernel/dot-navigation/actions/workflows/static-analysis.yml/badge.svg?branch=3.0)](https://github.com/dotkernel/dot-navigation/actions/workflows/static-analysis.yml) -[![SymfonyInsight](https://insight.symfony.com/projects/68b7c728-4cc9-40ac-a3be-cf17f9b2eaf1/big.svg)](https://insight.symfony.com/projects/68b7c728-4cc9-40ac-a3be-cf17f9b2eaf1) - -Allows you to easily define and parse menus inside templates, configuration based approach. +Allows you to easily define and parse menus inside templates, configuration-based approach. ## Installation -Run +Run the following Composer command: - composer require dotkernel/dot-navigation +```shell +composer require dotkernel/dot-navigation +``` Merge `ConfigProvider` to your application's configuration. -The package uses dot-helpers package, please merge dot-helpers `ConfigProvider` to your application's configuration -also, if it's not merged already! +The package uses dot-helpers package, please merge dot-helpers `ConfigProvider` to your application's configuration also, if it's not merged already. Register `NavigationMiddleware` in your middleware pipe between the routing and the dispatching middleware. @@ -34,21 +34,30 @@ Locate dot-navigation's distributable config file `vendor/dotkernel/dot-navigati ## Components -A menu, or navigation container, is a class implementing the \RecursiveIterator interface. It has a hierarchical structure, with nodes called pages(see the `Page` class) that may have children. It is basically a tree +A menu, or navigation container, is a class implementing the \RecursiveIterator interface. +It has a hierarchical structure, with nodes called pages (see the `Page` class) that may have children. +It is basically a tree. -A Page extends the NavigationContainer class. The NavigationContainer is the top most node which represents the entire menu. The children of this node are Page instances that defines each navigation item. +A Page extends the NavigationContainer class. +The NavigationContainer is the top most node which represents the entire menu. +The children of this node are Page instances that define each navigation item. -A page has a reference to its parent, and can have options and attributes. There are no limitation on what is accepted as options or attributes. +A page has a reference to its parent and can have options and attributes. +There is no limitation on what is accepted as options or attributes. -Options can be any piece of information that describes a page. Some predefined options exists, in order for the navigation module to work seamlessly with other dot modules. +Options can be any piece of information that describes a page. +Some predefined options exist in order for the navigation module to work seamlessly with other dot modules. -Attributes are key value pairs that defines the menu item. They are usually inserted as html attributes when parsing the menu, but of course, this is implementation specific. +Attributes are key value pairs that define the menu item. +They are usually inserted as HTML attributes when parsing the menu, but of course, this is implementation-specific. -A `NavigationService` class, is the service that handles all defined menu container. It can fetch the container from its provider, check if a page is active or not and get the page's generated URI. +A `NavigationService` class, is the service that handles all defined menu containers. +It can fetch the container from its provider, check if a page is active or not, and get the page's generated URI. ## Container providers -Each menu can be created from different sources. The responsibility of creating a menu container from the source falls on a container provider. +Each menu can be created from different sources. +The responsibility of creating a menu container from the source falls on a container provider. Each provider must implement the interface `ProviderInterface` and be registered in the ProviderPluginManager. @@ -56,15 +65,16 @@ We offer just one provider for now, `ArrayProvider`, that is able to fetch and c ## NavigationRenderer -Used to render the navigation container in a displayable format. It can render a simple HTML ul list or use a partial template, to render the menu in a template engine. +Used to render the navigation container in a displayable format. +It can render a simple HTML ul list or use a partial template to render the menu in a template engine. The partial method is usually the more flexible one, custom rules can be defined and checked in the template. -If you are using twig, there is already a twig extension provided in package dot-twigrenderer, that you can use to easily parse the menus inside your templates +If you are using twig, there is already a twig extension provided in package `dotkerel/dot-twigrenderer`, that you can use to easily parse the menus inside your templates. -When using the partial method, the template will receive as parameters the container, the navigation service and any extra parameters set by the developer. +When using the partial method, the template will receive as parameters the container, the navigation service, and any extra parameters set by the developer. -Navigation containers are referred, when parsed, by their name, as defined in the configuration file. +Navigation containers are referred to, when parsed, by their name, as defined in the configuration file. ## Required page options and attributes @@ -72,4 +82,4 @@ The following are options that each page should define in the configuration * `label` - the text of the menu item * `route` or `uri` - defines the route or link the menu item will have -* `permission` - can be used optionally, if authorization service is present, in order to omit menu items that are not authorized to visit. +* `permission` - can be used optionally if authorization service is present, to omit menu items that are not authorized to visit. diff --git a/SECURITY.md b/SECURITY.md index 5cef5a6..1b04845 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,17 +2,15 @@ ## Supported Versions - -| Version | Supported | PHP Version | -|---------|--------------------|---------------------------------------------------------------------------------------------------------------| -| 3.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-navigation/3.4.2)| - +| Version | Supported | PHP Version | +|---------|--------------------|----------------------------------------------------------------------------------------------------------------| +| 3.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-navigation/3.6.0) | +| <= 2.x | :x: | | ## Reporting Potential Security Issues -If you have encountered a potential security vulnerability in this project, -please report it to us at . We will work with you to -verify the vulnerability and patch it. +If you have encountered a potential security vulnerability in this project, please report it to us at . +We will work with you to verify the vulnerability and patch it. When reporting issues, please provide the following information: @@ -20,20 +18,12 @@ When reporting issues, please provide the following information: - A description indicating how to reproduce the issue - A summary of the security vulnerability and impact -We request that you contact us via the email address above and give the -project contributors a chance to resolve the vulnerability and issue a new -release prior to any public exposure; this helps protect the project's -users, and provides them with a chance to upgrade and/or update in order to -protect their applications. - +We request that you contact us via the email address above and give the project contributors a chance to resolve the vulnerability and issue a new release prior to any public exposure; +this helps protect the project's users and provides them with a chance to upgrade and/or update to protect their applications. ## Policy If we verify a reported security vulnerability, our policy is: -- We will patch the current release branch, as well as the immediate prior minor - release branch. - -- After patching the release branches, we will immediately issue new security - fix releases for each patched release branch. - +- We will patch the current release branch, as well as the immediate prior minor release branch. +- After patching the release branches, we will immediately issue new security fix releases for each patched release branch. diff --git a/composer.json b/composer.json index 46272b2..3cc3fcd 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "dotkernel/dot-navigation", "type": "library", - "description": "DotKernel navigation component", + "description": "Dotkernel navigation component", "license": "MIT", "homepage": "https://github.com/dotkernel/dot-navigation", "keywords": [ @@ -13,7 +13,7 @@ ], "authors": [ { - "name": "DotKernel Team", + "name": "Dotkernel Team", "email": "team@dotkernel.com" } ], @@ -24,7 +24,7 @@ } }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", "dotkernel/dot-authorization": "^3.4.1", "dotkernel/dot-helpers": "^3.5.1", "laminas/laminas-escaper": "^2.13.0", @@ -34,9 +34,10 @@ "psr/http-server-middleware": "^1.0.2" }, "require-dev": { - "laminas/laminas-coding-standard": "^2.5.0", - "phpunit/phpunit": "^10.4.2", - "vimeo/psalm": "^5.16.0" + "laminas/laminas-coding-standard": "^3.0.0", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.4.2" }, "autoload": { "psr-4": { @@ -51,12 +52,12 @@ "scripts": { "check": [ "@cs-check", - "@test" + "@test", + "@static-analysis" ], "cs-check": "phpcs", "cs-fix": "phpcbf", - "test": "phpunit --colors=always", - "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", - "static-analysis": "psalm --shepherd --stats" + "static-analysis": "phpstan analyse --memory-limit 1G", + "test": "phpunit --colors=always" } } diff --git a/docs/book/index.md b/docs/book/index.md deleted file mode 100644 index ae42a26..0000000 --- a/docs/book/index.md +++ /dev/null @@ -1 +0,0 @@ -../../README.md diff --git a/docs/book/v3/components.md b/docs/book/v3/components.md deleted file mode 100644 index 30b9399..0000000 --- a/docs/book/v3/components.md +++ /dev/null @@ -1,13 +0,0 @@ -# Components - -A menu, or navigation container, is a class implementing the \RecursiveIterator interface. It has a hierarchical structure, with nodes called pages(see the `Page` class) that may have children. It is basically a tree - -A Page extends the NavigationContainer class. The NavigationContainer is the top most node which represents the entire menu. The children of this node are Page instances that defines each navigation item. - -A page has a reference to its parent, and can have options and attributes. There are no limitation on what is accepted as options or attributes. - -Options can be any piece of information that describes a page. Some predefined options exists, in order for the navigation module to work seamlessly with other dot modules. - -Attributes are key value pairs that defines the menu item. They are usually inserted as html attributes when parsing the menu, but of course, this is implementation specific. - -A `NavigationService` class, is the service that handles all defined menu container. It can fetch the container from its provider, check if a page is active or not and get the page's generated URI. diff --git a/docs/book/v3/configuration.md b/docs/book/v3/configuration.md deleted file mode 100644 index 0b01125..0000000 --- a/docs/book/v3/configuration.md +++ /dev/null @@ -1,7 +0,0 @@ -# Configuration - -After installation, register `dot-navigation` in your project by adding the below line to your configuration aggregator (usually: `config/config.php`): - - Dot\Navigation\ConfigProvider::class, - -Locate dot-navigation's distributable config file `vendor/dotkernel/dot-navigation/config/autoload/navigation.global.php.dist` and duplicate it in your project as `config/autoload/navigation.global.php` diff --git a/docs/book/v3/container-providers.md b/docs/book/v3/container-providers.md deleted file mode 100644 index c8b4825..0000000 --- a/docs/book/v3/container-providers.md +++ /dev/null @@ -1,7 +0,0 @@ -# Container providers - -Each menu can be created from different sources. The responsibility of creating a menu container from the source falls on a container provider. - -Each provider must implement the interface `ProviderInterface` and be registered in the ProviderPluginManager. - -We offer just one provider for now, `ArrayProvider`, that is able to fetch and create a menu container from a php array that is defined in the configuration file. diff --git a/docs/book/v3/installation.md b/docs/book/v3/installation.md deleted file mode 100644 index b4eb651..0000000 --- a/docs/book/v3/installation.md +++ /dev/null @@ -1,5 +0,0 @@ -# Installation - -Install `dotkernel/dot-navigation` by executing the following Composer command: - - composer require dotkernel/dot-navigation diff --git a/docs/book/v3/navigation-renderer.md b/docs/book/v3/navigation-renderer.md deleted file mode 100644 index cb66fac..0000000 --- a/docs/book/v3/navigation-renderer.md +++ /dev/null @@ -1,11 +0,0 @@ -# NavigationRenderer - -Used to render the navigation container in a displayable format. It can render a simple HTML ul list or use a partial template, to render the menu in a template engine. - -The partial method is usually the more flexible one, custom rules can be defined and checked in the template. - -If you are using twig, there is already a twig extension provided in package dot-twigrenderer, that you can use to easily parse the menus inside your templates - -When using the partial method, the template will receive as parameters the container, the navigation service and any extra parameters set by the developer. - -Navigation containers are referred, when parsed, by their name, as defined in the configuration file. diff --git a/docs/book/v3/overview.md b/docs/book/v3/overview.md deleted file mode 100644 index 510ecd9..0000000 --- a/docs/book/v3/overview.md +++ /dev/null @@ -1,3 +0,0 @@ -# Overview - -`dot-navigation` is DotKernel's component that allows you to easily define and parse menus inside templates, using a configuration based approach. diff --git a/docs/book/v3/required-page-options.md b/docs/book/v3/required-page-options.md deleted file mode 100644 index 36dd780..0000000 --- a/docs/book/v3/required-page-options.md +++ /dev/null @@ -1,7 +0,0 @@ -# Required page options and attributes - -The following are options that each page should define in the configuration - -* `label` - the text of the menu item -* `route` or `uri` - defines the route or link the menu item will have -* `permission` - can be used optionally, if authorization service is present, in order to omit menu items that are not authorized to visit. diff --git a/docs/book/v3/usage.md b/docs/book/v3/usage.md deleted file mode 100644 index 666ce60..0000000 --- a/docs/book/v3/usage.md +++ /dev/null @@ -1,58 +0,0 @@ -# Usage - -Below is an example with content of `config/autoload/navigation.global.php`. - -Here we can change or modify the menu structure that will be rendered in the application we are building. - -```php - [ - //enable menu item active if any child is active - 'active_recursion' => true, - - //map a provider name to its config - 'containers' => [ - 'default' => [ - 'type' => 'ArrayProvider', - 'options' => [ - 'items' => [ - [ - 'options' => [ - 'label' => 'Menu #1', - 'route' => [ - 'route_name' => 'home', - 'route_params' => [], - 'query_params' => [], - 'fragment_id' => null, - 'options' => [], - - //the below parameters are not used in route generation - //they are used in finding if a page is active by omitting some parameters from the check - 'ignore_params' => [] - ], - ], - 'attributes' => [ - 'name' => 'Menu #1', - ] - ], - [ - 'options' => [ - 'label' => 'Menu #2', - 'route' => ['route_name' => 'home'], - ], - 'attributes' => [ - 'name' => 'Menu #1', - ] - ] - ], - ], - ], - ], - - //register custom providers here - 'provider_manager' => [], - ], -]; -``` diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 38b44dd..0000000 --- a/mkdocs.yml +++ /dev/null @@ -1,23 +0,0 @@ -docs_dir: docs/book -site_dir: docs/html -extra: - project: Packages - current_version: v3 - versions: - - v3 -nav: - - Home: index.md - - v3: - - Overview: v3/overview.md - - Configuration: v3/configuration.md - - Installation: v3/installation.md - - Components: v3/components.md - - "Container Providers": v3/container-providers.md - - "Navigation Renderer": v3/navigation-renderer.md - - "Required Page Options": v3/required-page-options.md - - Usage: v3/usage.md -site_name: dot-navigation -site_description: "DotKernel's component that allows you to easily define and parse menus inside templates, using a configuration based approach." -repo_url: "https://github.com/dotkernel/dot-navigation" -plugins: - - search diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..349be25 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,8 @@ +includes: + - vendor/phpstan/phpstan-phpunit/extension.neon +parameters: + level: 5 + paths: + - src + - test + treatPhpDocTypesAsCertain: false diff --git a/psalm-baseline.xml b/psalm-baseline.xml deleted file mode 100644 index 915d9e0..0000000 --- a/psalm-baseline.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - IsAllowedFilter - - - - - RecursiveIterator - - - - - ProviderPluginManager - - - diff --git a/psalm.xml b/psalm.xml deleted file mode 100644 index 9dd8f07..0000000 --- a/psalm.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - diff --git a/src/Provider/ProviderPluginManager.php b/src/Provider/ProviderPluginManager.php index 4ea540c..0f4a0d6 100644 --- a/src/Provider/ProviderPluginManager.php +++ b/src/Provider/ProviderPluginManager.php @@ -9,7 +9,7 @@ class ProviderPluginManager extends AbstractPluginManager { - /** @var string $instanceOf */ + /** @inheritDoc */ protected $instanceOf = ProviderInterface::class; /** @var array */ diff --git a/src/View/AbstractNavigationRenderer.php b/src/View/AbstractNavigationRenderer.php index 8f14c4d..23822ac 100644 --- a/src/View/AbstractNavigationRenderer.php +++ b/src/View/AbstractNavigationRenderer.php @@ -98,11 +98,11 @@ protected function getContainer(string|NavigationContainer $container): Navigati { if (is_string($container)) { return $this->navigation->getContainer($container); - } elseif (! $container instanceof NavigationContainer) { + } elseif ($container instanceof NavigationContainer) { + return $container; + } else { throw new RuntimeException('Container must be a string or an instance of ' . NavigationContainer::class); } - - return $container; } protected function cleanAttributes(array $input, array $valid): array diff --git a/test/Exception/InvalidArgumentExceptionTest.php b/test/Exception/InvalidArgumentExceptionTest.php index d283fd9..87c6373 100644 --- a/test/Exception/InvalidArgumentExceptionTest.php +++ b/test/Exception/InvalidArgumentExceptionTest.php @@ -13,7 +13,6 @@ class InvalidArgumentExceptionTest extends TestCase public function testWillReturnCorrectInstances(): void { $exception = new InvalidArgumentException('test'); - $this->assertInstanceOf(InvalidArgumentException::class, $exception); - $this->assertInstanceOf(ExceptionInterface::class, $exception); + $this->assertContainsOnlyInstancesOf(ExceptionInterface::class, [$exception]); } } diff --git a/test/Exception/RuntimeExceptionTest.php b/test/Exception/RuntimeExceptionTest.php index 9d46320..f4323f1 100644 --- a/test/Exception/RuntimeExceptionTest.php +++ b/test/Exception/RuntimeExceptionTest.php @@ -18,7 +18,6 @@ public function setUp(): void public function testWillReturnCorrectInstances(): void { $exception = new RuntimeException('test'); - $this->assertInstanceOf(RuntimeException::class, $exception); - $this->assertInstanceOf(ExceptionInterface::class, $exception); + $this->assertContainsOnlyInstancesOf(ExceptionInterface::class, [$exception]); } } diff --git a/test/Factory/NavigationMiddlewareFactoryTest.php b/test/Factory/NavigationMiddlewareFactoryTest.php index d075b39..79463c6 100644 --- a/test/Factory/NavigationMiddlewareFactoryTest.php +++ b/test/Factory/NavigationMiddlewareFactoryTest.php @@ -54,6 +54,6 @@ public function testWillCreateMiddleware(): void ->willReturn($navigation); $middleware = (new NavigationMiddlewareFactory())($container); - $this->assertInstanceOf(NavigationMiddleware::class, $middleware); + $this->assertContainsOnlyInstancesOf(NavigationMiddleware::class, [$middleware]); } } diff --git a/test/Factory/NavigationOptionsFactoryTest.php b/test/Factory/NavigationOptionsFactoryTest.php index 6455d8d..4e6d4ed 100644 --- a/test/Factory/NavigationOptionsFactoryTest.php +++ b/test/Factory/NavigationOptionsFactoryTest.php @@ -79,6 +79,6 @@ public function testWillCreateNavigationOptions(): void ]); $options = (new NavigationOptionsFactory())($container); - $this->assertInstanceOf(NavigationOptions::class, $options); + $this->assertContainsOnlyInstancesOf(NavigationOptions::class, [$options]); } } diff --git a/test/Factory/NavigationRendererFactoryTest.php b/test/Factory/NavigationRendererFactoryTest.php index 38e91ff..50c6c7b 100644 --- a/test/Factory/NavigationRendererFactoryTest.php +++ b/test/Factory/NavigationRendererFactoryTest.php @@ -97,6 +97,6 @@ public function testWillNotCreateNavigationRenderer(): void ]); $renderer = (new NavigationRendererFactory())($container); - $this->assertInstanceOf(NavigationRenderer::class, $renderer); + $this->assertContainsOnlyInstancesOf(NavigationRenderer::class, [$renderer]); } } diff --git a/test/Factory/NavigationServiceFactoryTest.php b/test/Factory/NavigationServiceFactoryTest.php index fbe9326..56adbcb 100644 --- a/test/Factory/NavigationServiceFactoryTest.php +++ b/test/Factory/NavigationServiceFactoryTest.php @@ -101,7 +101,7 @@ public function testWillCreateNavigationServiceWithoutAuthorizationInterface(): ]); $service = (new NavigationServiceFactory())($container); - $this->assertInstanceOf(Navigation::class, $service); + $this->assertContainsOnlyInstancesOf(Navigation::class, [$service]); } /** @@ -132,6 +132,6 @@ public function testWillCreateNavigationServiceWithAuthorizationInterface(): voi ]); $service = (new NavigationServiceFactory())($container); - $this->assertInstanceOf(Navigation::class, $service); + $this->assertContainsOnlyInstancesOf(Navigation::class, [$service]); } } diff --git a/test/Factory/ProviderPluginManagerFactoryTest.php b/test/Factory/ProviderPluginManagerFactoryTest.php index e79cfa5..db5ca75 100644 --- a/test/Factory/ProviderPluginManagerFactoryTest.php +++ b/test/Factory/ProviderPluginManagerFactoryTest.php @@ -106,6 +106,6 @@ public function testWillCreateProviderPluginManager(): void ]); $manager = (new ProviderPluginManagerFactory())($container); - $this->assertInstanceOf(ProviderPluginManager::class, $manager); + $this->assertContainsOnlyInstancesOf(ProviderPluginManager::class, [$manager]); } } diff --git a/test/Filter/IsAllowedFilterTest.php b/test/Filter/IsAllowedFilterTest.php index 6f9f3ed..29c342b 100644 --- a/test/Filter/IsAllowedFilterTest.php +++ b/test/Filter/IsAllowedFilterTest.php @@ -74,7 +74,7 @@ public function testGetChildren(): void $navigation = $this->createMock(NavigationInterface::class); $filter = new IsAllowedFilter(new NavigationContainer([new Page()]), $navigation); - $this->assertInstanceOf(IsAllowedFilter::class, $filter->getChildren()); + $this->assertContainsOnlyInstancesOf(IsAllowedFilter::class, [$filter->getChildren()]); } /** @@ -86,6 +86,6 @@ public function testWillCreateFilter(): void $navigation = $this->createMock(NavigationInterface::class); $filter = new IsAllowedFilter($iterator, $navigation); - $this->assertInstanceOf(IsAllowedFilter::class, $filter); + $this->assertContainsOnlyInstancesOf(IsAllowedFilter::class, [$filter]); } } diff --git a/test/NavigationMiddlewareTest.php b/test/NavigationMiddlewareTest.php index 2063f7a..9dbe51b 100644 --- a/test/NavigationMiddlewareTest.php +++ b/test/NavigationMiddlewareTest.php @@ -22,7 +22,7 @@ public function testWillCreateMiddleware(): void $navigation = $this->createMock(NavigationInterface::class); $middleware = new NavigationMiddleware($navigation); - $this->assertInstanceOf(NavigationMiddleware::class, $middleware); + $this->assertContainsOnlyInstancesOf(NavigationMiddleware::class, [$middleware]); } /** @@ -36,6 +36,6 @@ public function testWillProcessRequest(): void $middleware = new NavigationMiddleware($navigation); $response = $middleware->process($request, $handler); - $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertContainsOnlyInstancesOf(ResponseInterface::class, [$response]); } } diff --git a/test/Options/NavigationOptionsTest.php b/test/Options/NavigationOptionsTest.php index dfe4f2d..0f3fd73 100644 --- a/test/Options/NavigationOptionsTest.php +++ b/test/Options/NavigationOptionsTest.php @@ -12,7 +12,7 @@ class NavigationOptionsTest extends TestCase public function testWillCreateNavigationOptions(): void { $options = new NavigationOptions(); - $this->assertInstanceOf(NavigationOptions::class, $options); + $this->assertContainsOnlyInstancesOf(NavigationOptions::class, [$options]); } public function testAccessors(): void diff --git a/test/PageTest.php b/test/PageTest.php index cc6601e..8f93bfd 100644 --- a/test/PageTest.php +++ b/test/PageTest.php @@ -11,8 +11,7 @@ class PageTest extends TestCase { public function testWillCreatePage(): void { - $page = new Page(); - $this->assertInstanceOf(Page::class, $page); + $this->assertContainsOnlyInstancesOf(Page::class, [new Page()]); } public function testParentAccessors(): void diff --git a/test/Provider/ArrayProviderTest.php b/test/Provider/ArrayProviderTest.php index 329194e..b718e3e 100644 --- a/test/Provider/ArrayProviderTest.php +++ b/test/Provider/ArrayProviderTest.php @@ -13,7 +13,7 @@ class ArrayProviderTest extends TestCase public function testWillCreateArrayProvider(): void { $provider = new ArrayProvider(); - $this->assertInstanceOf(ArrayProvider::class, $provider); + $this->assertContainsOnlyInstancesOf(ArrayProvider::class, [$provider]); } public function testAccessors(): void @@ -40,7 +40,7 @@ public function testGetContainer(): void 'items' => $pageSpecs, ]); $container = $provider->getContainer(); - $this->assertInstanceOf(NavigationContainer::class, $container); + $this->assertContainsOnlyInstancesOf(NavigationContainer::class, [$container]); $this->assertCount(2, $container->getChildren()); } } diff --git a/test/Provider/FactoryTest.php b/test/Provider/FactoryTest.php index 1570f7e..af7ed9d 100644 --- a/test/Provider/FactoryTest.php +++ b/test/Provider/FactoryTest.php @@ -26,7 +26,7 @@ public function testWillCreateFactoryWithoutProviderPluginManager(): void $container = $this->createMock(ContainerInterface::class); $factory = new Factory($container); - $this->assertInstanceOf(Factory::class, $factory); + $this->assertContainsOnlyInstancesOf(Factory::class, [$factory]); } /** @@ -38,7 +38,7 @@ public function testWillCreateFactoryWithProviderPluginManager(): void $manager = $this->createMock(ProviderPluginManager::class); $factory = new Factory($container, $manager); - $this->assertInstanceOf(Factory::class, $factory); + $this->assertContainsOnlyInstancesOf(Factory::class, [$factory]); } /** @@ -85,7 +85,7 @@ public function testFactoryWillCreateProviderWithValidProviderTypeAndNoOptions() $provider = $factory->create([ 'type' => ArrayProvider::class, ]); - $this->assertInstanceOf(ProviderInterface::class, $provider); + $this->assertContainsOnlyInstancesOf(ProviderInterface::class, [$provider]); } /** @@ -100,7 +100,7 @@ public function testFactoryWillCreateProviderWithValidProviderTypeAndOptions(): 'type' => ArrayProvider::class, 'options' => [], ]); - $this->assertInstanceOf(ProviderInterface::class, $provider); + $this->assertContainsOnlyInstancesOf(ProviderInterface::class, [$provider]); } /** @@ -111,7 +111,7 @@ public function testFactoryWillGetProviderPluginManagerWithoutInitialProviderPlu $container = $this->createMock(ContainerInterface::class); $factory = new Factory($container); - $this->assertInstanceOf(ProviderPluginManager::class, $factory->getProviderPluginManager()); + $this->assertContainsOnlyInstancesOf(ProviderPluginManager::class, [$factory->getProviderPluginManager()]); } /** @@ -123,6 +123,6 @@ public function testFactoryWillGetProviderPluginManagerWithInitialProviderPlugin $manager = $this->createMock(ProviderPluginManager::class); $factory = new Factory($container, $manager); - $this->assertInstanceOf(ProviderPluginManager::class, $factory->getProviderPluginManager()); + $this->assertContainsOnlyInstancesOf(ProviderPluginManager::class, [$factory->getProviderPluginManager()]); } } diff --git a/test/Service/NavigationTest.php b/test/Service/NavigationTest.php index 01c6574..865fb24 100644 --- a/test/Service/NavigationTest.php +++ b/test/Service/NavigationTest.php @@ -14,9 +14,11 @@ use Dot\Navigation\Provider\FactoryInterface; use Dot\Navigation\Service\Navigation; use Dot\Navigation\Service\NavigationInterface; +use Mezzio\Router\Route; use Mezzio\Router\RouteResult; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; +use Psr\Http\Server\MiddlewareInterface; class NavigationTest extends TestCase { @@ -25,12 +27,12 @@ class NavigationTest extends TestCase */ public function testNavigationWillInitialize(): void { - $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); - $options = $this->createMock(NavigationOptions::class); + $factory = $this->createMock(FactoryInterface::class); + $routeHelper = $this->createMock(RouteHelper::class); + $options = $this->createMock(NavigationOptions::class); - $navigation = new Navigation($factory, $route, $options); - $this->assertInstanceOf(NavigationInterface::class, $navigation); + $navigation = new Navigation($factory, $routeHelper, $options); + $this->assertContainsOnlyInstancesOf(NavigationInterface::class, [$navigation]); } /** @@ -39,11 +41,11 @@ public function testNavigationWillInitialize(): void public function testAccessors(): void { $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); + $routeHelper = $this->createMock(RouteHelper::class); $options = $this->createMock(NavigationOptions::class); - $routeResult = $this->createMock(RouteResult::class); + $routeResult = RouteResult::fromRouteFailure([]); - $navigation = new Navigation($factory, $route, $options); + $navigation = new Navigation($factory, $routeHelper, $options); $this->assertNull($navigation->getRouteResult()); $navigation->setRouteResult($routeResult); $this->assertInstanceOf(RouteResult::class, $navigation->getRouteResult()); @@ -57,13 +59,13 @@ public function testAccessors(): void */ public function testNavigationWillNotGetInvalidContainer(): void { - $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); - $options = $this->createMock(NavigationOptions::class); + $factory = $this->createMock(FactoryInterface::class); + $routeHelper = $this->createMock(RouteHelper::class); + $options = $this->createMock(NavigationOptions::class); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Container `test` is not defined'); - $navigation = new Navigation($factory, $route, $options); + $navigation = new Navigation($factory, $routeHelper, $options); $navigation->getContainer('test'); } @@ -72,8 +74,8 @@ public function testNavigationWillNotGetInvalidContainer(): void */ public function testNavigationWillGetValidContainer(): void { - $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); + $factory = $this->createMock(FactoryInterface::class); + $routeHelper = $this->createMock(RouteHelper::class); $options = new NavigationOptions([ 'containers' => [ @@ -83,8 +85,8 @@ public function testNavigationWillGetValidContainer(): void ], ]); - $navigation = new Navigation($factory, $route, $options); - $this->assertInstanceOf(NavigationContainer::class, $navigation->getContainer('default')); + $navigation = new Navigation($factory, $routeHelper, $options); + $this->assertContainsOnlyInstancesOf(NavigationContainer::class, [$navigation->getContainer('default')]); } /** @@ -92,11 +94,11 @@ public function testNavigationWillGetValidContainer(): void */ public function testIsAllowedWillReturnTrueWithoutAuthorization(): void { - $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); - $options = $this->createMock(NavigationOptions::class); + $factory = $this->createMock(FactoryInterface::class); + $routeHelper = $this->createMock(RouteHelper::class); + $options = $this->createMock(NavigationOptions::class); - $navigation = new Navigation($factory, $route, $options); + $navigation = new Navigation($factory, $routeHelper, $options); $this->assertTrue($navigation->isAllowed(new Page())); } @@ -106,11 +108,11 @@ public function testIsAllowedWillReturnTrueWithoutAuthorization(): void public function testIsAllowedWillReturnTrueWithAuthorizationWhenPageHasNoPermission(): void { $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); + $routeHelper = $this->createMock(RouteHelper::class); $options = $this->createMock(NavigationOptions::class); $authorization = $this->createMock(AuthorizationInterface::class); - $navigation = new Navigation($factory, $route, $options, $authorization); + $navigation = new Navigation($factory, $routeHelper, $options, $authorization); $this->assertTrue($navigation->isAllowed(new Page())); } @@ -120,7 +122,7 @@ public function testIsAllowedWillReturnTrueWithAuthorizationWhenPageHasNoPermiss public function testIsAllowedWillReturnTrueWithAuthorizationWhenPageHasNoRoles(): void { $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); + $routeHelper = $this->createMock(RouteHelper::class); $options = $this->createMock(NavigationOptions::class); $authorization = $this->createMock(AuthorizationInterface::class); @@ -128,7 +130,7 @@ public function testIsAllowedWillReturnTrueWithAuthorizationWhenPageHasNoRoles() $page = new Page(); $page->setOption('permission', ''); - $navigation = new Navigation($factory, $route, $options, $authorization); + $navigation = new Navigation($factory, $routeHelper, $options, $authorization); $this->assertTrue($navigation->isAllowed($page)); } @@ -138,7 +140,7 @@ public function testIsAllowedWillReturnTrueWithAuthorizationWhenPageHasNoRoles() public function testIsAllowedWillReturnTrueWithAuthorizationWhenPageHasPermissionsAndRoles(): void { $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); + $routeHelper = $this->createMock(RouteHelper::class); $options = $this->createMock(NavigationOptions::class); $authorization = $this->createMock(AuthorizationInterface::class); @@ -147,7 +149,7 @@ public function testIsAllowedWillReturnTrueWithAuthorizationWhenPageHasPermissio $page = new Page(); $page->setOption('permission', ''); $page->setOption('roles', []); - $navigation = new Navigation($factory, $route, $options, $authorization); + $navigation = new Navigation($factory, $routeHelper, $options, $authorization); $this->assertTrue($navigation->isAllowed($page)); } @@ -156,11 +158,11 @@ public function testIsAllowedWillReturnTrueWithAuthorizationWhenPageHasPermissio */ public function testIsActiveWillCacheResults(): void { - $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); - $options = $this->createMock(NavigationOptions::class); + $factory = $this->createMock(FactoryInterface::class); + $routeHelper = $this->createMock(RouteHelper::class); + $options = $this->createMock(NavigationOptions::class); - $navigation = new Navigation($factory, $route, $options); + $navigation = new Navigation($factory, $routeHelper, $options); $this->assertIsArray($navigation->getIsActiveCache()); $this->assertEmpty($navigation->getIsActiveCache()); $navigation->isActive(new Page()); @@ -173,11 +175,11 @@ public function testIsActiveWillCacheResults(): void */ public function testIsActiveWillReturnFalseWithoutRouteResult(): void { - $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); - $options = $this->createMock(NavigationOptions::class); + $factory = $this->createMock(FactoryInterface::class); + $routeHelper = $this->createMock(RouteHelper::class); + $options = $this->createMock(NavigationOptions::class); - $navigation = new Navigation($factory, $route, $options); + $navigation = new Navigation($factory, $routeHelper, $options); $this->assertFalse($navigation->isActive(new Page())); } @@ -187,13 +189,11 @@ public function testIsActiveWillReturnFalseWithoutRouteResult(): void public function testIsActiveWillReturnFalseWithoutSuccessfulRouteResult(): void { $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); + $routeHelper = $this->createMock(RouteHelper::class); $options = $this->createMock(NavigationOptions::class); - $routeResult = $this->createMock(RouteResult::class); + $routeResult = RouteResult::fromRouteFailure([]); - $routeResult->expects($this->once())->method('isSuccess')->willReturn(false); - - $navigation = new Navigation($factory, $route, $options); + $navigation = new Navigation($factory, $routeHelper, $options); $navigation->setRouteResult($routeResult); $this->assertFalse($navigation->isActive(new Page())); } @@ -204,13 +204,16 @@ public function testIsActiveWillReturnFalseWithoutSuccessfulRouteResult(): void public function testIsActiveWillReturnFalseWhenPageHasNoRoute(): void { $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); + $routeHelper = $this->createMock(RouteHelper::class); $options = $this->createMock(NavigationOptions::class); - $routeResult = $this->createMock(RouteResult::class); - - $routeResult->expects($this->once())->method('isSuccess')->willReturn(true); - - $navigation = new Navigation($factory, $route, $options); + $routeResult = RouteResult::fromRoute( + new Route( + 'path', + $this->createMock(MiddlewareInterface::class), + ) + ); + + $navigation = new Navigation($factory, $routeHelper, $options); $navigation->setRouteResult($routeResult); $this->assertFalse($navigation->isActive(new Page())); } @@ -221,18 +224,22 @@ public function testIsActiveWillReturnFalseWhenPageHasNoRoute(): void public function testIsActiveWillReturnTrueWhenRequestedRouteMatchesPageRoute(): void { $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); + $routeHelper = $this->createMock(RouteHelper::class); $options = $this->createMock(NavigationOptions::class); - $routeResult = $this->createMock(RouteResult::class); - - $routeResult->expects($this->once())->method('isSuccess')->willReturn(true); - $routeResult->expects($this->once())->method('getMatchedRouteName')->willReturn('test'); + $routeResult = RouteResult::fromRoute( + new Route( + 'path', + $this->createMock(MiddlewareInterface::class), + Route::HTTP_METHOD_ANY, + 'test' + ) + ); $page = new Page(); $page->setOption('route', [ 'route_name' => 'test', ]); - $navigation = new Navigation($factory, $route, $options); + $navigation = new Navigation($factory, $routeHelper, $options); $navigation->setRouteResult($routeResult); $navigation->setIsActiveRecursion(false); $this->assertTrue($navigation->isActive($page)); @@ -244,12 +251,16 @@ public function testIsActiveWillReturnTrueWhenRequestedRouteMatchesPageRoute(): public function testIsActiveWillReturnTrueWhenRequestedRouteMatchesChildPageRoute(): void { $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); + $routeHelper = $this->createMock(RouteHelper::class); $options = $this->createMock(NavigationOptions::class); - $routeResult = $this->createMock(RouteResult::class); - - $routeResult->expects($this->any())->method('isSuccess')->willReturn(true); - $routeResult->expects($this->any())->method('getMatchedRouteName')->willReturn('child'); + $routeResult = RouteResult::fromRoute( + new Route( + 'path', + $this->createMock(MiddlewareInterface::class), + Route::HTTP_METHOD_ANY, + 'child' + ) + ); $childPage = new Page(); $childPage->setOption('route', [ @@ -260,7 +271,7 @@ public function testIsActiveWillReturnTrueWhenRequestedRouteMatchesChildPageRout 'route_name' => 'parent', ]); $parentPage->addPage($childPage); - $navigation = new Navigation($factory, $route, $options); + $navigation = new Navigation($factory, $routeHelper, $options); $navigation->setRouteResult($routeResult); $navigation->setIsActiveRecursion(true); $this->assertTrue($navigation->isActive($parentPage)); @@ -271,11 +282,11 @@ public function testIsActiveWillReturnTrueWhenRequestedRouteMatchesChildPageRout */ public function testGetHrefWillCacheResults(): void { - $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); - $options = $this->createMock(NavigationOptions::class); + $factory = $this->createMock(FactoryInterface::class); + $routeHelper = $this->createMock(RouteHelper::class); + $options = $this->createMock(NavigationOptions::class); - $navigation = new Navigation($factory, $route, $options); + $navigation = new Navigation($factory, $routeHelper, $options); $this->assertIsArray($navigation->getHrefCache()); $this->assertEmpty($navigation->getHrefCache()); @@ -299,11 +310,11 @@ public function testGetHrefWillCacheResults(): void */ public function testWillNotGetHrefForInvalidPage(): void { - $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); - $options = $this->createMock(NavigationOptions::class); + $factory = $this->createMock(FactoryInterface::class); + $routeHelper = $this->createMock(RouteHelper::class); + $options = $this->createMock(NavigationOptions::class); - $navigation = new Navigation($factory, $route, $options); + $navigation = new Navigation($factory, $routeHelper, $options); $this->expectException(RuntimeException::class); $this->expectExceptionMessageMatches('/^Unable to assemble href for navigation page.*/'); @@ -316,11 +327,11 @@ public function testWillNotGetHrefForInvalidPage(): void */ public function testWillGetHrefForValidPage(): void { - $factory = $this->createMock(FactoryInterface::class); - $route = $this->createMock(RouteHelper::class); - $options = $this->createMock(NavigationOptions::class); + $factory = $this->createMock(FactoryInterface::class); + $routeHelper = $this->createMock(RouteHelper::class); + $options = $this->createMock(NavigationOptions::class); - $navigation = new Navigation($factory, $route, $options); + $navigation = new Navigation($factory, $routeHelper, $options); $page = new Page(); $page->setOption('uri', 'page1'); diff --git a/test/View/NavigationRendererTest.php b/test/View/NavigationRendererTest.php index 12d25da..5a3ba21 100644 --- a/test/View/NavigationRendererTest.php +++ b/test/View/NavigationRendererTest.php @@ -24,7 +24,7 @@ public function testWillCreateNavigationRenderer(): void $options = $this->createMock(NavigationOptions::class); $renderer = new NavigationRenderer($navigation, $template, $options); - $this->assertInstanceOf(NavigationRenderer::class, $renderer); + $this->assertContainsOnlyInstancesOf(NavigationRenderer::class, [$renderer]); } /**