diff --git a/.gitattributes b/.gitattributes index c07c43344..694ae5e5a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,3 +19,4 @@ /CONTRIBUTING.md export-ignore /LICENSE export-ignore /README.md export-ignore +/SECURITY.md export-ignore diff --git a/.github/FUNDING.yaml b/.github/FUNDING.yaml index b7ee86d40..e8066d795 100644 --- a/.github/FUNDING.yaml +++ b/.github/FUNDING.yaml @@ -1,12 +1,12 @@ # These are supported funding model platforms -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: crynobone patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: crynobone +liberapay: # crynobone issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username -custom: ["https://paypal.me/crynobone"] +custom: # ["https://paypal.me/crynobone"] diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 000000000..ca79ca5b4 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/analyse.yaml b/.github/workflows/analyse.yaml index 2c34f3f82..da5e16598 100644 --- a/.github/workflows/analyse.yaml +++ b/.github/workflows/analyse.yaml @@ -16,13 +16,13 @@ jobs: continue-on-error: false strategy: matrix: - php: [8.2] + php: [8.3] name: PHP:${{ matrix.php }} Code Analysis steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -37,14 +37,11 @@ jobs: dependency-versions: "highest" composer-options: "--prefer-dist --no-cache" - - name: Installed dependencies - run: composer show -D - - - name: Execute Code Style Analysis + - name: Execute Code Style run: vendor/bin/pint --test - name: Execute Static Code Analysis - run: vendor/bin/phpstan analyse + run: vendor/bin/phpstan analyse --verbose lint: runs-on: "ubuntu-latest" @@ -52,10 +49,9 @@ jobs: strategy: matrix: php: - - '8.0' - 8.3 - dependencies: - - "highest" + - 8.4 + - 8.5 experimental: - false @@ -63,7 +59,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/audits.yaml b/.github/workflows/audits.yaml index 99fcf3f8c..4c9b7db0c 100644 --- a/.github/workflows/audits.yaml +++ b/.github/workflows/audits.yaml @@ -17,20 +17,17 @@ jobs: strategy: matrix: php: - - '8.0' - - 8.1 - - 8.2 - 8.3 + - 8.4 + - 8.5 experimental: - true - exclude: - - php: '8.0' name: PHP:${{ matrix.php }} Code Audit steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/collision-tests.yaml b/.github/workflows/collision-tests.yaml index 94980db03..0fedff057 100644 --- a/.github/workflows/collision-tests.yaml +++ b/.github/workflows/collision-tests.yaml @@ -17,24 +17,17 @@ jobs: strategy: matrix: php: - - '8.0' - - 8.1 - - 8.2 - 8.3 - dependencies: - - "highest" - - "lowest" + - 8.4 + - 8.5 experimental: - false - exclude: - - php: 8.3 - dependencies: "lowest" - name: PHP:${{ matrix.php }} / Collision:^6.2 ${{ matrix.dependencies == 'highest' && '⬆️' || '⬇️' }} + name: PHP:${{ matrix.php }} / Collision:^8.0 ⬆️ steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -43,30 +36,24 @@ jobs: extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, intl, fileinfo, :php-psr coverage: none - - name: Install PHPUnit ^9.5.10 + - name: Install Collision run: | - composer require "phpunit/phpunit:^9.5.10" --no-interaction --no-update - composer require "nunomaduro/collision:^6.2" --dev --no-interaction --no-update + composer require "nunomaduro/collision:^8.0" --dev --no-interaction --no-update - name: Install dependencies uses: ramsey/composer-install@v4 with: - dependency-versions: "${{ matrix.dependencies }}" - composer-options: "--prefer-dist --no-cache" + dependency-versions: "highest" + composer-options: "--prefer-dist --prefer-stable --no-cache" - name: Installed dependencies run: | composer show -D - name: Execute tests (with deprecations) - run: ./testbench package:test --exclude-group commander - if: matrix.dependencies == 'highest' - - - name: Execute tests (without deprecations) - run: ./testbench package:test --exclude-group commander,deprecations + run: php testbench package:test --exclude-group commander env: - TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS: false - if: matrix.dependencies != 'highest' + RAY_ENABLED: false tests-with-coverage: runs-on: "ubuntu-latest" @@ -74,20 +61,17 @@ jobs: strategy: matrix: php: - - '8.0' - - 8.1 - - 8.2 - 8.3 - dependencies: - - "highest" + - 8.4 + - 8.5 experimental: - false - name: PHP:${{ matrix.php }} / Collision:^6.2 Code Coverage ${{ matrix.dependencies == 'highest' && '⬆️' || '⬇️' }} + name: PHP:${{ matrix.php }} / Collision:^8.0 Code Coverage ⬆️ steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -96,22 +80,22 @@ jobs: extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, intl, fileinfo, :php-psr coverage: xdebug - - name: Install PHPUnit ^9.5.10 + - name: Install Collision run: | - composer require "phpunit/phpunit:^9.5.10" --no-interaction --no-update - composer require "nunomaduro/collision:^6.2" --dev --no-interaction --no-update + composer require "nunomaduro/collision:^8.0" --dev --no-interaction --no-update - name: Install dependencies uses: ramsey/composer-install@v4 with: - dependency-versions: "${{ matrix.dependencies }}" - composer-options: "--prefer-dist --no-cache" + dependency-versions: "highest" + composer-options: "--prefer-dist --prefer-stable --no-cache" - name: Installed dependencies run: | composer show -D - name: Execute tests - run: ./testbench package:test --coverage --exclude-group commander,deprecations + run: php testbench package:test --coverage --exclude-group commander --exclude-group deprecations env: + RAY_ENABLED: false TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS: false diff --git a/.github/workflows/coveralls.yaml b/.github/workflows/coveralls.yaml index 83e50280d..fed8d7027 100644 --- a/.github/workflows/coveralls.yaml +++ b/.github/workflows/coveralls.yaml @@ -16,13 +16,14 @@ jobs: continue-on-error: false strategy: matrix: - php: [8.2] + php: [8.3] + phpunit: ["^12.5.8"] - name: PHP:${{ matrix.php }} Code Coverage + name: PHP:${{ matrix.php }} / PHPUnit:${{ matrix.phpunit }} Code Coverage steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -38,10 +39,13 @@ jobs: composer-options: "--prefer-dist" - name: Installed dependencies - run: composer show -D + run: | + composer show -D - name: Execute tests run: vendor/bin/phpunit --coverage-clover build/logs/clover.xml + env: + RAY_ENABLED: false - name: Upload coverage results to Coveralls env: diff --git a/.github/workflows/parallel-tests.yaml b/.github/workflows/parallel-tests.yaml index 2fda6999f..d8049a78f 100644 --- a/.github/workflows/parallel-tests.yaml +++ b/.github/workflows/parallel-tests.yaml @@ -11,23 +11,129 @@ permissions: contents: read jobs: - tests: + tests-on-phpunit13: + runs-on: "ubuntu-latest" + continue-on-error: ${{ matrix.experimental }} + strategy: + matrix: + php: + - 8.4 + - 8.5 + collision: + - "^8.0" + paratest: + - "^7.18" + phpunit: + - "~13.0.0" + - "~13.1.0" + - "~13.2.0" + experimental: + - false + + name: PHP:${{ matrix.php }} / Paratest:${{ matrix.paratest }} / PHPUnit:${{ matrix.phpunit }} ⬆️ + + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, intl, fileinfo, :php-psr + coverage: none + + - name: Install Paratest + run: | + composer require "nunomaduro/collision:${{ matrix.collision }}" "brianium/paratest:${{ matrix.paratest }}" --dev --no-interaction --no-update + + - name: Install dependencies + uses: ramsey/composer-install@v4 + with: + dependency-versions: "highest" + composer-options: "--prefer-dist --no-cache --with=phpunit/phpunit:${{ matrix.phpunit }}" + + - name: Installed dependencies + run: | + composer show -D + + - name: Execute tests + run: php testbench package:test --parallel --exclude-group commander --exclude-group without-parallel --exclude-group database --exclude-group session --no-coverage + env: + RAY_ENABLED: false + + tests-on-phpunit12: + runs-on: "ubuntu-latest" + continue-on-error: ${{ matrix.experimental }} + strategy: + matrix: + php: + - 8.3 + - 8.4 + - 8.5 + collision: + - "^8.0" + paratest: + - "^7.3" + phpunit: + - "~12.5.8" + experimental: + - false + + name: PHP:${{ matrix.php }} / Paratest:${{ matrix.paratest }} / PHPUnit:${{ matrix.phpunit }} ⬆️ + + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, intl, fileinfo, :php-psr + coverage: none + + - name: Install Paratest + run: | + composer require "nunomaduro/collision:${{ matrix.collision }}" "brianium/paratest:${{ matrix.paratest }}" --dev --no-interaction --no-update + + - name: Install dependencies + uses: ramsey/composer-install@v4 + with: + dependency-versions: "highest" + composer-options: "--prefer-dist --no-cache --with=phpunit/phpunit:${{ matrix.phpunit }}" + + - name: Installed dependencies + run: | + composer show -D + + - name: Execute tests + run: php testbench package:test --parallel --exclude-group commander --exclude-group without-parallel --exclude-group database --exclude-group session --no-coverage + env: + RAY_ENABLED: false + + tests-on-phpunit11: runs-on: "ubuntu-latest" continue-on-error: ${{ matrix.experimental }} strategy: matrix: php: - - 8.1 - - 8.2 - 8.3 + - 8.4 + collision: + - "^8.0" + paratest: + - "^7.3" + phpunit: + - "~11.5.50" experimental: - false - name: PHP:${{ matrix.php }} / Paratest:^6.4 ⬆️ + name: PHP:${{ matrix.php }} / Paratest:${{ matrix.paratest }} / PHPUnit:${{ matrix.phpunit }} ⬆️ steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -36,20 +142,21 @@ jobs: extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, intl, fileinfo, :php-psr coverage: none - - name: Install PHPUnit ^9.5.10 + - name: Install Paratest run: | - composer require "phpunit/phpunit:^9.5.10" --no-interaction --no-update - composer require "nunomaduro/collision:^6.2" "brianium/paratest:^6.4" --dev --no-interaction --no-update + composer require "nunomaduro/collision:${{ matrix.collision }}" "brianium/paratest:${{ matrix.paratest }}" --dev --no-interaction --no-update - name: Install dependencies uses: ramsey/composer-install@v4 with: dependency-versions: "highest" - composer-options: "--prefer-dist --no-cache" + composer-options: "--prefer-dist --no-cache --with=phpunit/phpunit:${{ matrix.phpunit }}" - name: Installed dependencies run: | composer show -D - name: Execute tests - run: ./testbench package:test --parallel --exclude-group without-parallel,commander,database,session + run: php testbench package:test --parallel --exclude-group commander --exclude-group without-parallel --exclude-group database --exclude-group session --no-coverage + env: + RAY_ENABLED: false diff --git a/.github/workflows/strict-tests.yaml b/.github/workflows/strict-tests.yaml new file mode 100644 index 000000000..5fa2b972b --- /dev/null +++ b/.github/workflows/strict-tests.yaml @@ -0,0 +1,73 @@ +name: strict-tests + +on: + push: + branches: + - '*.x' + pull_request: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' + +jobs: + strict-tests: + runs-on: "ubuntu-latest" + continue-on-error: ${{ matrix.experimental }} + strategy: + matrix: + php: + - 8.3 + - 8.4 + - 8.5 + phpunit: + - "~11.5.50" + - "~12.5.8" + - "~13.0.0" + - "~13.1.0" + - "~13.2.0" + - "~13.3.0" + experimental: + - false + include: + - php: 8.6 + phpunit: "~13.3.0" + experimental: true + exclude: + - php: 8.3 + phpunit: "~13.0.0" + - php: 8.2 + phpunit: "~13.1.0" + - php: 8.3 + phpunit: "~13.1.0" + - php: 8.3 + phpunit: "~13.2.0" + - php: 8.3 + phpunit: "~13.3.0" + + name: PHP${{ matrix.php }} / PHPUnit:${{ matrix.phpunit }} ⬆️ + + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, intl, fileinfo, :php-psr + coverage: none + + - name: Install dependencies + uses: ramsey/composer-install@v4 + with: + dependency-versions: "highest" + composer-options: "--prefer-dist --no-cache --with=phpunit/phpunit:${{ matrix.phpunit }} ${{ matrix.experimental == true && '--ignore-platform-reqs' || '' }}" + + - name: Installed dependencies + run: | + composer show -D + + - name: Execute tests (with deprecations) + run: vendor/bin/phpunit --fail-on-warning --fail-on-deprecation --fail-on-notice --display-deprecations + env: + RAY_ENABLED: false diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 2fc85f6df..3521b8cf8 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -13,7 +13,7 @@ permissions: contents: read jobs: - tests-on-phpunit-9: + tests: runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} strategy: @@ -22,23 +22,42 @@ jobs: - "ubuntu-latest" - "windows-latest" php: - - '8.0' - - 8.1 - - 8.2 - 8.3 + - 8.4 + - 8.5 phpunit: - - "9.x" + - "~11.5.50" + - "~12.5.8" + - "~13.0.0" + - "~13.1.0" + - "~13.2.0" + - "~13.3.0" dependencies: - "highest" - "lowest" experimental: - false + include: + - php: 8.6 + phpunit: "~13.3.0" + os: "ubuntu-latest" + dependencies: "highest" + experimental: true + exclude: + - php: 8.3 + phpunit: "~13.0.0" + - php: 8.3 + phpunit: "~13.1.0" + - php: 8.3 + phpunit: "~13.2.0" + - php: 8.3 + phpunit: "~13.3.0" name: PHP:${{ matrix.php }} / PHPUnit:${{ matrix.phpunit }} / OS:${{ matrix.os }} ${{ matrix.dependencies == 'highest' && '⬆️' || '⬇️' }} steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -47,26 +66,25 @@ jobs: extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, intl, fileinfo, :php-psr coverage: none - - name: Install PHPUnit ${{ matrix.phpunit }} - run: | - composer require "phpunit/phpunit:${{ matrix.phpunit }}" --no-interaction --no-update - - name: Install dependencies uses: ramsey/composer-install@v4 with: dependency-versions: "${{ matrix.dependencies }}" - composer-options: "--prefer-dist --no-cache" + composer-options: "--prefer-dist --no-cache --with=phpunit/phpunit:${{ matrix.phpunit }} ${{ matrix.experimental == true && '--ignore-platform-reqs' || '' }}" - name: Installed dependencies run: | composer show -D - name: Execute tests (with deprecations) - run: vendor/bin/phpunit + run: vendor/bin/phpunit --display-deprecations ${{ matrix.dependencies == 'highest' && '--fail-on-deprecation' || '' }} + env: + RAY_ENABLED: false if: matrix.os != 'windows-latest' - name: Execute tests (without deprecations) - run: vendor/bin/phpunit --exclude-group=deprecations + run: vendor/bin/phpunit --exclude-group deprecations env: + RAY_ENABLED: false TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS: false if: matrix.os == 'windows-latest' diff --git a/.github/workflows/update-skeleton.yaml b/.github/workflows/update-skeleton.yaml new file mode 100644 index 000000000..25ef56ce4 --- /dev/null +++ b/.github/workflows/update-skeleton.yaml @@ -0,0 +1,43 @@ +name: "update-skeleton" + +on: + workflow_dispatch: + +permissions: + contents: write + +jobs: + update: + name: Update Skeleton + + runs-on: ubuntu-latest + continue-on-error: false + + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, intl, fileinfo, :php-psr + coverage: none + + - name: Install Dependencies + run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress + + - name: Installed dependencies + run: composer show -D + + - name: Sync changes + run: | + php bin/sync + + - name: Execute Code Style Analysis + run: vendor/bin/pint + + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: Update Skeleton diff --git a/CHANGELOG-10.x.md b/CHANGELOG-10.x.md new file mode 100644 index 000000000..48c4f7a63 --- /dev/null +++ b/CHANGELOG-10.x.md @@ -0,0 +1,343 @@ +# Changes for 10.x + +This changelog references the relevant changes (bug and security fixes) done to `orchestra/testbench-core`. + +## 10.15.0 + +Released: 2026-09-23 + +### Added + +* Add `Orchestra\Testbench\Foundation\Env::flushState()` method and revert class deprecation. +* Add `Orchestra\Testbench\php_version_compare()` function. +* Allow `--pretend` options on relevant commands. + +## 10.14.1 + +Released: 2026-04-24 + +### Fixes + +* Fix `Orchestra\Testbench\Attributes\WithConfig` shouldn't defer setting up framework configuration. + +## 10.14.0 + +Released: 2026-04-23 + +### Changes + +* Loads `Orchestra\Testbench\Attributes\WithConfig` after application has been booted to allow merging configuration via Service Provider by default. Use `defer: false` parameter to disable this. + +### Fixes + +* Fix missing `--without-cache` option when using `package:test` command with `nunomaduro/collision` version `8.9.4+`. + +## 10.13.0 + +Released: 2026-04-07 + +### Changes + +* Supports PHPUnit 13.1. +* Removed no longer relevants `method_exists()` and `class_exists()` usage. + +## 10.12.1 + +Released: 2026-03-31 + +### Changes + +* Overrides `ServeCommand::trap()` method to use `TerminatingConsole`. + +## 10.12.0 + +Released: 2026-03-24 + +### Changes + +* Add `TESTBENCH_USER_MODEL` environment variable when running `serve` command. +* Utilise `Orchestra\Testbench\Foundation\Console\TerminatingConsole` when running `serve`. + +## 10.11.0 + +Released: 2026-03-18 + +### Changes + +* Supports for Laravel Framework 12.55.0. + +### Fixes + +* Fix `--parallel` compatibility with `WithFixtures` trait. + +## 10.10.0 + +Released: 2026-03-16 + +### Changes + +* Run seeder when `testbench.yaml` configured with `seeders: true`. +* Use predefined `$__filename` to resolve original PestPHP's testCase file. +* Update skeleton. + +### Fixes + +* Fix `#[UsesVendor]` attribute fails due to unbooted application, causing `BindingResolutionException` to be thrown. + +## 10.9.0 + +Released: 2026-01-12 + +### Added + +* Add `Orchestra\Testbench\package_version_compare()` function. +* Add `Orchestra\Testbench\Concerns\WithFixtures` to automatically loads fixtures file for test. + +### Changes + +* Use `Orchestra\Sidekick\package_path()` for better root package path detection. + +## 10.8.2 + +Released: 2025-12-26 + +### Changes + +* Supports flushing `JsonResource` and `JsonApiResource` states. + +## 10.8.1 + +Released: 2025-12-08 + +### Changes + +* Supports PHPUnit 12.5. + +## 10.8.0 + +Released: 2025-11-24 + +### Changes + +* PHP 8.5 Compatibility. +* Supports for Laravel Framework 12.40.0 or above. +* Fix symlink removal on Windows environment in the following classes: + - `Orchestra\Testbench\Workbench\Actions\AddAssetSymlinkFolders` + - `Orchestra\Testbench\Workbench\Actions\RemoveAssetSymlinkFolders` + +## 10.7.0 + +Released: 2025-10-14 + +### Changes + +* Supports for Laravel Framework 12.28.0 or above. +* Supports PHPUnit 12.4. +* Support disabling Laravel default service providers. +* Flush `Illuminate\Support\Str` states via during test teardown. + +## 10.6.5 + +Released: 2025-09-03 + +### Added + +* Add `Orchestra\Testbench\uses_default_skeleton()` function. +* Supports flushing `Illuminate\Validator\Validator` state. + +### Fixes + +* fix loading framework configuration for `laravel/framework` repository. + +## 10.6.4 + +Released: 2025-09-03 + +### Added + +* Added `Orchestra\Testbench\bail()` function to allow using `exit()` on `pnctl` enabled tests. + +### Changes + +* Ability to merge Framework configurations when using custom skeleton. + +## 10.6.3 + +Released: 2025-08-28 + +### Added + +* Added `Orchestra\Testbench\terminate()` function to allow using `exit()` on `pnctl` enabled tests. + +## 10.6.2 + +Released: 2025-08-27 + +### Changes + +* Update skeleton's configuration. + +### Fixes + +* Fix `Orchestra\Testbench\remote()` with serializable closure usage. + +## 10.6.1 + +Released: 2025-08-20 + +### Changes + +* Revert minimum supported Laravel Framework version to 12.8.0 and above. + +## 10.6.0 + +Released: 2025-08-12 + +### Changes + +* Supports for Laravel Framework 12.23.2 or above (fixed integration with PHPUnit 12.3.4). + +## 10.5.0 + +Released: 2025-08-07 + +### Changes + +* Supports PHPUnit 12.3. +* Remove temporary SQLite database files available via `journal_mode` configuration. +* Convert `Collection::make()` to `new Collection()`. + +## 10.4.0 + +Released: 2025-06-08 + +### Changes + +* Supports PHPUnit 12.2. +* `Orchestra\Foundation\Env` now extends `Orchestra\Sidekick\Env`. +* Update skeleton's configuration. + +## 10.3.0 + +Released: 2025-05-12 + +### Changes + +* Requires Laravel Framework 12.8.0 and above. +* `Orchestra\Testbench\PHPUnit\TestCase` now implements `Orchestra\Testbench\Concerns\InteractsWithMockery`. + +### Fixes + +* Fix handling deprecations logging when logger is not not available when running tests. + +## 10.2.3 + +Released: 2025-05-07 + +### Changes + +* Flush `Illuminate\Database\Eloquent\Model::automaticallyEagerLoadRelationships()` state between tests if the method exists. + +## 10.2.2 + +Released: 2025-04-27 + +### Changes + +* Flush `Illuminate\Database\Eloquent\Model` states between tests. + +## 10.2.1 + +Released: 2025-04-13 + +### Changes + +* Remove `symfony/polyfill-php84`. + +## 10.2.0 + +Released: 2025-04-06 + +### Added + +* Add ability to pass `Closure` to `Orchestra\Testbench\remote()` function. + +### Changes + +* Add support for PHPUnit 12.1. +* Refactor `Orchestra\Testbench\remote()` function to use `Orchestra\Testbench\Foundation\Process\RemoteCommand`. +* Rename `TESTBENCH_ENVIRONMENT_FILE_USING` to `TESTBENCH_ENVIRONMENT_FILE_USING` (internal environment variable). + +## 10.1.0 + +Released: 2025-03-06 + +### Added + +* Added `resolvePhpUnitTestClassName()` and `resolvePhpUnitTestMethodName()` to `Orchestra\Testbench\Concerns\InteractsWithPHPUnit` trait. + +### Changes + +* Allows `usesTestingFeature()` to register attribute directly on test method. +* Improves `vendor` detection on the default skeleton. +* Utilise `Orchestra\Sidekick\is_symlink()` function instead of `is_link()` to improves support on Windows. +* Use `::class` instead of `get_class()`. +* Delete `vendor` symlink via `package:purge-skeleton` command. + +### Fixes + +* Fix static variable via `Orchestra\Testbench\Attributes\UsesVendor::beforeEach()` method. + +### Deprecate + +* Deprecate following PHPUnit annotations: + - `@environment-setup` + - `@define-env` + - `@define-database` + - `@define-route` + +## 10.0.3 + +Released: 2025-03-03 + +### Fixes + +* Fix `Orchestra\Testbench\Attributes\UsesVendor` causes IoC Container to be out of sync. + +## 10.0.2 + +Released: 2025-02-25 + +### Changes + +* Revert `filesystems.disks.local.serve` default configuration value to `true`. + +## 10.0.1 + +Released: 2025-02-24 + +### Changes + +* Set `filesystems.disks.local.serve` default configuration value to `false`. + +## 10.0.0 + +Released: 2025-02-24 + +### Changes + +* Update support for Laravel Framework v12. +* Update `Orchestra\Testbench\TestCase` to use `Illuminate\Foundation\Testing\Concerns\InteractsWithViews` trait. + +### Removed + +* Remove deprecated functions: + - `Orchestra\Testbench\once()` + - `Orchestra\Testbench\transform_relative_path()` +* Remove deprecated methods in `Orchestra\Testbench\Concerns\CreatesApplication` trait: + - `getBasePath()` + - `getDefaultApplicationBootstrapFile()` +* Remove deprecated methods in `Orchestra\Testbench\Concerns\InteractsWithMigrations` trait: + - `loadMigrationsWithoutRollbackFrom()` + - `loadLaravelMigrationsWithoutRollback()` + - `runLaravelMigrationsWithoutRollback()` diff --git a/CHANGELOG-11.x.md b/CHANGELOG-11.x.md new file mode 100644 index 000000000..49c309542 --- /dev/null +++ b/CHANGELOG-11.x.md @@ -0,0 +1,155 @@ +# Changes for 11.x + +This changelog references the relevant changes (bug and security fixes) done to `orchestra/testbench-core`. + +## 11.5.0 + +Released: 2026-09-23 + +### Added + +* Add `Orchestra\Testbench\Foundation\Env::flushState()` method and revert class deprecation. +* Add `Orchestra\Testbench\php_version_compare()` function. +* Allow `--pretend` options on relevant commands. + +### Changes + +* Supports for Laravel Framework 13.32.0. +* Experimental supports for PHP 8.6. + +## 11.4.0 + +Released: 2026-08-07 + +### Changes + +* Supports for Laravel Framework 13.23.0. +* Supports for PHPUnit 13.3.0. + +## 11.3.6 + +Released: 2026-08-05 + +### Changes + +* Update skeleton. + +## 11.3.5 + +Released: 2026-06-23 + +### Changes + +* Supports for PHPUnit 13.2.0. + +## 11.3.4 + +Released: 2026-05-20 + +### Changes + +* Supports for Laravel Framework 13.10.0. +* Update skeleton. + +## 11.3.3 + +Released: 2026-05-14 + +### Changes + +* Supports for Laravel Framework 13.9.0. +* Update skeleton. + +## 11.3.2 + +Released: 2026-05-06 + +### Changes + +* Supports for Laravel Framework 13.7.0. +* Requires `symfony/polyfill-php84` for `Pdo\Mysql` class on PHP 8.3 and below. +* Update skeleton. + +## 11.3.1 + +Released: 2026-04-24 + +### Fixes + +* Fix `Orchestra\Testbench\Attributes\WithConfig` shouldn't defer setting up framework configuration. + +## 11.3.0 + +Released: 2026-04-23 + +### Changes + +* Loads `Orchestra\Testbench\Attributes\WithConfig` after application has been booted to allow merging configuration via Service Provider by default. Use `defer: false` parameter to disable this. +* Update skeleton. + +### Fixes + +* Fix missing `--without-cache` option when using `package:test` command with `nunomaduro/collision` version `8.9.4+`. + +## 11.2.1 + +Released: 2026-04-09 + +### Changes + +* Add `flushState()` to `FormRequest` to reset global strict mode between tests. + +## 11.2.0 + +Released: 2026-04-07 + +### Changes + +* Supports PHPUnit 13.1. +* Removed no longer relevants `method_exists()` and `class_exists()` usage. + +## 11.1.1 + +Released: 2026-03-31 + +### Changes + +* Overrides `ServeCommand::trap()` method to use `TerminatingConsole`. + +## 11.1.0 + +Released: 2026-03-24 + +### Changes + +* Supports for Laravel Framework 13.1.1. +* Add `TESTBENCH_USER_MODEL` environment variable when running `serve` command. +* Utilise `Orchestra\Testbench\Foundation\Console\TerminatingConsole` when running `serve`. + +## 11.0.1 + +Released: 2026-03-18 + +### Fixes + +* Fix `--parallel` compatibility with `WithFixtures` trait. + +## 11.0.0 + +Released: 2026-03-16 + +### Changes + +* Update support for Laravel Framework v13. + +### Removed + +* Remove deprecated `Orchestra\Testbench\Foundation\Env` class. +* Remove deprecated `Orchestra\Testbench\Foundation\Console\Concerns\HandleTerminatingConsole` trait. +* Remove deprecated `Orchestra\Testbench\Foundation\Console\Actions\Action::pathLocation()` method. +* Remove deprecated `Orchestra\Testbench\laravel_migration_path()` function. +* Remove supports for deprecated annotations: + - `@define-env` + - `@environment-setup` + - `@define-db` + - `@define-route` diff --git a/CHANGELOG-6.x.md b/CHANGELOG-6.x.md deleted file mode 100644 index ed93a5fb4..000000000 --- a/CHANGELOG-6.x.md +++ /dev/null @@ -1,1136 +0,0 @@ -# Changes for 6.x - -This changelog references the relevant changes (bug and security fixes) done to `orchestra/testbench-core`. - -## 6.53.1 - -Released: 2024-10-06 - -### Fixes - -* Prevent seeder from being executed when `shouldSeed()` exists and return `false`. - -## 6.53.0 - -Released: 2024-09-23 - -### Added - -* Added `markTestSkippedWhen()` and `markTestSkippedUnless()` assertion helper to conditionally handle `markTestSkipped()`. -* Added `Orchestra\Testbench\default_migration_path()` helper function. -* Added `Orchestra\Testbench\laravel_vendor_exists()` helper function. - -### Changes - -* Allow Testbench to delete `vendor` symlink directory if it was created while running tests. - -### Fixes - -* Fixes `view.paths` configuration not being updated to include `workbench/resources/views` due to IoC booting sequence. - -### Deprecated - -* Deprecated `Orchestra\Testbench\laravel_migration_path()`, use `default_migration_path()` instead. - -## 6.52.0 - -Released: 2024-08-26 - -### Added - -* Added `artisan` binary to Laravel skeleton. -* Added `Orchestra\Testbench\join_paths()` function. - -### Changes - -* Change `afterApplicationCreated()`, `afterApplicationRefreshed()`, and `beforeApplicationDestroyed()` methods visibility from `protected` to `public`. -* Improvements to `Orchestra\Testbench\default_skeleton_path()`, `Orchestra\Testbench\package_path()`, and `Orchestra\Testbench\workbench_path()` usage based on new `Orchestra\Testbench\join_paths()` function. - -## 6.51.2 - -Released: 2024-08-14 - -### Changes - -* Code Improvements. - -## 6.51.1 - -Released: 2024-07-19 - -### Fixes - -* Fixes `InteractsWithPublishedFiles` should only flush published files within `database/migrations` directory. - -## 6.51.0 - -Released: 2024-07-13 - -### Added - -* Added new attributes: - - `Orchestra\Testbench\Attributes\ResolvesLaravel` - - `Orchestra\Testbench\Attributes\UsesFrameworkConfiguration` -* Allows to discover `factories` using Workbench to map `Workbench\App\Models` to `Workbench\Database\Factories` classes. -* Allows to auto discover console command classes from `workbench/app/Console/Commands`. - -### Changes - -* Implements `JsonSerializable` to `Orchestra\Testbench\Foundation\UndefinedValue`. -* Update skeleton to use `workbench` as default environment value. -* Allow `Orchestra\Testbench\Attributes\Define` and `Orchestra\Testbench\Attributes\DefineEnvironment` to be used on the class level by [@danjohnson95](https://github.com/danjohnson95) - -## 6.50.2 - -Released: 2024-06-04 - -### Fixes - -* Fixes `Orchestra\Testbench\Workench\Workbench::applicationExceptionHandler()` usage to detect `Workbench\App\Exceptions\Handler` class. - -## 6.50.1 - -Released: 2024-06-01 - -### Fixes - -* Fixes `Orchestra\Testbench\Attributes\RequiresLaravel` attribute usage. - -## 6.50.0 - -Released: 2024-05-21 - -### Changes - -* Uses `TESTBENCH_WORKING_PATH` from environment variable before fallback to `getcwd()`. -* PHPStan Improvements. - -## 6.49.7 - -Released: 2024-04-21 - -### Fixes - -* Backport fixes to routing registration using macro with Workbench. - -## 6.49.6 - -Released: 2024-04-13 - -### Changes - -* Flush Static Improvements. -* Allows `Orchestra\Testbench\remote` to accept `$env` with either `array` or `string`. - -## 6.49.5 - -Released: 2024-03-25 - -### Fixes - -* Fixes `RefreshDatabase` to be executed on `tearDown()` only limited when ad-hoc migrations was added during test. - -## 6.49.4 - -Released: 2024-03-19 - -### Fixes - -* Fixes `beforeApplicationDestroyed()` usage on `loadLaravelMigrations()` method. - -## 6.49.3 - -Released: 2024-03-19 - -### Fixes - -* Fixes `RefreshDatabase` usage does not reset the database migrations between tests. - -## 6.49.2 - -Released: 2024-03-18 - -### Changes - -* Check against `RefreshDatabaseState::$migrated` before loading migration paths to the instance of `migrator`. - -## 6.49.1 - -Released: 2024-03-15 - -### Fixes - -* Fixes `class_implements(): Class AllowDynamicProperties does not exist and could not be loaded` error on PHP 8.1 and lower. - -## 6.49.0 - -Released: 2024-03-13 - -### Added - -* Added `Orchestra\Testbench\Attributes\RequiresLaravel` attribute. -* Added `Orchestra\Testbench\load_migration_paths()` function. -* Added `Orchestra\Testbench\laravel_version_compare()` function. -* Added `Orchestra\Testbench\phpunit_version_compare()` function. - -### Changes - -* Validate `MYSQL_*`, `MSSQL_*`, `SQLITE_*` and `POSTGRES_*` environment variables before trying to override the configuration values. - -## 6.48.0 - -Released: 2024-02-21 - -### Added - -* Added `Orchestra\Testbench\Foundation\Env::has()` method. -* Added `Orchestra\Testbench\once()` function. - -## 6.47.1 - -Released: 2024-01-22 - -### Changes - -* Support nested configuration files. - -### Fixes - -* Fixes issue with Livewire testing where calling `$router->getRoutes()->refreshActionLookups()` caused tests to fail. - -## 6.47.0 - -Released: 2024-01-19 - -### Added - -* Added `Orchestra\Testbench\Attributes\WithImmutableDates` attribute to force `Illuminate\Support\Date` to use `Carbon\CarbonImmutable`. -* Added following helper functions: - - `Orchestra\Testbench\default_skeleton_path` - - `Orchestra\Testbench\refresh_router_lookups` - -## 6.46.0 - -Released: 2024-01-10 - -### Added - -* Added `Orchestra\Testbench\Features\TestingFeature` as replacement to `HandlesTestingFeature` trait. - -### Deprecated - -* Deprecate `Orchestra\Testbench\Concerns\HandlesTestingFeature` trait. - -## 6.45.0 - -Released: 2023-12-19 - -### Added - -* Added `Orchestra\Testbench\Attributes\RequiresEnv` attribute to force an environment variables to be required for the test. -* Added `Orchestra\Testbench\Attributes\WithConfig` attribute add a configuration value for the test. -* Added `Orchestra\Testbench\Attributes\WithEnv` attribute add an environment variable value for the test. -* Added `set()` and `forget()` methods to `Orchestra\Testbench\Foundation\Env`. - -## 6.44.0 - -Released: 2023-12-06 - -### Added - -* Supports Workbench `discovers.components` configuration. - -### Changes - -* Sync `view.paths` configuration when Workbench discover views. - -## 6.43.0 - -Released: 2023-12-04 - -### Added - -* Added `Orchestra\Testbench\Attributes\ResetRefreshDatabaseState` attribute to force refreshing database before executing the test. -* Added `Orchestra\Testbench\Foundation\Bootstrap\SyncDatabaseEnvironmentVariables` bootstrap class and allow database collation to be configurable via environment variables using `MYSQL_COLLATION`, `POSTGRES_COLLATION` and `MSSQL_COLLATION`. - -### Changes - -* Refactor handling attributes: - - Add ability to handle actions directly from the attribute. - - Add ability to set `defer` when using `Orchestra\Testbench\Attributes\DefineDatabase`. - -### Deprecated - -* Deprecate `Orchestra\Testbench\Concerns\Database\HandlesConnections` trait. - -## 6.42.1 - -Released: 2023-11-21 - -### Added - -* Added `encode()` method to `Orchestra\Testbench\Foundation\Env` class. - -### Fixes - -* Fixes registering discovery paths when the path doesn't exist. - -## 6.42.0 - -Released: 2023-11-10 - -### Added - -* Added new PHPUnit Attribute to run the default `laravel`, `cache`, `notifications`, `queue` and `session` database migrations using `Orchestra\Testbench\Attributes\WithMigration`. -* Added `Orchestra\Testbench\Bootstrap\ConfigureRay` class. -* Added `Orchestra\Testbench\defined_environment_variables()` function. -* Added `Orchestra\Testbench\laravel_migration_path()` function. -* Added `Orchestra\Testbench\remote()` function. - -### Changes - -* Mark the following classes as `@api`: - - `Orchestra\Testbench\Foundation\Application` - - `Orchestra\Testbench\Foundation\Config` - - `Orchestra\Testbench\Foundation\Env` -* Cache results from `Orchestra\Testbench\PHPUnit\AttributeParser`. - -## 6.41.1 - -Released: 2023-10-24 - -### Fixes - -* Fixes compatibility with Testbench Dusk when handling PHPUnit Attributes. - -## 6.41.0 - -Released: 2023-10-24 - -### Added - -* Added `Orchestra\Testbench\Workbench\Workbench` to handle integrations with Workbench. -* Added `Orchestra\Testbench\Foundation\Config::getWorkbenchDiscoversAttributes()` method. -* Added `Orchestra\Testbench\Concerns\Database\InteractsWithSqliteDatabaseFile` trait. -* Added support for PHPUnit Attributes as replacements to Annotations: - - `@define-env` and `@environment-setup` will be replaced with `Orchestra\Testbench\Attributes\DefineEnvironment`. - - `@define-db` will be replaced with `Orchestra\Testbench\Attributes\DefineDatabase`. - - `@define-route` will be replaced with `Orchestra\Testbench\Attributes\DefineRoute`. - -### Fixes - -* Fixes generating path using `Orchestra\Testbench\package_path()` and `Orchestra\Testbench\workbench_path()`. - -### Deprecated - -* Deprecate `Orchestra\Testbench\Concerns\WithFactories`. - -### Removed - -* Remove `Orchestra\Testbench\Foundation\Bootstrap\StartWorkbench`, use `Orchestra\Testbench\Workbench\Workbench::start()` instead. - -## 6.40.0 - -Released: 2023-10-09 - -### Changes - -* Code refactors. -* Mark `Orchestra\Testbench\Bootstrap\LoadEnvironmentVariables` class as `@internal`. - -## 6.39.1 - -Released: 2023-09-25 - -### Changes - -* Add `Orchestra\Testbench\Foundation\Config::cacheFromYaml()` to cache `testbench.yaml` for testing environment. -* Code refactors. - -## 6.39.0 - -Released: 2023-09-25 - -### Added - -* Added `cachedConfigurationForWorkbench()` to `Orchestra\Testbench\Concern\InteractsWithWorkbench` trait. -* Add the ability to read `TESTBENCH_WORKING_PATH` from environment variables for Testbench Dusk usage. -* Supports Workbench `discovers` configuration. -* Add the ability to properly forward Environment Variables. -* Add `usesSqliteInMemoryDatabaseConnection` to `Orchestra\Testbench\Concerns\HandlesDatabases` trait. - -## 6.38.2 - -Released: 2023-09-21 - -### Changes - -* Allow deferring Laravel Migrations when TestCase also uses `Illuminate\Foundation\Testing\RefreshDatabase`. - -## 6.38.1 - -Released: 2023-09-09 - -### Changes - -* Prevents loading Laravel Migrations using `Orchestra\Testbench\Concerns\WithLaravelMigrations` when TestCase class also uses `Orchestra\Testbench\Concerns\WithWorkbench` with `workbench.install=true` configuration. - -## 6.38.0 - -Released: 2023-08-29 - -### Added - -* Add ability to automatically run default Laravel migrations using `Orchestra\Testbench\Concerns\WithLaravelMigrations`. - -## 6.37.1 - -Released: 2023-08-22 - -### Fixes - -* Fixes missing import for `Orchestra\Testbench\after_resolving` helper function. - -## 6.37.0 - -Released: 2023-08-22 - -### Added - -* Added `Orchestra\Testbench\after_resolving` helper function. - -### Changes - -* Allow using `$model` property override when extending `Orchestra\Testbench\Factories\UserFactory`. - -## 6.36.0 - -Released: 2023-08-19 - -### Added - -* Added new `workbench.welcome` configuration option. - -### Changes - -* Allow `testbench.yaml` configuration fallback similar to `.env`. - -## 6.35.1 - -Released: 2023-08-17 - -### Fixes - -* Fixes configuration leak when running some TestCase without `Orchestra\Testbench\Concerns\WithWorkbench`. - -## 6.35.0 - -Released: 2023-08-15 - -### Added - -* Added `Orchestra\Testbench\package_path()` function. - -### Changes - -* Rename `Orchestra\Testbench\Workbench\Bootstrap\StartWorkbench` to `Orchestra\Testbench\Foundation\Bootstrap\StartWorkbench`. - -### Fixes - -* Fixes `boolean` usage with `Orchestra\Testbench\parse_environment_variables()` function. - -### Remove - -* Remove experimental Workbench support. - -## 6.34.0 - -Released: 2023-08-12 - -### Changes - -* Change `HandlesRoutes` loading sequence to match common Laravel bootstrap steps. -* Refactor `HandlesAnnotations` and `InteractsWithPHPUnit` traits. -* Workbench integration improvements. - -## 6.33.3 - -Released: 2023-08-11 - -### Changes - -* Update `workbench` configuration schema. - -### Fixes - -* Fixes `Illuminate\Foundation\Application::runningUnitTests()` detection. - -## 6.33.2 - -Released: 2023-08-10 - -### Fixes - -* Fixes `app()->environment()` detection when creating application `Orchestra\Testbench\Concerns\CreatesApplication` outside of `PHPUnit`. - -## 6.33.1 - -Released: 2023-08-09 - -### Added - -* Add new `Orchestra\Testbench\Concerns\InteractsWithPHPUnit` to handle `CreatesApplication` within PHPUnit. - -### Fixes - -* Fixes `workbench.start` path when accessing the `/` route return 404. -* Only Configure `TESTBENCH_APP_BASE_PATH` environment variable only when running under tests. - -## 6.33.0 - -Released: 2023-08-08 - -### Added - -* Added new Workbench support (experimental feature). - - Register routes under `/_workbench` prefix. - - Automatically run configured seeds when executing `migrate:fresh` and `migrate:refresh` - - Bind `Orchestra\Testbench\Contracts\Config` to IoC Container and introduce the new `Orchestra\Testbench\workbench` and `Orchestra\Testbench\workbench_path` helper function. -* Add PHPStan analysis. -* Add new `Orchestra\Testbench\Concerns\WithWorkbench` to automatically loads configuration from `testbench.yaml` when running tests. - -## 6.32.0 - -Released: 2023-06-13 - -### Added - -* `Orchestra\Testbench\Bootstrap\LoadEnvironmentVariables` to manage loading `.env` file during tests, backport from Testbench Core 8 releases. - -### Changes - -* Automate registering `tearDownInteractsWithPublishedFiles()` from `setUpInteractsWithPublishedFiles()` method. - -## 6.31.2 - -Released: 2023-04-11 - -### Changes - -* Add `setUpTheTestEnvironmentTraitToBeIgnored()` method to determine `setup` and `teardown` with imported traits that should be used on a given trait. - -## 6.31.1 - -Released: 2023-04-02 - -### Fixes - -* Fixes `Orchestra\Testbench\Foundation\Config::addProviders()` usage. -* Fixes `Orchestra\Testbench\transform_relative_path()` logic. - -## 6.31.0 - -Released: 2023-04-01 - -### Added - -* Added `Orchestra\Testbench\Foundation\Bootstrap\LoadMigrationsFromArray` class to handle loading migrations from `testbench.yaml`. - - You can now disable loading default migrations using either `migrations: false` in `testbench.yaml` or adding `TESTBENCH_WITHOUT_DEFAULT_MIGRATIONS=(true)` environment variable. -* Added additional configuration options to `testbench.yaml`: - - `migrations: ` - - `bootstrappers: ` -* Added `Orchestra\Testbench\parse_environment_variables()` function. -* Added `Orchestra\Testbench\transform_relative_path()` function. - -### Changes - -* `env` configuration from `testbench.yaml` with have higher priority than `default_environment_variables()`. -* Disable `Dotenv\Repository\Adapter\PutenvAdapter` when generating environment variable on the fly using `Orchestra\Testbench\Foundation\Application`. - -### Fixes - -* Fixes console output when an exception is thrown before application can be bootstrapped. -* Fixes some configuration value leaks between tests due to the way it set environment values including `APP_KEY`, `APP_DEBUG` etc. - -## 6.30.0 - -Released: 2023-03-27 - -### Added - -* Added `Orchestra\Testbench\Foundation\Config` to read Yaml file from `testbench.yaml`. -* Added `Orchestra\Testbench\Foundation\Application::createVendorSymlink()` method. - - The feature uses `Orchestra\Testbench\Foundation\Bootstrap\CreateVendorSymlink`. -* Added `resolveApplicationEnvironmentVariables()` method. -* Add supports for `setup` and `teardown` with imported traits. - -### Changes - -* Bump minimum laravel/framework to `8.83.26`. -* Improves PHPUnit memory leaks. -* Refactor the following classes to match Testbench 7: - - `Orchestra\Testbench\Concerns\HandlesRoutes` - - `Orchestra\Testbench\Console\Commander` - - `Orchestra\Testbench\Foundation\Application` - -------------- - -> **Warning**: Breaking change is possible if your package contains any traits with `setup` or `teardown` -> -> This version now will automatically run those methods during application bootstrap and terminate to be consistent with Laravel Framework implementations. - -## 6.29.1 - -Released: 2022-10-11 - -### Fixes - -* Remove `bootstrap/cache/packages.php` on vendor symlink event. - -## 6.29.0 - -Released: 2022-08-24 - -### Added - -* Added `loadLaravelMigrationsWithoutRollback()` and `runLaravelMigrationsWithoutRollback()` helpers. - -## 6.28.1 - -Released: 2022-02-08 - -### Changes - -* Update skeleton to match v8.6.11. - -## 6.28.0 - -Released: 2022-01-13 - -### Changes - -* Allow package discoveries by adding `$enablesPacakgeDiscoveries = true` property. -* Allow to run `defineCacheRoute()` before application is ready. -* Support defining custom `$basePath` when using `Orchestra\Testbench\container` function. - -## 6.27.4 - -Released: 2021-12-23 - -### Changes - -* Update skeleton to match v8.6.10. - -## 6.27.3 - -Released: 2021-12-04 - -### Changes - -* Improves docblock. - -## 6.27.2 - -Released: 2021-12-02 - -### Changes - -* Update skeleton to match v8.6.8. - -## 6.27.1 - -Released: 2021-11-17 - -### Changes - -* Update skeleton to match v8.6.7. - -## 6.27.0 - -Released: 2021-11-10 - -### Added - -* Add ability to define database migrations using `TestCase::defineDatabaseMigrationsAfterDatabaseRefreshed()` method, the method will only be executed via `Illuminate\Database\Events\DatabaseRefreshed` event. -* Add ability to destroy database migrations using `TestCase::destroyDatabaseMigrations()`. - -## 6.26.0 - -Released: 2021-10-21 - -### Added - -* Added draft support for PHP 8.1. -* Added `Orchestra\Testbench\container()` function to easily create an application instance. - -### Changes - -* Update skeleton to match v8.6.4. -* Improves docblock. - -## 6.25.2 - -Released: 2021-09-18 - -### Changes - -* Ability to use `App\Http\Kernel` and `App\Console\Kernel` via Commander if the class exists. - -## 6.25.1 - -Released: 2021-09-11 - -### Fixes - -* Fixes missing `Orchestra\Testbench\Contracts\TestCase` to `artisan()` helper function. - -## 6.25.0 - -Released: 2021-09-08 - -### Added - -* Add ability to define database seeder using `TestCase::defineDatabaseSeeders()` method. - -### Changes - -* Update skeleton to match v8.6.2. - -## 6.24.1 - -Released: 2021-08-25 - -### Changes - -* Update skeleton to match v8.6.1. - -## 6.24.0 - -Released: 2021-08-12 - -### Changes - -* Bump minimum `laravel/framework` to `8.54`. -* Update skeleton to match v8.5.24. - -## 6.23.1 - -Released: 2021-07-14 - -### Changes - -* Update skeleton to match v8.5.22. - -## 6.23.0 - -Released: 2021-06-16 - -### Changes - -* Improves generating cached routes during testing. -* Allows to loads `.env` when using `Orchestra\Testbench\Foundation\Application`. -* Update skeleton. - -## 6.22.0 - -Released: 2021-05-25 - -### Added - -* Added `Orchestra\Testbench\Foundation\Application` to allow creating remote application using Testbench. -* Added static public method `Orchestra\Testbench\Concerns\CreatesApplication::applicationBasePath()` to replace `getBasePath()`. - -### Changes - -* Update skeleton. - -## 6.21.3 - -Released: 2021-05-18 - -### Changes - -* Update skeleton to match v8.5.18. - -### Fixes - -* Fixes missing `PHPUnit\Util\Test::parseTestMethodAnnotations()` on PHPUnit 10. - -## 6.21.2 - -Released: 2021-05-12 - -### Changes - -* Check whether skeleton `vendor` is an actual directory before trying to symlink with base directory `vendor`. - -## 6.21.1 - -Released: 2021-04-21 - -### Changes - -* Update skeleton to match v8.5.16. - -## 6.21.0 - -Released: 2021-04-06 - -### Added - -* Added capability to fetch package discovery from root project. -* Added database-specific environment variables based on ChipperCI. - -### Changes - -* Allow configuration to be loaded from `Application::basePath()` instead of hardcoded value. - -## 6.20.0 - -Released: 2021-03-31 - -### Added - -* Added ability to run multiple database by adding database specific environment variable. E.g: `MYSQL_HOST`, `POSTGRES_HOST` and `MSSQL_HOST` instead of just `DB_HOST`. - -### Changes - -* Use `getcwd()` instead of relative path to setup `TESTBENCH_WORKING_PATH` constant when executing it via `bootstrap/app.php`. -* Accept `APP_BASE_PATH` environment variable to configure `getBasePath()`. - -## 6.19.1 - -Released: 2021-03-24 - -### Changes - -* Update Laravel skeleton. - - Update `validation` language file. - -## 6.19.0 - -Released: 2021-03-21 - -### Added - -* Added `TESTBENCH_WORKING_DIRECTORY` constant. - -### Removed - -* Remove package discovery for `Orchestra\Testbench\Foundation\TestbenchServiceProvider`, the service provider will only be registered from CLI Commander. - -## 6.18.0 - -Released: 2021-03-16 - -### Added - -* Added support for PHPUnit 10. - -### Changes - -* Update Laravel skeleton. - - Add `Date` aliases. - - Update `logging` configuration. - - Update `validation` language file. - -## 6.17.1 - -Released: 2021-03-10 - -### Changes - -* Update Laravel skeleton. - - Update `queue` configuration. - - Update `validation` language file. - -## 6.17.0 - -Released: 2021-03-08 - -### Added - -* Added `Orchestra\Testbench\Foundation\Console\Kernel` and `Orchestra\Testbench\Foundation\Http\Kernel`. - -## 6.16.0 - -Released: 2021-02-21 - -### Changes - -* Flush `Queue::createPayloadUsing()` on `Orchestra\Testbench\TestCase::tearDown()`. - -## 6.15.2 - -Released: 2021-02-13 - -### Fixes - -* Always attempt to delete `laravel/vendor` symlink folder. - -## 6.15.1 - -Released: 2021-02-09 - -### Fixes - -* Fixes tests. - -## 6.15.0 - -Released: 2021-02-09 - -### Added - -* Add `defineWebRoutes()` to automatically define routes under `web` middleware. - -## 6.14.0 - -Released: 2021-02-07 - -### Added - -* Added `$loadEnvironmentVariables` property options to toggle loading `.env` file if available. - -## 6.13.0 - -Released: 2021-01-30 - -### Added - -* Added `dont-discover` configuration to `testbench.yaml`. - -## 6.12.0 - -Released: 2021-01-29 - -### Added - -* Added support for Laravel 8 parallel testing: - - Added `package:test` command. - - Added `Orchestra\Testbench\Foundation\TestbenchServiceProvider` class. - -## 6.11.2 - -Released: 2021-01-21 - -### Fixes - -* Handle exception on `Orchestra\Testbench\Console\Commander`. - -## 6.11.1 - -Released: 2021-01-18 - -### Fixes - -* Fixes tests example. - -## 6.11.0 - -Released: 2021-01-17 - -### Changes - -* Improves support for Package Discovery support on test environment and also `testbench` command. - -## 6.10.0 - -Released: 2021-01-17 - -### Added - -* Added `ignorePackageDiscoveriesFrom()` method to `Orchestra\Testbench\Concerns\CreatesApplication` trait to allow enable package discoveries during tests. -* `Orchestra\Testbench\Console\Commander` will automatically discover packages. - -## 6.9.2 - -Released: 2020-12-30 - -### Changes - -* Update Laravel skeleton. - - Add `caches.stores.database.lock_connection` and `caches.stores.redis.lock_connection` configuration. - -### Fixes - -* Fixes `database/seeders` folder. - -## 6.9.1 - -Released: 2020-12-15 - -### Fixes - -* Hardcode `vlucas/phpdotenv` dependencies to avoid missing `Dotenv\Store\StoreInterface` interface. - -## 6.9.0 - -Released: 2020-12-15 - -### Changes - -* Bump `mockery/mockery` to `v1.3.2` and above. -* Opt to use `method_exists()` to detect support for `parseTestMethodAnnotations()` under `HandlesDatabases` and `HandlesRoutes` trait. -* Update `Orchestra\Testbench\Bootstrap\LoadConfiguration::getConfigurationFiles()` to return `Generator` instead of array. - -## 6.8.0 - -Released: 2020-12-09 - -### Added - -* Added following traits: - - `Orchestra\Testbench\Concerns\HandlesAnnotations`. - - `Orchestra\Testbench\Concerns\HandlesDatabases`. - - `Orchestra\Testbench\Concerns\HandlesRoutes`. -* Added `defineRoutes()` and `defineCacheRoutes()` to group dedicated tests routing. - -## 6.7.0 - -Released: 2020-12-01 - -### Added - -* Added `defineEnvironment()` and `defineDatabaseMigrations()` method to `Orchestra\Testbench\TestCase`. - - `defineEnvironment()` usage is identical to `getEnvironmentSetUp()` but the original function will remain functioning for now. - - Use `defineDatabaseMigrations()` to load any database migrations for the tests. This will allows Testbench to loads it early on the test cycle before to avoid it being clashing usage with `DatabaseTransactions` trait. -* Add support to read environment variable from `.env` on skeleton when it's available when used with `testbench` bin command. - -### Changes - -* Update Laravel skeleton. - - Remove `filesystems.cloud` configuration. - -## 6.6.2 - -Released: 2020-11-25 - -### Changes - -* Update Laravel skeleton. - -## 6.6.1 - -Released: 2020-11-17 - -### Changes - -* Update Laravel app skeleton. - -### Fixes - -* Use `TestCase::getName(false)` when resolving annotations for PHPUnit. - -## 6.6.0 - -Released: 2020-11-07 - -### Added - -* Added `Orchestra\Testbench\Concerns\InteractsWithPublishedFiles` trait. - -### Changes - -* Uses `PHPUnit\Util\Test` to parse annotations instead of relying on deprecated `TestCase::getAnnotations()`. - -## 6.5.0 - -Released: 2020-10-28 - -### Changes - -* Replace `fzaninotto/faker` with `fakerphp/faker`. - -## 6.4.1 - -Released: 2020-10-27 - -### Changes - -* Added support for PHP 8. - -## 6.4.0 - -Released: 2020-10-20 - -### Added - -* Added ability to use custom Laravel path for `testbench` CLI. - -## 6.3.0 - -Released: 2020-10-11 - -### Added - -* Added `drop-sqlite-db` command. - -### Changes - -* Configuration changes: - - Add `LOG_LEVEL` environment configuration. - -## 6.2.0 - -Released: 2020-09-28 - -### Added - -* Add following folders to Laravel skeleton: - - `app/Console` - - `app/Exceptions` - - `app/Http/Controllers` - - `app/Http/Middleware` - - `app/Models` - - `app/Providers` - - `database/seeds` - -## 6.1.1 - -Released: 2020-09-26 - -### Fixes - -* Allows to skipped `env` key from `testbench.yaml`. - -## 6.1.0 - -Released: 2020-09-24 - -### Added - -Added experimental support for running artisan commands outside of Laravel. e.g: - - ./vendor/bin/testbench migrate - -This would allows you to setup the testing environment before running `phpunit` instead of executing everything from within `TestCase::setUp()`. - -### Changes - -* `Orchestra\Testbench\TestCase` now uses `Illuminate\Foundation\Testing\Concerns\InteractsWithTime`. - -## 6.0.1 - -Released: 2020-09-09 - -### Changes - -* Throw explicit exception when using `withFactories()` without `laravel/legacy-factories`. - -## 6.0.0 - -Released: 2020-09-08 - -### Added - -* Added `Orchestra\Testbench\Factories\UserFactory` to handle `Illuminate\Foundation\Auth\User` model. -* Automatically autoloads `Illuminate\Database\Eloquent\LegacyFactoryServiceProvider` if the service provider exists. - -### Changes - -* Update support for Laravel Framework v8. -* Increase minimum PHP version to 7.3 and above (tested with 7.3 and 7.4). -* Configuration changes: - - Changed `auth.providers.users.model` to `Illuminate\Foundation\Auth\User`. - - Changed `queue.failed.driver` to `database-uuid`. diff --git a/CHANGELOG-7.x.md b/CHANGELOG-7.x.md deleted file mode 100644 index 1e366604f..000000000 --- a/CHANGELOG-7.x.md +++ /dev/null @@ -1,1391 +0,0 @@ -# Changes for 7.x - -This changelog references the relevant changes (bug and security fixes) done to `orchestra/testbench-core`. - -## 7.63.0 - -Released: 2026-09-23 - -### Added - -* Add `Orchestra\Testbench\Foundation\Env::flushState()` method and revert class deprecation. -* Add `Orchestra\Testbench\php_version_compare()` function. -* Allow `--pretend` options on relevant commands. - -## 7.62.1 - -Released: 2026-04-24 - -### Fixes - -* Fix `Orchestra\Testbench\Attributes\WithConfig` shouldn't defer setting up framework configuration. - -## 7.62.0 - -Released: 2026-04-23 - -### Changes - -* Loads `Orchestra\Testbench\Attributes\WithConfig` after application has been booted to allow merging configuration via Service Provider by default. Use `defer: false` parameter to disable this. - -## 7.61.0 - -Released: 2026-03-24 - -### Changes - -* Add `TESTBENCH_USER_MODEL` environment variable when running `serve` command. -* Utilise `Orchestra\Testbench\Foundation\Console\TerminatingConsole` when running `serve`. - -## 7.60.1 - -Released: 2026-03-18 - -### Fixes - -* Fix `--parallel` compatibility with `WithFixtures` trait. - -## 7.60.0 - -Released: 2026-03-16 - -### Changes - -* Run seeder when `testbench.yaml` configured with `seeders: true`. - -### Fixes - -* Fix `#[UsesVendor]` attribute fails due to unbooted application, causing `BindingResolutionException` to be thrown. - -## 7.59.0 - -Released: 2026-01-12 - -### Added - -* Add `Orchestra\Testbench\package_version_compare()` function. -* Add `Orchestra\Testbench\Concerns\WithFixtures` to automatically loads fixtures file for test. - -### Changes - -* Use `Orchestra\Sidekick\package_path()` for better root package path detection. - -## 7.58.0 - -Released: 2025-10-14 - -### Changes - -* Support disabling Laravel default service providers. -* Flush `Illuminate\Support\Str` states via during test teardown. - -## 7.57.0 - -Released: 2025-06-08 - -### Changes - -* `Orchestra\Foundation\Env` now extends `Orchestra\Sidekick\Env`. - -## 7.56.0 - -Released: 2025-05-12 - -### Changes - -* `Orchestra\Testbench\PHPUnit\TestCase` now implements `Orchestra\Testbench\Concerns\InteractsWithMockery`. - -## 7.55.0 - -Released: 2025-04-06 - -### Changes - -* Refactor `Orchestra\Testbench\remote()` function to use `Orchestra\Testbench\Foundation\Process\RemoteCommand`. -* Rename `TESTBENCH_ENVIRONMENT_FILE_USING` to `TESTBENCH_ENVIRONMENT_FILE_USING` (internal environment variable). -* Add support for `workbench/bootstrap/providers.php`. - -## 7.54.0 - -Released: 2025-03-06 - -### Added - -* Added `resolvePhpUnitTestClassName()` and `resolvePhpUnitTestMethodName()` to `Orchestra\Testbench\Concerns\InteractsWithPHPUnit` trait. - -### Changes - -* Allows `usesTestingFeature()` to register attribute directly on test method. -* Improves `vendor` detection on the default skeleton. -* Utilise `Orchestra\Sidekick\is_symlink()` function instead of `is_link()` to improves support on Windows. -* Use `::class` instead of `get_class()`. -* Delete `vendor` symlink via `package:purge-skeleton` command. - -### Fixes - -* Fix static variable via `Orchestra\Testbench\Attributes\UsesVendor::beforeEach()` method. - -## 7.53.0 - -Released: 2025-02-19 - -### Added - -* Use `orchestra/sidekick`. -* Implements `Illuminate\Foundation\Testing\Concerns\InteractsWithViews`. - -### Deprecated - -* Re-add deprecated `getBasePath()` method pending removal in Testbench 10.0. - -## 7.52.0 - -Released: 2025-02-15 - -### Added - -* Add `Orchestra\Testbench\Concerns\CreatesApplication::resolveApplicationFacades()` method. -* Add `Orchestra\Testbench\Concerns\CreatesApplication::getApplicationBasePath()` method as replacement to `Orchestra\Testbench\Concerns\CreatesApplication::getBasePath()`. -* Add `Orchestra\Testbench\Foundation\Application::deleteVendorSymlink()` method. - -### Changes - -* Dynamically resolve workbench namespace for `discover.factories`. -* Clear `vendor` symlink when terminating Testbench CLI. - -## 7.51.3 - -Released: 2025-02-06 - -### Changes - -* Improves `Orchestra\Testbench\Foundation\Application::flushState()` to be forward compatible with Testbench 9. - -## 7.51.2 - -Released: 2025-02-02 - -### Changes - -* Flush `Illuminate\Database\Schema\Builder` static properties state. - -## 7.51.1 - -Released: 2025-01-07 - -### Fixes - -* Fix `Orchestra\Testbench\Workbench\Workbench::applicationUserModel()` to detect `App\Models\User`. -* Fix authentication route registrations from being loaded with `auth: false` configuration when executed Testbench CLI. - -## 7.51.0 - -Released: 2024-12-24 - -### Added - -* Add ability to symlink directory in from skeleton to package. - -### Changes - -* Use `realpath()` on `vendor:publish` output. - -## 7.50.1 - -Released: 2024-12-19 - -### Changes - -* Add `Orchestra\Testbench\Workbench\Workbench::flushCachedClassAndNamespaces()` to flush cached namespaces and classes during installation. - -## 7.50.0 - -Released: 2024-12-15 - -### Added - -* Added `Orchestra\Testbench\transform_realpath_to_relative()` function. -* Override Laravel's `vendor:publish` command. - -### Changes - -* Add `$force` parameter to `Orchestra\Testbench\Workbench\Workbench::detectNamespace()` method. - -## 7.49.1 - -Released: 2024-12-14 - -### Fixes - -* Fixes database seeding using `Orchestra\Testbench\Concerns\WithWorkbench` and `Illuminate\Foundation\Testing\RefreshDatabase`. - -## 7.49.0 - -Released: 2024-12-01 - -### Added - -* Added ability to detect Workbench namespace via `Orchestra\Testbench\Workbench\Workbench::detectNamespace()` method. -* Added ability to detect the default user model via `Orchestra\Testbench\Workbench\Workbench::applicationUserModel()` method. -* Added support for authentication routes within Workbench by configurating `workbench.auth` config to `true`. -* Added new `package:sync-skeleton` command. - -### Changes - -* Testbench Dusk integration improvements: - - Refactor `Orchestra\Testbench\Bootstrap\LoadConfiguration` and `Orchestra\Testbench\Bootstrap\LoadConfigurationWithWorkbench` to allow being extended by Testbench Dusk. - - Refactor `Orchestra\Testbench\Console\Commander`. -* Add multiple environment variables to Laravel 9 skeleton's configuration files based on changes made for Laravel 11. -* Add `$tty` parameter to `Orchestra\Testbench\remote()` function. -* Refactor `Orchestra\Testbench\Foundation\Bootstrap\CreateVendorSymlink` class and mark it as `@api`. -* Add `$backupExistingFile` and `$resetOnTerminating` parameter to following methods in `Orchestra\Testbench\Foundation\Console\Concerns\CopyTestbenchFiles` trait: - - `copyTestbenchConfigurationFile()` - - `copyTestbenchDotEnvFile()` - -### Deprecated - -* Deprecate `Orchestra\Testbench\Foundation\Console\Concerns\HandleTerminatingConsole` trait, use `Orchestra\Testbench\Foundation\Console\TerminatingConsole` class instead. - -## 7.48.0 - -Released: 2024-11-18 - -### Added - -* Added `Orchestra\Testbench\Foundation\Bootstrap\DeleteVendorSymlink` class. -* Added `Orchestra\Testbench\Concerns\InteractsWithMockery` trait. -* Added `--database` option to `package:create-sqlite-db` command. -* Added `--database` and `--all` options to `package:drop-sqlite-db` command. -* Added `Orchestra\Testbench\php_binary()` function. -* Added support for PHP 8.3. - -### Changes - -* Change `resolveApplicationResolvingCallback()` method visibility from `private` to `protected`. - -## 7.47.4 - -Released: 2024-10-25 - -### Changes - -* Ensure database directory exists when running `package:create-sqlite-db`. -* Allow configuring `PHP_CLI_SERVER_WORKERS` via Composer Script. - -## 7.47.3 - -Released: 2024-10-24 - -### Changes - -* Improves `CTRL+C` and `CTRL+BREAK` supports on Windows without `pcntl` extension. -* `Orchestra\Testbench\Foundation\Console\Actions\GeneratesFile` should be able to handle `$from` and `$to` when given as `false` or `null`. - -## 7.47.2 - -Released: 2024-10-06 - -### Fixes - -* Prevent seeder from being executed when `shouldSeed()` exists and return `false`. - -## 7.47.1 - -Released: 2024-10-05 - -### Changes - -* Listen to `SIGHUP`, `SIGUSR1`, and `SIGUSR2` signals via Testbench CLI. - -### Fixes - -* Fixes Testbench CLI signals via `serve` command to reset published `.env` and `testbench.yaml`. - -## 7.47.0 - -Released: 2024-09-23 - -### Added - -* Added `markTestSkippedWhen()` and `markTestSkippedUnless()` assertion helper to conditionally handle `markTestSkipped()`. -* Added `Orchestra\Testbench\default_migration_path()` helper function. -* Added `Orchestra\Testbench\laravel_vendor_exists()` helper function. -* Allows TestCase to inherit Attributes defined on parent TestCase by @BlackLanzer in #233. - -### Changes - -* Allow Testbench to delete `vendor` symlink directory if it was created while running tests. - -### Fixes - -* Fixes `view.paths` configuration not being updated to include `workbench/resources/views` due to IoC booting sequence. - -### Deprecated - -* Deprecated `Orchestra\Testbench\laravel_migration_path()`, use `default_migration_path()` instead. - - - -## 7.46.0 - -Released: 2024-08-26 - -### Added - -* Added `artisan` binary to Laravel skeleton. -* Added `Orchestra\Testbench\join_paths()` function. -* Added `Orchestra\Testbench\Attributes\UsesVendor` attribute class. -* Added `defineStashRoutes()` method to register adhoc route for test. - -### Changes - -* Change `afterApplicationCreated()`, `afterApplicationRefreshed()`, and `beforeApplicationDestroyed()` methods visibility from `protected` to `public`. -* Improvements to `Orchestra\Testbench\default_skeleton_path()`, `Orchestra\Testbench\package_path()`, and `Orchestra\Testbench\workbench_path()` usage based on new `Orchestra\Testbench\join_paths()` function. - -## 7.45.0 - -Released: 2024-08-14 - -### Changes - -* Update `Orchestra\Testbench\Foundation\Console\Actions\GeneratesFile` to remove `.gitkeep` file when directory contain one or more files. -* Code Improvements. - -## 7.44.1 - -Released: 2024-07-19 - -### Fixes - -* Fixes `InteractsWithPublishedFiles` should only flush published files within `database/migrations` directory. - -## 7.44.0 - -Released: 2024-07-13 - -### Added - -* Added new attributes: - - `Orchestra\Testbench\Attributes\ResolvesLaravel` - - `Orchestra\Testbench\Attributes\UsesFrameworkConfiguration` -* Allows to discover `factories` using Workbench to map `Workbench\App\Models` to `Workbench\Database\Factories` classes. -* Allows to auto discover console command classes from `workbench/app/Console/Commands`. - -### Changes - -* Implements `JsonSerializable` to `Orchestra\Testbench\Foundation\UndefinedValue`. -* Update skeleton to use `workbench` as default environment value. -* Allow `Orchestra\Testbench\Attributes\Define` and `Orchestra\Testbench\Attributes\DefineEnvironment` to be used on the class level by [@danjohnson95](https://github.com/danjohnson95) - -### Fixes - -* Ensure `usesTestingFeature()` attribute registration is loaded before class attributes instead of method attributes. - -## 7.43.3 - -Released: 2024-06-04 - -### Fixes - -* Fixes `Orchestra\Testbench\Workench\Workbench::applicationExceptionHandler()` usage to detect `Workbench\App\Exceptions\Handler` class. - -## 7.43.2 - -Released: 2024-06-01 - -### Fixes - -* Fixes `Orchestra\Testbench\Attributes\RequiresLaravel` attribute usage. - -## 7.43.1 - -Released: 2024-05-23 - -### Changes - -* Utilise `Orchestra\Testbench\package_path()` function instead of `TESTBENCH_WORKING_PATH` constant. - -## 7.43.0 - -Released: 2024-05-21 - -### Changes - -* Uses `TESTBENCH_WORKING_PATH` from environment variable before fallback to `getcwd()`. -* PHPStan Improvements. - -## 7.42.7 - -Released: 2024-04-21 - -### Fixes - -* Backport fixes to routing registration using macro with Workbench. - -## 7.42.6 - -Released: 2024-04-13 - -### Changes - -* Flush Static Improvements. -* Allows `Orchestra\Testbench\remote` to accept `$env` with either `array` or `string`. -* Includes `TESTBENCH_PACKAGE_REMOTE=true` when running command using `Orchestra\Testbench\remote`. - -## 7.42.5 - -Released: 2024-03-25 - -### Fixes - -* Fixes `RefreshDatabase` to be executed on `tearDown()` only limited when ad-hoc migrations was added during test. - -## 7.42.4 - -Released: 2024-03-19 - -### Changes - -* Run `ResetRefreshDatabaseState` via `tearDownTheTestEnvironmentUsingTestCase()` method. - -### Fixes - -* Fixes `beforeApplicationDestroyed()` usage on `loadLaravelMigrations()` method. - -## 7.42.3 - -Released: 2024-03-19 - -### Fixes - -* Fixes `RefreshDatabase` usage does not reset the database migrations between tests. - -## 7.42.2 - -Released: 2024-03-18 - -### Changes - -* Check against `RefreshDatabaseState::$migrated` and `RefreshDatabaseState::$lazilyRefreshed` before loading migration paths to the instance of `migrator`. - -## 7.42.1 - -Released: 2024-03-15 - -### Fixes - -* Fixes `class_implements(): Class AllowDynamicProperties does not exist and could not be loaded` error on PHP 8.1 and lower. - -## 7.42.0 - -Released: 2024-03-13 - -### Added - -* Added `Orchestra\Testbench\Attributes\RequiresLaravel` attribute. -* Added `Orchestra\Testbench\load_migration_paths()` function. -* Added `usesRefreshDatabaseTestingConcern()` helper method to `Orchestra\Testbench\Concerns\InteractsWithTestCase` trait. - -### Changes - -* Validate `MYSQL_*`, `MSSQL_*`, `SQLITE_*` and `POSTGRES_*` environment variables before trying to override the configuration values. - -## 7.41.1 - -Released: 2024-02-22 - -### Fixes - -* Fixes `Orchestra\Testbench\Attributes\ResetRefreshDatabaseState` attribute declaration to only `Attribute::TARGET_CLASS`. - -## 7.41.0 - -Released: 2024-02-21 - -### Added - -* Added `Orchestra\Testbench\Foundation\Env::has()` method. -* Added `Orchestra\Testbench\once()` function. - -### Changes - -* Allow passing `$command` to `Orchestra\Testbench\remote()` function using `array` instead of just `string`. - -## 7.40.1 - -Released: 2024-01-22 - -### Changes - -* Support nested configuration files. - -### Fixes - -* Fixes issue with Livewire testing where calling `$router->getRoutes()->refreshActionLookups()` caused tests to fail. - -## 7.40.0 - -Released: 2024-01-19 - -### Added - -* Added `Orchestra\Testbench\Attributes\WithImmutableDates` attribute to force `Illuminate\Support\Date` to use `Carbon\CarbonImmutable`. -* Added following helper functions: - - `Orchestra\Testbench\default_skeleton_path` - - `Orchestra\Testbench\refresh_router_lookups` - -## 7.39.1 - -Released: 2024-01-10 - -### Changes - -* Run `route:cache` using `Orchestra\Testbench\remote` function. -* Add `Orchestra\Testbench\Concerns\InteractsWithTestCase` to `setUpTheTestEnvironmentTraitToBeIgnored` method. - -## 7.39.0 - -Released: 2023-12-28 - -### Added - -* Added `Orchestra\Testbench\Features\TestingFeature` as replacement to `HandlesTestingFeature` trait. -* Added support for `LOG_DEPRECATIONS_WHILE_TESTING` (default to `true`) environment variables. -* Add following interfaces for Attribute handling: - - `Orchestra\Testbench\Contracts\Attributes\AfterAll` - - `Orchestra\Testbench\Contracts\Attributes\AfterEach` - - `Orchestra\Testbench\Contracts\Attributes\BeforeAll` - - `Orchestra\Testbench\Contracts\Attributes\BeforeEach` - -### Changes - -* Refactor `Orchestra\Testbench\Concerns\InteractsWithPHPUnit`. - -### Deprecated - -* Deprecate `Orchestra\Testbench\Concerns\HandlesTestingFeature` trait. - -## 7.38.0 - -Released: 2023-12-19 - -### Added - -* Added `Orchestra\Testbench\Attributes\RequiresEnv` attribute to force an environment variables to be required for the test. -* Added `Orchestra\Testbench\Attributes\WithConfig` attribute add a configuration value for the test. -* Added `Orchestra\Testbench\Attributes\WithEnv` attribute add an environment variable value for the test. -* Added `set()` and `forget()` methods to `Orchestra\Testbench\Foundation\Env`. - -## 7.37.1 - -Released: 2023-12-06 - -### Fixes - -* Testbench CLI should handle `SIGTERM` and `SIGQUIT` signal. - -## 7.37.0 - -Released: 2023-12-06 - -### Added - -* Supports Workbench `discovers.components` configuration. - -### Changes - -* Sync `view.paths` configuration when Workbench discover views. - -## 7.36.0 - -Released: 2023-12-04 - -### Added - -* Added `Orchestra\Testbench\Attributes\ResetRefreshDatabaseState` attribute to force refreshing database before executing the test. -* Added `Orchestra\Testbench\Foundation\Bootstrap\SyncDatabaseEnvironmentVariables` bootstrap class and allow database collation to be configurable via environment variables using `MYSQL_COLLATION`, `POSTGRES_COLLATION` and `MSSQL_COLLATION`. - -### Changes - -* Refactor handling attributes: - - Add ability to handle actions directly from the attribute. - - Add ability to set `defer` when using `Orchestra\Testbench\Attributes\DefineDatabase`. - -### Deprecated - -* Deprecate `Orchestra\Testbench\Concerns\Database\HandlesConnections` trait. - -## 7.35.2 - -Released: 2023-11-21 - -### Added - -* Added `encode()` method to `Orchestra\Testbench\Foundation\Env` class. - -### Fixes - -* Fixes registering discovery paths when the path doesn't exist. - -## 7.35.1 - -Released: 2023-11-10 - -### Changes - -* Move `$setupHasRun` property to `Orchestra\Testbench\Concerns\ApplicationTestingHooks`. - -## 7.35.0 - -Released: 2023-11-10 - -### Added - -* Added new PHPUnit Attribute to run the default `laravel`, `cache`, `notifications`, `queue` and `session` database migrations using `Orchestra\Testbench\Attributes\WithMigration`. -* Added `Orchestra\Testbench\defined_environment_variables()` function. -* Added `Orchestra\Testbench\laravel_migration_path()` function. -* Added `Orchestra\Testbench\remote()` function. - -### Changes - -* Mark the following classes as `@api`: - - `Orchestra\Testbench\Foundation\Application` - - `Orchestra\Testbench\Foundation\Config` - - `Orchestra\Testbench\Foundation\Env` -* Cache results from `Orchestra\Testbench\PHPUnit\AttributeParser`. - -## 7.34.2 - -Released: 2023-10-30 - -### Added - -* Added `Orchestra\Testbench\Concerns\ApplicationTestingHooks` concern based from `Orchestra\Testbench\Concerns\Testing`. - -## 7.34.1 - -Released: 2023-10-24 - -### Fixes - -* Fixes compatibility with Testbench Dusk when handling PHPUnit Attributes. - -## 7.34.0 - -Released: 2023-10-24 - -### Added - -* Added `Orchestra\Testbench\Workbench\Workbench` to handle integrations with Workbench. -* Added `Orchestra\Testbench\Foundation\Config::getWorkbenchDiscoversAttributes()` method. -* Added `Orchestra\Testbench\Concerns\Database\InteractsWithSqliteDatabaseFile` trait. -* Added following methods to `Orchestra\Testbench\Foundation\Application`: - - `make()` - - `makeFromConfig()` - - `createFromConfig()` -* Added support for PHPUnit Attributes as replacements to Annotations: - - `@define-env` and `@environment-setup` will be replaced with `Orchestra\Testbench\Attributes\DefineEnvironment`. - - `@define-db` will be replaced with `Orchestra\Testbench\Attributes\DefineDatabase`. - - `@define-route` will be replaced with `Orchestra\Testbench\Attributes\DefineRoute`. - -### Fixes - -* Fixes generating path using `Orchestra\Testbench\package_path()` and `Orchestra\Testbench\workbench_path()`. - -### Deprecated - -* Deprecate `Orchestra\Testbench\Concerns\WithFactories`. - -### Removed - -* Remove `Orchestra\Testbench\Foundation\Bootstrap\StartWorkbench`, use `Orchestra\Testbench\Workbench\Workbench::start()` or `Orchestra\Testbench\Workbench\Workbench::startWithProviders()` instead. - -## 7.33.0 - -Released: 2023-10-09 - -### Changes - -* Code refactors. -* Mark `Orchestra\Testbench\Bootstrap\LoadEnvironmentVariables` class as `@internal`. - -## 7.32.1 - -Released: 2023-09-25 - -### Changes - -* Add `Orchestra\Testbench\Foundation\Config::cacheFromYaml()` to cache `testbench.yaml` for testing environment. -* Code refactors. - -## 7.32.0 - -Released: 2023-09-25 - -### Added - -* Added `cachedConfigurationForWorkbench()` to `Orchestra\Testbench\Concern\InteractsWithWorkbench` trait. -* Add the ability to read `TESTBENCH_WORKING_PATH` from environment variables for Testbench Dusk usage. -* Supports Workbench `discovers` configuration. -* Add the ability to properly forward Environment Variables. -* Add `usesSqliteInMemoryDatabaseConnection` to `Orchestra\Testbench\Concerns\HandlesDatabases` trait. - -## 7.31.3 - -Released: 2023-09-25 - -### Fixes - -* Fixes deferring Laravel Migrations when TestCase uses `Illuminate\Foundation\Testing\RefreshDatabase`. - -## 7.31.2 - -Released: 2023-09-21 - -### Changes - -* Allow deferring Laravel Migrations when TestCase also uses `Illuminate\Foundation\Testing\RefreshDatabase` or `Illuminate\Foundation\Testing\LazilyRefreshDatabase`. - -## 7.31.1 - -Released: 2023-09-19 - -### Fixes - -* Fixes `cleanUpPublishedFiles` to assign path from `base_path()` before using `glob`. - -## 7.31.0 - -Released: 2023-09-19 - -### Added - -* Added methods to `Orchestra\Testbench\Concerns\InteractsWithPublishedFiles` trait: - - `assertMigrationFileExists`. - - `assertMigrationFileNotExists`. - -### Changes - -* Allow passing wildcard filenames to `Orchestra\Testbench\Concerns\InteractsWithPublishedFiles::$files` property. -* Allow using custom directory on `assertMigrationFileContains` and `assertMigrationFileNotContains` from `Orchestra\Testbench\Concerns\InteractsWithPublishedFiles` trait. -* Rename methods in `Orchestra\Testbench\Concerns\InteractsWithPublishedFiles` trait: - - `cleanUpFiles` to `cleanUpPublishedFiles`. - - `cleanUpMigrationFiles` to `cleanUpPublishedMigrationFiles`. - - `getMigrationFile` to `findFirstPublishedMigrationFile`. - -## 7.30.1 - -Released: 2023-09-09 - -### Changes - -* Prevents loading Laravel Migrations using `Orchestra\Testbench\Concerns\WithLaravelMigrations` when TestCase class also uses `Orchestra\Testbench\Concerns\WithWorkbench` with `workbench.install=true` configuration. - -## 7.30.0 - -Released: 2023-08-29 - -### Added - -* Add ability to automatically run default Laravel migrations using `Orchestra\Testbench\Concerns\WithLaravelMigrations`. -* Add Console Actions classes: - - `Orchestra\Testbench\Foundation\Console\Actions\DeleteFiles` - - `Orchestra\Testbench\Foundation\Console\Actions\DeleteDirectories` - - `Orchestra\Testbench\Foundation\Console\Actions\EnsureDirectoryExists` - - `Orchestra\Testbench\Foundation\Console\Actions\GeneratesFile` - -## 7.29.1 - -Released: 2023-08-22 - -### Changes - -* Allow using `$model` property override when extending `Orchestra\Testbench\Factories\UserFactory`. - -## 7.29.0 - -Released: 2023-08-19 - -### Added - -* Added new `workbench.welcome` configuration option. - -### Changes - -* Allow `testbench.yaml` configuration fallback similar to `.env`. -* Utilise `Illuminate\Support\LazyCollection`. - -## 7.28.4 - -Released: 2023-08-18 - -### Changes - -* Skip loading `Orchestra\Workbench\WorkbenchServiceProvider` when applying `Orchestra\Testbench\Concerns\WithWorkbench`. - -## 7.28.3 - -Released: 2023-08-17 - -### Fixes - -* Fixes configuration leak when running some TestCase without `Orchestra\Testbench\Concerns\WithWorkbench`. - -## 7.28.2 - -Released: 2023-08-17 - -### Changes - -* Disable Composer default timeout when using `serve` command under Composer's script. - -## 7.28.1 - -Released: 2023-08-16 - -### Added - -* Readd deprecated `Orchestra\Testbench\Foundation\Console\DevToolCommand` for integration compatibility. - -## 7.28.0 - -Released: 2023-08-15 - -### Added - -* Added `package:purge-skeleton` command. -* Added `Orchestra\Testbench\Concerns\Database\InteractsWithSqliteDatabaseFile` trait. -* Added `Orchestra\Testbench\package_path()` function. -* Added support for `orchestra/workbench`. - -### Changes - -* Rename `Orchestra\Testbench\Workbench\Bootstrap\StartWorkbench` to `Orchestra\Testbench\Foundation\Bootstrap\StartWorkbench`. - -### Fixes - -* Fixes `serve` command usage. - -## 7.27.1 - -Released: 2023-08-12 - -### Fixes - -* Fixes class namespace. - -## 7.27.0 - -Released: 2023-08-12 - -### Added - -* Added following events: - - `Orchestra\Testbench\Workbench\Events\WorkbenchInstallStarted` - - `Orchestra\Testbench\Workbench\Events\WorkbenchInstallEnded` - -### Changes - -* Change `HandlesRoutes` loading sequence to match common Laravel bootstrap steps. -* Refactor `HandlesAnnotations` and `InteractsWithPHPUnit` traits. -* Workbench integration improvements. - -## 7.26.3 - -Released: 2023-08-11 - -### Added - -* Added following events: - - `Orchestra\Testbench\Foundation\Events\ServeCommandStarted` - - `Orchestra\Testbench\Foundation\Events\ServeCommandEnded` - -### Changes - -* Update `workbench` configuration schema. - -### Fixes - -* Fixes `Illuminate\Foundation\Application::runningUnitTests()` detection. - -## 7.26.2 - -Released: 2023-08-10 - -### Fixes - -* Fixes `app()->environment()` detection when creating application `Orchestra\Testbench\Concerns\CreatesApplication` outside of `PHPUnit`. -* Fixes error `Undefined array key "autoload-dev"` when executing `workbench:install` command. - -## 7.26.1 - -Released: 2023-08-09 - -### Added - -* Add new `Orchestra\Testbench\Concerns\InteractsWithPHPUnit` to handle `CreatesApplication` within PHPUnit. - -### Fixes - -* Fixes `workbench.start` path when accessing the `/` route return 404. -* Only Configure `TESTBENCH_APP_BASE_PATH` environment variable only when running under tests. - -## 7.26.0 - -Released: 2023-08-08 - -### Added - -* Added new Workbench support (experimental feature). - - Register routes under `/_workbench` prefix. - - Automatically run configured seeds when executing `migrate:fresh` and `migrate:refresh` - - Bind `Orchestra\Testbench\Contracts\Config` to IoC Container and introduce the new `Orchestra\Testbench\workbench` and `Orchestra\Testbench\workbench_path` helper function. - - Add `workbench:install`, `workbench:create-sqlite-db` and `workbench:drop-sqlite-db` commands. -* Add new `Orchestra\Testbench\Concerns\WithWorkbench` to automatically loads configuration from `testbench.yaml` when running tests. - -### Deprecated - -* Deprecated `package:devtool`, `package:create-sqlite-db` and `package:drop-sqlite-db` commands. - -## 7.25.0 - -Released: 2023-06-13 - -### Added - -* `Orchestra\Testbench\Bootstrap\LoadEnvironmentVariables` to manage loading `.env` file during tests, backport from Testbench Core 8 releases. - -### Changes - -* Bump minimum `laravel/framework` to `9.52.9`. -* Automate registering `tearDownInteractsWithPublishedFiles()` from `setUpInteractsWithPublishedFiles()` method. - -## 7.24.3 - -Released: 2023-04-27 - -### Fixes - -* Fixes resetting `testbench.yaml` from backup is only needed if the backup file exists. - -## 7.24.2 - -Released: 2023-04-11 - -### Changes - -* Add `setUpTheTestEnvironmentTraitToBeIgnored()` method to determine `setup` and `teardown` with imported traits that should be used on a given trait. - -## 7.24.1 - -Released: 2023-04-02 - -### Fixes - -* Fixes `Orchestra\Testbench\Foundation\Config::addProviders()` usage. -* Fixes `Orchestra\Testbench\transform_relative_path()` logic. - -## 7.24.0 - -Released: 2023-04-01 - -### Added - -* Added `Orchestra\Testbench\Foundation\Bootstrap\LoadMigrationsFromArray` class to handle loading migrations from `testbench.yaml`. - - You can now disable loading default migrations using either `migrations: false` in `testbench.yaml` or adding `TESTBENCH_WITHOUT_DEFAULT_MIGRATIONS=(true)` environment variable. -* Added additional configuration options to `testbench.yaml`: - - `migrations: ` - - `bootstrappers: ` -* Added `Orchestra\Testbench\parse_environment_variables()` function. -* Added `Orchestra\Testbench\transform_relative_path()` function. - -### Changes - -* `env` configuration from `testbench.yaml` with have higher priority than `default_environment_variables()`. -* Disable `Dotenv\Repository\Adapter\PutenvAdapter` when generating environment variable on the fly using `Orchestra\Testbench\Foundation\Application`. - -### Fixes - -* Fixes console output when an exception is thrown before application can be bootstrapped. -* Fixes some configuration value leaks between tests due to the way it set environment values including `APP_KEY`, `APP_DEBUG` etc. - -## 7.23.0 - -Released: 2023-03-27 - -### Added - -* Add supports for `setup` and `teardown` with imported traits. - -------------- - -> **Warning**: Breaking change is possible if your package contains any traits with `setup` or `teardown` -> -> This version now will automatically run those methods during application bootstrap and terminate to be consistent with Laravel Framework implementations. - -## 7.22.2 - -Released: 2023-03-23 - -### Fixes - -* Avoid database connection from eager loaded via `spatie/laravel-ray`. - -## 7.22.1 - -Released: 2023-02-03 - -### Changes - -* Bump minimum `laravel/framework` to `9.52.4`. - -## 7.22.0 - -Released: 2023-02-08 - -### Changes - -* Improve `package:test --parallel` command usage. -* Use `$app->bootstrapPath()` instead of `$app->basePath('bootstrap')` whenever possible. - -## 7.21.0 - -Released: 2023-02-03 - -### Added - -* Added support for `Illuminate\Foundation\Testing\DatabaseTruncation`. - -### Changes - -* Bump minimum `laravel/framework` to `9.50.2`. - -## 7.20.0 - -Released: 2023-02-01 - -### Changes - -* Improves `package:test` commands. -* Update skeleton to match v9.5.2. - -## 7.19.0 - -Released: 2023-01-10 - -### Added - -* Added `Illuminate\Foundation\Testing\InteractsWithDeprecationHandling` to `Orchestra\Testbench\TestCase`. - -## 7.18.0 - -Released: 2023-01-03 - -### Added - -* Added `Orchestra\Testbench\laravel_version_compare` function as alias to `version_compare` specifically for Laravel Framework. -* Added `Orchestra\Testbench\phpunit_version_compare` function as alias to `version_compare` specifically for PHPUnit. -* Added `Orchestra\Testbench\Exceptions\PHPUnitErrorException` class. - -### Changes - -* Mark `Orchestra\Testbench\Bootstrap\ConfigureRay` class as `final`. -* Refactor `Orchestra\Testbench\Concerns\HandlesAnnotations` trait. - -## 7.17.0 - -Released: 2022-12-22 - -### Changes - -* Bump minimum `laravel/framework` to `9.45.0`. -* Update skeleton to match v9.4.1. - -## 7.16.0 - -Released: 2022-12-17 - -### Added - -* Added `resolveApplicationEnvironmentVariables()` method. -* Added `Orchestra\Testbench\Bootstrap\HandleExceptions` bootstrap to allow catching deprecation errors during tests. - - Throws `Orchestra\Testbench\Exceptions\DeprecatedException` exception when deprecation occured. - - Set `logging.deprecations.trace` to `true`. - - Set deprecations log file to `storage/logs/deprecations.log` when `LOG_DEPRECATIONS_CHANNEL=single`. - -### Changes - -* Bump minimum `laravel/framework` to `9.44.0`. - -## 7.15.0 - -Released: 2022-11-30 - -### Changes - -* Bump minimum `laravel/framework` to `9.41.0`. - -## 7.14.1 - -Released: 2022-11-29 - -### Fixes - -* Fixes `serve` command with `no-reload` options. - -## 7.14.0 - -Released: 2022-11-22 - -### Added - -* Added `Orchestra\Testbench\Exceptions\ApplicationNotAvailableException` exception when trying to access `$this->app` outside of booted application. -* Added `tests/CreatesApplication.php` to skeleton. - -### Changes - -* Update skeleton to match v9.3.11. - -## 7.13.0 - -Released: 2022-11-14 - -### Added - -* Added `Orchestra\Testbench\Bootstrap\ConfigureRay` and use it when creating Application. - -## 7.12.1 - -Released: 2022-11-12 - -### Fixes - -* Fixes where the default database connection as `sqlite` causes an exception when the database file isn't available. The loaded application should revert to `testing` database connection for the state. - -## 7.12.0 - -Released: 2022-11-12 - -### Added - -* Added support for `about` artisan command. -* Added `package:devtool` to generate `.env`, `testbench.yaml` and `database.sqlite` file. -* Added `package:create-sqlite-db` and `package:drop-sqlite-db` command. -* Improves support for `serve` command. - -## 7.11.2 - -Released: 2022-11-05 - -### Changes - -* Improves `create-sqlite-db` and `drop-sqlite-db` command. -* Improves `Orchestra\Testbench\Foundation\Application` to allow uses `App\Http\Kernel` and `App\Console\Kernel` when available. - -## 7.11.1 - -Released: 2022-11-05 - -### Changes - -* Improves PHPStan support. - -## 7.11.0 - -Released: 2022-10-19 - -### Added - -* Added `Orchestra\Testbench\Foundation\Application::createVendorSymlink()` method. - - The feature uses `Orchestra\Testbench\Foundation\Bootstrap\CreateVendorSymlink`. - -### Changes - -* Bump minimum `laravel/framework` to `9.36.0` - - Forget View Component's cache and factory between tests. - -## 7.10.2 - -Released: 2022-10-14 - -### Fixes - -* Don't attempt to discover any packages on vendor symlink event. - -## 7.10.1 - -Released: 2022-10-11 - -### Fixes - -* Remove `bootstrap/cache/packages.php` on vendor symlink event. - -## 7.10.0 - -Released: 2022-10-11 - -### Added - -* Added `Orchestra\Testbench\Foundation\Config` to read Yaml file from `testbench.yaml`. - -## 7.9.0 - -Released: 2022-10-05 - -### Added - -* Added draft support for PHP 8.2. - -### Changes - -* Bump minimum `laravel/framework` to `9.34.0`. -* Bump minimum `mockery/mockery` to `1.5.1`. -* Bump minimum `symfony` dependencies to `6.0.9`. - -## 7.8.1 - -Released: 2022-10-03 - -### Fixes - -* Fixes missing `Illuminate\Support\Arr` import on `HandlesTestFailures` trait. - -## 7.8.0 - -Released: 2022-09-28 - -### Changes - -* Bump minimum `laravel/framework` to `9.32.0`. -* Improves PHPUnit memory leaks. - -## 7.7.1 - -Released: 2022-09-28 - -### Changes - -* Update skeleton to match v9.3.8. - -## 7.7.0 - -Released: 2022-08-24 - -### Added - -* Added `loadLaravelMigrationsWithoutRollback()` and `runLaravelMigrationsWithoutRollback()` helpers. - -### Changes - -* Update skeleton to match v9.3.5. - -## 7.6.1 - -Released: 2022-08-10 - -### Changes - -* Update skeleton to match v9.3.3. - -## 7.6.0 - -Released: 2022-06-30 - -### Changes - -* Bump minimum `laravel/framework` to `9.12.0`. -* Update skeleton to match v9.2.0. - -## 7.5.0 - -Released: 2022-05-11 - -### Changes - -* Bump minimum `laravel/framework` to `9.12.0`. -* Update skeleton to match v9.1.8. - -## 7.4.0 - -Released: 2022-04-13 - -### Changes - -* Bump minimum `laravel/framework` to `9.7.0`. -* Add support for `--drop-databases` on `package:test` command. -* Update skeleton to match v9.1.5. - -## 7.3.0 - -Released: 2022-03-30 - -### Changes - -* Bump minimum `laravel/framework` to `9.6.0`. -* Update skeleton to match v9.1.3. - -## 7.2.0 - -Released: 2022-03-20 - -### Changes - -* Bump minimum `laravel/framework` to `9.5.1`. -* Update skeleton to match v9.1.1. - -## 7.1.0 - -Released: 2022-02-22 - -### Changes - -* Bump minimum `laravel/framework` to `9.2`. -* Remove Laravel 9 beta compatibilities codes. - -### Removed - -* Remove `sanctum.php` configuration from skeleton. - -## 7.0.2 - -Released: 2022-02-16 - -### Changes - -* Update skeleton to match v9.0.1. - -## 7.0.1 - -Released: 2022-02-14 - -### Changes - -* Add missing `lang/en.json` skeleton file. - -## 7.0.0 - -Released: 2022-02-08 - -### Added - -* Allows customizing default RateLimiter configuration via `resolveApplicationRateLimiting()` method. -* Added `Orchestra\Testbench\Http\Middleware\PreventRequestsDuringMaintenance` middleware. - -### Changes - -* Update support for Laravel Framework v9. -* Increase minimum PHP version to 8.0 and above (tested with 8.0 and 8.1). -* `$loadEnvironmentVariables` property is now set to `true` by default. -* Following internal classes has been marked as `final`: - - `Orchestra\Testbench\Bootstrap\LoadConfiguration` - - `Orchestra\Testbench\Console\Kernel` - - `Orchestra\Testbench\Http\Kernel` -* Moved `resources/lang` skeleton files to `lang` directory. - -### Removed - -* Remove deprecated `Illuminate\Foundation\Testing\Concerns\MocksApplicationServices` trait. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aa797d119..18dfe6972 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,4 +28,4 @@ $ vendor/bin/phpunit If the test suite passes on your local machine you should be good to go. -When you make a pull request, the tests will automatically be run again by [Travis CI](https://travis-ci.org/) on multiple php versions and hhvm. +When you make a pull request, the tests will automatically be run again by [GitHub Actions](https://github.com/orchestral/testbench-core/actions) on multiple php versions. diff --git a/README.md b/README.md index 4177a2f77..9f4640970 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -Testing Helper for Laravel Development +Testing Framework for Laravel Development ============== -Testbench Component is a simple package that has been designed to help you write tests for your Laravel package. +Testbench Core is a framework designed to help you write tests and interact with your Laravel package. -[![tests](https://github.com/orchestral/testbench-core/actions/workflows/tests.yaml/badge.svg?branch=7.x)](https://github.com/orchestral/testbench-core/actions/workflows/tests.yaml) +[![tests](https://github.com/orchestral/testbench-core/actions/workflows/tests.yaml/badge.svg?branch=9.x)](https://github.com/orchestral/testbench-core/actions/workflows/tests.yaml) [![Latest Stable Version](https://poser.pugx.org/orchestra/testbench-core/v/stable)](https://packagist.org/packages/orchestra/testbench-core) [![Total Downloads](https://poser.pugx.org/orchestra/testbench-core/downloads)](https://packagist.org/packages/orchestra/testbench-core) [![Latest Unstable Version](https://poser.pugx.org/orchestra/testbench-core/v/unstable)](https://packagist.org/packages/orchestra/testbench-core) @@ -13,37 +13,41 @@ Testbench Component is a simple package that has been designed to help you write Laravel | Testbench Core :---------|:---------- - 5.4.x | 3.4.x - 5.5.x | 3.5.x - 5.6.x | 3.6.x - 5.7.x | 3.7.x - 5.8.x | 3.8.x 6.x | 4.x 7.x | 5.x 8.x | 6.x 9.x | 7.x - -## Usage + 10.x | 8.x + 11.x | 9.x + 12.x | 10.x + 13.x | 11.x -**Testbench Core** is being built to enable [Laravel Framework](https://github.com/laravel/framework) to build and run integration tests for the framework itself. For package developers please use any of the following testbench projects: +## Usage ### [Testbench](https://github.com/orchestral/testbench) -It loads Laravel apps and enable you to run artisan commands, migrations, factories and basic routing from within your tests. +It loads a Laravel application within your package repository and enables you to run artisan commands, migrations, factories, and basic routing from within your tests. [![Latest Stable Version](https://poser.pugx.org/orchestra/testbench/v/stable)](https://packagist.org/packages/orchestra/testbench) [![Total Downloads](https://poser.pugx.org/orchestra/testbench/downloads)](https://packagist.org/packages/orchestra/testbench) ### [Testbench BrowserKit](https://github.com/orchestral/testbench-browser-kit) -It extends **Testbench** and allows you to interact with views using CSS selectors (interacting with form, button, link etc) but without JavaScript being loaded. +It extends **Testbench** and allows you to interact with views using CSS selectors (form, button, link, etc.) without loading JavaScript. [![Latest Stable Version](https://poser.pugx.org/orchestra/testbench-browser-kit/v/stable)](https://packagist.org/packages/orchestra/testbench-browser-kit) [![Total Downloads](https://poser.pugx.org/orchestra/testbench-browser-kit/downloads)](https://packagist.org/packages/orchestra/testbench-browser-kit) ### [Testbench Dusk](https://github.com/orchestral/testbench-dusk) -It extends **Testbench** and allows you to interact with views using CSS selectors (interacting with form, button, link etc). By loading the pages using Google Chrome it enable you to interacts with JavaScript powered content. +It extends **Testbench** and allows you to interact with views using CSS selectors (form, button, link, etc). Loading the pages using Google Chrome enables you to interact with JavaScript-powered content. [![Latest Stable Version](https://poser.pugx.org/orchestra/testbench-dusk/v/stable)](https://packagist.org/packages/orchestra/testbench-dusk) [![Total Downloads](https://poser.pugx.org/orchestra/testbench-dusk/downloads)](https://packagist.org/packages/orchestra/testbench-dusk) + +### [Workbench](https://github.com/orchestral/workbench) + +It loads a Laravel application within your package repository and enables you to preview the package directly within the repository. + +[![Latest Stable Version](https://poser.pugx.org/orchestra/workbench/v/stable)](https://packagist.org/packages/orchestra/workbench) +[![Total Downloads](https://poser.pugx.org/orchestra/workbench/downloads)](https://packagist.org/packages/orchestra/workbench) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..bddf7c5db --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +**PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, [SEE BELOW](#reporting-a-vulnerability).** + +## Reporting a Vulnerability + +If you discover a security vulnerability, please report it privately using one of the following channels: + +1. **GitHub Private Vulnerability Reporting** (preferred) — go to the repository's **Security** tab and click **"Report a vulnerability"**. This creates a private advisory visible only to maintainers and provides a structured workflow for triage, fix coordination, and CVE assignment. + +2. **Email** — send the details to Mior Muhammad Zaki at **crynobone@gmail.com**. + +All security vulnerabilities will be promptly addressed. diff --git a/bin/configure-skeleton.php b/bin/configure-skeleton.php index 0a32fe76a..ea15c17f8 100644 --- a/bin/configure-skeleton.php +++ b/bin/configure-skeleton.php @@ -9,7 +9,7 @@ } $input = new ArgvInput; -$version = ($input->hasParameterOption('--dev') || $input->hasParameterOption('--stable') === false) ? '9.x-dev' : '^9.0'; +$version = ($input->hasParameterOption('--dev') && $input->hasParameterOption('--stable') === false) ? '13.x-dev' : '^13.0'; echo '> composer create-project "laravel/laravel:'.$version.'" skeleton --no-scripts --no-plugins --quiet --no-install'.PHP_EOL; @@ -20,49 +20,45 @@ collect([ 'artisan', '.env.example', - 'config/*.php', 'database/.gitignore', - 'database/migrations/2014_10_12_000000_create_users_table.php', - 'database/migrations/2014_10_12_100000_create_password_resets_table.php', - 'database/migrations/2019_08_19_000000_create_failed_jobs_table.php', - 'lang/en/*.php', - 'lang/*.json', + 'database/migrations/0001_01_01_000000_create_users_table.php', + 'database/migrations/0001_01_01_000001_create_cache_table.php', + 'database/migrations/0001_01_01_000002_create_jobs_table.php', 'resources/views/*', 'public/index.php', - 'tests/CreatesApplication.php', + // 'tests/CreatesApplication.php', ])->transform(fn ($file) => "{$workingPath}/skeleton/{$file}") ->map(fn ($file) => str_contains($file, '*') ? [...$files->glob($file)] : $file) ->flatten() ->each(function ($file) use ($files, $workingPath) { $files->copy($file, "{$workingPath}/laravel".Str::after($file, "{$workingPath}/skeleton")); }); -$files->delete("{$workingPath}/laravel/config/sanctum.php"); -$files->move("{$workingPath}/laravel/database/migrations/2014_10_12_000000_create_users_table.php", "{$workingPath}/laravel/migrations/2014_10_12_000000_testbench_create_users_table.php"); -$files->move("{$workingPath}/laravel/database/migrations/2014_10_12_100000_create_password_resets_table.php", "{$workingPath}/laravel/migrations/2014_10_12_100000_testbench_create_password_resets_table.php"); -$files->move("{$workingPath}/laravel/database/migrations/2019_08_19_000000_create_failed_jobs_table.php", "{$workingPath}/laravel/migrations/2019_08_19_000000_testbench_create_failed_jobs_table.php"); +$files->move("{$workingPath}/laravel/database/migrations/0001_01_01_000000_create_users_table.php", "{$workingPath}/laravel/migrations/0001_01_01_000000_testbench_create_users_table.php"); +$files->move("{$workingPath}/laravel/database/migrations/0001_01_01_000001_create_cache_table.php", "{$workingPath}/laravel/migrations/0001_01_01_000001_testbench_create_cache_table.php"); +$files->move("{$workingPath}/laravel/database/migrations/0001_01_01_000002_create_jobs_table.php", "{$workingPath}/laravel/migrations/0001_01_01_000002_testbench_create_jobs_table.php"); collect([ - 'cache/0001_01_01_000000_testbench_create_cache_table' => 'Cache/Console/stubs/cache.stub', - 'notifications/0001_01_01_000000_testbench_create_notifications_table' => 'Notifications/Console/stubs/notifications.stub', - 'queue/0001_01_01_000000_testbench_create_jobs_table' => 'Queue/Console/stubs/jobs.stub', - 'queue/0001_01_01_000000_testbench_create_job_batches_table' => 'Queue/Console/stubs/batches.stub', - // 'queue/0001_01_01_000000_testbench_create_failed_jobs_table' => 'Queue/Console/stubs/failed_jobs.stub', - 'session/0001_01_01_000000_testbench_create_sessions_table' => 'Session/Console/stubs/database.stub', + // 'cache/0001_01_02_000000_testbench_create_cache_table' => 'Cache/Console/stubs/cache.stub', + 'notifications/0001_01_02_000000_testbench_create_notifications_table' => 'Notifications/Console/stubs/notifications.stub', + // 'queue/0001_01_02_000000_testbench_create_jobs_table' => 'Queue/Console/stubs/jobs.stub', + // 'queue/0001_01_02_000000_testbench_create_job_batches_table' => 'Queue/Console/stubs/batches.stub', + // 'queue/0001_01_02_000000_testbench_create_failed_jobs_table' => 'Queue/Console/stubs/failed_jobs.stub', + // 'session/0001_01_02_000000_testbench_create_sessions_table' => 'Session/Console/stubs/database.stub', ])->transform(fn ($file) => "{$workingPath}/vendor/laravel/framework/src/Illuminate/{$file}") ->each(function ($from, $to) use ($files, $workingPath) { $files->copy($from, "{$workingPath}/laravel/migrations/{$to}.php"); })->keys() ->push(...[ - '2014_10_12_000000_testbench_create_users_table', - '2014_10_12_100000_testbench_create_password_resets_table', - '2019_08_19_000000_testbench_create_failed_jobs_table', + '0001_01_01_000000_testbench_create_users_table', + '0001_01_01_000001_testbench_create_cache_table', + '0001_01_01_000002_testbench_create_jobs_table', ])->each(function ($migration) use ($files, $workingPath) { $files->replaceInFile('class Create', 'class TestbenchCreate', "{$workingPath}/laravel/migrations/{$migration}.php"); })->filter(fn ($migration) => str_starts_with($migration, 'queue')) ->mapWithKeys(fn ($migration) => match ($migration) { - 'queue/0001_01_01_000000_testbench_create_jobs_table' => [$migration => 'jobs'], - 'queue/0001_01_01_000000_testbench_create_job_batches_table' => [$migration => 'job_batches'], - // 'queue/0001_01_01_000000_testbench_create_failed_jobs_table' => [$migration => 'failed_jobs'], + // 'queue/0001_01_02_000000_testbench_create_jobs_table' => [$migration => 'jobs'], + // 'queue/0001_01_02_000000_testbench_create_job_batches_table' => [$migration => 'job_batches'], + // 'queue/0001_01_02_000000_testbench_create_failed_jobs_table' => [$migration => 'failed_jobs'], })->each(function ($table, $migration) use ($files, $workingPath) { $files->replaceInFile(['{{tableClassName}}', '{{table}}'], [Str::studly($table), $table], "{$workingPath}/laravel/migrations/{$migration}.php"); }); @@ -83,102 +79,29 @@ line('DB_DATABASE=', 0) => line('# DB_DATABASE=', 0), line('DB_USERNAME=', 0) => line('# DB_USERNAME=', 0), line('DB_PASSWORD=', 0) => line('# DB_PASSWORD=', 0), + line('SESSION_DRIVER=database', 0) => line('SESSION_DRIVER=cookie', 0), + line('PHP_CLI_SERVER_WORKERS=', 0) => line('# PHP_CLI_SERVER_WORKERS=', 0), ], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/.env.example")); +collect([ + 'config/*.php', +])->transform(fn ($file) => "{$workingPath}/vendor/laravel/framework/{$file}") + ->map(fn ($file) => str_contains($file, '*') ? [...$files->glob($file)] : $file) + ->flatten() + ->each(function ($file) use ($files, $workingPath) { + $files->copy($file, "{$workingPath}/laravel".Str::after($file, "{$workingPath}/vendor/laravel/framework")); + }); + transform([ line("'env' => env('APP_ENV', 'production'),", 1) => line("'env' => env('APP_ENV', 'workbench'),", 1), - line("'timezone' => 'UTC',", 1) => line("'timezone' => env('APP_TIMEZONE', 'UTC'),", 1), - line("'locale' => 'en',", 1) => line("'locale' => env('APP_LOCALE', 'en'),", 1), - line("'fallback_locale' => 'en',", 1) => line("'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),", 1), ], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/app.php")); transform([ - line("'guard' => 'web',", 2) => line("'guard' => env('AUTH_GUARD', 'web'),", 2), - line("'passwords' => 'users',", 2) => line("'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),", 2), - line("'model' => App\Models\User::class,", 3) => line("'model' => env('AUTH_MODEL', Illuminate\Foundation\Auth\User::class),", 3), - // line("'table' => 'password_resets',", 3) => line("table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_resets'),", 3), - line("'password_timeout' => 10800,", 1) => line("'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),", 1), + line("'model' => env('AUTH_MODEL', App\Models\User::class),", 3) => line("'model' => env('AUTH_MODEL', Illuminate\Foundation\Auth\User::class),", 3), ], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/auth.php")); transform([ - line("'connection' => null,", 3) => line("'connection' => env('DB_CACHE_CONNECTION'),", 3), - line("'table' => 'cache',", 3) => line("'table' => env('DB_CACHE_TABLE', 'cache'),", 3), - line("'lock_connection' => null,", 3) => line("'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),", 3), -], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/cache.php")); - -transform([ - line("'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci',", 3) => line("'charset' => env('DB_CHARSET', 'utf8mb4'), - 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),", 3), - line("'charset' => 'utf8',", 3) => line("'charset' => env('DB_CHARSET', 'utf8'),", 3), -], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/database.php")); - -transform([ - line("'driver' => 'bcrypt',", 1) => line("'driver' => env('HASH_DRIVER', 'bcrypt'),", 1), - line("'argon' => [ - 'memory' => 65536, - 'threads' => 1, - 'time' => 4,", 1) => line("'argon' => [ - 'memory' => env('ARGON_MEMORY', 65536), - 'threads' => env('ARGON_THREADS', 1), - 'time' => env('ARGON_TIME', 4),", 1), + line("'bcrypt' => [ + 'rounds' => env('BCRYPT_ROUNDS', 12),", 1) => line("'bcrypt' => [ + 'rounds' => env('BCRYPT_ROUNDS', 10),", 1), ], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/hashing.php")); - -transform([ - line("'deprecations' => [ - 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), - 'trace' => false,", 1) => line("'deprecations' => [ - 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), - 'trace' => env('LOG_DEPRECATIONS_TRACE', false),", 1), - line("'stack' => [ - 'driver' => 'stack', - 'channels' => ['single'],", 2) => line("'stack' => [ - 'driver' => 'stack', - 'channels' => explode(',', env('LOG_STACK', 'single')),", 2), - line("'daily' => [ - 'driver' => 'daily', - 'path' => storage_path('logs/laravel.log'), - 'level' => env('LOG_LEVEL', 'debug'), - 'days' => 14,", 2) => line("'daily' => [ - 'driver' => 'daily', - 'path' => storage_path('logs/laravel.log'), - 'level' => env('LOG_LEVEL', 'debug'), - 'days' => env('LOG_DAILY_DAYS', 14),", 2), -], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/logging.php")); - -transform([ - line("'markdown' => [ - 'theme' => 'default',", 1) => line("'markdown' => [ - 'theme' => env('MAIL_MARKDOWN_THEME', 'default'),", 1), -], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/mail.php")); - -transform([ - line("'database' => [ - 'driver' => 'database', - 'table' => 'jobs', - 'queue' => 'default', - 'retry_after' => 90,", 2) => line("'database' => [ - 'driver' => 'database', - 'connection' => env('DB_QUEUE_CONNECTION'), - 'table' => env('DB_QUEUE_TABLE', 'jobs'), - 'queue' => env('DB_QUEUE', 'default'), - 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),", 2), - line("'beanstalkd' => [ - 'driver' => 'beanstalkd', - 'host' => 'localhost', - 'queue' => 'default', - 'retry_after' => 90,", 2) => line("'beanstalkd' => [ - 'driver' => 'beanstalkd', - 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), - 'queue' => env('BEANSTALKD_QUEUE', 'default'), - 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),", 2), -], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/queue.php")); - -transform([ - line("'expire_on_close' => false,", 1) => line("'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),", 1), - line("'encrypt' => false,", 1) => line("'encrypt' => env('SESSION_ENCRYPT', false),", 1), - line("'table' => 'sessions',", 1) => line("'table' => env('SESSION_TABLE', 'sessions'),", 1), - line("'path' => '/',", 1) => line("'path' => env('SESSION_PATH', '/'),", 1), - line("'http_only' => true,", 1) => line("'http_only' => env('SESSION_HTTP_ONLY', true),", 1), - line("'same_site' => 'lax',", 1) => line("'same_site' => env('SESSION_SAME_SITE', 'lax'),", 1), -], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/session.php")); diff --git a/bin/sync b/bin/sync index 20118ba72..8b75ec88e 100755 --- a/bin/sync +++ b/bin/sync @@ -9,6 +9,7 @@ $files = new Illuminate\Filesystem\Filesystem(); $purgeSkeletonDirectories = static function () use ($files, $workingPath) { $files->delete("{$workingPath}/laravel/.env"); + $files->delete("{$workingPath}/laravel/config/sanctum.php"); $files->deleteDirectory("{$workingPath}/skeleton"); }; @@ -50,15 +51,19 @@ transform([ ], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/app.php")); transform([ - line("'default' => env('CACHE_DRIVER', 'file'),", 1) => line("'default' => env('CACHE_DRIVER', 'array'),", 1), + line("'default' => env('CACHE_STORE', 'database'),", 1) => line("'default' => env('CACHE_STORE', 'array'),", 1), ], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/cache.php")); transform([ - // line("'default' => env('MAIL_MAILER', 'smtp'),", 1) => line("'default' => env('MAIL_MAILER', 'log'),", 1), -], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/mail.php")); + line("'serve' => true,", 3) => line("'serve' => false,", 3), +], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/filesystems.php")); transform([ - line("'driver' => env('SESSION_DRIVER', 'file'),", 1) => line("'driver' => env('SESSION_DRIVER', 'array'),", 1), + line("'default' => env('QUEUE_CONNECTION', 'database'),", 1) => line("'default' => env('QUEUE_CONNECTION', 'sync'),", 1), +], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/queue.php")); + +transform([ + line("'driver' => env('SESSION_DRIVER', 'database'),", 1) => line("'driver' => env('SESSION_DRIVER', 'array'),", 1), ], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/config/session.php")); /** @@ -72,7 +77,6 @@ transform([ "getcwd()" => "__DIR__.'/public'", ], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/laravel/server.php")); - /** * ---------------------------------------------------------------------- * Update `Orchestra\Testbench\Factories\UserFactory` @@ -83,8 +87,9 @@ $files->copy("{$workingPath}/skeleton/database/factories/UserFactory.php", "{$wo transform([ 'namespace Database\Factories;' => 'namespace Orchestra\Testbench\Factories;', - 'use Illuminate\Database\Eloquent\Factories\Factory;' => 'use Illuminate\Database\Eloquent\Factories\Factory;'.line('use Illuminate\Foundation\Auth\User;'), - ' * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>' => ' * @phpstan-type TModel \Illuminate\Foundation\Auth\User + 'use App\Models\User;' => 'use Illuminate\Foundation\Auth\User;', + 'use Illuminate\Database\Eloquent\Factories\Factory;' => 'use Illuminate\Database\Eloquent\Factories\Factory;', + ' * @extends Factory' => ' * @phpstan-type TModel \Illuminate\Foundation\Auth\User * * @extends \Illuminate\Database\Eloquent\Factories\Factory * @@ -98,12 +103,11 @@ transform([ */ public function modelName() { - return $this->model ?? config(\'auth.providers.users.model\', User::class); + return $this->model ?? config(\'auth.providers.users.model\') ?? env(\'AUTH_MODEL\', User::class); } }'.PHP_EOL, 1), ], fn ($changes) => $files->replaceInFile(array_keys($changes), array_values($changes), "{$workingPath}/src/Factories/UserFactory.php")); - /** * ---------------------------------------------------------------------- * Cleanup diff --git a/composer.json b/composer.json index d3eaf5aef..a7fb21b08 100644 --- a/composer.json +++ b/composer.json @@ -36,68 +36,56 @@ "testbench" ], "require": { - "php": "^8.0", + "php": "^8.3", "composer-runtime-api": "^2.2", "orchestra/sidekick": "~1.1.25|~1.2.22", "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-php83": "^1.32" + "symfony/polyfill-php84": "^1.34.0" }, "require-dev": { - "fakerphp/faker": "^1.21", - "laravel/framework": "^9.52.21", - "laravel/pint": "^1.5", - "mockery/mockery": "^1.5.1", + "fakerphp/faker": "^1.24", + "laravel/framework": "^13.32.0", + "laravel/pint": "^1.24", + "laravel/serializable-closure": "^2.0.10", + "mockery/mockery": "^1.6.10", "phpstan/phpstan": "2.2.16", - "phpunit/phpunit": "^9.5.10", - "spatie/laravel-ray": "^1.40.2", - "symfony/process": "^6.0.9", - "symfony/yaml": "^6.0.9", - "vlucas/phpdotenv": "^5.4.1" + "phpunit/phpunit": "^11.5.50|^12.5.8|^13.0.0", + "spatie/laravel-ray": "^1.43.6", + "symfony/process": "^7.4.5|^8.0.5", + "symfony/yaml": "^7.4.0|^8.0.0", + "vlucas/phpdotenv": "^5.6.1" }, "conflict": { - "brianium/paratest": "<6.4.0|>=7.0.0", - "laravel/framework": "<9.52.21|>=10.0.0", - "laravel/serializable-closure": "<1.3.0|>=2.0.0", - "orchestra/testbench-dusk": "<7.50.0|>=8.0.0", - "orchestra/workbench": "<1.0.0", - "nunomaduro/collision": "<6.2.0|>=7.0.0", - "phpunit/phpunit": "<9.5.10|>=10.0.0" + "brianium/paratest": "<7.3.0|>=8.0.0", + "laravel/framework": "<13.32.0|>=14.0.0", + "laravel/serializable-closure": ">=2.0.0 <2.0.10|>=3.0.0", + "nunomaduro/collision": "<8.9.0|>=9.0.0", + "phpunit/phpunit": "<11.5.50|>=12.0.0 <12.5.8|>=13.4.0" }, "suggest": { "ext-pcntl": "Required to use all features of the console signal trapping.", - "brianium/paratest": "Allow using parallel testing (^6.4).", - "fakerphp/faker": "Allow using Faker for testing (^1.21).", - "laravel/framework": "Required for testing (^9.52.20).", - "mockery/mockery": "Allow using Mockery for testing (^1.5.1).", - "nunomaduro/collision": "Allow using Laravel style tests output and parallel testing (^6.2).", - "orchestra/testbench-browser-kit": "Allow using legacy Laravel BrowserKit for testing (^7.0).", - "orchestra/testbench-dusk": "Allow using Laravel Dusk for testing (^7.0).", - "phpunit/phpunit": "Allow using PHPUnit for testing (^9.5.10).", - "symfony/process": "Required to use Orchestra\\Testbench\\remote function (^6.0.9).", - "symfony/yaml": "Required for Testbench CLI (^6.0.9).", - "vlucas/phpdotenv": "Required for Testbench CLI (^5.4.1)." + "brianium/paratest": "Allow using parallel testing (^7.3).", + "fakerphp/faker": "Allow using Faker for testing (^1.23).", + "laravel/framework": "Required for testing (^13.32.0).", + "mockery/mockery": "Allow using Mockery for testing (^1.6).", + "nunomaduro/collision": "Allow using Laravel style tests output and parallel testing (^8.9).", + "orchestra/testbench-dusk": "Allow using Laravel Dusk for testing (^11.0).", + "phpunit/phpunit": "Allow using PHPUnit for testing (^11.5.50|^12.5.8|^13.0.0).", + "symfony/process": "Required to use Orchestra\\Testbench\\remote function (^7.4|^8.0).", + "symfony/yaml": "Required for Testbench CLI (^7.4|^8.0).", + "vlucas/phpdotenv": "Required for Testbench CLI (^5.6.1)." }, "config": { "audit": { - "block-insecure": false, - "ignore": [ - "PKSA-m5cs-t1y6-qpcs", - "PKSA-3r5d-mb8f-1qw9", - "PKSA-mdq4-51ck-6kdq", - "PKSA-8qx3-n5y5-vvnd", - "PKSA-d5tc-s1qs-h781" - ] + "block-insecure": true, + "ignore": [] }, "preferred-install": { + "laravel/framework": "source", "*": "auto" }, "sort-packages": true }, - "extra": { - "branch-alias": { - "dev-master": "7.0-dev" - } - }, "scripts": { "post-autoload-dump": [ "@clear", @@ -116,7 +104,7 @@ "@php vendor/bin/phpstan analyse --verbose" ], "test": [ - "@php vendor/bin/phpunit --no-coverage --color -c ./" + "@php vendor/bin/phpunit --no-coverage --no-configuration --bootstrap vendor/autoload.php --exclude-group phpunit-configuration --color tests" ], "sync": [ "@clear", @@ -129,10 +117,12 @@ "@lint" ], "ci": [ + "@composer audit", "@post-autoload-dump", "@lint", "@test" ] }, - "minimum-stability": "stable" + "prefer-stable": true, + "minimum-stability": "dev" } diff --git a/create-sqlite-db b/create-sqlite-db deleted file mode 100755 index def8bc38f..000000000 --- a/create-sqlite-db +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env php - [], - 'dont-discover' => [], - ], -); - -$directory = ($config['laravel'] ? $config['laravel'] : $skeletonPath).'/database'; - -if (! file_exists("{$directory}/database.sqlite")) { - copy("{$skeletonPath}/database/database.sqlite.example", "{$directory}/database.sqlite"); -} diff --git a/drop-sqlite-db b/drop-sqlite-db deleted file mode 100755 index 381c72daa..000000000 --- a/drop-sqlite-db +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env php - [], - 'dont-discover' => [], - ], -); - -$directory = ($config['laravel'] ? $config['laravel'] : $skeletonPath).'/database'; - -if (file_exists("{$directory}/database.sqlite")) { - @unlink("{$directory}/database.sqlite"); -} diff --git a/laravel/.env.example b/laravel/.env.example index cdb75f936..e33fed000 100644 --- a/laravel/.env.example +++ b/laravel/.env.example @@ -2,9 +2,21 @@ APP_NAME=Laravel APP_ENV=local APP_KEY=AckfSECXIvnK5r28GVIWUAxmbBSjTsmF APP_DEBUG=true -APP_URL=http://localhost +APP_URL=http://localhost:8000 + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +# PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 LOG_CHANNEL=stack +LOG_STACK=single LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug @@ -15,25 +27,32 @@ DB_CONNECTION=sqlite # DB_USERNAME=root # DB_PASSWORD= -BROADCAST_DRIVER=log -CACHE_DRIVER=file -FILESYSTEM_DISK=local -QUEUE_CONNECTION=sync -SESSION_DRIVER=file +SESSION_DRIVER=cookie SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= MEMCACHED_HOST=127.0.0.1 +REDIS_CLIENT=phpredis REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 -MAIL_MAILER=smtp -MAIL_HOST=mailpit -MAIL_PORT=1025 +MAIL_MAILER=log +MAIL_SCHEME=null +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null -MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS="hello@example.com" MAIL_FROM_NAME="${APP_NAME}" @@ -43,16 +62,4 @@ AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false -PUSHER_APP_ID= -PUSHER_APP_KEY= -PUSHER_APP_SECRET= -PUSHER_HOST= -PUSHER_PORT=443 -PUSHER_SCHEME=https -PUSHER_APP_CLUSTER=mt1 - -VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" -VITE_PUSHER_HOST="${PUSHER_HOST}" -VITE_PUSHER_PORT="${PUSHER_PORT}" -VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" -VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" +VITE_APP_NAME="${APP_NAME}" diff --git a/laravel/artisan b/laravel/artisan index 86ca98a6d..b3b70d2ee 100755 --- a/laravel/artisan +++ b/laravel/artisan @@ -1,53 +1,18 @@ #!/usr/bin/env php make(Illuminate\Contracts\Console\Kernel::class); - -$status = $kernel->handle( - $input = new Symfony\Component\Console\Input\ArgvInput, - new Symfony\Component\Console\Output\ConsoleOutput -); - -/* -|-------------------------------------------------------------------------- -| Shutdown The Application -|-------------------------------------------------------------------------- -| -| Once Artisan has finished running, we will fire off the shutdown events -| so that any final work may be done by the application before we shut -| down the process. This is the last thing to happen to the request. -| -*/ - -$kernel->terminate($input, $status); +$status = $app->handleCommand(new ArgvInput); exit($status); diff --git a/laravel/bootstrap/providers.php b/laravel/bootstrap/providers.php new file mode 100644 index 000000000..3ac44ad10 --- /dev/null +++ b/laravel/bootstrap/providers.php @@ -0,0 +1,5 @@ + env('APP_URL', 'http://localhost'), + 'frontend_url' => env('FRONTEND_URL', 'http://localhost:3000'), + 'asset_url' => env('ASSET_URL'), /* @@ -64,12 +67,12 @@ |-------------------------------------------------------------------------- | | Here you may specify the default timezone for your application, which - | will be used by the PHP date and date-time functions. We have gone - | ahead and set this to a sensible default for you out of the box. + | will be used by the PHP date and date-time functions. The timezone + | is set to "UTC" by default as it is suitable for most use cases. | */ - 'timezone' => env('APP_TIMEZONE', 'UTC'), + 'timezone' => 'UTC', /* |-------------------------------------------------------------------------- @@ -77,8 +80,8 @@ |-------------------------------------------------------------------------- | | The application locale determines the default locale that will be used - | by the translation service provider. You are free to set this value - | to any of the locales which will be supported by the application. + | by Laravel's translation / localization methods. This option can be + | set to any locale for which you plan to have translation strings. | */ @@ -89,9 +92,9 @@ | Application Fallback Locale |-------------------------------------------------------------------------- | - | The fallback locale determines the locale to use when the current one + | The fallback locale determines the locale to use when the default one | is not available. You may change the value to correspond to any of - | the language folders that are provided through your application. + | the languages which are currently supported by your application. | */ @@ -108,22 +111,28 @@ | */ - 'faker_locale' => 'en_US', + 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), /* |-------------------------------------------------------------------------- | Encryption Key |-------------------------------------------------------------------------- | - | This key is used by the Illuminate encrypter service and should be set - | to a random, 32 character string, otherwise these encrypted strings - | will not be safe. Please do this before deploying an application! + | This key is utilized by Laravel's encryption services and should be set + | to a random, 32 character string to ensure that all encrypted values + | are secure. You should do this prior to deploying the application. | */ + 'cipher' => 'AES-256-CBC', + 'key' => env('APP_KEY'), - 'cipher' => 'AES-256-CBC', + 'previous_keys' => [ + ...array_filter( + explode(',', (string) env('APP_PREVIOUS_KEYS', '')) + ), + ], /* |-------------------------------------------------------------------------- @@ -134,13 +143,13 @@ | manage Laravel's "maintenance mode" status. The "cache" driver will | allow maintenance mode to be controlled across multiple machines. | - | Supported drivers: "file", "cache" + | Supported drivers: "file", "cache", "array" | */ 'maintenance' => [ - 'driver' => 'file', - // 'store' => 'redis', + 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), ], /* @@ -148,54 +157,20 @@ | Autoloaded Service Providers |-------------------------------------------------------------------------- | - | The service providers listed here will be automatically loaded on the - | request to your application. Feel free to add your own services to - | this array to grant expanded functionality to your applications. + | The service providers listed here will be automatically loaded on any + | requests to your application. You may add your own services to the + | arrays below to provide additional features to this application. | */ - 'providers' => [ - - /* - * Laravel Framework Service Providers... - */ - Illuminate\Auth\AuthServiceProvider::class, - Illuminate\Broadcasting\BroadcastServiceProvider::class, - Illuminate\Bus\BusServiceProvider::class, - Illuminate\Cache\CacheServiceProvider::class, - Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, - Illuminate\Cookie\CookieServiceProvider::class, - Illuminate\Database\DatabaseServiceProvider::class, - Illuminate\Encryption\EncryptionServiceProvider::class, - Illuminate\Filesystem\FilesystemServiceProvider::class, - Illuminate\Foundation\Providers\FoundationServiceProvider::class, - Illuminate\Hashing\HashServiceProvider::class, - Illuminate\Mail\MailServiceProvider::class, - Illuminate\Notifications\NotificationServiceProvider::class, - Illuminate\Pagination\PaginationServiceProvider::class, - Illuminate\Pipeline\PipelineServiceProvider::class, - Illuminate\Queue\QueueServiceProvider::class, - Illuminate\Redis\RedisServiceProvider::class, - Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, - Illuminate\Session\SessionServiceProvider::class, - Illuminate\Translation\TranslationServiceProvider::class, - Illuminate\Validation\ValidationServiceProvider::class, - Illuminate\View\ViewServiceProvider::class, - - /* - * Package Service Providers... - */ - - /* - * Application Service Providers... - */ + 'providers' => ServiceProvider::defaultProviders()->merge([ + // Package Service Providers... + ])->merge([ + // Application Service Providers... // App\Providers\AppServiceProvider::class, - // App\Providers\AuthServiceProvider::class, - // App\Providers\BroadcastServiceProvider::class, - // App\Providers\EventServiceProvider::class, - // App\Providers\RouteServiceProvider::class, - - ], + ])->merge([ + // Added Service Providers (Do not remove this line)... + ])->toArray(), /* |-------------------------------------------------------------------------- @@ -203,13 +178,13 @@ |-------------------------------------------------------------------------- | | This array of class aliases will be registered when this application - | is started. However, feel free to register as many as you wish as - | the aliases are "lazy" loaded so they don't hinder performance. + | is started. You may add any additional class aliases which should + | be loaded to the array. For speed, all aliases are lazy loaded. | */ 'aliases' => Facade::defaultAliases()->merge([ - // 'ExampleClass' => App\Example\ExampleClass::class, + // 'Example' => App\Facades\Example::class, ])->toArray(), ]; diff --git a/laravel/config/auth.php b/laravel/config/auth.php index 6199c4fea..c2a816bc3 100644 --- a/laravel/config/auth.php +++ b/laravel/config/auth.php @@ -7,8 +7,8 @@ | Authentication Defaults |-------------------------------------------------------------------------- | - | This option controls the default authentication "guard" and password - | reset options for your application. You may change these defaults + | This option defines the default authentication "guard" and password + | reset "broker" for your application. You may change these values | as required, but they're a perfect start for most applications. | */ @@ -25,11 +25,11 @@ | | Next, you may define every authentication guard for your application. | Of course, a great default configuration has been defined for you - | here which uses session storage and the Eloquent user provider. + | which utilizes session storage plus the Eloquent user provider. | - | All authentication drivers have a user provider. This defines how the + | All authentication guards have a user provider, which defines how the | users are actually retrieved out of your database or other storage - | mechanisms used by this application to persist your user's data. + | system used by the application. Typically, Eloquent is utilized. | | Supported: "session" | @@ -47,12 +47,12 @@ | User Providers |-------------------------------------------------------------------------- | - | All authentication drivers have a user provider. This defines how the + | All authentication guards have a user provider, which defines how the | users are actually retrieved out of your database or other storage - | mechanisms used by this application to persist your user's data. + | system used by the application. Typically, Eloquent is utilized. | | If you have multiple user tables or models you may configure multiple - | sources which represent each model / table. These sources may then + | providers to represent the model / table. These providers may then | be assigned to any extra authentication guards you have defined. | | Supported: "database", "eloquent" @@ -76,11 +76,11 @@ | Resetting Passwords |-------------------------------------------------------------------------- | - | You may specify multiple password reset configurations if you have more - | than one user table or model in the application and you want to have - | separate password reset settings based on the specific user types. + | These configuration options specify the behavior of Laravel's password + | reset functionality, including the table utilized for token storage + | and the user provider that is invoked to actually retrieve users. | - | The expire time is the number of minutes that each reset token will be + | The expiry time is the number of minutes that each reset token will be | considered valid. This security feature keeps tokens short-lived so | they have less time to be guessed. You may change this as needed. | @@ -93,7 +93,7 @@ 'passwords' => [ 'users' => [ 'provider' => 'users', - 'table' => 'password_resets', + 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), 'expire' => 60, 'throttle' => 60, ], @@ -104,8 +104,8 @@ | Password Confirmation Timeout |-------------------------------------------------------------------------- | - | Here you may define the amount of seconds before a password confirmation - | times out and the user is prompted to re-enter their password via the + | Here you may define the number of seconds before a password confirmation + | window expires and users are asked to re-enter their password via the | confirmation screen. By default, the timeout lasts for three hours. | */ diff --git a/laravel/config/broadcasting.php b/laravel/config/broadcasting.php index 9e4d4aa44..84604e802 100644 --- a/laravel/config/broadcasting.php +++ b/laravel/config/broadcasting.php @@ -11,11 +11,11 @@ | framework when an event needs to be broadcast. You may set this to | any of the connections defined in the "connections" array below. | - | Supported: "pusher", "ably", "redis", "log", "null" + | Supported: "reverb", "pusher", "ably", "mercure", "redis", "log", "null" | */ - 'default' => env('BROADCAST_DRIVER', 'null'), + 'default' => env('BROADCAST_CONNECTION', 'null'), /* |-------------------------------------------------------------------------- @@ -23,19 +23,36 @@ |-------------------------------------------------------------------------- | | Here you may define all of the broadcast connections that will be used - | to broadcast events to other systems or over websockets. Samples of + | to broadcast events to other systems or over WebSockets. Samples of | each available type of connection are provided inside this array. | */ 'connections' => [ + 'reverb' => [ + 'driver' => 'reverb', + 'key' => env('REVERB_APP_KEY'), + 'secret' => env('REVERB_APP_SECRET'), + 'app_id' => env('REVERB_APP_ID'), + 'options' => [ + 'host' => env('REVERB_HOST'), + 'port' => env('REVERB_PORT', 443), + 'scheme' => env('REVERB_SCHEME', 'https'), + 'useTLS' => env('REVERB_SCHEME', 'https') === 'https', + ], + 'client_options' => [ + // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html + ], + ], + 'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'options' => [ + 'cluster' => env('PUSHER_APP_CLUSTER'), 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', 'port' => env('PUSHER_PORT', 443), 'scheme' => env('PUSHER_SCHEME', 'https'), @@ -52,9 +69,18 @@ 'key' => env('ABLY_KEY'), ], - 'redis' => [ - 'driver' => 'redis', - 'connection' => 'default', + 'mercure' => [ + 'driver' => 'mercure', + 'url' => env('MERCURE_URL'), + 'public_url' => env('MERCURE_PUBLIC_URL'), + 'secret' => env('MERCURE_JWT_SECRET'), + 'encryption_key' => env('MERCURE_ENCRYPTION_KEY'), + 'claims' => [ + 'iss' => env('MERCURE_JWT_ISSUER'), + 'client_id' => env('APP_NAME'), + ], + 'cookie_name' => env('MERCURE_COOKIE_NAME'), + 'subscribe_expiration' => (int) env('MERCURE_SUBSCRIBE_EXPIRATION', 5), ], 'log' => [ diff --git a/laravel/config/cache.php b/laravel/config/cache.php index c1946d4da..2f29c357e 100644 --- a/laravel/config/cache.php +++ b/laravel/config/cache.php @@ -9,13 +9,13 @@ | Default Cache Store |-------------------------------------------------------------------------- | - | This option controls the default cache connection that gets used while - | using this caching library. This connection is used when another is - | not explicitly specified when executing a given caching function. + | This option controls the default cache store that will be used by the + | framework. This connection is utilized if another isn't explicitly + | specified when running a cache operation inside the application. | */ - 'default' => env('CACHE_DRIVER', 'array'), + 'default' => env('CACHE_STORE', 'array'), /* |-------------------------------------------------------------------------- @@ -26,32 +26,42 @@ | well as their drivers. You may even define multiple stores for the | same cache driver to group types of items stored in your caches. | - | Supported drivers: "apc", "array", "database", "file", - | "memcached", "redis", "dynamodb", "octane", "null" + | Supported drivers: "array", "database", "file", "memcached", + | "redis", "dynamodb", "storage", "octane", + | "session", "failover", "null" | */ 'stores' => [ - 'apc' => [ - 'driver' => 'apc', - ], - 'array' => [ 'driver' => 'array', 'serialize' => false, ], + 'session' => [ + 'driver' => 'session', + 'key' => env('SESSION_CACHE_KEY', '_cache'), + ], + 'database' => [ 'driver' => 'database', - 'table' => env('DB_CACHE_TABLE', 'cache'), 'connection' => env('DB_CACHE_CONNECTION'), + 'table' => env('DB_CACHE_TABLE', 'cache'), 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), + 'lock_table' => env('DB_CACHE_LOCK_TABLE'), ], 'file' => [ 'driver' => 'file', 'path' => storage_path('framework/cache/data'), + 'lock_path' => storage_path('framework/cache/data'), + ], + + 'storage' => [ + 'driver' => 'storage', + 'disk' => env('CACHE_STORAGE_DISK'), + 'path' => env('CACHE_STORAGE_PATH', 'framework/cache/data'), ], 'memcached' => [ @@ -75,8 +85,8 @@ 'redis' => [ 'driver' => 'redis', - 'connection' => 'cache', - 'lock_connection' => 'default', + 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), + 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), ], 'dynamodb' => [ @@ -92,6 +102,14 @@ 'driver' => 'octane', ], + 'failover' => [ + 'driver' => 'failover', + 'stores' => [ + 'database', + 'array', + ], + ], + ], /* @@ -99,12 +117,12 @@ | Cache Key Prefix |-------------------------------------------------------------------------- | - | When utilizing the APC, database, memcached, Redis, or DynamoDB cache - | stores there might be other applications using the same cache. For + | When utilizing the APC, database, memcached, Redis, and DynamoDB cache + | stores, there might be other applications using the same cache. For | that reason, you may prefix every cache key to avoid collisions. | */ - 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), + 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'), ]; diff --git a/laravel/config/concurrency.php b/laravel/config/concurrency.php new file mode 100644 index 000000000..cb8022b46 --- /dev/null +++ b/laravel/config/concurrency.php @@ -0,0 +1,20 @@ + env('CONCURRENCY_DRIVER', 'process'), + +]; diff --git a/laravel/config/database.php b/laravel/config/database.php index e64d4bbbc..24f99b802 100644 --- a/laravel/config/database.php +++ b/laravel/config/database.php @@ -1,6 +1,7 @@ env('DB_CONNECTION', 'mysql'), + 'default' => env('DB_CONNECTION', 'sqlite'), /* |-------------------------------------------------------------------------- | Database Connections |-------------------------------------------------------------------------- | - | Here are each of the database connections setup for your application. - | Of course, examples of configuring each database platform that is - | supported by Laravel is shown below to make development simple. - | - | - | All database work in Laravel is done through the PHP PDO facilities - | so make sure you have the driver for your particular database of - | choice installed on your machine before you begin development. + | Below are all of the database connections defined for your application. + | An example configuration is provided for each database system which + | is supported by Laravel. You're free to add / remove connections. | */ @@ -37,58 +34,97 @@ 'sqlite' => [ 'driver' => 'sqlite', - 'url' => env('DATABASE_URL'), + 'url' => env('DB_URL'), 'database' => env('DB_DATABASE', database_path('database.sqlite')), 'prefix' => '', + 'prefix_indexes' => null, + 'mask_bindings_in_exception_messages' => env('DB_MASK_BINDINGS', false), 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), + 'busy_timeout' => null, + 'journal_mode' => null, + 'synchronous' => null, + 'transaction_mode' => 'DEFERRED', + 'pragmas' => [], ], 'mysql' => [ 'driver' => 'mysql', - 'url' => env('DATABASE_URL'), + 'url' => env('DB_URL'), 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', ''), 'unix_socket' => env('DB_SOCKET', ''), 'charset' => env('DB_CHARSET', 'utf8mb4'), 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 'prefix' => '', 'prefix_indexes' => true, + 'mask_bindings_in_exception_messages' => env('DB_MASK_BINDINGS', false), 'strict' => true, 'engine' => null, 'options' => extension_loaded('pdo_mysql') ? array_filter([ - PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + Mysql::ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'mariadb' => [ + 'driver' => 'mariadb', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'mask_bindings_in_exception_messages' => env('DB_MASK_BINDINGS', false), + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + Mysql::ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), ]) : [], ], 'pgsql' => [ 'driver' => 'pgsql', - 'url' => env('DATABASE_URL'), + 'url' => env('DB_URL'), 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '5432'), - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', ''), 'charset' => env('DB_CHARSET', 'utf8'), 'prefix' => '', 'prefix_indexes' => true, + 'mask_bindings_in_exception_messages' => env('DB_MASK_BINDINGS', false), 'search_path' => 'public', 'sslmode' => 'prefer', + 'pooled' => env('DB_POOLED', false), + 'direct' => array_filter([ + 'host' => env('DB_DIRECT_HOST'), + 'port' => env('DB_DIRECT_PORT'), + 'username' => env('DB_DIRECT_USERNAME'), + 'password' => env('DB_DIRECT_PASSWORD'), + 'sslmode' => env('DB_DIRECT_SSLMODE'), + ]), ], 'sqlsrv' => [ 'driver' => 'sqlsrv', - 'url' => env('DATABASE_URL'), + 'url' => env('DB_URL'), 'host' => env('DB_HOST', 'localhost'), 'port' => env('DB_PORT', '1433'), - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', ''), 'charset' => env('DB_CHARSET', 'utf8'), 'prefix' => '', 'prefix_indexes' => true, + 'mask_bindings_in_exception_messages' => env('DB_MASK_BINDINGS', false), // 'encrypt' => env('DB_ENCRYPT', 'yes'), // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), ], @@ -102,11 +138,14 @@ | | This table keeps track of all the migrations that have already run for | your application. Using this information, we can determine which of - | the migrations on disk haven't actually been run in the database. + | the migrations on disk haven't actually been run on the database. | */ - 'migrations' => 'migrations', + 'migrations' => [ + 'table' => 'migrations', + 'update_date_on_publish' => true, + ], /* |-------------------------------------------------------------------------- @@ -115,7 +154,7 @@ | | Redis is an open source, fast, and advanced key-value store that also | provides a richer body of commands than a typical key-value system - | such as APC or Memcached. Laravel makes it easy to dig right in. + | such as Memcached. You may define your connection settings here. | */ @@ -125,7 +164,8 @@ 'options' => [ 'cluster' => env('REDIS_CLUSTER', 'redis'), - 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'), + 'persistent' => env('REDIS_PERSISTENT', false), ], 'default' => [ @@ -135,6 +175,11 @@ 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', '6379'), 'database' => env('REDIS_DB', '0'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), + 'command_retries' => env('REDIS_COMMAND_RETRIES', 0), ], 'cache' => [ @@ -144,6 +189,11 @@ 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', '6379'), 'database' => env('REDIS_CACHE_DB', '1'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), + 'command_retries' => env('REDIS_COMMAND_RETRIES', 0), ], ], diff --git a/laravel/config/filesystems.php b/laravel/config/filesystems.php index e9d9dbdbe..d72c55447 100644 --- a/laravel/config/filesystems.php +++ b/laravel/config/filesystems.php @@ -9,7 +9,7 @@ | | Here you may specify the default filesystem disk that should be used | by the framework. The "local" disk, as well as a variety of cloud - | based disks are available to your application. Just store away! + | based disks are available to your application for file storage. | */ @@ -20,11 +20,11 @@ | Filesystem Disks |-------------------------------------------------------------------------- | - | Here you may configure as many filesystem "disks" as you wish, and you - | may even configure multiple disks of the same driver. Defaults have - | been set up for each driver as an example of the required values. + | Below you may configure as many filesystem disks as necessary, and you + | may even configure multiple disks for the same driver. Examples for + | most supported storage drivers are configured here for reference. | - | Supported Drivers: "local", "ftp", "sftp", "s3" + | Supported drivers: "local", "ftp", "sftp", "s3" | */ @@ -32,16 +32,19 @@ 'local' => [ 'driver' => 'local', - 'root' => storage_path('app'), + 'root' => storage_path('app/private'), + 'serve' => false, 'throw' => false, + 'report' => false, ], 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), - 'url' => env('APP_URL').'/storage', + 'url' => rtrim((string) env('APP_URL'), '/').'/storage', 'visibility' => 'public', 'throw' => false, + 'report' => false, ], 's3' => [ @@ -54,6 +57,7 @@ 'endpoint' => env('AWS_ENDPOINT'), 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 'throw' => false, + 'report' => false, ], ], diff --git a/laravel/config/hashing.php b/laravel/config/hashing.php index ab5e451d3..2653fabb4 100644 --- a/laravel/config/hashing.php +++ b/laravel/config/hashing.php @@ -30,6 +30,8 @@ 'bcrypt' => [ 'rounds' => env('BCRYPT_ROUNDS', 10), + 'verify' => env('HASH_VERIFY', true), + 'limit' => env('BCRYPT_LIMIT', null), ], /* @@ -47,6 +49,20 @@ 'memory' => env('ARGON_MEMORY', 65536), 'threads' => env('ARGON_THREADS', 1), 'time' => env('ARGON_TIME', 4), + 'verify' => env('HASH_VERIFY', true), ], + /* + |-------------------------------------------------------------------------- + | Rehash On Login + |-------------------------------------------------------------------------- + | + | Setting this option to true will tell Laravel to automatically rehash + | the user's password during login if the configured work factor for + | the algorithm has changed, allowing graceful upgrades of hashes. + | + */ + + 'rehash_on_login' => true, + ]; diff --git a/laravel/config/image.php b/laravel/config/image.php new file mode 100644 index 000000000..bfef7f9ee --- /dev/null +++ b/laravel/config/image.php @@ -0,0 +1,20 @@ + env('IMAGE_DRIVER', 'gd'), + +]; diff --git a/laravel/config/images.php b/laravel/config/images.php new file mode 100644 index 000000000..bfef7f9ee --- /dev/null +++ b/laravel/config/images.php @@ -0,0 +1,20 @@ + env('IMAGE_DRIVER', 'gd'), + +]; diff --git a/laravel/config/logging.php b/laravel/config/logging.php index d7f11ca77..cc21b2bb6 100644 --- a/laravel/config/logging.php +++ b/laravel/config/logging.php @@ -3,6 +3,7 @@ use Monolog\Handler\NullHandler; use Monolog\Handler\StreamHandler; use Monolog\Handler\SyslogUdpHandler; +use Monolog\Processor\PsrLogMessageProcessor; return [ @@ -11,9 +12,9 @@ | Default Log Channel |-------------------------------------------------------------------------- | - | This option defines the default log channel that gets used when writing - | messages to the logs. The name specified in this option should match - | one of the channels defined in the "channels" configuration array. + | This option defines the default log channel that is utilized to write + | messages to your logs. The value provided here should match one of + | the channels present in the list of "channels" configured below. | */ @@ -40,20 +41,20 @@ | Log Channels |-------------------------------------------------------------------------- | - | Here you may configure the log channels for your application. Out of - | the box, Laravel uses the Monolog PHP logging library. This gives - | you a variety of powerful log handlers / formatters to utilize. + | Here you may configure the log channels for your application. Laravel + | utilizes the Monolog PHP logging library, which includes a variety + | of powerful log handlers and formatters that you're free to use. | - | Available Drivers: "single", "daily", "slack", "syslog", - | "errorlog", "monolog", - | "custom", "stack" + | Available drivers: "single", "daily", "monthly", "slack", "syslog", + | "errorlog", "monolog", "custom", "stack" | */ 'channels' => [ + 'stack' => [ 'driver' => 'stack', - 'channels' => explode(',', env('LOG_STACK', 'single')), + 'channels' => explode(',', (string) env('LOG_STACK', 'single')), 'ignore_exceptions' => false, ], @@ -61,21 +62,32 @@ 'driver' => 'single', 'path' => storage_path('logs/laravel.log'), 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, ], 'daily' => [ 'driver' => 'daily', 'path' => storage_path('logs/laravel.log'), 'level' => env('LOG_LEVEL', 'debug'), - 'days' => env('LOG_DAILY_DAYS', 14), + 'max_files' => env('LOG_DAILY_DAYS', 14), + 'replace_placeholders' => true, + ], + + 'monthly' => [ + 'driver' => 'monthly', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'max_files' => 3, + 'replace_placeholders' => true, ], 'slack' => [ 'driver' => 'slack', 'url' => env('LOG_SLACK_WEBHOOK_URL'), - 'username' => 'Laravel Log', - 'emoji' => ':boom:', + 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), + 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, ], 'papertrail' => [ @@ -87,26 +99,31 @@ 'port' => env('PAPERTRAIL_PORT'), 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), ], + 'processors' => [PsrLogMessageProcessor::class], ], 'stderr' => [ 'driver' => 'monolog', 'level' => env('LOG_LEVEL', 'debug'), 'handler' => StreamHandler::class, - 'formatter' => env('LOG_STDERR_FORMATTER'), - 'with' => [ + 'handler_with' => [ 'stream' => 'php://stderr', ], + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'processors' => [PsrLogMessageProcessor::class], ], 'syslog' => [ 'driver' => 'syslog', 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), + 'replace_placeholders' => true, ], 'errorlog' => [ 'driver' => 'errorlog', 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, ], 'null' => [ @@ -117,6 +134,7 @@ 'emergency' => [ 'path' => storage_path('logs/laravel.log'), ], + ], ]; diff --git a/laravel/config/mail.php b/laravel/config/mail.php index 68f4b555a..6ea5a8a3a 100644 --- a/laravel/config/mail.php +++ b/laravel/config/mail.php @@ -7,13 +7,14 @@ | Default Mailer |-------------------------------------------------------------------------- | - | This option controls the default mailer that is used to send any email - | messages sent by your application. Alternative mailers may be setup - | and used as needed; however, this mailer will be used by default. + | This option controls the default mailer that is used to send all email + | messages unless another mailer is explicitly specified when sending + | the message. All additional mailers can be configured within the + | "mailers" array. Examples of each type of mailer are provided. | */ - 'default' => env('MAIL_MAILER', 'smtp'), + 'default' => env('MAIL_MAILER', 'log'), /* |-------------------------------------------------------------------------- @@ -24,37 +25,44 @@ | their respective settings. Several examples have been configured for | you and you are free to add your own as your application requires. | - | Laravel supports a variety of mail "transport" drivers to be used while - | sending an e-mail. You will specify which one you are using for your - | mailers below. You are free to add additional mailers as required. + | Laravel supports a variety of mail "transport" drivers that can be used + | when delivering an email. You may specify which one you're using for + | your mailers below. You may also add additional mailers if needed. | - | Supported: "smtp", "sendmail", "mailgun", "ses", - | "postmark", "log", "array", "failover" + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "resend", "log", "array", + | "failover", "roundrobin" | */ 'mailers' => [ + 'smtp' => [ 'transport' => 'smtp', - 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), - 'port' => env('MAIL_PORT', 587), - 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'scheme' => env('MAIL_SCHEME'), + 'url' => env('MAIL_URL'), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), 'timeout' => null, - 'local_domain' => env('MAIL_EHLO_DOMAIN'), + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)), ], 'ses' => [ 'transport' => 'ses', ], - 'mailgun' => [ - 'transport' => 'mailgun', - ], - 'postmark' => [ 'transport' => 'postmark', + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'resend' => [ + 'transport' => 'resend', ], 'sendmail' => [ @@ -77,7 +85,18 @@ 'smtp', 'log', ], + 'retry_after' => 60, + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + 'retry_after' => 60, ], + ], /* @@ -85,9 +104,9 @@ | Global "From" Address |-------------------------------------------------------------------------- | - | You may wish for all e-mails sent by your application to be sent from - | the same address. Here, you may specify a name and address that is - | used globally for all e-mails that are sent by your application. + | You may wish for all emails sent by your application to be sent from + | the same address. Here you may specify a name and address that is + | used globally for all emails that are sent by your application. | */ @@ -103,7 +122,7 @@ | | If you are using Markdown based email rendering, you may configure your | theme and component paths here, allowing you to customize the design - | of the emails. Or, you may simply stick with the Laravel defaults! + | of the emails. Or, you may simply stick with the Laravel defaults. | */ @@ -113,6 +132,10 @@ 'paths' => [ resource_path('views/vendor/mail'), ], + + 'extensions' => [ + // \League\CommonMark\Extension\Strikethrough\StrikethroughExtension::class, + ], ], ]; diff --git a/laravel/config/queue.php b/laravel/config/queue.php index 6232736d0..e0ecdb58a 100644 --- a/laravel/config/queue.php +++ b/laravel/config/queue.php @@ -7,9 +7,9 @@ | Default Queue Connection Name |-------------------------------------------------------------------------- | - | Laravel's queue API supports an assortment of back-ends via a single - | API, giving you convenient access to each back-end using the same - | syntax for every one. Here you may define a default connection. + | Laravel's queue supports a variety of backends via a single, unified + | API, giving you convenient access to each backend using identical + | syntax for each. The default queue connection is defined below. | */ @@ -20,11 +20,12 @@ | Queue Connections |-------------------------------------------------------------------------- | - | Here you may configure the connection information for each server that - | is used by your application. A default configuration has been added - | for each back-end shipped with Laravel. You are free to add more. + | Here you may configure the connection options for every queue backend + | used by your application. An example configuration is provided for + | each backend supported by Laravel. You're also free to add more. | - | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", + | "deferred", "failover", "null" | */ @@ -61,17 +62,52 @@ 'suffix' => env('SQS_SUFFIX'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 'after_commit' => false, + 'overflow' => [ + 'enabled' => env('SQS_OVERFLOW_ENABLED', false), + 'store' => env('SQS_OVERFLOW_STORE'), + 'always' => false, + 'delete_after_processing' => true, + 'flush_on_clear' => env('SQS_OVERFLOW_FLUSH_ON_CLEAR', false), + ], ], 'redis' => [ 'driver' => 'redis', - 'connection' => 'default', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), 'queue' => env('REDIS_QUEUE', 'default'), - 'retry_after' => 90, + 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), 'block_for' => null, 'after_commit' => false, ], + 'deferred' => [ + 'driver' => 'deferred', + ], + + 'failover' => [ + 'driver' => 'failover', + 'connections' => [ + 'database', + 'deferred', + ], + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Job Batching + |-------------------------------------------------------------------------- + | + | The following options configure the database and table that store job + | batching information. These options can be updated to any database + | connection and table which has been defined by your application. + | + */ + + 'batching' => [ + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'job_batches', ], /* @@ -80,14 +116,16 @@ |-------------------------------------------------------------------------- | | These options configure the behavior of failed queue job logging so you - | can control which database and table are used to store the jobs that - | have failed. You may change them to any database / table you wish. + | can control how and where failed jobs are stored. Laravel ships with + | support for storing failed jobs in a simple file or in a database. + | + | Supported drivers: "database-uuids", "dynamodb", "file", "null" | */ 'failed' => [ 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), - 'database' => env('DB_CONNECTION', 'mysql'), + 'database' => env('DB_CONNECTION', 'sqlite'), 'table' => 'failed_jobs', ], diff --git a/laravel/config/services.php b/laravel/config/services.php index 0ace530e8..6182e4b90 100644 --- a/laravel/config/services.php +++ b/laravel/config/services.php @@ -14,21 +14,25 @@ | */ - 'mailgun' => [ - 'domain' => env('MAILGUN_DOMAIN'), - 'secret' => env('MAILGUN_SECRET'), - 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), - 'scheme' => 'https', - ], - 'postmark' => [ 'token' => env('POSTMARK_TOKEN'), ], + 'resend' => [ + 'key' => env('RESEND_KEY'), + ], + 'ses' => [ 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), ], + 'slack' => [ + 'notifications' => [ + 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), + 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), + ], + ], + ]; diff --git a/laravel/config/session.php b/laravel/config/session.php index e1045ada2..cb85e264d 100644 --- a/laravel/config/session.php +++ b/laravel/config/session.php @@ -9,12 +9,12 @@ | Default Session Driver |-------------------------------------------------------------------------- | - | This option controls the default session "driver" that will be used on - | requests. By default, we will use the lightweight native driver but - | you may specify any of the other wonderful drivers provided here. + | This option determines the default session driver that is utilized for + | incoming requests. Laravel supports a variety of storage options to + | persist session data. Database storage is a great default choice. | - | Supported: "file", "cookie", "database", "apc", - | "memcached", "redis", "dynamodb", "array" + | Supported: "file", "cookie", "database", "memcached", + | "redis", "dynamodb", "array" | */ @@ -27,11 +27,12 @@ | | Here you may specify the number of minutes that you wish the session | to be allowed to remain idle before it expires. If you want them - | to immediately expire on the browser closing, set that option. + | to expire immediately when the browser is closed then you may + | indicate that via the expire_on_close configuration option. | */ - 'lifetime' => env('SESSION_LIFETIME', 120), + 'lifetime' => (int) env('SESSION_LIFETIME', 120), 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), @@ -41,8 +42,8 @@ |-------------------------------------------------------------------------- | | This option allows you to easily specify that all of your session data - | should be encrypted before it is stored. All encryption will be run - | automatically by Laravel and you can use the Session like normal. + | should be encrypted before it's stored. All encryption is performed + | automatically by Laravel and you may use the session like normal. | */ @@ -53,9 +54,9 @@ | Session File Location |-------------------------------------------------------------------------- | - | When using the native session driver, we need a location where session - | files may be stored. A default has been set for you but a different - | location may be specified. This is only needed for file sessions. + | When utilizing the "file" session driver, the session files are placed + | on disk. The default storage location is defined here; however, you + | are free to provide another location where they should be stored. | */ @@ -79,9 +80,9 @@ | Session Database Table |-------------------------------------------------------------------------- | - | When using the "database" session driver, you may specify the table we - | should use to manage the sessions. Of course, a sensible default is - | provided for you; however, you are free to change this as needed. + | When using the "database" session driver, you may specify the table to + | be used to store sessions. Of course, a sensible default is defined + | for you; however, you're welcome to change this to another table. | */ @@ -92,11 +93,11 @@ | Session Cache Store |-------------------------------------------------------------------------- | - | While using one of the framework's cache driven session backends you may - | list a cache store that should be used for these sessions. This value - | must match with one of the application's configured cache "stores". + | When using one of the framework's cache driven session backends, you may + | define the cache store which should be used to store the session data + | between requests. This must match one of your defined cache stores. | - | Affects: "apc", "dynamodb", "memcached", "redis" + | Affects: "dynamodb", "memcached", "redis" | */ @@ -120,15 +121,15 @@ | Session Cookie Name |-------------------------------------------------------------------------- | - | Here you may change the name of the cookie used to identify a session - | instance by ID. The name specified here will get used every time a - | new session cookie is created by the framework for every driver. + | Here you may change the name of the session cookie that is created by + | the framework. Typically, you should not need to change this value + | since doing so does not grant a meaningful security improvement. | */ 'cookie' => env( 'SESSION_COOKIE', - Str::slug(env('APP_NAME', 'laravel'), '_').'_session' + Str::snake((string) env('APP_NAME', 'laravel')).'_session' ), /* @@ -138,7 +139,7 @@ | | The session cookie path determines the path for which the cookie will | be regarded as available. Typically, this will be the root path of - | your application but you are free to change this when necessary. + | your application, but you're free to change this when necessary. | */ @@ -149,9 +150,9 @@ | Session Cookie Domain |-------------------------------------------------------------------------- | - | Here you may change the domain of the cookie used to identify a session - | in your application. This will determine which domains the cookie is - | available to in your application. A sensible default has been set. + | This value determines the domain and subdomains the session cookie is + | available to. By default, the cookie will be available to the root + | domain and all subdomains. Typically, this shouldn't be changed. | */ @@ -177,7 +178,7 @@ | | Setting this value to true will prevent JavaScript from accessing the | value of the cookie and the cookie will only be accessible through - | the HTTP protocol. You are free to modify this option if needed. + | the HTTP protocol. It's unlikely you should disable this option. | */ @@ -190,7 +191,9 @@ | | This option determines how your cookies behave when cross-site requests | take place, and can be used to mitigate CSRF attacks. By default, we - | will set this value to "lax" since this is a secure default value. + | will set this value to "lax" to permit secure cross-site requests. + | + | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value | | Supported: "lax", "strict", "none", null | @@ -198,4 +201,17 @@ 'same_site' => env('SESSION_SAME_SITE', 'lax'), + /* + |-------------------------------------------------------------------------- + | Partitioned Cookies + |-------------------------------------------------------------------------- + | + | Setting this value to true will tie the cookie to the top-level site for + | a cross-site context. Partitioned cookies are accepted by the browser + | when flagged "secure" and the Same-Site attribute is set to "none". + | + */ + + 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), + ]; diff --git a/laravel/lang/en/auth.php b/laravel/lang/en/auth.php deleted file mode 100644 index 6598e2c06..000000000 --- a/laravel/lang/en/auth.php +++ /dev/null @@ -1,20 +0,0 @@ - 'These credentials do not match our records.', - 'password' => 'The provided password is incorrect.', - 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', - -]; diff --git a/laravel/lang/en/pagination.php b/laravel/lang/en/pagination.php deleted file mode 100644 index d48141187..000000000 --- a/laravel/lang/en/pagination.php +++ /dev/null @@ -1,19 +0,0 @@ - '« Previous', - 'next' => 'Next »', - -]; diff --git a/laravel/lang/en/passwords.php b/laravel/lang/en/passwords.php deleted file mode 100644 index 2345a56b5..000000000 --- a/laravel/lang/en/passwords.php +++ /dev/null @@ -1,22 +0,0 @@ - 'Your password has been reset!', - 'sent' => 'We have emailed your password reset link!', - 'throttled' => 'Please wait before retrying.', - 'token' => 'This password reset token is invalid.', - 'user' => "We can't find a user with that email address.", - -]; diff --git a/laravel/lang/en/validation.php b/laravel/lang/en/validation.php deleted file mode 100644 index 70407c9d9..000000000 --- a/laravel/lang/en/validation.php +++ /dev/null @@ -1,184 +0,0 @@ - 'The :attribute must be accepted.', - 'accepted_if' => 'The :attribute must be accepted when :other is :value.', - 'active_url' => 'The :attribute is not a valid URL.', - 'after' => 'The :attribute must be a date after :date.', - 'after_or_equal' => 'The :attribute must be a date after or equal to :date.', - 'alpha' => 'The :attribute must only contain letters.', - 'alpha_dash' => 'The :attribute must only contain letters, numbers, dashes and underscores.', - 'alpha_num' => 'The :attribute must only contain letters and numbers.', - 'array' => 'The :attribute must be an array.', - 'ascii' => 'The :attribute must only contain single-byte alphanumeric characters and symbols.', - 'before' => 'The :attribute must be a date before :date.', - 'before_or_equal' => 'The :attribute must be a date before or equal to :date.', - 'between' => [ - 'array' => 'The :attribute must have between :min and :max items.', - 'file' => 'The :attribute must be between :min and :max kilobytes.', - 'numeric' => 'The :attribute must be between :min and :max.', - 'string' => 'The :attribute must be between :min and :max characters.', - ], - 'boolean' => 'The :attribute field must be true or false.', - 'confirmed' => 'The :attribute confirmation does not match.', - 'current_password' => 'The password is incorrect.', - 'date' => 'The :attribute is not a valid date.', - 'date_equals' => 'The :attribute must be a date equal to :date.', - 'date_format' => 'The :attribute does not match the format :format.', - 'decimal' => 'The :attribute must have :decimal decimal places.', - 'declined' => 'The :attribute must be declined.', - 'declined_if' => 'The :attribute must be declined when :other is :value.', - 'different' => 'The :attribute and :other must be different.', - 'digits' => 'The :attribute must be :digits digits.', - 'digits_between' => 'The :attribute must be between :min and :max digits.', - 'dimensions' => 'The :attribute has invalid image dimensions.', - 'distinct' => 'The :attribute field has a duplicate value.', - 'doesnt_end_with' => 'The :attribute may not end with one of the following: :values.', - 'doesnt_start_with' => 'The :attribute may not start with one of the following: :values.', - 'email' => 'The :attribute must be a valid email address.', - 'ends_with' => 'The :attribute must end with one of the following: :values.', - 'enum' => 'The selected :attribute is invalid.', - 'exists' => 'The selected :attribute is invalid.', - 'file' => 'The :attribute must be a file.', - 'filled' => 'The :attribute field must have a value.', - 'gt' => [ - 'array' => 'The :attribute must have more than :value items.', - 'file' => 'The :attribute must be greater than :value kilobytes.', - 'numeric' => 'The :attribute must be greater than :value.', - 'string' => 'The :attribute must be greater than :value characters.', - ], - 'gte' => [ - 'array' => 'The :attribute must have :value items or more.', - 'file' => 'The :attribute must be greater than or equal to :value kilobytes.', - 'numeric' => 'The :attribute must be greater than or equal to :value.', - 'string' => 'The :attribute must be greater than or equal to :value characters.', - ], - 'image' => 'The :attribute must be an image.', - 'in' => 'The selected :attribute is invalid.', - 'in_array' => 'The :attribute field does not exist in :other.', - 'integer' => 'The :attribute must be an integer.', - 'ip' => 'The :attribute must be a valid IP address.', - 'ipv4' => 'The :attribute must be a valid IPv4 address.', - 'ipv6' => 'The :attribute must be a valid IPv6 address.', - 'json' => 'The :attribute must be a valid JSON string.', - 'lowercase' => 'The :attribute must be lowercase.', - 'lt' => [ - 'array' => 'The :attribute must have less than :value items.', - 'file' => 'The :attribute must be less than :value kilobytes.', - 'numeric' => 'The :attribute must be less than :value.', - 'string' => 'The :attribute must be less than :value characters.', - ], - 'lte' => [ - 'array' => 'The :attribute must not have more than :value items.', - 'file' => 'The :attribute must be less than or equal to :value kilobytes.', - 'numeric' => 'The :attribute must be less than or equal to :value.', - 'string' => 'The :attribute must be less than or equal to :value characters.', - ], - 'mac_address' => 'The :attribute must be a valid MAC address.', - 'max' => [ - 'array' => 'The :attribute must not have more than :max items.', - 'file' => 'The :attribute must not be greater than :max kilobytes.', - 'numeric' => 'The :attribute must not be greater than :max.', - 'string' => 'The :attribute must not be greater than :max characters.', - ], - 'max_digits' => 'The :attribute must not have more than :max digits.', - 'mimes' => 'The :attribute must be a file of type: :values.', - 'mimetypes' => 'The :attribute must be a file of type: :values.', - 'min' => [ - 'array' => 'The :attribute must have at least :min items.', - 'file' => 'The :attribute must be at least :min kilobytes.', - 'numeric' => 'The :attribute must be at least :min.', - 'string' => 'The :attribute must be at least :min characters.', - ], - 'min_digits' => 'The :attribute must have at least :min digits.', - 'missing' => 'The :attribute field must be missing.', - 'missing_if' => 'The :attribute field must be missing when :other is :value.', - 'missing_unless' => 'The :attribute field must be missing unless :other is :value.', - 'missing_with' => 'The :attribute field must be missing when :values is present.', - 'missing_with_all' => 'The :attribute field must be missing when :values are present.', - 'multiple_of' => 'The :attribute must be a multiple of :value.', - 'not_in' => 'The selected :attribute is invalid.', - 'not_regex' => 'The :attribute format is invalid.', - 'numeric' => 'The :attribute must be a number.', - 'password' => [ - 'letters' => 'The :attribute must contain at least one letter.', - 'mixed' => 'The :attribute must contain at least one uppercase and one lowercase letter.', - 'numbers' => 'The :attribute must contain at least one number.', - 'symbols' => 'The :attribute must contain at least one symbol.', - 'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.', - ], - 'present' => 'The :attribute field must be present.', - 'prohibited' => 'The :attribute field is prohibited.', - 'prohibited_if' => 'The :attribute field is prohibited when :other is :value.', - 'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.', - 'prohibits' => 'The :attribute field prohibits :other from being present.', - 'regex' => 'The :attribute format is invalid.', - 'required' => 'The :attribute field is required.', - 'required_array_keys' => 'The :attribute field must contain entries for: :values.', - 'required_if' => 'The :attribute field is required when :other is :value.', - 'required_if_accepted' => 'The :attribute field is required when :other is accepted.', - 'required_unless' => 'The :attribute field is required unless :other is in :values.', - 'required_with' => 'The :attribute field is required when :values is present.', - 'required_with_all' => 'The :attribute field is required when :values are present.', - 'required_without' => 'The :attribute field is required when :values is not present.', - 'required_without_all' => 'The :attribute field is required when none of :values are present.', - 'same' => 'The :attribute and :other must match.', - 'size' => [ - 'array' => 'The :attribute must contain :size items.', - 'file' => 'The :attribute must be :size kilobytes.', - 'numeric' => 'The :attribute must be :size.', - 'string' => 'The :attribute must be :size characters.', - ], - 'starts_with' => 'The :attribute must start with one of the following: :values.', - 'string' => 'The :attribute must be a string.', - 'timezone' => 'The :attribute must be a valid timezone.', - 'unique' => 'The :attribute has already been taken.', - 'uploaded' => 'The :attribute failed to upload.', - 'uppercase' => 'The :attribute must be uppercase.', - 'url' => 'The :attribute must be a valid URL.', - 'ulid' => 'The :attribute must be a valid ULID.', - 'uuid' => 'The :attribute must be a valid UUID.', - - /* - |-------------------------------------------------------------------------- - | Custom Validation Language Lines - |-------------------------------------------------------------------------- - | - | Here you may specify custom validation messages for attributes using the - | convention "attribute.rule" to name the lines. This makes it quick to - | specify a specific custom language line for a given attribute rule. - | - */ - - 'custom' => [ - 'attribute-name' => [ - 'rule-name' => 'custom-message', - ], - ], - - /* - |-------------------------------------------------------------------------- - | Custom Validation Attributes - |-------------------------------------------------------------------------- - | - | The following language lines are used to swap our attribute placeholder - | with something more reader friendly such as "E-Mail Address" instead - | of "email". This simply helps us make our message more expressive. - | - */ - - 'attributes' => [], - -]; diff --git a/laravel/migrations/0001_01_01_000000_testbench_create_users_table.php b/laravel/migrations/0001_01_01_000000_testbench_create_users_table.php new file mode 100644 index 000000000..05fb5d9ea --- /dev/null +++ b/laravel/migrations/0001_01_01_000000_testbench_create_users_table.php @@ -0,0 +1,49 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + }); + + Schema::create('password_reset_tokens', function (Blueprint $table) { + $table->string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + + Schema::create('sessions', function (Blueprint $table) { + $table->string('id')->primary(); + $table->foreignId('user_id')->nullable()->index(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->longText('payload'); + $table->integer('last_activity')->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + Schema::dropIfExists('password_reset_tokens'); + Schema::dropIfExists('sessions'); + } +}; diff --git a/laravel/migrations/cache/0001_01_01_000000_testbench_create_cache_table.php b/laravel/migrations/0001_01_01_000001_testbench_create_cache_table.php similarity index 78% rename from laravel/migrations/cache/0001_01_01_000000_testbench_create_cache_table.php rename to laravel/migrations/0001_01_01_000001_testbench_create_cache_table.php index eee35e943..06dc7a5ee 100644 --- a/laravel/migrations/cache/0001_01_01_000000_testbench_create_cache_table.php +++ b/laravel/migrations/0001_01_01_000001_testbench_create_cache_table.php @@ -8,30 +8,26 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('cache', function (Blueprint $table) { $table->string('key')->primary(); $table->mediumText('value'); - $table->integer('expiration'); + $table->bigInteger('expiration')->index(); }); Schema::create('cache_locks', function (Blueprint $table) { $table->string('key')->primary(); $table->string('owner'); - $table->integer('expiration'); + $table->bigInteger('expiration')->index(); }); } /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('cache'); Schema::dropIfExists('cache_locks'); diff --git a/laravel/migrations/0001_01_01_000002_testbench_create_jobs_table.php b/laravel/migrations/0001_01_01_000002_testbench_create_jobs_table.php new file mode 100644 index 000000000..edac6fee5 --- /dev/null +++ b/laravel/migrations/0001_01_01_000002_testbench_create_jobs_table.php @@ -0,0 +1,59 @@ +id(); + $table->string('queue')->index(); + $table->longText('payload'); + $table->unsignedSmallInteger('attempts'); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + }); + + Schema::create('job_batches', function (Blueprint $table) { + $table->string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + + Schema::create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->string('uuid')->unique(); + $table->string('connection'); + $table->string('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + + $table->index(['connection', 'queue', 'failed_at']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('jobs'); + Schema::dropIfExists('job_batches'); + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/laravel/migrations/2014_10_12_000000_testbench_create_users_table.php b/laravel/migrations/2014_10_12_000000_testbench_create_users_table.php deleted file mode 100644 index cf6b77661..000000000 --- a/laravel/migrations/2014_10_12_000000_testbench_create_users_table.php +++ /dev/null @@ -1,36 +0,0 @@ -id(); - $table->string('name'); - $table->string('email')->unique(); - $table->timestamp('email_verified_at')->nullable(); - $table->string('password'); - $table->rememberToken(); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('users'); - } -}; diff --git a/laravel/migrations/2014_10_12_100000_testbench_create_password_resets_table.php b/laravel/migrations/2014_10_12_100000_testbench_create_password_resets_table.php deleted file mode 100644 index e5f1397c9..000000000 --- a/laravel/migrations/2014_10_12_100000_testbench_create_password_resets_table.php +++ /dev/null @@ -1,32 +0,0 @@ -string('email')->primary(); - $table->string('token'); - $table->timestamp('created_at')->nullable(); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('password_resets'); - } -}; diff --git a/laravel/migrations/2019_08_19_000000_testbench_create_failed_jobs_table.php b/laravel/migrations/2019_08_19_000000_testbench_create_failed_jobs_table.php deleted file mode 100644 index 17191986b..000000000 --- a/laravel/migrations/2019_08_19_000000_testbench_create_failed_jobs_table.php +++ /dev/null @@ -1,36 +0,0 @@ -id(); - $table->string('uuid')->unique(); - $table->text('connection'); - $table->text('queue'); - $table->longText('payload'); - $table->longText('exception'); - $table->timestamp('failed_at')->useCurrent(); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('failed_jobs'); - } -}; diff --git a/laravel/migrations/notifications/0001_01_01_000000_testbench_create_notifications_table.php b/laravel/migrations/notifications/0001_01_02_000000_testbench_create_notifications_table.php similarity index 86% rename from laravel/migrations/notifications/0001_01_01_000000_testbench_create_notifications_table.php rename to laravel/migrations/notifications/0001_01_02_000000_testbench_create_notifications_table.php index 4357c9efa..d73803223 100644 --- a/laravel/migrations/notifications/0001_01_01_000000_testbench_create_notifications_table.php +++ b/laravel/migrations/notifications/0001_01_02_000000_testbench_create_notifications_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('notifications', function (Blueprint $table) { $table->uuid('id')->primary(); @@ -25,10 +23,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('notifications'); } diff --git a/laravel/migrations/queue/0001_01_01_000000_testbench_create_job_batches_table.php b/laravel/migrations/queue/0001_01_01_000000_testbench_create_job_batches_table.php deleted file mode 100644 index 525b3891f..000000000 --- a/laravel/migrations/queue/0001_01_01_000000_testbench_create_job_batches_table.php +++ /dev/null @@ -1,39 +0,0 @@ -string('id')->primary(); - $table->string('name'); - $table->integer('total_jobs'); - $table->integer('pending_jobs'); - $table->integer('failed_jobs'); - $table->longText('failed_job_ids'); - $table->mediumText('options')->nullable(); - $table->integer('cancelled_at')->nullable(); - $table->integer('created_at'); - $table->integer('finished_at')->nullable(); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('job_batches'); - } -}; diff --git a/laravel/migrations/queue/0001_01_01_000000_testbench_create_jobs_table.php b/laravel/migrations/queue/0001_01_01_000000_testbench_create_jobs_table.php deleted file mode 100644 index a786a8910..000000000 --- a/laravel/migrations/queue/0001_01_01_000000_testbench_create_jobs_table.php +++ /dev/null @@ -1,36 +0,0 @@ -bigIncrements('id'); - $table->string('queue')->index(); - $table->longText('payload'); - $table->unsignedTinyInteger('attempts'); - $table->unsignedInteger('reserved_at')->nullable(); - $table->unsignedInteger('available_at'); - $table->unsignedInteger('created_at'); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('jobs'); - } -}; diff --git a/laravel/migrations/session/0001_01_01_000000_testbench_create_sessions_table.php b/laravel/migrations/session/0001_01_01_000000_testbench_create_sessions_table.php deleted file mode 100644 index 06c3e4501..000000000 --- a/laravel/migrations/session/0001_01_01_000000_testbench_create_sessions_table.php +++ /dev/null @@ -1,35 +0,0 @@ -string('id')->primary(); - $table->foreignId('user_id')->nullable()->index(); - $table->string('ip_address', 45)->nullable(); - $table->text('user_agent')->nullable(); - $table->longText('payload'); - $table->integer('last_activity')->index(); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('sessions'); - } -}; diff --git a/laravel/public/index.php b/laravel/public/index.php index 660d25d84..ccf0e8791 100644 --- a/laravel/public/index.php +++ b/laravel/public/index.php @@ -1,55 +1,20 @@ make(Kernel::class); - -$response = $kernel->handle( - $request = Request::capture() -)->send(); - -$kernel->terminate($request, $response); +$app->handleRequest(Request::capture()); diff --git a/laravel/resources/views/welcome.blade.php b/laravel/resources/views/welcome.blade.php index 9faad4e85..26e294ab7 100644 --- a/laravel/resources/views/welcome.blade.php +++ b/laravel/resources/views/welcome.blade.php @@ -4,129 +4,220 @@ - Laravel - - - - - - - - + {{ config('app.name', 'Laravel') }} + + @fonts + + + @if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot'))) + @vite(['resources/css/app.css', 'resources/js/app.js']) + @else + + @endif - -
+ +
@if (Route::has('login')) - + @endif - -
-
- - - - - -
- -
-
-
- - -
-
- Laravel has wonderful, thorough documentation covering every aspect of the framework. Whether you are new to the framework or have previous experience with Laravel, we recommend reading all of the documentation from beginning to end. -
-
-
- -
-
- - -
- -
-
- Laracasts offers thousands of video tutorials on Laravel, PHP, and JavaScript development. Check them out, see for yourself, and massively level up your development skills in the process. -
-
-
- -
-
- - -
- -
-
- Laravel News is a community driven portal and newsletter aggregating all of the latest and most important news in the Laravel ecosystem, including new package releases and tutorials. -
-
-
- -
-
- -
Vibrant Ecosystem
-
- -
-
- Laravel's robust library of first-party tools and libraries, such as Forge, Vapor, Nova, and Envoyer help you take your projects to the next level. Pair them with powerful open source libraries like Cashier, Dusk, Echo, Horizon, Sanctum, Telescope, and more. -
-
-
-
-
- -
-
-
- - - - - - Shop +
+ + {{-- 13 --}} + + + + + + + + + + + + -
- Laravel v{{ Illuminate\Foundation\Application::VERSION }} (PHP v{{ PHP_VERSION }}) -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- + + + @if (Route::has('login')) + + @endif diff --git a/laravel/server.php b/laravel/server.php index 05cdbf056..bee2268b8 100644 --- a/laravel/server.php +++ b/laravel/server.php @@ -13,4 +13,11 @@ return false; } +$formattedDateTime = date('D M j H:i:s Y'); + +$requestMethod = $_SERVER['REQUEST_METHOD']; +$remoteAddress = $_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT']; + +file_put_contents('php://stdout', "[$formattedDateTime] $remoteAddress [$requestMethod] URI: $uri\n"); + require_once $publicPath.'/index.php'; diff --git a/laravel/tests/CreatesApplication.php b/laravel/tests/CreatesApplication.php index 547152f6a..0cd56d079 100644 --- a/laravel/tests/CreatesApplication.php +++ b/laravel/tests/CreatesApplication.php @@ -3,15 +3,14 @@ namespace Tests; use Illuminate\Contracts\Console\Kernel; +use Illuminate\Foundation\Application; trait CreatesApplication { /** - * Creates the application. - * - * @return \Illuminate\Foundation\Application + * Create a new application instance. */ - public function createApplication() + public function createApplication(): Application { $app = require __DIR__.'/../bootstrap/app.php'; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index aab499115..215718009 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,2 +1,7 @@ parameters: - ignoreErrors: [] + ignoreErrors: + - + message: '#^Strict comparison using \!\=\= between mixed and false will always evaluate to true\.$#' + identifier: notIdentical.alwaysTrue + count: 1 + path: src/Foundation/Bootstrap/SyncDatabaseEnvironmentVariables.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index b2f1dfa5e..c18f8cbac 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -17,8 +17,10 @@ parameters: - '#Unsafe usage of new static#' excludePaths: + - src/Bootstrap/ConfigureRay.php - src/Exceptions/DeprecatedException.php - src/Exceptions/PHPUnitErrorException.php - src/Foundation/Console/TestCommand.php + - src/PHPUnit/TestCase.php treatPhpDocTypesAsCertain: false diff --git a/phpunit.xml b/phpunit.xml index d5cc5e62e..c48b01566 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,29 +1,26 @@ - + cacheDirectory=".phpunit.cache" + backupStaticProperties="false"> + + + + ./tests/ + + + src/ src/Http/Middleware - - - - ./tests/ - - + diff --git a/pint.json b/pint.json index fccaaf83d..32a1104e7 100644 --- a/pint.json +++ b/pint.json @@ -7,14 +7,7 @@ "scope": "namespaced", "strict": true }, - "new_with_parentheses": { - "named_class": false, - "anonymous_class": false - }, "no_superfluous_phpdoc_tags": false, - "php_unit_method_casing": false, - "nullable_type_declaration_for_default_null_value": { - "use_nullable_type_declaration": true - } + "php_unit_method_casing": false } } diff --git a/src/Attributes/Define.php b/src/Attributes/Define.php index 0b4fc363f..da599c0ba 100644 --- a/src/Attributes/Define.php +++ b/src/Attributes/Define.php @@ -16,8 +16,8 @@ final class Define implements ResolvableContract * @param string $method */ public function __construct( - public string $group, - public string $method + public readonly string $group, + public readonly string $method ) {} /** diff --git a/src/Attributes/DefineDatabase.php b/src/Attributes/DefineDatabase.php index 48db5f382..2b45ce2e5 100644 --- a/src/Attributes/DefineDatabase.php +++ b/src/Attributes/DefineDatabase.php @@ -18,8 +18,8 @@ final class DefineDatabase implements ActionableContract, AfterEachContract, Bef * @param bool $defer */ public function __construct( - public string $method, - public bool $defer = true + public readonly string $method, + public readonly bool $defer = true ) {} /** diff --git a/src/Attributes/DefineEnvironment.php b/src/Attributes/DefineEnvironment.php index 63280a9a9..f24bef1b7 100644 --- a/src/Attributes/DefineEnvironment.php +++ b/src/Attributes/DefineEnvironment.php @@ -15,7 +15,7 @@ final class DefineEnvironment implements ActionableContract * @param string $method */ public function __construct( - public string $method + public readonly string $method ) {} /** diff --git a/src/Attributes/DefineRoute.php b/src/Attributes/DefineRoute.php index 051f2d948..ecd7d73b8 100644 --- a/src/Attributes/DefineRoute.php +++ b/src/Attributes/DefineRoute.php @@ -15,7 +15,7 @@ final class DefineRoute implements ActionableContract * @param string $method */ public function __construct( - public string $method + public readonly string $method ) {} /** diff --git a/src/Attributes/RequiresDatabase.php b/src/Attributes/RequiresDatabase.php new file mode 100644 index 000000000..ab0bfab25 --- /dev/null +++ b/src/Attributes/RequiresDatabase.php @@ -0,0 +1,101 @@ +default = $default; + + if (\is_array($driver) && $default === true) { + throw new InvalidArgumentException('Unable to validate default connection when given an array of database drivers'); + } + } + + /** + * Handle the attribute. + * + * @param \Illuminate\Foundation\Application $app + * @param \Closure(string, array):void $action + * @return void + */ + public function handle($app, Closure $action): void + { + $connection = DB::connection($this->connection); + + if ( + ($this->default ?? false) === true + && \is_string($this->driver) + && $connection->getDriverName() !== $this->driver + ) { + \call_user_func($action, 'markTestSkipped', [\sprintf('Requires %s to configured for "%s" database connection', $this->driver, $connection->getName())]); + + return; + } + + $drivers = (new Collection( + Arr::wrap($this->driver) + ))->filter(fn ($driver) => $driver === $connection->getDriverName()); + + if ($drivers->isEmpty()) { + \call_user_func( + $action, + 'markTestSkipped', + [\sprintf('Requires [%s] to be configured for "%s" database connection', Arr::join(Arr::wrap($this->driver), '/'), $connection->getName())] + ); + + return; + } + + if ( + \is_string($this->driver) + && ! \is_null($this->versionRequirement) + && preg_match('/(?P[<>=!]{0,2})\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m', $this->versionRequirement, $matches) + ) { + if (empty($matches['operator'])) { + $matches['operator'] = '>='; + } + + if (! version_compare($connection->getServerVersion(), $matches['version'], $matches['operator'])) { + \call_user_func( + $action, + 'markTestSkipped', + [\sprintf('Requires %s:%s to be configured for "%s" database connection', $this->driver, $this->versionRequirement, $connection->getName())] + ); + } + } + } +} diff --git a/src/Attributes/RequiresEnv.php b/src/Attributes/RequiresEnv.php index 34e33b5a2..6d39b96b9 100644 --- a/src/Attributes/RequiresEnv.php +++ b/src/Attributes/RequiresEnv.php @@ -17,8 +17,8 @@ final class RequiresEnv implements ActionableContract * @param string|null $message */ public function __construct( - public string $key, - public ?string $message = null + public readonly string $key, + public readonly ?string $message = null ) {} /** diff --git a/src/Attributes/RequiresLaravel.php b/src/Attributes/RequiresLaravel.php index 18a5f9e76..f4629bcd1 100644 --- a/src/Attributes/RequiresLaravel.php +++ b/src/Attributes/RequiresLaravel.php @@ -17,7 +17,7 @@ final class RequiresLaravel implements ActionableContract * @param string $versionRequirement */ public function __construct( - public string $versionRequirement + public readonly string $versionRequirement ) {} /** diff --git a/src/Attributes/ResetRefreshDatabaseState.php b/src/Attributes/ResetRefreshDatabaseState.php index ebf10004a..6a34df972 100644 --- a/src/Attributes/ResetRefreshDatabaseState.php +++ b/src/Attributes/ResetRefreshDatabaseState.php @@ -37,6 +37,7 @@ public function afterAll(): void */ public static function run(): void { + RefreshDatabaseState::$inMemoryConnections = []; RefreshDatabaseState::$migrated = false; RefreshDatabaseState::$lazilyRefreshed = false; } diff --git a/src/Attributes/ResolvesLaravel.php b/src/Attributes/ResolvesLaravel.php index 09e6dbcbe..d9f9feaf8 100644 --- a/src/Attributes/ResolvesLaravel.php +++ b/src/Attributes/ResolvesLaravel.php @@ -15,7 +15,7 @@ final class ResolvesLaravel implements ActionableContract * @param string $method */ public function __construct( - public string $method + public readonly string $method ) {} /** diff --git a/src/Attributes/UsesFrameworkConfiguration.php b/src/Attributes/UsesFrameworkConfiguration.php index 284804648..3f6dddcd8 100644 --- a/src/Attributes/UsesFrameworkConfiguration.php +++ b/src/Attributes/UsesFrameworkConfiguration.php @@ -6,6 +6,8 @@ use Illuminate\Foundation\Bootstrap\LoadConfiguration; use Orchestra\Testbench\Contracts\Attributes\Invokable as InvokableContract; +use function Orchestra\Testbench\package_path; + #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] final class UsesFrameworkConfiguration implements InvokableContract { @@ -18,5 +20,9 @@ final class UsesFrameworkConfiguration implements InvokableContract public function __invoke($app): void { $app->bind(LoadConfiguration::class, LoadConfiguration::class); + + $app->useConfigPath(package_path(['vendor', 'laravel', 'framework', 'config'])); + + $app->dontMergeFrameworkConfiguration(); } } diff --git a/src/Attributes/WithCachedViews.php b/src/Attributes/WithCachedViews.php new file mode 100644 index 000000000..d475d4aeb --- /dev/null +++ b/src/Attributes/WithCachedViews.php @@ -0,0 +1,51 @@ +bound('view')) { + $app->make('view')->flushFinderCache(); + } + }; + + if (in_parallel_testing()) { + ParallelTesting::setUpTestCase($callback); + + return; + } + + value($callback); + } + + /** + * Handle the attribute. + * + * @param \Illuminate\Foundation\Application $app + * @return void + */ + public function afterEach($app): void + { + artisan($app, 'view:clear'); + } +} diff --git a/src/Attributes/WithConfig.php b/src/Attributes/WithConfig.php index cafbba676..c4f326f5b 100644 --- a/src/Attributes/WithConfig.php +++ b/src/Attributes/WithConfig.php @@ -37,9 +37,9 @@ final class WithConfig implements InvokableContract * @param bool $defer */ public function __construct( - public string $key, - public mixed $value, - public bool $defer = true + public readonly string $key, + public readonly mixed $value, + public readonly bool $defer = true ) {} /** diff --git a/src/Attributes/WithEnv.php b/src/Attributes/WithEnv.php index 2960c7105..2d2c6db90 100644 --- a/src/Attributes/WithEnv.php +++ b/src/Attributes/WithEnv.php @@ -18,8 +18,8 @@ final class WithEnv implements InvokableContract * @param string|null $value */ public function __construct( - public string $key, - public ?string $value + public readonly string $key, + public readonly ?string $value ) {} /** diff --git a/src/Attributes/WithMigration.php b/src/Attributes/WithMigration.php index 5b4fb822b..f90dff0af 100644 --- a/src/Attributes/WithMigration.php +++ b/src/Attributes/WithMigration.php @@ -17,7 +17,7 @@ final class WithMigration implements InvokableContract * * @var array */ - public array $types = []; + public readonly array $types; /** * Construct a new attribute. @@ -26,7 +26,9 @@ final class WithMigration implements InvokableContract */ public function __construct() { - $this->types = \func_num_args() > 0 ? \func_get_args() : ['laravel']; + $this->types = (new Collection(\func_num_args() > 0 ? \func_get_args() : ['laravel'])) + ->transform(static fn ($type) => \in_array($type, ['cache', 'queue', 'session']) ? 'laravel' : $type) + ->all(); } /** diff --git a/src/Bootstrap/ConfigureRay.php b/src/Bootstrap/ConfigureRay.php index c9b04b046..1bf6d84ac 100644 --- a/src/Bootstrap/ConfigureRay.php +++ b/src/Bootstrap/ConfigureRay.php @@ -3,28 +3,32 @@ namespace Orchestra\Testbench\Bootstrap; use Illuminate\Contracts\Foundation\Application; +use Spatie\LaravelRay\RayServiceProvider; use Spatie\Ray\Settings\Settings; use function Orchestra\Testbench\after_resolving; /** * @internal - * - * @phpstan-type TLaravel \Illuminate\Contracts\Foundation\Application */ final class ConfigureRay { /** * Bootstrap the given application. * - * @param TLaravel $app + * @param \Illuminate\Contracts\Foundation\Application $app * @return void * * @codeCoverageIgnore */ public function bootstrap(Application $app): void { + if (! $app->providerIsLoaded(RayServiceProvider::class)) { + return; + } + after_resolving($app, Settings::class, static function ($settings, $app) { + /** @var \Illuminate\Contracts\Foundation\Application $app */ /** @var \Spatie\Ray\Settings\Settings $settings */ /** @var \Illuminate\Contracts\Config\Repository $config */ $config = $app->make('config'); diff --git a/src/Bootstrap/HandleExceptions.php b/src/Bootstrap/HandleExceptions.php index 719bcd915..ae249b54c 100644 --- a/src/Bootstrap/HandleExceptions.php +++ b/src/Bootstrap/HandleExceptions.php @@ -7,7 +7,6 @@ use Orchestra\Testbench\Exceptions\DeprecatedException; use function Orchestra\Sidekick\Filesystem\join_paths; -use function Orchestra\Sidekick\phpunit_version_compare; /** * @internal @@ -15,41 +14,31 @@ final class HandleExceptions extends \Illuminate\Foundation\Bootstrap\HandleExceptions { /** - * Create a new exception handler instance. + * {@inheritDoc} * - * @param \PHPUnit\Framework\TestCase|null $testbench + * @throws \Orchestra\Testbench\Exceptions\DeprecatedException */ - public function __construct( - protected $testbench = null - ) {} - - /** {@inheritDoc} */ #[\Override] public function handleDeprecationError($message, $file, $line, $level = E_DEPRECATED) { - parent::handleDeprecationError($message, $file, $line, $level); + rescue(function () use ($message, $file, $line, $level) { + parent::handleDeprecationError($message, $file, $line, $level); + }, null, false); - $testbenchConvertDeprecationsToExceptions = Env::get('TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS'); - - $error = new DeprecatedException($message, $level, $file, $line); + $testbenchConvertDeprecationsToExceptions = (bool) Env::get( + 'TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS', false + ); if ($testbenchConvertDeprecationsToExceptions === true) { - throw $error; - } - - if ($testbenchConvertDeprecationsToExceptions !== false && $this->getPhpUnitConvertDeprecationsToExceptions() === true) { - throw $error; + throw new DeprecatedException($message, $level, $file, $line); } } - /** - * Ensure the "deprecations" logger is configured. - * - * @return void - */ + /** {@inheritDoc} */ + #[\Override] protected function ensureDeprecationLoggerIsConfigured() { - with(self::$app['config'], static function ($config) { /** @phpstan-ignore offsetAccess.nonOffsetAccessible */ + with(self::$app->make('config'), static function ($config) { /** @var \Illuminate\Contracts\Config\Repository $config */ if ($config->get('logging.channels.deprecations')) { return; @@ -81,34 +70,12 @@ protected function ensureDeprecationLoggerIsConfigured() }); } - /** - * Get PHPUnit convert deprecations to exceptions from TestResult. - * - * @phpunit-overrides - * - * @return bool - */ - protected function getPhpUnitConvertDeprecationsToExceptions(): bool - { - if (phpunit_version_compare('10', '>=')) { - return false; - } - - /** @var \PHPUnit\Framework\TestResult|null $testResult */ - $testResult = $this->testbench?->getTestResultObject(); - - return $testResult?->getConvertDeprecationsToExceptions() ?? false; - } - - /** - * Determine if deprecation error should be ignored. - * - * @return bool - */ + /** {@inheritDoc} */ + #[\Override] protected function shouldIgnoreDeprecationErrors() { return ! class_exists(LogManager::class) || ! self::$app->hasBeenBootstrapped() - || ! Env::get('LOG_DEPRECATIONS_WHILE_TESTING', true); + || ! (bool) Env::get('LOG_DEPRECATIONS_WHILE_TESTING', true); } } diff --git a/src/Bootstrap/LoadConfiguration.php b/src/Bootstrap/LoadConfiguration.php index ea4e992c6..b7d8aea67 100644 --- a/src/Bootstrap/LoadConfiguration.php +++ b/src/Bootstrap/LoadConfiguration.php @@ -2,16 +2,20 @@ namespace Orchestra\Testbench\Bootstrap; +use Generator; use Illuminate\Config\Repository; use Illuminate\Contracts\Config\Repository as RepositoryContract; use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\Collection; use Illuminate\Support\LazyCollection; use Orchestra\Sidekick\Env; +use RuntimeException; use SplFileInfo; use Symfony\Component\Finder\Finder; use function Orchestra\Testbench\default_skeleton_path; +use function Orchestra\Testbench\package_path; +use function Orchestra\Testbench\uses_default_skeleton; /** * @internal @@ -20,6 +24,13 @@ */ class LoadConfiguration { + /** + * Cached Laravel Framework default configuration. + * + * @var \Illuminate\Contracts\Config\Repository|null + */ + protected static ?RepositoryContract $cachedFrameworkConfigurations = null; + /** * Bootstrap the given application. * @@ -54,22 +65,48 @@ public function bootstrap(Application $app): void */ private function loadConfigurationFiles(Application $app, RepositoryContract $config): void { + $shouldMerge = method_exists($app, 'shouldMergeFrameworkConfiguration') + ? $app->shouldMergeFrameworkConfiguration() + : true; + + static::$cachedFrameworkConfigurations ??= new Repository( + (new Collection(uses_default_skeleton($app->basePath()) ? [] : $this->getFrameworkDefaultConfigurations())) + ->transform(fn ($path, $key) => require $path) + ->all() + ); + $this->extendsLoadedConfiguration( (new LazyCollection(function () use ($app) { $path = $this->getConfigurationPath($app); if (\is_string($path)) { - foreach (Finder::create()->files()->name('*.php')->in($path) as $file) { - $directory = $this->getNestedDirectory($file, $path); - - yield $directory.basename($file->getRealPath(), '.php') => $file->getRealPath(); - } + yield from $this->getConfigurationsFromPath($path); } })) ->collect() ->transform(fn ($path, $key) => $this->resolveConfigurationFile($path, $key)) )->each(static function ($path, $key) use ($config) { $config->set($key, require $path); + })->when($shouldMerge === true, static function ($configurations) use ($config) { + /** @var \Illuminate\Contracts\Config\Repository $baseConfigurations */ + $baseConfigurations = static::$cachedFrameworkConfigurations; + + /** @var array $excludes */ + $excludes = $configurations->keys()->all(); + + (new Collection($baseConfigurations->all()))->reject( + fn ($data, $key) => \in_array($key, $excludes) + )->each(function ($data, $key) use ($config) { + $config->set($key, $data); + }); + + return $configurations->each(static function ($data, $key) use ($config, $baseConfigurations) { + foreach (static::mergeableOptions($key) as $option) { + $name = "{$key}.{$option}"; + + $config->set($name, array_merge(($baseConfigurations->get($name) ?? []), ($config->get($name) ?? []))); + } + }); }); } @@ -132,11 +169,68 @@ protected function configureDefaultDatabaseConnection(RepositoryContract $config * * @param TLaravel $app * @return string + * + * @throws \RuntimeException */ protected function getConfigurationPath(Application $app): string { - return is_dir($app->basePath('config')) + $configurationPath = is_dir($app->basePath('config')) ? $app->basePath('config') : default_skeleton_path('config'); + + if ($configurationPath === false) { + throw new RuntimeException('Unable to locate configuration path'); + } + + return $configurationPath; + } + + /** + * Get the framework default configurations. + * + * @return array + * + * @codeCoverageIgnore + */ + protected function getFrameworkDefaultConfigurations(): array + { + return (new LazyCollection(function () { + yield from $this->getConfigurationsFromPath(package_path(['vendor', 'laravel', 'framework', 'config'])); + }))->all(); + } + + /** + * Get the configurations from path. + * + * @param string $path + * @return \Generator + */ + protected function getConfigurationsFromPath(string $path): Generator + { + foreach (Finder::create()->files()->name('*.php')->in($path) as $file) { + $directory = $this->getNestedDirectory($file, $path); + + yield $directory.basename($file->getRealPath(), '.php') => $file->getRealPath(); + } + } + + /** + * Get the options within the configuration file that should be merged again. + * + * @param string $name + * @return array + */ + public static function mergeableOptions(string $name): array + { + return [ + 'auth' => ['guards', 'providers', 'passwords'], + 'broadcasting' => ['connections'], + 'cache' => ['stores'], + 'database' => ['connections'], + 'filesystems' => ['disks'], + 'logging' => ['channels'], + 'mail' => ['mailers'], + 'queue' => ['connections'], + ][$name] ?? []; } } diff --git a/src/Bootstrap/LoadConfigurationWithWorkbench.php b/src/Bootstrap/LoadConfigurationWithWorkbench.php index edf5580d0..2b19ab31e 100644 --- a/src/Bootstrap/LoadConfigurationWithWorkbench.php +++ b/src/Bootstrap/LoadConfigurationWithWorkbench.php @@ -21,7 +21,7 @@ class LoadConfigurationWithWorkbench extends LoadConfiguration * * @var bool */ - protected bool $usesWorkbenchConfigFile = false; + protected readonly bool $usesWorkbenchConfigFile; /** * Construct a new bootstrap class. @@ -49,9 +49,9 @@ public function bootstrap(Application $app): void #[\Override] protected function resolveConfigurationFile(string $path, string $key): string { - return $this->usesWorkbenchConfigFile === true && is_file(workbench_path('config', "{$key}.php")) - ? workbench_path('config', "{$key}.php") - : $path; + $config = workbench_path('config', "{$key}.php"); + + return $this->usesWorkbenchConfigFile === true && is_file($config) ? $config : $path; } /** {@inheritDoc} */ diff --git a/src/Bootstrap/LoadEnvironmentVariables.php b/src/Bootstrap/LoadEnvironmentVariables.php index 23464417c..29b59ba5b 100644 --- a/src/Bootstrap/LoadEnvironmentVariables.php +++ b/src/Bootstrap/LoadEnvironmentVariables.php @@ -16,7 +16,7 @@ final class LoadEnvironmentVariables extends \Illuminate\Foundation\Bootstrap\Lo #[\Override] protected function createDotenv($app) { - /** @phpstan-ignore method.notFound, method.notFound */ + /** @var \Illuminate\Contracts\Foundation\Application&\Illuminate\Foundation\Application $app */ if (! is_file(join_paths($app->environmentPath(), $app->environmentFile()))) { return Dotenv::create( Env::getRepository(), (string) realpath(join_paths(__DIR__, 'stubs')), '.env.testbench' diff --git a/src/Bootstrap/RegisterProviders.php b/src/Bootstrap/RegisterProviders.php index 4f0ab7911..b9f13bf13 100644 --- a/src/Bootstrap/RegisterProviders.php +++ b/src/Bootstrap/RegisterProviders.php @@ -2,37 +2,8 @@ namespace Orchestra\Testbench\Bootstrap; -use Illuminate\Contracts\Foundation\Application; - class RegisterProviders extends \Illuminate\Foundation\Bootstrap\RegisterProviders { - /** - * The service providers that should be merged before registration. - * - * @laravel-overrides - * - * @var array - */ - protected static $merge = []; - - /** - * The path to the bootstrap provider configuration file. - * - * @laravel-overrides - * - * @var string|null - */ - protected static $bootstrapProviderPath; - - /** {@inheritDoc} */ - #[\Override] - public function bootstrap(Application $app) - { - $this->mergeAdditionalProviders($app); - - parent::bootstrap($app); - } - /** * Merge additional providers for Testbench. * @@ -58,74 +29,15 @@ public static function mergeAdditionalProvidersForTestbench(array $providers): a } } - return tap( + return with( array_merge($providers, static::$merge, array_values($packageProviders ?? [])), static function ($providers) { + /** @var TProviders $providers */ static::$merge = []; static::$bootstrapProviderPath = null; - } - ); - } - /** - * Merge the additional configured providers into the configuration. - * - * @laravel-overrides - * - * @param \Illuminate\Contracts\Foundation\Application $app - */ - protected function mergeAdditionalProviders(Application $app): void - { - if (static::$bootstrapProviderPath && - file_exists(static::$bootstrapProviderPath)) { - $packageProviders = require static::$bootstrapProviderPath; - - foreach ($packageProviders as $index => $provider) { - if (! class_exists($provider)) { - unset($packageProviders[$index]); - } + return $providers; } - } - - $app->make('config')->set( - 'app.providers', - array_merge( - $app->make('config')->get('app.providers'), - static::$merge, - array_values($packageProviders ?? []), - ), ); } - - /** - * Merge the given providers into the provider configuration before registration. - * - * @laravel-overrides - * - * @param array $providers - * @param string|null $bootstrapProviderPath - * @return void - */ - public static function merge(array $providers, ?string $bootstrapProviderPath = null): void - { - static::$bootstrapProviderPath = $bootstrapProviderPath; - - static::$merge = array_values(array_filter(array_unique( - array_merge(static::$merge, $providers) - ))); - } - - /** - * Flush the bootstrapper's global state. - * - * @laravel-overrides - * - * @return void - */ - public static function flushState(): void - { - static::$bootstrapProviderPath = null; - - static::$merge = []; - } } diff --git a/src/Concerns/ApplicationTestingHooks.php b/src/Concerns/ApplicationTestingHooks.php index 107b56688..b3e0bc04c 100644 --- a/src/Concerns/ApplicationTestingHooks.php +++ b/src/Concerns/ApplicationTestingHooks.php @@ -14,6 +14,7 @@ trait ApplicationTestingHooks { use InteractsWithMockery; + use InteractsWithPest; use InteractsWithPHPUnit; use InteractsWithTestCase; @@ -50,18 +51,20 @@ trait ApplicationTestingHooks * * @var \Throwable|null */ - protected $callbackException; + protected ?Throwable $callbackException = null; /** * Indicates if we have made it through the base setUp function. * * @var bool */ - protected $setUpHasRun = false; + protected bool $setUpHasRun = false; /** * Setup the testing hooks. * + * @internal + * * @param (\Closure():(void))|null $callback * @return void */ @@ -94,6 +97,8 @@ final protected function setUpTheApplicationTestingHooks(?Closure $callback = nu /** * Teardown the testing hooks. * + * @internal + * * @param (\Closure():(void))|null $callback * @return void * @@ -130,7 +135,7 @@ final protected function tearDownTheApplicationTestingHooks(?Closure $callback = $this->afterApplicationCreatedCallbacks = []; $this->beforeApplicationDestroyedCallbacks = []; - Testbench::flushState(); + Testbench::flushState($this); if ($this->callbackException) { throw $this->callbackException; @@ -139,10 +144,14 @@ final protected function tearDownTheApplicationTestingHooks(?Closure $callback = /** * Setup parallel testing callback. + * + * @internal + * + * @return void */ protected function setUpParallelTestingCallbacks(): void { - if (class_exists(ParallelTesting::class) && $this instanceof PHPUnitTestCase) { + if ($this instanceof PHPUnitTestCase) { /** @phpstan-ignore staticMethod.notFound, argument.type */ ParallelTesting::callSetUpTestCaseCallbacks($this); } @@ -150,10 +159,14 @@ protected function setUpParallelTestingCallbacks(): void /** * Teardown parallel testing callback. + * + * @internal + * + * @return void */ protected function tearDownParallelTestingCallbacks(): void { - if (class_exists(ParallelTesting::class) && $this instanceof PHPUnitTestCase) { + if ($this instanceof PHPUnitTestCase) { /** @phpstan-ignore staticMethod.notFound, argument.type */ ParallelTesting::callTearDownTestCaseCallbacks($this); } @@ -162,6 +175,8 @@ protected function tearDownParallelTestingCallbacks(): void /** * Register a callback to be run after the application is refreshed. * + * @api + * * @param callable():void $callback * @return void */ @@ -177,6 +192,8 @@ public function afterApplicationRefreshed(callable $callback): void /** * Execute the application's post-refreshed callbacks. * + * @internal + * * @return void */ protected function callAfterApplicationRefreshedCallbacks(): void @@ -189,6 +206,8 @@ protected function callAfterApplicationRefreshedCallbacks(): void /** * Register a callback to be run after the application is created. * + * @api + * * @param callable():void $callback * @return void */ @@ -204,6 +223,8 @@ public function afterApplicationCreated(callable $callback): void /** * Execute the application's post-creation callbacks. * + * @internal + * * @return void */ protected function callAfterApplicationCreatedCallbacks(): void @@ -216,6 +237,8 @@ protected function callAfterApplicationCreatedCallbacks(): void /** * Register a callback to be run before the application is destroyed. * + * @api + * * @param callable():void $callback * @return void */ @@ -227,6 +250,8 @@ public function beforeApplicationDestroyed(callable $callback): void /** * Execute the application's pre-destruction callbacks. * + * @internal + * * @return void */ protected function callBeforeApplicationDestroyedCallbacks(): void diff --git a/src/Concerns/CreatesApplication.php b/src/Concerns/CreatesApplication.php index 9cb703e48..85a1657dd 100644 --- a/src/Concerns/CreatesApplication.php +++ b/src/Concerns/CreatesApplication.php @@ -6,10 +6,13 @@ use Illuminate\Contracts\Console\Kernel as ConsoleKernelContract; use Illuminate\Contracts\Http\Kernel as HttpKernelContract; use Illuminate\Foundation\Application; +use Illuminate\Foundation\Configuration\ApplicationBuilder; +use Illuminate\Foundation\Configuration\Middleware; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Facade; use Illuminate\Support\Facades\RateLimiter; +use Illuminate\Support\ServiceProvider; use Orchestra\Testbench\Attributes\DefineEnvironment; use Orchestra\Testbench\Attributes\RequiresEnv; use Orchestra\Testbench\Attributes\RequiresLaravel; @@ -24,13 +27,11 @@ use Orchestra\Testbench\Foundation\PackageManifest; use PHPUnit\Framework\TestCase as PHPUnitTestCase; -use function Orchestra\Testbench\after_resolving; +use function Orchestra\Sidekick\after_resolving; use function Orchestra\Testbench\default_skeleton_path; use function Orchestra\Testbench\refresh_router_lookups; /** - * @api - * * @property bool|null $enablesPackageDiscoveries * @property bool|null $loadEnvironmentVariables */ @@ -42,6 +43,8 @@ trait CreatesApplication /** * Get the application's base path. * + * @api + * * @return string */ public static function applicationBasePath() @@ -52,6 +55,8 @@ public static function applicationBasePath() /** * Ignore package discovery from. * + * @api + * * @return array */ public function ignorePackageDiscoveriesFrom() @@ -62,6 +67,8 @@ public function ignorePackageDiscoveriesFrom() /** * Get the application timezone. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return string|null */ @@ -73,6 +80,8 @@ protected function getApplicationTimezone($app) /** * Override application bindings. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return array */ @@ -99,6 +108,8 @@ final protected function resolveApplicationBindings($app): void /** * Get application aliases. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return array */ @@ -110,6 +121,8 @@ protected function getApplicationAliases($app) /** * Override application aliases. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return array */ @@ -133,7 +146,6 @@ final protected function resolveApplicationAliases($app): array ))->merge($this->getPackageAliases($app)); if (! empty($overrides = $this->overrideApplicationAliases($app))) { - /** @phpstan-ignore argument.type */ $aliases->transform(static function ($alias, $name) use ($overrides) { return with($overrides[$name] ?? $alias, static function ($alias) { return $alias !== false ? $alias : null; @@ -141,12 +153,15 @@ final protected function resolveApplicationAliases($app): array }); } + /** @var \Illuminate\Support\Collection $aliases */ return $aliases->filter()->all(); } /** * Get package aliases. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return array */ @@ -158,6 +173,8 @@ protected function getPackageAliases($app) /** * Get package bootstrapper. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return array */ @@ -169,17 +186,21 @@ protected function getPackageBootstrappers($app) /** * Get application providers. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return array */ protected function getApplicationProviders($app) { - return $app['config']['app.providers']; + return $app['config']['app.providers'] ?? ServiceProvider::defaultProviders()->toArray(); } /** * Override application aliases. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return array */ @@ -198,12 +219,12 @@ protected function overrideApplicationProviders($app) */ final protected function resolveApplicationProviders($app): array { + /** @var \Illuminate\Support\Collection $providers */ $providers = (new Collection( RegisterProviders::mergeAdditionalProvidersForTestbench($this->getApplicationProviders($app)) ))->merge($this->getPackageProviders($app)); if (! empty($overrides = $this->overrideApplicationProviders($app))) { - /** @phpstan-ignore argument.type */ $providers->transform(static function (string $provider) use ($overrides) { return with($overrides[$provider] ?? $provider, static function ($provider) { return $provider !== false ? $provider : null; @@ -211,12 +232,15 @@ final protected function resolveApplicationProviders($app): array }); } + /** @phpstan-ignore return.type */ return $providers->filter()->values()->all(); } /** * Get package providers. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return array */ @@ -237,26 +261,10 @@ protected function getApplicationBasePath() return static::applicationBasePath(); } - /** - * Resolve the application's base path (deprecated). - * - * @api - * - * @return string|null - * - * @deprecated 6.22.0 Use `applicationBasePath()` static method instead. - */ - protected function getBasePath() - { - trigger_deprecation('orchestra/testbench-core', '6.22.0', 'Use `%s` static method instead.', 'applicationBasePath()'); - - return static::applicationBasePath(); - } - /** * Creates the application. * - * Needs to be implemented by subclasses. + * @internal * * @return \Illuminate\Foundation\Application */ @@ -274,8 +282,10 @@ public function createApplication() $this->resolveApplicationEnvironmentVariables($app); $this->resolveApplicationConfiguration($app); $this->resolveApplicationHttpKernel($app); + $this->resolveApplicationHttpMiddlewares($app); $this->resolveApplicationConsoleKernel($app); $this->resolveApplicationBootstrappers($app); + $this->refreshApplicationRouteNameLookups($app); return $app; } @@ -283,20 +293,38 @@ public function createApplication() /** * Create the default application implementation. * + * @internal + * * @return \Illuminate\Foundation\Application */ final protected function resolveDefaultApplication() { - return new Application($this->getApplicationBasePath()); + return (new ApplicationBuilder(new Application($this->getApplicationBasePath()))) + ->withProviders() + ->withMiddleware(static function ($middleware) { + // + }) + ->withCommands() + ->create(); } /** * Resolve application implementation. * + * @api + * * @return \Illuminate\Foundation\Application */ protected function resolveApplication() { + static::$cacheApplicationBootstrapFile ??= $this->getApplicationBootstrapFile('app.php'); + + if (\is_string(static::$cacheApplicationBootstrapFile)) { + $APP_BASE_PATH = $this->getApplicationBasePath(); + + return require static::$cacheApplicationBootstrapFile; + } + return $this->resolveDefaultApplication(); } @@ -333,6 +361,8 @@ protected function resolveApplicationFacades($app) /** * Resolve application core environment variables implementation. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return void */ @@ -365,6 +395,8 @@ protected function resolveApplicationEnvironmentVariables($app) /** * Resolve application core configuration implementation. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return void */ @@ -388,9 +420,7 @@ protected function resolveApplicationConfiguration($app) tap($app['config'], function ($config) use ($app) { if (! $app->bound('env')) { - $app->detectEnvironment(static function () use ($config) { - return $config->get('app.env', 'workbench'); - }); + $app->detectEnvironment(static fn () => $config->get('app.env', 'workbench')); } if (\is_string($bootstrapProviderPath = $this->getApplicationBootstrapFile('providers.php'))) { @@ -412,21 +442,23 @@ protected function resolveApplicationConfiguration($app) /** * Resolve application core implementation. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return void */ protected function resolveApplicationCore($app) { if ($this->isRunningTestCase()) { - $app->detectEnvironment(static function () { - return 'testing'; - }); + $app->detectEnvironment(static fn () => 'testing'); } } /** * Resolve application Console Kernel implementation. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return void */ @@ -438,6 +470,8 @@ protected function resolveApplicationConsoleKernel($app) /** * Resolve application HTTP Kernel implementation. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return void */ @@ -446,9 +480,31 @@ protected function resolveApplicationHttpKernel($app) $app->singleton(HttpKernelContract::class, $this->applicationHttpKernelUsingWorkbench($app)); } + /** + * Resolve application HTTP default middlewares. + * + * @internal + * + * @param \Illuminate\Foundation\Application $app + * @return void + */ + protected function resolveApplicationHttpMiddlewares($app) + { + after_resolving($app, HttpKernelContract::class, function ($kernel, $app) { + /** @var \Illuminate\Foundation\Http\Kernel $kernel */ + $middleware = new Middleware; + + $kernel->setGlobalMiddleware($middleware->getGlobalMiddleware()); + $kernel->setMiddlewareGroups($middleware->getMiddlewareGroups()); + $kernel->setMiddlewareAliases($middleware->getMiddlewareAliases()); + }); + } + /** * Resolve application HTTP exception handler. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return void */ @@ -460,16 +516,18 @@ protected function resolveApplicationExceptionHandler($app) /** * Resolve application bootstrapper. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return void */ protected function resolveApplicationBootstrappers($app) { - if ($this instanceof PHPUnitTestCase) { - $app->make('Orchestra\Testbench\Bootstrap\HandleExceptions', ['testbench' => $this])->bootstrap($app); - } else { - $app->make('Illuminate\Foundation\Bootstrap\HandleExceptions')->bootstrap($app); - } + $app->make( + $this->isRunningTestCase() + ? 'Orchestra\Testbench\Bootstrap\HandleExceptions' + : 'Illuminate\Foundation\Bootstrap\HandleExceptions' + )->bootstrap($app); $app->make('Illuminate\Foundation\Bootstrap\RegisterFacades')->bootstrap($app); $app->make('Illuminate\Foundation\Bootstrap\SetRequestForConsole')->bootstrap($app); @@ -485,14 +543,11 @@ protected function resolveApplicationBootstrappers($app) $this->defineEnvironment($app); $this->getEnvironmentSetUp($app); }, - annotation: function () use ($app) { - $this->parseTestMethodAnnotations($app, 'environment-setup'); /** @phpstan-ignore method.notFound */ - $this->parseTestMethodAnnotations($app, 'define-env'); /** @phpstan-ignore method.notFound */ - }, attribute: function () use ($app) { $this->parseTestMethodAttributes($app, WithImmutableDates::class); /** @phpstan-ignore method.notFound */ $this->parseTestMethodAttributes($app, DefineEnvironment::class); /** @phpstan-ignore method.notFound */ - } + }, + pest: fn () => $this->defineEnvironmentUsingPest($app), /** @phpstan-ignore method.notFound */ ); $this->resolveApplicationRateLimiting($app); @@ -510,17 +565,17 @@ protected function resolveApplicationBootstrappers($app) $app->make('Illuminate\Foundation\Bootstrap\BootProviders')->bootstrap($app); foreach ($this->getPackageBootstrappers($app) as $bootstrap) { - $app->make($bootstrap)->bootstrap($app); + $app->make($bootstrap)->bootstrap($app); /** @phpstan-ignore method.notFound */ } $app->make(ConsoleKernelContract::class)->bootstrap(); - - $this->refreshApplicationRouteNameLookups($app); } /** * Refresh route name lookup for the application. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return void */ @@ -539,14 +594,18 @@ final protected function refreshApplicationRouteNameLookups($app): void /** * Resolve application rate limiting configuration. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return void */ protected function resolveApplicationRateLimiting($app) { - RateLimiter::for( - 'api', static fn (Request $request) => Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()) - ); + after_resolving($app, 'cache.store', function () { + RateLimiter::for( + 'api', static fn (Request $request) => Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()) + ); + }); } /** @@ -565,6 +624,8 @@ final protected function resetApplicationArtisanCommands($app): void /** * Define environment setup. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return void */ @@ -576,8 +637,12 @@ protected function defineEnvironment($app) /** * Define environment setup. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return void + * + * @deprecated 10.0 Use "defineEnvironment()" instead. */ protected function getEnvironmentSetUp($app) { diff --git a/src/Concerns/Database/HandlesConnections.php b/src/Concerns/Database/HandlesConnections.php deleted file mode 100644 index b25c387d9..000000000 --- a/src/Concerns/Database/HandlesConnections.php +++ /dev/null @@ -1,15 +0,0 @@ -delete( - (new Collection($filesystem->glob(database_path('database.sqlite.backup-*')))) - ->filter(static function ($file) use ($filesystem) { - return $filesystem->exists($file); - })->all() + (new Collection([ + ...$filesystem->glob(database_path('database.sqlite.backup-*')), + ...$filesystem->glob(database_path('database.sqlite-*')), + ]))->filter(static fn ($file) => $filesystem->exists($file)) + ->all() ); } } diff --git a/src/Concerns/HandlesAnnotations.php b/src/Concerns/HandlesAnnotations.php deleted file mode 100644 index 7afeb8b64..000000000 --- a/src/Concerns/HandlesAnnotations.php +++ /dev/null @@ -1,42 +0,0 @@ -resolvePhpUnitAnnotations() - ->lazy() - ->filter(static function ($actions, string $key) use ($name) { - return $key === $name && ! empty($actions); - })->flatten() - ->filter(fn ($method) => \is_string($method) && method_exists($this, $method)) - ->each($callback ?? function ($method) use ($app) { - $this->{$method}($app); - }); - } - - /** - * Resolve PHPUnit method annotations. - * - * @phpunit-overrides - * - * @return \Illuminate\Support\Collection - */ - abstract protected function resolvePhpUnitAnnotations(): Collection; -} diff --git a/src/Concerns/HandlesAssertions.php b/src/Concerns/HandlesAssertions.php index a89b43915..3a6ed92cf 100644 --- a/src/Concerns/HandlesAssertions.php +++ b/src/Concerns/HandlesAssertions.php @@ -7,7 +7,7 @@ trait HandlesAssertions /** * Mark the test as skipped when condition is not equivalent to true. * - * @param (\Closure($this): bool)|bool|null $condition + * @param (\Closure(): bool)|bool $condition * @param string $message * @return void * @@ -23,7 +23,7 @@ protected function markTestSkippedUnless($condition, string $message): void /** * Mark the test as skipped when condition is equivalent to true. * - * @param (\Closure($this): bool)|bool|null $condition + * @param (\Closure(): bool)|bool $condition * @param string $message * @return void * @@ -31,6 +31,7 @@ protected function markTestSkippedUnless($condition, string $message): void */ protected function markTestSkippedWhen($condition, string $message): void { + /** @phpstan-ignore argument.type */ if (value($condition)) { $this->markTestSkipped($message); } diff --git a/src/Concerns/HandlesAttributes.php b/src/Concerns/HandlesAttributes.php index 5795d0abb..05af6173e 100644 --- a/src/Concerns/HandlesAttributes.php +++ b/src/Concerns/HandlesAttributes.php @@ -17,6 +17,8 @@ trait HandlesAttributes /** * Parse test method attributes. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @param class-string $attribute * @return \Orchestra\Testbench\Features\FeaturesCollection @@ -25,9 +27,8 @@ protected function parseTestMethodAttributes($app, string $attribute): Collectio { /** @var \Illuminate\Support\Collection $attributes */ $attributes = $this->resolvePhpUnitAttributes() - ->filter(static function ($attributes, string $key) use ($attribute) { - return $key === $attribute && ! empty($attributes); - })->flatten() + ->filter(static fn ($attributes, string $key) => $key === $attribute && ! empty($attributes)) + ->flatten() ->map(function ($instance) use ($app) { if ($instance instanceof InvokableContract) { return $instance($app); diff --git a/src/Concerns/HandlesDatabases.php b/src/Concerns/HandlesDatabases.php index fd04bab4f..003f1d7d2 100644 --- a/src/Concerns/HandlesDatabases.php +++ b/src/Concerns/HandlesDatabases.php @@ -5,6 +5,7 @@ use Closure; use Illuminate\Database\Events\DatabaseRefreshed; use Orchestra\Testbench\Attributes\DefineDatabase; +use Orchestra\Testbench\Attributes\RequiresDatabase; use Orchestra\Testbench\Attributes\WithMigration; use Orchestra\Testbench\Features\TestingFeature; @@ -18,12 +19,19 @@ trait HandlesDatabases /** * Setup database requirements. * + * @internal + * * @param \Closure():void $callback */ protected function setUpDatabaseRequirements(Closure $callback): void { $app = laravel_or_fail($this->app); + TestingFeature::run( + testCase: $this, + attribute: fn () => $this->parseTestMethodAttributes($app, RequiresDatabase::class), + ); + $app['events']->listen(DatabaseRefreshed::class, function () { $this->defineDatabaseMigrationsAfterDatabaseRefreshed(); }); @@ -43,20 +51,29 @@ protected function setUpDatabaseRequirements(Closure $callback): void $this->defineDatabaseMigrations(); $this->beforeApplicationDestroyed(fn () => $this->destroyDatabaseMigrations()); }, - annotation: fn () => $this->parseTestMethodAnnotations($app, 'define-db'), - attribute: fn () => $this->parseTestMethodAttributes($app, DefineDatabase::class) + attribute: fn () => $this->parseTestMethodAttributes($app, DefineDatabase::class), + pest: function () { + $this->defineDatabaseMigrationsUsingPest(); /** @phpstan-ignore method.notFound */ + $this->beforeApplicationDestroyed(fn () => $this->destroyDatabaseMigrationsUsingPest()); /** @phpstan-ignore method.notFound */ + }, )->get('attribute'); $callback(); $attributeCallbacks->handle(); - $this->defineDatabaseSeeders(); + TestingFeature::run( + testCase: $this, + default: fn () => $this->defineDatabaseSeeders(), + pest: fn () => $this->defineDatabaseSeedersUsingPest(), /** @phpstan-ignore method.notFound */ + ); } /** * Determine if using in-memory SQLite database connection * + * @api + * * @param string|null $connection * @return bool */ @@ -73,12 +90,20 @@ protected function usesSqliteInMemoryDatabaseConnection(?string $connection = nu /** @var array{driver: string, database: string}|null $database */ $database = $config->get("database.connections.{$connection}"); - return ! \is_null($database) && $database['driver'] === 'sqlite' && $database['database'] == ':memory:'; + if (\is_null($database) || $database['driver'] !== 'sqlite') { + return false; + } + + return $database['database'] == ':memory:' + || str_contains($database['database'], '?mode=memory') + || str_contains($database['database'], '&mode=memory'); } /** * Define database migrations. * + * @api + * * @return void */ protected function defineDatabaseMigrations() @@ -89,6 +114,8 @@ protected function defineDatabaseMigrations() /** * Define database migrations after database refreshed. * + * @api + * * @return void */ protected function defineDatabaseMigrationsAfterDatabaseRefreshed() @@ -99,6 +126,8 @@ protected function defineDatabaseMigrationsAfterDatabaseRefreshed() /** * Destroy database migrations. * + * @api + * * @return void */ protected function destroyDatabaseMigrations() @@ -109,6 +138,8 @@ protected function destroyDatabaseMigrations() /** * Define database seeders. * + * @api + * * @return void */ protected function defineDatabaseSeeders() diff --git a/src/Concerns/HandlesRoutes.php b/src/Concerns/HandlesRoutes.php index 5d99b3048..b91d74959 100644 --- a/src/Concerns/HandlesRoutes.php +++ b/src/Concerns/HandlesRoutes.php @@ -10,16 +10,12 @@ use Orchestra\Testbench\Attributes\DefineRoute; use Orchestra\Testbench\Attributes\UsesVendor; use Orchestra\Testbench\Features\TestingFeature; -use Orchestra\Testbench\Foundation\Application; use Orchestra\Testbench\Foundation\Bootstrap\SyncTestbenchCachedRoutes; use function Orchestra\Sidekick\Filesystem\join_paths; use function Orchestra\Testbench\refresh_router_lookups; use function Orchestra\Testbench\remote; -/** - * @internal - */ trait HandlesRoutes { use InteractsWithPHPUnit; @@ -35,6 +31,8 @@ trait HandlesRoutes /** * Setup routes requirements. * + * @internal + * * @param \Illuminate\Foundation\Application $app */ protected function setUpApplicationRoutes($app): void @@ -54,10 +52,13 @@ protected function setUpApplicationRoutes($app): void $router->middleware('web') ->group(fn ($router) => $this->defineWebRoutes($router)); }, - annotation: fn () => $this->parseTestMethodAnnotations($app, 'define-route', function ($method) use ($router) { - $this->{$method}($router); - }), - attribute: fn () => $this->parseTestMethodAttributes($app, DefineRoute::class) + attribute: fn () => $this->parseTestMethodAttributes($app, DefineRoute::class), + pest: function () use ($router) { + $this->defineRoutesUsingPest($router); // @phpstan-ignore method.notFound + + $router->middleware('web') + ->group(fn ($router) => $this->defineWebRoutesUsingPest($router)); /** @phpstan-ignore method.notFound */ + } ); refresh_router_lookups($router); @@ -66,6 +67,8 @@ protected function setUpApplicationRoutes($app): void /** * Define routes setup. * + * @api + * * @param \Illuminate\Routing\Router $router * @return void */ @@ -77,6 +80,8 @@ protected function defineRoutes($router) /** * Define web routes setup. * + * @api + * * @param \Illuminate\Routing\Router $router * @return void */ @@ -88,6 +93,8 @@ protected function defineWebRoutes($router) /** * Define stash routes setup. * + * @api + * * @param \Closure|string $route * @return void */ @@ -99,6 +106,8 @@ protected function defineStashRoutes(Closure|string $route): void /** * Define cache routes setup. * + * @api + * * @param \Closure|string $route * @param bool $cached * @return void @@ -120,14 +129,16 @@ protected function defineCacheRoutes(Closure|string $route, bool $cached = true) $time = time(); $basePath = static::applicationBasePath(); - $bootstrapPath = join_paths($basePath, 'bootstrap'); + $bootstrapPath = $files->isDirectory(join_paths($basePath, '.laravel')) + ? join_paths($basePath, '.laravel') + : join_paths($basePath, 'bootstrap'); if ($route instanceof Closure) { $cached = false; /** @var string $serializeRoute */ $serializeRoute = serialize(SerializableClosure::unsigned($route)); $stub = $files->get(join_paths(__DIR__, 'stubs', 'routes.stub')); - $route = str_replace('{{routes}}', (string) json_encode($serializeRoute), $stub); + $route = str_replace('{{routes}}', var_export($serializeRoute, true), $stub); } $files->put( @@ -149,6 +160,11 @@ protected function defineCacheRoutes(Closure|string $route, bool $cached = true) /** * Require application cached routes. + * + * @internal + * + * @param \Illuminate\Filesystem\Filesystem $files + * @return void */ protected function requireApplicationCachedRoutes(Filesystem $files, bool $cached): void { diff --git a/src/Concerns/HandlesTestingFeature.php b/src/Concerns/HandlesTestingFeature.php deleted file mode 100644 index 3ec13da1b..000000000 --- a/src/Concerns/HandlesTestingFeature.php +++ /dev/null @@ -1,37 +0,0 @@ -cachedTestMigratorProcessors) > 0 && static::usesRefreshDatabaseTestingConcern()) { + if ( + (\count($this->cachedTestMigratorProcessors) > 0 && static::usesRefreshDatabaseTestingConcern()) + || ($this->usesSqliteInMemoryDatabaseConnection() && ! empty(RefreshDatabaseState::$inMemoryConnections)) + ) { ResetRefreshDatabaseState::run(); } @@ -57,10 +60,12 @@ protected function tearDownInteractsWithMigrations(): void /** * Define hooks to migrate the database before and after each test. * + * @api + * * @param array|string $paths * @return void */ - protected function loadMigrationsFrom($paths): void + protected function loadMigrationsFrom(array|string $paths): void { $app = laravel_or_fail($this->app); @@ -70,28 +75,13 @@ protected function loadMigrationsFrom($paths): void && RefreshDatabaseState::$migrated === false && RefreshDatabaseState::$lazilyRefreshed === false ) { - /** @var array|string $paths */ + /** @var list|string $paths */ load_migration_paths($app, $paths); return; } /** @var array|string $paths */ - $this->loadMigrationsWithoutRollbackFrom($paths); - } - - /** - * Define hooks to migrate the database before each test without rollback after. - * - * @param array|string $paths - * @return void - * - * @deprecated - */ - protected function loadMigrationsWithoutRollbackFrom($paths): void - { - $app = laravel_or_fail($this->app); - $migrator = new MigrateProcessor($this, $this->resolvePackageMigrationsOptions($paths)); $migrator->up(); @@ -103,12 +93,14 @@ protected function loadMigrationsWithoutRollbackFrom($paths): void /** * Resolve Package Migrations Artisan command options. * + * @internal + * * @param array|string $paths * @return array * * @throws \InvalidArgumentException */ - protected function resolvePackageMigrationsOptions($paths = []): array + protected function resolvePackageMigrationsOptions(array|string $paths = []): array { $options = \is_array($paths) ? $paths : ['--path' => $paths]; @@ -124,23 +116,12 @@ protected function resolvePackageMigrationsOptions($paths = []): array /** * Migrate Laravel's default migrations. * - * @param array|string $database - * @return void - */ - protected function loadLaravelMigrations($database = []): void - { - $this->loadLaravelMigrationsWithoutRollback($database); - } - - /** - * Migrate Laravel's default migrations without rollback. + * @api * * @param array|string $database * @return void - * - * @deprecated */ - protected function loadLaravelMigrationsWithoutRollback($database = []): void + protected function loadLaravelMigrations(array|string $database = []): void { $app = laravel_or_fail($this->app); @@ -159,23 +140,12 @@ protected function loadLaravelMigrationsWithoutRollback($database = []): void /** * Migrate all Laravel's migrations. * - * @param array|string $database - * @return void - */ - protected function runLaravelMigrations($database = []): void - { - $this->runLaravelMigrationsWithoutRollback($database); - } - - /** - * Migrate all Laravel's migrations without rollback. + * @api * * @param array|string $database * @return void - * - * @deprecated */ - protected function runLaravelMigrationsWithoutRollback($database = []): void + protected function runLaravelMigrations(array|string $database = []): void { $app = laravel_or_fail($this->app); @@ -190,10 +160,12 @@ protected function runLaravelMigrationsWithoutRollback($database = []): void /** * Resolve Laravel Migrations Artisan command options. * + * @internal + * * @param array|string $database * @return array */ - protected function resolveLaravelMigrationsOptions($database = []): array + protected function resolveLaravelMigrationsOptions(array|string $database = []): array { $options = \is_array($database) ? $database : ['--database' => $database]; diff --git a/src/Concerns/InteractsWithMockery.php b/src/Concerns/InteractsWithMockery.php index d1a3b78a8..0ecfe18fe 100644 --- a/src/Concerns/InteractsWithMockery.php +++ b/src/Concerns/InteractsWithMockery.php @@ -16,7 +16,10 @@ protected function tearDownTheTestEnvironmentUsingMockery(): void { if (class_exists(Mockery::class) && $this instanceof PHPUnitTestCase) { if ($container = Mockery::getContainer()) { - $this->addToAssertionCount($container->mockery_getExpectationCount()); + /** @var int<0, max> $expectationCount */ + $expectationCount = $container->mockery_getExpectationCount(); + + $this->addToAssertionCount($expectationCount); } Mockery::close(); diff --git a/src/Concerns/InteractsWithPHPUnit.php b/src/Concerns/InteractsWithPHPUnit.php index eb1bf3257..c175a558b 100644 --- a/src/Concerns/InteractsWithPHPUnit.php +++ b/src/Concerns/InteractsWithPHPUnit.php @@ -2,10 +2,11 @@ namespace Orchestra\Testbench\Concerns; +use Closure; use Illuminate\Support\Collection; use Orchestra\Testbench\PHPUnit\AttributeParser; use PHPUnit\Framework\TestCase as PHPUnitTestCase; -use PHPUnit\Util\Annotation\Registry as PHPUnit9Registry; +use PHPUnit\Metadata\Annotation\Parser\Registry as PHPUnitRegistry; use ReflectionClass; /** @@ -17,6 +18,20 @@ trait InteractsWithPHPUnit { use InteractsWithTestCase; + /** + * The cached test case setUp resolver. + * + * @var (\Closure(\Closure):(void))|null + */ + protected ?Closure $testCaseSetUpCallback = null; + + /** + * The cached test case tearDown resolver. + * + * @var (\Closure(\Closure):(void))|null + */ + protected ?Closure $testCaseTearDownCallback = null; + /** * The cached class attributes for test case. * @@ -38,6 +53,8 @@ trait InteractsWithPHPUnit /** * Determine if the trait is used within testing. * + * @api + * * @return bool */ public function isRunningTestCase(): bool @@ -80,38 +97,14 @@ public function resolvePhpUnitTestMethodName(): ?string return null; } - return $this->getName(false); - } - - /** - * Resolve PHPUnit method annotations. - * - * @phpunit-overrides - * - * @return \Illuminate\Support\Collection - */ - protected function resolvePhpUnitAnnotations(): Collection - { - $className = $this->resolvePhpUnitTestClassName(); - $methodName = $this->resolvePhpUnitTestMethodName(); - - if (\is_null($className) || \is_null($methodName)) { - return new Collection; - } - - /** @var array $annotations */ - $annotations = rescue( - fn () => PHPUnit9Registry::getInstance()->forMethod($className, $methodName)->symbolAnnotations(), - [], - false - ); - - return new Collection($annotations); + return $this->name(); } /** * Resolve PHPUnit method attributes. * + * @internal + * * @phpunit-overrides * * @return \Illuminate\Support\Collection> @@ -133,6 +126,8 @@ protected function resolvePhpUnitAttributes(): Collection /** * Resolve PHPUnit method attributes for specific method. * + * @internal + * * @phpunit-overrides * * @param class-string $className @@ -144,15 +139,15 @@ protected function resolvePhpUnitAttributes(): Collection protected static function resolvePhpUnitAttributesForMethod(string $className, ?string $methodName = null): Collection { if (! isset(static::$cachedTestCaseClassAttributes[$className])) { - static::$cachedTestCaseClassAttributes[$className] = rescue(static function () use ($className) { - return AttributeParser::forClass($className); - }, [], false); + static::$cachedTestCaseClassAttributes[$className] = rescue( + static fn () => AttributeParser::forClass($className), [], false + ); } if (! \is_null($methodName) && ! isset(static::$cachedTestCaseMethodAttributes["{$className}:{$methodName}"])) { - static::$cachedTestCaseMethodAttributes["{$className}:{$methodName}"] = rescue(static function () use ($className, $methodName) { - return AttributeParser::forMethod($className, $methodName); - }, [], false); + static::$cachedTestCaseMethodAttributes["{$className}:{$methodName}"] = rescue( + static fn () => AttributeParser::forMethod($className, $methodName), [], false + ); } /** @var \Illuminate\Support\Collection, array> $attributes */ @@ -173,9 +168,41 @@ protected static function resolvePhpUnitAttributesForMethod(string $className, ? return $attributes; } + /** + * Define the setUp environment using callback. + * + * @internal + * + * @param \Closure(\Closure):void $setUp + * @return void + * + * @codeCoverageIgnore + */ + public function setUpTheEnvironmentUsing(Closure $setUp): void + { + $this->testCaseSetUpCallback = $setUp; + } + + /** + * Define the tearDown environment using callback. + * + * @internal + * + * @param \Closure(\Closure):void $tearDown + * @return void + * + * @codeCoverageIgnore + */ + public function tearDownTheEnvironmentUsing(Closure $tearDown): void + { + $this->testCaseTearDownCallback = $tearDown; + } + /** * Prepare the testing environment before the running the test case. * + * @internal + * * @return void * * @codeCoverageIgnore @@ -188,6 +215,8 @@ public static function setUpBeforeClassUsingPHPUnit(): void /** * Clean up the testing environment before the next test case. * + * @internal + * * @return void * * @codeCoverageIgnore @@ -198,9 +227,13 @@ public static function tearDownAfterClassUsingPHPUnit(): void static::$cachedTestCaseClassAttributes = []; static::$cachedTestCaseMethodAttributes = []; - (function () { - $this->classDocBlocks = []; - $this->methodDocBlocks = []; - })->call(PHPUnit9Registry::getInstance()); + if (class_exists(PHPUnitRegistry::class)) { + $registry = PHPUnitRegistry::getInstance(); + + (function () { + $this->classDocBlocks = []; + $this->methodDocBlocks = []; + })->call($registry); + } } } diff --git a/src/Concerns/InteractsWithPest.php b/src/Concerns/InteractsWithPest.php new file mode 100644 index 000000000..3132c5284 --- /dev/null +++ b/src/Concerns/InteractsWithPest.php @@ -0,0 +1,34 @@ +isRunningTestCase() && static::isRunningViaPestPrinter($this); + } + + /** + * Determine if the object implements Pest test runner. + * + * @return bool + */ + protected static function isRunningViaPestPrinter(object|string $object): bool + { + /** @phpstan-ignore-next-line */ + return isset(class_implements($object, false)[HasPrintableTestCaseName::class]); + } +} diff --git a/src/Concerns/InteractsWithPublishedFiles.php b/src/Concerns/InteractsWithPublishedFiles.php index 5ce68d55a..5b398687d 100644 --- a/src/Concerns/InteractsWithPublishedFiles.php +++ b/src/Concerns/InteractsWithPublishedFiles.php @@ -4,6 +4,8 @@ use Illuminate\Support\Collection; +use function Orchestra\Sidekick\Filesystem\join_paths; + /** * @internal */ @@ -25,6 +27,10 @@ trait InteractsWithPublishedFiles /** * Setup Interacts with Published Files environment. + * + * @internal + * + * @return void */ protected function setUpInteractsWithPublishedFiles(): void { @@ -36,6 +42,10 @@ protected function setUpInteractsWithPublishedFiles(): void /** * Teardown Interacts with Published Files environment. + * + * @internal + * + * @return void */ protected function tearDownInteractsWithPublishedFiles(): void { @@ -65,7 +75,10 @@ protected function cacheExistingMigrationsFiles() /** * Assert file does contains data. * + * @api + * * @param array $contains + * @return void */ protected function assertFileContains(array $contains, string $file, string $message = ''): void { @@ -83,7 +96,10 @@ protected function assertFileContains(array $contains, string $file, string $mes /** * Assert file doesn't contains data. * + * @api + * * @param array $contains + * @return void */ protected function assertFileDoesNotContains(array $contains, string $file, string $message = ''): void { @@ -101,7 +117,10 @@ protected function assertFileDoesNotContains(array $contains, string $file, stri /** * Assert file doesn't contains data. * + * @api + * * @param array $contains + * @return void */ protected function assertFileNotContains(array $contains, string $file, string $message = ''): void { @@ -111,7 +130,10 @@ protected function assertFileNotContains(array $contains, string $file, string $ /** * Assert file does contains data. * + * @api + * * @param array $contains + * @return void */ protected function assertMigrationFileContains(array $contains, string $file, string $message = '', ?string $directory = null): void { @@ -129,7 +151,10 @@ protected function assertMigrationFileContains(array $contains, string $file, st /** * Assert file doesn't contains data. * + * @api + * * @param array $contains + * @return void */ protected function assertMigrationFileDoesNotContains(array $contains, string $file, string $message = '', ?string $directory = null): void { @@ -147,7 +172,10 @@ protected function assertMigrationFileDoesNotContains(array $contains, string $f /** * Assert file doesn't contains data. * + * @api + * * @param array $contains + * @return void */ protected function assertMigrationFileNotContains(array $contains, string $file, string $message = '', ?string $directory = null): void { @@ -156,6 +184,11 @@ protected function assertMigrationFileNotContains(array $contains, string $file, /** * Assert filename exists. + * + * @api + * + * @param string $file + * @return void */ protected function assertFilenameExists(string $file): void { @@ -166,6 +199,11 @@ protected function assertFilenameExists(string $file): void /** * Assert filename not exists. + * + * @api + * + * @param string $file + * @return void */ protected function assertFilenameDoesNotExists(string $file): void { @@ -176,6 +214,11 @@ protected function assertFilenameDoesNotExists(string $file): void /** * Assert filename not exists. + * + * @api + * + * @param string $file + * @return void */ protected function assertFilenameNotExists(string $file): void { @@ -184,6 +227,12 @@ protected function assertFilenameNotExists(string $file): void /** * Assert migration filename exists. + * + * @api + * + * @param string $file + * @param string|null $directory + * @return void */ protected function assertMigrationFileExists(string $file, ?string $directory = null): void { @@ -194,6 +243,12 @@ protected function assertMigrationFileExists(string $file, ?string $directory = /** * Assert migration filename not exists. + * + * @api + * + * @param string $file + * @param string|null $directory + * @return void */ protected function assertMigrationFileDoesNotExists(string $file, ?string $directory = null): void { @@ -204,6 +259,12 @@ protected function assertMigrationFileDoesNotExists(string $file, ?string $direc /** * Assert migration filename not exists. + * + * @api + * + * @param string $file + * @param string|null $directory + * @return void */ protected function assertMigrationFileNotExists(string $file, ?string $directory = null): void { @@ -212,6 +273,10 @@ protected function assertMigrationFileNotExists(string $file, ?string $directory /** * Removes generated files. + * + * @internal + * + * @return void */ protected function cleanUpPublishedFiles(): void { @@ -221,14 +286,19 @@ protected function cleanUpPublishedFiles(): void ->map(fn ($file) => str_contains($file, '*') ? [...$this->app['files']->glob($file)] : $file) ->flatten() ->filter(fn ($file) => $this->app['files']->exists($file)) - ->reject(static function ($file) { - return str_ends_with($file, '.gitkeep') || str_ends_with($file, '.gitignore'); - })->all() + ->reject(static fn ($file) => str_ends_with($file, '.gitkeep') || str_ends_with($file, '.gitignore')) + ->all() ); } /** * Removes generated migration files. + * + * @api + * + * @param string $file + * @param string|null $directory + * @return void */ protected function findFirstPublishedMigrationFile(string $filename, ?string $directory = null): ?string { @@ -236,11 +306,15 @@ protected function findFirstPublishedMigrationFile(string $filename, ?string $di ? $this->app->basePath($directory) : $this->app->databasePath('migrations'); - return $this->app['files']->glob("{$migrationPath}/*{$filename}")[0] ?? null; + return $this->app['files']->glob(join_paths($migrationPath, "*{$filename}"))[0] ?? null; } /** * Removes generated migration files. + * + * @internal + * + * @return void */ protected function cleanUpPublishedMigrationFiles(): void { diff --git a/src/Concerns/InteractsWithTestCase.php b/src/Concerns/InteractsWithTestCase.php index fe6c9fa42..030a27e96 100644 --- a/src/Concerns/InteractsWithTestCase.php +++ b/src/Concerns/InteractsWithTestCase.php @@ -16,13 +16,18 @@ use function Orchestra\Testbench\laravel_or_fail; /** - * @internal - * * @phpstan-import-type TTestingFeature from \Orchestra\Testbench\PHPUnit\AttributeParser * @phpstan-import-type TAttributes from \Orchestra\Testbench\PHPUnit\AttributeParser */ trait InteractsWithTestCase { + /** + * The cached application bootstrap file. + * + * @var string|null + */ + protected static string|bool|null $cacheApplicationBootstrapFile = null; + /** * The cached uses for test case. * @@ -51,6 +56,8 @@ trait InteractsWithTestCase /** * Determine if the trait is using given trait (or default to \Orchestra\Testbench\Concerns\Testing trait). * + * @api + * * @param class-string|null $trait * @return bool */ @@ -72,6 +79,8 @@ public static function usesRefreshDatabaseTestingConcern(): bool /** * Define or get the cached uses for test case. * + * @internal + * * @return array */ public static function cachedUsesForTestCase(): array @@ -89,6 +98,8 @@ public static function cachedUsesForTestCase(): array /** * Uses testing feature (attribute) on the current test. * + * @api + * * @param object $attribute * @param int $flag * @@ -137,6 +148,8 @@ abstract protected static function resolvePhpUnitAttributesForMethod(string $cla /** * Prepare the testing environment before the running the test case. * + * @internal + * * @return void */ protected function setUpTheTestEnvironmentUsingTestCase(): void @@ -154,6 +167,8 @@ protected function setUpTheTestEnvironmentUsingTestCase(): void /** * Prepare the testing environment before the running the test case. * + * @internal + * * @return void */ protected function tearDownTheTestEnvironmentUsingTestCase(): void @@ -173,6 +188,8 @@ protected function tearDownTheTestEnvironmentUsingTestCase(): void /** * Prepare the testing environment before the running the test case. * + * @internal + * * @return void * * @codeCoverageIgnore @@ -195,6 +212,8 @@ public static function setUpBeforeClassUsingTestCase(): void /** * Clean up the testing environment before the next test case. * + * @internal + * * @return void * * @codeCoverageIgnore @@ -208,8 +227,7 @@ public static function tearDownAfterClassUsingTestCase(): void $instance->afterAll(); }); - /** @phpstan-ignore-next-line */ - static::$latestResponse = null; static::$testCaseTestingFeatures = []; + static::$cacheApplicationBootstrapFile = null; } } diff --git a/src/Concerns/InteractsWithWorkbench.php b/src/Concerns/InteractsWithWorkbench.php index 679ec794e..f9c38c379 100644 --- a/src/Concerns/InteractsWithWorkbench.php +++ b/src/Concerns/InteractsWithWorkbench.php @@ -10,12 +10,15 @@ */ trait InteractsWithWorkbench { + use InteractsWithPest; use InteractsWithPHPUnit; use InteractsWithTestCase; /** * Get Application's base path. * + * @internal + * * @return string|null */ public static function applicationBasePathUsingWorkbench(): ?string @@ -30,6 +33,8 @@ public static function applicationBasePathUsingWorkbench(): ?string /** * Ignore package discovery from. * + * @internal + * * @return array|null */ public function ignorePackageDiscoveriesFromUsingWorkbench(): ?array @@ -46,6 +51,8 @@ public function ignorePackageDiscoveriesFromUsingWorkbench(): ?array /** * Get package bootstrapper. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return array|null */ @@ -63,6 +70,8 @@ protected function getPackageBootstrappersUsingWorkbench($app): ?array /** * Get package providers. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return array>|null */ @@ -81,6 +90,7 @@ protected function getPackageProvidersUsingWorkbench($app): ?array return null; } + /** @phpstan-ignore return.type */ return static::usesTestingConcern(WithWorkbench::class) || ! static::usesTestingConcern() ? Arr::wrap($providers) : []; @@ -89,6 +99,8 @@ protected function getPackageProvidersUsingWorkbench($app): ?array /** * Resolve application Console Kernel implementation. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return string */ @@ -104,6 +116,8 @@ protected function applicationConsoleKernelUsingWorkbench($app): string /** * Get application HTTP Kernel implementation using Workbench. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return string */ @@ -119,6 +133,8 @@ protected function applicationHttpKernelUsingWorkbench($app): string /** * Get application HTTP exception handler using Workbench. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return string */ @@ -144,6 +160,8 @@ public static function cachedConfigurationForWorkbench() /** * Prepare the testing environment before the running the test case. * + * @internal + * * @return void * * @codeCoverageIgnore @@ -164,6 +182,8 @@ public static function setUpBeforeClassUsingWorkbench(): void /** * Clean up the testing environment before the next test case. * + * @internal + * * @return void * * @codeCoverageIgnore diff --git a/src/Concerns/Testing.php b/src/Concerns/Testing.php index 0e8809fc5..2e6b781a5 100644 --- a/src/Concerns/Testing.php +++ b/src/Concerns/Testing.php @@ -2,14 +2,18 @@ namespace Orchestra\Testbench\Concerns; +use Closure; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTruncation; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\WithFaker; -use Illuminate\Foundation\Testing\WithoutEvents; use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Support\LazyCollection; +use Orchestra\Testbench\Pest\WithPest; +use PHPUnit\Framework\TestCase as PHPUnitTestCase; + +use function Orchestra\Sidekick\once; /** * @api @@ -18,7 +22,6 @@ trait Testing { use ApplicationTestingHooks; use CreatesApplication; - use HandlesAnnotations; use HandlesAssertions; use HandlesAttributes; use HandlesDatabases; @@ -35,9 +38,22 @@ trait Testing */ final protected function setUpTheTestEnvironment(): void { - $this->setUpTheApplicationTestingHooks(function () { - $this->setUpTraits(); + $setUp = once(function () { + $this->setUpTheApplicationTestingHooks(function () { + $this->setUpTraits(); + }); }); + + /** @phpstan-ignore class.notFound */ + if ($this instanceof PHPUnitTestCase && static::usesTestingConcern(WithPest::class)) { + $this->setUpTheEnvironmentUsingPest(); /** @phpstan-ignore method.notFound */ + } + + if ($this->testCaseSetUpCallback instanceof Closure) { + value($this->testCaseSetUpCallback, $setUp); + } + + value($setUp); } /** @@ -49,23 +65,39 @@ final protected function setUpTheTestEnvironment(): void */ final protected function tearDownTheTestEnvironment(): void { - $this->tearDownTheApplicationTestingHooks(function () { - if (property_exists($this, 'serverVariables')) { - $this->serverVariables = []; - } + $tearDown = once(function () { + $this->tearDownTheApplicationTestingHooks(function () { + if (property_exists($this, 'serverVariables')) { + $this->serverVariables = []; + } - if (property_exists($this, 'defaultHeaders')) { - $this->defaultHeaders = []; - } + if (property_exists($this, 'defaultHeaders')) { + $this->defaultHeaders = []; + } - if (property_exists($this, 'originalExceptionHandler')) { - $this->originalExceptionHandler = null; - } + if (property_exists($this, 'originalExceptionHandler')) { + $this->originalExceptionHandler = null; + } - if (property_exists($this, 'originalDeprecationHandler')) { - $this->originalDeprecationHandler = null; - } + if (property_exists($this, 'originalDeprecationHandler')) { + $this->originalDeprecationHandler = null; + } + }); }); + + /** @phpstan-ignore class.notFound */ + if ($this instanceof PHPUnitTestCase && static::usesTestingConcern(WithPest::class)) { + $this->tearDownTheEnvironmentUsingPest(); /** @phpstan-ignore method.notFound */ + } + + if ($this->testCaseTearDownCallback instanceof Closure) { + value($this->testCaseTearDownCallback, $tearDown); + } + + value($tearDown); + + $this->testCaseSetUpCallback = null; + $this->testCaseTearDownCallback = null; } /** @@ -79,12 +111,12 @@ final protected function tearDownTheTestEnvironment(): void final protected function setUpTheTestEnvironmentTraits(array $uses): array { if (isset($uses[WithWorkbench::class])) { - $this->setUpWithWorkbench(); /** @phpstan-ignore method.notFound */ + $this->setUpWithWorkbench(); /** @phpstan-ignore method.notFound */ } $this->setUpDatabaseRequirements(function () use ($uses) { if (isset($uses[RefreshDatabase::class])) { - $this->refreshDatabase(); /** @phpstan-ignore method.notFound */ + $this->refreshDatabase(); /** @phpstan-ignore method.notFound */ } if (isset($uses[DatabaseMigrations::class])) { @@ -104,10 +136,6 @@ final protected function setUpTheTestEnvironmentTraits(array $uses): array $this->disableMiddlewareForAllTests(); /** @phpstan-ignore method.notFound */ } - if (isset($uses[WithoutEvents::class])) { - $this->disableEventsForAllTests(); /** @phpstan-ignore method.notFound */ - } - if (isset($uses[WithFaker::class])) { $this->setUpFaker(); /** @phpstan-ignore method.notFound */ } @@ -139,19 +167,12 @@ final protected function setUpTheTestEnvironmentTraits(array $uses): array return $uses; } - /** - * Determine trait should be ignored from being autoloaded. - * - * @param class-string $use - * @return bool - */ - protected function setUpTheTestEnvironmentTraitToBeIgnored(string $use): bool - { - return false; - } - /** * Reload the application instance with cached routes. + * + * @api + * + * @return void */ protected function reloadApplication(): void { @@ -159,6 +180,14 @@ protected function reloadApplication(): void $this->setUpTheTestEnvironment(); } + /** + * Determine trait should be ignored from being autoloaded. + * + * @param class-string $use + * @return bool + */ + abstract protected function setUpTheTestEnvironmentTraitToBeIgnored(string $use): bool; + /** * Boot the testing helper traits. * diff --git a/src/Concerns/WithCachedViews.php b/src/Concerns/WithCachedViews.php new file mode 100644 index 000000000..cf37eff89 --- /dev/null +++ b/src/Concerns/WithCachedViews.php @@ -0,0 +1,42 @@ +app; + + artisan($app, 'view:cache'); + + if ($app->bound('view')) { + $app->make('view')->flushFinderCache(); + } + } + + /** + * Clear cached views after each test. + * + * @after + * + * @return void + */ + protected function tearDownWithCachedViews(): void + { + /** @var \Illuminate\Foundation\Application $app */ + $app = $this->app; + + artisan($app, 'view:clear'); + } +} diff --git a/src/Concerns/WithFactories.php b/src/Concerns/WithFactories.php index b23fe7420..19368b1a3 100644 --- a/src/Concerns/WithFactories.php +++ b/src/Concerns/WithFactories.php @@ -10,15 +10,17 @@ /** * @api * - * @codeCoverageIgnore - * * @deprecated + * + * @codeCoverageIgnore */ trait WithFactories { /** * Load model factories from path. * + * @internal + * * @param string $path * @return $this * diff --git a/src/Concerns/WithFixtures.php b/src/Concerns/WithFixtures.php index 67a23cc50..06273e24c 100644 --- a/src/Concerns/WithFixtures.php +++ b/src/Concerns/WithFixtures.php @@ -14,6 +14,8 @@ */ trait WithFixtures { + use InteractsWithPest; + /** * Setup test case to include fixture file using ".fixtures.php" suffix if it's available. * @@ -21,7 +23,9 @@ trait WithFixtures */ protected static function setupWithFixturesForTestingEnvironment(): void { - $classFileName = filename_from_classname(static::class); + $classFileName = static::isRunningViaPestPrinter(static::class) + ? static::$__filename + : filename_from_classname(static::class); if ($classFileName === false) { return; diff --git a/src/Concerns/WithLaravelBootstrapFile.php b/src/Concerns/WithLaravelBootstrapFile.php index a7b9d1c9e..1bfcdc6b4 100644 --- a/src/Concerns/WithLaravelBootstrapFile.php +++ b/src/Concerns/WithLaravelBootstrapFile.php @@ -5,6 +5,7 @@ use Orchestra\Testbench\Foundation\Application as Testbench; use function Orchestra\Sidekick\Filesystem\join_paths; +use function Orchestra\Testbench\uses_default_skeleton; use function Orchestra\Testbench\workbench_path; trait WithLaravelBootstrapFile @@ -34,6 +35,19 @@ protected function getApplicationBootstrapFile(string $filename): string|false return $bootstrapFile; } + /** + * Determine if application is using a custom application kernels. + * + * @internal + * + * @return bool + */ + protected function hasCustomApplicationKernels(): bool + { + return ! $this->usesTestbenchDefaultSkeleton() + && ((static::$cacheApplicationBootstrapFile ??= $this->getApplicationBootstrapFile('app.php')) !== false); + } + /** * Determine if application is bootstrapped using Testbench's default skeleton. * @@ -41,7 +55,7 @@ protected function getApplicationBootstrapFile(string $filename): string|false */ protected function usesTestbenchDefaultSkeleton(): bool { - return realpath(join_paths($this->getApplicationBasePath(), 'bootstrap', '.testbench-default-skeleton')) !== false; + return uses_default_skeleton($this->getApplicationBasePath()); } /** diff --git a/src/Concerns/WithLaravelMigrations.php b/src/Concerns/WithLaravelMigrations.php index b3fbe445e..f9faadb43 100644 --- a/src/Concerns/WithLaravelMigrations.php +++ b/src/Concerns/WithLaravelMigrations.php @@ -7,9 +7,6 @@ use function Orchestra\Testbench\after_resolving; use function Orchestra\Testbench\default_migration_path; -/** - * @api - */ trait WithLaravelMigrations { use InteractsWithWorkbench; @@ -17,6 +14,8 @@ trait WithLaravelMigrations /** * Bootstrap with laravel migrations. * + * @internal + * * @return void */ protected function setUpWithLaravelMigrations(): void diff --git a/src/Concerns/WithWorkbench.php b/src/Concerns/WithWorkbench.php index 4fa6905ab..2244151d9 100644 --- a/src/Concerns/WithWorkbench.php +++ b/src/Concerns/WithWorkbench.php @@ -8,9 +8,6 @@ use Orchestra\Testbench\Foundation\Bootstrap\LoadMigrationsFromArray; use Orchestra\Testbench\Workbench\Workbench; -/** - * @api - */ trait WithWorkbench { use InteractsWithPHPUnit; @@ -19,6 +16,8 @@ trait WithWorkbench /** * Bootstrap with Workbench. * + * @internal + * * @return void */ protected function setUpWithWorkbench(): void @@ -45,6 +44,8 @@ protected function setUpWithWorkbench(): void /** * Bootstrap discover routes. * + * @internal + * * @param \Illuminate\Contracts\Foundation\Application $app * @return void */ diff --git a/src/Console/Commander.php b/src/Console/Commander.php index 40cad388f..ceb5916b1 100644 --- a/src/Console/Commander.php +++ b/src/Console/Commander.php @@ -50,21 +50,14 @@ class Commander * * @var \Orchestra\Testbench\Foundation\Config */ - protected $config; - - /** - * Working path. - * - * @var string - */ - protected $workingPath; + protected readonly Config $config; /** * The environment file name. * * @var string */ - protected $environmentFile = '.env'; + protected string $environmentFile = '.env'; /** * The testbench implementation class. @@ -90,10 +83,11 @@ class Commander * * @phpstan-param \Orchestra\Testbench\Foundation\Config|TConfig $config */ - public function __construct($config, string $workingPath) - { + public function __construct( + Config|array $config, + protected readonly string $workingPath + ) { $this->config = $config instanceof Config ? $config : new Config($config); - $this->workingPath = $workingPath; $_ENV['TESTBENCH_ENVIRONMENT_FILENAME'] = $this->environmentFile; } @@ -103,7 +97,7 @@ public function __construct($config, string $workingPath) * * @return void */ - public function handle() + public function handle(): void { $input = new ArgvInput; $output = new ConsoleOutput; @@ -122,7 +116,7 @@ public function handle() } finally { TerminatingConsole::handle(); Workbench::flush(); - static::$testbench::flushState(); + static::$testbench::flushState($this); $this->untrap(); } @@ -179,7 +173,7 @@ function ($app) use ($filesystem, $hasEnvironmentFile) { } /** - * Resolve application implementation. + * Resolve application implementation callback. * * @return \Closure(\Illuminate\Foundation\Application): void */ @@ -203,13 +197,15 @@ protected function resolveApplicationCallback() /** * Resolve the application's base path. * + * @api + * * @return string */ protected function getApplicationBasePath() { $path = $this->config['laravel'] ?? null; - if (! \is_null($path)) { + if (! \is_null($path) && ! isset($_ENV['APP_BASE_PATH'])) { return tap(transform_relative_path($path, $this->workingPath), static function ($path) { $_ENV['APP_BASE_PATH'] = $path; }); @@ -237,7 +233,7 @@ public static function applicationBasePath() * @param \Throwable $error * @return int */ - protected function handleException(OutputInterface $output, Throwable $error) + protected function handleException(OutputInterface $output, Throwable $error): int { if ($this->app instanceof LaravelApplication) { tap($this->app->make(ExceptionHandler::class), static function ($handler) use ($error, $output) { @@ -258,9 +254,7 @@ protected function handleException(OutputInterface $output, Throwable $error) */ protected function prepareCommandSignals(): void { - Signals::resolveAvailabilityUsing(static function () { - return \extension_loaded('pcntl'); - }); + Signals::resolveAvailabilityUsing(static fn () => \extension_loaded('pcntl')); Signals::whenAvailable(function () { $this->signals ??= new Signals(new SignalRegistry); diff --git a/src/Console/Kernel.php b/src/Console/Kernel.php index 5cf7916e4..c0ab38dd2 100644 --- a/src/Console/Kernel.php +++ b/src/Console/Kernel.php @@ -5,8 +5,6 @@ use Orchestra\Testbench\Foundation\Console\Kernel as ConsoleKernel; use Throwable; -use function Orchestra\Sidekick\Filesystem\join_paths; - /** * @codeCoverageIgnore */ @@ -19,18 +17,6 @@ final class Kernel extends ConsoleKernel */ protected $commands = []; - /** - * Register the commands for the application. - * - * @return void - */ - protected function commands() - { - if (is_file($console = base_path(join_paths('routes', 'console.php')))) { - require $console; - } - } - /** * Report the exception to the exception handler. * @@ -39,8 +25,20 @@ protected function commands() * * @throws \Throwable */ + #[\Override] protected function reportException(Throwable $e) { throw $e; } + + /** + * Determine if the kernel should discover commands. + * + * @return bool + */ + #[\Override] + protected function shouldDiscoverCommands() + { + return \get_class($this) === __CLASS__; + } } diff --git a/src/Contracts/Laravel.stub b/src/Contracts/Laravel.stub index a892a5ea0..13a8c4283 100644 --- a/src/Contracts/Laravel.stub +++ b/src/Contracts/Laravel.stub @@ -79,6 +79,8 @@ interface Laravel /** * Get Application's base path. * + * @api + * * @return string */ public static function applicationBasePath(); diff --git a/src/Database/MigrateProcessor.php b/src/Database/MigrateProcessor.php index 2984c5c3d..f1898ec94 100644 --- a/src/Database/MigrateProcessor.php +++ b/src/Database/MigrateProcessor.php @@ -2,7 +2,6 @@ namespace Orchestra\Testbench\Database; -use Illuminate\Database\Migrations\Migrator; use Orchestra\Testbench\Contracts\TestCase; use function Orchestra\Testbench\artisan; @@ -12,30 +11,17 @@ */ class MigrateProcessor { - /** - * The testbench instance. - * - * @var \Orchestra\Testbench\Contracts\TestCase - */ - protected $testbench; - - /** - * The migrator options. - * - * @var array - */ - protected $options = []; - /** * Construct a new schema migrator. * * @param \Orchestra\Testbench\Contracts\TestCase $testbench * @param array $options */ - public function __construct(TestCase $testbench, array $options = []) - { - $this->testbench = $testbench; - $this->options = $options; + public function __construct( + protected readonly TestCase $testbench, + protected readonly array $options = [] + ) { + // } /** diff --git a/src/Exceptions/ApplicationNotAvailableException.php b/src/Exceptions/ApplicationNotAvailableException.php index 8d1b605a8..2da1e66f0 100644 --- a/src/Exceptions/ApplicationNotAvailableException.php +++ b/src/Exceptions/ApplicationNotAvailableException.php @@ -15,7 +15,7 @@ class ApplicationNotAvailableException extends RuntimeException * @param string|null $caller * @return static */ - public static function make(?string $caller) + public static function make(?string $caller): static { return new static(\sprintf('Application is not available to run [%s]', $caller ?? 'N/A')); } diff --git a/src/Exceptions/DeprecatedException.php b/src/Exceptions/DeprecatedException.php index 951f4a385..dc7b6062b 100644 --- a/src/Exceptions/DeprecatedException.php +++ b/src/Exceptions/DeprecatedException.php @@ -2,7 +2,7 @@ namespace Orchestra\Testbench\Exceptions; -use Illuminate\Support\Collection; +use PHPUnit\Util\Filter; /** * @internal @@ -11,19 +11,6 @@ */ class DeprecatedException extends PHPUnitErrorException { - /** - * List of Testbench exception/error handlers. - * - * @return array - */ - protected function testbenchExceptionHandlers() - { - return [ - \Illuminate\Foundation\Bootstrap\HandleExceptions::class, - \Orchestra\Testbench\Bootstrap\HandleExceptions::class, - ]; - } - /** * Convert exception to string. * @@ -31,18 +18,8 @@ protected function testbenchExceptionHandlers() */ public function __toString(): string { - $traces = (new Collection($this->getPHPUnitExceptionTrace())) - ->transform(function (array $trace): ?string { - if ((isset($trace['class']) && \in_array($trace['class'], $this->testbenchExceptionHandlers())) - || ! isset($trace['file']) - || ! isset($trace['line'])) { - return null; - } - - return \sprintf('%s:%d', $trace['file'], $trace['line']); - })->filter() - ->values(); + $stackTrace = Filter::stackTraceFromThrowableAsString($this); - return \sprintf('%s'.PHP_EOL.PHP_EOL.'%s', $this->getMessage(), $traces->join(PHP_EOL)); + return \sprintf('%s'.PHP_EOL.PHP_EOL.'%s', $this->getMessage(), $stackTrace); } } diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php index 0c923c832..d7949fa9a 100644 --- a/src/Exceptions/Handler.php +++ b/src/Exceptions/Handler.php @@ -10,22 +10,6 @@ */ class Handler extends ExceptionHandler { - /** {@inheritDoc} */ - protected $dontReport = [ - // - ]; - - /** {@inheritDoc} */ - protected $levels = [ - // - ]; - - /** {@inheritDoc} */ - protected $dontFlash = [ - 'password', - 'password_confirmation', - ]; - /** {@inheritDoc} */ #[\Override] public function report(Throwable $e) diff --git a/src/Exceptions/PHPUnitErrorException.php b/src/Exceptions/PHPUnitErrorException.php index b99737d8f..4c9c30e70 100644 --- a/src/Exceptions/PHPUnitErrorException.php +++ b/src/Exceptions/PHPUnitErrorException.php @@ -2,18 +2,26 @@ namespace Orchestra\Testbench\Exceptions; -/** - * @codeCoverageIgnore - */ -class PHPUnitErrorException extends \PHPUnit\Framework\Error\Error +class PHPUnitErrorException extends \PHPUnit\Framework\Exception { + /** {@inheritDoc} */ + public function __construct(string $message, int $code, string $file, int $line, ?\Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->file = $file; + $this->line = $line; + } + /** * Get serializable trace for PHPUnit. * * @return array + * + * @codeCoverageIgnore */ public function getPHPUnitExceptionTrace(): array { - return $this->getTrace(); + return $this->serializableTrace; } } diff --git a/src/Factories/UserFactory.php b/src/Factories/UserFactory.php index 25f63c242..e7444e050 100644 --- a/src/Factories/UserFactory.php +++ b/src/Factories/UserFactory.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Foundation\Auth\User; +use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; /** @@ -15,28 +16,31 @@ */ class UserFactory extends Factory { + /** + * The current password being used by the factory. + */ + protected static ?string $password; + /** * Define the model's default state. * * @return array */ - public function definition() + public function definition(): array { return [ 'name' => fake()->name(), 'email' => fake()->unique()->safeEmail(), 'email_verified_at' => now(), - 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'password' => static::$password ??= Hash::make('password'), 'remember_token' => Str::random(10), ]; } /** * Indicate that the model's email address should be unverified. - * - * @return static */ - public function unverified() + public function unverified(): static { return $this->state(fn (array $attributes) => [ 'email_verified_at' => null, @@ -50,6 +54,6 @@ public function unverified() */ public function modelName() { - return $this->model ?? config('auth.providers.users.model', User::class); + return $this->model ?? config('auth.providers.users.model') ?? env('AUTH_MODEL', User::class); } } diff --git a/src/Features/ParallelRunner.php b/src/Features/ParallelRunner.php index 15d0e40d2..f41981ec8 100644 --- a/src/Features/ParallelRunner.php +++ b/src/Features/ParallelRunner.php @@ -18,6 +18,7 @@ class ParallelRunner extends \Illuminate\Testing\ParallelRunner * * @return \Illuminate\Contracts\Foundation\Application */ + #[\Override] protected function createApplication() { if (! \defined('TESTBENCH_WORKING_PATH')) { @@ -28,9 +29,7 @@ protected function createApplication() $_ENV['TESTBENCH_APP_BASE_PATH'] = Env::get('TESTBENCH_APP_BASE_PATH'); } - $applicationResolver = static::$applicationResolver ?: static function () { - return container()->createApplication(); - }; + $applicationResolver = static::$applicationResolver ?: static fn () => container()->createApplication(); return $applicationResolver(); } diff --git a/src/Features/TestingFeature.php b/src/Features/TestingFeature.php index 141dcb8f5..3fceb5262 100644 --- a/src/Features/TestingFeature.php +++ b/src/Features/TestingFeature.php @@ -4,8 +4,8 @@ use Closure; use Illuminate\Support\Fluent; -use Orchestra\Testbench\Concerns\HandlesAnnotations; use Orchestra\Testbench\Concerns\HandlesAttributes; +use Orchestra\Testbench\Pest\WithPest; use PHPUnit\Framework\TestCase as PHPUnitTestCase; use function Orchestra\Sidekick\once; @@ -20,15 +20,15 @@ final class TestingFeature * * @param object $testCase * @param (\Closure():(void))|null $default - * @param (\Closure():(void))|null $annotation * @param (\Closure():(mixed))|null $attribute + * @param (\Closure(\Closure|null):(mixed))|null $pest * @return \Illuminate\Support\Fluent */ public static function run( object $testCase, ?Closure $default = null, - ?Closure $annotation = null, - ?Closure $attribute = null + ?Closure $attribute = null, + ?Closure $pest = null ): Fluent { /** @var \Illuminate\Support\Fluent $result */ $result = new Fluent(['attribute' => new FeaturesCollection]); @@ -36,17 +36,20 @@ public static function run( $defaultResolver = once($default); if ($testCase instanceof PHPUnitTestCase) { - /** @phpstan-ignore staticMethod.notFound */ - if ($testCase::usesTestingConcern(HandlesAnnotations::class)) { - value($annotation, $defaultResolver); - } - /** @phpstan-ignore staticMethod.notFound */ if ($testCase::usesTestingConcern(HandlesAttributes::class)) { $result['attribute'] = value($attribute, $defaultResolver); } } + if ( + $testCase instanceof PHPUnitTestCase + && $pest instanceof Closure + && $testCase::usesTestingConcern(WithPest::class) /** @phpstan-ignore staticMethod.notFound, class.notFound */ + ) { + value($pest, $defaultResolver); + } + $defaultResolver(); return $result; diff --git a/src/Foundation/Actions/CreateVendorSymlink.php b/src/Foundation/Actions/CreateVendorSymlink.php index 9bfe414ca..6e47251f9 100644 --- a/src/Foundation/Actions/CreateVendorSymlink.php +++ b/src/Foundation/Actions/CreateVendorSymlink.php @@ -19,7 +19,7 @@ final class CreateVendorSymlink * @param string $workingPath */ public function __construct( - protected string $workingPath + protected readonly string $workingPath ) {} /** diff --git a/src/Foundation/Application.php b/src/Foundation/Application.php index 0a96468a1..0eea1e0bb 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -5,20 +5,43 @@ use Illuminate\Console\Application as Artisan; use Illuminate\Console\Scheduling\ScheduleListCommand; use Illuminate\Console\Signals; +use Illuminate\Database\Eloquent\Factories\Factory; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Migrations\Migrator; use Illuminate\Database\Schema\Builder as SchemaBuilder; use Illuminate\Foundation\Bootstrap\HandleExceptions; use Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables; +use Illuminate\Foundation\Bootstrap\RegisterProviders; +use Illuminate\Foundation\Console\AboutCommand; +use Illuminate\Foundation\Console\ChannelListCommand; use Illuminate\Foundation\Console\RouteListCommand; +use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull; +use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance; +use Illuminate\Foundation\Http\Middleware\TrimStrings; +use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken; +use Illuminate\Http\Middleware\TrustHosts; +use Illuminate\Http\Middleware\TrustProxies; use Illuminate\Http\Resources\Json\JsonResource; +use Illuminate\Http\Resources\JsonApi\JsonApiResource; +use Illuminate\Mail\Markdown; +use Illuminate\Queue\Console\WorkCommand; use Illuminate\Queue\Queue; +use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Support\Arr; +use Illuminate\Support\EncodedHtmlString; +use Illuminate\Support\Once; +use Illuminate\Support\Sleep; use Illuminate\Support\Str; +use Illuminate\Validation\Validator; use Illuminate\View\Component; -use Orchestra\Testbench\Bootstrap\RegisterProviders; use Orchestra\Testbench\Concerns\CreatesApplication; +use Orchestra\Testbench\Console\Commander; use Orchestra\Testbench\Contracts\Config as ConfigContract; use Orchestra\Testbench\Workbench\Workbench; +use function Orchestra\Sidekick\Filesystem\join_paths; + /** * @api * @@ -45,13 +68,6 @@ class Application */ protected $app; - /** - * The application base path. - * - * @var string|null - */ - protected $basePath; - /** * List of configurations. * @@ -78,7 +94,7 @@ class Application * * @var bool */ - protected $loadEnvironmentVariables = false; + protected bool $loadEnvironmentVariables = false; /** * Create new application resolver. @@ -86,9 +102,10 @@ class Application * @param string|null $basePath * @param (callable(\Illuminate\Foundation\Application):(void))|null $resolvingCallback */ - public function __construct(?string $basePath = null, ?callable $resolvingCallback = null) - { - $this->basePath = $basePath; + public function __construct( + protected readonly ?string $basePath = null, + ?callable $resolvingCallback = null + ) { $this->resolvingCallback = $resolvingCallback; } @@ -199,26 +216,55 @@ public static function createFromConfig(ConfigContract $config, ?callable $resol /** * Flush the application states. * - * @param \Orchestra\Testbench\Console\Commander|\Orchestra\Testbench\PHPUnit\TestCase|null $instance + * @param \Orchestra\Testbench\Console\Commander|\Orchestra\Testbench\PHPUnit\TestCase $instance * @return void */ - public static function flushState(?object $instance = null): void + public static function flushState(object $instance): void { + AboutCommand::flushState(); Artisan::forgetBootstrappers(); + ChannelListCommand::resolveTerminalWidthUsing(null); Component::flushCache(); Component::forgetComponentsResolver(); Component::forgetFactory(); - HandleExceptions::forgetApp(); - JsonResource::wrap('data'); + ConvertEmptyStringsToNull::flushState(); + EncodedHtmlString::flushState(); + Factory::flushState(); + FormRequest::flushState(); + + if (! $instance instanceof Commander) { + HandleExceptions::flushState($instance); + } + + JsonResource::flushState(); + JsonApiResource::flushState(); + Markdown::flushState(); + Migrator::withoutMigrations([]); + Model::handleDiscardedAttributeViolationUsing(null); + Model::handleLazyLoadingViolationUsing(null); + Model::handleMissingAttributeViolationUsing(null); + Model::automaticallyEagerLoadRelationships(false); + Model::preventAccessingMissingAttributes(false); + Model::preventLazyLoading(false); + Model::preventSilentlyDiscardingAttributes(false); + Once::flush(); + PreventRequestsDuringMaintenance::flushState(); Queue::createPayloadUsing(null); RegisterProviders::flushState(); RouteListCommand::resolveTerminalWidthUsing(null); ScheduleListCommand::resolveTerminalWidthUsing(null); SchemaBuilder::$defaultStringLength = 255; SchemaBuilder::$defaultMorphKeyType = 'int'; - Signals::resolveAvailabilityUsing(null); - Str::createRandomStringsNormally(); - Str::createUuidsNormally(); + Signals::resolveAvailabilityUsing(null); // @phpstan-ignore argument.type + Sleep::fake(false); + Str::resetFactoryState(); + ThrottleRequests::shouldHashKeys(); + TrimStrings::flushState(); + TrustProxies::flushState(); + TrustHosts::flushState(); + Validator::flushState(); + ValidateCsrfToken::flushState(); + WorkCommand::flushState(); } /** @@ -251,7 +297,9 @@ public function configure(array $options) /** * Ignore package discovery from. * - * @return array + * @api + * + * @return array */ public function ignorePackageDiscoveriesFrom() { @@ -261,8 +309,10 @@ public function ignorePackageDiscoveriesFrom() /** * Get package providers. * + * @api + * * @param \Illuminate\Foundation\Application $app - * @return array + * @return array */ protected function getPackageProviders($app) { @@ -272,6 +322,8 @@ protected function getPackageProviders($app) /** * Get package bootstrapper. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return array */ @@ -287,10 +339,12 @@ protected function getPackageBootstrappers($app) /** * Resolve application resolving callback. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return void */ - private function resolveApplicationResolvingCallback($app): void + protected function resolveApplicationResolvingCallback($app): void { $this->resolveApplicationResolvingCallbackFromTrait($app); @@ -314,6 +368,8 @@ protected function getApplicationBasePath() /** * Resolve application core environment variables implementation. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return void */ @@ -335,6 +391,8 @@ protected function resolveApplicationEnvironmentVariables($app) /** * Resolve application core configuration implementation. * + * @internal + * * @param \Illuminate\Foundation\Application $app * @return void */ @@ -348,14 +406,20 @@ protected function resolveApplicationConfiguration($app) /** * Resolve application Console Kernel implementation. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return void */ protected function resolveApplicationConsoleKernel($app) { + if ($this->hasCustomApplicationKernels() === true) { + return; + } + $kernel = Workbench::applicationConsoleKernel() ?? 'Orchestra\Testbench\Console\Kernel'; - if (is_file($app->basePath('app/Console/Kernel.php')) && class_exists('App\Console\Kernel')) { + if (is_file($app->basePath(join_paths('app', 'Console', 'Kernel.php'))) && class_exists('App\Console\Kernel')) { $kernel = 'App\Console\Kernel'; } @@ -365,14 +429,20 @@ protected function resolveApplicationConsoleKernel($app) /** * Resolve application HTTP Kernel implementation. * + * @api + * * @param \Illuminate\Foundation\Application $app * @return void */ protected function resolveApplicationHttpKernel($app) { + if ($this->hasCustomApplicationKernels() === true) { + return; + } + $kernel = Workbench::applicationHttpKernel() ?? 'Orchestra\Testbench\Http\Kernel'; - if (is_file($app->basePath('app/Http/Kernel.php')) && class_exists('App\Http\Kernel')) { + if (is_file($app->basePath(join_paths('app', 'Http', 'Kernel.php'))) && class_exists('App\Http\Kernel')) { $kernel = 'App\Http\Kernel'; } diff --git a/src/Foundation/Bootstrap/CreateVendorSymlink.php b/src/Foundation/Bootstrap/CreateVendorSymlink.php index f395b84cf..743c1b406 100644 --- a/src/Foundation/Bootstrap/CreateVendorSymlink.php +++ b/src/Foundation/Bootstrap/CreateVendorSymlink.php @@ -16,7 +16,7 @@ final class CreateVendorSymlink * @param string $workingPath */ public function __construct( - public string $workingPath + protected readonly string $workingPath ) {} /** diff --git a/src/Foundation/Bootstrap/LoadEnvironmentVariablesFromArray.php b/src/Foundation/Bootstrap/LoadEnvironmentVariablesFromArray.php index 9c7dca9d4..f6ae97256 100644 --- a/src/Foundation/Bootstrap/LoadEnvironmentVariablesFromArray.php +++ b/src/Foundation/Bootstrap/LoadEnvironmentVariablesFromArray.php @@ -2,6 +2,7 @@ namespace Orchestra\Testbench\Foundation\Bootstrap; +use Dotenv\Parser\Entry; use Dotenv\Parser\Parser; use Dotenv\Store\StringStore; use Illuminate\Contracts\Foundation\Application; @@ -13,21 +14,15 @@ */ final class LoadEnvironmentVariablesFromArray { - /** - * The environment variables. - * - * @var array - */ - public $environmentVariables; - /** * Construct a new Create Vendor Symlink bootstrapper. * * @param array $environmentVariables */ - public function __construct(array $environmentVariables) - { - $this->environmentVariables = $environmentVariables; + public function __construct( + public readonly array $environmentVariables + ) { + // } /** @@ -41,11 +36,9 @@ public function bootstrap(Application $app): void $store = new StringStore(implode(PHP_EOL, $this->environmentVariables)); $parser = new Parser; - Collection::make($parser->parse($store->read())) - ->filter(static function ($entry) { - /** @var \Dotenv\Parser\Entry $entry */ - return $entry->getValue()->isDefined(); - })->each(static function ($entry) { + (new Collection($parser->parse($store->read()))) + ->filter(static fn (Entry $entry) => $entry->getValue()->isDefined()) + ->each(static function (Entry $entry) { /** @var \Dotenv\Parser\Entry $entry */ Env::set($entry->getName(), $entry->getValue()->get()->getChars()); }); diff --git a/src/Foundation/Bootstrap/LoadMigrationsFromArray.php b/src/Foundation/Bootstrap/LoadMigrationsFromArray.php index 4746f8c7e..f0d2f08f5 100644 --- a/src/Foundation/Bootstrap/LoadMigrationsFromArray.php +++ b/src/Foundation/Bootstrap/LoadMigrationsFromArray.php @@ -19,30 +19,17 @@ */ final class LoadMigrationsFromArray { - /** - * The migrations. - * - * @var array|bool|string - */ - public $migrations; - - /** - * The seeders. - * - * @var array|bool|class-string - */ - public $seeders; - /** * Construct a new Create Vendor Symlink bootstrapper. * * @param array|bool|string $migrations * @param array|bool|class-string $seeders */ - public function __construct($migrations = [], $seeders = false) - { - $this->migrations = $migrations; - $this->seeders = $seeders; + public function __construct( + public readonly array|bool|string $migrations = [], + public readonly array|bool|string $seeders = false + ) { + // } /** diff --git a/src/Foundation/Bootstrap/SyncDatabaseEnvironmentVariables.php b/src/Foundation/Bootstrap/SyncDatabaseEnvironmentVariables.php index 07fe0cfd3..6eadbf224 100644 --- a/src/Foundation/Bootstrap/SyncDatabaseEnvironmentVariables.php +++ b/src/Foundation/Bootstrap/SyncDatabaseEnvironmentVariables.php @@ -23,6 +23,7 @@ public function bootstrap(Application $app): void { tap($app->make('config'), function (Repository $config) { $this->usesDatabaseConnectionsEnvironmentVariables($config, 'mysql', 'MYSQL'); + $this->usesDatabaseConnectionsEnvironmentVariables($config, 'mariadb', 'MARIADB'); $this->usesDatabaseConnectionsEnvironmentVariables($config, 'pgsql', 'POSTGRES'); $this->usesDatabaseConnectionsEnvironmentVariables($config, 'sqlsrv', 'MSSQL'); }); diff --git a/src/Foundation/Bootstrap/SyncTestbenchCachedRoutes.php b/src/Foundation/Bootstrap/SyncTestbenchCachedRoutes.php index 22af51e94..8dd19faa0 100644 --- a/src/Foundation/Bootstrap/SyncTestbenchCachedRoutes.php +++ b/src/Foundation/Bootstrap/SyncTestbenchCachedRoutes.php @@ -17,7 +17,10 @@ class SyncTestbenchCachedRoutes */ public function bootstrap(Application $app): void { - /** @var \Illuminate\Routing\Router $router */ + /** + * @var \Illuminate\Foundation\Application&\Illuminate\Contracts\Foundation\Application $app + * @var \Illuminate\Routing\Router $router + */ $router = $app->make('router'); /** @phpstan-ignore argument.type */ diff --git a/src/Foundation/Concerns/HandlesDatabaseConnections.php b/src/Foundation/Concerns/HandlesDatabaseConnections.php index 6cbe77af7..6a1db84f2 100644 --- a/src/Foundation/Concerns/HandlesDatabaseConnections.php +++ b/src/Foundation/Concerns/HandlesDatabaseConnections.php @@ -38,7 +38,7 @@ final protected function usesDatabaseConnectionsEnvironmentVariables(Repository $configuration = (new Collection(Arr::wrap($options['env']))) ->transform(static fn ($value) => Env::get("{$keyword}_{$value}")) ->first( - $options['rules'] ?? static fn ($value) => ! empty($value) && $value !== false && \is_string($value) // @phpstan-ignore notIdentical.alwaysTrue + $options['rules'] ?? static fn ($value) => ! empty($value) && $value !== false && \is_string($value) ) ?? $config->get($name); return [ diff --git a/src/Foundation/Config.php b/src/Foundation/Config.php index d807cea17..70691fd64 100644 --- a/src/Foundation/Config.php +++ b/src/Foundation/Config.php @@ -42,6 +42,7 @@ * install: bool, * auth: bool, * welcome: bool|null, + * health: bool|null, * sync: array, * build: array|string>, * assets: array, @@ -54,6 +55,7 @@ * install?: bool, * auth?: bool, * welcome?: bool|null, + * health?: bool|null, * sync?: array, * build?: array|string>, * assets?: array, @@ -147,6 +149,7 @@ class Config extends FluentDecorator implements ConfigContract 'install' => true, 'auth' => false, 'welcome' => null, + 'health' => null, 'sync' => [], 'build' => [], 'assets' => [], diff --git a/src/Foundation/Console/Actions/Action.php b/src/Foundation/Console/Actions/Action.php index 43124b31b..a27877c0b 100644 --- a/src/Foundation/Console/Actions/Action.php +++ b/src/Foundation/Console/Actions/Action.php @@ -2,8 +2,6 @@ namespace Orchestra\Testbench\Foundation\Console\Actions; -use function Orchestra\Testbench\transform_realpath_to_relative; - /** * @api */ @@ -13,21 +11,4 @@ abstract class Action * Determine if action should only be pretended. */ protected bool $pretending = false; - - /** - * Normalise file location. - * - * @param string $path - * @return string - * - * @deprecated - * - * @codeCoverageIgnore - */ - protected function pathLocation(string $path): string - { - return transform_realpath_to_relative( - $path, property_exists($this, 'workingPath') ? $this->workingPath : null - ); - } } diff --git a/src/Foundation/Console/Actions/DeleteDirectories.php b/src/Foundation/Console/Actions/DeleteDirectories.php index 8647861aa..db6fc021d 100644 --- a/src/Foundation/Console/Actions/DeleteDirectories.php +++ b/src/Foundation/Console/Actions/DeleteDirectories.php @@ -7,6 +7,7 @@ use Illuminate\Support\LazyCollection; use Orchestra\Sidekick\Console\Task; +use function Laravel\Prompts\confirm; use function Orchestra\Testbench\transform_realpath_to_relative; /** @@ -20,12 +21,14 @@ class DeleteDirectories extends Action * @param \Illuminate\Filesystem\Filesystem $filesystem * @param \Illuminate\Console\View\Components\Factory $components * @param string|null $workingPath + * @param bool $confirmation * @param bool $pretending */ public function __construct( - public Filesystem $filesystem, - public ?ComponentsFactory $components = null, + public readonly Filesystem $filesystem, + public readonly ?ComponentsFactory $components = null, public ?string $workingPath = null, + public bool $confirmation = false, bool $pretending = false, ) { $this->pretending = $pretending; @@ -58,6 +61,10 @@ public function handle(iterable $directories): void return false; } + if ($this->confirmation === true && confirm(\sprintf('Delete [%s] directory?', $location)) === false) { + return false; + } + return true; })->dispatch($this->pretending); }); diff --git a/src/Foundation/Console/Actions/DeleteFiles.php b/src/Foundation/Console/Actions/DeleteFiles.php index f9260024e..70cf34d0b 100644 --- a/src/Foundation/Console/Actions/DeleteFiles.php +++ b/src/Foundation/Console/Actions/DeleteFiles.php @@ -7,6 +7,7 @@ use Illuminate\Support\LazyCollection; use Orchestra\Sidekick\Console\Task; +use function Laravel\Prompts\confirm; use function Orchestra\Testbench\transform_realpath_to_relative; /** @@ -20,12 +21,14 @@ class DeleteFiles extends Action * @param \Illuminate\Filesystem\Filesystem $filesystem * @param \Illuminate\Console\View\Components\Factory $components * @param string|null $workingPath + * @param bool $confirmation * @param bool $pretending */ public function __construct( - public Filesystem $filesystem, - public ?ComponentsFactory $components = null, + public readonly Filesystem $filesystem, + public readonly ?ComponentsFactory $components = null, public ?string $workingPath = null, + public bool $confirmation = false, bool $pretending = false, ) { $this->pretending = $pretending; @@ -59,6 +62,10 @@ public function handle(iterable $files): void return false; } + if ($this->confirmation === true && confirm(\sprintf('Delete [%s] file?', $location)) === false) { + return false; + } + return true; })->dispatch($this->pretending); }); diff --git a/src/Foundation/Console/Actions/EnsureDirectoryExists.php b/src/Foundation/Console/Actions/EnsureDirectoryExists.php index 4955506e3..836395be4 100644 --- a/src/Foundation/Console/Actions/EnsureDirectoryExists.php +++ b/src/Foundation/Console/Actions/EnsureDirectoryExists.php @@ -7,6 +7,7 @@ use Illuminate\Support\LazyCollection; use Orchestra\Sidekick\Console\Task; +use function Laravel\Prompts\confirm; use function Orchestra\Sidekick\Filesystem\join_paths; use function Orchestra\Testbench\transform_realpath_to_relative; @@ -21,12 +22,14 @@ class EnsureDirectoryExists extends Action * @param \Illuminate\Filesystem\Filesystem $filesystem * @param \Illuminate\Console\View\Components\Factory|null $components * @param string|null $workingPath + * @param bool $confirmation * @param bool $pretending */ public function __construct( - public Filesystem $filesystem, - public ?ComponentsFactory $components = null, + public readonly Filesystem $filesystem, + public readonly ?ComponentsFactory $components = null, public ?string $workingPath = null, + public bool $confirmation = false, bool $pretending = false, ) { $this->pretending = $pretending; @@ -61,6 +64,10 @@ public function handle(iterable $directories): void return false; } + if ($this->confirmation === true && confirm(\sprintf('Ensure [%s] directory exists?', $location)) === false) { + return false; + } + return true; })->dispatch($this->pretending); }); diff --git a/src/Foundation/Console/Actions/GeneratesFile.php b/src/Foundation/Console/Actions/GeneratesFile.php index 4211e51fb..95df57a98 100644 --- a/src/Foundation/Console/Actions/GeneratesFile.php +++ b/src/Foundation/Console/Actions/GeneratesFile.php @@ -6,6 +6,8 @@ use Illuminate\Filesystem\Filesystem; use Orchestra\Sidekick\Console\Task; +use function Laravel\Prompts\confirm; +use function Orchestra\Sidekick\Filesystem\join_paths; use function Orchestra\Testbench\transform_realpath_to_relative; /** @@ -20,13 +22,15 @@ class GeneratesFile extends Action * @param \Illuminate\Console\View\Components\Factory|null $components * @param bool $force * @param string|null $workingPath + * @param bool $confirmation * @param bool $pretending */ public function __construct( - public Filesystem $filesystem, - public ?ComponentsFactory $components = null, - public bool $force = false, + public readonly Filesystem $filesystem, + public readonly ?ComponentsFactory $components = null, + public readonly bool $force = false, public ?string $workingPath = null, + public bool $confirmation = false, bool $pretending = false, ) { $this->pretending = $pretending; @@ -39,7 +43,7 @@ public function __construct( * @param string|false|null $to * @return void */ - public function handle($from, $to): void + public function handle(string|false|null $from, string|false|null $to): void { if (! \is_string($from) || ! \is_string($to)) { return; @@ -50,7 +54,7 @@ public function handle($from, $to): void Task::action(function () use ($from, $to) { $copied = $this->filesystem->copy($from, $to); - $gitKeepFile = \sprintf('%s/.gitkeep', \dirname($to)); + $gitKeepFile = join_paths(\dirname($to), '.gitkeep'); if ($this->filesystem->exists($gitKeepFile)) { $this->filesystem->delete($gitKeepFile); @@ -80,6 +84,10 @@ public function handle($from, $to): void return false; } + if ($this->confirmation === true && confirm(\sprintf('Generate [%s] file?', $location)) === false) { + return false; + } + return true; })->dispatch($this->pretending); } diff --git a/src/Foundation/Console/Concerns/HandleTerminatingConsole.php b/src/Foundation/Console/Concerns/HandleTerminatingConsole.php deleted file mode 100644 index c04e6d508..000000000 --- a/src/Foundation/Console/Concerns/HandleTerminatingConsole.php +++ /dev/null @@ -1,38 +0,0 @@ -components->task(\sprintf( - 'Copying %s [%s] to [%s]', - $type, - $from, - $to, - )); - } -} diff --git a/src/Foundation/Console/CreateSqliteDbCommand.php b/src/Foundation/Console/CreateSqliteDbCommand.php index f0e2dfa53..fd9fbcf6b 100644 --- a/src/Foundation/Console/CreateSqliteDbCommand.php +++ b/src/Foundation/Console/CreateSqliteDbCommand.php @@ -30,7 +30,6 @@ class CreateSqliteDbCommand extends Command */ public function handle(Filesystem $filesystem) { - $workingPath = $this->laravel->basePath(); $databasePath = $this->laravel->databasePath(); /** @var bool $force */ diff --git a/src/Foundation/Console/DevToolCommand.php b/src/Foundation/Console/DevToolCommand.php deleted file mode 100644 index 87bd86d41..000000000 --- a/src/Foundation/Console/DevToolCommand.php +++ /dev/null @@ -1,46 +0,0 @@ -call('workbench:install', ['--force' => $this->option('force')]); - } -} diff --git a/src/Foundation/Console/DropSqliteDbCommand.php b/src/Foundation/Console/DropSqliteDbCommand.php index c1c8ae78b..214077e7d 100644 --- a/src/Foundation/Console/DropSqliteDbCommand.php +++ b/src/Foundation/Console/DropSqliteDbCommand.php @@ -33,7 +33,6 @@ public function handle(Filesystem $filesystem) /** @var bool $pretending */ $pretending = $this->option('pretend'); - $workingPath = $this->laravel->basePath(); $databasePath = $this->laravel->databasePath(); (new Actions\DeleteFiles( diff --git a/src/Foundation/Console/Kernel.php b/src/Foundation/Console/Kernel.php index 2e07aefcc..cf5a44188 100644 --- a/src/Foundation/Console/Kernel.php +++ b/src/Foundation/Console/Kernel.php @@ -11,8 +11,20 @@ abstract class Kernel extends ConsoleKernel * * @return array */ + #[\Override] protected function bootstrappers() { return []; } + + /** + * Determine if the kernel should discover commands. + * + * @return bool + */ + #[\Override] + protected function shouldDiscoverCommands() + { + return \get_class($this) === __CLASS__; + } } diff --git a/src/Foundation/Console/PurgeSkeletonCommand.php b/src/Foundation/Console/PurgeSkeletonCommand.php index 18fad84af..68ef1aec3 100644 --- a/src/Foundation/Console/PurgeSkeletonCommand.php +++ b/src/Foundation/Console/PurgeSkeletonCommand.php @@ -74,11 +74,11 @@ public function handle(Filesystem $filesystem, ConfigContract $config) pretending: $pretending, ))->handle( (new LazyCollection(function () use ($filesystem) { - yield $this->laravel->basePath(join_paths('database', 'database.sqlite')); + yield $this->laravel->databasePath('database.sqlite'); yield $filesystem->glob($this->laravel->basePath(join_paths('routes', 'testbench-*.php'))); - yield $filesystem->glob($this->laravel->basePath(join_paths('storage', 'app', 'public', '*'))); - yield $filesystem->glob($this->laravel->basePath(join_paths('storage', 'app', '*'))); - yield $filesystem->glob($this->laravel->basePath(join_paths('storage', 'framework', 'sessions', '*'))); + yield $filesystem->glob($this->laravel->storagePath(join_paths('app', 'public', '*'))); + yield $filesystem->glob($this->laravel->storagePath(join_paths('app', '*'))); + yield $filesystem->glob($this->laravel->storagePath(join_paths('framework', 'sessions', '*'))); }))->flatten() ); @@ -89,12 +89,9 @@ public function handle(Filesystem $filesystem, ConfigContract $config) ))->handle( (new LazyCollection($files)) ->map(fn ($file) => $this->laravel->basePath($file)) - ->map(static function ($file) use ($filesystem) { - return str_contains($file, '*') - ? [...$filesystem->glob($file)] - : $file; - })->flatten() - ->reject(fn ($file) => str_contains($file, '*')) + ->map(static fn ($file) => str_contains($file, '*') ? [...$filesystem->glob($file)] : $file) + ->flatten() + ->reject(static fn ($file) => str_contains($file, '*')) ); (new Actions\DeleteDirectories( @@ -104,14 +101,9 @@ public function handle(Filesystem $filesystem, ConfigContract $config) ))->handle( (new Collection($directories)) ->map(fn ($directory) => $this->laravel->basePath($directory)) - ->map(static function ($directory) use ($filesystem) { - return str_contains($directory, '*') - ? [...$filesystem->glob($directory)] - : $directory; - })->flatten() - ->reject(static function ($directory) { - return str_contains($directory, '*'); - }) + ->map(static fn ($directory) => str_contains($directory, '*') ? [...$filesystem->glob($directory)] : $directory) + ->flatten() + ->reject(static fn ($directory) => str_contains($directory, '*')) ); TerminatingConsole::before(function () { diff --git a/src/Foundation/Console/ServeCommand.php b/src/Foundation/Console/ServeCommand.php index db238e72d..c658aa982 100644 --- a/src/Foundation/Console/ServeCommand.php +++ b/src/Foundation/Console/ServeCommand.php @@ -21,7 +21,7 @@ class ServeCommand extends Command { /** {@inheritDoc} */ #[\Override] - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { if ( class_exists(ComposerConfig::class, false) @@ -30,10 +30,16 @@ class_exists(ComposerConfig::class, false) ComposerConfig::disableProcessTimeout(); } - $workers = getenv('PHP_CLI_SERVER_WORKERS'); + $serverWorkers = getenv('PHP_CLI_SERVER_WORKERS'); - if (\is_string($workers) && filter_var($workers, FILTER_VALIDATE_INT) && ! isset($_ENV['PHP_CLI_SERVER_WORKERS'])) { - $_ENV['PHP_CLI_SERVER_WORKERS'] = (int) $workers; + if (\is_string($serverWorkers) && filter_var($serverWorkers, FILTER_VALIDATE_INT) && ! isset($_ENV['PHP_CLI_SERVER_WORKERS'])) { + /** @var int<2, max>|false $workers */ + $workers = transform( + $serverWorkers, + static fn (int $workers) => $workers > 1 ? $workers : false // @phpstan-ignore argument.type + ); + + $this->phpServerWorkers = $workers; } $this->addPassThroughEnvironmentVariable('TESTBENCH_WORKING_PATH', package_path()); @@ -64,13 +70,15 @@ protected function addPassThroughEnvironmentVariable(string $name, mixed $value) #[\Override] protected function startProcess($hasEnvironment) { - return tap(parent::startProcess($hasEnvironment), function (Process $process) { - TerminatingConsole::before(static function ($signal) use ($process) { - if ($process->isRunning()) { - $process->stop(10, $signal); - } - }); - }); + return parent::startProcess($hasEnvironment); + } + + /** {@inhertiDoc} */ + #[\Override] + public function trap($signals, $callback) + { + /** @phpstan-ignore argument.type */ + TerminatingConsole::before($callback); } /** {@inheritDoc} */ diff --git a/src/Foundation/Console/SyncSkeletonCommand.php b/src/Foundation/Console/SyncSkeletonCommand.php index 797127bfd..79fb71567 100644 --- a/src/Foundation/Console/SyncSkeletonCommand.php +++ b/src/Foundation/Console/SyncSkeletonCommand.php @@ -28,7 +28,7 @@ class SyncSkeletonCommand extends Command /** {@inheritDoc} */ #[\Override] - protected function configure() + protected function configure(): void { parent::configure(); diff --git a/src/Foundation/Console/TestCommand.php b/src/Foundation/Console/TestCommand.php index 45f26fc11..3b7ccfcc5 100644 --- a/src/Foundation/Console/TestCommand.php +++ b/src/Foundation/Console/TestCommand.php @@ -6,8 +6,10 @@ use NunoMaduro\Collision\Adapters\Laravel\Commands\TestCommand as Command; use Orchestra\Sidekick\Env; use Orchestra\Testbench\Features\ParallelRunner; +use Symfony\Component\Console\Input\InputOption; use function Orchestra\Sidekick\is_testbench_cli; +use function Orchestra\Sidekick\package_version_compare; use function Orchestra\Testbench\defined_environment_variables; use function Orchestra\Testbench\package_path; @@ -23,14 +25,16 @@ class TestCommand extends Command */ protected $signature = 'package:test {--without-tty : Disable output to TTY} - {--c|configuration= : Read configuration from XML file} {--compact : Indicates whether the compact printer should be used} - {--coverage : Indicates whether code coverage information should be collected} - {--min= : Indicates the minimum threshold enforcement for code coverage} + {--configuration= : Read configuration from XML file} + {--coverage : Indicates whether the coverage information should be collected} + {--min= : Indicates the minimum threshold enforcement for coverage} {--p|parallel : Indicates if the tests should run in parallel} {--profile : Lists top 10 slowest tests} {--recreate-databases : Indicates if the test databases should be re-created} {--drop-databases : Indicates if the test databases should be dropped} + {--without-databases : Indicates if database configuration should be performed} + {--c|--custom-argument : Add custom env variables} '; /** @@ -42,13 +46,21 @@ class TestCommand extends Command /** {@inheritDoc} */ #[\Override] - public function configure() + public function configure(): void { parent::configure(); if (! is_testbench_cli()) { $this->setHidden(true); } + + if (package_version_compare('nunomaduro/collision', '8.9.4', '>=')) { + $this->addOption( + name: 'without-cache', + mode: InputOption::VALUE_NONE, + description: 'Indicates if cache configuration should be performed', + ); + } } /** {@inheritDoc} */ @@ -96,10 +108,8 @@ protected function paratestArguments($options) $file = $this->phpUnitConfigurationFile(); return (new Collection(parent::paratestArguments($options))) - ->reject(static function (string $option) { - return str_starts_with($option, '--configuration=') - || str_starts_with($option, '--runner='); - })->merge([ + ->reject(static fn (string $option) => str_starts_with($option, '--configuration=') || str_starts_with($option, '--runner=')) + ->merge([ \sprintf('--configuration=%s', $file), \sprintf('--runner=%s', ParallelRunner::class), ])->all(); @@ -132,4 +142,15 @@ protected function paratestEnvironmentVariables() ])->merge(parent::paratestEnvironmentVariables()) ->all(); } + + /** + * Get the configuration file. + * + * @return string + */ + #[\Override] + protected function getConfigurationFile() + { + return $this->phpUnitConfigurationFile(); + } } diff --git a/src/Foundation/Console/TestFallbackCommand.php b/src/Foundation/Console/TestFallbackCommand.php index 0f32c5ede..ea3bc1fa7 100644 --- a/src/Foundation/Console/TestFallbackCommand.php +++ b/src/Foundation/Console/TestFallbackCommand.php @@ -7,6 +7,7 @@ use Symfony\Component\Process\Exception\ProcessSignaledException; use Symfony\Component\Process\Process; +use function Laravel\Prompts\confirm; use function Orchestra\Sidekick\is_testbench_cli; use function Orchestra\Testbench\package_path; use function Orchestra\Testbench\php_binary; @@ -23,14 +24,17 @@ class TestFallbackCommand extends Command */ protected $signature = 'package:test {--without-tty : Disable output to TTY} - {--c|configuration= : Read configuration from XML file} {--compact : Indicates whether the compact printer should be used} - {--coverage : Indicates whether code coverage information should be collected} - {--min= : Indicates the minimum threshold enforcement for code coverage} + {--configuration= : Read configuration from XML file} + {--coverage : Indicates whether the coverage information should be collected} + {--min= : Indicates the minimum threshold enforcement for coverage} {--p|parallel : Indicates if the tests should run in parallel} {--profile : Lists top 10 slowest tests} {--recreate-databases : Indicates if the test databases should be re-created} {--drop-databases : Indicates if the test databases should be dropped} + {--without-cache : Indicates if cache configuration should be performed} + {--without-databases : Indicates if database configuration should be performed} + {--c|--custom-argument : Add custom env variables} '; /** @@ -42,7 +46,7 @@ class TestFallbackCommand extends Command /** {@inheritDoc} */ #[\Override] - public function configure() + public function configure(): void { parent::configure(); @@ -58,13 +62,13 @@ public function configure() */ public function handle() { - if (! $this->confirm('Running tests requires "nunomaduro/collision". Do you wish to install it as a dev dependency?')) { - return 1; + if (! confirm('Running tests requires "nunomaduro/collision". Do you wish to install it as a dev dependency?')) { + return Command::FAILURE; } $this->installCollisionDependencies(); - return 0; + return Command::SUCCESS; } /** @@ -72,9 +76,11 @@ public function handle() * * @return void */ - protected function installCollisionDependencies() + protected function installCollisionDependencies(): void { - $command = \sprintf('%s require "nunomaduro/collision:^%s" --dev', $this->findComposer(), '6.2'); + $version = '8.9'; + + $command = \sprintf('%s require "nunomaduro/collision:^%s" --dev', $this->findComposer(), $version); $process = Process::fromShellCommandline($command, null, null, null, null); @@ -102,7 +108,7 @@ protected function installCollisionDependencies() * * @return string */ - protected function findComposer() + protected function findComposer(): string { $composerPath = package_path('composer.phar'); diff --git a/src/Foundation/Env.php b/src/Foundation/Env.php index 40dd53435..a58084716 100644 --- a/src/Foundation/Env.php +++ b/src/Foundation/Env.php @@ -16,5 +16,6 @@ public static function flushState(): void { static::$putenv = true; static::$repository = null; + static::$customAdapters = []; } } diff --git a/src/Foundation/Events/ServeCommandEnded.php b/src/Foundation/Events/ServeCommandEnded.php index 08447ccf9..e92d97229 100644 --- a/src/Foundation/Events/ServeCommandEnded.php +++ b/src/Foundation/Events/ServeCommandEnded.php @@ -11,7 +11,7 @@ * * @codeCoverageIgnore */ -class ServeCommandEnded +readonly class ServeCommandEnded { /** * Construct a new event. diff --git a/src/Foundation/Events/ServeCommandStarted.php b/src/Foundation/Events/ServeCommandStarted.php index 189ebe7f1..bde62c7d7 100644 --- a/src/Foundation/Events/ServeCommandStarted.php +++ b/src/Foundation/Events/ServeCommandStarted.php @@ -11,7 +11,7 @@ * * @codeCoverageIgnore */ -class ServeCommandStarted +readonly class ServeCommandStarted { /** * Construct a new event. diff --git a/src/Foundation/Http/Kernel.php b/src/Foundation/Http/Kernel.php index b8269ec39..93106f3d5 100644 --- a/src/Foundation/Http/Kernel.php +++ b/src/Foundation/Http/Kernel.php @@ -14,6 +14,7 @@ abstract class Kernel extends HttpKernel * * @return array */ + #[\Override] protected function bootstrappers() { return []; diff --git a/src/Foundation/PackageManifest.php b/src/Foundation/PackageManifest.php index 1104abd0d..3e30d5567 100644 --- a/src/Foundation/PackageManifest.php +++ b/src/Foundation/PackageManifest.php @@ -8,7 +8,6 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\ParallelTesting; use Orchestra\Testbench\Contracts\TestCase as TestCaseContract; -use RuntimeException; use function Orchestra\Sidekick\Filesystem\join_paths; use function Orchestra\Sidekick\is_testbench_cli; @@ -32,7 +31,8 @@ class PackageManifest extends IlluminatePackageManifest * * @var array */ - protected $requiredPackages = [ + protected array $requiredPackages = [ + 'laravel/dusk', 'spatie/laravel-ray', ]; @@ -59,7 +59,7 @@ public function __construct(Filesystem $files, $basePath, $manifestPath, $testbe * @param object|null $testbench * @return void */ - public static function swap($app, $testbench = null) + public static function swap($app, $testbench = null): void { /** @var \Illuminate\Foundation\PackageManifest $base */ $base = $app->make(IlluminatePackageManifest::class); @@ -91,7 +91,7 @@ public function setTestbench($testbench): void */ public function requires(...$packages) { - $this->requiredPackages = array_merge($this->requiredPackages, Arr::wrap($packages)); + $this->requiredPackages = array_merge($this->requiredPackages, Arr::wrap($packages)); // @phpstan-ignore assign.propertyType return $this; } @@ -131,11 +131,8 @@ protected function getManifest() })->filter()->all(); } - /** - * Get all of the package names that should be ignored. - * - * @return array - */ + /** {@inheritDoc} */ + #[\Override] protected function packagesToIgnore() { return []; @@ -146,25 +143,30 @@ protected function packagesToIgnore() * * @return array */ - protected function providersFromRoot() + protected function providersFromRoot(): array { - $composerFile = package_path('composer.json'); + $package = $this->providersFromTestbench(); - if (! is_testbench_cli() || ! is_file($composerFile)) { - return []; - } + return \is_array($package) ? [ + $this->format($package['name']) => $package['extra']['laravel'] ?? [], + ] : []; + } - $package = transform(file_get_contents($composerFile), static function ($json) use ($composerFile) { - if (json_validate($json) === false) { - throw new RuntimeException("Unable to parse [{$composerFile}] file"); - } + /** + * Get testbench root composer file. + * + * @return array{name: string, extra?: array{laravel?: array}}|null + */ + protected function providersFromTestbench(): ?array + { + if (is_testbench_cli() && is_file($composerFile = package_path('composer.json'))) { + /** @var array{name: string, extra?: array{laravel?: array}} $composer */ + $composer = $this->files->json($composerFile); - return json_decode($json, true); - }); + return $composer; + } - return [ - $this->format($package['name']) => $package['extra']['laravel'] ?? [], - ]; + return null; } /** {@inheritDoc} */ diff --git a/src/Foundation/Process/ProcessDecorator.php b/src/Foundation/Process/ProcessDecorator.php index e7026ac12..704d3b209 100644 --- a/src/Foundation/Process/ProcessDecorator.php +++ b/src/Foundation/Process/ProcessDecorator.php @@ -2,6 +2,7 @@ namespace Orchestra\Testbench\Foundation\Process; +use Closure; use Illuminate\Support\Traits\ForwardsCalls; use Symfony\Component\Process\Process; @@ -18,11 +19,11 @@ final class ProcessDecorator * Create a new process decorator instance. * * @param \Symfony\Component\Process\Process $process - * @param array|string $command + * @param (\Closure():(mixed))|array|string $command */ public function __construct( protected Process $process, - protected array|string $command, + protected Closure|array|string $command, ) {} /** diff --git a/src/Foundation/Process/ProcessResult.php b/src/Foundation/Process/ProcessResult.php index df4b65913..45797db1c 100644 --- a/src/Foundation/Process/ProcessResult.php +++ b/src/Foundation/Process/ProcessResult.php @@ -2,13 +2,14 @@ namespace Orchestra\Testbench\Foundation\Process; +use Closure; use Illuminate\Support\Traits\ForwardsCalls; use Symfony\Component\Process\Process; /** * @internal */ -final class ProcessResult +final class ProcessResult extends \Illuminate\Process\ProcessResult { use ForwardsCalls; @@ -29,82 +30,37 @@ final class ProcessResult * Create a new process result instance. * * @param \Symfony\Component\Process\Process $process - * @param array|string $command + * @param (\Closure():(mixed))|array|string $command */ public function __construct( - protected Process $process, - protected array|string $command, - ) {} - - /** - * Get the original command executed by the process. - * - * @return string - */ - public function command() - { - return $this->process->getCommandLine(); - } - - /** - * Determine if the process was successful. - * - * @return bool - */ - public function successful() - { - return $this->process->isSuccessful(); + Process $process, + protected Closure|array|string $command, + ) { + parent::__construct($process); } - /** - * Determine if the process failed. - * - * @return bool - */ - public function failed() + /** {@inheritDoc} */ + #[\Override] + public function output() { - return ! $this->successful(); - } + $output = $this->process->getOutput(); - /** - * Get the exit code of the process. - * - * @return int|null - */ - public function exitCode() - { - return $this->process->getExitCode(); - } + if (! $this->command instanceof Closure) { + return $output; + } - /** - * Get the standard output of the process. - * - * @return mixed - */ - public function output() - { - return $this->process->getOutput(); - } + /** @var array{successful: bool, result: string, exception: \Throwable, parameters: array, message: string} $result */ + $result = json_decode($output, true); - /** - * Determine if the output contains the given string. - * - * @param string $output - * @return bool - */ - public function seeInOutput(string $output) - { - return str_contains($this->output(), $output); - } + if (! $result['successful']) { + throw new $result['exception']( + ...(! empty(array_filter($result['parameters'])) + ? $result['parameters'] + : [$result['message']]) + ); + } - /** - * Get the error output of the process. - * - * @return string - */ - public function errorOutput() - { - return $this->process->getErrorOutput(); + return unserialize($result['result']); } /** diff --git a/src/Foundation/Process/RemoteCommand.php b/src/Foundation/Process/RemoteCommand.php index 82851a26b..b1c58fa46 100644 --- a/src/Foundation/Process/RemoteCommand.php +++ b/src/Foundation/Process/RemoteCommand.php @@ -2,8 +2,10 @@ namespace Orchestra\Testbench\Foundation\Process; +use Closure; use Illuminate\Support\Arr; use Illuminate\Support\ProcessUtils; +use Laravel\SerializableClosure\SerializableClosure; use Symfony\Component\Process\Process; use function Orchestra\Testbench\defined_environment_variables; @@ -30,17 +32,28 @@ public function __construct( /** * Execute the command. * - * @param array|string $command + * @param (\Closure():(mixed))|array|string $command * @return \Orchestra\Testbench\Foundation\Process\ProcessDecorator */ - public function handle(string $commander, array|string $command): ProcessDecorator + public function handle(string $commander, Closure|array|string $command): ProcessDecorator { $env = \is_string($this->env) ? ['APP_ENV' => $this->env] : $this->env; Arr::add($env, 'TESTBENCH_PACKAGE_REMOTE', '(true)'); + if ($command instanceof Closure) { + $env['LARAVEL_INVOKABLE_CLOSURE'] = transform(serialize(new SerializableClosure($command)), function ($invokableClosure) { + return base64_encode($invokableClosure); + }); + + $env['APP_KEY'] = $env['APP_KEY'] ?? config('app.key') ?? false; + $commands = ['invoke-serialized-closure']; + } else { + $commands = Arr::wrap($command); + } + $process = Process::fromShellCommandline( - command: Arr::join([php_binary(true), ProcessUtils::escapeArgument($commander), ...Arr::wrap($command)], ' '), + command: Arr::join([php_binary(true), ProcessUtils::escapeArgument($commander), ...$commands], ' '), cwd: $this->workingPath, env: array_merge(defined_environment_variables(), $env) ); diff --git a/src/Foundation/TestbenchServiceProvider.php b/src/Foundation/TestbenchServiceProvider.php index 8fa3b2169..666c73041 100644 --- a/src/Foundation/TestbenchServiceProvider.php +++ b/src/Foundation/TestbenchServiceProvider.php @@ -21,10 +21,12 @@ class TestbenchServiceProvider extends ServiceProvider */ public function register(): void { - AboutCommand::add('Testbench', fn () => [ - 'Core Version' => class_exists(InstalledVersions::class) ? InstalledVersions::getPrettyVersion('orchestra/testbench-core') : '-', - 'Skeleton Path' => str_replace(package_path(), '', $this->app->basePath()), - ]); + AboutCommand::add('Testbench', fn () => array_filter([ + 'Core Version' => InstalledVersions::getPrettyVersion('orchestra/testbench-core'), + 'Dusk Version' => InstalledVersions::isInstalled('orchestra/testbench-dusk') ? InstalledVersions::getPrettyVersion('orchestra/testbench-dusk') : null, + 'Skeleton Path' => AboutCommand::format($this->app->basePath(), console: fn ($value) => str_replace(package_path(), '', $value)), + 'Version' => InstalledVersions::isInstalled('orchestra/testbench') ? InstalledVersions::getPrettyVersion('orchestra/testbench') : null, + ])); } /** @@ -40,7 +42,6 @@ public function boot(): void ? Console\TestCommand::class : Console\TestFallbackCommand::class, Console\CreateSqliteDbCommand::class, - Console\DevToolCommand::class, Console\DropSqliteDbCommand::class, Console\PurgeSkeletonCommand::class, Console\SyncSkeletonCommand::class, diff --git a/src/Http/Kernel.php b/src/Http/Kernel.php index e85a728b1..e5276d1ee 100644 --- a/src/Http/Kernel.php +++ b/src/Http/Kernel.php @@ -9,61 +9,5 @@ */ final class Kernel extends HttpKernel { - /** - * The application's middleware stack. - * - * These middleware are run during every request to your application. - * - * @var array - */ - protected $middleware = [ - // Middleware\TrustHosts::class, - // Middleware\TrustProxies::class, - Middleware\PreventRequestsDuringMaintenance::class, - \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, - Middleware\TrimStrings::class, - \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, - ]; - - /** - * The application's route middleware groups. - * - * @var array> - */ - protected $middlewareGroups = [ - 'web' => [ - \Illuminate\Cookie\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - // \Illuminate\Session\Middleware\AuthenticateSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - Middleware\VerifyCsrfToken::class, - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ], - - 'api' => [ - 'throttle:api', - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ], - ]; - - /** - * The application's route middleware. - * - * These middleware may be assigned to groups or used individually. - * - * @var array - */ - protected $routeMiddleware = [ - 'auth' => Middleware\Authenticate::class, - 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, - 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, - 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, - 'can' => \Illuminate\Auth\Middleware\Authorize::class, - 'guest' => Middleware\RedirectIfAuthenticated::class, - 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, - 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, - 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, - 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, - ]; + // } diff --git a/src/Http/Middleware/Authenticate.php b/src/Http/Middleware/Authenticate.php deleted file mode 100644 index 4cfad9cf8..000000000 --- a/src/Http/Middleware/Authenticate.php +++ /dev/null @@ -1,19 +0,0 @@ -expectsJson() ? null : route('login'); - } -} diff --git a/src/Http/Middleware/PreventRequestsDuringMaintenance.php b/src/Http/Middleware/PreventRequestsDuringMaintenance.php deleted file mode 100644 index 6bf18a254..000000000 --- a/src/Http/Middleware/PreventRequestsDuringMaintenance.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ - protected $except = [ - // - ]; -} diff --git a/src/Http/Middleware/RedirectIfAuthenticated.php b/src/Http/Middleware/RedirectIfAuthenticated.php deleted file mode 100644 index e8d081464..000000000 --- a/src/Http/Middleware/RedirectIfAuthenticated.php +++ /dev/null @@ -1,26 +0,0 @@ -check()) { - return redirect('/home'); - } - - return $next($request); - } -} diff --git a/src/Http/Middleware/TrimStrings.php b/src/Http/Middleware/TrimStrings.php deleted file mode 100644 index f7eb134e8..000000000 --- a/src/Http/Middleware/TrimStrings.php +++ /dev/null @@ -1,18 +0,0 @@ - - */ - protected $except = [ - 'password', - 'password_confirmation', - ]; -} diff --git a/src/Http/Middleware/TrustHosts.php b/src/Http/Middleware/TrustHosts.php deleted file mode 100644 index 8d740dfed..000000000 --- a/src/Http/Middleware/TrustHosts.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ - public function hosts() - { - return [ - $this->allSubdomainsOfApplicationUrl(), - ]; - } -} diff --git a/src/Http/Middleware/TrustProxies.php b/src/Http/Middleware/TrustProxies.php deleted file mode 100644 index 48102a5ac..000000000 --- a/src/Http/Middleware/TrustProxies.php +++ /dev/null @@ -1,28 +0,0 @@ -|string|null - */ - protected $proxies; - - /** - * The headers that should be used to detect proxies. - * - * @var int - */ - protected $headers = - Request::HEADER_X_FORWARDED_FOR | - Request::HEADER_X_FORWARDED_HOST | - Request::HEADER_X_FORWARDED_PORT | - Request::HEADER_X_FORWARDED_PROTO | - Request::HEADER_X_FORWARDED_AWS_ELB; -} diff --git a/src/Http/Middleware/VerifyCsrfToken.php b/src/Http/Middleware/VerifyCsrfToken.php deleted file mode 100644 index 93841d635..000000000 --- a/src/Http/Middleware/VerifyCsrfToken.php +++ /dev/null @@ -1,24 +0,0 @@ - - */ - protected $except = [ - // - ]; -} diff --git a/src/PHPUnit/AttributeParser.php b/src/PHPUnit/AttributeParser.php index 7b8ce5147..d98af29fc 100644 --- a/src/PHPUnit/AttributeParser.php +++ b/src/PHPUnit/AttributeParser.php @@ -113,10 +113,8 @@ protected static function resolveAttribute(ReflectionAttribute $attribute): arra return rescue(static function () use ($attribute) { /** @var TTestingFeature|null $instance */ $instance = isset(class_implements($attribute->getName())[ResolvableContract::class]) - ? transform($attribute->newInstance(), static function ($instance) { - /** @var \Orchestra\Testbench\Contracts\Attributes\Resolvable $instance */ - return $instance->resolve(); - }) : $attribute->newInstance(); + ? transform($attribute->newInstance(), static fn (ResolvableContract $instance) => $instance->resolve()) /** @phpstan-ignore argument.type */ + : $attribute->newInstance(); if (\is_null($instance)) { return [null, null]; diff --git a/src/PHPUnit/TestCase.php b/src/PHPUnit/TestCase.php index 89828ae22..ede0643d7 100644 --- a/src/PHPUnit/TestCase.php +++ b/src/PHPUnit/TestCase.php @@ -4,8 +4,9 @@ use Orchestra\Testbench\Concerns\HandlesAssertions; use Orchestra\Testbench\Concerns\InteractsWithMockery; +use Throwable; -abstract class TestCase extends \PHPUnit\Framework\TestCase +class TestCase extends \PHPUnit\Framework\TestCase { use HandlesAssertions; use InteractsWithMockery; @@ -16,4 +17,11 @@ protected function tearDown(): void { $this->tearDownTheTestEnvironmentUsingMockery(); } + + /** {@inheritDoc} */ + #[\Override] + protected function transformException(Throwable $error): Throwable + { + return $error; + } } diff --git a/src/TestCase.php b/src/TestCase.php index df304f975..e65e60941 100644 --- a/src/TestCase.php +++ b/src/TestCase.php @@ -3,7 +3,6 @@ namespace Orchestra\Testbench; use Illuminate\Foundation\Testing; -use Throwable; abstract class TestCase extends PHPUnit\TestCase implements Contracts\TestCase { @@ -48,8 +47,6 @@ abstract class TestCase extends PHPUnit\TestCase implements Contracts\TestCase #[\Override] protected function setUp(): void { - static::$latestResponse = null; - $this->setUpTheTestEnvironment(); } @@ -87,7 +84,6 @@ protected function setUpTheTestEnvironmentTraitToBeIgnored(string $use): bool Testing\DatabaseMigrations::class, Testing\DatabaseTransactions::class, Testing\WithoutMiddleware::class, - Testing\WithoutEvents::class, Testing\WithFaker::class, Testing\Concerns\InteractsWithAuthentication::class, Testing\Concerns\InteractsWithConsole::class, @@ -101,15 +97,15 @@ protected function setUpTheTestEnvironmentTraitToBeIgnored(string $use): bool Testing\Concerns\MakesHttpRequests::class, Concerns\ApplicationTestingHooks::class, Concerns\CreatesApplication::class, - Concerns\Database\HandlesConnections::class, - Concerns\HandlesAnnotations::class, Concerns\HandlesDatabases::class, Concerns\HandlesRoutes::class, + Concerns\InteractsWithPest::class, Concerns\InteractsWithPHPUnit::class, Concerns\InteractsWithTestCase::class, Concerns\InteractsWithWorkbench::class, Concerns\Testing::class, Concerns\WithFactories::class, + Concerns\WithLaravelBootstrapFile::class, Concerns\WithWorkbench::class, ]); } @@ -131,9 +127,16 @@ protected function refreshApplication() * * @codeCoverageIgnore */ + #[\Override] public static function setUpBeforeClass(): void { static::setUpBeforeClassUsingPHPUnit(); + + /** @phpstan-ignore class.notFound */ + if (static::usesTestingConcern(Pest\WithPest::class)) { + static::setUpBeforeClassUsingPest(); /** @phpstan-ignore staticMethod.notFound */ + } + static::setUpBeforeClassUsingTestCase(); static::setUpBeforeClassUsingWorkbench(); } @@ -145,25 +148,17 @@ public static function setUpBeforeClass(): void * * @codeCoverageIgnore */ + #[\Override] public static function tearDownAfterClass(): void { static::tearDownAfterClassUsingWorkbench(); static::tearDownAfterClassUsingTestCase(); - static::tearDownAfterClassUsingPHPUnit(); - } - /** - * {@inheritDoc} - * - * @codeCoverageIgnore - */ - #[\Override] - protected function onNotSuccessfulTest(Throwable $exception): void - { - parent::onNotSuccessfulTest( - ! \is_null(static::$latestResponse) - ? static::$latestResponse->transformNotSuccessfulException($exception) - : $exception - ); + /** @phpstan-ignore class.notFound */ + if (static::usesTestingConcern(Pest\WithPest::class)) { + static::tearDownAfterClassUsingPest(); /** @phpstan-ignore staticMethod.notFound */ + } + + static::tearDownAfterClassUsingPHPUnit(); } } diff --git a/src/Workbench/Actions/AddAssetSymlinkFolders.php b/src/Workbench/Actions/AddAssetSymlinkFolders.php index a18a34bee..7563d4aa2 100644 --- a/src/Workbench/Actions/AddAssetSymlinkFolders.php +++ b/src/Workbench/Actions/AddAssetSymlinkFolders.php @@ -63,7 +63,7 @@ public function handle(): void $to = $pair['to']; if (is_symlink($to)) { - windows_os() ? $this->files->deleteDirectory($to) : $this->files->delete($to); + windows_os() ? @rmdir($to) : $this->files->delete($to); } elseif ($this->files->isDirectory($to)) { $this->files->deleteDirectory($to); } diff --git a/src/Workbench/Actions/RemoveAssetSymlinkFolders.php b/src/Workbench/Actions/RemoveAssetSymlinkFolders.php index e1e573448..8f49e5672 100644 --- a/src/Workbench/Actions/RemoveAssetSymlinkFolders.php +++ b/src/Workbench/Actions/RemoveAssetSymlinkFolders.php @@ -50,7 +50,7 @@ public function handle(): void if (is_symlink($to)) { return [$to, function ($to) { - windows_os() ? $this->files->deleteDirectory($to) : $this->files->delete($to); + windows_os() ? @rmdir($to) : $this->files->delete($to); }]; } diff --git a/src/Workbench/Workbench.php b/src/Workbench/Workbench.php index 805181843..fab3c5323 100644 --- a/src/Workbench/Workbench.php +++ b/src/Workbench/Workbench.php @@ -6,14 +6,18 @@ use Illuminate\Console\Command; use Illuminate\Contracts\Foundation\Application as ApplicationContract; use Illuminate\Database\Eloquent\Factories\Factory; +use Illuminate\Foundation\Events\DiagnosingHealth; use Illuminate\Routing\Router; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Event; +use Illuminate\Support\Facades\View; use Illuminate\Support\Str; use Orchestra\Sidekick\Env; use Orchestra\Testbench\Contracts\Config as ConfigContract; use Orchestra\Testbench\Foundation\Config; use ReflectionClass; use Symfony\Component\Finder\Finder; +use Throwable; use function Orchestra\Sidekick\Filesystem\join_paths; use function Orchestra\Testbench\after_resolving; @@ -114,13 +118,45 @@ public static function discoverRoutes(ApplicationContract $app, ConfigContract $ /** @var TWorkbenchDiscoversConfig $discoversConfig */ $discoversConfig = $config->getWorkbenchDiscoversAttributes(); - $app->booted(function ($app) use ($discoversConfig) { - tap($app->make('router'), static function (Router $router) use ($discoversConfig) { - foreach (['web', 'api'] as $group) { - if (($discoversConfig[$group] ?? false) === true) { - if (is_file($route = workbench_path('routes', "{$group}.php"))) { - $router->middleware($group)->group($route); + $healthCheckEnabled = $config->getWorkbenchAttributes()['health'] ?? false; + + $app->booted(static function ($app) use ($discoversConfig, $healthCheckEnabled) { + tap($app->make('router'), static function (Router $router) use ($discoversConfig, $healthCheckEnabled) { + if (($discoversConfig['api'] ?? false) === true) { + if (is_file($route = workbench_path('routes', 'api.php'))) { + $router->middleware('api')->group($route); + } + } + + if ($healthCheckEnabled === true) { + $router->get('/up', static function () { + $exception = null; + + try { + Event::dispatch(new DiagnosingHealth); + } catch (Throwable $error) { + if (app()->hasDebugModeEnabled()) { + throw $error; + } + + report($error); + + $exception = $error->getMessage(); } + + return response( + View::file( + package_path('vendor', 'laravel', 'framework', 'src', 'Illuminate', 'Foundation', 'resources', 'health-up.blade.php'), + ['exception' => $exception], + ), + status: $exception ? 500 : 200, + ); + }); + } + + if (($discoversConfig['web'] ?? false) === true) { + if (is_file($route = workbench_path('routes', 'web.php'))) { + $router->middleware('web')->group($route); } } }); @@ -257,11 +293,7 @@ public static function discoverCommandsRoutes(ApplicationContract $app): void */ public static function configuration(): ConfigContract { - if (\is_null(static::$cachedConfiguration)) { - static::$cachedConfiguration = Config::cacheFromYaml(package_path()); - } - - return static::$cachedConfiguration; + return static::$cachedConfiguration ??= Config::cacheFromYaml(package_path()); } /** diff --git a/src/functions.php b/src/functions.php index 251aff8b1..b85e6842b 100644 --- a/src/functions.php +++ b/src/functions.php @@ -16,6 +16,8 @@ use Illuminate\Testing\PendingCommand; use InvalidArgumentException; use Orchestra\Sidekick; +use PHPUnit\Framework\TestCase as PHPUnitTestCase; +use PHPUnit\Runner\ShutdownHandler; /** * Create Laravel application instance. @@ -25,10 +27,19 @@ * @param string|null $basePath * @param (callable(\Illuminate\Foundation\Application):(void))|null $resolvingCallback * @param array{extra?: array{providers?: array, dont-discover?: array, env?: array}, load_environment_variables?: bool, enabled_package_discoveries?: bool} $options + * @param \Orchestra\Testbench\Foundation\Config|null $config * @return \Orchestra\Testbench\Foundation\Application */ -function container(?string $basePath = null, ?callable $resolvingCallback = null, array $options = []): Foundation\Application -{ +function container( + ?string $basePath = null, + ?callable $resolvingCallback = null, + array $options = [], + ?Foundation\Config $config = null +): Foundation\Application { + if ($config instanceof Foundation\Config) { + return Foundation\Application::makeFromConfig($config, $resolvingCallback, $options); + } + return Foundation\Application::make($basePath, $resolvingCallback, $options); } @@ -53,17 +64,45 @@ function artisan(Contracts\TestCase|ApplicationContract $context, string $comman return $command instanceof PendingCommand ? $command->run() : $command; } +/** + * Emit an exit event within a test. + * + * @param \PHPUnit\Framework\TestCase|object|null $testCase + * @param string|int $status + * @return never + */ +function bail(?object $testCase, string|int $status = 0): never +{ + if ($testCase instanceof PHPUnitTestCase && Sidekick\phpunit_version_compare('12.3.5', '>=')) { + ShutdownHandler::resetMessage(); + } + + exit($status); +} + +/** + * Emit an exit event within a test. + * + * @param \PHPUnit\Framework\TestCase|object|null $testCase + * @param string|int $status + * @return never + */ +function terminate(?object $testCase, string|int $status = 0): never +{ + bail($testCase, $status); +} + /** * Run remote action using Testbench CLI. * * @api * - * @param array|string $command + * @param (\Closure():(mixed))|array|string $command * @param array|string $env * @param bool|null $tty * @return \Orchestra\Testbench\Foundation\Process\ProcessDecorator */ -function remote(array|string $command, array|string $env = [], ?bool $tty = null): Foundation\Process\ProcessDecorator +function remote(Closure|array|string $command, array|string $env = [], ?bool $tty = null): Foundation\Process\ProcessDecorator { $remote = new Foundation\Process\RemoteCommand( package_path(), $env, $tty @@ -78,23 +117,6 @@ function remote(array|string $command, array|string $env = [], ?bool $tty = null return $remote->handle($commander, $command); } -/** - * Run callback only once. - * - * @api - * - * @param mixed $callback - * @return \Closure():mixed - * - * @deprecated 7.55.0 Use `Orchestra\Sidekick\once()` instead. - * - * @codeCoverageIgnore - */ -function once($callback): Closure -{ - return Sidekick\once($callback); -} - /** * Register after resolving callback. * @@ -131,20 +153,6 @@ function load_migration_paths(ApplicationContract $app, array|string $paths): vo }); } -/** - * Get default environment variables. - * - * @return array - * - * @deprecated - * - * @codeCoverageIgnore - */ -function default_environment_variables(): array -{ - return []; -} - /** * Get defined environment variables. * @@ -230,38 +238,33 @@ function transform_realpath_to_relative(string $path, ?string $workingPath = nul } /** - * Transform relative path. + * Get the default skeleton path. * * @api * - * @param string $path - * @param string $workingPath - * @return string - * - * @deprecated 7.55.0 Use `Orchestra\Sidekick\transform_relative_path()` instead. + * @no-named-arguments * - * @codeCoverageIgnore + * @param array|string ...$path + * @return ($path is '' ? string : string|false) */ -function transform_relative_path(string $path, string $workingPath): string +function default_skeleton_path(array|string $path = ''): string|false { - return Sidekick\transform_relative_path($path, $workingPath); + return realpath( + Sidekick\Filesystem\join_paths(__DIR__, '..', 'laravel', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path)) + ); } /** - * Get the default skeleton path. + * Determine if application is bootstrapped using Testbench's default skeleton. * - * @api - * - * @no-named-arguments - * - * @param array|string ...$path - * @return string + * @param string|null $basePath + * @return bool */ -function default_skeleton_path(array|string $path = ''): string +function uses_default_skeleton(?string $basePath = null): bool { - return (string) realpath( - Sidekick\Filesystem\join_paths(__DIR__, '..', 'laravel', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path)) - ); + $basePath ??= base_path(); + + return realpath(Sidekick\Filesystem\join_paths($basePath, 'bootstrap', '.testbench-default-skeleton')) !== false; } /** @@ -311,7 +314,7 @@ function package_path(array|string $path = ''): string return str_starts_with($path, './') ? Sidekick\transform_relative_path($path, $workingPath) - : Sidekick\Filesystem\join_paths($workingPath, $path); + : Sidekick\Filesystem\join_paths(rtrim($workingPath, DIRECTORY_SEPARATOR), $path); } /** @@ -346,23 +349,6 @@ function workbench_path(array|string $path = ''): string return package_path('workbench', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path)); } -/** - * Get the migration path by type. - * - * @api - * - * @param string|null $type - * @return string - * - * @throws \InvalidArgumentException - * - * @deprecated - */ -function laravel_migration_path(?string $type = null): string -{ - return default_migration_path($type); -} - /** * Determine if vendor symlink exists on the laravel application. * @@ -379,8 +365,8 @@ function laravel_vendor_exists(ApplicationContract $app, ?string $workingPath = $appVendorPath = $app->basePath('vendor'); $workingPath ??= package_path('vendor'); - return $filesystem->isFile(join_paths($appVendorPath, 'autoload.php')) && - $filesystem->hash(join_paths($appVendorPath, 'autoload.php')) === $filesystem->hash(join_paths($workingPath, 'autoload.php')); + return $filesystem->isFile(Sidekick\join_paths($appVendorPath, 'autoload.php')) && + $filesystem->hash(Sidekick\join_paths($appVendorPath, 'autoload.php')) === $filesystem->hash(Sidekick\join_paths($workingPath, 'autoload.php')); } /** @@ -398,8 +384,10 @@ function laravel_vendor_exists(ApplicationContract $app, ?string $workingPath = * @return int|bool * * @phpstan-return (TOperator is null ? int : bool) + * + * @codeCoverageIgnore */ -function laravel_version_compare(string $version, ?string $operator = null) +function laravel_version_compare(string $version, ?string $operator = null): int|bool { return Sidekick\laravel_version_compare($version, $operator); } @@ -423,6 +411,8 @@ function laravel_version_compare(string $version, ?string $operator = null) * * @throws \OutOfBoundsException * @throws \RuntimeException + * + * @codeCoverageIgnore */ function package_version_compare(string $package, string $version, ?string $operator = null) { @@ -468,8 +458,10 @@ function php_version_compare(string $version, ?string $operator = null) * * @throws \OutOfBoundsException * @throws \RuntimeException + * + * @codeCoverageIgnore */ -function phpunit_version_compare(string $version, ?string $operator = null) +function phpunit_version_compare(string $version, ?string $operator = null): int|bool { return Sidekick\phpunit_version_compare($version, $operator); } @@ -521,12 +513,10 @@ function laravel_or_fail($app, ?string $caller = null): Application } if (\is_null($caller)) { - $caller = transform(debug_backtrace()[1] ?? null, function ($debug) { - if (isset($debug['class']) && isset($debug['function'])) { - return \sprintf('%s::%s', $debug['class'], $debug['function']); - } - - return $debug['function']; + $caller = transform(debug_backtrace()[1] ?? null, static fn ($debug) => match (true) { + ! \is_array($debug) => null, + isset($debug['class']) => \sprintf('%s::%s', $debug['class'], $debug['function']), + default => $debug['function'], }); } diff --git a/testbench.yaml b/testbench.yaml index 314e155eb..21fc2c408 100644 --- a/testbench.yaml +++ b/testbench.yaml @@ -8,6 +8,7 @@ migrations: - workbench/database/migrations workbench: + health: true discovers: config: true factories: true diff --git a/tests/AnnotationDefineEnvTest.php b/tests/AnnotationDefineEnvTest.php deleted file mode 100644 index e53de9ab5..000000000 --- a/tests/AnnotationDefineEnvTest.php +++ /dev/null @@ -1,80 +0,0 @@ -assertSame('testbench', config('database.default')); - $this->assertSame('testbench', config('testbench.one')); - $this->assertNull(config('testbench.two')); - } - - /** - * @test - * - * @define-env secondConfig - */ - public function it_loads_second_config_helper() - { - $this->assertSame('testbench', config('database.default')); - $this->assertNull(config('testbench.one')); - $this->assertSame('testbench', config('testbench.two')); - } - - /** - * @test - * - * @define-env firstConfig - * @define-env secondConfig - */ - public function it_loads_both_config_helper() - { - $this->assertSame('testbench', config('database.default')); - $this->assertSame('testbench', config('testbench.one')); - $this->assertSame('testbench', config('testbench.two')); - } - - /** - * Define environment setup. - * - * @param Illuminate\Foundation\Application $app - * @return void - */ - protected function firstConfig($app) - { - $app['config']->set('testbench.one', 'testbench'); - } - - /** - * Define environment setup. - * - * @param Illuminate\Foundation\Application $app - * @return void - */ - protected function secondConfig($app) - { - $app['config']->set('testbench.two', 'testbench'); - } - - /** - * Define environment setup. - * - * @param Illuminate\Foundation\Application $app - * @return void - */ - protected function defineEnvironment($app) - { - $app['config']->set('database.default', 'testbench'); - $app['config']->set('database.connections.testbench', [ - 'driver' => 'sqlite', - 'database' => ':memory:', - ]); - } -} diff --git a/tests/AnnotationEnvironmentSetupTest.php b/tests/AnnotationEnvironmentSetupTest.php deleted file mode 100644 index 9e7661ecb..000000000 --- a/tests/AnnotationEnvironmentSetupTest.php +++ /dev/null @@ -1,80 +0,0 @@ -assertSame('testbench', config('database.default')); - $this->assertSame('testbench', config('testbench.one')); - $this->assertNull(config('testbench.two')); - } - - /** - * @test - * - * @environment-setup secondConfig - */ - public function it_loads_second_config_helper() - { - $this->assertSame('testbench', config('database.default')); - $this->assertNull(config('testbench.one')); - $this->assertSame('testbench', config('testbench.two')); - } - - /** - * @test - * - * @environment-setup firstConfig - * @environment-setup secondConfig - */ - public function it_loads_both_config_helper() - { - $this->assertSame('testbench', config('database.default')); - $this->assertSame('testbench', config('testbench.one')); - $this->assertSame('testbench', config('testbench.two')); - } - - /** - * Define environment setup. - * - * @param Illuminate\Foundation\Application $app - * @return void - */ - protected function firstConfig($app) - { - $app['config']->set('testbench.one', 'testbench'); - } - - /** - * Define environment setup. - * - * @param Illuminate\Foundation\Application $app - * @return void - */ - protected function secondConfig($app) - { - $app['config']->set('testbench.two', 'testbench'); - } - - /** - * Define environment setup. - * - * @param Illuminate\Foundation\Application $app - * @return void - */ - protected function defineEnvironment($app) - { - $app['config']->set('database.default', 'testbench'); - $app['config']->set('database.connections.testbench', [ - 'driver' => 'sqlite', - 'database' => ':memory:', - ]); - } -} diff --git a/tests/AssertPublishedFilesTest.php b/tests/AssertPublishedFilesTest.php index 34775578d..fab0c68b3 100644 --- a/tests/AssertPublishedFilesTest.php +++ b/tests/AssertPublishedFilesTest.php @@ -3,12 +3,13 @@ namespace Orchestra\Testbench\Tests; use Orchestra\Testbench\Concerns\InteractsWithPublishedFiles; +use PHPUnit\Framework\Attributes\Test; class AssertPublishedFilesTest extends TestCase { use InteractsWithPublishedFiles; - /** @test */ + #[Test] public function it_can_test_assert_file_contains() { $this->assertFileContains([ @@ -24,7 +25,7 @@ public function it_can_test_assert_file_contains() ], 'composer.json'); } - /** @test */ + #[Test] public function it_can_test_assert_file_exists() { $this->assertFilenameExists('composer.json'); @@ -33,7 +34,7 @@ public function it_can_test_assert_file_exists() $this->assertFilenameNotExists('composer.lock'); } - /** @test */ + #[Test] public function it_can_test_assert_migrations_files() { $this->assertMigrationFileContains([ @@ -45,7 +46,7 @@ public function it_can_test_assert_migrations_files() 'class TestbenchCreateUsersTable extends Migration', ], 'testbench_create_users_table.php', directory: 'migrations'); - $this->assertMigrationFileExists('2014_10_12_100000_testbench_create_password_resets_table.php', 'migrations'); - $this->assertMigrationFileDoesNotExists('2014_10_12_100000_create_password_resets_table.php', 'migrations'); + $this->assertMigrationFileExists('0001_01_01_000000_testbench_create_users_table.php', 'migrations'); + $this->assertMigrationFileDoesNotExists('0001_01_01_000000_create_users_table.php', 'migrations'); } } diff --git a/tests/AttributeEnvironmentSetupTest.php b/tests/AttributeEnvironmentSetupTest.php index 1f8023d07..fd6a25649 100644 --- a/tests/AttributeEnvironmentSetupTest.php +++ b/tests/AttributeEnvironmentSetupTest.php @@ -9,9 +9,8 @@ #[Define('env', 'classConfig')] class AttributeEnvironmentSetupTest extends TestCase { - /** - * Setup the test environment. - */ + /** {@inheritDoc} */ + #[\Override] protected function setUp(): void { static::usesTestingFeature(new Define('env', 'globalConfig')); @@ -19,13 +18,13 @@ protected function setUp(): void parent::setUp(); } - /** @test */ + #[Test] public function it_loads_class_config_helper() { $this->assertSame('testbench', config('testbench.class')); } - /** @test */ + #[Test] #[Define('env', 'firstConfig')] public function it_loads_first_config_helper() { @@ -35,7 +34,7 @@ public function it_loads_first_config_helper() $this->assertNull(config('testbench.two')); } - /** @test */ + #[Test] #[DefineEnvironment('secondConfig')] public function it_loads_second_config_helper() { @@ -45,8 +44,8 @@ public function it_loads_second_config_helper() $this->assertSame('testbench', config('testbench.two')); } - /** @test */ - #[Define('env', 'firstConfig')] + #[Test] + #[DefineEnvironment('firstConfig')] #[DefineEnvironment('secondConfig')] public function it_loads_both_config_helper() { @@ -56,7 +55,7 @@ public function it_loads_both_config_helper() $this->assertSame('testbench', config('testbench.two')); } - /** @test */ + #[Test] #[Define('foo', 'firstConfig')] public function it_doesnt_load_invalid_environment_config() { diff --git a/tests/Attributes/AttributesInheritanceTest.php b/tests/Attributes/AttributesInheritanceTest.php index d7dc70816..020b484d3 100644 --- a/tests/Attributes/AttributesInheritanceTest.php +++ b/tests/Attributes/AttributesInheritanceTest.php @@ -4,14 +4,14 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\BeforeClass; +use PHPUnit\Framework\Attributes\Test; #[WithConfig('fake.parent_attribute', true)] #[WithConfig('fake.override_attribute', 'parent')] abstract class AttributesInheritanceTestBaseTestCase extends TestCase { - /** - * @beforeClass - */ + #[BeforeClass] public static function defineTestingFeatures() { static::usesTestingFeature(new WithConfig('fake.override_attribute_2', 'parent')); @@ -21,21 +21,19 @@ public static function defineTestingFeatures() #[WithConfig('fake.override_attribute', 'child')] class AttributesInheritanceTest extends AttributesInheritanceTestBaseTestCase { - /** - * @beforeClass - */ + #[BeforeClass] public static function defineChildTestingFeatures() { static::usesTestingFeature(new WithConfig('fake.override_attribute_2', 'child')); } - /** @test */ + #[Test] public function it_can_see_parent_attributes() { $this->assertSame(true, config('fake.parent_attribute')); } - /** @test */ + #[Test] public function it_can_override_parent_attributes() { $this->assertSame('child', config('fake.override_attribute')); diff --git a/tests/Attributes/DefineDatabaseTest.php b/tests/Attributes/DefineDatabaseTest.php index bc9fc2396..c17d16c6a 100644 --- a/tests/Attributes/DefineDatabaseTest.php +++ b/tests/Attributes/DefineDatabaseTest.php @@ -7,10 +7,11 @@ use Mockery as m; use Orchestra\Testbench\Attributes\DefineDatabase; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; class DefineDatabaseTest extends TestCase { - /** @test */ + #[Test] public function it_can_resolve_definition() { $attribute = new DefineDatabase('defineCallback', defer: false); @@ -20,7 +21,7 @@ public function it_can_resolve_definition() $this->assertFalse($attribute->defer); } - /** @test */ + #[Test] public function it_can_handle_defer_definition() { $attribute = new DefineDatabase('defineCallback', defer: true); @@ -39,7 +40,7 @@ public function it_can_handle_defer_definition() $callback(); } - /** @test */ + #[Test] public function it_can_handle_eager_definition() { $attribute = new DefineDatabase('defineCallback', defer: false); diff --git a/tests/Attributes/DefineRouteTest.php b/tests/Attributes/DefineRouteTest.php index 7fe7ad32c..1fd1fcdff 100644 --- a/tests/Attributes/DefineRouteTest.php +++ b/tests/Attributes/DefineRouteTest.php @@ -7,10 +7,11 @@ use Mockery as m; use Orchestra\Testbench\Attributes\DefineRoute; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; class DefineRouteTest extends TestCase { - /** @test */ + #[Test] public function it_can_resolve_definition() { $attribute = new DefineRoute('defineCallback'); @@ -19,7 +20,7 @@ public function it_can_resolve_definition() $this->assertSame('defineCallback', $attribute->method); } - /** @test */ + #[Test] public function it_can_handle_definition() { $attribute = new DefineRoute('defineCallback'); diff --git a/tests/Attributes/DefineTest.php b/tests/Attributes/DefineTest.php index 9244d420f..02a37eaba 100644 --- a/tests/Attributes/DefineTest.php +++ b/tests/Attributes/DefineTest.php @@ -7,10 +7,11 @@ use Orchestra\Testbench\Attributes\DefineEnvironment; use Orchestra\Testbench\Attributes\DefineRoute; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class DefineTest extends TestCase { - /** @test */ + #[Test] public function it_can_resolve_environment_definition() { $attribute = (new Define('env', 'setupEnvironmentData'))->resolve(); @@ -19,7 +20,7 @@ public function it_can_resolve_environment_definition() $this->assertSame('setupEnvironmentData', $attribute->method); } - /** @test */ + #[Test] public function it_can_resolve_database_definition() { $attribute = (new Define('db', 'setupDatabaseData'))->resolve(); @@ -28,7 +29,7 @@ public function it_can_resolve_database_definition() $this->assertSame('setupDatabaseData', $attribute->method); } - /** @test */ + #[Test] public function it_can_resolve_route_definition() { $attribute = (new Define('route', 'setupRouteData'))->resolve(); @@ -37,7 +38,7 @@ public function it_can_resolve_route_definition() $this->assertSame('setupRouteData', $attribute->method); } - /** @test */ + #[Test] public function it_cannot_resolve_unknown_definition() { $attribute = (new Define('unknown', 'setupRouteData'))->resolve(); diff --git a/tests/Attributes/RequiresDatabaseTest.php b/tests/Attributes/RequiresDatabaseTest.php new file mode 100644 index 000000000..5d77d443d --- /dev/null +++ b/tests/Attributes/RequiresDatabaseTest.php @@ -0,0 +1,61 @@ +handle($this->app, function () { + throw new \Exception; + }); + + $this->addToAssertionCount(1); + + $stub = new RequiresDatabase(['pgsql', 'sqlite']); + + $stub->handle($this->app, function () { + throw new \Exception; + }); + + $this->addToAssertionCount(1); + } + + #[Test] + public function it_can_invalidate_unmatched_database() + { + $stub = new RequiresDatabase('mysql'); + + $stub->handle($this->app, function ($method, $parameters) { + $this->assertSame('markTestSkipped', $method); + $this->assertSame(['Requires mysql to configured for "testing" database connection'], $parameters); + }); + + $stub = new RequiresDatabase(['mysql', 'mariadb']); + + $stub->handle($this->app, function ($method, $parameters) { + $this->assertSame('markTestSkipped', $method); + $this->assertSame(['Requires [mysql/mariadb] to be configured for "testing" database connection'], $parameters); + }); + } + + #[Test] + public function it_can_invalidate_unmatched_database_version() + { + $stub = new RequiresDatabase('sqlite', '<2.0.0'); + + $stub->handle($this->app, function ($method, $parameters) { + $this->assertSame('markTestSkipped', $method); + $this->assertSame(['Requires sqlite:<2.0.0 to be configured for "testing" database connection'], $parameters); + }); + } +} diff --git a/tests/Attributes/RequiresEnvTest.php b/tests/Attributes/RequiresEnvTest.php index 3e8bf5d6c..1ab7c36d2 100644 --- a/tests/Attributes/RequiresEnvTest.php +++ b/tests/Attributes/RequiresEnvTest.php @@ -6,15 +6,16 @@ use Mockery as m; use Orchestra\Testbench\Attributes\RequiresEnv; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; class RequiresEnvTest extends TestCase { - /** @test */ + #[Test] public function it_should_run_the_test_when_env_variable_is_missing() { $attribute = new RequiresEnv('TESTBENCH_MISSING_ENV'); - $callback = $attribute->handle($app = m::mock(Application::class), function ($method, $parameters) { + $callback = $attribute->handle(m::mock(Application::class), function ($method, $parameters) { $this->assertSame('markTestSkipped', $method); $this->assertSame(['Missing required environment variable `TESTBENCH_MISSING_ENV`'], $parameters); }); diff --git a/tests/Attributes/RequiresLaravelTest.php b/tests/Attributes/RequiresLaravelTest.php index a3a35b94f..c9813b6dd 100644 --- a/tests/Attributes/RequiresLaravelTest.php +++ b/tests/Attributes/RequiresLaravelTest.php @@ -4,14 +4,13 @@ use Orchestra\Testbench\Attributes\RequiresLaravel; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; class RequiresLaravelTest extends TestCase { - /** - * @test - * - * @dataProvider compatibleVersionDataProvider - */ + #[Test] + #[DataProvider('compatibleVersionDataProvider')] public function it_can_validate_matching_laravel_versions($version) { $stub = new RequiresLaravel($version); @@ -30,7 +29,7 @@ public static function compatibleVersionDataProvider() yield ['>=6.0.0']; } - /** @test */ + #[Test] public function it_can_invalidate_unmatched_laravel_versions() { $stub = new RequiresLaravel('<6.0.0'); diff --git a/tests/Attributes/ResolvesLaravelTest.php b/tests/Attributes/ResolvesLaravelTest.php index e11891903..90ba8204a 100644 --- a/tests/Attributes/ResolvesLaravelTest.php +++ b/tests/Attributes/ResolvesLaravelTest.php @@ -5,10 +5,11 @@ use Illuminate\Foundation\Bootstrap\LoadConfiguration; use Orchestra\Testbench\Attributes\ResolvesLaravel; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class ResolvesLaravelTest extends TestCase { - /** @test */ + #[Test] #[ResolvesLaravel('laravelDefaultConfiguration')] public function it_can_resolve_defined_configuration() { diff --git a/tests/Attributes/UsesFrameworkConfigurationTest.php b/tests/Attributes/UsesFrameworkConfigurationTest.php index bccc5835f..3fc3b7cb3 100644 --- a/tests/Attributes/UsesFrameworkConfigurationTest.php +++ b/tests/Attributes/UsesFrameworkConfigurationTest.php @@ -3,6 +3,7 @@ namespace Orchestra\Testbench\Tests\Attributes; use Illuminate\Foundation\Bootstrap\LoadConfiguration; +use Orchestra\Sidekick\Env; use Orchestra\Testbench\Attributes\UsesFrameworkConfiguration; use Orchestra\Testbench\Tests\TestCase; use PHPUnit\Framework\Attributes\Test; @@ -16,16 +17,26 @@ class UsesFrameworkConfigurationTest extends TestCase */ protected $loadEnvironmentVariables = false; - /** @test */ + #[Test] public function it_can_load_using_testbench_configurations() { $this->assertSame('Orchestra\Testbench\Bootstrap\LoadConfiguration', \get_class($this->app[LoadConfiguration::class])); + + $environment = Env::has('TESTBENCH_PACKAGE_TESTER') ? 'testing' : 'workbench'; + + $this->assertSame($environment, config('app.env')); + $this->assertSame('Illuminate\Foundation\Auth\User', config('auth.providers.users.model')); } - /** @test */ + #[Test] #[UsesFrameworkConfiguration] public function it_can_load_using_laravel_configurations() { $this->assertSame(LoadConfiguration::class, \get_class($this->app[LoadConfiguration::class])); + + $environment = Env::has('TESTBENCH_PACKAGE_TESTER') ? 'testing' : 'production'; + + $this->assertSame($environment, config('app.env')); + $this->assertSame('App\Models\User', config('auth.providers.users.model')); } } diff --git a/tests/Attributes/UsesTestingFeaturesTest.php b/tests/Attributes/UsesTestingFeaturesTest.php index f0d13b25e..b4afcce0b 100644 --- a/tests/Attributes/UsesTestingFeaturesTest.php +++ b/tests/Attributes/UsesTestingFeaturesTest.php @@ -4,12 +4,12 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\BeforeClass; +use PHPUnit\Framework\Attributes\Test; abstract class UsesTestingFeaturesTestBaseTestCase extends TestCase { - /** - * @beforeClass - */ + #[BeforeClass] public static function defineTestingFeatures() { static::usesTestingFeature(new WithConfig('fake.parent_attribute', true)); @@ -21,21 +21,19 @@ public static function defineTestingFeatures() #[WithConfig('fake.override_attribute', 'child')] class UsesTestingFeaturesTest extends UsesTestingFeaturesTestBaseTestCase { - /** - * @beforeClass - */ + #[BeforeClass] public static function defineChildTestingFeatures() { static::usesTestingFeature(new WithConfig('fake.override_attribute_2', 'child')); } - /** @test */ + #[Test] public function it_can_see_parent_attributes() { $this->assertSame(true, config('fake.parent_attribute')); } - /** @test */ + #[Test] public function it_can_override_parent_attributes() { $this->assertSame('child', config('fake.override_attribute')); diff --git a/tests/Attributes/UsesVendorTest.php b/tests/Attributes/UsesVendorTest.php index d4d93bf1a..044855f36 100644 --- a/tests/Attributes/UsesVendorTest.php +++ b/tests/Attributes/UsesVendorTest.php @@ -10,6 +10,7 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Attributes\WithMigration; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Sidekick\Filesystem\join_paths; use function Orchestra\Testbench\package_path; @@ -19,7 +20,7 @@ class UsesVendorTest extends TestCase { use LazilyRefreshDatabase; - /** @test */ + #[Test] #[UsesVendor] public function it_can_uses_vendor_attribute() { @@ -31,7 +32,7 @@ public function it_can_uses_vendor_attribute() ); } - /** @test */ + #[Test] #[UsesVendor] public function it_can_uses_config_from_attribute() { @@ -40,7 +41,7 @@ public function it_can_uses_config_from_attribute() }); } - /** @test */ + #[Test] #[UsesVendor] #[WithMigration] public function it_can_resolve_config_from_container() diff --git a/tests/Attributes/WithCachedViewsTest.php b/tests/Attributes/WithCachedViewsTest.php new file mode 100644 index 000000000..5ad23b511 --- /dev/null +++ b/tests/Attributes/WithCachedViewsTest.php @@ -0,0 +1,54 @@ +make('config'), function (ConfigRepository $config) { + $config->set('view.paths', [workbench_path('resources', 'views')]); + }); + } + + #[Test] + #[WithCachedViews] + public function it_can_cached_views() + { + $compiledPath = realpath($this->getCompiledPathForView('testbench')); + + $this->assertNotFalse($compiledPath); + $this->assertFileExists($compiledPath); + } + + #[Test] + #[Depends('it_can_cached_views')] + public function it_does_not_persist_cache_after_test() + { + $compiledPath = realpath($this->getCompiledPathForView('testbench')); + + $this->assertFalse($compiledPath); + $this->assertFileDoesNotExist($compiledPath); + } + + /** + * Get the compiled path for a view name. + */ + protected function getCompiledPathForView(string $name): string + { + $path = view($name)->getPath(); + + return $this->app->make('blade.compiler')->getCompiledPath(realpath($path) ?: $path); + } +} diff --git a/tests/Attributes/WithConfigTest.php b/tests/Attributes/WithConfigTest.php index 06e19f952..b710d046b 100644 --- a/tests/Attributes/WithConfigTest.php +++ b/tests/Attributes/WithConfigTest.php @@ -5,6 +5,8 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Concerns\WithFixtures; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; class WithConfigTest extends TestCase { @@ -23,18 +25,15 @@ protected function getPackageProviders($app) : []; } - /** @test */ + #[Test] #[WithConfig('testbench.attribute', true)] public function it_can_resolve_defined_configuration() { $this->assertSame(true, config('testbench.attribute')); } - /** - * @test - * - * @group without-parallel - */ + #[Test] + #[Group('without-parallel')] #[WithConfig('testbench.session.attribute', true)] public function it_can_deferred_resolve_defined_configuration() { @@ -43,11 +42,8 @@ public function it_can_deferred_resolve_defined_configuration() $this->assertSame(1, config('testbench.api')); } - /** - * @test - * - * @group without-parallel - */ + #[Test] + #[Group('without-parallel')] #[WithConfig('testbench.session.attribute', true, defer: false)] public function it_can_eagerly_resolve_defined_configuration() { @@ -56,7 +52,7 @@ public function it_can_eagerly_resolve_defined_configuration() $this->assertSame(1, config('testbench.api')); } - /** @test */ + #[Test] public function it_does_not_persist_defined_configuration_between_tests() { $this->assertNull(config('testbench.attribute')); diff --git a/tests/Attributes/WithEnvTest.php b/tests/Attributes/WithEnvTest.php index 21972af24..07b106f29 100644 --- a/tests/Attributes/WithEnvTest.php +++ b/tests/Attributes/WithEnvTest.php @@ -4,13 +4,14 @@ use Illuminate\Foundation\Application; use Mockery as m; +use Orchestra\Sidekick\Env; use Orchestra\Testbench\Attributes\WithEnv; -use Orchestra\Testbench\Foundation\Env; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; class WithEnvTest extends TestCase { - /** @test */ + #[Test] public function it_can_resolve_defined_env_variables() { $attribute = new WithEnv('TESTING_USING_ATTRIBUTE', '(true)'); @@ -26,13 +27,13 @@ public function it_can_resolve_defined_env_variables() $this->assertNull(Env::get('TESTING_USING_ATTRIBUTE')); } - /** @test */ + #[Test] public function it_does_not_persist_defined_env_variables_between_tests() { $this->assertNull(Env::get('TESTING_USING_ATTRIBUTE')); } - /** @test */ + #[Test] public function it_cannot_change_defined_env_variables() { $_ENV['LARAVEL_KEY'] = 'AckfSECXIvnK5r28GVIWUAxmbBSjTsmF'; diff --git a/tests/Attributes/WithImmutableDatesTest.php b/tests/Attributes/WithImmutableDatesTest.php index 4298084b4..c51607898 100644 --- a/tests/Attributes/WithImmutableDatesTest.php +++ b/tests/Attributes/WithImmutableDatesTest.php @@ -8,10 +8,11 @@ use Illuminate\Support\Facades\Date; use Orchestra\Testbench\Attributes\WithImmutableDates; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class WithImmutableDatesTest extends TestCase { - /** @test */ + #[Test] #[WithImmutableDates] public function it_uses_immutable_dates() { diff --git a/tests/Attributes/WithMigrationTest.php b/tests/Attributes/WithMigrationTest.php index 963729c26..c340d06fb 100644 --- a/tests/Attributes/WithMigrationTest.php +++ b/tests/Attributes/WithMigrationTest.php @@ -3,13 +3,14 @@ namespace Orchestra\Testbench\Attributes; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; class WithMigrationTest extends TestCase { - /** @test */ + #[Test] public function it_can_be_resolved() { $this->assertSame(['laravel'], (new WithMigration)->types); - $this->assertSame(['queue'], (new WithMigration('queue'))->types); + $this->assertSame(['laravel'], (new WithMigration('queue'))->types); } } diff --git a/tests/CommanderTest.php b/tests/CommanderTest.php index 2f35bf36c..f063c2cb1 100644 --- a/tests/CommanderTest.php +++ b/tests/CommanderTest.php @@ -5,21 +5,19 @@ use Illuminate\Foundation\Application; use Illuminate\Support\Facades\DB; use Orchestra\Testbench\Concerns\Database\InteractsWithSqliteDatabaseFile; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RequiresOperatingSystem; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Testbench\remote; -/** - * @requires OS Linux|DAR - */ +#[RequiresOperatingSystem('Linux|DAR')] class CommanderTest extends TestCase { use InteractsWithSqliteDatabaseFile; - /** - * @test - * - * @group commander - */ + #[Test] + #[Group('commander')] public function it_can_call_commander_using_cli_and_get_current_version() { $this->withoutSqliteDatabase(function () { @@ -30,11 +28,8 @@ public function it_can_call_commander_using_cli_and_get_current_version() }); } - /** - * @test - * - * @group commander - */ + #[Test] + #[Group('commander')] public function it_can_call_commander_using_cli_and_get_current_environment() { $this->withoutSqliteDatabase(function () { @@ -45,11 +40,8 @@ public function it_can_call_commander_using_cli_and_get_current_environment() }); } - /** - * @test - * - * @group commander - */ + #[Test] + #[Group('commander')] public function it_output_correct_defaults() { $this->withoutSqliteDatabase(function () { @@ -59,16 +51,13 @@ public function it_output_correct_defaults() $output = json_decode($process->getOutput(), true); $this->assertSame('Testbench', $output['environment']['application_name']); - $this->assertSame('ENABLED', $output['environment']['debug_mode']); + $this->assertSame(true, $output['environment']['debug_mode']); $this->assertSame('testing', $output['drivers']['database']); }); } - /** - * @test - * - * @group commander - */ + #[Test] + #[Group('commander')] public function it_output_correct_defaults_with_database_file() { $this->withSqliteDatabase(function () { @@ -78,16 +67,13 @@ public function it_output_correct_defaults_with_database_file() $output = json_decode($process->getOutput(), true); $this->assertSame('Testbench', $output['environment']['application_name']); - $this->assertSame('ENABLED', $output['environment']['debug_mode']); + $this->assertSame(true, $output['environment']['debug_mode']); $this->assertSame('sqlite', $output['drivers']['database']); }); } - /** - * @test - * - * @group commander - */ + #[Test] + #[Group('commander')] public function it_output_correct_defaults_with_environment_overrides() { $this->withSqliteDatabase(function () { @@ -101,45 +87,41 @@ public function it_output_correct_defaults_with_environment_overrides() $output = json_decode($process->getOutput(), true); $this->assertSame('Testbench Tests', $output['environment']['application_name']); - $this->assertSame('OFF', $output['environment']['debug_mode']); + $this->assertSame(false, $output['environment']['debug_mode']); $this->assertSame('testing', $output['drivers']['database']); }); } - /** - * @test - * - * @group commander - */ + #[Test] + #[Group('commander')] public function it_can_call_commander_using_cli_and_run_migration() { $this->withSqliteDatabase(function () { - $process = remote('migrate', ['DB_CONNECTION' => 'sqlite']); - + $process = remote('migrate', [ + 'DB_CONNECTION' => 'sqlite', + ]); $process->mustRun(); $this->assertSame([ + '0001_01_01_000000_testbench_create_users_table', + '0001_01_01_000001_testbench_create_cache_table', + '0001_01_01_000002_testbench_create_jobs_table', '2013_07_26_182750_create_testbench_users_table', - '2014_10_12_000000_testbench_create_users_table', - '2014_10_12_100000_testbench_create_password_resets_table', - '2019_08_19_000000_testbench_create_failed_jobs_table', ], DB::connection('sqlite')->table('migrations')->pluck('migration')->all()); }); } - /** - * @test - * - * @group commander - */ + #[Test] + #[Group('commander')] public function it_can_call_commander_using_cli_and_run_migration_without_default_migration() { $this->withSqliteDatabase(function () { $process = remote('migrate', [ 'DB_CONNECTION' => 'sqlite', 'TESTBENCH_WITHOUT_DEFAULT_MIGRATIONS' => '(true)', + 'APP_MAINTENANCE_STORE' => 'array', + 'CACHE_STORE' => 'array', ]); - $process->mustRun(); $this->assertSame([ diff --git a/tests/Concerns/HandlesAssertionsTest.php b/tests/Concerns/HandlesAssertionsTest.php index 3c60654a5..23bcc82c0 100644 --- a/tests/Concerns/HandlesAssertionsTest.php +++ b/tests/Concerns/HandlesAssertionsTest.php @@ -3,10 +3,11 @@ namespace Orchestra\Testbench\Tests\Concerns; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; class HandlesAssertionsTest extends TestCase { - /** @test */ + #[Test] public function it_should_mark_the_tests_as_skipped_when_condition_is_true() { $this->expectOutputString('Successfully skipped current test'); @@ -16,7 +17,7 @@ public function it_should_mark_the_tests_as_skipped_when_condition_is_true() $this->assertTrue(false, 'Test incorrectly executed.'); } - /** @test */ + #[Test] public function it_should_mark_the_tests_as_skipped_when_condition_is_false() { $this->markTestSkippedWhen(function () { @@ -26,7 +27,7 @@ public function it_should_mark_the_tests_as_skipped_when_condition_is_false() $this->assertTrue(true, 'Test is correctly executed.'); } - /** @test */ + #[Test] public function it_should_mark_the_tests_as_skipped_unless_condition_is_false() { $this->expectOutputString('Successfully skipped current test'); @@ -36,7 +37,7 @@ public function it_should_mark_the_tests_as_skipped_unless_condition_is_false() $this->assertTrue(false, 'Test incorrectly executed.'); } - /** @test */ + #[Test] public function it_should_mark_the_tests_as_skipped_unless_condition_is_true() { $this->markTestSkippedUnless(function () { diff --git a/tests/Concerns/InteractsWithPHPUnitTest.php b/tests/Concerns/InteractsWithPHPUnitTest.php index e720eb2f5..ebe6be7cf 100644 --- a/tests/Concerns/InteractsWithPHPUnitTest.php +++ b/tests/Concerns/InteractsWithPHPUnitTest.php @@ -3,10 +3,11 @@ namespace Orchestra\Testbench\Tests\Concerns; use Orchestra\Testbench\TestCase; +use PHPUnit\Framework\Attributes\Test; class InteractsWithPHPUnitTest extends TestCase { - /** @test */ + #[Test] public function it_can_resolve_the_correct_class_and_method_name() { $this->assertSame(__CLASS__, $this->resolvePhpUnitTestClassName()); diff --git a/tests/Concerns/WithCachedViewsTest.php b/tests/Concerns/WithCachedViewsTest.php new file mode 100644 index 000000000..76a93319d --- /dev/null +++ b/tests/Concerns/WithCachedViewsTest.php @@ -0,0 +1,48 @@ +make('config'), function (ConfigRepository $config) { + $config->set('view.paths', [workbench_path('resources', 'views')]); + }); + } + + /** + * @test + */ + public function it_caches_and_clears_views() + { + $compiledPath = $this->getCompiledPathForView('testbench'); + + $this->assertFileExists($compiledPath); + + $this->tearDownWithCachedViews(); + + $this->assertFileDoesNotExist($compiledPath); + } + + /** + * Get the compiled path for a view name. + */ + protected function getCompiledPathForView(string $name): string + { + $path = view($name)->getPath(); + + return $this->app->make('blade.compiler')->getCompiledPath(realpath($path) ?: $path); + } +} diff --git a/tests/CreatesApplicationTest.php b/tests/CreatesApplicationTest.php index 974e9330e..a8db70af9 100644 --- a/tests/CreatesApplicationTest.php +++ b/tests/CreatesApplicationTest.php @@ -4,13 +4,24 @@ use Illuminate\Foundation\Application; use Orchestra\Testbench\Concerns\CreatesApplication; +use Orchestra\Testbench\Foundation\Application as Testbench; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; class CreatesApplicationTest extends TestCase { use CreatesApplication; - /** @test */ + /** {@inheritDoc} */ + #[\Override] + protected function tearDown(): void + { + Testbench::flushState($this); + + parent::tearDown(); + } + + #[Test] public function it_properly_loads_laravel_application() { $app = $this->createApplication(); diff --git a/tests/Databases/DatabaseMigrationsTest.php b/tests/Databases/DatabaseMigrationsTest.php index d9d25b07d..90a85733e 100644 --- a/tests/Databases/DatabaseMigrationsTest.php +++ b/tests/Databases/DatabaseMigrationsTest.php @@ -10,13 +10,16 @@ use Orchestra\Testbench\Concerns\WithLaravelMigrations; use Orchestra\Testbench\Concerns\WithWorkbench; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; #[WithConfig('database.default', 'testing')] class DatabaseMigrationsTest extends TestCase { - use DatabaseMigrations, WithLaravelMigrations, WithWorkbench; + use DatabaseMigrations; + use WithLaravelMigrations; + use WithWorkbench; - /** @test */ + #[Test] public function it_runs_the_migrations() { $users = DB::table('testbench_users')->where('id', '=', 1)->first(); diff --git a/tests/Databases/DatabaseMigrationsUsingEventsTest.php b/tests/Databases/DatabaseMigrationsUsingEventsTest.php new file mode 100644 index 000000000..698190d98 --- /dev/null +++ b/tests/Databases/DatabaseMigrationsUsingEventsTest.php @@ -0,0 +1,50 @@ +increments('id'); + $table->string('email'); + $table->string('password'); + + $table->timestamps(); + }); + } + + /** {@inheritDoc} */ + #[\Override] + protected function destroyDatabaseMigrations() + { + Schema::dropIfExists('testbench_staffs'); + } + + #[Test] + public function it_create_database_migrations() + { + $this->assertEquals([ + 'id', + 'email', + 'password', + 'created_at', + 'updated_at', + ], Schema::getColumnListing('testbench_staffs')); + } +} diff --git a/tests/Databases/DatabaseTest.php b/tests/Databases/DatabaseTest.php index 3e03b9190..10a5640e3 100644 --- a/tests/Databases/DatabaseTest.php +++ b/tests/Databases/DatabaseTest.php @@ -4,10 +4,11 @@ use Illuminate\Support\Facades\DB; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class DatabaseTest extends TestCase { - /** @test */ + #[Test] public function testbench_doesnt_automatically_create_database_connection() { $this->assertCount(0, DB::getConnections()); diff --git a/tests/Databases/MigrateDatabaseTest.php b/tests/Databases/MigrateDatabaseTest.php index eab7f6961..1fb8a5e09 100644 --- a/tests/Databases/MigrateDatabaseTest.php +++ b/tests/Databases/MigrateDatabaseTest.php @@ -8,6 +8,7 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Concerns\WithWorkbench; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Testbench\artisan; @@ -16,17 +17,14 @@ class MigrateDatabaseTest extends TestCase { use WithWorkbench; - /** - * Define database migrations. - * - * @return void - */ + /** {@inheritDoc} */ + #[\Override] protected function defineDatabaseMigrations() { artisan($this, 'migrate', ['--database' => 'testing']); } - /** @test */ + #[Test] public function it_runs_the_migrations() { $user = DB::table('testbench_users')->where('id', '=', 1)->first(); diff --git a/tests/Databases/MigrateDatabaseWithoutMockedConsoleTest.php b/tests/Databases/MigrateDatabaseWithoutMockedConsoleTest.php index 77f6c0398..f6aa3b771 100644 --- a/tests/Databases/MigrateDatabaseWithoutMockedConsoleTest.php +++ b/tests/Databases/MigrateDatabaseWithoutMockedConsoleTest.php @@ -8,6 +8,7 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Concerns\WithWorkbench; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Testbench\artisan; @@ -16,17 +17,14 @@ class MigrateDatabaseWithoutMockedConsoleTest extends TestCase { use WithWorkbench; - /** - * Define database migrations. - * - * @return void - */ + /** {@inheritDoc} */ + #[\Override] protected function defineDatabaseMigrations() { artisan($this, 'migrate', ['--database' => 'testing']); } - /** @test */ + #[Test] public function it_runs_the_migrations_without_mocked_console() { $users = DB::table('testbench_users')->where('id', '=', 1)->first(); diff --git a/tests/Databases/MigrateWithLaravelMigrationsTest.php b/tests/Databases/MigrateWithLaravelMigrationsTest.php index 37aab4f61..2144e977e 100644 --- a/tests/Databases/MigrateWithLaravelMigrationsTest.php +++ b/tests/Databases/MigrateWithLaravelMigrationsTest.php @@ -9,13 +9,15 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Concerns\WithLaravelMigrations; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; #[WithConfig('database.default', 'testing')] class MigrateWithLaravelMigrationsTest extends TestCase { - use LazilyRefreshDatabase, WithLaravelMigrations; + use LazilyRefreshDatabase; + use WithLaravelMigrations; - /** @test */ + #[Test] public function it_loads_the_migrations() { $now = Carbon::now(); diff --git a/tests/Databases/MigrateWithLaravelMigrationsUsingDatabaseMigrationsTest.php b/tests/Databases/MigrateWithLaravelMigrationsUsingDatabaseMigrationsTest.php index 31149dc21..a5ab4862e 100644 --- a/tests/Databases/MigrateWithLaravelMigrationsUsingDatabaseMigrationsTest.php +++ b/tests/Databases/MigrateWithLaravelMigrationsUsingDatabaseMigrationsTest.php @@ -9,6 +9,7 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Concerns\WithLaravelMigrations; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; #[WithConfig('database.default', 'testing')] class MigrateWithLaravelMigrationsUsingDatabaseMigrationsTest extends TestCase @@ -16,7 +17,7 @@ class MigrateWithLaravelMigrationsUsingDatabaseMigrationsTest extends TestCase use DatabaseMigrations; use WithLaravelMigrations; - /** @test */ + #[Test] public function it_loads_the_migrations() { $now = Carbon::now(); diff --git a/tests/Databases/MigrateWithLaravelMigrationsUsingRefreshDatabaseTest.php b/tests/Databases/MigrateWithLaravelMigrationsUsingRefreshDatabaseTest.php index 800b43106..4e3c36f3c 100644 --- a/tests/Databases/MigrateWithLaravelMigrationsUsingRefreshDatabaseTest.php +++ b/tests/Databases/MigrateWithLaravelMigrationsUsingRefreshDatabaseTest.php @@ -9,6 +9,7 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Concerns\WithLaravelMigrations; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; #[WithConfig('database.default', 'testing')] class MigrateWithLaravelMigrationsUsingRefreshDatabaseTest extends TestCase @@ -16,7 +17,7 @@ class MigrateWithLaravelMigrationsUsingRefreshDatabaseTest extends TestCase use RefreshDatabase; use WithLaravelMigrations; - /** @test */ + #[Test] public function it_loads_the_migrations() { $now = Carbon::now(); diff --git a/tests/Databases/MigrateWithLaravelTest.php b/tests/Databases/MigrateWithLaravelTest.php index b34865f82..0bcf9ba61 100644 --- a/tests/Databases/MigrateWithLaravelTest.php +++ b/tests/Databases/MigrateWithLaravelTest.php @@ -8,18 +8,16 @@ use Orchestra\Testbench\Attributes\DefineDatabase; use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Testbench\after_resolving; -use function Orchestra\Testbench\laravel_migration_path; +use function Orchestra\Testbench\default_migration_path; #[WithConfig('database.default', 'testing')] class MigrateWithLaravelTest extends TestCase { - /** - * @test - * - * @define-db loadApplicationMigrations - */ + #[Test] + #[DefineDatabase('loadApplicationMigrations')] public function it_loads_the_migrations() { $now = Carbon::now(); @@ -38,7 +36,7 @@ public function it_loads_the_migrations() $this->assertTrue(Hash::check('456', $users->password)); } - /** @test */ + #[Test] #[DefineDatabase('runApplicationMigrations')] public function it_runs_the_migrations() { @@ -66,7 +64,7 @@ public function loadApplicationMigrations() public function runApplicationMigrations() { after_resolving($this->app, 'migrator', function ($migrator) { - $migrator->path(laravel_migration_path()); + $migrator->path(default_migration_path()); }); $this->runLaravelMigrations(['--database' => 'testing']); diff --git a/tests/Databases/MigrateWithRealpathAndLaravelTest.php b/tests/Databases/MigrateWithRealpathAndLaravelTest.php index 56882e775..f0bc5f463 100644 --- a/tests/Databases/MigrateWithRealpathAndLaravelTest.php +++ b/tests/Databases/MigrateWithRealpathAndLaravelTest.php @@ -6,15 +6,13 @@ use Illuminate\Support\Facades\Hash; use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; #[WithConfig('database.default', 'testing')] class MigrateWithRealpathAndLaravelTest extends TestCase { - /** - * Define database migrations. - * - * @return void - */ + /** {@inheritDoc} */ + #[\Override] protected function defineDatabaseMigrations() { $this->loadLaravelMigrations(['--database' => 'testing']); @@ -27,7 +25,7 @@ protected function defineDatabaseMigrations() ]); } - /** @test */ + #[Test] public function it_runs_the_migrations() { $users = DB::table('testbench_users')->where('id', '=', 1)->first(); diff --git a/tests/Databases/MigrateWithRealpathTest.php b/tests/Databases/MigrateWithRealpathTest.php index 53a73321c..b655fbb5d 100644 --- a/tests/Databases/MigrateWithRealpathTest.php +++ b/tests/Databases/MigrateWithRealpathTest.php @@ -6,15 +6,13 @@ use Illuminate\Support\Facades\Hash; use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; #[WithConfig('database.default', 'testing')] class MigrateWithRealpathTest extends TestCase { - /** - * Define database migrations. - * - * @return void - */ + /** {@inheritDoc} */ + #[\Override] protected function defineDatabaseMigrations() { // call migrations specific to our tests, e.g. to seed the db @@ -22,7 +20,7 @@ protected function defineDatabaseMigrations() $this->loadMigrationsFrom(realpath(__DIR__.'/../../workbench/database/migrations')); } - /** @test */ + #[Test] public function it_runs_the_migrations() { $users = DB::table('testbench_users')->where('id', '=', 1)->first(); diff --git a/tests/Databases/RefreshDatabaseTest.php b/tests/Databases/RefreshDatabaseTest.php index 2e67d975f..33870476a 100644 --- a/tests/Databases/RefreshDatabaseTest.php +++ b/tests/Databases/RefreshDatabaseTest.php @@ -8,17 +8,22 @@ use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Schema; use Orchestra\Testbench\Attributes\DefineDatabase; +use Orchestra\Testbench\Attributes\ResetRefreshDatabaseState; use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Concerns\WithLaravelMigrations; use Orchestra\Testbench\Concerns\WithWorkbench; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; +#[ResetRefreshDatabaseState] #[WithConfig('database.default', 'testing')] class RefreshDatabaseTest extends TestCase { - use RefreshDatabase, WithLaravelMigrations, WithWorkbench; + use RefreshDatabase; + use WithLaravelMigrations; + use WithWorkbench; - /** @test */ + #[Test] public function it_runs_the_migrations() { $users = DB::table('testbench_users')->where('id', '=', 1)->first(); @@ -35,7 +40,7 @@ public function it_runs_the_migrations() ], Schema::getColumnListing('testbench_users')); } - /** @test */ + #[Test] #[DefineDatabase('addAdditionalTableAtRuntime')] public function it_can_modify_migrations_at_runtime() { @@ -68,7 +73,7 @@ public function addAdditionalTableAtRuntime() }); } - /** @test */ + #[Test] public function it_can_reset_with_refresh_database_on_runtime() { $this->assertTrue(Schema::hasTable('testbench_users')); diff --git a/tests/Databases/RefreshDatabaseUsingEventsTest.php b/tests/Databases/RefreshDatabaseUsingEventsTest.php new file mode 100644 index 000000000..50abbccf6 --- /dev/null +++ b/tests/Databases/RefreshDatabaseUsingEventsTest.php @@ -0,0 +1,45 @@ +increments('id'); + $table->string('email'); + $table->string('password'); + + $table->timestamps(); + }); + + $this->beforeApplicationDestroyed(fn () => Schema::dropIfExists('testbench_staffs')); + } + + #[Test] + public function it_create_database_migrations() + { + $this->assertEquals([ + 'id', + 'email', + 'password', + 'created_at', + 'updated_at', + ], Schema::getColumnListing('testbench_staffs')); + } +} diff --git a/tests/Databases/RefreshDatabaseWithMigrationWithinSetUpTest.php b/tests/Databases/RefreshDatabaseWithMigrationWithinSetUpTest.php index b36226f1c..18082bccc 100644 --- a/tests/Databases/RefreshDatabaseWithMigrationWithinSetUpTest.php +++ b/tests/Databases/RefreshDatabaseWithMigrationWithinSetUpTest.php @@ -9,15 +9,16 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Concerns\WithWorkbench; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; #[WithConfig('database.default', 'testing')] class RefreshDatabaseWithMigrationWithinSetUpTest extends TestCase { - use RefreshDatabase, WithWorkbench; + use RefreshDatabase; + use WithWorkbench; - /** - * Setup the test environment. - */ + /** {@inheritDoc} */ + #[\Override] protected function setUp(): void { parent::setUp(); @@ -25,7 +26,7 @@ protected function setUp(): void $this->loadLaravelMigrations(); } - /** @test */ + #[Test] public function it_runs_the_migrations() { $users = DB::table('testbench_users')->where('id', '=', 1)->first(); diff --git a/tests/Databases/WithMigrationAttributeTest.php b/tests/Databases/WithMigrationAttributeTest.php index cc9f5b56a..5d8b566b0 100644 --- a/tests/Databases/WithMigrationAttributeTest.php +++ b/tests/Databases/WithMigrationAttributeTest.php @@ -7,6 +7,7 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Attributes\WithMigration; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; #[WithMigration] #[WithConfig('database.default', 'testing')] @@ -14,75 +15,72 @@ class WithMigrationAttributeTest extends TestCase { use DatabaseMigrations; - /** @test */ + #[Test] public function it_loads_default_migrations() { $this->assertTrue(Schema::hasTable('users')); - $this->assertTrue(Schema::hasTable('password_resets')); - $this->assertFalse(Schema::hasTable('cache')); - $this->assertFalse(Schema::hasTable('cache_locks')); - $this->assertFalse(Schema::hasTable('jobs')); - $this->assertFalse(Schema::hasTable('job_batches')); - $this->assertTrue(Schema::hasTable('failed_jobs')); - $this->assertFalse(Schema::hasTable('notifications')); - $this->assertFalse(Schema::hasTable('sessions')); + $this->assertTrue(Schema::hasTable('password_reset_tokens')); + $this->assertTrue(Schema::hasTable('cache')); + $this->assertTrue(Schema::hasTable('cache_locks')); + $this->assertTrue(Schema::hasTable('jobs')); + $this->assertTrue(Schema::hasTable('job_batches')); } - /** @test */ + #[Test] #[WithMigration('cache')] public function it_loads_caches_migrations() { $this->assertTrue(Schema::hasTable('users')); - $this->assertTrue(Schema::hasTable('password_resets')); + $this->assertTrue(Schema::hasTable('password_reset_tokens')); $this->assertTrue(Schema::hasTable('cache')); $this->assertTrue(Schema::hasTable('cache_locks')); - $this->assertFalse(Schema::hasTable('jobs')); - $this->assertFalse(Schema::hasTable('job_batches')); + $this->assertTrue(Schema::hasTable('jobs')); + $this->assertTrue(Schema::hasTable('job_batches')); $this->assertTrue(Schema::hasTable('failed_jobs')); $this->assertFalse(Schema::hasTable('notifications')); - $this->assertFalse(Schema::hasTable('sessions')); + $this->assertTrue(Schema::hasTable('sessions')); } - /** @test */ + #[Test] #[WithMigration('notifications')] public function it_loads_notifications_migrations() { $this->assertTrue(Schema::hasTable('users')); - $this->assertTrue(Schema::hasTable('password_resets')); - $this->assertFalse(Schema::hasTable('cache')); - $this->assertFalse(Schema::hasTable('cache_locks')); - $this->assertFalse(Schema::hasTable('jobs')); - $this->assertFalse(Schema::hasTable('job_batches')); + $this->assertTrue(Schema::hasTable('password_reset_tokens')); + $this->assertTrue(Schema::hasTable('cache')); + $this->assertTrue(Schema::hasTable('cache_locks')); + $this->assertTrue(Schema::hasTable('jobs')); + $this->assertTrue(Schema::hasTable('job_batches')); $this->assertTrue(Schema::hasTable('failed_jobs')); $this->assertTrue(Schema::hasTable('notifications')); - $this->assertFalse(Schema::hasTable('sessions')); + $this->assertTrue(Schema::hasTable('sessions')); } - /** @test */ + #[Test] #[WithMigration('queue')] public function it_loads_queue_migrations() { $this->assertTrue(Schema::hasTable('users')); - $this->assertTrue(Schema::hasTable('password_resets')); - $this->assertFalse(Schema::hasTable('cache')); - $this->assertFalse(Schema::hasTable('cache_locks')); + $this->assertTrue(Schema::hasTable('password_reset_tokens')); + $this->assertTrue(Schema::hasTable('cache')); + $this->assertTrue(Schema::hasTable('cache_locks')); $this->assertTrue(Schema::hasTable('jobs')); $this->assertTrue(Schema::hasTable('job_batches')); $this->assertTrue(Schema::hasTable('failed_jobs')); $this->assertFalse(Schema::hasTable('notifications')); - $this->assertFalse(Schema::hasTable('sessions')); + $this->assertTrue(Schema::hasTable('sessions')); } - /** @test */ + #[Test] #[WithMigration('session')] public function it_loads_session_migrations() { $this->assertTrue(Schema::hasTable('users')); - $this->assertTrue(Schema::hasTable('password_resets')); - $this->assertFalse(Schema::hasTable('cache')); - $this->assertFalse(Schema::hasTable('cache_locks')); - $this->assertFalse(Schema::hasTable('jobs')); - $this->assertFalse(Schema::hasTable('job_batches')); + $this->assertTrue(Schema::hasTable('password_reset_tokens')); + $this->assertTrue(Schema::hasTable('cache')); + $this->assertTrue(Schema::hasTable('cache_locks')); + $this->assertTrue(Schema::hasTable('jobs')); + $this->assertTrue(Schema::hasTable('job_batches')); $this->assertTrue(Schema::hasTable('failed_jobs')); $this->assertFalse(Schema::hasTable('notifications')); $this->assertTrue(Schema::hasTable('sessions')); diff --git a/tests/DefaultConfigurationTest.php b/tests/DefaultConfigurationTest.php index d2d397772..6ca722869 100644 --- a/tests/DefaultConfigurationTest.php +++ b/tests/DefaultConfigurationTest.php @@ -8,35 +8,34 @@ use Illuminate\Foundation\Auth\User; use Illuminate\Foundation\Bootstrap\LoadConfiguration; use Illuminate\Support\Facades\Date; +use Orchestra\Sidekick\Env; use Orchestra\Testbench\Attributes\WithConfig; -use Orchestra\Testbench\Foundation\Env; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; #[WithConfig('app.key', 'AckfSECXIvnK5r28GVIWUAxmbBSjTsmF')] class DefaultConfigurationTest extends TestCase { - /** @test */ + #[Test] public function it_can_load_using_testbench_configurations() { $this->assertSame('Orchestra\Testbench\Bootstrap\LoadConfiguration', \get_class($this->app[LoadConfiguration::class])); } - /** @test */ + #[Test] public function it_populate_expected_debug_config() { - $this->assertSame((Env::get('TESTBENCH_PACKAGE_TESTER') === true ? true : false), $this->app['config']['app.debug']); + $this->assertSame((Env::has('TESTBENCH_PACKAGE_TESTER') ? true : false), $this->app['config']['app.debug']); } - /** - * @test - * - * @group phpunit-configuration - */ + #[Test] + #[Group('phpunit-configuration')] public function it_populate_expected_app_key_config() { $this->assertSame('AckfSECXIvnK5r28GVIWUAxmbBSjTsmF', $this->app['config']['app.key']); } - /** @test */ + #[Test] public function it_populate_expected_testing_config() { tap($this->app['config']['database.connections.testing'], function ($config) { @@ -52,19 +51,19 @@ public function it_populate_expected_testing_config() $this->assertFalse($this->usesSqliteInMemoryDatabaseConnection('sqlite')); } - /** @test */ + #[Test] public function it_populate_expected_cache_defaults() { - $this->assertEquals((Env::get('TESTBENCH_PACKAGE_TESTER') === true ? 'file' : 'array'), $this->app['config']['cache.default']); + $this->assertEquals(Env::has('TESTBENCH_PACKAGE_TESTER') ? 'database' : 'array', $this->app['config']['cache.default']); } - /** @test */ + #[Test] public function it_populate_expected_session_defaults() { - $this->assertEquals((Env::get('TESTBENCH_PACKAGE_TESTER') === true ? 'file' : 'array'), $this->app['config']['session.driver']); + $this->assertEquals(Env::has('TESTBENCH_PACKAGE_TESTER') ? 'cookie' : 'array', $this->app['config']['session.driver']); } - /** @test */ + #[Test] public function it_uses_mutable_dates_by_default() { $date = Date::parse('2023-01-01'); @@ -74,7 +73,7 @@ public function it_uses_mutable_dates_by_default() $this->assertNotInstanceOf(DateTimeImmutable::class, $date); } - /** @test */ + #[Test] public function it_resolve_the_default_user_model() { $this->assertSame(User::class, $this->app['config']['auth.providers.users.model']); diff --git a/tests/DiscoverPackagesTest.php b/tests/DiscoverPackagesTest.php index 27c70a1ff..57280be0c 100644 --- a/tests/DiscoverPackagesTest.php +++ b/tests/DiscoverPackagesTest.php @@ -2,11 +2,13 @@ namespace Orchestra\Testbench\Tests; +use PHPUnit\Framework\Attributes\Test; + class DiscoverPackagesTest extends TestCase { protected $enablesPackageDiscoveries = true; - /** @test */ + #[Test] public function it_can_auto_detect_packages() { $loadedProviders = collect($this->app->getLoadedProviders())->keys()->all(); diff --git a/tests/DontDiscoverPackagesTest.php b/tests/DontDiscoverPackagesTest.php index 272365771..fe77fb1bf 100644 --- a/tests/DontDiscoverPackagesTest.php +++ b/tests/DontDiscoverPackagesTest.php @@ -2,19 +2,18 @@ namespace Orchestra\Testbench\Tests; +use PHPUnit\Framework\Attributes\Test; + class DontDiscoverPackagesTest extends TestCase { - /** - * Ignore package discovery from. - * - * @return array - */ + /** {@inheritDoc} */ + #[\Override] public function ignorePackageDiscoveriesFrom() { return ['spatie/laravel-ray', '*']; } - /** @test */ + #[Test] public function it_cant_auto_detect_packages() { $loadedProviders = collect($this->app->getLoadedProviders())->keys()->all(); diff --git a/tests/Exceptions/DeprecatedExceptionTest.php b/tests/Exceptions/DeprecatedExceptionTest.php new file mode 100644 index 000000000..3e5dbca21 --- /dev/null +++ b/tests/Exceptions/DeprecatedExceptionTest.php @@ -0,0 +1,18 @@ +assertStringContainsString('Error'.PHP_EOL.PHP_EOL.__FILE__.':3', (string) $exception); + } +} diff --git a/tests/Factories/UserFactoryTest.php b/tests/Factories/UserFactoryTest.php index b2971ea93..47784ed09 100644 --- a/tests/Factories/UserFactoryTest.php +++ b/tests/Factories/UserFactoryTest.php @@ -3,16 +3,24 @@ namespace Orchestra\Testbench\Tests\Factories; use Carbon\CarbonInterface; -use Illuminate\Foundation\Auth\User; use Orchestra\Testbench\Concerns\WithWorkbench; use Orchestra\Testbench\Factories\UserFactory; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; +use Workbench\App\Models\User; class UserFactoryTest extends TestCase { use WithWorkbench; - /** @test */ + #[Test] + public function it_has_the_default_configuration() + { + $this->assertSame(User::class, config('auth.providers.users.model')); + $this->assertNull(env('AUTH_MODEL')); + } + + #[Test] public function it_can_generate_user() { $user = UserFactory::new()->make(); @@ -24,7 +32,7 @@ public function it_can_generate_user() $this->assertInstanceOf(CarbonInterface::class, $user->email_verified_at); } - /** @test */ + #[Test] public function it_can_generate_unverified_user() { $user = UserFactory::new()->unverified()->make(); diff --git a/tests/Foundation/ApplicationTest.php b/tests/Foundation/ApplicationTest.php index 0bb612d52..2310f2952 100644 --- a/tests/Foundation/ApplicationTest.php +++ b/tests/Foundation/ApplicationTest.php @@ -2,18 +2,26 @@ namespace Orchestra\Testbench\Tests\Foundation; +use Orchestra\Sidekick\Env; use Orchestra\Testbench\Foundation\Application; use Orchestra\Testbench\Foundation\Config; -use Orchestra\Testbench\Foundation\Env; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; class ApplicationTest extends TestCase { - /** - * @test - * - * @group core - */ + /** {@inheritDoc} */ + #[\Override] + protected function tearDown(): void + { + Application::flushState($this); + + parent::tearDown(); + } + + #[Test] + #[Group('core')] public function it_can_create_an_application() { $testbench = new Application(realpath(__DIR__.'/../../laravel')); @@ -31,11 +39,8 @@ public function it_can_create_an_application() $this->assertFalse($testbench->isRunningTestCase()); } - /** - * @test - * - * @group core - */ + #[Test] + #[Group('core')] public function it_can_create_an_application_using_create_helper() { $app = Application::create(realpath(__DIR__.'/../../laravel')); @@ -50,11 +55,8 @@ public function it_can_create_an_application_using_create_helper() $this->assertSame(Env::has('TESTBENCH_PACKAGE_TESTER'), $app->runningUnitTests()); } - /** - * @test - * - * @group core - */ + #[Test] + #[Group('core')] public function it_can_create_an_application_using_create_from_config_helper() { $config = new Config([ diff --git a/tests/Foundation/Bootstrap/CreateVendorSymlinkTest.php b/tests/Foundation/Bootstrap/CreateVendorSymlinkTest.php index 50e339a79..63c99a5c6 100644 --- a/tests/Foundation/Bootstrap/CreateVendorSymlinkTest.php +++ b/tests/Foundation/Bootstrap/CreateVendorSymlinkTest.php @@ -6,6 +6,7 @@ use Orchestra\Testbench\Foundation\Actions\DeleteVendorSymlink; use Orchestra\Testbench\Foundation\Bootstrap\CreateVendorSymlink; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Testbench\container; use function Orchestra\Testbench\laravel_vendor_exists; @@ -13,7 +14,7 @@ class CreateVendorSymlinkTest extends TestCase { - /** @test */ + #[Test] public function it_can_create_vendor_symlink() { $workingPath = package_path('vendor'); @@ -31,7 +32,7 @@ public function it_can_create_vendor_symlink() $this->assertTrue($laravel['TESTBENCH_VENDOR_SYMLINK']); } - /** @test */ + #[Test] public function it_can_skip_existing_vendor_symlink() { $workingPath = package_path('vendor'); diff --git a/tests/Foundation/Bootstrap/LoadMigrationsFromArrayTest.php b/tests/Foundation/Bootstrap/LoadMigrationsFromArrayTest.php index 0677d5b4d..88a6dee29 100644 --- a/tests/Foundation/Bootstrap/LoadMigrationsFromArrayTest.php +++ b/tests/Foundation/Bootstrap/LoadMigrationsFromArrayTest.php @@ -7,15 +7,19 @@ use Mockery as m; use Orchestra\Testbench\Foundation\Bootstrap\LoadMigrationsFromArray; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; +use Workbench\Database\Seeders\TestbenchDatabaseSeeder; + +use function Orchestra\Sidekick\Filesystem\join_paths; class LoadMigrationsFromArrayTest extends TestCase { - /** @test */ + #[Test] public function it_can_register_migrations() { $this->instance('migrator', $migrator = m::mock(Migrator::class)); - $paths = [__DIR__.'/../../migrations']; + $paths = [(string) realpath(join_paths(__DIR__, '..', '..', 'migrations'))]; $migrator->shouldReceive('path')->once()->with($paths[0])->andReturnNull() ->shouldReceive('path')->once()->with($this->app->basePath('migrations'))->andReturnNull(); @@ -23,7 +27,7 @@ public function it_can_register_migrations() (new LoadMigrationsFromArray($paths))->bootstrap($this->app); } - /** @test */ + #[Test] public function it_can_skip_migrations_registration() { $this->instance('migrator', $migrator = m::mock(Migrator::class)); @@ -33,14 +37,14 @@ public function it_can_skip_migrations_registration() (new LoadMigrationsFromArray(false))->bootstrap($this->app); } - /** @test */ + #[Test] public function it_can_seed_database_after_refreshed() { (new LoadMigrationsFromArray(false, [ - 'seeders' => ['\TestbenchDatabaseSeeder'], + 'seeders' => [TestbenchDatabaseSeeder::class], ]))->bootstrap($this->app); - $this->instance('\TestbenchDatabaseSeeder', $seeder = m::mock('TestbenchDatabaseSeeder')); + $this->instance(TestbenchDatabaseSeeder::class, $seeder = m::mock(TestbenchDatabaseSeeder::class)); $seeder->shouldReceive('setContainer')->once()->with($this->app)->andReturnSelf() ->shouldReceive('setCommand')->once()->andReturnSelf() @@ -49,12 +53,12 @@ public function it_can_seed_database_after_refreshed() app('events')->dispatch(new DatabaseRefreshed); } - /** @test */ + #[Test] public function it_can_skip_database_seeding_after_refreshed() { (new LoadMigrationsFromArray(false, false))->bootstrap($this->app); - $this->instance('\TestbenchDatabaseSeeder', $seeder = m::mock('TestbenchDatabaseSeeder')); + $this->instance(TestbenchDatabaseSeeder::class, $seeder = m::mock(TestbenchDatabaseSeeder::class)); $seeder->shouldNotReceive('setContainer')->with($this->app) ->shouldNotReceive('setCommand') diff --git a/tests/Foundation/Concerns/HandlesDatabaseConnectionsTest.php b/tests/Foundation/Concerns/HandlesDatabaseConnectionsTest.php index 2d3e1e16e..e57de0c59 100644 --- a/tests/Foundation/Concerns/HandlesDatabaseConnectionsTest.php +++ b/tests/Foundation/Concerns/HandlesDatabaseConnectionsTest.php @@ -6,12 +6,13 @@ use Mockery as m; use Orchestra\Testbench\Foundation\Concerns\HandlesDatabaseConnections; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; class HandlesDatabaseConnectionsTest extends TestCase { use HandlesDatabaseConnections; - /** @test */ + #[Test] public function it_can_build_mysql_connection() { $config = m::mock(Repository::class); diff --git a/tests/Foundation/ConfigTest.php b/tests/Foundation/ConfigTest.php index bf5c0f12a..84607e8d2 100644 --- a/tests/Foundation/ConfigTest.php +++ b/tests/Foundation/ConfigTest.php @@ -5,10 +5,11 @@ use Orchestra\Testbench\Foundation\Config; use Orchestra\Testbench\Foundation\TestbenchServiceProvider; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; class ConfigTest extends TestCase { - /** @test */ + #[Test] public function it_can_load_configuration_file() { $config = Config::loadFromYaml(__DIR__.'/stubs/'); @@ -44,6 +45,7 @@ public function it_can_load_configuration_file() 'install' => true, 'auth' => false, 'welcome' => null, + 'health' => null, 'sync' => [], 'build' => [], 'assets' => [], @@ -69,7 +71,7 @@ public function it_can_load_configuration_file() ], $config->getWorkbenchDiscoversAttributes()); } - /** @test */ + #[Test] public function it_can_load_default_configuration() { $config = new Config; @@ -101,6 +103,7 @@ public function it_can_load_default_configuration() 'install' => true, 'auth' => false, 'welcome' => null, + 'health' => null, 'sync' => [], 'build' => [], 'assets' => [], @@ -126,7 +129,7 @@ public function it_can_load_default_configuration() ], $config->getWorkbenchDiscoversAttributes()); } - /** @test */ + #[Test] public function it_can_add_additional_providers_to_configuration_file() { $config = Config::loadFromYaml(__DIR__.'/stubs/'); @@ -145,7 +148,7 @@ public function it_can_add_additional_providers_to_configuration_file() ], $config['providers']); } - /** @test */ + #[Test] public function it_cant_add_duplicated_providers_to_configuration_file() { $config = Config::loadFromYaml(__DIR__.'/stubs/'); diff --git a/tests/Foundation/Console/Actions/DeleteDirectoriesTest.php b/tests/Foundation/Console/Actions/DeleteDirectoriesTest.php index 74f5e0c61..6b24467c7 100644 --- a/tests/Foundation/Console/Actions/DeleteDirectoriesTest.php +++ b/tests/Foundation/Console/Actions/DeleteDirectoriesTest.php @@ -6,10 +6,11 @@ use Illuminate\Filesystem\Filesystem; use Mockery as m; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class DeleteDirectoriesTest extends TestCase { - /** @test */ + #[Test] public function it_can_ensure_directory_exists() { $filesystem = m::mock(Filesystem::class); diff --git a/tests/Foundation/Console/Actions/DeleteFilesTest.php b/tests/Foundation/Console/Actions/DeleteFilesTest.php index d4585b909..7d0163630 100644 --- a/tests/Foundation/Console/Actions/DeleteFilesTest.php +++ b/tests/Foundation/Console/Actions/DeleteFilesTest.php @@ -6,10 +6,11 @@ use Illuminate\Filesystem\Filesystem; use Mockery as m; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class DeleteFilesTest extends TestCase { - /** @test */ + #[Test] public function it_can_ensure_directory_exists() { $filesystem = m::mock(Filesystem::class); diff --git a/tests/Foundation/Console/Actions/EnsureDirectoryExistsTest.php b/tests/Foundation/Console/Actions/EnsureDirectoryExistsTest.php index 4c67d8950..8d6ea1995 100644 --- a/tests/Foundation/Console/Actions/EnsureDirectoryExistsTest.php +++ b/tests/Foundation/Console/Actions/EnsureDirectoryExistsTest.php @@ -6,12 +6,13 @@ use Illuminate\Filesystem\Filesystem; use Mockery as m; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Sidekick\Filesystem\join_paths; class EnsureDirectoryExistsTest extends TestCase { - /** @test */ + #[Test] public function it_can_ensure_directory_exists() { $filesystem = m::mock(Filesystem::class); diff --git a/tests/Foundation/Console/Actions/GeneratesFileTest.php b/tests/Foundation/Console/Actions/GeneratesFileTest.php index ff853eed2..47b39d971 100644 --- a/tests/Foundation/Console/Actions/GeneratesFileTest.php +++ b/tests/Foundation/Console/Actions/GeneratesFileTest.php @@ -7,10 +7,13 @@ use Mockery as m; use Orchestra\Testbench\Foundation\Console\Actions\GeneratesFile; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; + +use function Orchestra\Sidekick\Filesystem\join_paths; class GeneratesFileTest extends TestCase { - /** @test */ + #[Test] public function it_can_generates_file() { $filesystem = m::mock(Filesystem::class); @@ -19,8 +22,8 @@ public function it_can_generates_file() $filesystem->shouldReceive('exists')->once()->with('a')->andReturnTrue() ->shouldReceive('exists')->once()->with('b')->andReturnFalse() ->shouldReceive('copy')->once()->with('a', 'b') - ->shouldReceive('exists')->once()->with('./.gitkeep')->andReturnTrue() - ->shouldReceive('delete')->once()->with('./.gitkeep'); + ->shouldReceive('exists')->once()->with(join_paths('.', '.gitkeep'))->andReturnTrue() + ->shouldReceive('delete')->once()->with(join_paths('.', '.gitkeep')); $components->shouldReceive('task')->once()->with('File [b] generated'); @@ -30,7 +33,7 @@ public function it_can_generates_file() ))->handle('a', 'b'); } - /** @test */ + #[Test] public function it_cannot_generates_file_when_file_already_generated() { $filesystem = m::mock(Filesystem::class); @@ -49,7 +52,7 @@ public function it_cannot_generates_file_when_file_already_generated() ))->handle('a', 'b'); } - /** @test */ + #[Test] public function it_can_generates_file_when_file_already_generated_using_force() { $filesystem = m::mock(Filesystem::class); @@ -58,8 +61,8 @@ public function it_can_generates_file_when_file_already_generated_using_force() $filesystem->shouldReceive('exists')->once()->with('a')->andReturnTrue() ->shouldReceive('exists')->never()->with('b') ->shouldReceive('copy')->once()->with('a', 'b') - ->shouldReceive('exists')->once()->with('./.gitkeep')->andReturnTrue() - ->shouldReceive('delete')->once()->with('./.gitkeep'); + ->shouldReceive('exists')->once()->with(join_paths('.', '.gitkeep'))->andReturnTrue() + ->shouldReceive('delete')->once()->with(join_paths('.', '.gitkeep')); $components->shouldReceive('task')->once()->with('File [b] generated'); @@ -70,7 +73,7 @@ public function it_can_generates_file_when_file_already_generated_using_force() ))->handle('a', 'b'); } - /** @test */ + #[Test] public function it_cannot_generates_file_when_source_file_does_not_exists() { $filesystem = m::mock(Filesystem::class); diff --git a/tests/Foundation/Console/CreateSqliteDbCommandTest.php b/tests/Foundation/Console/CreateSqliteDbCommandTest.php index ec5276940..42ff480b3 100644 --- a/tests/Foundation/Console/CreateSqliteDbCommandTest.php +++ b/tests/Foundation/Console/CreateSqliteDbCommandTest.php @@ -5,22 +5,18 @@ use Orchestra\Testbench\Concerns\Database\InteractsWithSqliteDatabaseFile; use Orchestra\Testbench\Foundation\TestbenchServiceProvider; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RequiresOperatingSystem; +use PHPUnit\Framework\Attributes\Test; -/** - * @requires OS Linux|DAR - * - * @group database - */ +#[RequiresOperatingSystem('Linux|DAR')] +#[Group('database')] class CreateSqliteDbCommandTest extends TestCase { use InteractsWithSqliteDatabaseFile; - /** - * Get package providers. - * - * @param \Illuminate\Foundation\Application $app - * @return array> - */ + /** {@inheritDoc} */ + #[\Override] protected function getPackageProviders($app) { return [ @@ -28,7 +24,7 @@ protected function getPackageProviders($app) ]; } - /** @test */ + #[Test] public function it_can_generate_database_using_command() { $this->withoutSqliteDatabase(function () { @@ -42,7 +38,7 @@ public function it_can_generate_database_using_command() }); } - /** @test */ + #[Test] public function it_cannot_generate_database_using_command_when_database_already_exists() { $this->withSqliteDatabase(function () { diff --git a/tests/Foundation/Console/DropSqliteDbCommandTest.php b/tests/Foundation/Console/DropSqliteDbCommandTest.php index 9cb7b0e31..0acc0f9f5 100644 --- a/tests/Foundation/Console/DropSqliteDbCommandTest.php +++ b/tests/Foundation/Console/DropSqliteDbCommandTest.php @@ -5,23 +5,19 @@ use Orchestra\Testbench\Concerns\Database\InteractsWithSqliteDatabaseFile; use Orchestra\Testbench\Foundation\TestbenchServiceProvider; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RequiresOperatingSystem; +use PHPUnit\Framework\Attributes\Test; -/** - * @requires OS Linux|DAR - * - * @group database - * @group without-parallel - */ +#[RequiresOperatingSystem('Linux|DAR')] +#[Group('database')] +#[Group('without-parallel')] class DropSqliteDbCommandTest extends TestCase { use InteractsWithSqliteDatabaseFile; - /** - * Get package providers. - * - * @param \Illuminate\Foundation\Application $app - * @return array> - */ + /** {@inheritDoc} */ + #[\Override] protected function getPackageProviders($app) { return [ @@ -29,7 +25,7 @@ protected function getPackageProviders($app) ]; } - /** @test */ + #[Test] public function it_can_drop_database_using_command() { $this->withSqliteDatabase(function () { @@ -43,7 +39,7 @@ public function it_can_drop_database_using_command() }); } - /** @test */ + #[Test] public function it_cannot_drop_database_using_command_when_database_doesnt_exists() { $this->withoutSqliteDatabase(function () { diff --git a/tests/Foundation/Console/TerminatingConsoleTest.php b/tests/Foundation/Console/TerminatingConsoleTest.php index 77e5bf54a..c97382773 100644 --- a/tests/Foundation/Console/TerminatingConsoleTest.php +++ b/tests/Foundation/Console/TerminatingConsoleTest.php @@ -4,10 +4,11 @@ use Orchestra\Testbench\Foundation\Console\TerminatingConsole; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; class TerminatingConsoleTest extends TestCase { - /** @test */ + #[Test] public function it_can_handle_terminating_callbacks_on_terminal() { $this->assertFalse(isset($_SERVER['TerminatingConsole.before'])); diff --git a/tests/Foundation/EnvTest.php b/tests/Foundation/EnvTest.php index d019f6fdc..25ebd4c96 100644 --- a/tests/Foundation/EnvTest.php +++ b/tests/Foundation/EnvTest.php @@ -2,38 +2,23 @@ namespace Orchestra\Testbench\Tests\Foundation; -use Orchestra\Testbench\Foundation\Env; +use Orchestra\Sidekick\Env; +use Orchestra\Testbench\Attributes\WithEnv; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; -/** - * @group phpunit-configuration - */ +#[Group('phpunit-configuration')] class EnvTest extends TestCase { - /** {@inheritDoc} */ - protected function setUp(): void - { - $_ENV['APP_KEY'] = 'AckfSECXIvnK5r28GVIWUAxmbBSjTsmF'; - - parent::setUp(); - } - - /** {@inheritDoc} */ - protected function tearDown(): void - { - unset($_ENV['APP_KEY']); - - parent::tearDown(); - } - - /** @test */ + #[Test] public function it_can_determined_has_env_values() { $_ENV['TESTING_TRUE_EXAMPLE'] = true; $_ENV['TESTING_FALSE_EXAMPLE'] = false; $_ENV['TESTING_EMPTY_EXAMPLE'] = ''; - $this->assertTrue(Env::has('APP_KEY')); + $this->assertSame(Env::has('TESTBENCH_PACKAGE_TESTER'), Env::has('APP_KEY')); $this->assertFalse(Env::has('ASSET_URL')); $this->assertSame(Env::has('TESTBENCH_PACKAGE_TESTER') ? true : false, Env::has('LOG_DEPRECATIONS_CHANNEL')); $this->assertTrue(Env::has('TESTING_TRUE_EXAMPLE')); @@ -47,17 +32,18 @@ public function it_can_determined_has_env_values() ); } - /** @test */ + #[Test] + #[WithEnv('TESTING_USING_ATTRIBUTE', '(true)')] public function it_can_correctly_forward_env_values() { $_ENV['TESTING_TRUE_EXAMPLE'] = true; $_ENV['TESTING_FALSE_EXAMPLE'] = false; $_ENV['TESTING_EMPTY_EXAMPLE'] = ''; - $this->assertSame('AckfSECXIvnK5r28GVIWUAxmbBSjTsmF', Env::forward('APP_KEY')); + $this->assertSame(Env::has('TESTBENCH_PACKAGE_TESTER') ? 'AckfSECXIvnK5r28GVIWUAxmbBSjTsmF' : false, Env::forward('APP_KEY')); $this->assertFalse(Env::forward('ASSET_URL')); $this->assertSame('(null)', Env::forward('ASSET_URL', null)); - $this->assertSame(Env::get('TESTBENCH_PACKAGE_TESTER') ? '(null)' : false, Env::forward('LOG_DEPRECATIONS_CHANNEL')); + $this->assertSame(Env::has('TESTBENCH_PACKAGE_TESTER') ? '(null)' : false, Env::forward('LOG_DEPRECATIONS_CHANNEL')); $this->assertSame('(null)', Env::forward('LOG_DEPRECATIONS_CHANNEL', null)); $this->assertSame('(true)', Env::forward('TESTING_TRUE_EXAMPLE')); $this->assertSame('(false)', Env::forward('TESTING_FALSE_EXAMPLE')); diff --git a/tests/Foundation/PackageManifestTest.php b/tests/Foundation/PackageManifestTest.php index 64c036cab..6cc5ea6d5 100644 --- a/tests/Foundation/PackageManifestTest.php +++ b/tests/Foundation/PackageManifestTest.php @@ -6,17 +6,16 @@ use Orchestra\Testbench\Attributes\UsesVendor; use Orchestra\Testbench\Foundation\PackageManifest; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Sidekick\Filesystem\join_paths; #[UsesVendor] class PackageManifestTest extends TestCase { - /** - * @test - * - * @group core - */ + #[Test] + #[Group('core')] public function it_can_build_manifest() { if (! \defined('TESTBENCH_WORKING_PATH')) { @@ -31,13 +30,81 @@ public function it_can_build_manifest() $packageManifest->build(); - clearstatcache(true, $manifestPath); + $packages = Collection::make(require $manifestPath); + + $installedPackages = [ + 'nesbot/carbon', + 'nunomaduro/termwind', + // 'spatie/laravel-ray', + ]; + + foreach ($installedPackages as $installedPackage) { + $this->assertTrue(\in_array($installedPackage, $packages->keys()->all()), "Unable to discover {$installedPackage}"); + } + + $this->app['files']->delete($manifestPath); + } + + #[Test] + #[Group('core')] + public function it_can_build_manifest_without_root_composer_file() + { + $packageManifest = new class($this->app['files'], $this->app->basePath(), join_paths(realpath(__DIR__), 'tmp', 'manifest.php'), $this) extends PackageManifest + { + protected function providersFromTestbench(): ?array + { + return null; + } + }; + + $manifestPath = $packageManifest->getManifestPath(); + + $packageManifest->build(); + + $packages = Collection::make(require $manifestPath); + + $installedPackages = [ + 'nesbot/carbon', + 'nunomaduro/termwind', + // 'spatie/laravel-ray', + ]; + + foreach ($installedPackages as $installedPackage) { + $this->assertTrue(\in_array($installedPackage, $packages->keys()->all()), "Unable to discover {$installedPackage}"); + } + + $this->app['files']->delete($manifestPath); + } + + #[Test] + #[Group('core')] + public function it_can_build_manifest_without_any_discovery() + { + $packageManifest = new class($this->app['files'], $this->app->basePath(), join_paths(realpath(__DIR__), 'tmp', 'manifest.php'), $this) extends PackageManifest + { + protected function providersFromTestbench(): ?array + { + return [ + 'name' => 'testbench/example', + 'extra' => [ + 'laravel' => [ + 'dont-discover' => '*', + ], + ], + ]; + } + }; + + $manifestPath = $packageManifest->getManifestPath(); + + $packageManifest->build(); $packages = Collection::make(require $manifestPath); $installedPackages = [ 'nesbot/carbon', - 'spatie/laravel-ray', + 'nunomaduro/termwind', + // 'spatie/laravel-ray', ]; foreach ($installedPackages as $installedPackage) { diff --git a/tests/Foundation/Process/RemoteCommandTest.php b/tests/Foundation/Process/RemoteCommandTest.php index ca4f23e2c..7f50ccb51 100644 --- a/tests/Foundation/Process/RemoteCommandTest.php +++ b/tests/Foundation/Process/RemoteCommandTest.php @@ -21,7 +21,7 @@ class RemoteCommandTest extends TestCase { use InteractsWithSqliteDatabaseFile; - /** @test */ + #[Test] public function it_can_call_remote_and_get_current_version() { $this->withoutSqliteDatabase(function () { @@ -32,6 +32,17 @@ public function it_can_call_remote_and_get_current_version() $this->assertInstanceOf(ProcessResult::class, $result); $this->assertSame('Laravel Framework '.Application::VERSION.PHP_EOL, $process->getOutput()); $this->assertSame('Laravel Framework '.Application::VERSION.PHP_EOL, $result->output()); + + $process = remote(function () { + return 1 + 1; + }); + + $result = $process->mustRun(); + + $this->assertInstanceOf(ProcessDecorator::class, $process); + $this->assertInstanceOf(ProcessResult::class, $result); + $this->assertSame('{"successful":true,"result":"i:2;"}', $process->getOutput()); + $this->assertSame(2, $result->output()); }); } } diff --git a/tests/Foundation/TestbenchServiceProviderTest.php b/tests/Foundation/TestbenchServiceProviderTest.php index f1a84caac..0c643aacd 100644 --- a/tests/Foundation/TestbenchServiceProviderTest.php +++ b/tests/Foundation/TestbenchServiceProviderTest.php @@ -8,18 +8,14 @@ use Orchestra\Testbench\Foundation\Console\TestFallbackCommand; use Orchestra\Testbench\Foundation\TestbenchServiceProvider; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; -/** - * @covers \Orchestra\Testbench\Foundation\TestbenchServiceProvider - */ +#[CoversClass(TestbenchServiceProvider::class)] class TestbenchServiceProviderTest extends TestCase { - /** - * Get package providers. - * - * @param \Illuminate\Foundation\Application $app - * @return array> - */ + /** {@inheritDoc} */ + #[\Override] protected function getPackageProviders($app) { return [ @@ -27,7 +23,7 @@ protected function getPackageProviders($app) ]; } - /** @test */ + #[Test] public function it_register_the_correct_command() { tap($this->app[ConsoleKernel::class]->all(), function ($commands) { diff --git a/tests/Functions/ArtisanTest.php b/tests/Functions/ArtisanTest.php index 949252450..8ab703698 100644 --- a/tests/Functions/ArtisanTest.php +++ b/tests/Functions/ArtisanTest.php @@ -4,12 +4,13 @@ use Illuminate\Console\Command; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Testbench\artisan; class ArtisanTest extends TestCase { - /** @test */ + #[Test] public function it_can_run_artisan_command() { $this->assertSame(Command::SUCCESS, artisan($this, 'env')); diff --git a/tests/Functions/PackagePathTest.php b/tests/Functions/PackagePathTest.php index de1d08bad..daf1741fc 100644 --- a/tests/Functions/PackagePathTest.php +++ b/tests/Functions/PackagePathTest.php @@ -3,30 +3,45 @@ namespace Orchestra\Testbench\Tests\Functions; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; +use function Illuminate\Filesystem\join_paths; use function Orchestra\Testbench\package_path; -/** - * @group workbench - */ +#[Group('workbench')] class PackagePathTest extends TestCase { - /** @test */ + #[Test] public function it_can_use_package_path() { $this->assertSame(realpath(__DIR__.'/../../'), package_path()); $this->assertSame(implode('', [realpath(__DIR__.'/../../'), DIRECTORY_SEPARATOR]), package_path(DIRECTORY_SEPARATOR)); } - /** - * @test - * - * @dataProvider pathDataProvider - */ + #[Test] + #[DataProvider(('pathDataProvider'))] public function it_can_resolve_correct_package_path(string $path) { $this->assertSame( - realpath(__DIR__.DIRECTORY_SEPARATOR.'PackagePathTest.php'), $path + realpath(join_paths(__DIR__, 'PackagePathTest.php')), + package_path(join_paths('./tests', 'Functions', 'PackagePathTest.php')) + ); + + $this->assertSame( + realpath(join_paths(__DIR__, 'PackagePathTest.php')), + package_path(join_paths('tests', 'Functions', 'PackagePathTest.php')) + ); + + $this->assertSame( + realpath(join_paths(__DIR__, 'PackagePathTest.php')), + package_path(DIRECTORY_SEPARATOR.join_paths('tests', 'Functions', 'PackagePathTest.php')) + ); + + $this->assertSame( + realpath(join_paths(__DIR__, 'PackagePathTest.php')), + package_path(join_paths('tests', 'Functions', 'PackagePathTest.php')) ); } diff --git a/tests/Functions/ParseEnvironmentVariablesTest.php b/tests/Functions/ParseEnvironmentVariablesTest.php index 5fb242b3c..c2402f06d 100644 --- a/tests/Functions/ParseEnvironmentVariablesTest.php +++ b/tests/Functions/ParseEnvironmentVariablesTest.php @@ -3,12 +3,13 @@ namespace Orchestra\Testbench\Tests\Functions; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Testbench\parse_environment_variables; class ParseEnvironmentVariablesTest extends TestCase { - /** @test */ + #[Test] public function it_can_parse_environment_variables() { $given = [ diff --git a/tests/Functions/TransformRealpathToRelativeTest.php b/tests/Functions/TransformRealpathToRelativeTest.php index f7b4f871c..ad035691c 100644 --- a/tests/Functions/TransformRealpathToRelativeTest.php +++ b/tests/Functions/TransformRealpathToRelativeTest.php @@ -3,18 +3,18 @@ namespace Orchestra\Testbench\Tests\Functions; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Sidekick\Filesystem\join_paths; use function Orchestra\Testbench\default_skeleton_path; use function Orchestra\Testbench\package_path; use function Orchestra\Testbench\transform_realpath_to_relative; -/** - * @group core - */ +#[Group('core')] class TransformRealpathToRelativeTest extends TestCase { - /** @test */ + #[Test] public function it_can_use_transform_realpath_to_relative() { $this->assertSame('Testbench.php', transform_realpath_to_relative('Testbench.php')); @@ -35,7 +35,7 @@ public function it_can_use_transform_realpath_to_relative() ); } - /** @test */ + #[Test] public function it_can_use_transform_realpath_to_relative_using_custom_working_path() { $this->assertSame( diff --git a/tests/HelpersTest.php b/tests/HelpersTest.php index 44e4d0891..313907b01 100644 --- a/tests/HelpersTest.php +++ b/tests/HelpersTest.php @@ -3,12 +3,13 @@ namespace Orchestra\Testbench\Tests; use Orchestra\Testbench\Exceptions\ApplicationNotAvailableException; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Testbench\laravel_or_fail; class HelpersTest extends TestCase { - /** @test */ + #[Test] public function it_can_compare_laravel_version() { $laravelVersion = \Orchestra\Sidekick\laravel_normalize_version(); @@ -17,7 +18,7 @@ public function it_can_compare_laravel_version() $this->assertTrue(\Orchestra\Testbench\laravel_version_compare($laravelVersion, '==')); } - /** @test */ + #[Test] public function it_can_compare_php_version() { $phpVersion = \Orchestra\Sidekick\php_normalize_version(); @@ -26,7 +27,7 @@ public function it_can_compare_php_version() $this->assertTrue(\Orchestra\Testbench\php_version_compare($phpVersion, '==')); } - /** @test */ + #[Test] public function it_can_compare_phpunit_version() { $phpunitVersion = \Orchestra\Sidekick\phpunit_normalize_version(); @@ -35,7 +36,7 @@ public function it_can_compare_phpunit_version() $this->assertTrue(\Orchestra\Testbench\phpunit_version_compare($phpunitVersion, '==')); } - /** @test */ + #[Test] public function it_can_throw_application_not_available_application_when_app_is_not_laravel() { $this->expectException(ApplicationNotAvailableException::class); diff --git a/tests/Integrations/AggregateServiceProviderTest.php b/tests/Integrations/AggregateServiceProviderTest.php index 6b27d4196..afe79269e 100644 --- a/tests/Integrations/AggregateServiceProviderTest.php +++ b/tests/Integrations/AggregateServiceProviderTest.php @@ -3,23 +3,20 @@ namespace Orchestra\Testbench\Tests\Integrations; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class AggregateServiceProviderTest extends TestCase { - /** - * Get package providers. - * - * @param \Illuminate\Foundation\Application $app - * @return array - */ + /** {@inheritDoc} */ + #[\Override] protected function getPackageProviders($app) { return [ - 'Orchestra\Testbench\Tests\Fixtures\Providers\ParentServiceProvider', + \Orchestra\Testbench\Tests\Fixtures\Providers\ParentServiceProvider::class, ]; } - /** @test */ + #[Test] public function it_populate_expected_services() { $this->assertTrue($this->app->bound('parent.loaded')); diff --git a/tests/Integrations/ApplicationProvidersTest.php b/tests/Integrations/ApplicationProvidersTest.php index 02dd86928..546b17206 100644 --- a/tests/Integrations/ApplicationProvidersTest.php +++ b/tests/Integrations/ApplicationProvidersTest.php @@ -3,10 +3,11 @@ namespace Orchestra\Testbench\Tests\Integrations; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class ApplicationProvidersTest extends TestCase { - /** @test */ + #[Test] public function it_loaded_the_default_services() { $this->assertTrue($this->app->bound('blade.compiler')); diff --git a/tests/Integrations/ApplicationProvidersWithDisabledServicesTest.php b/tests/Integrations/ApplicationProvidersWithDisabledServicesTest.php index 38704bff7..ac7d55a0f 100644 --- a/tests/Integrations/ApplicationProvidersWithDisabledServicesTest.php +++ b/tests/Integrations/ApplicationProvidersWithDisabledServicesTest.php @@ -4,6 +4,7 @@ use Illuminate\View\ViewServiceProvider; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class ApplicationProvidersWithDisabledServicesTest extends TestCase { @@ -14,7 +15,7 @@ protected function overrideApplicationProviders($app) return [ViewServiceProvider::class => false]; } - /** @test */ + #[Test] public function it_does_not_loads_the_default_services() { $this->assertFalse($this->app->bound('blade.compiler')); diff --git a/tests/Integrations/ArtisanTest.php b/tests/Integrations/ArtisanTest.php index df5ebcfa5..c3b33ac5f 100644 --- a/tests/Integrations/ArtisanTest.php +++ b/tests/Integrations/ArtisanTest.php @@ -4,28 +4,25 @@ use Orchestra\Testbench\Attributes\UsesVendor; use Orchestra\Testbench\Tests\TestCase; -use Symfony\Component\Process\PhpExecutableFinder; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; use Symfony\Component\Process\Process; +use function Illuminate\Support\php_binary; use function Orchestra\Testbench\package_path; use function Orchestra\Testbench\remote; #[UsesVendor] class ArtisanTest extends TestCase { - /** - * @test - * - * @group core - */ + #[Test] + #[Group('core')] public function it_can_generate_the_same_output() { - $phpBinary = \defined('PHP_BINARY') ? PHP_BINARY : (new PhpExecutableFinder)->find(); - $remote = remote('--version --no-ansi')->mustRun(); $artisan = (new Process( - command: [$phpBinary, 'artisan', '--version', '--no-ansi'], + command: [php_binary(), 'artisan', '--version', '--no-ansi'], cwd: package_path('laravel'), env: ['TESTBENCH_WORKING_PATH' => package_path()], ))->mustRun(); diff --git a/tests/Integrations/CacheRouteTest.php b/tests/Integrations/CacheRouteTest.php index df42bfe81..f429a2820 100644 --- a/tests/Integrations/CacheRouteTest.php +++ b/tests/Integrations/CacheRouteTest.php @@ -2,33 +2,48 @@ namespace Orchestra\Testbench\Tests\Integrations; +use Illuminate\Support\Facades\Log; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; class CacheRouteTest extends TestCase { - /** - * Setup the test environment. - */ + /** {@inheritDoc} */ + #[\Override] protected function setUp(): void { $this->defineCacheRoutes(<<info('hello'); +})->where(['all' => '.*']); PHP); parent::setUp(); } - /** - * @test - * - * @group without-parallel - */ + #[Test] + #[Group('without-parallel')] public function it_can_cache_route() { $this->get('stubs-controller') ->assertOk() ->assertSee('ExampleController@index'); } + + #[Test] + #[Group('without-parallel')] + public function it_can_cache_closure_route() + { + Log::spy()->shouldReceive('info')->with('hello'); + + $this->get('logger') + ->assertOk(); + } } diff --git a/tests/Integrations/CommandTest.php b/tests/Integrations/CommandTest.php index 28e1fcb5a..35ca63cfa 100644 --- a/tests/Integrations/CommandTest.php +++ b/tests/Integrations/CommandTest.php @@ -4,12 +4,13 @@ use Orchestra\Testbench\Concerns\WithWorkbench; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class CommandTest extends TestCase { use WithWorkbench; - /** @test */ + #[Test] public function it_can_show_expected_output() { $this->artisan('sample:command') diff --git a/tests/Integrations/ConfigTest.php b/tests/Integrations/ConfigTest.php index ea7383cdc..e872f2f3d 100644 --- a/tests/Integrations/ConfigTest.php +++ b/tests/Integrations/ConfigTest.php @@ -2,16 +2,14 @@ namespace Orchestra\Testbench\Tests\Integrations; +use Illuminate\Support\Facades\Config; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class ConfigTest extends TestCase { - /** - * Define environment setup. - * - * @param \Illuminate\Foundation\Application $app - * @return void - */ + /** {@inheritDoc} */ + #[\Override] protected function defineEnvironment($app) { $app['config']->set('database.default', 'testbench'); @@ -21,13 +19,13 @@ protected function defineEnvironment($app) ]); } - /** @test */ + #[Test] public function it_loads_config_facade() { - $this->assertEquals('testbench', \Config::get('database.default')); + $this->assertEquals('testbench', Config::get('database.default')); } - /** @test */ + #[Test] public function it_loads_config_helper() { $this->assertEquals('testbench', config('database.default')); diff --git a/tests/Integrations/CustomConfigurationTest.php b/tests/Integrations/CustomConfigurationTest.php index 0dd8d051c..2e2762b9d 100644 --- a/tests/Integrations/CustomConfigurationTest.php +++ b/tests/Integrations/CustomConfigurationTest.php @@ -3,23 +3,20 @@ namespace Orchestra\Testbench\Tests\Integrations; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class CustomConfigurationTest extends TestCase { - /** - * Get package providers. - * - * @param \Illuminate\Foundation\Application $app - * @return array - */ + /** {@inheritDoc} */ + #[\Override] protected function getPackageProviders($app) { return [ - 'Orchestra\Testbench\Tests\Fixtures\Providers\CustomConfigServiceProvider', + \Orchestra\Testbench\Tests\Fixtures\Providers\CustomConfigServiceProvider::class, ]; } - /** @test */ + #[Test] public function it_can_override_existing_configuration_on_register() { $this->assertSame('bar', config('database.redis.foo')); diff --git a/tests/Integrations/DispatchJobTest.php b/tests/Integrations/DispatchJobTest.php index e5740737a..534b2f85e 100644 --- a/tests/Integrations/DispatchJobTest.php +++ b/tests/Integrations/DispatchJobTest.php @@ -2,18 +2,19 @@ namespace Orchestra\Testbench\Tests; -use Illuminate\Foundation\Testing\Concerns\MocksApplicationServices; +use Illuminate\Support\Facades\Bus; +use PHPUnit\Framework\Attributes\Test; use Workbench\App\Jobs\RegisterUser; class DispatchJobTest extends TestCase { - use MocksApplicationServices; - - /** @test */ + #[Test] public function it_can_triggers_expected_jobs() { - $this->expectsJobs(RegisterUser::class); + Bus::fake(); dispatch(new RegisterUser); + + Bus::assertDispatched(RegisterUser::class); } } diff --git a/tests/Integrations/EncryptionTest.php b/tests/Integrations/EncryptionTest.php index 4bc7c18c8..7c11a23be 100644 --- a/tests/Integrations/EncryptionTest.php +++ b/tests/Integrations/EncryptionTest.php @@ -4,15 +4,14 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; #[WithConfig('app.key', 'AckfSECXIvnK5r28GVIWUAxmbBSjTsmF')] class EncryptionTest extends TestCase { - /** - * @test - * - * @group phpunit-configuration - */ + #[Test] + #[Group('phpunit-configuration')] public function it_can_encrypt_string() { $this->assertIsString(encrypt('laravel')); diff --git a/tests/Integrations/EnvironmentVariablesTest.php b/tests/Integrations/EnvironmentVariablesTest.php new file mode 100644 index 000000000..fc12b091e --- /dev/null +++ b/tests/Integrations/EnvironmentVariablesTest.php @@ -0,0 +1,42 @@ +set('database.default', 'testing'); + } + + /** {@inheritDoc} */ + #[\Override] + protected function defineDatabaseMigrations() + { + $this->loadLaravelMigrations(['--database' => 'testing']); + } + + #[Test] + #[Group('commander')] + public function it_can_be_used_without_having_an_environment_variables_file() + { + if (Env::has('TESTBENCH_PACKAGE_TESTER')) { + $this->markTestSkipped('Will always failed via `package:test` command'); + } + + $user = UserFactory::new()->create(); + + $this->assertFalse(file_exists(realpath(__DIR__.'/../../laravel/.env'))); + $this->assertFalse(file_exists(base_path('./env'))); + + $this->assertInstanceOf(User::class, $user); + } +} diff --git a/tests/Integrations/InlineCacheRouteTest.php b/tests/Integrations/InlineCacheRouteTest.php index e9f2b5b72..20bc23da6 100644 --- a/tests/Integrations/InlineCacheRouteTest.php +++ b/tests/Integrations/InlineCacheRouteTest.php @@ -3,16 +3,17 @@ namespace Orchestra\Testbench\Tests\Integrations; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; class InlineCacheRouteTest extends TestCase { - /** - * @test - * - * @group without-parallel - */ + #[Test] + #[Group('without-parallel')] public function it_can_cache_route() { + $this->assertFalse($this->app->routesAreCached()); + $this->defineCacheRoutes(<<get('stubs-controller') ->assertOk() ->assertSee('ExampleController@index'); + + $this->assertTrue($this->app->routesAreCached()); + + $this->reloadApplication(); + + $this->assertFalse($this->app->routesAreCached()); } } diff --git a/tests/Integrations/LatestResponseExceptionTest.php b/tests/Integrations/LatestResponseExceptionTest.php index 7308bfe1d..fdb0bcdfc 100644 --- a/tests/Integrations/LatestResponseExceptionTest.php +++ b/tests/Integrations/LatestResponseExceptionTest.php @@ -8,9 +8,6 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Tests\TestCase; -/** - * @covers \Orchestra\Testbench\TestCase::onNotSuccessfulTest - */ #[WithConfig('app.debug', false)] class LatestResponseExceptionTest extends TestCase { diff --git a/tests/Integrations/LoadUsingFrameworkConfigurationTest.php b/tests/Integrations/LoadUsingFrameworkConfigurationTest.php new file mode 100644 index 000000000..b904f0a39 --- /dev/null +++ b/tests/Integrations/LoadUsingFrameworkConfigurationTest.php @@ -0,0 +1,33 @@ +assertSame(LoadConfiguration::class, \get_class($this->app[LoadConfiguration::class])); + + $environment = Env::has('TESTBENCH_PACKAGE_TESTER') ? 'testing' : 'production'; + + $this->assertSame($environment, config('app.env')); + $this->assertSame('App\Models\User', config('auth.providers.users.model')); + } + + protected function overrideLaravelConfiguration($app) + { + $app->instance(LoadConfiguration::class, new LoadConfiguration); + + $app->useConfigPath(package_path(['vendor', 'laravel', 'framework', 'config-stubs'])); + } +} diff --git a/tests/Integrations/PhpUnit9DeprecationsTest.php b/tests/Integrations/PhpUnit9DeprecationsTest.php deleted file mode 100644 index c21d2b0d6..000000000 --- a/tests/Integrations/PhpUnit9DeprecationsTest.php +++ /dev/null @@ -1,118 +0,0 @@ -expectException(DeprecatedException::class); - - Log::shouldReceive('channel') - ->once()->with('deprecations') - ->andReturnSelf() - ->shouldReceive('warning') - ->once() - ->withArgs(function ($message) { - return strpos($message, 'zzz in') !== false; - }); - - trigger_error('zzz', E_USER_DEPRECATED); - } - - /** @test */ - public function handle_php81_deprecations_using_phpunit_exception() - { - $this->expectException(DeprecatedException::class); - $this->expectExceptionMessage('zzz'); - - trigger_error('zzz', E_USER_DEPRECATED); - } - - /** - * @test - * - * @define-env defineConvertDeprecationsToExceptions - */ - public function handle_php81_deprecations_using_laravel_exception() - { - $this->expectException(DeprecatedException::class); - $this->expectExceptionMessage('zzz'); - - trigger_error('zzz', E_USER_DEPRECATED); - } - - /** - * @test - * - * @define-env defineSkippedConvertDeprecationsToExceptions - */ - public function handle_php81_deprecations_using_laravel_exception_using_() - { - $this->withoutDeprecationHandling(); - - $this->expectException(ErrorException::class); - $this->expectExceptionMessage('zzz'); - - trigger_error('zzz', E_USER_DEPRECATED); - } - - /** - * Define environment. - * - * @param \Illuminate\Foundation\Application $app - * @return void - */ - protected function defineConvertDeprecationsToExceptions($app) - { - $value = $_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS'] ?? null; - $_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS'] = true; - - $this->beforeApplicationDestroyed(function () use ($value) { - if (\is_null($value)) { - unset($_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS']); - } else { - $_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS'] = $value; - } - }); - } - - /** - * Define environment. - * - * @param \Illuminate\Foundation\Application $app - * @return void - */ - protected function defineSkippedConvertDeprecationsToExceptions($app) - { - $value = $_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS'] ?? null; - $_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS'] = false; - - $this->beforeApplicationDestroyed(function () use ($value) { - if (\is_null($value)) { - unset($_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS']); - } else { - $_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS'] = $value; - } - }); - } -} diff --git a/tests/Integrations/RequestTest.php b/tests/Integrations/RequestTest.php index 730b09eaf..b333b101e 100644 --- a/tests/Integrations/RequestTest.php +++ b/tests/Integrations/RequestTest.php @@ -4,21 +4,18 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; #[WithConfig('app.key', 'AckfSECXIvnK5r28GVIWUAxmbBSjTsmF')] +#[WithConfig('app.url', 'http://localhost:8000')] class RequestTest extends TestCase { - /** - * Define routes setup. - * - * @param \Illuminate\Routing\Router $router - * @return void - */ + /** {@inheritDoc} */ + #[\Override] protected function defineRoutes($router) { - $router->get('hello', ['uses' => function () { - return 'hello world'; - }]); + $router->get('hello', ['uses' => fn () => 'hello world']); } /** @@ -37,21 +34,18 @@ protected function defineWebRoutes($router) }]); } - /** @test */ + #[Test] public function it_can_get_request_information() { $this->call('GET', 'hello?foo=bar'); - $this->assertSame('http://localhost/hello?foo=bar', url()->full()); - $this->assertSame('http://localhost/hello', url()->current()); + $this->assertSame('http://localhost:8000/hello?foo=bar', url()->full()); + $this->assertSame('http://localhost:8000/hello', url()->current()); $this->assertSame(['foo' => 'bar'], request()->all()); } - /** - * @test - * - * @group session - */ + #[Test] + #[Group('session')] public function it_flashes_request_values() { $this->call('GET', 'web/hello'); diff --git a/tests/Integrations/RouteServiceProviderHealthTest.php b/tests/Integrations/RouteServiceProviderHealthTest.php new file mode 100644 index 000000000..2c46892dd --- /dev/null +++ b/tests/Integrations/RouteServiceProviderHealthTest.php @@ -0,0 +1,31 @@ +withRouting( + web: join_paths(__DIR__, 'fixtures', 'web.php'), + health: '/up', + )->create(); + } + + #[Test] + public function it_can_load_health_page() + { + $this->get('/up')->assertOk(); + } +} diff --git a/tests/Integrations/RouteTest.php b/tests/Integrations/RouteTest.php index 1ce835cf6..c92292d67 100644 --- a/tests/Integrations/RouteTest.php +++ b/tests/Integrations/RouteTest.php @@ -2,49 +2,61 @@ namespace Orchestra\Testbench\Tests\Integrations; +use Exception; use Illuminate\Routing\Router; +use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; use Workbench\App\Http\Controllers\ExampleController; +#[WithConfig('app.key', 'AckfSECXIvnK5r28GVIWUAxmbBSjTsmF')] +#[WithConfig('app.url', 'http://localhost:8000')] class RouteTest extends TestCase { - /** - * Define routes setup. - * - * @param \Illuminate\Routing\Router $router - * @return void - */ + /** {@inheritDoc} */ + #[\Override] protected function defineRoutes($router) { + $router->middleware('web')->get('web/test', fn () => 'Test using web'); + $router->middleware('api')->get('api/test', fn () => 'Test using api'); + $router->domain('api.localhost') ->group(function (Router $router) { - $router->get('hello', function () { - return 'hello from api'; - }); + $router->get('hello', fn () => 'hello from api'); }); - $router->get('hello', ['as' => 'hi', 'uses' => function () { - return 'hello world'; - }]); + $router->get('hello', ['as' => 'hi', 'uses' => fn () => 'hello world']); - $router->get('goodbye', function () { - return 'goodbye world'; - })->name('bye'); + $router->get('goodbye', fn () => 'goodbye world')->name('bye'); $router->group(['prefix' => 'boss'], function (Router $router) { - $router->get('hello', ['as' => 'boss.hi', 'uses' => function () { - return 'hello boss'; - }]); + $router->get('hello', ['as' => 'boss.hi', 'uses' => fn () => 'hello boss']); - $router->get('goodbye', function () { - return 'goodbye boss'; - })->name('boss.bye'); + $router->get('goodbye', fn () => 'goodbye boss')->name('boss.bye'); }); $router->resource('foo', ExampleController::class); } - /** @test */ + #[Test] + #[Group('phpunit-configuration')] + public function it_can_resolve_web_group_route() + { + $crawler = $this->call('GET', 'web/test'); + + $this->assertEquals('Test using web', $crawler->getContent()); + } + + #[Test] + public function it_can_resolve_api_group_route() + { + $crawler = $this->call('GET', 'api/test'); + + $this->assertEquals('Test using api', $crawler->getContent()); + } + + #[Test] public function it_can_resolve_get_routes() { $crawler = $this->call('GET', 'hello'); @@ -56,7 +68,7 @@ public function it_can_resolve_get_routes() $this->assertEquals('goodbye world', $crawler->getContent()); } - /** @test */ + #[Test] public function it_can_resolve_get_routes_with_prefixes() { $crawler = $this->call('GET', 'boss/hello'); @@ -68,7 +80,7 @@ public function it_can_resolve_get_routes_with_prefixes() $this->assertEquals('goodbye boss', $crawler->getContent()); } - /** @test */ + #[Test] public function it_can_resolve_resource_controller() { $response = $this->call('GET', 'foo'); @@ -77,7 +89,7 @@ public function it_can_resolve_resource_controller() $this->assertEquals('ExampleController@index', $response->getContent()); } - /** @test */ + #[Test] public function it_can_resolve_domain_route() { $response = $this->get('http://api.localhost/hello'); @@ -86,25 +98,21 @@ public function it_can_resolve_domain_route() $this->assertEquals('hello from api', $response->getContent()); } - /** @test */ + #[Test] public function it_can_resolve_name_routes() { - $this->app['router']->get('passthrough', function () { - return route('bye'); - })->name('pass'); + $this->app['router']->get('passthrough', fn () => route('bye'))->name('pass'); $response = $this->call('GET', route('pass')); $response->assertStatus(200); - $this->assertEquals('http://localhost/goodbye', $response->getContent()); + $this->assertEquals('http://localhost:8000/goodbye', $response->getContent()); } - /** @test */ + #[Test] public function it_can_handle_route_throwing_exception() { - $this->app['router']->get('bad-route', function () { - throw new \Exception('Route error!'); - })->name('bad'); + $this->app['router']->get('bad-route', fn () => throw new Exception('Route error!'))->name('bad'); $response = $this->call('GET', route('bad')); diff --git a/tests/Integrations/SlimSkeletonApplicationTest.php b/tests/Integrations/SlimSkeletonApplicationTest.php new file mode 100644 index 000000000..2314b58f6 --- /dev/null +++ b/tests/Integrations/SlimSkeletonApplicationTest.php @@ -0,0 +1,52 @@ +get(route('welcome')) + ->assertOk(); + } + + #[Test] + public function it_throws_exception_when_trying_to_access_authenticated_routes_as_guest_without_login_route_name() + { + $this->expectException(RouteNotFoundException::class); + $this->expectExceptionMessage('Route [login] not defined.'); + + $this->withoutExceptionHandling() + ->get(route('dashboard')); + } + + #[Test] + #[DefineRoute('defineLoginRoutes')] + public function it_can_be_redirected_to_login_route_name_when_trying_to_access_authenticated_routes() + { + $this->get(route('dashboard')) + ->assertRedirectToRoute('login'); + } + + /** + * Define login routes setup. + * + * @param \Illuminate\Routing\Router $router + * @return void + */ + protected function defineLoginRoutes($router) + { + $router->get('/login', fn () => 'Login')->name('login'); + } +} diff --git a/tests/Integrations/StashRouteTest.php b/tests/Integrations/StashRouteTest.php index 1de1f7dac..46835f9cf 100644 --- a/tests/Integrations/StashRouteTest.php +++ b/tests/Integrations/StashRouteTest.php @@ -8,9 +8,8 @@ class StashRouteTest extends TestCase { - /** - * Setup the test environment. - */ + /** {@inheritDoc} */ + #[\Override] protected function setUp(): void { $this->defineStashRoutes(function () { @@ -20,7 +19,7 @@ protected function setUp(): void parent::setUp(); } - /** @test */ + #[Test] public function it_can_cache_route() { $this->get('stubs-controller') diff --git a/tests/Integrations/TranslationTest.php b/tests/Integrations/TranslationTest.php index 036545865..098bcecc7 100644 --- a/tests/Integrations/TranslationTest.php +++ b/tests/Integrations/TranslationTest.php @@ -3,16 +3,17 @@ namespace Orchestra\Testbench\Tests\Integrations; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class TranslationTest extends TestCase { - /** @test */ + #[Test] public function it_can_resolve_default_language_path() { $this->assertSame(base_path('lang'), $this->app->langPath()); } - /** @test */ + #[Test] public function it_can_resolve_validation_language_string() { $this->assertSame('The name field is required.', __('validation.required', ['attribute' => 'name'])); diff --git a/tests/Integrations/WithFakerTest.php b/tests/Integrations/WithFakerTest.php index a0439d33b..db8159bc0 100644 --- a/tests/Integrations/WithFakerTest.php +++ b/tests/Integrations/WithFakerTest.php @@ -6,18 +6,19 @@ use Illuminate\Foundation\Testing\WithFaker; use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class WithFakerTest extends TestCase { use WithFaker; - /** @test */ + #[Test] public function it_can_use_faker() { $this->assertInstanceOf(Generator::class, $this->faker); } - /** @test */ + #[Test] #[WithConfig('app.faker_locale', 'it_IT')] public function it_can_override_faker_locale() { diff --git a/tests/Integrations/fixtures/web.php b/tests/Integrations/fixtures/web.php new file mode 100644 index 000000000..9f6343bc9 --- /dev/null +++ b/tests/Integrations/fixtures/web.php @@ -0,0 +1,5 @@ + response('Not found!', 404)); diff --git a/tests/PHPUnit/AttributeParserTest.php b/tests/PHPUnit/AttributeParserTest.php index d44a14424..b60771619 100644 --- a/tests/PHPUnit/AttributeParserTest.php +++ b/tests/PHPUnit/AttributeParserTest.php @@ -4,10 +4,11 @@ use Orchestra\Testbench\PHPUnit\AttributeParser; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; class AttributeParserTest extends TestCase { - /** @test */ + #[Test] public function it_can_validate_attribute() { $this->assertFalse(AttributeParser::validAttribute('TestCase::class')); diff --git a/tests/PhpUnit10DeprecationsTest.php b/tests/PhpUnit10DeprecationsTest.php new file mode 100644 index 000000000..4ac89e1b4 --- /dev/null +++ b/tests/PhpUnit10DeprecationsTest.php @@ -0,0 +1,56 @@ +once()->with('deprecations') + ->andReturnSelf() + ->shouldReceive('warning') + ->once() + ->withArgs(fn ($message) => strpos($message, 'zzz in') !== false); + + trigger_error('zzz', E_USER_DEPRECATED); + } + + #[Test] + #[DefineEnvironment('defineConvertDeprecationsToExceptions')] + public function handle_php81_deprecations_using_laravel_exception() + { + $this->expectException(DeprecatedException::class); + $this->expectExceptionMessage('zzz'); + + trigger_error('zzz', E_USER_DEPRECATED); + } + + /** + * Define environment. + * + * @param \Illuminate\Foundation\Application $app + * @return void + */ + protected function defineConvertDeprecationsToExceptions($app) + { + $value = $_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS'] ?? null; + $_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS'] = true; + + $this->beforeApplicationDestroyed(function () use ($value) { + if (\is_null($value)) { + unset($_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS']); + } else { + $_ENV['TESTBENCH_CONVERT_DEPRECATIONS_TO_EXCEPTIONS'] = $value; + } + }); + } +} diff --git a/tests/TestCaseTest.php b/tests/TestCaseTest.php index 3787fc6dd..99907329f 100644 --- a/tests/TestCaseTest.php +++ b/tests/TestCaseTest.php @@ -4,18 +4,31 @@ use Illuminate\Config\Repository as ConfigRepository; use Illuminate\Foundation\Application; +use Orchestra\Sidekick\Env; use Orchestra\Testbench\Contracts\TestCase as TestCaseContract; -use Orchestra\Testbench\Foundation\Env; +use Orchestra\Testbench\Foundation\Application as Testbench; use Orchestra\Testbench\PHPUnit\TestCase; +use PHPUnit\Framework\Attributes\Test; use function Orchestra\Testbench\container; class TestCaseTest extends TestCase { - /** @test */ + /** {@inheritDoc} */ + #[\Override] + protected function tearDown(): void + { + Testbench::flushState($this); + + parent::tearDown(); + } + + #[Test] public function it_can_create_the_testcase() { - $testbench = new class($this->getName()) extends \Orchestra\Testbench\TestCase + $methodName = $this->name(); + + $testbench = new class($methodName) extends \Orchestra\Testbench\TestCase { // }; @@ -31,11 +44,12 @@ public function it_can_create_the_testcase() $this->assertInstanceOf(TestCaseContract::class, $testbench); $this->assertTrue($testbench->isRunningTestCase()); + $this->assertFalse($testbench->isRunningTestCaseUsingPest()); $app->terminate(); } - /** @test */ + #[Test] public function it_can_create_a_container() { $container = container(); @@ -52,6 +66,7 @@ public function it_can_create_a_container() $this->assertInstanceOf(ConfigRepository::class, $app['config']); $this->assertFalse($container->isRunningTestCase()); + $this->assertFalse($container->isRunningTestCaseUsingPest()); $app->terminate(); } diff --git a/tests/TestbenchTest.php b/tests/TestbenchTest.php index 03d0136cc..27486284a 100644 --- a/tests/TestbenchTest.php +++ b/tests/TestbenchTest.php @@ -4,22 +4,21 @@ use Illuminate\Contracts\Bus\QueueingDispatcher; use Illuminate\Queue\Queue; +use Orchestra\Testbench\Attributes\DefineEnvironment; +use PHPUnit\Framework\Attributes\Test; use Workbench\App\Jobs\CustomPayloadJob; class TestbenchTest extends TestCase { - /** @test */ + #[Test] public function it_can_resolve_uses_testing_concerns() { $this->assertTrue(static::usesTestingConcern(\Orchestra\Testbench\Concerns\Testing::class)); $this->assertFalse(static::usesTestingConcern(\Orchestra\Testbench\Concerns\WithWorkbench::class)); } - /** - * @test - * - * @define-env registerCustomQueuePayload - */ + #[Test] + #[DefineEnvironment('registerCustomQueuePayload')] public function it_can_handle_custom_queue_payload() { $dispatcher = $this->app->make(QueueingDispatcher::class); @@ -31,9 +30,7 @@ public function it_can_handle_custom_queue_payload() protected function registerCustomQueuePayload($app) { - $app->bind('one.time.password', function () { - return random_int(1, 10); - }); + $app->bind('one.time.password', fn () => random_int(1, 10)); Queue::createPayloadUsing(function () use ($app) { $password = $app->make('one.time.password'); diff --git a/tests/TimezoneTest.php b/tests/TimezoneTest.php index 8cc78ab99..8bfab5d99 100644 --- a/tests/TimezoneTest.php +++ b/tests/TimezoneTest.php @@ -3,21 +3,18 @@ namespace Orchestra\Testbench\Tests; use Carbon\Carbon; +use PHPUnit\Framework\Attributes\Test; class TimezoneTest extends TestCase { - /** - * Get application timezone. - * - * @param \Illuminate\Foundation\Application $app - * @return string|null - */ + /** {@inheritDoc} */ + #[\Override] protected function getApplicationTimezone($app) { return 'Asia/Kuala_Lumpur'; } - /** @test */ + #[Test] public function it_can_override_timezone() { $this->assertEquals('Asia/Kuala_Lumpur', Carbon::now()->timezoneName); diff --git a/tests/WithWorkbenchTest.php b/tests/WithWorkbenchTest.php index 3c1a57af6..d6e700133 100644 --- a/tests/WithWorkbenchTest.php +++ b/tests/WithWorkbenchTest.php @@ -2,18 +2,22 @@ namespace Orchestra\Testbench\Tests; +use Orchestra\Sidekick\Env; use Orchestra\Testbench\Concerns\WithFixtures; use Orchestra\Testbench\Concerns\WithWorkbench; use Orchestra\Testbench\Contracts\Config as ConfigContract; use Orchestra\Testbench\Foundation\Config; use Orchestra\Testbench\Workbench\Workbench; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; class WithWorkbenchTest extends TestCase { use WithFixtures; use WithWorkbench; - /** @test */ + #[Test] public function it_can_be_resolved() { $cachedConfig = Workbench::configuration(); @@ -30,7 +34,7 @@ public function it_can_be_resolved() ], $cachedConfig->getExtraAttributes()); } - /** @test */ + #[Test] public function it_can_be_manually_resolved() { $cachedConfig = static::cachedConfigurationForWorkbench(); @@ -44,7 +48,8 @@ public function it_can_be_manually_resolved() $this->assertSame($cachedConfig->toArray(), $config->toArray()); } - /** @test */ + #[Test] + #[Group('without-parallel')] public function it_can_auto_detect_packages_via_bootstrap_providers_file() { $loadedProviders = collect($this->app->getLoadedProviders())->keys()->all(); @@ -52,23 +57,17 @@ public function it_can_auto_detect_packages_via_bootstrap_providers_file() $this->assertContains('Workbench\App\Providers\AppServiceProvider', $loadedProviders); } - /** - * @test - * - * @group without-parallel - */ + #[Test] + #[Group('without-parallel')] public function it_can_resolve_user_model_from_workbench() { + $this->assertFalse(Env::has('AUTH_MODEL')); $this->assertSame('Workbench\App\Models\User', config('auth.providers.users.model')); } - /** - * @test - * - * @dataProvider seedersDataProvider - * - * @group without-parallel - */ + #[Test] + #[DataProvider('seedersDataProvider')] + #[Group('without-parallel')] public function it_can_merge_seeders_with_illuminate_database_refresh( bool $seed, string|false $seeder, diff --git a/tests/Workbench/ActionsTest.php b/tests/Workbench/ActionsTest.php new file mode 100644 index 000000000..67ffee6e6 --- /dev/null +++ b/tests/Workbench/ActionsTest.php @@ -0,0 +1,94 @@ +filesystem = new Filesystem; + $skeletonPath = default_skeleton_path(); + + $this->afterApplicationCreated(function () use ($skeletonPath) { + $this->filesystem->copyDirectory(join_paths($skeletonPath, 'storage'), join_paths($skeletonPath, 'storage.bak')); + $this->ensureSymlinkExists(); + }); + + $this->beforeApplicationDestroyed(function () use ($skeletonPath) { + if (! default_skeleton_path('storage', 'framework')) { + $this->filesystem->moveDirectory(join_paths($skeletonPath, 'storage.bak'), join_paths($skeletonPath, 'storage'), true); + } else { + $this->filesystem->deleteDirectory(join_paths($skeletonPath, 'storage.bak')); + } + }); + + parent::setUp(); + } + + #[Test] + public function it_does_not_wipe_target_directory_while_recreating_asset_symlink() + { + (new AddAssetSymlinkFolders($this->filesystem, static::cachedConfigurationForWorkbench()))->handle(); + + $this->assertDirectoryExists(join_paths(default_skeleton_path(), 'storage', 'framework')); + } + + #[Test] + public function it_does_not_wipe_target_directory_while_removing_asset_symlink() + { + (new RemoveAssetSymlinkFolders($this->filesystem, static::cachedConfigurationForWorkbench()))->handle(); + + $this->assertDirectoryExists(join_paths(default_skeleton_path(), 'storage', 'framework')); + } + + /** + * Ensure symlink directory exists for the test. + * + * @return void + */ + protected function ensureSymlinkExists(): void + { + if (! is_symlink(workbench_path('storage'))) { + $this->filesystem->link(default_skeleton_path('storage'), workbench_path('storage')); + } + } + + /** + * Define or get the cached uses for test case. + * + * @return \Orchestra\Testbench\Contracts\Config + */ + public static function cachedConfigurationForWorkbench() + { + return new Config([ + 'workbench' => [ + 'sync' => [[ + 'from' => 'storage', + 'to' => 'workbench/storage', + 'reverse' => true, + ]], + ], + ]); + } +} diff --git a/tests/Workbench/DiscoversTest.php b/tests/Workbench/DiscoversTest.php index 4e41cf84d..76cad003e 100644 --- a/tests/Workbench/DiscoversTest.php +++ b/tests/Workbench/DiscoversTest.php @@ -8,6 +8,8 @@ use Orchestra\Testbench\Attributes\WithConfig; use Orchestra\Testbench\Concerns\WithWorkbench; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestWith; #[WithConfig('app.key', 'AckfSECXIvnK5r28GVIWUAxmbBSjTsmF')] class DiscoversTest extends TestCase @@ -15,23 +17,34 @@ class DiscoversTest extends TestCase use InteractsWithViews; use WithWorkbench; - /** @test */ + /** {@inheritDoc} */ + #[\Override] + protected function setUp(): void + { + if (! \defined('LARAVEL_START')) { + \define('LARAVEL_START', microtime(true)); + } + + parent::setUp(); + } + + #[Test] public function it_can_resolve_web_routes_from_discovers() { - $this->get('/hello') + $this->get('/api/hello') ->assertOk(); } - /** @test */ + #[Test] public function it_can_resolve_web_routes_using_macro_from_discovers() { $this->get('/hello-world') ->assertOk() ->assertSee('Hello world') - ->assertHeader('Content-Type', 'text/plain; charset=UTF-8'); + ->assertHeader('Content-Type', 'text/plain; charset=utf-8'); } - /** @test */ + #[Test] public function it_can_resolve_views_from_discovers() { $this->get('/testbench') @@ -40,7 +53,7 @@ public function it_can_resolve_views_from_discovers() ->assertSee('Notification Component'); } - /** @test */ + #[Test] public function it_can_resolve_errors_views_from_discovers() { $this->get('/root') @@ -49,19 +62,19 @@ public function it_can_resolve_errors_views_from_discovers() ->assertDontSeeText('412'); } - /** @test */ + #[Test] public function it_can_resolve_route_name_from_discovers() { $this->assertSame(url('/testbench'), route('testbench')); } - /** @test */ + #[Test] public function it_can_resolve_commands_from_discovers() { $this->artisan('workbench:inspire')->assertOk(); } - /** @test */ + #[Test] public function it_can_discover_config_files() { $this->assertSame(InstalledVersions::isInstalled('orchestra/workbench'), config('workbench.installed')); @@ -69,7 +82,7 @@ public function it_can_discover_config_files() $this->assertSame(InstalledVersions::isInstalled('orchestra/workbench'), config('nested.workbench.installed')); } - /** @test */ + #[Test] public function it_can_discover_views_files() { $this->view('workbench::testbench') @@ -81,24 +94,21 @@ public function it_can_discover_views_files() ->assertSee('Notification Component'); } - /** @test */ + #[Test] public function it_can_discover_translation_files() { $this->assertSame('Good Morning', __('workbench::welcome.morning')); } - /** - * @test - * - * @testWith ["Workbench\\Database\\Factories\\Illuminate\\Foundation\\Auh\\UserFactory", "Illuminate\\Foundation\\Auh\\User"] - * ["Workbench\\Database\\Factories\\UserFactory", "Workbench\\App\\Models\\User"] - */ + #[Test] + #[TestWith(['Workbench\\Database\\Factories\\Illuminate\\Foundation\\Auh\\UserFactory', 'Illuminate\\Foundation\\Auh\\User'])] + #[TestWith(['Workbench\\Database\\Factories\\UserFactory', 'Workbench\\App\\Models\\User'])] public function it_can_discover_database_factories_from_model(string $factory, string $model) { $this->assertSame($factory, Factory::resolveFactoryName($model)); } - /** @test */ + #[Test] public function it_can_discover_model_from_factory() { $this->assertSame('Workbench\App\Models\User', \Workbench\Database\Factories\UserFactory::new()->modelName()); diff --git a/tests/Workbench/ErrorPageTest.php b/tests/Workbench/ErrorPageTest.php new file mode 100644 index 000000000..fc348e448 --- /dev/null +++ b/tests/Workbench/ErrorPageTest.php @@ -0,0 +1,75 @@ +get('/failed') + ->assertInternalServerError() + ->assertSee('RuntimeException') + ->assertSee('Bad route!'); + } + + #[Test] + #[WithConfig('app.debug', true)] + public function it_can_resolve_exception_without_exception_handling() + { + $this->expectException('RuntimeException'); + $this->expectExceptionMessage('Bad route!'); + + $this->withoutExceptionHandling() + ->get('/failed'); + } + + #[Test] + public function it_can_resolve_exception_page_without_enabling_debug_mode() + { + $this->get('/failed') + ->assertInternalServerError() + ->assertSee('500') + ->assertSee('Server Error'); + } + + #[Test] + public function it_can_resolve_exception_without_exception_handling_without_enabling_debug_mode() + { + $this->expectException('RuntimeException'); + $this->expectExceptionMessage('Bad route!'); + + $this->withoutExceptionHandling() + ->get('/failed'); + } + + #[Test] + #[WithConfig('app.debug', true)] + public function it_can_resolve_exception_page_using_json_request() + { + $this->getJson('/api/failed') + ->assertInternalServerError() + ->assertSee('RuntimeException') + ->assertSee('Bad route!'); + } + + #[Test] + #[WithConfig('app.debug', true)] + public function it_can_resolve_exception_using_json_request_without_exception_handling() + { + $this->expectException('RuntimeException'); + $this->expectExceptionMessage('Bad route!'); + + $this->withoutExceptionHandling() + ->get('/failed'); + } +} diff --git a/tests/Workbench/HelpersTest.php b/tests/Workbench/HelpersTest.php index c67d4a155..a20c043bf 100644 --- a/tests/Workbench/HelpersTest.php +++ b/tests/Workbench/HelpersTest.php @@ -5,14 +5,17 @@ use Orchestra\Testbench\Contracts\Config as ConfigContract; use Orchestra\Testbench\Foundation\Config; use Orchestra\Testbench\Tests\TestCase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\Test; +use function Illuminate\Filesystem\join_paths; use function Orchestra\Testbench\package_path; use function Orchestra\Testbench\workbench; use function Orchestra\Testbench\workbench_path; class HelpersTest extends TestCase { - /** @test */ + #[Test] public function it_can_resolve_workbench() { $this->instance(ConfigContract::class, new Config([ @@ -22,6 +25,7 @@ public function it_can_resolve_workbench() 'guard' => 'web', 'install' => false, 'welcome' => false, + 'health' => false, 'discovers' => [ 'web' => true, ], @@ -35,6 +39,7 @@ public function it_can_resolve_workbench() 'install' => false, 'auth' => false, 'welcome' => false, + 'health' => false, 'sync' => [], 'build' => [], 'assets' => [], @@ -50,7 +55,7 @@ public function it_can_resolve_workbench() ], workbench()); } - /** @test */ + #[Test] public function it_can_resolve_workbench_without_bound() { $this->assertSame([ @@ -60,6 +65,7 @@ public function it_can_resolve_workbench_without_bound() 'install' => true, 'auth' => false, 'welcome' => null, + 'health' => null, 'sync' => [], 'build' => [], 'assets' => [], @@ -75,18 +81,15 @@ public function it_can_resolve_workbench_without_bound() ], workbench()); } - /** - * @test - * - * @group workbench - */ + #[Test] + #[Group('workbench')] public function it_can_resolve_workbench_path() { $expected = realpath(package_path('workbench/database/migrations/2013_07_26_182750_create_testbench_users_table.php')); $this->assertSame( - $expected, - workbench_path('database'.DIRECTORY_SEPARATOR.'migrations'.DIRECTORY_SEPARATOR.'2013_07_26_182750_create_testbench_users_table.php') + realpath(package_path('workbench/database/migrations/2013_07_26_182750_create_testbench_users_table.php')), + workbench_path(join_paths('database'.DIRECTORY_SEPARATOR.'migrations'.DIRECTORY_SEPARATOR.'2013_07_26_182750_create_testbench_users_table.php')) ); $this->assertSame( diff --git a/types/functions.php b/types/functions.php index 6182e7642..47b77b30f 100644 --- a/types/functions.php +++ b/types/functions.php @@ -8,9 +8,9 @@ use function PHPStan\Testing\assertType; assertType('string', default_skeleton_path()); -assertType('string', default_skeleton_path('app')); -assertType('string', default_skeleton_path('app', '.gitkeep')); -assertType('string', default_skeleton_path(['app', '.gitkeep'])); +assertType('string|false', default_skeleton_path('app')); +assertType('string|false', default_skeleton_path('app', '.gitkeep')); +assertType('string|false', default_skeleton_path(['app', '.gitkeep'])); assertType('string', package_path()); assertType('string', package_path('laravel')); diff --git a/workbench/bootstrap/app.php b/workbench/bootstrap/app.php new file mode 100644 index 000000000..4e53a010a --- /dev/null +++ b/workbench/bootstrap/app.php @@ -0,0 +1,29 @@ +withProviders() + ->withRouting( + web: __DIR__.'/web.php', + // api: __DIR__.'/../routes/api.php', + commands: __DIR__.'/../routes/console.php', + // channels: __DIR__.'/../routes/channels.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware) { + // + }) + ->withExceptions(function (Exceptions $exceptions) { + // + })->create(); diff --git a/workbench/bootstrap/web.php b/workbench/bootstrap/web.php new file mode 100644 index 000000000..c9c075de5 --- /dev/null +++ b/workbench/bootstrap/web.php @@ -0,0 +1,7 @@ +middleware(['auth'])->name('dashboard'); diff --git a/workbench/database/seeders/TestbenchDatabaseSeeder.php b/workbench/database/seeders/TestbenchDatabaseSeeder.php new file mode 100644 index 000000000..3b4fd5dcf --- /dev/null +++ b/workbench/database/seeders/TestbenchDatabaseSeeder.php @@ -0,0 +1,18 @@ +json('hello world'); }); + +Route::get('api/failed', fn () => throw new RuntimeException('Bad route!')); diff --git a/workbench/routes/web.php b/workbench/routes/web.php index 3289daa50..58bf708c2 100644 --- a/workbench/routes/web.php +++ b/workbench/routes/web.php @@ -13,9 +13,9 @@ | */ -Route::get('/', function () { - return view('welcome'); -}); +Route::get('failed', fn () => throw new RuntimeException('Bad route!')); + +Route::get('/', fn () => view('welcome'))->name('welcome'); Route::view('/testbench', 'workbench::testbench')->name('testbench'); Route::text('/hello-world', 'Hello world');