diff --git a/.env.dusk.local b/.env.dusk.local deleted file mode 100644 index 6f0c215..0000000 --- a/.env.dusk.local +++ /dev/null @@ -1,24 +0,0 @@ -APP_NAME=DishPlanner -APP_ENV=testing -APP_KEY=base64:KSKZNT+cJuaBRBv4Y2HQqav6hzREKoLkNIKN8yszU1Q= -APP_DEBUG=true -APP_URL=http://dishplanner_app:8000 - -LOG_CHANNEL=single - -# Test database -DB_CONNECTION=mysql -DB_HOST=db -DB_PORT=3306 -DB_DATABASE=dishplanner_test -DB_USERNAME=dishplanner -DB_PASSWORD=dishplanner - -BROADCAST_DRIVER=log -CACHE_DRIVER=array -FILESYSTEM_DISK=local -QUEUE_CONNECTION=sync -SESSION_DRIVER=file -SESSION_LIFETIME=120 - -MAIL_MAILER=array \ No newline at end of file diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index a38115d..9af7437 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -65,4 +65,16 @@ jobs: run: vendor/bin/phpstan analyse --memory-limit=1G - name: Tests - run: php -d memory_limit=512M vendor/bin/phpunit + run: vendor/bin/pest + + - name: Install frontend dependencies + run: npm ci + + - name: Install Playwright browser + run: npx playwright install --with-deps chromium + + - name: Build frontend assets + run: npm run build + + - name: Browser tests + run: vendor/bin/pest tests/Browser diff --git a/.gitignore b/.gitignore index cc770c4..154859c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.phpunit.cache +/tests/Browser/Screenshots /coverage /node_modules /public/build diff --git a/Dockerfile.dev b/Dockerfile.dev index c36d143..5471195 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -14,6 +14,7 @@ RUN apk add --no-cache \ # Install PHP extensions including xdebug for development RUN install-php-extensions \ pdo_mysql \ + sockets \ opcache \ zip \ gd \ diff --git a/composer.json b/composer.json index 06e97c5..93ab662 100644 --- a/composer.json +++ b/composer.json @@ -18,14 +18,15 @@ "require-dev": { "fakerphp/faker": "^1.23", "larastan/larastan": "^3.10", - "laravel/dusk": "^8.3", "laravel/pail": "^1.1", "laravel/pint": "^1.13", "laravel/sail": "^1.26", "mockery/mockery": "^1.6", "nunomaduro/collision": "^8.1", - "phpstan/phpstan-mockery": "^2.0", - "phpunit/phpunit": "^11.0.1" + "pestphp/pest": "^4.7", + "pestphp/pest-plugin-browser": "^4.3", + "pestphp/pest-plugin-laravel": "^4.0", + "phpstan/phpstan-mockery": "^2.0" }, "autoload": { "psr-4": { @@ -61,7 +62,8 @@ "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite" ], "test": [ - "@php artisan test" + "@php artisan test", + "@php vendor/bin/pest tests/Browser" ], "test:coverage": [ "Composer\\Config::disableProcessTimeout", diff --git a/composer.lock b/composer.lock index 2a576e1..1259044 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2d432ae148aec8fd888017ec1261a7a3", + "content-hash": "111133a6973d69029180126b5262f4ab", "packages": [ { "name": "brick/math", @@ -6162,6 +6162,1563 @@ } ], "packages-dev": [ + { + "name": "amphp/amp", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "73c38b323ff8d790abf0f76c56fcc892bda4111a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/73c38b323ff8d790abf0f76c56fcc892bda4111a", + "reference": "73c38b323ff8d790abf0f76c56fcc892bda4111a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v3.1.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-07-19T17:59:20+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/parser": "^1.1", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2.3" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v2.1.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T17:10:27+00:00" + }, + { + "name": "amphp/cache", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/cache.git", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Cache\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A fiber-aware cache API based on Amp and Revolt.", + "homepage": "https://amphp.org/cache", + "support": { + "issues": "https://github.com/amphp/cache/issues", + "source": "https://github.com/amphp/cache/tree/v2.0.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:38:06+00:00" + }, + { + "name": "amphp/dns", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/dns.git", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/dns/zipball/78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/process": "^2", + "daverandom/libdns": "^2.0.2", + "ext-filter": "*", + "ext-json": "*", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Wright", + "email": "addr@daverandom.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Async DNS resolution for Amp.", + "homepage": "https://github.com/amphp/dns", + "keywords": [ + "amp", + "amphp", + "async", + "client", + "dns", + "resolve" + ], + "support": { + "issues": "https://github.com/amphp/dns/issues", + "source": "https://github.com/amphp/dns/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-01-19T15:43:40+00:00" + }, + { + "name": "amphp/hpack", + "version": "v3.2.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/hpack.git", + "reference": "291da27078e7e149a9bad4d08ff05bf7d81c89f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/hpack/zipball/291da27078e7e149a9bad4d08ff05bf7d81c89f4", + "reference": "291da27078e7e149a9bad4d08ff05bf7d81c89f4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "http2jp/hpack-test-case": "^1", + "nikic/php-fuzzer": "^0.0.11", + "phpunit/phpunit": "^7 | ^8 | ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Amp\\Http\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "HTTP/2 HPack implementation.", + "homepage": "https://github.com/amphp/hpack", + "keywords": [ + "headers", + "hpack", + "http-2" + ], + "support": { + "issues": "https://github.com/amphp/hpack/issues", + "source": "https://github.com/amphp/hpack/tree/v3.2.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-05-03T19:28:59+00:00" + }, + { + "name": "amphp/http", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/http.git", + "reference": "3680d80bd38b5d6f3c2cef2214ca6dd6cef26588" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/http/zipball/3680d80bd38b5d6f3c2cef2214ca6dd6cef26588", + "reference": "3680d80bd38b5d6f3c2cef2214ca6dd6cef26588", + "shasum": "" + }, + "require": { + "amphp/hpack": "^3", + "amphp/parser": "^1.1", + "league/uri-components": "^2.4.2 | ^7.1", + "php": ">=8.1", + "psr/http-message": "^1 | ^2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "league/uri": "^6.8 | ^7.1", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.26.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/constants.php" + ], + "psr-4": { + "Amp\\Http\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Basic HTTP primitives which can be shared by servers and clients.", + "support": { + "issues": "https://github.com/amphp/http/issues", + "source": "https://github.com/amphp/http/tree/v2.1.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-11-23T14:57:26+00:00" + }, + { + "name": "amphp/http-client", + "version": "v5.3.6", + "source": { + "type": "git", + "url": "https://github.com/amphp/http-client.git", + "reference": "ca155026acafa74a612d776a97202d53077fee86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/http-client/zipball/ca155026acafa74a612d776a97202d53077fee86", + "reference": "ca155026acafa74a612d776a97202d53077fee86", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/hpack": "^3", + "amphp/http": "^2", + "amphp/pipeline": "^1", + "amphp/socket": "^2", + "amphp/sync": "^2", + "league/uri": "^7", + "league/uri-components": "^7", + "league/uri-interfaces": "^7.1", + "php": ">=8.1", + "psr/http-message": "^1 | ^2", + "revolt/event-loop": "^1" + }, + "conflict": { + "amphp/file": "<3 | >=5" + }, + "require-dev": { + "amphp/file": "^3 | ^4", + "amphp/http-server": "^3", + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "ext-json": "*", + "kelunik/link-header-rfc5988": "^1", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "suggest": { + "amphp/file": "Required for file request bodies and HTTP archive logging", + "ext-json": "Required for logging HTTP archives", + "ext-zlib": "Allows using compression for response bodies." + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\Http\\Client\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "An advanced async HTTP client library for PHP, enabling efficient, non-blocking, and concurrent requests and responses.", + "homepage": "https://amphp.org/http-client", + "keywords": [ + "async", + "client", + "concurrent", + "http", + "non-blocking", + "rest" + ], + "support": { + "issues": "https://github.com/amphp/http-client/issues", + "source": "https://github.com/amphp/http-client/tree/v5.3.6" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-05-15T23:29:38+00:00" + }, + { + "name": "amphp/http-server", + "version": "v3.4.6", + "source": { + "type": "git", + "url": "https://github.com/amphp/http-server.git", + "reference": "8a971bf92cf8cf2bc511f37a75b39126d5305315" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/http-server/zipball/8a971bf92cf8cf2bc511f37a75b39126d5305315", + "reference": "8a971bf92cf8cf2bc511f37a75b39126d5305315", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/hpack": "^3", + "amphp/http": "^2", + "amphp/pipeline": "^1", + "amphp/socket": "^2.1", + "amphp/sync": "^2.2", + "league/uri": "^7.1", + "league/uri-interfaces": "^7.1", + "php": ">=8.1", + "psr/http-message": "^1 | ^2", + "psr/log": "^1 | ^2 | ^3", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/http-client": "^5", + "amphp/log": "^2", + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "league/uri-components": "^7.1", + "monolog/monolog": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "suggest": { + "ext-zlib": "Allows GZip compression of response bodies" + }, + "type": "library", + "autoload": { + "files": [ + "src/Driver/functions.php", + "src/Middleware/functions.php", + "src/functions.php" + ], + "psr-4": { + "Amp\\Http\\Server\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "A non-blocking HTTP application server for PHP based on Amp.", + "homepage": "https://github.com/amphp/http-server", + "keywords": [ + "amp", + "amphp", + "async", + "http", + "non-blocking", + "server" + ], + "support": { + "issues": "https://github.com/amphp/http-server/issues", + "source": "https://github.com/amphp/http-server/tree/v3.4.6" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-06-27T10:31:48+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T19:16:53+00:00" + }, + { + "name": "amphp/pipeline", + "version": "v1.2.7", + "source": { + "type": "git", + "url": "https://github.com/amphp/pipeline.git", + "reference": "cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17", + "reference": "cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Pipeline\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous iterators and operators.", + "homepage": "https://amphp.org/pipeline", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "iterator", + "non-blocking" + ], + "support": { + "issues": "https://github.com/amphp/pipeline/issues", + "source": "https://github.com/amphp/pipeline/tree/v1.2.7" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-07-26T14:50:43+00:00" + }, + { + "name": "amphp/process", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "583959df17d00304ad7b0b32285373f985935643" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/583959df17d00304ad7b0b32285373f985935643", + "reference": "583959df17d00304ad7b0b32285373f985935643", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A fiber-aware process manager based on Amp and Revolt.", + "homepage": "https://amphp.org/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v2.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-05-31T15:11:55+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/fdf2834d78cebb0205fb2672676c1b1eb84371f0", + "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "ext-json": "*", + "ext-zlib": "*", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/v1.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-04-05T15:59:53+00:00" + }, + { + "name": "amphp/socket", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/socket.git", + "reference": "dadb63c5d3179fd83803e29dfeac27350e619314" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/socket/zipball/dadb63c5d3179fd83803e29dfeac27350e619314", + "reference": "dadb63c5d3179fd83803e29dfeac27350e619314", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/dns": "^2", + "ext-openssl": "*", + "kelunik/certificate": "^1.1", + "league/uri": "^7", + "league/uri-interfaces": "^7", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/process": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php", + "src/SocketAddress/functions.php" + ], + "psr-4": { + "Amp\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.", + "homepage": "https://github.com/amphp/socket", + "keywords": [ + "amp", + "async", + "encryption", + "non-blocking", + "sockets", + "tcp", + "tls" + ], + "support": { + "issues": "https://github.com/amphp/socket/issues", + "source": "https://github.com/amphp/socket/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2026-04-19T15:09:56+00:00" + }, + { + "name": "amphp/sync", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-08-03T19:31:26+00:00" + }, + { + "name": "amphp/websocket", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/amphp/websocket.git", + "reference": "963904b6a883c4b62d9222d1d9749814fac96a3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/websocket/zipball/963904b6a883c4b62d9222d1d9749814fac96a3b", + "reference": "963904b6a883c4b62d9222d1d9749814fac96a3b", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/parser": "^1", + "amphp/pipeline": "^1", + "amphp/socket": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "suggest": { + "ext-zlib": "Required for compression" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Websocket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + } + ], + "description": "Shared code for websocket servers and clients.", + "homepage": "https://github.com/amphp/websocket", + "keywords": [ + "amp", + "amphp", + "async", + "http", + "non-blocking", + "websocket" + ], + "support": { + "issues": "https://github.com/amphp/websocket/issues", + "source": "https://github.com/amphp/websocket/tree/v2.0.4" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-10-28T21:28:45+00:00" + }, + { + "name": "amphp/websocket-client", + "version": "v2.0.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/websocket-client.git", + "reference": "dc033fdce0af56295a23f63ac4f579b34d470d6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/websocket-client/zipball/dc033fdce0af56295a23f63ac4f579b34d470d6c", + "reference": "dc033fdce0af56295a23f63ac4f579b34d470d6c", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2.1", + "amphp/http": "^2.1", + "amphp/http-client": "^5", + "amphp/socket": "^2.2", + "amphp/websocket": "^2", + "league/uri": "^7.1", + "php": ">=8.1", + "psr/http-message": "^1|^2", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/http-server": "^3", + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/websocket-server": "^3|^4", + "phpunit/phpunit": "^9", + "psalm/phar": "~5.26.1", + "psr/log": "^1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Websocket\\Client\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Async WebSocket client for PHP based on Amp.", + "keywords": [ + "amp", + "amphp", + "async", + "client", + "http", + "non-blocking", + "websocket" + ], + "support": { + "issues": "https://github.com/amphp/websocket-client/issues", + "source": "https://github.com/amphp/websocket-client/tree/v2.0.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-08-24T17:25:34+00:00" + }, + { + "name": "brianium/paratest", + "version": "v7.20.0", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "81c80677c9ec0ed4ef16b246167f11dec81a6e3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/81c80677c9ec0ed4ef16b246167f11dec81a6e3d", + "reference": "81c80677c9ec0ed4ef16b246167f11dec81a6e3d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^1.3.0", + "jean85/pretty-package-versions": "^2.1.1", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "phpunit/php-code-coverage": "^12.5.3 || ^13.0.1", + "phpunit/php-file-iterator": "^6.0.1 || ^7", + "phpunit/php-timer": "^8 || ^9", + "phpunit/phpunit": "^12.5.14 || ^13.0.5", + "sebastian/environment": "^8.0.3 || ^9", + "symfony/console": "^7.4.7 || ^8.0.7", + "symfony/process": "^7.4.5 || ^8.0.5" + }, + "require-dev": { + "doctrine/coding-standard": "^14.0.0", + "ext-pcntl": "*", + "ext-pcov": "*", + "ext-posix": "*", + "phpstan/phpstan": "^2.1.44", + "phpstan/phpstan-deprecation-rules": "^2.0.4", + "phpstan/phpstan-phpunit": "^2.0.16", + "phpstan/phpstan-strict-rules": "^2.0.10", + "symfony/filesystem": "^7.4.6 || ^8.0.6" + }, + "bin": [ + "bin/paratest", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.20.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2026-03-29T15:46:14+00:00" + }, + { + "name": "composer/pcre", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<2.2.2" + }, + "require-dev": { + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2026-06-07T11:47:49+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "daverandom/libdns", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "Required for IDN support" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "LibDNS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "DNS protocol implementation written in pure PHP", + "keywords": [ + "dns" + ], + "support": { + "issues": "https://github.com/DaveRandom/LibDNS/issues", + "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" + }, + "time": "2024-04-12T12:12:48+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.6", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" + }, + "time": "2026-02-07T07:09:04+00:00" + }, { "name": "fakerphp/faker", "version": "v1.24.1", @@ -6225,6 +7782,67 @@ }, "time": "2024-11-21T13:46:39+00:00" }, + { + "name": "fidry/cpu-core-counter", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-08-14T07:29:31+00:00" + }, { "name": "filp/whoops", "version": "2.18.4", @@ -6392,6 +8010,124 @@ }, "time": "2026-01-28T22:20:33+00:00" }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", + "vimeo/psalm": "^4.3 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" + }, + "time": "2025-03-19T14:43:43+00:00" + }, + { + "name": "kelunik/certificate", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/kelunik/certificate.git", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=7.0" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^6 | 7 | ^8 | ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Kelunik\\Certificate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Access certificate details and transform between different formats.", + "keywords": [ + "DER", + "certificate", + "certificates", + "openssl", + "pem", + "x509" + ], + "support": { + "issues": "https://github.com/kelunik/certificate/issues", + "source": "https://github.com/kelunik/certificate/tree/v1.1.3" + }, + "time": "2023-02-03T21:26:53+00:00" + }, { "name": "larastan/larastan", "version": "v3.10.0", @@ -6482,80 +8218,6 @@ ], "time": "2026-05-28T08:00:58+00:00" }, - { - "name": "laravel/dusk", - "version": "v8.6.0", - "source": { - "type": "git", - "url": "https://github.com/laravel/dusk.git", - "reference": "e7fd48762c6a82ad2cd311db07587aa2a97ce143" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/dusk/zipball/e7fd48762c6a82ad2cd311db07587aa2a97ce143", - "reference": "e7fd48762c6a82ad2cd311db07587aa2a97ce143", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-zip": "*", - "guzzlehttp/guzzle": "^7.5", - "illuminate/console": "^10.0|^11.0|^12.0|^13.0", - "illuminate/support": "^10.0|^11.0|^12.0|^13.0", - "php": "^8.1", - "php-webdriver/webdriver": "^1.15.2", - "symfony/console": "^6.2|^7.0|^8.0", - "symfony/finder": "^6.2|^7.0|^8.0", - "symfony/process": "^6.2|^7.0|^8.0", - "vlucas/phpdotenv": "^5.2" - }, - "require-dev": { - "laravel/framework": "^10.0|^11.0|^12.0|^13.0", - "mockery/mockery": "^1.6", - "orchestra/testbench-core": "^8.19|^9.17|^10.8|^11.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.1|^11.0|^12.0.1", - "psy/psysh": "^0.11.12|^0.12", - "symfony/yaml": "^6.2|^7.0|^8.0" - }, - "suggest": { - "ext-pcntl": "Used to gracefully terminate Dusk when tests are running." - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Laravel\\Dusk\\DuskServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Laravel\\Dusk\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "Laravel Dusk provides simple end-to-end testing and browser automation.", - "keywords": [ - "laravel", - "testing", - "webdriver" - ], - "support": { - "issues": "https://github.com/laravel/dusk/issues", - "source": "https://github.com/laravel/dusk/tree/v8.6.0" - }, - "time": "2026-04-15T14:50:40+00:00" - }, { "name": "laravel/pail", "version": "v1.2.7", @@ -6769,6 +8431,90 @@ }, "time": "2026-08-10T13:09:27+00:00" }, + { + "name": "league/uri-components", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-components.git", + "reference": "848ff9db2f0be06229d6034b7c2e33d41b4fd675" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-components/zipball/848ff9db2f0be06229d6034b7c2e33d41b4fd675", + "reference": "848ff9db2f0be06229d6034b7c2e33d41b4fd675", + "shasum": "" + }, + "require": { + "league/uri": "^7.8.1", + "php": "^8.1" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-mbstring": "to use the sorting algorithm of URLSearchParams", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI components manipulation library", + "homepage": "http://uri.thephpleague.com", + "keywords": [ + "authority", + "components", + "fragment", + "host", + "middleware", + "modifier", + "path", + "port", + "query", + "rfc3986", + "scheme", + "uri", + "url", + "userinfo" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-components/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/nyamsprod", + "type": "github" + } + ], + "time": "2026-03-15T20:22:25+00:00" + }, { "name": "mockery/mockery", "version": "1.6.13", @@ -7007,6 +8753,553 @@ ], "time": "2026-07-15T19:09:14+00:00" }, + { + "name": "pestphp/pest", + "version": "v4.7.8", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest.git", + "reference": "5b2293f67adcf1b2320b33f521b94a692d18f360" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest/zipball/5b2293f67adcf1b2320b33f521b94a692d18f360", + "reference": "5b2293f67adcf1b2320b33f521b94a692d18f360", + "shasum": "" + }, + "require": { + "brianium/paratest": "^7.20.0", + "composer/xdebug-handler": "^3.0.5", + "nunomaduro/collision": "^8.9.5", + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest-plugin": "^4.0.0", + "pestphp/pest-plugin-arch": "^4.0.2", + "pestphp/pest-plugin-mutate": "^4.0.1", + "pestphp/pest-plugin-profanity": "^4.2.1", + "php": "^8.3.0", + "phpunit/phpunit": "^12.5.33", + "symfony/process": "^7.4.13|^8.1.0" + }, + "conflict": { + "filp/whoops": "<2.18.3", + "phpunit/phpunit": ">12.5.33", + "sebastian/exporter": "<7.0.0", + "webmozart/assert": "<1.11.0" + }, + "require-dev": { + "mrpunyapal/peststan": "^0.2.12", + "pestphp/pest-dev-tools": "^4.1.0", + "pestphp/pest-plugin-browser": "^4.3.1", + "pestphp/pest-plugin-type-coverage": "^4.0.4", + "psy/psysh": "^0.12.24" + }, + "bin": [ + "bin/pest" + ], + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Mutate\\Plugins\\Mutate", + "Pest\\Plugins\\Configuration", + "Pest\\Plugins\\Bail", + "Pest\\Plugins\\Cache", + "Pest\\Plugins\\Coverage", + "Pest\\Plugins\\Init", + "Pest\\Plugins\\Environment", + "Pest\\Plugins\\Help", + "Pest\\Plugins\\Memory", + "Pest\\Plugins\\Only", + "Pest\\Plugins\\Printer", + "Pest\\Plugins\\ProcessIsolation", + "Pest\\Plugins\\Profile", + "Pest\\Plugins\\Retry", + "Pest\\Plugins\\Snapshot", + "Pest\\Plugins\\Verbose", + "Pest\\Plugins\\Version", + "Pest\\Plugins\\Shard", + "Pest\\Plugins\\Parallel" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php", + "src/Pest.php" + ], + "psr-4": { + "Pest\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "The elegant PHP Testing Framework.", + "keywords": [ + "framework", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "issues": "https://github.com/pestphp/pest/issues", + "source": "https://github.com/pestphp/pest/tree/v4.7.8" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2026-08-03T20:49:44+00:00" + }, + { + "name": "pestphp/pest-plugin", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin.git", + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/9d4b93d7f73d3f9c3189bb22c220fef271cdf568", + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "composer-runtime-api": "^2.2.2", + "php": "^8.3" + }, + "conflict": { + "pestphp/pest": "<4.0.0" + }, + "require-dev": { + "composer/composer": "^2.8.10", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Pest\\Plugin\\Manager" + }, + "autoload": { + "psr-4": { + "Pest\\Plugin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest plugin manager", + "keywords": [ + "framework", + "manager", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin/tree/v4.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2025-08-20T12:35:58+00:00" + }, + { + "name": "pestphp/pest-plugin-arch", + "version": "v4.0.2", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-arch.git", + "reference": "3fb0d02a91b9da504b139dc7ab2a31efb7c3215c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/3fb0d02a91b9da504b139dc7ab2a31efb7c3215c", + "reference": "3fb0d02a91b9da504b139dc7ab2a31efb7c3215c", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "ta-tikoma/phpunit-architecture-test": "^0.8.7" + }, + "require-dev": { + "pestphp/pest": "^4.4.6", + "pestphp/pest-dev-tools": "^4.1.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Arch\\Plugin" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Arch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Arch plugin for Pest PHP.", + "keywords": [ + "arch", + "architecture", + "framework", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v4.0.2" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2026-04-10T17:20:19+00:00" + }, + { + "name": "pestphp/pest-plugin-browser", + "version": "v4.3.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-browser.git", + "reference": "b6e76d3e4a2f81da9f050ec54be2a29b402287c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-browser/zipball/b6e76d3e4a2f81da9f050ec54be2a29b402287c4", + "reference": "b6e76d3e4a2f81da9f050ec54be2a29b402287c4", + "shasum": "" + }, + "require": { + "amphp/amp": "^3.1.1", + "amphp/http-server": "^3.4.4", + "amphp/websocket-client": "^2.0.2", + "ext-sockets": "*", + "pestphp/pest": "^4.4.5", + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "symfony/process": "^7.4.8|^8.0.5" + }, + "require-dev": { + "ext-pcntl": "*", + "ext-posix": "*", + "livewire/livewire": "^3.7.15", + "nunomaduro/collision": "^8.9.3", + "orchestra/testbench": "^10.11.0", + "pestphp/pest-dev-tools": "^4.1.0", + "pestphp/pest-plugin-laravel": "^4.1", + "pestphp/pest-plugin-type-coverage": "^4.0.4" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Browser\\Plugin" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Browser\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Pest plugin to test browser interactions", + "keywords": [ + "browser", + "framework", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-browser/tree/v4.3.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2026-04-08T21:04:12+00:00" + }, + { + "name": "pestphp/pest-plugin-laravel", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-laravel.git", + "reference": "3057a36669ff11416cc0dc2b521b3aec58c488d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-laravel/zipball/3057a36669ff11416cc0dc2b521b3aec58c488d0", + "reference": "3057a36669ff11416cc0dc2b521b3aec58c488d0", + "shasum": "" + }, + "require": { + "laravel/framework": "^11.45.2|^12.52.0|^13.0", + "pestphp/pest": "^4.4.1", + "php": "^8.3.0" + }, + "require-dev": { + "laravel/dusk": "^8.3.6", + "orchestra/testbench": "^9.13.0|^10.9.0|^11.0", + "pestphp/pest-dev-tools": "^4.1.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Laravel\\Plugin" + ] + }, + "laravel": { + "providers": [ + "Pest\\Laravel\\PestServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Laravel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Laravel Plugin", + "keywords": [ + "framework", + "laravel", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-laravel/tree/v4.1.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2026-02-21T00:29:45+00:00" + }, + { + "name": "pestphp/pest-plugin-mutate", + "version": "v4.0.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-mutate.git", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/d9b32b60b2385e1688a68cc227594738ec26d96c", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.6.1", + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "psr/simple-cache": "^3.0.0" + }, + "require-dev": { + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0", + "pestphp/pest-plugin-type-coverage": "^4.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Pest\\Mutate\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + }, + { + "name": "Sandro Gehri", + "email": "sandrogehri@gmail.com" + } + ], + "description": "Mutates your code to find untested cases", + "keywords": [ + "framework", + "mutate", + "mutation", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v4.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-08-21T20:19:25+00:00" + }, + { + "name": "pestphp/pest-plugin-profanity", + "version": "v4.2.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-profanity.git", + "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-profanity/zipball/343cfa6f3564b7e35df0ebb77b7fa97039f72b27", + "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3" + }, + "require-dev": { + "faissaloux/pest-plugin-inside": "^1.9", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Profanity\\Plugin" + ] + } + }, + "autoload": { + "psr-4": { + "Pest\\Profanity\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Profanity Plugin", + "keywords": [ + "framework", + "pest", + "php", + "plugin", + "profanity", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-profanity/tree/v4.2.1" + }, + "time": "2025-12-08T00:13:17+00:00" + }, { "name": "phar-io/manifest", "version": "2.0.4", @@ -7126,70 +9419,227 @@ "time": "2022-02-21T01:04:05+00:00" }, { - "name": "php-webdriver/webdriver", - "version": "1.16.0", + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", "source": { "type": "git", - "url": "https://github.com/php-webdriver/php-webdriver.git", - "reference": "ac0662863aa120b4f645869f584013e4c4dba46a" + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/ac0662863aa120b4f645869f584013e4c4dba46a", - "reference": "ac0662863aa120b4f645869f584013e4c4dba46a", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-zip": "*", - "php": "^7.3 || ^8.0", - "symfony/polyfill-mbstring": "^1.12", - "symfony/process": "^5.0 || ^6.0 || ^7.0 || ^8.0" - }, - "replace": { - "facebook/webdriver": "*" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.20.0", - "ondram/ci-detector": "^4.0", - "php-coveralls/php-coveralls": "^2.4", - "php-mock/php-mock-phpunit": "^2.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.5", - "symfony/var-dumper": "^5.0 || ^6.0 || ^7.0 || ^8.0" - }, - "suggest": { - "ext-simplexml": "For Firefox profile creation" + "php": "^7.2 || ^8.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, "autoload": { - "files": [ - "lib/Exception/TimeoutException.php" - ], "psr-4": { - "Facebook\\WebDriver\\": "lib/" + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", - "homepage": "https://github.com/php-webdriver/php-webdriver", + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", "keywords": [ - "Chromedriver", - "geckodriver", - "php", - "selenium", - "webdriver" + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" ], "support": { - "issues": "https://github.com/php-webdriver/php-webdriver/issues", - "source": "https://github.com/php-webdriver/php-webdriver/tree/1.16.0" + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" }, - "time": "2025-12-28T23:57:40+00:00" + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/7bae67520aa9f5ecc506d646810bd40d9da54582", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^2.0", + "phpstan/phpdoc-parser": "^2.0", + "webmozart/assert": "^1.9.1 || ^2" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26", + "shipmonk/dead-code-detector": "^0.5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.3" + }, + "time": "2026-03-18T20:49:53+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" + }, + "time": "2026-01-06T21:53:42+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fb19eedd2bb67ff8cf7a5502ad329e701d6398a3", + "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.3" + }, + "time": "2026-07-08T07:01:06+00:00" }, { "name": "phpstan/phpstan", @@ -7306,16 +9756,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "11.0.12", + "version": "12.5.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" + "reference": "186dab580576598076de6818596d12b61801880e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", - "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/186dab580576598076de6818596d12b61801880e", + "reference": "186dab580576598076de6818596d12b61801880e", "shasum": "" }, "require": { @@ -7323,18 +9773,16 @@ "ext-libxml": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^5.7.0", - "php": ">=8.2", - "phpunit/php-file-iterator": "^5.1.0", - "phpunit/php-text-template": "^4.0.1", - "sebastian/code-unit-reverse-lookup": "^4.0.1", - "sebastian/complexity": "^4.0.1", - "sebastian/environment": "^7.2.1", - "sebastian/lines-of-code": "^3.0.1", - "sebastian/version": "^5.0.2", - "theseer/tokenizer": "^1.3.1" + "php": ">=8.3", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.1.2", + "sebastian/lines-of-code": "^4.0.1", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^11.5.46" + "phpunit/phpunit": "^12.5.28" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -7343,7 +9791,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "11.0.x-dev" + "dev-main": "12.5.x-dev" } }, "autoload": { @@ -7372,7 +9820,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.7" }, "funding": [ { @@ -7392,32 +9840,32 @@ "type": "tidelift" } ], - "time": "2025-12-24T07:01:01+00:00" + "time": "2026-06-01T13:24:19+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "5.1.1", + "version": "6.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", - "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -7445,7 +9893,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.1" }, "funding": [ { @@ -7465,28 +9913,28 @@ "type": "tidelift" } ], - "time": "2026-02-02T13:52:54+00:00" + "time": "2026-02-02T14:04:18+00:00" }, { "name": "phpunit/php-invoker", - "version": "5.0.1", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-pcntl": "*" @@ -7494,7 +9942,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -7521,7 +9969,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" }, "funding": [ { @@ -7529,32 +9977,32 @@ "type": "github" } ], - "time": "2024-07-03T05:07:44+00:00" + "time": "2025-02-07T04:58:58+00:00" }, { "name": "phpunit/php-text-template", - "version": "4.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -7581,7 +10029,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" }, "funding": [ { @@ -7589,32 +10037,32 @@ "type": "github" } ], - "time": "2024-07-03T05:08:43+00:00" + "time": "2025-02-07T04:59:16+00:00" }, { "name": "phpunit/php-timer", - "version": "7.0.1", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -7641,7 +10089,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", "security": "https://github.com/sebastianbergmann/php-timer/security/policy", - "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" }, "funding": [ { @@ -7649,20 +10097,20 @@ "type": "github" } ], - "time": "2024-07-03T05:09:35+00:00" + "time": "2025-02-07T04:59:38+00:00" }, { "name": "phpunit/phpunit", - "version": "11.5.56", + "version": "12.5.33", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256" + "reference": "b98e028a26c5c5ba7e4a54be96ccf35f2914d184" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f83edffa6967c3db468d48a695ec7bcb02e9256", - "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b98e028a26c5c5ba7e4a54be96ccf35f2914d184", + "reference": "b98e028a26c5c5ba7e4a54be96ccf35f2914d184", "shasum": "" }, "require": { @@ -7675,35 +10123,31 @@ "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.12", - "phpunit/php-file-iterator": "^5.1.1", - "phpunit/php-invoker": "^5.0.1", - "phpunit/php-text-template": "^4.0.1", - "phpunit/php-timer": "^7.0.1", - "sebastian/cli-parser": "^3.0.2", - "sebastian/code-unit": "^3.0.3", - "sebastian/comparator": "^6.3.3", - "sebastian/diff": "^6.0.2", - "sebastian/environment": "^7.2.1", - "sebastian/exporter": "^6.3.2", - "sebastian/global-state": "^7.0.2", - "sebastian/object-enumerator": "^6.0.1", - "sebastian/recursion-context": "^6.0.3", - "sebastian/type": "^5.1.3", - "sebastian/version": "^5.0.2", + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.5.7", + "phpunit/php-file-iterator": "^6.0.1", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.2.1", + "sebastian/comparator": "^7.1.8", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.1.2", + "sebastian/exporter": "^7.0.3", + "sebastian/global-state": "^8.0.3", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/recursion-context": "^7.0.1", + "sebastian/type": "^6.0.4", + "sebastian/version": "^6.0.0", "staabm/side-effects-detector": "^1.0.5" }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" - }, "bin": [ "phpunit" ], "type": "library", "extra": { "branch-alias": { - "dev-main": "11.5-dev" + "dev-main": "12.5-dev" } }, "autoload": { @@ -7735,7 +10179,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.56" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.33" }, "funding": [ { @@ -7743,32 +10187,104 @@ "type": "other" } ], - "time": "2026-07-06T14:52:39+00:00" + "time": "2026-07-28T13:58:09+00:00" }, { - "name": "sebastian/cli-parser", - "version": "3.0.2", + "name": "revolt/event-loop", + "version": "v1.0.9", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "44061cf513e53c6200372fc935ac42271566295d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/44061cf513e53c6200372fc935ac42271566295d", + "reference": "44061cf513e53c6200372fc935ac42271566295d", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.*" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Revolt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.9" + }, + "time": "2026-05-16T17:55:38+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "4.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "7d05781b13f7dec9043a629a21d086ed74582a15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/7d05781b13f7dec9043a629a21d086ed74582a15", + "reference": "7d05781b13f7dec9043a629a21d086ed74582a15", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.2-dev" } }, "autoload": { @@ -7792,152 +10308,51 @@ "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - } - ], - "time": "2024-07-03T04:41:36+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", - "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ + }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "security": "https://github.com/sebastianbergmann/code-unit/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" - }, - "funding": [ + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-03-19T07:56:08+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:45:54+00:00" + "time": "2026-05-17T05:29:34+00:00" }, { "name": "sebastian/comparator", - "version": "6.3.3", + "version": "7.1.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" + "reference": "7c65c1e79836812819705b473a90c12399542485" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", - "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/7c65c1e79836812819705b473a90c12399542485", + "reference": "7c65c1e79836812819705b473a90c12399542485", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/diff": "^6.0", - "sebastian/exporter": "^6.0" + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0.3" }, "require-dev": { - "phpunit/phpunit": "^11.4" + "phpunit/phpunit": "^12.5.25" }, "suggest": { "ext-bcmath": "For comparing BcMath\\Number objects" @@ -7945,7 +10360,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.3-dev" + "dev-main": "7.1-dev" } }, "autoload": { @@ -7985,7 +10400,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.8" }, "funding": [ { @@ -8005,33 +10420,33 @@ "type": "tidelift" } ], - "time": "2026-01-24T09:26:40+00:00" + "time": "2026-05-21T04:45:25+00:00" }, { "name": "sebastian/complexity", - "version": "4.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", "shasum": "" }, "require": { "nikic/php-parser": "^5.0", - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -8055,7 +10470,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" }, "funding": [ { @@ -8063,33 +10478,33 @@ "type": "github" } ], - "time": "2024-07-03T04:49:50+00:00" + "time": "2025-02-07T04:55:25+00:00" }, { "name": "sebastian/diff", - "version": "6.0.2", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -8122,7 +10537,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" }, "funding": [ { @@ -8130,27 +10545,27 @@ "type": "github" } ], - "time": "2024-07-03T04:53:05+00:00" + "time": "2025-02-07T04:55:46+00:00" }, { "name": "sebastian/environment", - "version": "7.2.1", + "version": "8.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" + "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", - "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/9d32c685773823b1983e256ae4ecd48a10d6e439", + "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^12.5.26" }, "suggest": { "ext-posix": "*" @@ -8158,7 +10573,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "7.2-dev" + "dev-main": "8.1-dev" } }, "autoload": { @@ -8186,7 +10601,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" + "source": "https://github.com/sebastianbergmann/environment/tree/8.1.2" }, "funding": [ { @@ -8206,34 +10621,34 @@ "type": "tidelift" } ], - "time": "2025-05-21T11:55:47+00:00" + "time": "2026-05-25T13:40:20+00:00" }, { "name": "sebastian/exporter", - "version": "6.3.2", + "version": "7.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" + "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", - "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23", + "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/recursion-context": "^6.0" + "php": ">=8.3", + "sebastian/recursion-context": "^7.0.1" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^12.5.25" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.3-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -8276,7 +10691,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.3" }, "funding": [ { @@ -8296,35 +10711,35 @@ "type": "tidelift" } ], - "time": "2025-09-24T06:12:51+00:00" + "time": "2026-05-20T04:37:17+00:00" }, { "name": "sebastian/global-state", - "version": "7.0.2", + "version": "8.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + "reference": "b164d3274d6537ab462591c5755f76a8f5b1aae9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b164d3274d6537ab462591c5755f76a8f5b1aae9", + "reference": "b164d3274d6537ab462591c5755f76a8f5b1aae9", "shasum": "" }, "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0.1" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.5.28" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -8350,41 +10765,53 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-07-03T04:57:36+00:00" + "time": "2026-06-01T15:10:33+00:00" }, { "name": "sebastian/lines-of-code", - "version": "3.0.1", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d543b8ef219dcd8da262cbb958639a96bedba10e", + "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e", "shasum": "" }, "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.2" + "nikic/php-parser": "^5.7.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.5.25" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -8408,42 +10835,54 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/lines-of-code", + "type": "tidelift" } ], - "time": "2024-07-03T04:58:38+00:00" + "time": "2026-05-19T16:22:07+00:00" }, { "name": "sebastian/object-enumerator", - "version": "6.0.1", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", "shasum": "" }, "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -8466,7 +10905,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" }, "funding": [ { @@ -8474,32 +10913,32 @@ "type": "github" } ], - "time": "2024-07-03T05:00:13+00:00" + "time": "2025-02-07T04:57:48+00:00" }, { "name": "sebastian/object-reflector", - "version": "4.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -8522,7 +10961,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" }, "funding": [ { @@ -8530,32 +10969,32 @@ "type": "github" } ], - "time": "2024-07-03T05:01:32+00:00" + "time": "2025-02-07T04:58:17+00:00" }, { "name": "sebastian/recursion-context", - "version": "6.0.3", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", - "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -8586,7 +11025,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" }, "funding": [ { @@ -8606,32 +11045,32 @@ "type": "tidelift" } ], - "time": "2025-08-13T04:42:22+00:00" + "time": "2025-08-13T04:44:59+00:00" }, { "name": "sebastian/type", - "version": "5.1.3", + "version": "6.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" + "reference": "82ff822c2edc46724be9f7411d3163021f602773" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", - "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/82ff822c2edc46724be9f7411d3163021f602773", + "reference": "82ff822c2edc46724be9f7411d3163021f602773", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^12.5.25" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -8655,7 +11094,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" + "source": "https://github.com/sebastianbergmann/type/tree/6.0.4" }, "funding": [ { @@ -8675,29 +11114,29 @@ "type": "tidelift" } ], - "time": "2025-08-09T06:55:48+00:00" + "time": "2026-05-20T06:45:45+00:00" }, { "name": "sebastian/version", - "version": "5.0.2", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", - "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -8721,7 +11160,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/version/issues", "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" }, "funding": [ { @@ -8729,7 +11168,7 @@ "type": "github" } ], - "time": "2024-10-09T05:16:32+00:00" + "time": "2025-02-07T05:00:38+00:00" }, { "name": "staabm/side-effects-detector", @@ -8860,24 +11299,83 @@ "time": "2026-07-21T15:13:06+00:00" }, { - "name": "theseer/tokenizer", - "version": "1.3.1", + "name": "ta-tikoma/phpunit-architecture-test", + "version": "0.8.7", "source": { "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", + "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/1248f3f506ca9641d4f68cebcd538fa489754db8", + "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18.0 || ^5.0.0", + "php": "^8.1.0", + "phpdocumentor/reflection-docblock": "^5.3.0 || ^6.0.0", + "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0 || ^13.0.0", + "symfony/finder": "^6.4.0 || ^7.0.0 || ^8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.13.7", + "phpstan/phpstan": "^1.10.52" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPUnit\\Architecture\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ni Shi", + "email": "futik0ma011@gmail.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Methods for testing application architecture", + "keywords": [ + "architecture", + "phpunit", + "stucture", + "test", + "testing" + ], + "support": { + "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", + "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.7" + }, + "time": "2026-02-17T17:25:14+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.1" }, "type": "library", "autoload": { @@ -8899,7 +11397,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" }, "funding": [ { @@ -8907,7 +11405,73 @@ "type": "github" } ], - "time": "2025-11-17T20:03:58+00:00" + "time": "2025-12-08T11:19:18+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.4.1", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, + "branch-alias": { + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.4.1" + }, + "time": "2026-06-15T15:31:57+00:00" } ], "aliases": [], @@ -8919,5 +11483,5 @@ "php": "^8.2" }, "platform-dev": {}, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.6.0" } diff --git a/database/factories/PlannerFactory.php b/database/factories/PlannerFactory.php index 5b60266..a783cb0 100644 --- a/database/factories/PlannerFactory.php +++ b/database/factories/PlannerFactory.php @@ -2,13 +2,13 @@ namespace Database\Factories; -use App\Models\User; +use App\Models\Planner; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; /** - * @extends Factory + * @extends Factory */ class PlannerFactory extends Factory { diff --git a/docker/build/Dockerfile.ci b/docker/build/Dockerfile.ci index 753e52b..193f27c 100644 --- a/docker/build/Dockerfile.ci +++ b/docker/build/Dockerfile.ci @@ -1,10 +1,13 @@ -# CI image: PHP + Composer only, no runtime server or frontend toolchain. -# Tests run against SQLite in memory (see .env.testing), so no database client -# or cache extension is needed. +# CI image: PHP + Composer + Node/npm. Unit/feature tests run against SQLite in +# memory (see .env.testing), so no database client or cache extension is needed. +# Browser tests need the `sockets` extension (pest-plugin-browser boots Laravel +# in-process) and Node/npm to drive Playwright; the Chromium binary itself is +# installed per run so it always matches the playwright version from the lockfile. # # Published as dishplanner-ci:php8.3-. The CI workflow -# builds and tags this image from the current lockfile, so a dependency change -# automatically yields a fresh, uniquely-tagged image (no manual revision bump). +# builds and tags this image from the current lockfile, so a PHP dependency +# change automatically yields a fresh, uniquely-tagged image (no manual revision +# bump). # # Debian-based rather than Alpine to avoid the DNS resolution timeouts against # codeload.github.com that the Alpine base hit during composer install. @@ -15,6 +18,7 @@ COPY --from=mlocati/php-extension-installer:2 /usr/bin/install-php-extensions /u RUN install-php-extensions \ pdo_sqlite \ + sockets \ mbstring \ dom \ xml \ @@ -22,10 +26,11 @@ RUN install-php-extensions \ pcntl \ zip -# git is needed by the checkout action; nodejs runs the Forgejo JavaScript -# actions (checkout, cache); unzip lets Composer extract dist archives. +# git is needed by the checkout action; nodejs+npm run the Forgejo JavaScript +# actions (checkout, cache) and Playwright; unzip lets Composer extract dist +# archives. RUN apt-get update \ - && apt-get install -y --no-install-recommends git unzip nodejs \ + && apt-get install -y --no-install-recommends git unzip nodejs npm \ && rm -rf /var/lib/apt/lists/* COPY --from=composer:2 /usr/bin/composer /usr/bin/composer diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index c2d8fd5..1d6e9ca 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -85,21 +85,6 @@ services: networks: - dishplanner - # Selenium for E2E testing with Dusk - selenium: - image: selenium/standalone-chrome:latest - container_name: dishplanner_selenium - restart: unless-stopped - ports: - - "4444:4444" # Selenium server - - "7900:7900" # VNC server for debugging - volumes: - - /dev/shm:/dev/shm - networks: - - dishplanner - environment: - - SE_VNC_PASSWORD=secret - # Optional: Redis for caching/sessions # redis: # image: redis:alpine diff --git a/package-lock.json b/package-lock.json index 09088a2..4993047 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "app", + "name": "dishplanner", "lockfileVersion": 3, "requires": true, "packages": { @@ -13,6 +13,7 @@ "axios": "^1.7.4", "concurrently": "^9.0.1", "laravel-vite-plugin": "^1.3.0", + "playwright": "^1.62.1", "postcss": "^8.5.6", "tailwindcss": "^3.4.19", "vite": "^6.4.1" @@ -2220,6 +2221,53 @@ "node": ">= 6" } }, + "node_modules/playwright": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", + "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz", + "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", diff --git a/package.json b/package.json index 7b6cec3..29180ef 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "axios": "^1.7.4", "concurrently": "^9.0.1", "laravel-vite-plugin": "^1.3.0", + "playwright": "^1.62.1", "postcss": "^8.5.6", "tailwindcss": "^3.4.19", "vite": "^6.4.1" diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9c757af..6e558a6 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1350,540 +1350,24 @@ parameters: count: 1 path: src/DishPlanner/UserDish/Requests/UpdateUserDishFixedRecurrenceRequest.php - - - message: '#^Property Tests\\Browser\\Auth\\LoginTest\:\:\$testEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Auth/LoginTest.php - - - - message: '#^Property Tests\\Browser\\Auth\\LoginTest\:\:\$testPassword has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Auth/LoginTest.php - - - - message: '#^Property Tests\\Browser\\Auth\\LoginTest\:\:\$testPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Auth/LoginTest.php - - - - message: '#^Method Tests\\Browser\\Components\\DishModal\:\:selectUsers\(\) has parameter \$userIds with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Browser/Components/DishModal.php - - - - message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:openCreateModal\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Browser/Dishes/CreateDishFormValidationTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishFormValidationTest\:\:\$createDishFormValidationTestEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishFormValidationTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishFormValidationTest\:\:\$createDishFormValidationTestPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishFormValidationTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishFormValidationTest\:\:\$testEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishFormValidationTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishFormValidationTest\:\:\$testPassword has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishFormValidationTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishFormValidationTest\:\:\$testPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishFormValidationTest.php - - - - message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:openCreateModal\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Browser/Dishes/CreateDishSuccessTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishSuccessTest\:\:\$createDishSuccessTestEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishSuccessTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishSuccessTest\:\:\$createDishSuccessTestPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishSuccessTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishSuccessTest\:\:\$testEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishSuccessTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishSuccessTest\:\:\$testPassword has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishSuccessTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishSuccessTest\:\:\$testPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishSuccessTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishTest\:\:\$createDishTestEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishTest\:\:\$createDishTestPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishTest\:\:\$testEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishTest\:\:\$testPassword has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\CreateDishTest\:\:\$testPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/CreateDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\DeleteDishTest\:\:\$deleteDishTestEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/DeleteDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\DeleteDishTest\:\:\$deleteDishTestPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/DeleteDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\DeleteDishTest\:\:\$testEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/DeleteDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\DeleteDishTest\:\:\$testPassword has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/DeleteDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\DeleteDishTest\:\:\$testPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/DeleteDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\DishDeletionSafetyTest\:\:\$dishDeletionSafetyTestEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/DishDeletionSafetyTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\DishDeletionSafetyTest\:\:\$dishDeletionSafetyTestPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/DishDeletionSafetyTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\DishDeletionSafetyTest\:\:\$testEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/DishDeletionSafetyTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\DishDeletionSafetyTest\:\:\$testPassword has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/DishDeletionSafetyTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\DishDeletionSafetyTest\:\:\$testPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/DishDeletionSafetyTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\EditDishTest\:\:\$editDishTestEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/EditDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\EditDishTest\:\:\$editDishTestPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/EditDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\EditDishTest\:\:\$testEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/EditDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\EditDishTest\:\:\$testPassword has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/EditDishTest.php - - - - message: '#^Property Tests\\Browser\\Dishes\\EditDishTest\:\:\$testPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Dishes/EditDishTest.php - - - - message: '#^Method Tests\\Browser\\Pages\\SchedulePage\:\:elements\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: tests/Browser/Pages/SchedulePage.php - - - - message: '#^Parameter \#2 \$value of method Laravel\\Dusk\\Browser\:\:select\(\) expects array\|string\|null, int given\.$#' - identifier: argument.type - count: 2 - path: tests/Browser/Pages/SchedulePage.php - - - - message: '#^Method Tests\\Browser\\RedirectTest\:\:test_login_page_loads\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Browser/RedirectTest.php - - - - message: '#^Method Tests\\Browser\\RedirectTest\:\:test_unauthenticated_redirects_to_login\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/Browser/RedirectTest.php - - - - message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:clickGenerate\(\)\.$#' - identifier: method.notFound - count: 3 - path: tests/Browser/Schedule/GenerateScheduleTest.php - - - - message: '#^Property Tests\\Browser\\Schedule\\GenerateScheduleTest\:\:\$dish has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Schedule/GenerateScheduleTest.php - - - - message: '#^Property Tests\\Browser\\Schedule\\GenerateScheduleTest\:\:\$email has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Schedule/GenerateScheduleTest.php - - - - message: '#^Property Tests\\Browser\\Schedule\\GenerateScheduleTest\:\:\$password has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Schedule/GenerateScheduleTest.php - - - - message: '#^Property Tests\\Browser\\Schedule\\GenerateScheduleTest\:\:\$planner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Schedule/GenerateScheduleTest.php - - - - message: '#^Property Tests\\Browser\\Schedule\\GenerateScheduleTest\:\:\$user has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Schedule/GenerateScheduleTest.php - - - - message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:goToNextMonth\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Browser/Schedule/SchedulePageTest.php - - - - message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:goToPreviousMonth\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Browser/Schedule/SchedulePageTest.php - - - - message: '#^Property Tests\\Browser\\Schedule\\SchedulePageTest\:\:\$schedulePageTestEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Schedule/SchedulePageTest.php - - - - message: '#^Property Tests\\Browser\\Schedule\\SchedulePageTest\:\:\$schedulePageTestPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Schedule/SchedulePageTest.php - - - - message: '#^Property Tests\\Browser\\Schedule\\SchedulePageTest\:\:\$testEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Schedule/SchedulePageTest.php - - - - message: '#^Property Tests\\Browser\\Schedule\\SchedulePageTest\:\:\$testPassword has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Schedule/SchedulePageTest.php - - - - message: '#^Property Tests\\Browser\\Schedule\\SchedulePageTest\:\:\$testPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Schedule/SchedulePageTest.php - - - - message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:openCreateModal\(\)\.$#' - identifier: method.notFound - count: 4 - path: tests/Browser/Users/CreateUserTest.php - - - - message: '#^Property Tests\\Browser\\Users\\CreateUserTest\:\:\$createUserTestEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Users/CreateUserTest.php - - - - message: '#^Property Tests\\Browser\\Users\\CreateUserTest\:\:\$createUserTestPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Users/CreateUserTest.php - - - - message: '#^Property Tests\\Browser\\Users\\CreateUserTest\:\:\$testEmail has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Users/CreateUserTest.php - - - - message: '#^Property Tests\\Browser\\Users\\CreateUserTest\:\:\$testPassword has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Users/CreateUserTest.php - - - - message: '#^Property Tests\\Browser\\Users\\CreateUserTest\:\:\$testPlanner has no type specified\.$#' - identifier: missingType.property - count: 1 - path: tests/Browser/Users/CreateUserTest.php - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) with string will always evaluate to true\.$#' identifier: method.alreadyNarrowedType count: 1 path: tests/Feature/AuthenticationTest.php - - - message: '#^Parameter \#1 \$user of method Illuminate\\Foundation\\Testing\\TestCase\:\:actingAs\(\) expects Illuminate\\Contracts\\Auth\\Authenticatable, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/AuthenticationTest.php - - - - message: '#^Method Tests\\Feature\\Dish\\AddUsersToDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Dish/AddUsersToDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Dish/AddUsersToDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Dish/AddUsersToDishTest.php - - - - message: '#^Property Tests\\Feature\\Dish\\AddUsersToDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Dish/AddUsersToDishTest.php - - message: '#^Called ''first'' on Laravel collection, but could have been retrieved as a query\.$#' identifier: larastan.noUnnecessaryCollectionCall count: 1 path: tests/Feature/Dish/CreateDishTest.php - - - message: '#^Method Tests\\Feature\\Dish\\CreateDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Dish/CreateDishTest.php - - message: '#^Method Tests\\Feature\\Dish\\CreateDishTest\:\:invalidNameValues\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue count: 1 path: tests/Feature/Dish/CreateDishTest.php - - - message: '#^Property Tests\\Feature\\Dish\\CreateDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Dish/CreateDishTest.php - - - - message: '#^Method Tests\\Feature\\Dish\\DeleteDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Dish/DeleteDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Dish/DeleteDishTest.php - - - - message: '#^Property Tests\\Feature\\Dish\\DeleteDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Dish/DeleteDishTest.php - - - - message: '#^Method Tests\\Feature\\Dish\\ListDishesTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Dish/ListDishesTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Dish/ListDishesTest.php - - - - message: '#^Property Tests\\Feature\\Dish\\ListDishesTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Dish/ListDishesTest.php - - - - message: '#^Method Tests\\Feature\\Dish\\RemoveUsersFromDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Dish/RemoveUsersFromDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Dish/RemoveUsersFromDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Dish/RemoveUsersFromDishTest.php - - - - message: '#^Property Tests\\Feature\\Dish\\RemoveUsersFromDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Dish/RemoveUsersFromDishTest.php - - - - message: '#^Method Tests\\Feature\\Dish\\ShowDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Dish/ShowDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Dish/ShowDishTest.php - - - - message: '#^Property Tests\\Feature\\Dish\\ShowDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Dish/ShowDishTest.php - - - - message: '#^Method Tests\\Feature\\Dish\\SyncUsersForDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Dish/SyncUsersForDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Dish/SyncUsersForDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Dish/SyncUsersForDishTest.php - - - - message: '#^Property Tests\\Feature\\Dish\\SyncUsersForDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Dish/SyncUsersForDishTest.php - - - - message: '#^Method Tests\\Feature\\Dish\\UpdateDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Dish/UpdateDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Dish/UpdateDishTest.php - - - - message: '#^Property Tests\\Feature\\Dish\\UpdateDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Dish/UpdateDishTest.php - - message: '#^Method Tests\\Feature\\RegistrationTest\:\:test_new_users_can_register\(\) has no return type specified\.$#' identifier: missingType.return @@ -1914,12 +1398,6 @@ parameters: count: 4 path: tests/Feature/Schedule/GenerateScheduleTest.php - - - message: '#^Method Tests\\Feature\\Schedule\\GenerateScheduleTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Schedule/GenerateScheduleTest.php - - message: '#^Parameter \#1 \$callback of method Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:map\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: array\{user_dish_id\: int\}, Closure\(App\\Models\\ScheduledUserDish\)\: array\{user_dish_id\: int\} given\.$#' identifier: argument.type @@ -1938,12 +1416,6 @@ parameters: count: 1 path: tests/Feature/Schedule/GenerateScheduleTest.php - - - message: '#^Property Tests\\Feature\\Schedule\\GenerateScheduleTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Schedule/GenerateScheduleTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' identifier: property.notFound @@ -1956,36 +1428,6 @@ parameters: count: 2 path: tests/Feature/Schedule/ListScheduleTest.php - - - message: '#^Method Tests\\Feature\\Schedule\\ListScheduleTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Schedule/ListScheduleTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Schedule/ListScheduleTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Schedule/ListScheduleTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Schedule/ListScheduleTest.php - - - - message: '#^Property Tests\\Feature\\Schedule\\ListScheduleTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Schedule/ListScheduleTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$date\.$#' identifier: property.notFound @@ -1998,42 +1440,12 @@ parameters: count: 1 path: tests/Feature/Schedule/ReadScheduleTest.php - - - message: '#^Method Tests\\Feature\\Schedule\\ReadScheduleTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Schedule/ReadScheduleTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Schedule/ReadScheduleTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Schedule/ReadScheduleTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/Schedule/ReadScheduleTest.php - - message: '#^Parameter \#1 \$userDish of method Database\\Factories\\ScheduledUserDishFactory\:\:userDish\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model given\.$#' identifier: argument.type count: 2 path: tests/Feature/Schedule/ReadScheduleTest.php - - - message: '#^Property Tests\\Feature\\Schedule\\ReadScheduleTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Schedule/ReadScheduleTest.php - - message: '#^Unable to resolve the template type TKey in call to function collect$#' identifier: argument.templateType @@ -2052,60 +1464,6 @@ parameters: count: 1 path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php - - - message: '#^Method Tests\\Feature\\Schedule\\ScheduleEdgeCasesTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php - - - - message: '#^Parameter \#1 \$user of method Illuminate\\Foundation\\Testing\\TestCase\:\:actingAs\(\) expects Illuminate\\Contracts\\Auth\\Authenticatable, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php - - - - message: '#^Property Tests\\Feature\\Schedule\\ScheduleEdgeCasesTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/Schedule/ScheduleUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/Schedule/ScheduleUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/Schedule/ScheduleUserDishTest.php - - - - message: '#^Parameter \#1 \$user of method Illuminate\\Foundation\\Testing\\TestCase\:\:actingAs\(\) expects Illuminate\\Contracts\\Auth\\Authenticatable, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/Schedule/ScheduleUserDishTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$dish_id\.$#' identifier: property.notFound @@ -2118,126 +1476,30 @@ parameters: count: 1 path: tests/Feature/Schedule/UpdateScheduleTest.php - - - message: '#^Method Tests\\Feature\\Schedule\\UpdateScheduleTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/Schedule/UpdateScheduleTest.php - - message: '#^Parameter \#1 \$userDish of method Database\\Factories\\ScheduledUserDishFactory\:\:userDish\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model given\.$#' identifier: argument.type count: 2 path: tests/Feature/Schedule/UpdateScheduleTest.php - - - message: '#^Property Tests\\Feature\\Schedule\\UpdateScheduleTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/Schedule/UpdateScheduleTest.php - - message: '#^Called ''first'' on Laravel collection, but could have been retrieved as a query\.$#' identifier: larastan.noUnnecessaryCollectionCall count: 1 path: tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php - - - message: '#^Method Tests\\Feature\\ScheduledUserDish\\CreateScheduledUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php - - - - message: '#^Property Tests\\Feature\\ScheduledUserDish\\CreateScheduledUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php - - - - message: '#^Method Tests\\Feature\\ScheduledUserDish\\DeleteScheduledUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php - - - - message: '#^Property Tests\\Feature\\ScheduledUserDish\\DeleteScheduledUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$user\.$#' identifier: property.notFound count: 2 path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php - - - message: '#^Method Tests\\Feature\\ScheduledUserDish\\ReadScheduledUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php - - message: '#^Parameter \#1 \$userDish of method Database\\Factories\\ScheduledUserDishFactory\:\:userDish\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model given\.$#' identifier: argument.type count: 2 path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php - - - message: '#^Property Tests\\Feature\\ScheduledUserDish\\ReadScheduledUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$dish\.$#' identifier: property.notFound @@ -2256,48 +1518,12 @@ parameters: count: 4 path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php - - - message: '#^Method Tests\\Feature\\ScheduledUserDish\\UpdateScheduledUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php - - message: '#^Method Tests\\Feature\\ScheduledUserDish\\UpdateScheduledUserDishTest\:\:generateDishes\(\) return type with generic class Illuminate\\Support\\Collection does not specify its types\: TKey, TValue$#' identifier: missingType.generics count: 2 path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Tests\\Feature\\ScheduledUserDish\\UpdateScheduledUserDishTest\:\:generateDishes\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Tests\\Feature\\ScheduledUserDish\\UpdateScheduledUserDishTest\:\:generateScheduledDishes\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php - - message: '#^Parameter \#1 \$userDish of method Database\\Factories\\ScheduledUserDishFactory\:\:userDish\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model given\.$#' identifier: argument.type @@ -2310,78 +1536,12 @@ parameters: count: 2 path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php - - - message: '#^Property Tests\\Feature\\ScheduledUserDish\\UpdateScheduledUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php - - message: '#^Unable to resolve the template type TKey in call to function collect$#' identifier: argument.templateType count: 1 path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php - - - message: '#^Method Tests\\Feature\\User\\CreateUserTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/User/CreateUserTest.php - - - - message: '#^Property Tests\\Feature\\User\\CreateUserTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/User/CreateUserTest.php - - - - message: '#^Method Tests\\Feature\\User\\DeleteUserTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/User/DeleteUserTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/User/DeleteUserTest.php - - - - message: '#^Property Tests\\Feature\\User\\DeleteUserTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/User/DeleteUserTest.php - - - - message: '#^Method Tests\\Feature\\User\\Dish\\ListUserDishesTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/User/Dish/ListUserDishesTest.php - - - - message: '#^Parameter \#1 \$user of method Illuminate\\Foundation\\Testing\\TestCase\:\:actingAs\(\) expects Illuminate\\Contracts\\Auth\\Authenticatable, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/User/Dish/ListUserDishesTest.php - - - - message: '#^Property Tests\\Feature\\User\\Dish\\ListUserDishesTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/User/Dish/ListUserDishesTest.php - - - - message: '#^Method Tests\\Feature\\User\\Dish\\RemoveDishesForUserTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/User/Dish/RemoveDishesForUserTest.php - - - - message: '#^Property Tests\\Feature\\User\\Dish\\RemoveDishesForUserTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/User/Dish/RemoveDishesForUserTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' identifier: property.notFound @@ -2389,155 +1549,29 @@ parameters: path: tests/Feature/User/Dish/ShowUserDishTest.php - - message: '#^Method Tests\\Feature\\User\\Dish\\ShowUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/User/Dish/ShowUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + message: '#^Parameter \#1 \$callback of method Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:map\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: array\{id\: int, dish\: array\{id\: int, name\: string\}, recurrences\: array\{\}\}, Closure\(App\\Models\\UserDish\)\: array\{id\: int, dish\: array\{id\: int, name\: string\}, recurrences\: array\{\}\} given\.$#' identifier: argument.type count: 1 - path: tests/Feature/User/Dish/ShowUserDishTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/User/Dish/ShowUserDishTest.php - - - - message: '#^Property Tests\\Feature\\User\\Dish\\ShowUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/User/Dish/ShowUserDishTest.php - - - - message: '#^Method Tests\\Feature\\User\\Dish\\StoreRecurrenceForUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/User/Dish/StoreRecurrenceForUserDishTest.php - - - - message: '#^Property Tests\\Feature\\User\\Dish\\StoreRecurrenceForUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/User/Dish/StoreRecurrenceForUserDishTest.php - - - - message: '#^Method Tests\\Feature\\User\\ListUsersTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 path: tests/Feature/User/ListUsersTest.php - message: '#^Parameter \#1 \$callback of method Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:map\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: array\{id\: int, dish\: array\{id\: int, name\: string\}, recurrences\: array\{\}\}, Closure\(App\\Models\\UserDish\)\: array\{id\: int, dish\: array\{id\: int, name\: string\}, recurrences\: array\{\}\} given\.$#' identifier: argument.type count: 1 - path: tests/Feature/User/ListUsersTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/User/ListUsersTest.php - - - - message: '#^Property Tests\\Feature\\User\\ListUsersTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/User/ListUsersTest.php - - - - message: '#^Method Tests\\Feature\\User\\ShowUserTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/User/ShowUserTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/User/ShowUserTest.php - - - - message: '#^Property Tests\\Feature\\User\\ShowUserTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/User/ShowUserTest.php - - - - message: '#^Method Tests\\Feature\\User\\ShowUserWithDishesTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 path: tests/Feature/User/ShowUserWithDishesTest.php - - - message: '#^Parameter \#1 \$callback of method Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:map\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: array\{id\: int, dish\: array\{id\: int, name\: string\}, recurrences\: array\{\}\}, Closure\(App\\Models\\UserDish\)\: array\{id\: int, dish\: array\{id\: int, name\: string\}, recurrences\: array\{\}\} given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/User/ShowUserWithDishesTest.php - - - - message: '#^Property Tests\\Feature\\User\\ShowUserWithDishesTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/User/ShowUserWithDishesTest.php - - - - message: '#^Method Tests\\Feature\\User\\UpdateUserTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Feature/User/UpdateUserTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Feature/User/UpdateUserTest.php - - - - message: '#^Property Tests\\Feature\\User\\UpdateUserTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Feature/User/UpdateUserTest.php - - - - message: '#^Method Tests\\Unit\\Actions\\EditUserActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/Actions/EditUserActionTest.php - - message: '#^Parameter \#1 \$user of method App\\Actions\\User\\EditUserAction\:\:execute\(\) expects App\\Models\\User, Mockery\\MockInterface given\.$#' identifier: argument.type count: 3 path: tests/Unit/Actions/EditUserActionTest.php - - - message: '#^Property Tests\\Unit\\Actions\\EditUserActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 2 - path: tests/Unit/Actions/EditUserActionTest.php - - - - message: '#^Method Tests\\Unit\\Actions\\RegenerateScheduleDayActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/Actions/RegenerateScheduleDayActionTest.php - - message: '#^Method Tests\\Unit\\Actions\\RegenerateScheduleDayActionTest\:\:generateDishes\(\) return type with generic class Illuminate\\Support\\Collection does not specify its types\: TKey, TValue$#' identifier: missingType.generics count: 1 path: tests/Unit/Actions/RegenerateScheduleDayActionTest.php - - - message: '#^Property Tests\\Unit\\Actions\\RegenerateScheduleDayActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Actions/RegenerateScheduleDayActionTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' identifier: property.notFound @@ -2562,12 +1596,6 @@ parameters: count: 3 path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php - - - message: '#^Method Tests\\Unit\\Actions\\RegenerateScheduleDayForUserActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php - - message: '#^PHPDoc tag @var for variable \$dishes contains generic class Illuminate\\Database\\Eloquent\\Collection but does not specify its types\: TKey, TModel$#' identifier: missingType.generics @@ -2580,72 +1608,24 @@ parameters: count: 2 path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php - - - message: '#^Property Tests\\Unit\\Actions\\RegenerateScheduleDayForUserActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php - - message: '#^Call to an undefined static method Illuminate\\Support\\Facades\\Log\:\:shouldHaveReceived\(\)\.$#' identifier: staticMethod.notFound count: 3 path: tests/Unit/Actions/User/CreateUserActionTest.php - - - message: '#^Property Tests\\Unit\\Actions\\User\\CreateUserActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Actions/User/CreateUserActionTest.php - - message: '#^Call to an undefined static method Illuminate\\Support\\Facades\\Log\:\:shouldHaveReceived\(\)\.$#' identifier: staticMethod.notFound count: 3 path: tests/Unit/Actions/User/DeleteUserActionTest.php - - - message: '#^Property Tests\\Unit\\Actions\\User\\DeleteUserActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Actions/User/DeleteUserActionTest.php - - - - message: '#^Property Tests\\Unit\\Actions\\UserActionIntegrationTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Actions/UserActionIntegrationTest.php - - - - message: '#^Method Tests\\Unit\\Schedule\\Actions\\ClearScheduleForMonthActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/Schedule/Actions/ClearScheduleForMonthActionTest.php - - - - message: '#^Property Tests\\Unit\\Schedule\\Actions\\ClearScheduleForMonthActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Schedule/Actions/ClearScheduleForMonthActionTest.php - - message: '#^Called ''count'' on Laravel collection, but could have been retrieved as a query\.$#' identifier: larastan.noUnnecessaryCollectionCall count: 1 path: tests/Unit/Schedule/Actions/DraftScheduleForDateActionTest.php - - - message: '#^Method Tests\\Unit\\Schedule\\Actions\\DraftScheduleForDateActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/Schedule/Actions/DraftScheduleForDateActionTest.php - - - - message: '#^Property Tests\\Unit\\Schedule\\Actions\\DraftScheduleForDateActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Schedule/Actions/DraftScheduleForDateActionTest.php - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) with App\\Models\\Planner will always evaluate to true\.$#' identifier: method.alreadyNarrowedType @@ -2658,54 +1638,18 @@ parameters: count: 1 path: tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php - - - message: '#^Method Tests\\Unit\\Schedule\\Actions\\DraftScheduleForPeriodActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php - - message: '#^Method Tests\\Unit\\Schedule\\Actions\\DraftScheduleForPeriodActionTest\:\:generateDishes\(\) return type with generic class Illuminate\\Support\\Collection does not specify its types\: TKey, TValue$#' identifier: missingType.generics count: 1 path: tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php - - - message: '#^Property Tests\\Unit\\Schedule\\Actions\\DraftScheduleForPeriodActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php - - - - message: '#^Method Tests\\Unit\\Schedule\\Actions\\GenerateScheduleForMonthActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/Schedule/Actions/GenerateScheduleForMonthActionTest.php - - - - message: '#^Property Tests\\Unit\\Schedule\\Actions\\GenerateScheduleForMonthActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Schedule/Actions/GenerateScheduleForMonthActionTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' identifier: property.notFound count: 1 path: tests/Unit/Schedule/Actions/RegenerateScheduleForDateForUsersActionTest.php - - - message: '#^Method Tests\\Unit\\Schedule\\Actions\\RegenerateScheduleForDateForUsersActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/Schedule/Actions/RegenerateScheduleForDateForUsersActionTest.php - - - - message: '#^Property Tests\\Unit\\Schedule\\Actions\\RegenerateScheduleForDateForUsersActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Schedule/Actions/RegenerateScheduleForDateForUsersActionTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$userDish\.$#' identifier: property.notFound @@ -2718,122 +1662,32 @@ parameters: count: 2 path: tests/Unit/Schedule/ScheduleGeneratorTest.php - - - message: '#^Method Tests\\Unit\\Schedule\\ScheduleGeneratorTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/Schedule/ScheduleGeneratorTest.php - - message: '#^Parameter \#1 \$callback of method Illuminate\\Support\\Collection\\:\:reduce\(\) expects callable\(Illuminate\\Support\\Carbon\|null, Carbon\\Carbon, int\)\: Illuminate\\Support\\Carbon, Closure\(Illuminate\\Support\\Carbon\|null, Illuminate\\Support\\Carbon\)\: Illuminate\\Support\\Carbon given\.$#' identifier: argument.type count: 1 path: tests/Unit/Schedule/ScheduleGeneratorTest.php - - - message: '#^Property Tests\\Unit\\Schedule\\ScheduleGeneratorTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Schedule/ScheduleGeneratorTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' identifier: property.notFound count: 3 path: tests/Unit/Schedule/Services/ScheduleCalendarServiceTest.php - - - message: '#^Method Tests\\Unit\\Schedule\\Services\\ScheduleCalendarServiceTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/Schedule/Services/ScheduleCalendarServiceTest.php - - - - message: '#^Property Tests\\Unit\\Schedule\\Services\\ScheduleCalendarServiceTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/Schedule/Services/ScheduleCalendarServiceTest.php - - - - message: '#^Method Tests\\Unit\\ScheduleRepositoryTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/ScheduleRepositoryTest.php - - - - message: '#^Property Tests\\Unit\\ScheduleRepositoryTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/ScheduleRepositoryTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' identifier: property.notFound count: 4 path: tests/Unit/ScheduledUserDish/Actions/DeleteScheduledUserDishForDateActionTest.php - - - message: '#^Method Tests\\Unit\\ScheduledUserDish\\Actions\\DeleteScheduledUserDishForDateActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/ScheduledUserDish/Actions/DeleteScheduledUserDishForDateActionTest.php - - - - message: '#^Property Tests\\Unit\\ScheduledUserDish\\Actions\\DeleteScheduledUserDishForDateActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/ScheduledUserDish/Actions/DeleteScheduledUserDishForDateActionTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' identifier: property.notFound count: 3 path: tests/Unit/ScheduledUserDish/Actions/SkipScheduledUserDishForDateActionTest.php - - - message: '#^Method Tests\\Unit\\ScheduledUserDish\\Actions\\SkipScheduledUserDishForDateActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/ScheduledUserDish/Actions/SkipScheduledUserDishForDateActionTest.php - - - - message: '#^Property Tests\\Unit\\ScheduledUserDish\\Actions\\SkipScheduledUserDishForDateActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/ScheduledUserDish/Actions/SkipScheduledUserDishForDateActionTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 2 - path: tests/Unit/UpdateScheduledUserDishActionTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Unit/UpdateScheduledUserDishActionTest.php - - - - message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' - identifier: argument.type - count: 1 - path: tests/Unit/UpdateScheduledUserDishActionTest.php - - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' identifier: property.notFound count: 1 path: tests/Unit/UserDish/Repositories/UserDishRepositoryTest.php - - - - message: '#^Method Tests\\Unit\\UserDish\\Repositories\\UserDishRepositoryTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' - identifier: return.type - count: 1 - path: tests/Unit/UserDish/Repositories/UserDishRepositoryTest.php - - - - message: '#^Property Tests\\Unit\\UserDish\\Repositories\\UserDishRepositoryTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' - identifier: assign.propertyType - count: 1 - path: tests/Unit/UserDish/Repositories/UserDishRepositoryTest.php diff --git a/phpunit.dusk.xml b/phpunit.dusk.xml deleted file mode 100644 index 24fbe45..0000000 --- a/phpunit.dusk.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - ./tests/Browser - - - diff --git a/shell.nix b/shell.nix index 5a07708..b977c72 100644 --- a/shell.nix +++ b/shell.nix @@ -91,6 +91,12 @@ pkgs.mkShell { dev-test() { podman-compose -f $COMPOSE_FILE exec -T app env $(grep -vE '^\s*(#|$)' .env.testing) php -d memory_limit=512M vendor/bin/phpunit "$@" } + + pest-browser() { + # Run Pest browser tests (Playwright) on the host. Pass --headed for a + # visible browser or --debug to pause on failure. Requires `npm run build`. + vendor/bin/pest tests/Browser "$@" + } dev-fix-permissions() { echo "🔧 Fixing file permissions..." @@ -169,6 +175,7 @@ pkgs.mkShell { echo " dev-shell - Enter app container" echo " dev-artisan - Run artisan commands" echo " dev-test [path] - Run PHPUnit suite (CI invocation)" + echo " pest-browser [--headed] - Run Pest browser tests (Playwright)" echo " dev-fix-permissions - Fix Docker-created file permissions" echo "" echo "Production commands:" diff --git a/tests/Browser/Auth/LoginTest.php b/tests/Browser/Auth/LoginTest.php index fa56d89..fe4431e 100644 --- a/tests/Browser/Auth/LoginTest.php +++ b/tests/Browser/Auth/LoginTest.php @@ -1,91 +1,39 @@ create([ + 'password' => Hash::make('password'), + ]); - protected static $testEmail = null; + visit('/login') + ->type('input[id="email"]', $planner->email) + ->type('input[id="password"]', 'password') + ->press('Sign In') + ->assertPathIs('/dashboard'); +}); - protected static $testPassword = 'password'; +it('rejects wrong credentials', function () { + $planner = Planner::factory()->create([ + 'password' => Hash::make('password'), + ]); - protected function ensureTestPlannerExists(): void - { - if (self::$testPlanner === null) { - // Generate unique email for this test run - self::$testEmail = fake()->unique()->safeEmail(); + visit('/login') + ->type('input[id="email"]', $planner->email) + ->type('input[id="password"]', 'wrong-password') + ->press('Sign In') + ->assertPathIs('/login') + ->assertSee('These credentials do not match our records'); +}); - self::$testPlanner = Planner::factory()->create([ - 'email' => self::$testEmail, - 'password' => Hash::make(self::$testPassword), - ]); - } - } - - public function test_successful_login(): void - { - $this->ensureTestPlannerExists(); - - $this->browse(function (Browser $browser) { - $browser->driver->manage()->deleteAllCookies(); - $browser->visit('http://dishplanner_app:8000/login') - ->waitFor('input[id="email"]', self::TIMEOUT_SHORT) - ->clear('input[id="email"]') - ->type('input[id="email"]', self::$testEmail) - ->clear('input[id="password"]') - ->type('input[id="password"]', self::$testPassword) - ->press('Login') - ->waitForLocation('/dashboard', self::TIMEOUT_MEDIUM) - ->assertPathIs('/dashboard'); - }); - } - - public function test_login_with_wrong_credentials(): void - { - $this->ensureTestPlannerExists(); - - $this->browse(function (Browser $browser) { - $browser->driver->manage()->deleteAllCookies(); - $browser->visit('http://dishplanner_app:8000/login') - ->waitFor('input[id="email"]', self::TIMEOUT_SHORT) - ->clear('input[id="email"]') - ->type('input[id="email"]', self::$testEmail) - ->clear('input[id="password"]') - ->type('input[id="password"]', 'wrongpassword') - ->press('Login') - ->pause(self::PAUSE_MEDIUM) - ->assertPathIs('/login') - ->assertSee('These credentials do not match our records'); - }); - } - - public function test_login_form_required_fields(): void - { - $this->browse(function (Browser $browser) { - $browser->driver->manage()->deleteAllCookies(); - $browser->visit('http://dishplanner_app:8000/login') - ->waitFor('input[id="email"]', self::TIMEOUT_SHORT); - - // Check that both fields have the required attribute - $browser->assertAttribute('input[id="email"]', 'required', 'true'); - $browser->assertAttribute('input[id="password"]', 'required', 'true'); - - // Verify email field is type email - $browser->assertAttribute('input[id="email"]', 'type', 'email'); - - // Verify password field is type password - $browser->assertAttribute('input[id="password"]', 'type', 'password'); - - // Test that we stay on login page if we try to submit with empty fields - $browser->press('Login') - ->pause(self::PAUSE_SHORT) - ->assertPathIs('/login'); - }); - } -} +it('requires the email and password fields', function () { + visit('/login') + ->assertScript("document.querySelector('input[id=\"email\"]').required") + ->assertScript("document.querySelector('input[id=\"password\"]').required") + ->assertAttribute('input[id="email"]', 'type', 'email') + ->assertAttribute('input[id="password"]', 'type', 'password') + ->press('Sign In') + ->assertPathIs('/login'); +}); diff --git a/tests/Browser/Components/DishModal.php b/tests/Browser/Components/DishModal.php deleted file mode 100644 index 1a31400..0000000 --- a/tests/Browser/Components/DishModal.php +++ /dev/null @@ -1,105 +0,0 @@ -mode = $mode; - } - - /** - * Get the root selector for the component. - */ - public function selector(): string - { - // Livewire modals typically have a specific structure - return '[role="dialog"], .fixed.inset-0'; - } - - /** - * Assert that the browser page contains the component. - */ - public function assert(Browser $browser): void - { - $browser->assertVisible($this->selector()); - - if ($this->mode === 'create') { - $browser->assertSee('Add New Dish'); - } else { - $browser->assertSee('Edit Dish'); - } - } - - /** - * Get the element shortcuts for the component. - * - * @return array - */ - public function elements(): array - { - return [ - '@name-input' => 'input[wire\\:model="name"]', - '@description-input' => 'textarea[wire\\:model="description"]', - '@users-section' => 'div:contains("Assign to Users")', - '@submit-button' => $this->mode === 'create' ? 'button:contains("Create Dish")' : 'button:contains("Update Dish")', - '@cancel-button' => 'button:contains("Cancel")', - '@validation-error' => '.text-red-500', - ]; - } - - /** - * Fill the dish form. - */ - public function fillForm(Browser $browser, string $name, ?string $description = null): void - { - $browser->waitFor('@name-input') - ->clear('@name-input') - ->type('@name-input', $name); - - if ($description !== null && $browser->element('@description-input')) { - $browser->clear('@description-input') - ->type('@description-input', $description); - } - } - - /** - * Select users to assign the dish to. - */ - public function selectUsers(Browser $browser, array $userIds): void - { - foreach ($userIds as $userId) { - $browser->check("input[type='checkbox'][value='{$userId}']"); - } - } - - /** - * Submit the form. - */ - public function submit(Browser $browser): void - { - $browser->press($this->mode === 'create' ? 'Create Dish' : 'Update Dish'); - } - - /** - * Cancel the modal. - */ - public function cancel(Browser $browser): void - { - $browser->press('Cancel'); - } - - /** - * Assert validation error is shown. - */ - public function assertValidationError(Browser $browser, string $message = 'required'): void - { - $browser->assertSee($message); - } -} diff --git a/tests/Browser/Components/LoginForm.php b/tests/Browser/Components/LoginForm.php deleted file mode 100644 index 2b817ee..0000000 --- a/tests/Browser/Components/LoginForm.php +++ /dev/null @@ -1,89 +0,0 @@ -assertVisible($this->selector()) - ->assertVisible('@email') - ->assertVisible('@password') - ->assertVisible('@submit'); - } - - /** - * Get the element shortcuts for the component. - * - * @return array - */ - public function elements(): array - { - return [ - '@email' => 'input[id="email"]', - '@password' => 'input[id="password"]', - '@submit' => 'button[type="submit"]', - '@remember' => 'input[name="remember"]', - '@error' => '.text-red-500', - ]; - } - - /** - * Fill in the login form. - */ - public function fillForm(Browser $browser, string $email, string $password): void - { - $browser->type('@email', $email) - ->type('@password', $password); - } - - /** - * Submit the login form. - */ - public function submit(Browser $browser): void - { - $browser->press('@submit'); - } - - /** - * Login with the given credentials. - */ - public function loginWith(Browser $browser, string $email, string $password): void - { - $this->fillForm($browser, $email, $password); - $this->submit($browser); - } - - /** - * Assert that the form fields are required. - */ - public function assertFieldsRequired(Browser $browser): void - { - $browser->assertAttribute('@email', 'required', 'true') - ->assertAttribute('@password', 'required', 'true') - ->assertAttribute('@email', 'type', 'email') - ->assertAttribute('@password', 'type', 'password'); - } - - /** - * Assert that the form has validation errors. - */ - public function assertHasErrors(Browser $browser): void - { - $browser->assertPresent('@error'); - } -} diff --git a/tests/Browser/Dishes/CreateDishFormValidationTest.php b/tests/Browser/Dishes/CreateDishFormValidationTest.php index 318becf..7269456 100644 --- a/tests/Browser/Dishes/CreateDishFormValidationTest.php +++ b/tests/Browser/Dishes/CreateDishFormValidationTest.php @@ -1,50 +1,8 @@ browse(function (Browser $browser) { - $this->loginAndGoToDishes($browser); - - $browser->on(new DishesPage) - ->openCreateModal() - ->within(new DishModal('create'), function ($browser) { - $browser->fillForm('', null) - ->submit() - ->pause(2000) - ->assertValidationError('required'); - }); - }); - } -} +it('validates the dish name field', function () { + loginAndGoToDishes() + ->click('button[wire\:click="create"]') + ->press('Create Dish') + ->assertSee('required'); +}); diff --git a/tests/Browser/Dishes/CreateDishSuccessTest.php b/tests/Browser/Dishes/CreateDishSuccessTest.php index aad8c7c..78501e7 100644 --- a/tests/Browser/Dishes/CreateDishSuccessTest.php +++ b/tests/Browser/Dishes/CreateDishSuccessTest.php @@ -1,53 +1,12 @@ browse(function (Browser $browser) { - $dishName = 'Test Dish '.uniqid(); - - $this->loginAndGoToDishes($browser); - - $browser->on(new DishesPage) - ->openCreateModal() - ->within(new DishModal('create'), function ($browser) use ($dishName) { - $browser->fillForm($dishName) - ->submit(); - }) - ->pause(3000) - ->assertDishVisible($dishName) - ->assertSee('Dish created successfully'); - }); - } -} + loginAndGoToDishes() + ->click('button[wire\:click="create"]') + ->type('input[wire\:model="name"]', $dishName) + ->press('Create Dish') + ->assertSee($dishName) + ->assertSee('Dish created successfully'); +}); diff --git a/tests/Browser/Dishes/CreateDishTest.php b/tests/Browser/Dishes/CreateDishTest.php index c20a99c..f4fc5f9 100644 --- a/tests/Browser/Dishes/CreateDishTest.php +++ b/tests/Browser/Dishes/CreateDishTest.php @@ -1,47 +1,8 @@ browse(function (Browser $browser) { - $this->loginAndGoToDishes($browser); - - $browser->on(new DishesPage) - ->assertSee('MANAGE DISHES') - ->assertSee('Add Dish'); - }); - } - - // TODO: Moved to separate single-method test files to avoid static planner issues - // See: OpenCreateDishModalTest, CreateDishFormValidationTest, CancelDishCreationTest, CreateDishSuccessTest -} +it('can access the dishes page', function () { + loginAndGoToDishes() + ->assertPathIs('/dishes') + ->assertSee('MANAGE DISHES') + ->assertSee('Add Dish'); +}); diff --git a/tests/Browser/Dishes/DeleteDishTest.php b/tests/Browser/Dishes/DeleteDishTest.php index f0d7651..4e392d3 100644 --- a/tests/Browser/Dishes/DeleteDishTest.php +++ b/tests/Browser/Dishes/DeleteDishTest.php @@ -1,77 +1,13 @@ create(['planner_id' => $planner->id, 'name' => 'Test Dish']); -class DeleteDishTest extends DuskTestCase -{ - use LoginHelpers; - - protected static $deleteDishTestPlanner = null; - - protected static $deleteDishTestEmail = null; - - protected function setUp(): void - { - parent::setUp(); - // Reset static planner for this specific test class - self::$testPlanner = self::$deleteDishTestPlanner; - self::$testEmail = self::$deleteDishTestEmail; - } - - protected function tearDown(): void - { - // Save the planner for next test method in this class - self::$deleteDishTestPlanner = self::$testPlanner; - self::$deleteDishTestEmail = self::$testEmail; - parent::tearDown(); - } - - public function test_can_access_delete_feature(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToDishes($browser) - ->assertPathIs('/dishes') - ->assertSee('MANAGE DISHES'); - - // Verify that delete functionality is available by looking for the text in the page source - $pageSource = $browser->driver->getPageSource(); - $this->assertStringContainsString('Delete', $pageSource); - }); - } - - // TODO: Fix static planner issue causing login failures in suite runs - // These tests pass in isolation but fail when run in full suite - /* - public function testDeleteModalComponents(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToDishes($browser) - ->assertSee('MANAGE DISHES') - ->assertSee('Add Dish'); - }); - } - - public function testDeletionSafetyFeatures(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToDishes($browser); - - // Check that Livewire component includes all CRUD features - $pageSource = $browser->driver->getPageSource(); - $this->assertStringContainsString('MANAGE DISHES', $pageSource); - $this->assertStringContainsString('Add Dish', $pageSource); - // Either we have dishes with Delete button OR "No dishes found" message - if (str_contains($pageSource, 'No dishes found')) { - $this->assertStringContainsString('No dishes found', $pageSource); - } else { - $this->assertStringContainsString('Delete', $pageSource); - } - }); - } - */ -} + loginAs($planner, '/dishes') + ->assertPathIs('/dishes') + ->assertSee('MANAGE DISHES') + ->assertSee('Delete'); +}); diff --git a/tests/Browser/Dishes/DishDeletionSafetyTest.php b/tests/Browser/Dishes/DishDeletionSafetyTest.php index 70b030f..5914ecb 100644 --- a/tests/Browser/Dishes/DishDeletionSafetyTest.php +++ b/tests/Browser/Dishes/DishDeletionSafetyTest.php @@ -1,50 +1,13 @@ create(['planner_id' => $planner->id, 'name' => 'Test Dish']); -class DishDeletionSafetyTest extends DuskTestCase -{ - use LoginHelpers; - - protected static $dishDeletionSafetyTestPlanner = null; - - protected static $dishDeletionSafetyTestEmail = null; - - protected function setUp(): void - { - parent::setUp(); - // Reset static planner for this specific test class - self::$testPlanner = self::$dishDeletionSafetyTestPlanner; - self::$testEmail = self::$dishDeletionSafetyTestEmail; - } - - protected function tearDown(): void - { - // Save the planner for next test method in this class - self::$dishDeletionSafetyTestPlanner = self::$testPlanner; - self::$dishDeletionSafetyTestEmail = self::$testEmail; - parent::tearDown(); - } - - public function test_deletion_safety_features(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToDishes($browser); - - // Check that Livewire component includes all CRUD features - $pageSource = $browser->driver->getPageSource(); - $this->assertStringContainsString('MANAGE DISHES', $pageSource); - $this->assertStringContainsString('Add Dish', $pageSource); - // Either we have dishes with Delete button OR "No dishes found" message - if (str_contains($pageSource, 'No dishes found')) { - $this->assertStringContainsString('No dishes found', $pageSource); - } else { - $this->assertStringContainsString('Delete', $pageSource); - } - }); - } -} + loginAs($planner, '/dishes') + ->assertSee('MANAGE DISHES') + ->assertSee('Add Dish') + ->assertSee('Delete'); +}); diff --git a/tests/Browser/Dishes/EditDishTest.php b/tests/Browser/Dishes/EditDishTest.php index c27fec8..99d367c 100644 --- a/tests/Browser/Dishes/EditDishTest.php +++ b/tests/Browser/Dishes/EditDishTest.php @@ -1,74 +1,28 @@ create(['planner_id' => $planner->id, 'name' => 'Test Dish']); -class EditDishTest extends DuskTestCase -{ - use LoginHelpers; + loginAs($planner, '/dishes') + ->assertPathIs('/dishes') + ->assertSee('MANAGE DISHES') + ->assertSee('Edit'); +}); - protected static $editDishTestPlanner = null; +it('shows the edit modal components', function () { + loginAndGoToDishes() + ->assertSee('MANAGE DISHES') + ->assertSee('Add Dish'); +}); - protected static $editDishTestEmail = null; +it('renders the dishes page structure', function () { + $planner = createPlanner(); + Dish::factory()->create(['planner_id' => $planner->id, 'name' => 'Test Dish']); - protected function setUp(): void - { - parent::setUp(); - // Reset static planner for this specific test class - self::$testPlanner = self::$editDishTestPlanner; - self::$testEmail = self::$editDishTestEmail; - } - - protected function tearDown(): void - { - // Save the planner for next test method in this class - self::$editDishTestPlanner = self::$testPlanner; - self::$editDishTestEmail = self::$testEmail; - parent::tearDown(); - } - - public function test_can_access_edit_feature(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToDishes($browser) - ->assertPathIs('/dishes') - ->assertSee('MANAGE DISHES'); - - // Verify that edit functionality is available by looking for the text in the page source - $pageSource = $browser->driver->getPageSource(); - $this->assertStringContainsString('Edit', $pageSource); - }); - } - - public function test_edit_modal_components(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToDishes($browser) - ->assertSee('MANAGE DISHES') - ->assertSee('Add Dish'); - }); - } - - public function test_dishes_page_structure(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToDishes($browser) - ->assertSee('MANAGE DISHES') - ->assertSee('Add Dish'); - - // Check that the dishes CRUD structure is present - $pageSource = $browser->driver->getPageSource(); - // Either we have dishes with Edit/Delete buttons OR "No dishes found" message - if (str_contains($pageSource, 'No dishes found')) { - $this->assertStringContainsString('No dishes found', $pageSource); - } else { - $this->assertStringContainsString('Edit', $pageSource); - $this->assertStringContainsString('Delete', $pageSource); - } - }); - } -} + loginAs($planner, '/dishes') + ->assertSee('Edit') + ->assertSee('Delete'); +}); diff --git a/tests/Browser/LoginHelpers.php b/tests/Browser/LoginHelpers.php deleted file mode 100644 index cfed692..0000000 --- a/tests/Browser/LoginHelpers.php +++ /dev/null @@ -1,66 +0,0 @@ -exists) { - // Generate unique email for this test run - self::$testEmail = fake()->unique()->safeEmail(); - - self::$testPlanner = Planner::factory()->create([ - 'email' => self::$testEmail, - 'password' => Hash::make(self::$testPassword), - ]); - } - } - - protected function loginAndNavigate(Browser $browser, string $page = '/dashboard'): Browser - { - $this->ensureTestPlannerExists(); - - // Clear browser session and cookies to start fresh - $browser->driver->manage()->deleteAllCookies(); - - return $browser->visit('http://dishplanner_app:8000/login') - ->waitFor('input[id="email"]', DuskTestCase::TIMEOUT_SHORT) - ->clear('input[id="email"]') - ->type('input[id="email"]', self::$testEmail) - ->clear('input[id="password"]') - ->type('input[id="password"]', self::$testPassword) - ->press('Sign In') - ->waitForLocation('/dashboard', DuskTestCase::TIMEOUT_MEDIUM) // Wait for successful login redirect - ->pause(DuskTestCase::PAUSE_SHORT) // Brief pause for any initialization - ->visit('http://dishplanner_app:8000'.$page) - ->pause(DuskTestCase::PAUSE_MEDIUM); // Let Livewire components initialize - } - - protected function loginAndGoToDishes(Browser $browser): Browser - { - return $this->loginAndNavigate($browser, '/dishes'); - } - - protected function loginAndGoToUsers(Browser $browser): Browser - { - return $this->loginAndNavigate($browser, '/users'); - } - - protected function loginAndGoToSchedule(Browser $browser): Browser - { - return $this->loginAndNavigate($browser, '/schedule'); - } -} diff --git a/tests/Browser/Pages/DishesPage.php b/tests/Browser/Pages/DishesPage.php deleted file mode 100644 index ae5005f..0000000 --- a/tests/Browser/Pages/DishesPage.php +++ /dev/null @@ -1,86 +0,0 @@ -assertPathIs($this->url()) - ->assertSee('MANAGE DISHES'); - } - - /** - * Get the element shortcuts for the page. - * - * @return array - */ - public function elements(): array - { - return [ - '@add-button' => 'button[wire\\:click="create"]', - '@dishes-list' => '[wire\\:id]', // Livewire component - '@search' => 'input[type="search"]', - '@no-dishes' => '*[text*="No dishes found"]', - ]; - } - - /** - * Open the create dish modal. - */ - public function openCreateModal(Browser $browser): void - { - $browser->waitFor('@add-button') - ->click('@add-button') - ->pause(1000); - } - - /** - * Click edit button for a dish. - */ - public function clickEditForDish(Browser $browser, string $dishName): void - { - $browser->within("tr:contains('{$dishName}')", function ($row) { - $row->click('button.bg-accent-blue'); - }); - } - - /** - * Click delete button for a dish. - */ - public function clickDeleteForDish(Browser $browser, string $dishName): void - { - $browser->within("tr:contains('{$dishName}')", function ($row) { - $row->click('button.bg-red-500'); - }); - } - - /** - * Assert a dish is visible in the list. - */ - public function assertDishVisible(Browser $browser, string $dishName): void - { - $browser->assertSee($dishName); - } - - /** - * Assert no dishes message is shown. - */ - public function assertNoDishes(Browser $browser): void - { - $browser->assertSee('No dishes found'); - } -} diff --git a/tests/Browser/Pages/LoginPage.php b/tests/Browser/Pages/LoginPage.php deleted file mode 100644 index d16642d..0000000 --- a/tests/Browser/Pages/LoginPage.php +++ /dev/null @@ -1,47 +0,0 @@ -assertPathIs($this->url()) - ->assertSee('Login') - ->assertPresent((new LoginForm)->selector()); - } - - /** - * Get the element shortcuts for the page. - * - * @return array - */ - public function elements(): array - { - return [ - '@register-link' => 'a[href*="register"]', - ]; - } - - /** - * Navigate to the registration page. - */ - public function goToRegistration(Browser $browser): void - { - $browser->click('@register-link'); - } -} diff --git a/tests/Browser/Pages/Page.php b/tests/Browser/Pages/Page.php deleted file mode 100644 index 54e0e22..0000000 --- a/tests/Browser/Pages/Page.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ - public static function siteElements(): array - { - return [ - '@nav' => 'nav', - '@alert' => '[role="alert"]', - ]; - } -} diff --git a/tests/Browser/Pages/SchedulePage.php b/tests/Browser/Pages/SchedulePage.php deleted file mode 100644 index bcd787f..0000000 --- a/tests/Browser/Pages/SchedulePage.php +++ /dev/null @@ -1,110 +0,0 @@ -assertPathIs($this->url()) - ->assertSee('SCHEDULE'); - } - - public function elements(): array - { - return [ - '@generate-button' => 'button[wire\\:click="generate"]', - '@clear-month-button' => 'button[wire\\:click="clearMonth"]', - '@previous-month' => 'button[wire\\:click="previousMonth"]', - '@next-month' => 'button[wire\\:click="nextMonth"]', - '@month-select' => 'select[wire\\:model="selectedMonth"]', - '@year-select' => 'select[wire\\:model="selectedYear"]', - '@clear-existing-checkbox' => 'input[wire\\:model="clearExisting"]', - '@calendar-grid' => '.grid.grid-cols-7', - ]; - } - - public function clickGenerate(Browser $browser): void - { - $browser->waitFor('@generate-button') - ->click('@generate-button') - ->pause(2000); // Wait for generation - } - - public function clickClearMonth(Browser $browser): void - { - $browser->waitFor('@clear-month-button') - ->click('@clear-month-button') - ->pause(1000); - } - - public function goToPreviousMonth(Browser $browser): void - { - $browser->waitFor('@previous-month') - ->click('@previous-month') - ->pause(500); - } - - public function goToNextMonth(Browser $browser): void - { - $browser->waitFor('@next-month') - ->click('@next-month') - ->pause(500); - } - - public function selectMonth(Browser $browser, int $month): void - { - $browser->waitFor('@month-select') - ->select('@month-select', $month) - ->pause(500); - } - - public function selectYear(Browser $browser, int $year): void - { - $browser->waitFor('@year-select') - ->select('@year-select', $year) - ->pause(500); - } - - public function toggleClearExisting(Browser $browser): void - { - $browser->waitFor('@clear-existing-checkbox') - ->click('@clear-existing-checkbox'); - } - - public function selectUser(Browser $browser, string $userName): void - { - $browser->check("input[type='checkbox'][value]", $userName); - } - - public function assertSuccessMessage(Browser $browser, ?string $message = null): void - { - if ($message) { - $browser->assertSee($message); - } else { - $browser->assertPresent('.border-success'); - } - } - - public function assertDishScheduled(Browser $browser, string $dishName): void - { - $browser->assertSee($dishName); - } - - public function assertNoDishesScheduled(Browser $browser): void - { - $browser->assertSee('No dishes scheduled'); - } - - public function assertMonthDisplayed(Browser $browser, string $monthYear): void - { - $browser->assertSee($monthYear); - } -} diff --git a/tests/Browser/Pages/UsersPage.php b/tests/Browser/Pages/UsersPage.php deleted file mode 100644 index 7f0aa54..0000000 --- a/tests/Browser/Pages/UsersPage.php +++ /dev/null @@ -1,93 +0,0 @@ -assertPathIs($this->url()) - ->assertSee('MANAGE USERS'); - } - - /** - * Get the element shortcuts for the page. - * - * @return array - */ - public function elements(): array - { - return [ - '@add-button' => 'button[wire\\:click="create"]', - '@users-list' => '[wire\\:id]', // Livewire component - '@no-users' => '*[text*="No users found"]', - ]; - } - - /** - * Open the create user modal. - */ - public function openCreateModal(Browser $browser): void - { - $browser->waitFor('@add-button') - ->click('@add-button') - ->pause(1000); - } - - /** - * Click delete button for a user. - */ - public function clickDeleteForUser(Browser $browser, string $userName): void - { - $browser->within("tr:contains('{$userName}')", function ($row) { - $row->click('button.bg-danger'); - }); - } - - /** - * Click the first available delete button. - */ - public function clickFirstDeleteButton(Browser $browser): void - { - $browser->waitFor('button.bg-danger', 5) - ->click('button.bg-danger') - ->pause(1000); - } - - /** - * Assert a user is visible in the list. - */ - public function assertUserVisible(Browser $browser, string $userName): void - { - $browser->assertSee($userName); - } - - /** - * Assert a user is not visible in the list. - */ - public function assertUserNotVisible(Browser $browser, string $userName): void - { - $browser->assertDontSee($userName); - } - - /** - * Assert success message is shown. - */ - public function assertSuccessMessage(Browser $browser, string $message): void - { - $browser->assertSee($message); - } -} diff --git a/tests/Browser/RedirectTest.php b/tests/Browser/RedirectTest.php index 9b8913c..7732ab1 100644 --- a/tests/Browser/RedirectTest.php +++ b/tests/Browser/RedirectTest.php @@ -1,38 +1,14 @@ assertPathIs('/login') + ->assertSee('Sign In'); +}); -use Illuminate\Foundation\Testing\DatabaseTransactions; -use Laravel\Dusk\Browser; -use Tests\DuskTestCase; - -class RedirectTest extends DuskTestCase -{ - use DatabaseTransactions; - - /** - * Test that unauthenticated users are redirected to login - */ - public function test_unauthenticated_redirects_to_login() - { - $this->browse(function (Browser $browser) { - $browser->visit('http://dishplanner_app:8000/dashboard') - ->assertPathIs('/login') - ->assertSee('Login'); - }); - } - - /** - * Test that login page loads correctly - */ - public function test_login_page_loads() - { - $this->browse(function (Browser $browser) { - $browser->visit('http://dishplanner_app:8000/login') - ->assertPathIs('/login') - ->assertSee('Login') - ->assertSee('Email') - ->assertSee('Password'); - }); - } -} +it('loads the login page', function () { + visit('/login') + ->assertPathIs('/login') + ->assertSee('Email') + ->assertSee('Password'); +}); diff --git a/tests/Browser/Schedule/GenerateScheduleTest.php b/tests/Browser/Schedule/GenerateScheduleTest.php index c2f1f71..ebec33a 100644 --- a/tests/Browser/Schedule/GenerateScheduleTest.php +++ b/tests/Browser/Schedule/GenerateScheduleTest.php @@ -1,128 +1,66 @@ create([ + 'email' => fake()->unique()->safeEmail(), + 'password' => Hash::make('password'), + ]); - protected static $email = null; + $user = User::factory()->create([ + 'planner_id' => $planner->id, + 'name' => 'Test User', + ]); - protected static $password = 'password'; + $dish = Dish::factory()->create([ + 'planner_id' => $planner->id, + 'name' => 'Test Dish', + ]); - protected static $user = null; + $dish->users()->attach($user); - protected static $dish = null; - - protected function setUp(): void - { - parent::setUp(); - - // Create test data if not exists - if (self::$planner === null) { - self::$email = fake()->unique()->safeEmail(); - self::$planner = Planner::factory()->create([ - 'email' => self::$email, - 'password' => Hash::make(self::$password), - ]); - - // Create a user for this planner - self::$user = User::factory()->create([ - 'planner_id' => self::$planner->id, - 'name' => 'Test User', - ]); - - // Create a dish and assign to user - self::$dish = Dish::factory()->create([ - 'planner_id' => self::$planner->id, - 'name' => 'Test Dish', - ]); - - // Attach user to dish (creates UserDish) - self::$dish->users()->attach(self::$user); - } - } - - protected function loginAsPlanner(Browser $browser): Browser - { - $browser->driver->manage()->deleteAllCookies(); - - return $browser->visit('http://dishplanner_app:8000/login') - ->waitFor('input[id="email"]', DuskTestCase::TIMEOUT_SHORT) - ->clear('input[id="email"]') - ->type('input[id="email"]', self::$email) - ->clear('input[id="password"]') - ->type('input[id="password"]', self::$password) - ->press('Login') - ->waitForLocation('/dashboard', DuskTestCase::TIMEOUT_MEDIUM) - ->pause(DuskTestCase::PAUSE_SHORT) - ->visit('http://dishplanner_app:8000/schedule') - ->pause(DuskTestCase::PAUSE_MEDIUM); - } - - public function test_can_generate_schedule_with_user_and_dish(): void - { - $this->browse(function (Browser $browser) { - $this->loginAsPlanner($browser); - - $browser->on(new SchedulePage) - ->assertSee('Test User') // User should be in selection - ->clickGenerate() - ->pause(2000) - // Verify schedule was generated by checking dish appears on calendar - ->assertSee('Test Dish'); - }); - } - - public function test_generated_schedule_shows_dish_on_calendar(): void - { - $this->browse(function (Browser $browser) { - $this->loginAsPlanner($browser); - - $browser->on(new SchedulePage) - ->clickGenerate() - ->pause(2000) - // The dish should appear somewhere on the calendar - ->assertSee('Test Dish'); - }); - } - - public function test_can_clear_month_schedule(): void - { - $this->browse(function (Browser $browser) { - $this->loginAsPlanner($browser); - - $browser->on(new SchedulePage) - // First generate a schedule - ->clickGenerate() - ->pause(2000) - ->assertSee('Test Dish') // Verify generated - // Then clear it - ->clickClearMonth() - ->pause(1000) - // After clearing, should see "No dishes scheduled" on calendar days - ->assertSee('No dishes scheduled'); - }); - } - - public function test_user_selection_affects_generation(): void - { - $this->browse(function (Browser $browser) { - $this->loginAsPlanner($browser); - - $browser->on(new SchedulePage) - // Verify the user checkbox is present - ->assertSee('Test User') - // User should be selected by default - ->assertChecked("input[value='".self::$user->id."']"); - }); - } + return [$planner, $user]; } + +it('can generate a schedule with a user and dish', function () { + [$planner] = makeScheduleFixture(); + + loginAs($planner, '/schedule') + ->assertSee('Test User') + ->click('button[wire\:click="generate"]') + ->assertSee('Test Dish'); +}); + +it('shows the generated dish on the calendar', function () { + [$planner] = makeScheduleFixture(); + + loginAs($planner, '/schedule') + ->click('button[wire\:click="generate"]') + ->assertSee('Test Dish'); +}); + +it('can clear the month schedule', function () { + [$planner] = makeScheduleFixture(); + + loginAs($planner, '/schedule') + ->click('button[wire\:click="generate"]') + ->assertSee('Test Dish') + ->click('button[wire\:click="clearMonth"]') + ->assertSee('No dishes scheduled'); +}); + +it('selects all users by default', function () { + [$planner, $user] = makeScheduleFixture(); + + loginAs($planner, '/schedule') + ->assertSee('Test User') + ->assertChecked('input[value="'.$user->id.'"]'); +}); diff --git a/tests/Browser/Schedule/SchedulePageTest.php b/tests/Browser/Schedule/SchedulePageTest.php index b4d59a0..7b5b5bc 100644 --- a/tests/Browser/Schedule/SchedulePageTest.php +++ b/tests/Browser/Schedule/SchedulePageTest.php @@ -1,108 +1,48 @@ assertSee('SCHEDULE') + ->assertSee('Generate Schedule'); +}); -use Laravel\Dusk\Browser; -use Tests\Browser\LoginHelpers; -use Tests\Browser\Pages\SchedulePage; -use Tests\DuskTestCase; +it('shows the month navigation', function () { + loginAndGoToSchedule() + ->assertPresent('button[wire\:click="previousMonth"]') + ->assertPresent('button[wire\:click="nextMonth"]') + ->assertSee(now()->format('F Y')); +}); -class SchedulePageTest extends DuskTestCase -{ - use LoginHelpers; +it('can navigate to the next month', function () { + $nextMonth = now()->addMonth(); - protected static $schedulePageTestPlanner = null; + loginAndGoToSchedule() + ->click('button[wire\:click="nextMonth"]') + ->assertSee($nextMonth->format('F Y')); +}); - protected static $schedulePageTestEmail = null; +it('can navigate to the previous month', function () { + $previousMonth = now()->subMonth(); - protected function setUp(): void - { - parent::setUp(); - self::$testPlanner = self::$schedulePageTestPlanner; - self::$testEmail = self::$schedulePageTestEmail; - } + loginAndGoToSchedule() + ->click('button[wire\:click="previousMonth"]') + ->assertSee($previousMonth->format('F Y')); +}); - protected function tearDown(): void - { - self::$schedulePageTestPlanner = self::$testPlanner; - self::$schedulePageTestEmail = self::$testEmail; - parent::tearDown(); - } +it('shows the user selection', function () { + loginAndGoToSchedule() + ->assertSee('Select Users') + ->assertPresent('button[wire\:click="generate"]') + ->assertPresent('button[wire\:click="clearMonth"]'); +}); - public function test_can_access_schedule_page(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToSchedule($browser); - - $browser->on(new SchedulePage) - ->assertSee('SCHEDULE') - ->assertSee('Generate Schedule'); - }); - } - - public function test_schedule_page_has_month_navigation(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToSchedule($browser); - - $browser->on(new SchedulePage) - ->assertPresent('@previous-month') - ->assertPresent('@next-month') - ->assertSee(now()->format('F Y')); - }); - } - - public function test_can_navigate_to_next_month(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToSchedule($browser); - - $nextMonth = now()->addMonth(); - - $browser->on(new SchedulePage) - ->goToNextMonth() - ->assertSee($nextMonth->format('F Y')); - }); - } - - public function test_can_navigate_to_previous_month(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToSchedule($browser); - - $prevMonth = now()->subMonth(); - - $browser->on(new SchedulePage) - ->goToPreviousMonth() - ->assertSee($prevMonth->format('F Y')); - }); - } - - public function test_schedule_generator_shows_user_selection(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToSchedule($browser); - - $browser->on(new SchedulePage) - ->assertSee('Select Users') - ->assertPresent('@generate-button') - ->assertPresent('@clear-month-button'); - }); - } - - public function test_calendar_displays_days_of_week(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToSchedule($browser); - - $browser->on(new SchedulePage) - ->assertSee('Mon') - ->assertSee('Tue') - ->assertSee('Wed') - ->assertSee('Thu') - ->assertSee('Fri') - ->assertSee('Sat') - ->assertSee('Sun'); - }); - } -} +it('shows the days of the week', function () { + loginAndGoToSchedule() + ->assertSee('Mon') + ->assertSee('Tue') + ->assertSee('Wed') + ->assertSee('Thu') + ->assertSee('Fri') + ->assertSee('Sat') + ->assertSee('Sun'); +}); diff --git a/tests/Browser/Users/CreateUserTest.php b/tests/Browser/Users/CreateUserTest.php index 6817560..5d32d7c 100644 --- a/tests/Browser/Users/CreateUserTest.php +++ b/tests/Browser/Users/CreateUserTest.php @@ -1,104 +1,43 @@ assertSee('MANAGE USERS') + ->assertSee('Add User'); +}); -use Laravel\Dusk\Browser; -use Tests\Browser\LoginHelpers; -use Tests\Browser\Pages\UsersPage; -use Tests\DuskTestCase; +it('can open the create user modal', function () { + loginAndGoToUsers() + ->click('button[wire\:click="create"]') + ->assertSee('Add New User') + ->assertSee('Name') + ->assertSee('Cancel') + ->assertSee('Create User'); +}); -class CreateUserTest extends DuskTestCase -{ - use LoginHelpers; +it('validates the user name field', function () { + loginAndGoToUsers() + ->click('button[wire\:click="create"]') + ->press('Create User') + ->assertSee('The name field is required'); +}); - protected static $createUserTestPlanner = null; +it('can create a user', function () { + $userName = 'TestCreate_'.uniqid(); - protected static $createUserTestEmail = null; + loginAndGoToUsers() + ->click('button[wire\:click="create"]') + ->type('input[wire\:model="name"]', $userName) + ->press('Create User') + ->assertSee('User created successfully') + ->assertSee($userName); +}); - protected function setUp(): void - { - parent::setUp(); - // Reset static planner for this specific test class - self::$testPlanner = self::$createUserTestPlanner; - self::$testEmail = self::$createUserTestEmail; - } - - protected function tearDown(): void - { - // Save the planner for next test method in this class - self::$createUserTestPlanner = self::$testPlanner; - self::$createUserTestEmail = self::$testEmail; - parent::tearDown(); - } - - public function test_can_access_users_page(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToUsers($browser); - - $browser->on(new UsersPage) - ->assertSee('MANAGE USERS') - ->assertSee('Add User'); - }); - } - - public function test_can_open_create_user_modal(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToUsers($browser); - - $browser->on(new UsersPage) - ->openCreateModal() - ->assertSee('Add New User') - ->assertSee('Name') - ->assertSee('Cancel') - ->assertSee('Create User'); - }); - } - - public function test_create_user_form_validation(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToUsers($browser); - - $browser->on(new UsersPage) - ->openCreateModal() - ->press('Create User') - ->pause(self::PAUSE_MEDIUM) - ->assertSee('The name field is required'); - }); - } - - public function test_can_create_user(): void - { - $this->browse(function (Browser $browser) { - $userName = 'TestCreate_'.uniqid(); - - $this->loginAndGoToUsers($browser); - - $browser->on(new UsersPage) - ->openCreateModal() - ->type('input[wire\\:model="name"]', $userName) - ->press('Create User') - ->pause(self::PAUSE_MEDIUM) - ->assertSee('User created successfully') - ->assertSee($userName); - }); - } - - public function test_can_cancel_user_creation(): void - { - $this->browse(function (Browser $browser) { - $this->loginAndGoToUsers($browser); - - $browser->on(new UsersPage) - ->openCreateModal() - ->type('input[wire\\:model="name"]', 'Test Cancel User') - ->press('Cancel') - ->pause(self::PAUSE_SHORT) - // Modal should be closed, we should be back on users page - ->assertSee('MANAGE USERS') - ->assertDontSee('Add New User'); - }); - } -} +it('can cancel user creation', function () { + loginAndGoToUsers() + ->click('button[wire\:click="create"]') + ->type('input[wire\:model="name"]', 'Test Cancel User') + ->press('Cancel') + ->assertSee('MANAGE USERS') + ->assertDontSee('Add New User'); +}); diff --git a/tests/Browser/console/.gitignore b/tests/Browser/console/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/tests/Browser/console/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/tests/Browser/screenshots/.gitignore b/tests/Browser/screenshots/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/tests/Browser/screenshots/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/tests/Browser/source/.gitignore b/tests/Browser/source/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/tests/Browser/source/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/tests/BrowserTestCase.php b/tests/BrowserTestCase.php new file mode 100644 index 0000000..7534b35 --- /dev/null +++ b/tests/BrowserTestCase.php @@ -0,0 +1,12 @@ +addArguments([ - '--window-size=1920,1080', - '--no-sandbox', - '--disable-dev-shm-usage', - '--disable-gpu', - '--headless=new', - '--disable-extensions', - '--disable-background-timer-throttling', - '--disable-backgrounding-occluded-windows', - '--disable-renderer-backgrounding', - ]); - - return RemoteWebDriver::create( - 'http://selenium:4444/wd/hub', // Connect to Selenium container - DesiredCapabilities::chrome()->setCapability( - ChromeOptions::CAPABILITY, $options - ) - ); - } -} diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..21631d2 --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,63 @@ +in('Feature', 'Unit'); +uses(BrowserTestCase::class, RefreshDatabase::class)->in('Browser'); + +// Livewire round-trips can be slower than the default 5s auto-wait budget. +pest()->browser()->timeout(10000); + +/** + * Create a planner with a known password for browser logins. + */ +function createPlanner(?string $email = null): Planner +{ + return Planner::factory()->create([ + 'email' => $email ?? fake()->unique()->safeEmail(), + 'password' => Hash::make('password'), + ]); +} + +/** + * Log in as the given planner and navigate to the target page. + * + * @return \Pest\Browser\Api\Webpage + */ +function loginAs(Planner $planner, string $page = '/dashboard') +{ + return visit('/login') + ->type('input[id="email"]', $planner->email) + ->type('input[id="password"]', 'password') + ->press('Sign In') + ->assertPathIs('/dashboard') + ->navigate($page); +} + +/** + * @return \Pest\Browser\Api\Webpage + */ +function loginAndGoToDishes() +{ + return loginAs(createPlanner(), '/dishes'); +} + +/** + * @return \Pest\Browser\Api\Webpage + */ +function loginAndGoToUsers() +{ + return loginAs(createPlanner(), '/users'); +} + +/** + * @return \Pest\Browser\Api\Webpage + */ +function loginAndGoToSchedule() +{ + return loginAs(createPlanner(), '/schedule'); +}