diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index deef99c..3633fe5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,17 +7,17 @@ on: branches: [ master ] jobs: - build: + ci: runs-on: ubuntu-latest strategy: matrix: - php-versions: [ '8.1', '8.2', '8.3' ] + php-versions: [ '8.3', '8.4' ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: - fetch-depth: 0 # To avoid "Shallow clone detected" error in SonarCloud report + fetch-depth: 0 #~ PHP Setup - name: Setup PHP ${{ matrix.php-versions }} @@ -48,30 +48,18 @@ jobs: #~ CI part - name: Dependencies - run: make deps + run: make php/deps - name: Check Code Style - run: make phpcs + run: make php/check - name: Units Tests - run: make tests + run: make php/tests - name: Fix unit tests report path run: | sed -i 's+'$GITHUB_WORKSPACE'+/github/workspace+g' build/reports/phpunit/clover.xml sed -i 's+'$GITHUB_WORKSPACE'+/github/workspace+g' build/reports/phpunit/unit.xml - - name: PHP 8.1 Compatibility - run: make php81compatibility - - - name: PHP 8.3 Compatibility - run: make php83compatibility - - name: PHP Static Analyze - run: make analyze - - - name: SonarCloud Scan - uses: SonarSource/sonarcloud-github-action@v2.1.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: make php/analyze diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml new file mode 100644 index 0000000..8aca7aa --- /dev/null +++ b/.github/workflows/sonarcloud.yml @@ -0,0 +1,46 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + sonarcloud: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # To avoid "Shallow clone detected" error in SonarCloud report + + #~ PHP Setup + - name: Setup PHP 8.3 # Minimum version + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + + #~ Composer Cache + - name: Get Composer Cache Directory + id: composer-cache + run: | + echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Install dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: make install + + - name: Units Tests + run: make php/tests # To generate the coverage report + + - name: SonarQube Scan + uses: SonarSource/sonarqube-scan-action@v5.2.0 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cbe1d0..f0c9e26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [7.0.0] - 2025-08-15 +[7.0.0]: https://github.com/eureka-framework/component-web/compare/6.0.0...7.0.0 +### Added +- Now support PHP 8.4 +### Removed +- Drop PHP 8.1 & 8.2 support +### Changed +- Update CI +- Fix from PHPStan +- Improve types in phpdoc + + +--- + ## [6.0.0] - 2024-03-12 [6.0.0]: https://github.com/eureka-framework/component-web/compare/5.3.0...6.0.0 ### Changed diff --git a/Makefile b/Makefile index b3857ec..28d874c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,8 @@ -.PHONY: validate install update deps phpcs phpcsf php81compatibility php83compatibility phpstan analyze tests testdox ci clean build/reports/phpunit build/reports/phpcs build/reports/phpstan +.PHONY: validate install update php/deps php/check php/fix php/min-compatibility php/max-compatibility php/phpstan php/analyze php/tests php/test php/testdox ci clean +COMPOSER_BIN := composer +PHP_VERSION_MIN := 8.3 +PHP_VERSION_MAX := 8.4 define header = @if [ -t 1 ]; then printf "\n\e[37m\e[100m \e[104m $(1) \e[0m\n"; else printf "\n### $(1)\n"; fi endef @@ -7,78 +10,71 @@ endef #~ Composer dependency validate: $(call header,Composer Validation) - @composer validate + @${COMPOSER_BIN} validate install: $(call header,Composer Install) - @composer install + @${COMPOSER_BIN} install update: $(call header,Composer Update) - @composer update - @composer bump --dev-only + @${COMPOSER_BIN} update + @${COMPOSER_BIN} bump --dev-only composer.lock: install #~ Vendor binaries dependencies -vendor/bin/php-cs-fixer: -vendor/bin/phpstan: -vendor/bin/phpunit: +vendor/bin/php-cs-fixer: composer.lock +vendor/bin/phpstan: composer.lock +vendor/bin/phpunit: composer.lock #~ Report directories dependencies build/reports/phpunit: @mkdir -p build/reports/phpunit -build/reports/phpcs: - @mkdir -p build/reports/cs - build/reports/phpstan: @mkdir -p build/reports/phpstan #~ main commands -deps: composer.json +php/deps: composer.json $(call header,Checking Dependencies) - @XDEBUG_MODE=off ./vendor/bin/composer-require-checker check + @XDEBUG_MODE=off ./vendor/bin/composer-dependency-analyser --config ./ci/composer-dependency-analyser.php # for shadow, unused required dependencies and ext-* missing dependencies -phpcs: vendor/bin/php-cs-fixer build/reports/phpcs +php/check: vendor/bin/php-cs-fixer $(call header,Checking Code Style) - @./vendor/bin/php-cs-fixer check - -phpcsf: vendor/bin/php-cs-fixer + @./vendor/bin/php-cs-fixer check -v --diff +php/fix: vendor/bin/php-cs-fixer $(call header,Fixing Code Style) @./vendor/bin/php-cs-fixer fix -v -php81compatibility: vendor/bin/phpstan build/reports/phpstan - $(call header,Checking PHP 8.1 compatibility) - @XDEBUG_MODE=off ./vendor/bin/phpstan analyse --configuration=./ci/php81-compatibility.neon --error-format=table +php/min-compatibility: vendor/bin/phpstan build/reports/phpstan + $(call header,Checking PHP ${PHP_VERSION_MIN} compatibility) + @XDEBUG_MODE=off ./vendor/bin/phpstan analyse --configuration=./ci/phpmin-compatibility.neon --error-format=table -php83compatibility: vendor/bin/phpstan build/reports/phpstan - $(call header,Checking PHP 8.3 compatibility) - @XDEBUG_MODE=off ./vendor/bin/phpstan analyse --configuration=./ci/php83-compatibility.neon --error-format=table +php/max-compatibility: vendor/bin/phpstan build/reports/phpstan #ci + $(call header,Checking PHP ${PHP_VERSION_MAX} compatibility) + @XDEBUG_MODE=off ./vendor/bin/phpstan analyse --configuration=./ci/phpmax-compatibility.neon --error-format=table -phpstan: vendor/bin/phpstan build/reports/phpstan - $(call header,Running Static Analyze) - @XDEBUG_MODE=off ./vendor/bin/phpstan analyse --error-format=checkstyle > ./build/reports/phpstan/phpstan.xml - -analyze: vendor/bin/phpstan build/reports/phpstan +php/analyze: vendor/bin/phpstan build/reports/phpstan #manual & ci $(call header,Running Static Analyze - Pretty tty format) @XDEBUG_MODE=off ./vendor/bin/phpstan analyse --error-format=table -tests: vendor/bin/phpunit build/reports/phpunit #ci +php/tests: vendor/bin/phpunit build/reports/phpunit #ci $(call header,Running Unit Tests) - @rm -rf ./tests/unit/Generated/Entity ./tests/unit/Generated/Infrastructure ./tests/unit/Generated/Repository - @XDEBUG_MODE=coverage php -dzend_extension=xdebug.so ./vendor/bin/phpunit --testsuite=unit --coverage-clover=./build/reports/phpunit/clover.xml --log-junit=./build/reports/phpunit/unit.xml --coverage-php=./build/reports/phpunit/unit.cov --coverage-html=./build/reports/coverage/ --fail-on-warning + @XDEBUG_MODE=coverage php ./vendor/bin/phpunit --testsuite=unit --coverage-clover=./build/reports/phpunit/clover.xml --log-junit=./build/reports/phpunit/unit.xml --coverage-php=./build/reports/phpunit/unit.cov --coverage-html=./build/reports/coverage/ --fail-on-warning + +php/test: php/tests -integration: vendor/bin/phpunit build/reports/phpunit #manual +php/integration: vendor/bin/phpunit build/reports/phpunit #manual $(call header,Running Integration Tests) - @XDEBUG_MODE=coverage php -dzend_extension=xdebug.so ./vendor/bin/phpunit --testsuite=integration --fail-on-warning + @XDEBUG_MODE=coverage php ./vendor/bin/phpunit --testsuite=integration --fail-on-warning -testdox: vendor/bin/phpunit #manual +php/testdox: vendor/bin/phpunit #manual $(call header,Running Unit Tests (Pretty format)) - @XDEBUG_MODE=coverage php -dzend_extension=xdebug.so ./vendor/bin/phpunit --testsuite=unit --fail-on-warning --testdox + @XDEBUG_MODE=coverage php ./vendor/bin/phpunit --testsuite=unit --fail-on-warning --testdox clean: - $(call header,Cleaning previous build) + $(call header,Cleaning previous build) #manual @if [ "$(shell ls -A ./build)" ]; then rm -rf ./build/*; fi; echo " done" -ci: clean validate deps install phpcs tests integration php81compatibility php83compatibility analyze +ci: clean validate install php/deps php/check php/tests php/integration php/min-compatibility php/max-compatibility php/analyze diff --git a/README.md b/README.md index 2fafd1c..78a899e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # component-web [![Current version](https://img.shields.io/packagist/v/eureka/component-web.svg?logo=composer)](https://packagist.org/packages/eureka/component-web) -[![Supported PHP version](https://img.shields.io/static/v1?logo=php&label=PHP&message=8.1%20-%208.3&color=777bb4)](https://packagist.org/packages/eureka/component-web) +[![Supported PHP version](https://img.shields.io/static/v1?logo=php&label=PHP&message=8.3%20-%208.4&color=777bb4)](https://packagist.org/packages/eureka/component-web) ![CI](https://github.com/eureka-framework/component-web/workflows/CI/badge.svg) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=eureka-framework_component-web&metric=alert_status)](https://sonarcloud.io/dashboard?id=eureka-framework_component-web) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=eureka-framework_component-web&metric=coverage)](https://sonarcloud.io/dashboard?id=eureka-framework_component-web) @@ -42,65 +42,43 @@ make update NB: For the components, the `composer.lock` file is not committed. -### Testing & CI (Continuous Integration) +## Testing & CI (Continuous Integration) -#### Tests -You can run unit tests (with coverage) on your side with following command: +You can run tests on your side with following commands: ```bash -make tests +make php/tests # run tests with coverage +make php/test # run tests with coverage +make php/testdox # run tests without coverage reports but with prettified output ``` -You can run integration tests (without coverage) on your side with following command: +You also can run code style check or code style fixes with following commands: ```bash -make integration +make php/check # run checks on check style +make php/fix # run check style auto fix ``` -For prettier output (but without coverage), you can use the following command: -```bash -make testdox # run tests without coverage reports but with prettified output -``` - -#### Code Style -You also can run code style check with following commands: -```bash -make phpcs -``` - -You also can run code style fixes with following commands: -```bash -make phpcsf -``` - -#### Check for missing explicit dependencies -You can check if any explicit dependency is missing with the following command: -```bash -make deps -``` - -#### Static Analysis To perform a static analyze of your code (with phpstan, lvl 9 at default), you can use the following command: ```bash -make analyse +make php/analyze # Same as phpstan but with CLI output as table ``` -To ensure you code still compatible with current supported version at Deezer and futures versions of php, you need to +To ensure you code still compatible with current supported version and futures versions of php, you need to run the following commands (both are required for full support): - -Minimal supported version: ```bash -make php81compatibility +make php/min-compatibility # run compatibility check on current minimal version of php we support +make php/max-compatibility # run compatibility check on last version of php we will support in future ``` -Maximal supported version: +And the last "helper" commands, you can run before commit and push is: ```bash -make php83compatibility +make ci ``` +This command clean the previous reports, install component if needed and run tests (with coverage report), +check the code style and check the php compatibility check, as it would be done in our CI. -#### CI Simulation -And the last "helper" commands, you can run before commit and push, is: -```bash -make ci -``` +## Contributing + +See the [CONTRIBUTING](CONTRIBUTING.md) file. ## License diff --git a/ci/composer-dependency-analyser.php b/ci/composer-dependency-analyser.php new file mode 100644 index 0000000..8a3ce41 --- /dev/null +++ b/ci/composer-dependency-analyser.php @@ -0,0 +1,11 @@ +addPathToScan(__DIR__ . '/../src', isDev: false) + ->addPathToScan(__DIR__ . '/../tests', isDev: true) +; diff --git a/ci/php81-compatibility.neon b/ci/phpmax-compatibility.neon similarity index 92% rename from ci/php81-compatibility.neon rename to ci/phpmax-compatibility.neon index fbd2148..2c7cc0b 100644 --- a/ci/php81-compatibility.neon +++ b/ci/phpmax-compatibility.neon @@ -3,7 +3,7 @@ includes: - ./../vendor/phpstan/phpstan-phpunit/rules.neon parameters: - phpVersion: 80100 + phpVersion: 80400 level: 0 paths: - ./../src diff --git a/ci/php83-compatibility.neon b/ci/phpmin-compatibility.neon similarity index 100% rename from ci/php83-compatibility.neon rename to ci/phpmin-compatibility.neon diff --git a/composer.json b/composer.json index 6aa637d..d953adf 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ }, "autoload-dev": { "psr-4": { - "Eureka\\Component\\Web\\Tests\\Unit\\": "./tests/unit" + "Eureka\\Component\\Web\\Tests\\": "./tests" } }, @@ -29,22 +29,20 @@ }, "require": { - "php": "8.1.*||8.2.*||8.3.*", + "php": "8.3.*||8.4.*", "ext-mbstring": "*", - "psr/http-message": "^1.0||^2.0", - "psr/cache": "^1.0||^2.0||^3.0" + "psr/http-message": "^1.0||^2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.51.0", - "maglnet/composer-require-checker": "^4.7.1", - "phpstan/phpstan": "^1.10.60", - "phpstan/phpstan-phpunit": "^1.3.16", - "phpstan/phpstan-strict-rules": "^1.5.2", - "phpunit/phpcov": "^9.0.2", - "phpunit/phpunit": "^10.5.12", - "symfony/cache": "^6.4.4||^7.0" + "friendsofphp/php-cs-fixer": "^3.86.0", + "phpstan/phpstan": "^2.1.22", + "phpstan/phpstan-phpunit": "^2.0.7", + "phpstan/phpstan-strict-rules": "^2.0.6", + "phpunit/phpcov": "^11.0.1", + "phpunit/phpunit": "^12.3.4", + "shipmonk/composer-dependency-analyser": "^1.8.3" } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 63a78f4..020ab94 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,9 +1,10 @@ includes: - ./vendor/phpstan/phpstan-phpunit/extension.neon - ./vendor/phpstan/phpstan-phpunit/rules.neon + - ./vendor/phpstan/phpstan-strict-rules/rules.neon parameters: - phpVersion: 80100 + phpVersion: 80300 level: max paths: - ./src @@ -12,5 +13,4 @@ parameters: bootstrapFiles: - ./vendor/autoload.php - ignoreErrors: - - "`Cannot access offset 'year' on mixed.`" + treatPhpDocTypesAsCertain: false diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 925796d..12fdea8 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,17 +1,37 @@ - - - ./tests/unit - - + failOnEmptyTestSuite="false" + failOnIncomplete="true" + failOnRisky="true" + failOnWarning="true" + displayDetailsOnTestsThatTriggerDeprecations="true" + displayDetailsOnPhpunitDeprecations="true" + displayDetailsOnTestsThatTriggerErrors="true" + displayDetailsOnTestsThatTriggerNotices="true" + displayDetailsOnTestsThatTriggerWarnings="true" + cacheDirectory="build/.phpunit.cache"> + + + + + ./src + + + + ./tests/Unit + + + ./tests/Integration + + + diff --git a/src/Breadcrumb/Breadcrumb.php b/src/Breadcrumb/Breadcrumb.php index 6c90307..44b960f 100644 --- a/src/Breadcrumb/Breadcrumb.php +++ b/src/Breadcrumb/Breadcrumb.php @@ -13,19 +13,8 @@ use Eureka\Component\Web\Collection\AbstractCollection; -/** - * Breadcrumb class - * - * @author Romain Cottard - */ class Breadcrumb extends AbstractCollection { - /** - * Add item. - * - * @param BreadcrumbItem $item - * @return $this - */ public function push(BreadcrumbItem $item): self { $this->pushItem($item); @@ -33,9 +22,6 @@ public function push(BreadcrumbItem $item): self return $this; } - /** - * @return BreadcrumbItem - */ public function pop(): BreadcrumbItem { /** @var BreadcrumbItem $item */ diff --git a/src/Breadcrumb/BreadcrumbItem.php b/src/Breadcrumb/BreadcrumbItem.php index d92ff0a..4357ce6 100644 --- a/src/Breadcrumb/BreadcrumbItem.php +++ b/src/Breadcrumb/BreadcrumbItem.php @@ -11,81 +11,38 @@ namespace Eureka\Component\Web\Breadcrumb; -/** - * Class to set breadcrumb item. - * - * @author Romain Cottard - */ class BreadcrumbItem { - /** @var string $name Item name */ private string $name = ''; - - /** @var string $icon Item icon */ private string $icon = ''; - - /** @var string $uri Item Uri */ private string $uri = ''; - - /** @var bool $isActive Item is active ? */ private bool $isActive = false; - /** - * BreadcrumbItem constructor. - * - * @param $name - */ public function __construct(string $name) { $this->setName($name); } - /** - * Get Name - * - * @return string - */ public function getName(): string { return $this->name; } - /** - * Get icon - * - * @return string - */ public function getIcon(): string { return $this->icon; } - /** - * Get Uri - * - * @return string - */ public function getUri(): string { return $this->uri; } - /** - * Get is active. - * - * @return bool - */ public function isActive(): bool { return $this->isActive; } - /** - * Set name - * - * @param string $name - * @return $this - */ public function setName(string $name): self { $this->name = $name; @@ -93,12 +50,6 @@ public function setName(string $name): self return $this; } - /** - * Set icon. - * - * @param string $icon - * @return $this - */ public function setIcon(string $icon): self { $this->icon = $icon; @@ -106,12 +57,6 @@ public function setIcon(string $icon): self return $this; } - /** - * Set uri - * - * @param string $uri - * @return $this - */ public function setUri(string $uri): self { $this->uri = $uri; @@ -119,12 +64,6 @@ public function setUri(string $uri): self return $this; } - /** - * Set is Active - * - * @param bool $isActive - * @return $this - */ public function setIsActive(bool $isActive): self { $this->isActive = $isActive; diff --git a/src/Carousel/Carousel.php b/src/Carousel/Carousel.php index bab9cc8..5812786 100644 --- a/src/Carousel/Carousel.php +++ b/src/Carousel/Carousel.php @@ -13,19 +13,8 @@ use Eureka\Component\Web\Collection\AbstractCollection; -/** - * Breadcrumb class - * - * @author Romain Cottard - */ class Carousel extends AbstractCollection { - /** - * Add item. - * - * @param CarouselItem $item - * @return $this - */ public function push(CarouselItem $item): self { $this->pushItem($item); @@ -33,9 +22,6 @@ public function push(CarouselItem $item): self return $this; } - /** - * @return CarouselItem - */ public function pop(): CarouselItem { /** @var CarouselItem $item */ diff --git a/src/Carousel/CarouselItem.php b/src/Carousel/CarouselItem.php index 85e6252..89d2fc3 100644 --- a/src/Carousel/CarouselItem.php +++ b/src/Carousel/CarouselItem.php @@ -18,100 +18,48 @@ */ class CarouselItem { - /** @var string $title Item title */ private string $title = ''; - - /** @var string $subTitle Item subtitle */ private string $subTitle = ''; - - /** @var string $image Item image */ private string $image = ''; - - /** @var string $linkTitle Item link title */ private string $linkTitle = ''; - - /** @var string $linkUri Item link Uri */ private string $linkUri = ''; - - /** @var bool $isActive Item is active ? */ private bool $isActive = false; - /** - * CarouselItem constructor. - * - * @param string $title - */ public function __construct(string $title = '') { $this->setTitle($title); } - /** - * Get Title - * - * @return string - */ public function getTitle(): string { return $this->title; } - /** - * Get sub title - * - * @return string - */ public function getSubTitle(): string { return $this->subTitle; } - /** - * Get image - * - * @return string - */ public function getImage(): string { return $this->image; } - /** - * Get link title - * - * @return string - */ public function getLinkTitle(): string { return $this->linkTitle; } - /** - * Get link Uri - * - * @return string - */ public function getLinkUri(): string { return $this->linkUri; } - /** - * Get is active. - * - * @return bool - */ public function isActive(): bool { return $this->isActive; } - /** - * Set Title - * - * @param string $title - * @return CarouselItem - */ public function setTitle(string $title): self { $this->title = $title; @@ -119,12 +67,6 @@ public function setTitle(string $title): self return $this; } - /** - * Set subtitle - * - * @param string $subTitle - * @return CarouselItem - */ public function setSubTitle(string $subTitle): self { $this->subTitle = $subTitle; @@ -132,12 +74,6 @@ public function setSubTitle(string $subTitle): self return $this; } - /** - * Set image. - * - * @param string $image - * @return $this - */ public function setImage(string $image): self { $this->image = $image; @@ -145,10 +81,6 @@ public function setImage(string $image): self return $this; } - /** - * @param string $linkTitle - * @return CarouselItem - */ public function setLinkTitle(string $linkTitle): self { $this->linkTitle = $linkTitle; @@ -156,10 +88,6 @@ public function setLinkTitle(string $linkTitle): self return $this; } - /** - * @param string $linkUri - * @return CarouselItem - */ public function setLinkUri(string $linkUri): self { $this->linkUri = $linkUri; @@ -167,12 +95,6 @@ public function setLinkUri(string $linkUri): self return $this; } - /** - * Set is Active - * - * @param bool $isActive - * @return $this - */ public function setIsActive(bool $isActive): self { $this->isActive = $isActive; diff --git a/src/Collection/AbstractCollection.php b/src/Collection/AbstractCollection.php index 6e01ac2..b098c05 100644 --- a/src/Collection/AbstractCollection.php +++ b/src/Collection/AbstractCollection.php @@ -17,17 +17,12 @@ use Eureka\Component\Web\Notification\NotificationInterface; /** - * Class AbstractCollection - * @implements \Iterator - * - * @author Romain Cottard + * @implements \Iterator */ class AbstractCollection implements \Iterator, \Countable { - /** @var int $index Current index key. */ private int $index = 0; - - /** @var int $count Number of element in breadcrumb */ + /** @var int<0, max> $count */ private int $count = 0; /** @var array $collection */ @@ -36,95 +31,52 @@ class AbstractCollection implements \Iterator, \Countable /** @var int[] $names */ private array $names = []; - - /** - * Check if is the last element of collection. - * - * @return bool - */ public function isLast(): bool { return ($this->index === ($this->count - 1)); } - /** - * Check if is the first element of collection. - * - * @return bool - */ public function isFirst(): bool { return ($this->index === 0); } - /** - * Current iterator method. - * - * @return mixed - */ - #[\ReturnTypeWillChange] - public function current() + public function current(): BreadcrumbItem|CarouselItem|MenuItem|NotificationInterface { return $this->collection[$this->index]; } - /** - * Key iterator method. - * - * @return int - */ public function key(): int { return $this->index; } - /** - * Next iterator method. - * - * @return void - */ public function next(): void { $this->index++; } - /** - * Rewind iterator method. - * - * @return void - */ public function rewind(): void { $this->index = 0; } - /** - * Valid iterator method. - * - * @return bool - */ public function valid(): bool { return ($this->index < $this->count); } /** - * Count countable method. - * - * @return int + * @return int<0, max> */ public function count(): int { return $this->count; } - /** - * @param BreadcrumbItem|CarouselItem|MenuItem|NotificationInterface $item - * @return $this - */ - protected function pushItem($item): self + protected function pushItem(BreadcrumbItem|CarouselItem|MenuItem|NotificationInterface $item): self { - $itemName = method_exists($item, 'getName') ? $item->getName() : (string) $this->count(); + $itemName = \method_exists($item, 'getName') ? $item->getName() : (string) $this->count(); $this->collection[$this->count] = $item; $this->names[$itemName] = $this->count; @@ -133,25 +85,23 @@ protected function pushItem($item): self return $this; } - /** - * @return BreadcrumbItem|CarouselItem|MenuItem|NotificationInterface|null - */ - protected function popItem() + protected function popItem(): BreadcrumbItem|CarouselItem|MenuItem|NotificationInterface { - $last = array_pop($this->collection); - $this->count--; + $last = \array_pop($this->collection); + + if ($last === null) { + throw new \LengthException('Cannot pop from an empty collection.'); + } + if ($this->count > 0) { + $this->count--; + } + $this->rewind(); return $last; } - /** - * Get menu item by name. - * - * @param string $name - * @return BreadcrumbItem|CarouselItem|MenuItem|NotificationInterface|null - */ - protected function getItem(string $name) + protected function getItem(string $name): BreadcrumbItem|CarouselItem|MenuItem|NotificationInterface|null { if (!isset($this->names[$name])) { return null; diff --git a/src/Menu/Menu.php b/src/Menu/Menu.php index 9dee095..8c50fa9 100644 --- a/src/Menu/Menu.php +++ b/src/Menu/Menu.php @@ -13,19 +13,8 @@ use Eureka\Component\Web\Collection\AbstractCollection; -/** - * Menu class - * - * @author Romain Cottard - */ class Menu extends AbstractCollection { - /** - * Add item. - * - * @param MenuItem $item - * @return self - */ public function push(MenuItem $item): self { $this->pushItem($item); @@ -33,12 +22,6 @@ public function push(MenuItem $item): self return $this; } - /** - * Get menu item by name. - * - * @param string $name - * @return MenuItem|null - */ public function get(string $name): ?MenuItem { /** @var MenuItem $item */ diff --git a/src/Menu/MenuControllerAwareTrait.php b/src/Menu/MenuControllerAwareTrait.php index ca5a9e6..8ef98b1 100644 --- a/src/Menu/MenuControllerAwareTrait.php +++ b/src/Menu/MenuControllerAwareTrait.php @@ -14,32 +14,43 @@ use Psr\Http\Message\ServerRequestInterface; /** - * Class Helper - * - * @author Romain Cottard + * @phpstan-type MenuConfigType array{ + * label: string, + * icon?: string|null, + * mustBeLogged?: bool, + * route?: string|null, + * route_params?: array, + * children?: null|MenuChildConfigType[], + * } + * @phpstan-type MenuChildConfigType array{ + * label: string, + * icon?: string|null, + * divider?: bool, + * mustBeLogged?: bool, + * route?: string|null, + * route_params?: array, + * } */ trait MenuControllerAwareTrait { - /** @var string $menuStateClosed */ private static string $menuStateClosed = 'closed'; - /** @var array> $menuConfigMain */ + /** @var MenuConfigType[] $menuConfigMain */ private array $menuConfigMain; - /** @var array> $menuConfigSecondary */ + /** @var MenuConfigType[] $menuConfigSecondary */ private array $menuConfigSecondary; - - /** @var string $cookieMenuStateName */ private string $cookieMenuStateName = ''; /** - * @param array> $menuConfigMain - * @param string $cookieMenuStateName - * @param array> $menuConfigSecondary - * @return void + * @param MenuConfigType[] $menuConfigMain + * @param MenuConfigType[] $menuConfigSecondary */ - public function setMenuConfig(array $menuConfigMain, string $cookieMenuStateName = '', array $menuConfigSecondary = []): void - { + public function setMenuConfig( + array $menuConfigMain, + string $cookieMenuStateName = '', + array $menuConfigSecondary = [], + ): void { $this->menuConfigMain = $menuConfigMain; $this->menuConfigSecondary = $menuConfigSecondary; $this->cookieMenuStateName = $cookieMenuStateName; @@ -47,20 +58,17 @@ public function setMenuConfig(array $menuConfigMain, string $cookieMenuStateName /** * Menu with max depth of 2. - * - * @param bool $isMain - * @param bool $isLogged - * @return Menu */ protected function getMenu(bool $isMain = true, bool $isLogged = false): Menu { $routeParams = $this->getRoute(); $currentRoute = $routeParams['_route'] ?? ''; - $menuConfig = $isMain ? $this->menuConfigMain : $this->menuConfigSecondary; + + $menuConfig = $isMain ? $this->menuConfigMain : $this->menuConfigSecondary; $menu = new Menu(); foreach ($menuConfig as $data) { - $mustBeLogged = (bool) ($data['mustBeLogged'] ?? false); + $mustBeLogged = $data['mustBeLogged'] ?? false; if ($mustBeLogged && !$isLogged) { continue; } @@ -74,7 +82,7 @@ protected function getMenu(bool $isMain = true, bool $isLogged = false): Menu ->setIsActive($menuRoute === $currentRoute) ; - if (!empty($data['children'])) { + if (isset($data['children']) && \is_array($data['children']) && $data['children'] !== []) { $item->setSubmenu($this->getSubMenu($data['children'], $item, $currentRoute, $isLogged)); } @@ -85,8 +93,6 @@ protected function getMenu(bool $isMain = true, bool $isLogged = false): Menu } /** - * @param ServerRequestInterface|null $request - * @return string * @codeCoverageIgnore */ protected function getMenuState(?ServerRequestInterface $request): string @@ -99,7 +105,7 @@ protected function getMenuState(?ServerRequestInterface $request): string $cookie = $request->getCookieParams(); - if (isset($cookie[$this->cookieMenuStateName])) { + if (isset($cookie[$this->cookieMenuStateName]) && \is_string($cookie[$this->cookieMenuStateName])) { $state = $cookie[$this->cookieMenuStateName]; } @@ -107,17 +113,13 @@ protected function getMenuState(?ServerRequestInterface $request): string } /** - * @param array> $children - * @param MenuItem $parent - * @param string $currentRoute - * @param bool $isLogged - * @return Menu + * @param MenuChildConfigType[] $children */ private function getSubMenu(array $children, MenuItem $parent, string $currentRoute, bool $isLogged = false): Menu { $menu = new Menu(); foreach ($children as $data) { - $mustBeLogged = (bool) ($data['mustBeLogged'] ?? false); + $mustBeLogged = $data['mustBeLogged'] ?? false; if ($mustBeLogged && !$isLogged) { continue; } @@ -128,7 +130,7 @@ private function getSubMenu(array $children, MenuItem $parent, string $currentRo $item = new MenuItem($data['label']); $item - ->setIsDivider((isset($data['divider']) && (bool) $data['divider'])) + ->setIsDivider((isset($data['divider']) && $data['divider'])) ->setUri($menuUri) ->setIcon($data['icon'] ?? '') ->setIsActive($currentRoute === $menuRoute) @@ -145,19 +147,17 @@ private function getSubMenu(array $children, MenuItem $parent, string $currentRo } /** - * @param string|null $menuRoute - * @param array $menuRouteParams - * @return string + * @param array $menuRouteParams */ private function getMenuUri(?string $menuRoute, array $menuRouteParams = []): string { - if (empty($menuRoute)) { + if ($menuRoute === null || $menuRoute === '') { $menuRoute = '#'; } - if (mb_substr($menuRoute, 0, 4) === 'http') { + if (\mb_substr($menuRoute, 0, 4) === 'http') { $menuUri = $menuRoute; - } elseif (mb_substr($menuRoute, 0, 1) === '#') { + } elseif (\mb_substr($menuRoute, 0, 1) === '#') { $menuUri = 'javascript:void(0);'; } else { $menuUri = $this->getRouteUri($menuRoute, $menuRouteParams); diff --git a/src/Menu/MenuItem.php b/src/Menu/MenuItem.php index d3fe802..7c52457 100644 --- a/src/Menu/MenuItem.php +++ b/src/Menu/MenuItem.php @@ -11,127 +11,60 @@ namespace Eureka\Component\Web\Menu; -/** - * Class to set menu item. - * - * @author Romain Cottard - */ class MenuItem { - /** @var string $name Menu name */ private string $name = ''; - - /** @var string $icon Menu icon */ private string $icon = ''; - - /** @var string $uri Menu URI */ private string $uri = ''; - - /** @var Menu|null $submenu Sub menu. */ private ?Menu $submenu = null; - - /** @var bool $isActive If is currently active */ private bool $isActive = false; - - /** @var bool $isDivider If is divider */ private bool $isDivider = false; - /** - * MenuItem constructor. - * - * @param string $name - */ public function __construct(string $name) { $this->setName($name); } - /** - * Get Name - * - * @return string - */ public function getName(): string { return $this->name; } - /** - * Get icon - * - * @return string - */ public function getIcon(): string { return $this->icon; } - /** - * Get Uri - * - * @return string - */ public function getUri(): string { return $this->uri; } - /** - * Get submenu - * - * @return Menu|null - */ public function getSubmenu(): ?Menu { return $this->submenu; } - /** - * Get is active. - * - * @return bool - */ public function isActive(): bool { return $this->isActive; } - /** - * Get is active. - * - * @return bool - */ public function isDivider(): bool { return $this->isDivider; } - /** - * If has submenu with elements. - * - * @return bool - */ public function hasIcon(): bool { - return !empty($this->getIcon()); + return $this->getIcon() !== ''; } - /** - * If has submenu with elements. - * - * @return bool - */ public function hasSubmenu(): bool { - return ($this->submenu instanceof Menu) && $this->submenu->count() > 0; + return $this->submenu instanceof Menu && $this->submenu->count() > 0; } - /** - * Set name - * - * @param string $name - * @return self - */ public function setName(string $name): self { $this->name = $name; @@ -139,12 +72,6 @@ public function setName(string $name): self return $this; } - /** - * Set icon. - * - * @param string $icon - * @return self - */ public function setIcon(string $icon): self { $this->icon = $icon; @@ -152,12 +79,6 @@ public function setIcon(string $icon): self return $this; } - /** - * Set uri - * - * @param string $uri - * @return self - */ public function setUri(string $uri): self { $this->uri = $uri; @@ -165,12 +86,6 @@ public function setUri(string $uri): self return $this; } - /** - * Set is Active - * - * @param bool $isActive - * @return self - */ public function setIsActive(bool $isActive): self { $this->isActive = $isActive; @@ -178,12 +93,6 @@ public function setIsActive(bool $isActive): self return $this; } - /** - * Set is divider - * - * @param bool $isDivider - * @return self - */ public function setIsDivider(bool $isDivider): self { $this->isDivider = $isDivider; @@ -191,12 +100,6 @@ public function setIsDivider(bool $isDivider): self return $this; } - /** - * Set submenu instance. - * - * @param Menu $submenu - * @return self - */ public function setSubmenu(Menu $submenu): self { $this->submenu = $submenu; diff --git a/src/Meta/MetaControllerAwareTrait.php b/src/Meta/MetaControllerAwareTrait.php index fdb00e4..e2f37c6 100644 --- a/src/Meta/MetaControllerAwareTrait.php +++ b/src/Meta/MetaControllerAwareTrait.php @@ -11,19 +11,13 @@ namespace Eureka\Component\Web\Meta; -/** - * Trait MetaControllerAwareTrait - * - * @author Romain Cottard - */ trait MetaControllerAwareTrait { - /** @var array> $metaConfig */ + /** @var array $metaConfig */ private array $metaConfig; /** - * @param array> $metaConfig - * @return void + * @param array $metaConfig */ public function setMetaConfig(array $metaConfig): void { @@ -31,13 +25,13 @@ public function setMetaConfig(array $metaConfig): void } /** - * @return array> + * @return array */ protected function getMeta(): array { $meta = $this->metaConfig; - if (isset($meta['copyright']) && isset($meta['copyright']['year']) && $meta['copyright']['year'] === 'now') { - $meta['copyright']['year'] = date('Y'); + if (isset($meta['copyright']['year']) && $meta['copyright']['year'] === 'now') { + $meta['copyright']['year'] = \date('Y'); } return $meta; diff --git a/src/Notification/AbstractNotification.php b/src/Notification/AbstractNotification.php index b83dcc3..d40df37 100644 --- a/src/Notification/AbstractNotification.php +++ b/src/Notification/AbstractNotification.php @@ -11,16 +11,11 @@ namespace Eureka\Component\Web\Notification; -/** - * Class for basic notifications on actions (save, delete...) - * - * @author Romain Cottard - */ abstract class AbstractNotification implements NotificationInterface { public function __construct( protected string $message, - protected NotificationType $type = NotificationType::Success + protected NotificationType $type = NotificationType::Success, ) {} public function getMessage(): string diff --git a/src/Notification/NotificationBootstrap.php b/src/Notification/NotificationBootstrap.php index 61fd34a..6294d24 100644 --- a/src/Notification/NotificationBootstrap.php +++ b/src/Notification/NotificationBootstrap.php @@ -11,18 +11,8 @@ namespace Eureka\Component\Web\Notification; -/** - * Class for basic notifications on actions (save, delete...) - * - * @author Romain Cottard - */ class NotificationBootstrap extends AbstractNotification { - /** - * Get css used. - * - * @return string - */ public function getCss(): string { return match ($this->type) { @@ -33,11 +23,6 @@ public function getCss(): string }; } - /** - * Get header title. - * - * @return string - */ public function getHeader(): string { return match ($this->type) { @@ -48,11 +33,6 @@ public function getHeader(): string }; } - /** - * Return icon name. - * - * @return string - */ public function getIcon(): string { return match ($this->type) { diff --git a/src/Notification/NotificationCollection.php b/src/Notification/NotificationCollection.php index aed092b..768e1bb 100644 --- a/src/Notification/NotificationCollection.php +++ b/src/Notification/NotificationCollection.php @@ -13,19 +13,8 @@ use Eureka\Component\Web\Collection\AbstractCollection; -/** - * Breadcrumb class - * - * @author Romain Cottar - */ class NotificationCollection extends AbstractCollection { - /** - * Add item. - * - * @param NotificationInterface $item - * @return $this - */ public function push(NotificationInterface $item): self { $this->pushItem($item); @@ -33,9 +22,6 @@ public function push(NotificationInterface $item): self return $this; } - /** - * @return NotificationInterface - */ public function pop(): NotificationInterface { /** @var NotificationInterface $item */ diff --git a/src/Notification/NotificationInterface.php b/src/Notification/NotificationInterface.php index 30255fd..233e13b 100644 --- a/src/Notification/NotificationInterface.php +++ b/src/Notification/NotificationInterface.php @@ -11,38 +11,13 @@ namespace Eureka\Component\Web\Notification; -/** - * Class for basic notifications on actions (save, delete...) - * - * @author Romain Cottard - */ interface NotificationInterface { - /** - * Get message - * - * @return string - */ public function getMessage(): string; - /** - * Get css - * - * @return string - */ public function getCss(): string; - /** - * Get Icon - * - * @return string - */ public function getIcon(): string; - /** - * Get header message. - * - * @return string - */ public function getHeader(): string; } diff --git a/src/Notification/NotificationType.php b/src/Notification/NotificationType.php index 8e52849..bf8fd0d 100644 --- a/src/Notification/NotificationType.php +++ b/src/Notification/NotificationType.php @@ -11,11 +11,6 @@ namespace Eureka\Component\Web\Notification; -/** - * Class NotificationType - * - * @author Romain Cottard - */ enum NotificationType: string { case Info = 'info'; diff --git a/src/Session/Session.php b/src/Session/Session.php index 6ed71a0..9322cc2 100644 --- a/src/Session/Session.php +++ b/src/Session/Session.php @@ -14,34 +14,26 @@ /** * $_SESSION data wrapper class. * Can handle flash (ephemeral) session variables. - * - * @author Romain Cottard */ class Session { /** @var string FLASH Session index name for ephemeral var in Session. */ - private const FLASH = '_flash'; + private const string FLASH = '_flash'; /** @var string ACTIVE Session index name for ephemeral var if active or not. */ - private const ACTIVE = 'active'; + private const string ACTIVE = 'active'; /** @var string VARIABLE Session index name for ephemeral var content. */ - private const VARIABLE = 'var'; + private const string VARIABLE = 'var'; /** @var array|null $session */ protected static ?array $session = null; - /** - * Session constructor. - */ public function __construct() { $this->initialize(); } - /** - * @return void - */ private function initialize(): void { if (self::$session === null) { @@ -57,9 +49,6 @@ private function initialize(): void /** * If session have given key. - * - * @param string $key - * @return bool */ public function has(string $key): bool { @@ -68,10 +57,6 @@ public function has(string $key): bool /** * Get session value. - * - * @param string $key - * @param mixed $default - * @return mixed|null */ public function get(string $key, mixed $default = null): mixed { @@ -80,10 +65,6 @@ public function get(string $key, mixed $default = null): mixed /** * Set value for a given key. - * - * @param string $key - * @param mixed $value - * @return self */ public function set(string $key, mixed $value): self { @@ -95,9 +76,6 @@ public function set(string $key, mixed $value): self /** * Remove key from bag container. * If key not exists, must throw an BagKeyNotFoundException - * - * @param string $key - * @return static */ public function remove(string $key): self { @@ -110,10 +88,6 @@ public function remove(string $key): self /** * Get Session ephemeral variable specified. - * - * @param string $name - * @param mixed|null $default - * @return mixed Variable value. */ public function getFlash(string $name, mixed $default = null): mixed { @@ -128,9 +102,6 @@ public function getFlash(string $name, mixed $default = null): mixed /** * Check if have specified ephemeral var in Session. - * - * @param string $name Index Session name. - * @return bool */ public function hasFlash(string $name): bool { @@ -142,8 +113,6 @@ public function hasFlash(string $name): bool /** * Initialize Session. Remove old ephemeral var in Session. - * - * @return $this */ public function clearFlash(): self { @@ -172,10 +141,6 @@ public function clearFlash(): self /** * Set ephemeral variable in Session. - * - * @param string $name - * @param mixed $value - * @return $this */ public function setFlash(string $name, mixed $value): self { diff --git a/src/Session/SessionAwareTrait.php b/src/Session/SessionAwareTrait.php index 0789c9e..15b1906 100644 --- a/src/Session/SessionAwareTrait.php +++ b/src/Session/SessionAwareTrait.php @@ -13,20 +13,10 @@ use Eureka\Component\Web\Notification\NotificationType; -/** - * Trait SessionAwareTrait - * - * @author Romain Cottard - */ trait SessionAwareTrait { - /** @var Session|null session */ protected Session|null $session = null; - /** - * @param Session $session - * @return void - */ public function setSession(Session $session): void { $this->session = $session; @@ -52,7 +42,6 @@ public function addFlashNotification(string $message, NotificationType $type): v /** * @param array $errors - * @return void */ public function setFormErrors(array $errors): void { diff --git a/tests/Integration/.gitkeep b/tests/Integration/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/BreadcrumbTest.php b/tests/Unit/BreadcrumbTest.php similarity index 67% rename from tests/unit/BreadcrumbTest.php rename to tests/Unit/BreadcrumbTest.php index dfb665b..a7fed47 100644 --- a/tests/unit/BreadcrumbTest.php +++ b/tests/Unit/BreadcrumbTest.php @@ -15,26 +15,8 @@ use Eureka\Component\Web\Breadcrumb\BreadcrumbItem; use PHPUnit\Framework\TestCase; -/** - * Class BreadcrumbTest - * - * @author Romain Cottard - */ class BreadcrumbTest extends TestCase { - /** - * @return void - */ - public function testICanInitializeBreadcrumb(): void - { - $breadcrumb = new Breadcrumb(); - - $this->assertInstanceOf(Breadcrumb::class, $breadcrumb); - } - - /** - * @return void - */ public function testICanPushAndPopItemToBreadcrumb(): void { $breadcrumb = new Breadcrumb(); @@ -43,12 +25,9 @@ public function testICanPushAndPopItemToBreadcrumb(): void $item = $breadcrumb->pop(); - $this->assertEquals('item 2', $item->getName()); + self::assertEquals('item 2', $item->getName()); } - /** - * @return void - */ public function testICanIterateOnBreadcrumbInstance(): void { $breadcrumb = new Breadcrumb(); @@ -57,26 +36,19 @@ public function testICanIterateOnBreadcrumbInstance(): void foreach ($breadcrumb as $index => $item) { /** @var BreadcrumbItem $item */ - $this->assertEquals('item ' . ($index + 1), $item->getName()); + self::assertEquals('item ' . ($index + 1), $item->getName()); } } - /** - * @return void - */ public function testICanCountItemInBreadcrumb(): void { $breadcrumb = new Breadcrumb(); $breadcrumb->push(new BreadcrumbItem('item 1')); $breadcrumb->push(new BreadcrumbItem('item 2')); - $this->assertCount(2, $breadcrumb); - $this->assertEquals(2, count($breadcrumb)); + self::assertCount(2, $breadcrumb); } - /** - * @return void - */ public function testICanCheckIfItIsTheFirstItemDuringTheIterationOnBreadcrumb(): void { $breadcrumb = new Breadcrumb(); @@ -85,12 +57,9 @@ public function testICanCheckIfItIsTheFirstItemDuringTheIterationOnBreadcrumb(): $breadcrumb->rewind(); - $this->assertTrue($breadcrumb->isFirst()); + self::assertTrue($breadcrumb->isFirst()); } - /** - * @return void - */ public function testICanCheckIfItIsTheLastItemDuringTheIterationOnBreadcrumb(): void { $breadcrumb = new Breadcrumb(); @@ -100,12 +69,9 @@ public function testICanCheckIfItIsTheLastItemDuringTheIterationOnBreadcrumb(): $breadcrumb->rewind(); $breadcrumb->next(); - $this->assertTrue($breadcrumb->isLast()); + self::assertTrue($breadcrumb->isLast()); } - /** - * @return void - */ public function testICanSetAndRetrieveAllInformationOnItem(): void { $item = (new BreadcrumbItem('item 1')) @@ -114,9 +80,9 @@ public function testICanSetAndRetrieveAllInformationOnItem(): void ->setIsActive(true) ; - $this->assertSame('item 1', $item->getName()); - $this->assertSame('icon', $item->getIcon()); - $this->assertSame('uri', $item->getUri()); - $this->assertTrue($item->isActive()); + self::assertSame('item 1', $item->getName()); + self::assertSame('icon', $item->getIcon()); + self::assertSame('uri', $item->getUri()); + self::assertTrue($item->isActive()); } } diff --git a/tests/unit/CarouselTest.php b/tests/Unit/CarouselTest.php similarity index 65% rename from tests/unit/CarouselTest.php rename to tests/Unit/CarouselTest.php index f720706..be6220d 100644 --- a/tests/unit/CarouselTest.php +++ b/tests/Unit/CarouselTest.php @@ -15,26 +15,8 @@ use Eureka\Component\Web\Carousel\CarouselItem; use PHPUnit\Framework\TestCase; -/** - * Class CarouselTest - * - * @author Romain Cottard - */ class CarouselTest extends TestCase { - /** - * @return void - */ - public function testICanInitializeCarousel(): void - { - $carousel = new Carousel(); - - $this->assertInstanceOf(Carousel::class, $carousel); - } - - /** - * @return void - */ public function testICanPushAndPopItemToCarousel(): void { $carousel = new Carousel(); @@ -43,12 +25,9 @@ public function testICanPushAndPopItemToCarousel(): void $item = $carousel->pop(); - $this->assertEquals('item 2', $item->getTitle()); + self::assertEquals('item 2', $item->getTitle()); } - /** - * @return void - */ public function testICanIterateOnCarouselInstance(): void { $carousel = new Carousel(); @@ -57,26 +36,19 @@ public function testICanIterateOnCarouselInstance(): void foreach ($carousel as $index => $item) { /** @var CarouselItem $item */ - $this->assertEquals('item ' . ($index + 1), $item->getTitle()); + self::assertEquals('item ' . ($index + 1), $item->getTitle()); } } - /** - * @return void - */ public function testICanCountItemInCarousel(): void { $carousel = new Carousel(); $carousel->push(new CarouselItem('item 1')); $carousel->push(new CarouselItem('item 2')); - $this->assertCount(2, $carousel); - $this->assertEquals(2, count($carousel)); + self::assertCount(2, $carousel); } - /** - * @return void - */ public function testICanCheckIfItIsTheFirstItemDuringTheIterationOnCarousel(): void { $carousel = new Carousel(); @@ -85,12 +57,9 @@ public function testICanCheckIfItIsTheFirstItemDuringTheIterationOnCarousel(): v $carousel->rewind(); - $this->assertTrue($carousel->isFirst()); + self::assertTrue($carousel->isFirst()); } - /** - * @return void - */ public function testICanCheckIfItIsTheLastItemDuringTheIterationOnCarousel(): void { $carousel = new Carousel(); @@ -100,12 +69,9 @@ public function testICanCheckIfItIsTheLastItemDuringTheIterationOnCarousel(): vo $carousel->rewind(); $carousel->next(); - $this->assertTrue($carousel->isLast()); + self::assertTrue($carousel->isLast()); } - /** - * @return void - */ public function testICanSetAndRetrieveAllInformationOnItem(): void { $item = (new CarouselItem('')) @@ -117,11 +83,11 @@ public function testICanSetAndRetrieveAllInformationOnItem(): void ->setIsActive(true) ; - $this->assertSame('title', $item->getTitle()); - $this->assertSame('sub title', $item->getSubTitle()); - $this->assertSame('image', $item->getImage()); - $this->assertSame('uri', $item->getLinkUri()); - $this->assertSame('uri title', $item->getLinkTitle()); - $this->assertTrue($item->isActive()); + self::assertSame('title', $item->getTitle()); + self::assertSame('sub title', $item->getSubTitle()); + self::assertSame('image', $item->getImage()); + self::assertSame('uri', $item->getLinkUri()); + self::assertSame('uri title', $item->getLinkTitle()); + self::assertTrue($item->isActive()); } } diff --git a/tests/unit/MenuTest.php b/tests/Unit/MenuTest.php similarity index 61% rename from tests/unit/MenuTest.php rename to tests/Unit/MenuTest.php index 32ceb57..0b793fc 100644 --- a/tests/unit/MenuTest.php +++ b/tests/Unit/MenuTest.php @@ -15,41 +15,19 @@ use Eureka\Component\Web\Menu\MenuItem; use PHPUnit\Framework\TestCase; -/** - * Class MenuTest - * - * @author Romain Cottard - */ class MenuTest extends TestCase { - /** - * @return void - */ - public function testICanInitializeMenu(): void - { - $menu = new Menu(); - - $this->assertInstanceOf(Menu::class, $menu); - } - - /** - * @return void - */ public function testICanPushAndGetItemToMenu(): void { $menu = new Menu(); $menu->push(new MenuItem('item 1')); $menu->push(new MenuItem('item 2')); - /** @var MenuItem $item */ $item = $menu->get('item 2'); - $this->assertEquals('item 2', $item->getName()); + self::assertEquals('item 2', $item?->getName()); } - /** - * @return void - */ public function testICanIterateOnMenuInstance(): void { $menu = new Menu(); @@ -58,26 +36,19 @@ public function testICanIterateOnMenuInstance(): void foreach ($menu as $index => $item) { /** @var MenuItem $item */ - $this->assertEquals('item ' . ($index + 1), $item->getName()); + self::assertEquals('item ' . ($index + 1), $item->getName()); } } - /** - * @return void - */ public function testICanCountItemInMenu(): void { $menu = new Menu(); $menu->push(new MenuItem('item 1')); $menu->push(new MenuItem('item 2')); - $this->assertCount(2, $menu); - $this->assertEquals(2, count($menu)); + self::assertCount(2, $menu); } - /** - * @return void - */ public function testICanCheckIfItIsTheFirstItemDuringTheIterationOnMenu(): void { $menu = new Menu(); @@ -86,12 +57,9 @@ public function testICanCheckIfItIsTheFirstItemDuringTheIterationOnMenu(): void $menu->rewind(); - $this->assertTrue($menu->isFirst()); + self::assertTrue($menu->isFirst()); } - /** - * @return void - */ public function testICanCheckIfItIsTheLastItemDuringTheIterationOnMenu(): void { $menu = new Menu(); @@ -101,12 +69,9 @@ public function testICanCheckIfItIsTheLastItemDuringTheIterationOnMenu(): void $menu->rewind(); $menu->next(); - $this->assertTrue($menu->isLast()); + self::assertTrue($menu->isLast()); } - /** - * @return void - */ public function testICanSetAndRetrieveAllInformationOnItem(): void { $item = (new MenuItem('')) @@ -118,13 +83,13 @@ public function testICanSetAndRetrieveAllInformationOnItem(): void ->setIsActive(true) ; - $this->assertSame('item 1', $item->getName()); - $this->assertSame('icon', $item->getIcon()); - $this->assertSame('uri', $item->getUri()); - $this->assertInstanceOf(Menu::class, $item->getSubmenu()); - $this->assertTrue($item->isActive()); - $this->assertTrue($item->isDivider()); - $this->assertTrue($item->hasIcon()); - $this->assertTrue($item->hasSubmenu()); + self::assertSame('item 1', $item->getName()); + self::assertSame('icon', $item->getIcon()); + self::assertSame('uri', $item->getUri()); + self::assertInstanceOf(Menu::class, $item->getSubmenu()); + self::assertTrue($item->isActive()); + self::assertTrue($item->isDivider()); + self::assertTrue($item->hasIcon()); + self::assertTrue($item->hasSubmenu()); } } diff --git a/tests/unit/MockControllerTest.php b/tests/Unit/MockControllerTest.php similarity index 52% rename from tests/unit/MockControllerTest.php rename to tests/Unit/MockControllerTest.php index 568512b..6ef9304 100644 --- a/tests/unit/MockControllerTest.php +++ b/tests/Unit/MockControllerTest.php @@ -21,9 +21,7 @@ use PHPUnit\Framework\TestCase; /** - * Class MockControllerTest - * - * @author Romain Cottard + * @phpstan-import-type MenuConfigType from MenuControllerAwareTrait */ class MockControllerTest extends TestCase { @@ -31,15 +29,12 @@ class MockControllerTest extends TestCase use MetaControllerAwareTrait; use SessionAwareTrait; - /** - * @return void - */ public function testICanSetAndGetSessionFromTrait(): void { $_SESSION = []; $this->setSession(new Session()); - $this->assertInstanceOf(Session::class, $this->getSession()); + self::assertInstanceOf(Session::class, $this->getSession()); } public function testICanAddAndRetrieveFlashNotifications(): void @@ -47,18 +42,18 @@ public function testICanAddAndRetrieveFlashNotifications(): void $this->setSession(new Session()); $this->addFlashNotification('flash notification', NotificationType::Success); - $this->assertSame(['flash notification'], $this->getFlashNotification(NotificationType::Success)); + self::assertSame(['flash notification'], $this->getFlashNotification(NotificationType::Success)); $this->addFlashNotification('other notification', NotificationType::Info); - $this->assertEquals( + self::assertEquals( (object) [ NotificationType::Success->value => ['flash notification'], NotificationType::Info->value => ['other notification'], NotificationType::Warning->value => [], NotificationType::Error->value => [], ], - $this->getAllFlashNotification() + $this->getAllFlashNotification(), ); } @@ -68,7 +63,7 @@ public function testICanSetAndRetrieveFormErrors(): void $this->setFormErrors(['invalid password']); - $this->assertSame(['invalid password'], $this->getFormErrors()); + self::assertSame(['invalid password'], $this->getFormErrors()); } public function testICanSetAndRetrieveMetaConfig(): void @@ -76,16 +71,17 @@ public function testICanSetAndRetrieveMetaConfig(): void $this->setMetaConfig([ 'copyright' => [ 'year' => 'now', - 'name' => 'me' + 'name' => 'me', ], 'title' => 'test', ]); + /** @var array{copyright: array{year?: string, name?: string}, title?: string} $meta */ $meta = $this->getMeta(); - $this->assertEquals(date('Y'), $meta['copyright']['year'] ?? ''); - $this->assertEquals('me', $meta['copyright']['name'] ?? ''); - $this->assertEquals('test', $meta['title'] ?? ''); + self::assertEquals(date('Y'), $meta['copyright']['year'] ?? ''); + self::assertEquals('me', $meta['copyright']['name'] ?? ''); + self::assertEquals('test', $meta['title'] ?? ''); } public function testICanSetMenuConfigAndGetPartialMenuCollectionWhenIAmNotLogged(): void @@ -93,16 +89,16 @@ public function testICanSetMenuConfigAndGetPartialMenuCollectionWhenIAmNotLogged $this->setMenuConfig($this->getTestMenuConfig(), 'open'); $menu = $this->getMenu(); - $subMenu = $menu->get('Test') && $menu->get('Test')->getSubmenu() ? $menu->get('Test')->getSubmenu() : new Menu(); + $subMenu = $menu->get('Test')?->getSubmenu() ?? new Menu(); - $this->assertInstanceOf(MenuItem::class, $menu->get('Home')); - $this->assertInstanceOf(MenuItem::class, $menu->get('Test')); - $this->assertInstanceOf(MenuItem::class, $subMenu->get('Sub Test 1')); - $this->assertInstanceOf(MenuItem::class, $subMenu->get('Sub Test 2')); - $this->assertInstanceOf(MenuItem::class, $subMenu->get('Sub Test 3')); + self::assertInstanceOf(MenuItem::class, $menu->get('Home')); + self::assertInstanceOf(MenuItem::class, $menu->get('Test')); + self::assertInstanceOf(MenuItem::class, $subMenu->get('Sub Test 1')); + self::assertInstanceOf(MenuItem::class, $subMenu->get('Sub Test 2')); + self::assertInstanceOf(MenuItem::class, $subMenu->get('Sub Test 3')); - $this->assertNull($menu->get('Logged')); - $this->assertNull($subMenu->get('Sub Test 4')); + self::assertNull($menu->get('Logged')); + self::assertNull($subMenu->get('Sub Test 4')); } public function testICanSetMenuConfigAndGetCompleteMenuCollectionWhenIAmLogged(): void @@ -112,60 +108,60 @@ public function testICanSetMenuConfigAndGetCompleteMenuCollectionWhenIAmLogged() $menu = $this->getMenu(true, true); $item1 = $menu->get('Home') ?? new MenuItem('void'); - $this->assertEquals('Home', $item1->getName()); - $this->assertEquals('', $item1->getIcon()); - $this->assertEquals('/home', $item1->getUri()); - $this->assertFalse($item1->hasSubmenu()); - $this->assertFalse($item1->hasIcon()); - $this->assertFalse($item1->isActive()); + self::assertEquals('Home', $item1->getName()); + self::assertEquals('', $item1->getIcon()); + self::assertEquals('/home', $item1->getUri()); + self::assertFalse($item1->hasSubmenu()); + self::assertFalse($item1->hasIcon()); + self::assertFalse($item1->isActive()); $item2 = $menu->get('Test') ?? new MenuItem('void'); - $this->assertEquals('Test', $item2->getName()); - $this->assertEquals('ico', $item2->getIcon()); - $this->assertEquals('javascript:void(0);', $item2->getUri()); - $this->assertTrue($item2->hasSubmenu()); - $this->assertTrue($item2->hasIcon()); - $this->assertTrue($item2->isActive()); + self::assertEquals('Test', $item2->getName()); + self::assertEquals('ico', $item2->getIcon()); + self::assertEquals('javascript:void(0);', $item2->getUri()); + self::assertTrue($item2->hasSubmenu()); + self::assertTrue($item2->hasIcon()); + self::assertTrue($item2->isActive()); $item3 = $menu->get('Logged') ?? new MenuItem('void'); - $this->assertEquals('Logged', $item3->getName()); - $this->assertEquals('', $item3->getIcon()); - $this->assertEquals('/home', $item3->getUri()); - $this->assertFalse($item3->hasSubmenu()); - $this->assertFalse($item3->hasIcon()); - $this->assertFalse($item3->isActive()); - - $subItem1 = $item2->getSubmenu() && $item2->getSubmenu()->get('Sub Test 1') ? $item2->getSubmenu()->get('Sub Test 1') : new MenuItem('void'); - $this->assertEquals('Sub Test 1', $subItem1->getName()); - $this->assertEquals('', $subItem1->getIcon()); - $this->assertEquals('javascript:void(0);', $subItem1->getUri()); - $this->assertFalse($subItem1->hasSubmenu()); - $this->assertFalse($subItem1->hasIcon()); - $this->assertFalse($subItem1->isActive()); - - $subItem2 = $item2->getSubmenu() && $item2->getSubmenu()->get('Sub Test 2') ? $item2->getSubmenu()->get('Sub Test 2') : new MenuItem('void'); - $this->assertEquals('Sub Test 2', $subItem2->getName()); - $this->assertEquals('', $subItem2->getIcon()); - $this->assertEquals('http://external.com', $subItem2->getUri()); - $this->assertFalse($subItem2->hasSubmenu()); - $this->assertFalse($subItem2->hasIcon()); - $this->assertFalse($subItem2->isActive()); - - $subItem3 = $item2->getSubmenu() && $item2->getSubmenu()->get('Sub Test 3') ? $item2->getSubmenu()->get('Sub Test 3') : new MenuItem('void'); - $this->assertEquals('Sub Test 3', $subItem3->getName()); - $this->assertEquals('', $subItem3->getIcon()); - $this->assertEquals('/home', $subItem3->getUri()); - $this->assertFalse($subItem3->hasSubmenu()); - $this->assertFalse($subItem3->hasIcon()); - $this->assertTrue($subItem3->isActive()); - - $subItem4 = $item2->getSubmenu() && $item2->getSubmenu()->get('Sub Test 4') ? $item2->getSubmenu()->get('Sub Test 4') : new MenuItem('void'); - $this->assertEquals('Sub Test 4', $subItem4->getName()); - $this->assertEquals('', $subItem4->getIcon()); - $this->assertEquals('/home', $subItem4->getUri()); - $this->assertFalse($subItem4->hasSubmenu()); - $this->assertFalse($subItem4->hasIcon()); - $this->assertFalse($subItem4->isActive()); + self::assertEquals('Logged', $item3->getName()); + self::assertEquals('', $item3->getIcon()); + self::assertEquals('/home', $item3->getUri()); + self::assertFalse($item3->hasSubmenu()); + self::assertFalse($item3->hasIcon()); + self::assertFalse($item3->isActive()); + + $subItem1 = $item2->getSubmenu()?->get('Sub Test 1') ?? new MenuItem('void'); + self::assertEquals('Sub Test 1', $subItem1->getName()); + self::assertEquals('', $subItem1->getIcon()); + self::assertEquals('javascript:void(0);', $subItem1->getUri()); + self::assertFalse($subItem1->hasSubmenu()); + self::assertFalse($subItem1->hasIcon()); + self::assertFalse($subItem1->isActive()); + + $subItem2 = $item2->getSubmenu()?->get('Sub Test 2') ?? new MenuItem('void'); + self::assertEquals('Sub Test 2', $subItem2->getName()); + self::assertEquals('', $subItem2->getIcon()); + self::assertEquals('http://external.com', $subItem2->getUri()); + self::assertFalse($subItem2->hasSubmenu()); + self::assertFalse($subItem2->hasIcon()); + self::assertFalse($subItem2->isActive()); + + $subItem3 = $item2->getSubmenu()?->get('Sub Test 3') ?? new MenuItem('void'); + self::assertEquals('Sub Test 3', $subItem3->getName()); + self::assertEquals('', $subItem3->getIcon()); + self::assertEquals('/home', $subItem3->getUri()); + self::assertFalse($subItem3->hasSubmenu()); + self::assertFalse($subItem3->hasIcon()); + self::assertTrue($subItem3->isActive()); + + $subItem4 = $item2->getSubmenu()?->get('Sub Test 4') ?? new MenuItem('void'); + self::assertEquals('Sub Test 4', $subItem4->getName()); + self::assertEquals('', $subItem4->getIcon()); + self::assertEquals('/home', $subItem4->getUri()); + self::assertFalse($subItem4->hasSubmenu()); + self::assertFalse($subItem4->hasIcon()); + self::assertFalse($subItem4->isActive()); } /** * Emulate getRoute() in application. @@ -182,9 +178,7 @@ protected function getRoute(): array /** * Emulate getRouteUri() in application. * - * @param string $routeName - * @param array $params - * @return string + * @param array $params */ protected function getRouteUri(string $routeName, array $params = []): string { @@ -192,7 +186,7 @@ protected function getRouteUri(string $routeName, array $params = []): string } /** - * @return array> + * @return MenuConfigType[] */ private function getTestMenuConfig(): array { diff --git a/tests/unit/NotificationTest.php b/tests/Unit/NotificationTest.php similarity index 73% rename from tests/unit/NotificationTest.php rename to tests/Unit/NotificationTest.php index 428ecd7..278db65 100644 --- a/tests/unit/NotificationTest.php +++ b/tests/Unit/NotificationTest.php @@ -15,28 +15,11 @@ use Eureka\Component\Web\Notification\NotificationBootstrap; use Eureka\Component\Web\Notification\NotificationInterface; use Eureka\Component\Web\Notification\NotificationType; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -/** - * Class NotificationTest - * - * @author Romain Cottard - */ class NotificationTest extends TestCase { - /** - * @return void - */ - public function testICanInitializeNotification(): void - { - $notifications = new NotificationCollection(); - - $this->assertInstanceOf(NotificationCollection::class, $notifications); - } - - /** - * @return void - */ public function testICanPushAndGetItemToNotification(): void { $notifications = new NotificationCollection(); @@ -45,12 +28,9 @@ public function testICanPushAndGetItemToNotification(): void $item = $notifications->pop(); - $this->assertEquals('item 2', $item->getMessage()); + self::assertEquals('item 2', $item->getMessage()); } - /** - * @return void - */ public function testICanIterateOnNotificationInstance(): void { $notifications = new NotificationCollection(); @@ -59,25 +39,19 @@ public function testICanIterateOnNotificationInstance(): void /** @var NotificationInterface $item */ foreach ($notifications as $index => $item) { - $this->assertEquals('item ' . ($index + 1), $item->getMessage()); + self::assertEquals('item ' . ($index + 1), $item->getMessage()); } } - /** - * @return void - */ public function testICanCountItemInNotification(): void { $notifications = new NotificationCollection(); $notifications->push(new NotificationBootstrap('item 1')); $notifications->push(new NotificationBootstrap('item 2')); - $this->assertCount(2, $notifications); + self::assertCount(2, $notifications); } - /** - * @return void - */ public function testICanCheckIfItIsTheFirstItemDuringTheIterationOnNotification(): void { $notifications = new NotificationCollection(); @@ -86,12 +60,9 @@ public function testICanCheckIfItIsTheFirstItemDuringTheIterationOnNotification( $notifications->rewind(); - $this->assertTrue($notifications->isFirst()); + self::assertTrue($notifications->isFirst()); } - /** - * @return void - */ public function testICanCheckIfItIsTheLastItemDuringTheIterationOnNotification(): void { $notifications = new NotificationCollection(); @@ -101,31 +72,29 @@ public function testICanCheckIfItIsTheLastItemDuringTheIterationOnNotification() $notifications->rewind(); $notifications->next(); - $this->assertTrue($notifications->isLast()); + self::assertTrue($notifications->isLast()); } - /** - * @dataProvider dataProviderNotification - */ + #[DataProvider('dataProviderNotification')] public function testICanSetAndRetrieveAllInformationOnItem( string $message, NotificationType $type, string $css, string $icon, - string $header + string $header, ): void { $notification = new NotificationBootstrap($message, $type); - $this->assertSame($message, $notification->getMessage()); - $this->assertSame($type, $notification->getType()); - $this->assertSame($css, $notification->getCss()); - $this->assertSame($icon, $notification->getIcon()); - $this->assertSame($header, $notification->getHeader()); + self::assertSame($message, $notification->getMessage()); + self::assertSame($type, $notification->getType()); + self::assertSame($css, $notification->getCss()); + self::assertSame($icon, $notification->getIcon()); + self::assertSame($header, $notification->getHeader()); $notification->setMessage($message); $notification->setType($type); - $this->assertSame($message, $notification->getMessage()); - $this->assertSame($type, $notification->getType()); + self::assertSame($message, $notification->getMessage()); + self::assertSame($type, $notification->getType()); } /** diff --git a/tests/unit/SessionTest.php b/tests/Unit/SessionTest.php similarity index 54% rename from tests/unit/SessionTest.php rename to tests/Unit/SessionTest.php index dc3438b..8caedfd 100644 --- a/tests/unit/SessionTest.php +++ b/tests/Unit/SessionTest.php @@ -14,62 +14,38 @@ use Eureka\Component\Web\Session\Session; use PHPUnit\Framework\TestCase; -/** - * Class SessionTest - * - * @author Romain Cottard - */ class SessionTest extends TestCase { - /** - * @return void - */ - public function testICanInitializeSessionService(): void - { - $session = new Session(); - - $this->assertInstanceOf(Session::class, $session); - } - - /** - * @return void - */ public function testICanSetAndRetrieveValueInSession(): void { $session = new Session(); - $this->assertFalse($session->has('foo')); + self::assertFalse($session->has('foo')); $session->set('foo', 'bar'); - $this->assertTrue($session->has('foo')); - $this->assertEquals('bar', $session->get('foo')); - $this->assertEquals('baz', $session->get('foz', 'baz')); + self::assertTrue($session->has('foo')); + self::assertEquals('bar', $session->get('foo')); + self::assertEquals('baz', $session->get('foz', 'baz')); $session->remove('foo'); - $this->assertFalse($session->has('foo')); + self::assertFalse($session->has('foo')); } - /** - * @return void - */ public function testICanSetAndRetrieveEphemeralValueInSession(): void { $session = new Session(); - $this->assertFalse($session->hasFlash('foo')); + self::assertFalse($session->hasFlash('foo')); $session->setFlash('foo', 'bar'); - $this->assertTrue($session->hasFlash('foo')); - $this->assertEquals('bar', $session->getFlash('foo')); - $this->assertEquals('baz', $session->getFlash('foz', 'baz')); + self::assertTrue($session->hasFlash('foo')); + self::assertEquals('bar', $session->getFlash('foo')); + self::assertEquals('baz', $session->getFlash('foz', 'baz')); $session->clearFlash(); $session->clearFlash(); } - /** - * @return void - */ public function testICanCleanEphemeralValueInSession(): void { $_SESSION = []; @@ -80,10 +56,10 @@ public function testICanCleanEphemeralValueInSession(): void //~ First call render ephemeral "not active" (= removed after the second call) $session->clearFlash(); - $this->assertTrue($session->hasFlash('foo')); + self::assertTrue($session->hasFlash('foo')); //~ First call render ephemeral "not active" (= removed after the second call) $session->clearFlash(); - $this->assertFalse($session->hasFlash('foo')); + self::assertFalse($session->hasFlash('foo')); } }