mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 07:47:22 +01:00
Compare commits
279 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 049ce1845e | |||
| faa6cd7deb | |||
| 1f9362c4e7 | |||
| be8a64e4b8 | |||
| 2336bc0f65 | |||
| da82eecbae | |||
| 4855987ba8 | |||
| a493db1873 | |||
| 4f677a6cc2 | |||
| 228f2deb64 | |||
| 0fadf9a02c | |||
| 2b138ad76b | |||
| e3e4815b55 | |||
| f76f353c32 | |||
| 16b9f54dc3 | |||
| 281166475e | |||
| 23805cb5d6 | |||
| 76d0f9cfc1 | |||
| 5b083e4eb1 | |||
| f48694b18a | |||
| 8fa59ddbf0 | |||
| 2619db4026 | |||
| f3a71fb100 | |||
| 04fafe742c | |||
| cad8a41e6d | |||
| 1567923cda | |||
| c7116afcae | |||
| 4e184b2f90 | |||
| 9b5f664f00 | |||
| 0e89525ea8 | |||
| 0b6cdf8f02 | |||
| 5f63d959e1 | |||
| be7fe41179 | |||
| 204f343831 | |||
| aa230a1716 | |||
| 97f98569bc | |||
| 1318bf9830 | |||
| 3b58f946f1 | |||
| dfc2470764 | |||
| f650978dd0 | |||
| c6ba469e68 | |||
| 3a9997f9af | |||
| fb6cb891be | |||
| 76beda74c9 | |||
| 0398d4223b | |||
| 360eeb4c7d | |||
| 79b4224a35 | |||
| 3c79c893c9 | |||
| a11f507191 | |||
| 6413f7040f | |||
| 278af4b835 | |||
| fe885fbfb6 | |||
| 61b2c426e4 | |||
| 4272d49fb7 | |||
| 014ab3b957 | |||
| 09a0a64f20 | |||
| bf79f7c63f | |||
| 36b879f97d | |||
| bcc206d183 | |||
| 1e7b6a0396 | |||
| 57a1ccd213 | |||
| 708b4b1d49 | |||
| 3695736b3a | |||
| 8cc9580253 | |||
| e1fbf56f3d | |||
| 50cd1056eb | |||
| cfe6a6728f | |||
| 38344c99f1 | |||
| 98ed779424 | |||
| 943707cbcd | |||
| 940e246f27 | |||
| 439ebcdcaf | |||
| d2db71bb78 | |||
| d85432933c | |||
| 3cfadee2bb | |||
| 051ca73cae | |||
| 50a273f1f1 | |||
| ffe5dde7f0 | |||
| 7e1747a364 | |||
| 356b9c01c7 | |||
| 4009177e56 | |||
| 46d1d46384 | |||
| 128ff1006f | |||
| 64bb853720 | |||
| 330dafa294 | |||
| a51c354268 | |||
| cc1abe7f06 | |||
| 0c16942d37 | |||
| fa413aafbb | |||
| 75f17bb118 | |||
| 19ce733207 | |||
| 7529d44f81 | |||
| e4e4eb0a57 | |||
| c8eb1397b4 | |||
| c4c768dcaa | |||
| 8c60a9fff5 | |||
| 206e613711 | |||
| 5fb81e9eb4 | |||
| d130a1aea0 | |||
| 03201cb8b7 | |||
| 46e900e8d2 | |||
| f0f79ab244 | |||
| 1e61144cd2 | |||
| 2751bc9674 | |||
| e2deaae6c9 | |||
| 1aec8bac55 | |||
| 32ef377284 | |||
| ab017e17e2 | |||
| 832882160f | |||
| e03d015120 | |||
| 819825bdd2 | |||
| d29c789788 | |||
| b4c45af785 | |||
| b4bf799d75 | |||
| 9f62f2d483 | |||
| 679082e805 | |||
| 42f0bd052e | |||
| 01b9bab55f | |||
| 3eb0a95955 | |||
| d11157f7b2 | |||
| 1f2ec74d6c | |||
| 1241e929b1 | |||
| db7c4b174f | |||
| 460ce45349 | |||
| 8e203e914e | |||
| 64a8ed04ff | |||
| 234b6847ad | |||
| 933820c8d3 | |||
| 4e9587da59 | |||
| 60a5db14ba | |||
| 7ea3e02afa | |||
| 421c52fb74 | |||
| a3644f7efa | |||
| c7326f430b | |||
| 9ea51caf3f | |||
| cb4c9563bd | |||
| fac44d9afe | |||
| 6e27c6d85d | |||
| 9a0cfaf339 | |||
| 321b3e8df3 | |||
| bff97418ed | |||
| a47ad6a1d3 | |||
| 74c14808cf | |||
| eff2d7f613 | |||
| e135979f34 | |||
| 15edde8e87 | |||
| 211f5c2433 | |||
| 5d58d58f71 | |||
| d5a4008d3e | |||
| 1bf802268f | |||
| 807a4b004f | |||
| 53a8c7b05e | |||
| ef5715ce10 | |||
| 0c04882389 | |||
| 323c5f211f | |||
| f7a3fa15f4 | |||
| 6dd3ca20e4 | |||
| d9ea378819 | |||
| 6aa0356570 | |||
| 45b0d5d899 | |||
| 5be1edd7b7 | |||
| 75f7ee0acf | |||
| d0a74931dd | |||
| 0738e113ad | |||
| 283d8f3e03 | |||
| 1c3e820283 | |||
| accd4eb7b4 | |||
| ae7991c7e9 | |||
| e9e72d607e | |||
| 40766f9275 | |||
| a3fd60ce4d | |||
| f9a3e39902 | |||
| 06d707fb41 | |||
| a70c64d704 | |||
| 3a78aaef8f | |||
| c79c0feec8 | |||
| 39a5a94f3e | |||
| 961bfcaff7 | |||
| 52ba5dbd00 | |||
| 518b056fb9 | |||
| f9a936b4d9 | |||
| 4448761852 | |||
| 1192d13e6b | |||
| 57b982de48 | |||
| a3366379e0 | |||
| cd8d8fce61 | |||
| 4254d71039 | |||
| bd48232c61 | |||
| 70b3c7ea1d | |||
| c186035a13 | |||
| 2efed3ef80 | |||
| 58f2581307 | |||
| 6c4fd61db5 | |||
| afbbc35984 | |||
| a13c19cc29 | |||
| 7d7c5f1ab1 | |||
| 865f33bf80 | |||
| e33419545c | |||
| b93cd724c1 | |||
| 40a5d067ec | |||
| 3640ab0945 | |||
| cb2336d220 | |||
| 71fcb281b0 | |||
| d24830121e | |||
| 5a00a732e3 | |||
| 38584bec93 | |||
| ffa3f1d683 | |||
| dff9bbc134 | |||
| f5f717f1ad | |||
| 9bdd254007 | |||
| 6e18912ea6 | |||
| d35320c697 | |||
| fe11140fc2 | |||
| 0d198f589d | |||
| 8a42d40506 | |||
| 83797431fb | |||
| c05df43217 | |||
| f6859eeb3b | |||
| a0b8082631 | |||
| 926d8ecb8d | |||
| 24f85354e2 | |||
| 163de28338 | |||
| 3d2c83a501 | |||
| b0c964d4d9 | |||
| 20d2d9f3b7 | |||
| 6b7aa10e91 | |||
| 067aa58817 | |||
| 337e751200 | |||
| b20f208b55 | |||
| 9899b3c3a4 | |||
| 6437db7aa0 | |||
| 7d38d4bd4f | |||
| e8d426f574 | |||
| bb711108a2 | |||
| a482341b99 | |||
| f5ce472006 | |||
| a53ff3e459 | |||
| e15d77f3f6 | |||
| 504128730c | |||
| 098acaecc0 | |||
| b2dd573a67 | |||
| 84c9078bb7 | |||
| aa1917c28d | |||
| c81dce0f6d | |||
| 249869c2db | |||
| d59d8d6245 | |||
| b0680b7e2c | |||
| c7fd21999b | |||
| 28d06663d3 | |||
| 73f56e58a5 | |||
| 76796ffc67 | |||
| 01daf0316c | |||
| 1f2976c0e0 | |||
| 2e7ed741b6 | |||
| a0a4730ef8 | |||
| 5ed927d226 | |||
| 78cdd4da36 | |||
| 332139e614 | |||
| b093e8ee29 | |||
| 937374431a | |||
| ddc08cf0f9 | |||
| 88d2391d2e | |||
| bd02196950 | |||
| 4de6019206 | |||
| 85630b0aa2 | |||
| 0fda39467c | |||
| 415f571910 | |||
| 8284219035 | |||
| d0d34c7872 | |||
| 2869f11ae5 | |||
| 340c7ca04e | |||
| 81a646d64e | |||
| c23f2e4bd6 | |||
| 4496e9d9ee | |||
| ce14ffd49a | |||
| c18c481628 | |||
| 0695ea5d33 | |||
| 2e321f5465 | |||
| cbeec31bfc |
@ -14,5 +14,5 @@ trim_trailing_whitespace = true
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
| Q | A
|
||||
| ------------- | ---
|
||||
| Bug fix? | yes/no
|
||||
| New feature? | yes/no
|
||||
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
|
||||
|
||||
<!--
|
||||
- Replace this comment by a description of what your PR is solving.
|
||||
-->
|
||||
|
||||
58
.github/workflows/changelog.yml
vendored
Normal file
58
.github/workflows/changelog.yml
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
name: Changelog
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- CHANGELOG.md
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- CHANGELOG.md
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: github.repository == 'pestphp/pest'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout website repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.CHANGELOG_KEY }}
|
||||
repository: pestphp/website
|
||||
path: pestphp-website
|
||||
- name: Read CHANGELOG.md
|
||||
id: package
|
||||
uses: juliangruber/read-file-action@v1
|
||||
with:
|
||||
path: ./CHANGELOG.md
|
||||
- name: Add file headers
|
||||
uses: DamianReeves/write-file-action@v1.0
|
||||
with:
|
||||
path: ./CHANGELOG.md
|
||||
contents: |
|
||||
---
|
||||
title: Changelog
|
||||
description: Changelog
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
${{ steps.package.outputs.content }}
|
||||
|
||||
----
|
||||
|
||||
Next section: [Upgrade Guide →](/docs/upgrade-guide)
|
||||
write-mode: overwrite
|
||||
- name: Copy CHANGELOG to website repository
|
||||
run: cp CHANGELOG.md pestphp-website/source/docs/changelog.md
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v2
|
||||
with:
|
||||
token: ${{ secrets.CHANGELOG_KEY }}
|
||||
commit-message: Update changelog.md
|
||||
committer: GitHub Action <noreply@github.com>
|
||||
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
|
||||
title: 'Update changelog.md'
|
||||
path: ./pestphp-website
|
||||
51
.github/workflows/ci.yml
vendored
51
.github/workflows/ci.yml
vendored
@ -1,51 +0,0 @@
|
||||
name: Continuous Integration
|
||||
|
||||
on: ['push', 'pull_request']
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
php: [7.3, 7.4]
|
||||
dependency-version: [prefer-lowest, prefer-stable]
|
||||
|
||||
name: CI - PHP ${{ matrix.php }} (${{ matrix.dependency-version }})
|
||||
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.composer/cache/files
|
||||
key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: mbstring, zip
|
||||
tools: prestissimo
|
||||
coverage: pcov
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer update --${{ matrix.dependency-version }} --no-interaction --prefer-dist
|
||||
|
||||
- name: Coding Style Checks
|
||||
run: |
|
||||
vendor/bin/rector process src --dry-run
|
||||
vendor/bin/php-cs-fixer fix -v --dry-run
|
||||
|
||||
- name: Type Checks
|
||||
run: vendor/bin/phpstan analyse --ansi
|
||||
|
||||
- name: Unit Tests
|
||||
run: bin/pest --colors=always --exclude-group=integration
|
||||
|
||||
- name: Integration Tests
|
||||
run: bin/pest --colors=always --group=integration
|
||||
54
.github/workflows/static.yml
vendored
Normal file
54
.github/workflows/static.yml
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
name: Static Analysis
|
||||
|
||||
on: ['push', 'pull_request']
|
||||
|
||||
jobs:
|
||||
cs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: Code Style
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 7.4
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
|
||||
- name: Install Dependencies
|
||||
run: composer update --no-interaction --no-progress
|
||||
|
||||
- name: Run Rector
|
||||
run: vendor/bin/rector process src --dry-run
|
||||
|
||||
- name: Run PHP-CS-Fixer
|
||||
run: vendor/bin/php-cs-fixer fix -v --allow-risky=yes --dry-run
|
||||
|
||||
phpstan:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
dependency-version: [prefer-lowest, prefer-stable]
|
||||
|
||||
name: PHPStan ${{ matrix.dependency-version }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 7.4
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
|
||||
- name: Install Dependencies
|
||||
run: composer update --prefer-stable --no-interaction --no-progress
|
||||
|
||||
- name: Run PHPStan
|
||||
run: vendor/bin/phpstan analyse --no-progress
|
||||
44
.github/workflows/tests.yml
vendored
Normal file
44
.github/workflows/tests.yml
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
name: Tests
|
||||
|
||||
on: ['push', 'pull_request']
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
php: ['7.3', '7.4', '8.0']
|
||||
dependency-version: [prefer-lowest, prefer-stable]
|
||||
|
||||
name: PHP ${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
|
||||
- name: Setup Problem Matches
|
||||
run: |
|
||||
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
||||
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
|
||||
- name: Install PHP 7 dependencies
|
||||
run: composer update --${{ matrix.dependency-version }} --no-interaction --no-progress
|
||||
if: "matrix.php < 8"
|
||||
|
||||
- name: Install PHP 8 dependencies
|
||||
run: composer update --${{ matrix.dependency-version }} --ignore-platform-req=php --no-interaction --no-progress
|
||||
if: "matrix.php >= 8"
|
||||
|
||||
- name: Unit Tests
|
||||
run: php bin/pest --colors=always --exclude-group=integration
|
||||
|
||||
- name: Integration Tests
|
||||
run: php bin/pest --colors=always --group=integration
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@ coverage.xml
|
||||
.temp/coverage.php
|
||||
*.swp
|
||||
*.swo
|
||||
.vscode/
|
||||
1
.php_cs
1
.php_cs
@ -3,7 +3,6 @@
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'tests')
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'bin')
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'compiled')
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'scripts')
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'stubs')
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'src')
|
||||
|
||||
93
CHANGELOG.md
93
CHANGELOG.md
@ -6,6 +6,99 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.3.8 (2020-10-03)](https://github.com/pestphp/pest/compare/v0.3.7...v0.3.8)
|
||||
### Added
|
||||
- Add support for PHPUnit 9.4.0 ([#199](https://github.com/pestphp/pest/pull/199))
|
||||
|
||||
### Fixed
|
||||
- Fix chained higher order assertions returning void ([#196](https://github.com/pestphp/pest/pull/196))
|
||||
|
||||
## [v0.3.7 (2020-09-30)](https://github.com/pestphp/pest/compare/v0.3.6...v0.3.7)
|
||||
### Added
|
||||
- Add support for PHPUnit 9.3.11 ([#193](https://github.com/pestphp/pest/pull/193))
|
||||
|
||||
## [v0.3.6 (2020-09-21)](https://github.com/pestphp/pest/compare/v0.3.5...v0.3.6)
|
||||
### Added
|
||||
- `toMatch` expectation ([#191](https://github.com/pestphp/pest/pull/191))
|
||||
- `toMatchConstraint` expectation ([#190](https://github.com/pestphp/pest/pull/190))
|
||||
|
||||
## [v0.3.5 (2020-09-16)](https://github.com/pestphp/pest/compare/v0.3.4...v0.3.5)
|
||||
### Added
|
||||
- `toStartWith` and `toEndWith` expectations ([#187](https://github.com/pestphp/pest/pull/187))
|
||||
|
||||
## [v0.3.4 (2020-09-15)](https://github.com/pestphp/pest/compare/v0.3.3...v0.3.4)
|
||||
### Added
|
||||
- `toMatchObject` expectation ([4e184b2](https://github.com/pestphp/pest/commit/4e184b2f906c318a5e9cd38fe693cdab5c48d8a2))
|
||||
|
||||
## [v0.3.3 (2020-09-13)](https://github.com/pestphp/pest/compare/v0.3.2...v0.3.3)
|
||||
### Added
|
||||
- `toHaveKeys` expectation ([204f343](https://github.com/pestphp/pest/commit/204f343831adc17bb3734553c24fac92d02f27c7))
|
||||
|
||||
## [v0.3.2 (2020-09-12)](https://github.com/pestphp/pest/compare/v0.3.1...v0.3.2)
|
||||
### Added
|
||||
- Support to PHPUnit 9.3.9, and 9.3.10 ([1318bf9](https://github.com/pestphp/pest/commit/97f98569bc86e8b87f8cde963fe7b4bf5399623b))
|
||||
|
||||
## [v0.3.1 (2020-08-29)](https://github.com/pestphp/pest/compare/v0.3.0...v0.3.1)
|
||||
### Added
|
||||
- Support to PHPUnit 9.3.8 ([#174](https://github.com/pestphp/pest/pull/174))
|
||||
|
||||
## [v0.3.0 (2020-08-27)](https://github.com/pestphp/pest/compare/v0.2.3...v0.3.0)
|
||||
### Added
|
||||
- Expectation API (TODO)
|
||||
- PHPUnit 9.3 and PHP 8 support ([#128](https://github.com/pestphp/pest/pull/128))
|
||||
- Fowards `$this` calls to globals ([#169](https://github.com/pestphp/pest/pull/169))
|
||||
|
||||
### Fixed
|
||||
- don't decorate output if --colors=never is set ([36b879f](https://github.com/pestphp/pest/commit/36b879f97d7b187c87a94eb60af5b7d3b7253d56))
|
||||
|
||||
## [v0.2.3 (2020-07-01)](https://github.com/pestphp/pest/compare/v0.2.2...v0.2.3)
|
||||
### Added
|
||||
- `--init` and `pest:install` artisan command output changes ([#118](https://github.com/pestphp/pest/pull/118), [db7c4b1](https://github.com/pestphp/pest/commit/db7c4b174f0974969450dda71dcd649ef0c073a3))
|
||||
- `--version` option to view the current version of Pest ([9ea51ca](https://github.com/pestphp/pest/commit/9ea51caf3f74569debb1e465992e9ea916cb80fe))
|
||||
|
||||
## [v0.2.2 (2020-06-21)](https://github.com/pestphp/pest/compare/v0.2.1...v0.2.2)
|
||||
### Added
|
||||
- `depends` phpunit feature ([#103](https://github.com/pestphp/pest/pull/103))
|
||||
|
||||
### Fixes
|
||||
- datasets name conflit ([#101](https://github.com/pestphp/pest/pull/101))
|
||||
|
||||
## [v0.2.1 (2020-06-17)](https://github.com/pestphp/pest/compare/v0.2.0...v0.2.1)
|
||||
### Fixes
|
||||
- Multiple `uses` in the same path override previous `uses` ([#97](https://github.com/pestphp/pest/pull/97))
|
||||
|
||||
## [v0.2.0 (2020-06-14)](https://github.com/pestphp/pest/compare/v0.1.5...v0.2.0)
|
||||
### Adds
|
||||
- `--init` option to install Pest on a new blank project ([70b3c7e](https://github.com/pestphp/pest/commit/70b3c7ea1ddb031f3bbfaabdc28d56270608ebbd))
|
||||
- pending higher orders tests aka tests without description ([aa1917c](https://github.com/pestphp/pest/commit/aa1917c28d9b69c2bd1d51f986c4f61318ee7e16))
|
||||
|
||||
### Fixed
|
||||
- `--verbose` and `--colors` options not being used by printers ([#51](https://github.com/pestphp/pest/pull/51))
|
||||
- missing support on windows ([#61](https://github.com/pestphp/pest/pull/61))
|
||||
|
||||
### Changed
|
||||
- `helpers.php` stub provides now namespaced functions
|
||||
- functions provided by plugins are now namespaced functions:
|
||||
|
||||
```php
|
||||
use function Pest\Faker\faker;
|
||||
|
||||
it('foo', function () {
|
||||
$name = faker()->name;
|
||||
});
|
||||
```
|
||||
|
||||
## [v0.1.5 (2020-05-24)](https://github.com/pestphp/pest/compare/v0.1.4...v0.1.5)
|
||||
### Fixed
|
||||
- Missing default decorated output on coverage ([88d2391](https://github.com/pestphp/pest/commit/88d2391d2e6fe9c9416462734b9b523cb418f469))
|
||||
|
||||
## [v0.1.4 (2020-05-24)](https://github.com/pestphp/pest/compare/v0.1.3...v0.1.4)
|
||||
### Added
|
||||
- Support to Lumen on artisan commands ([#18](https://github.com/pestphp/pest/pull/18))
|
||||
|
||||
### Fixed
|
||||
- Mockery tests without assertions being considered risky ([415f571](https://github.com/pestphp/pest/commit/415f5719101b30c11d87f74810a71686ef2786c6))
|
||||
|
||||
## [v0.1.3 (2020-05-21)](https://github.com/pestphp/pest/compare/v0.1.2...v0.1.3)
|
||||
### Added
|
||||
- `Plugin::uses()` method for making traits globally available ([6c4be01](https://github.com/pestphp/pest/commit/6c4be0190e9493702a976b996bbbf5150cc6bb53))
|
||||
|
||||
12
README.md
12
README.md
@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/pestphp/art/master/readme.png" width="600" alt="PEST">
|
||||
<p align="center">
|
||||
<a href="https://github.com/pestphp/pest/actions"><img alt="GitHub Workflow Status (master)" src="https://img.shields.io/github/workflow/status/pestphp/pest/Continuous Integration/master"></a>
|
||||
<a href="https://github.com/pestphp/pest/actions"><img alt="GitHub Workflow Status (master)" src="https://img.shields.io/github/workflow/status/pestphp/pest/Tests/master"></a>
|
||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Total Downloads" src="https://img.shields.io/packagist/dt/pestphp/pest"></a>
|
||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Latest Version" src="https://img.shields.io/packagist/v/pestphp/pest"></a>
|
||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="License" src="https://img.shields.io/packagist/l/pestphp/pest"></a>
|
||||
@ -9,10 +9,18 @@
|
||||
</p>
|
||||
|
||||
------
|
||||
**Pest** it's an elegant PHP Testing Framework with a focus on simplicity. It was carefully crafted to bring the joy of testing to PHP.
|
||||
**Pest** is an elegant PHP Testing Framework with a focus on simplicity. It was carefully crafted to bring the joy of testing to PHP.
|
||||
|
||||
- Explore the docs: **[pestphp.com »](https://pestphp.com)**
|
||||
- Follow us on Twitter: **[@pestphp »](https://twitter.com/pestphp)**
|
||||
- Join us on the Discord Server: **[discord.gg/bMAJv82 »](https://discord.gg/bMAJv82)**
|
||||
|
||||
## Pest Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Pest development. If you are interested in becoming a sponsor, please visit the Nuno Maduro's [Sponsors page](https://github.com/sponsors/nunomaduro).
|
||||
|
||||
### Premium Sponsors
|
||||
|
||||
- **[Scout APM](https://github.com/scoutapp)**
|
||||
|
||||
Pest was created by **[Nuno Maduro](https://twitter.com/enunomaduro)** under the **[Sponsorware license](https://github.com/sponsorware/docs)**. It got open-sourced and is now licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
|
||||
|
||||
21
bin/pest
21
bin/pest
@ -1,31 +1,42 @@
|
||||
#!/usr/bin/env php
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
use NunoMaduro\Collision\Provider;
|
||||
use Pest\Actions\ValidatesEnvironment;
|
||||
use Pest\Console\Command;
|
||||
use Pest\Support\Container;
|
||||
use Pest\TestSuite;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
(static function () {
|
||||
// Used when Pest is required using composer.
|
||||
$vendorPath = realpath(__DIR__ . '/../../../../vendor/autoload.php');
|
||||
$vendorPath = dirname(__DIR__, 4) . '/vendor/autoload.php';
|
||||
|
||||
// Used when Pest maintainers are running Pest tests.
|
||||
$localPath = realpath(__DIR__ . '/../vendor/autoload.php');
|
||||
$localPath = dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
if ($vendorPath) {
|
||||
if (file_exists($vendorPath)) {
|
||||
include_once $vendorPath;
|
||||
} else {
|
||||
include_once $localPath;
|
||||
}
|
||||
|
||||
(new \NunoMaduro\Collision\Provider)->register();
|
||||
(new Provider())->register();
|
||||
|
||||
$rootPath = getcwd();
|
||||
|
||||
$testSuite = TestSuite::getInstance($rootPath);
|
||||
|
||||
$isDecorated = (new ArgvInput())->getParameterOption('--colors', 'always') !== 'never';
|
||||
$output = new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, $isDecorated);
|
||||
|
||||
$container = Container::getInstance();
|
||||
$container->add(TestSuite::class, $testSuite);
|
||||
$container->add(OutputInterface::class, $output);
|
||||
|
||||
ValidatesEnvironment::in($testSuite);
|
||||
|
||||
exit((new Command($testSuite, new ConsoleOutput()))->run($_SERVER['argv']));
|
||||
exit($container->get(Command::class)->run($_SERVER['argv']));
|
||||
})();
|
||||
|
||||
1926
compiled/globals.php
1926
compiled/globals.php
File diff suppressed because it is too large
Load Diff
@ -17,10 +17,12 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3",
|
||||
"php": "^7.3 || ^8.0",
|
||||
"nunomaduro/collision": "^5.0",
|
||||
"phpunit/phpunit": "^9.1.4",
|
||||
"sebastian/environment": "^5.1"
|
||||
"pestphp/pest-plugin": "^0.3",
|
||||
"pestphp/pest-plugin-coverage": "^0.3",
|
||||
"pestphp/pest-plugin-init": "^0.3",
|
||||
"phpunit/phpunit": ">= 9.3.7 <= 9.4.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -28,7 +30,7 @@
|
||||
},
|
||||
"files": [
|
||||
"src/globals.php",
|
||||
"compiled/globals.php"
|
||||
"src/Pest.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
@ -40,16 +42,10 @@
|
||||
]
|
||||
},
|
||||
"require-dev": {
|
||||
"ergebnis/phpstan-rules": "^0.14.4",
|
||||
"friendsofphp/php-cs-fixer": "^2.16.3",
|
||||
"illuminate/console": "^7.10.3",
|
||||
"illuminate/support": "^7.10.3",
|
||||
"mockery/mockery": "^1.3.1",
|
||||
"phpstan/phpstan": "^0.12.25",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"rector/rector": "^0.7.25",
|
||||
"symfony/var-dumper": "^5.0.8",
|
||||
"thecodingmachine/phpstan-strict-rules": "^0.12.0"
|
||||
"illuminate/console": "^7.16.1",
|
||||
"illuminate/support": "^7.16.1",
|
||||
"mockery/mockery": "^1.4.1",
|
||||
"pestphp/pest-dev-tools": "dev-master"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
@ -61,13 +57,12 @@
|
||||
"bin/pest"
|
||||
],
|
||||
"scripts": {
|
||||
"compile": "@php ./scripts/compile.php",
|
||||
"lint": "rector process src && php-cs-fixer fix -v",
|
||||
"test:lint": "php-cs-fixer fix -v --dry-run && rector process src --dry-run",
|
||||
"test:types": "phpstan analyse --ansi",
|
||||
"test:unit": "bin/pest --colors=always --exclude-group=integration",
|
||||
"test:integration": "bin/pest --colors=always --group=integration",
|
||||
"test:integration:snapshots": "REBUILD_SNAPSHOTS=true bin/pest --colors=always",
|
||||
"test:types": "phpstan analyse --ansi --memory-limit=0",
|
||||
"test:unit": "php bin/pest --colors=always --exclude-group=integration",
|
||||
"test:integration": "php bin/pest --colors=always --group=integration",
|
||||
"update:snapshots": "REBUILD_SNAPSHOTS=true php bin/pest --colors=always",
|
||||
"test": [
|
||||
"@test:lint",
|
||||
"@test:types",
|
||||
@ -76,6 +71,14 @@
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.4.x-dev"
|
||||
},
|
||||
"pest": {
|
||||
"plugins": [
|
||||
"Pest\\Plugins\\Version"
|
||||
]
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Pest\\Laravel\\PestServiceProvider"
|
||||
|
||||
13
phpstan.neon
13
phpstan.neon
@ -15,8 +15,19 @@ parameters:
|
||||
reportUnmatchedIgnoredErrors: true
|
||||
|
||||
ignoreErrors:
|
||||
- "#Undefined variable: \\$this#"
|
||||
- "#is not allowed to extend#"
|
||||
- "#Language construct eval#"
|
||||
- "# with null as default value#"
|
||||
- "#Using \\$this in static method#"
|
||||
- "#has parameter \\$closure with default value.#"
|
||||
- "#has parameter \\$description with default value.#"
|
||||
- "#Method Pest\\\\Support\\\\Reflection::getParameterClassName\\(\\) has a nullable return type declaration.#"
|
||||
-
|
||||
message: '#Call to an undefined method PHPUnit\\Framework\\Test::getName\(\)#'
|
||||
path: src/TeamCity.php
|
||||
-
|
||||
message: '#invalid typehint type Pest\\Concerns\\TestCase#'
|
||||
path: src/TeamCity.php
|
||||
-
|
||||
message: '#is not subtype of native type PHPUnit\\Framework\\Test#'
|
||||
path: src/TeamCity.php
|
||||
|
||||
@ -8,9 +8,9 @@
|
||||
<directory suffix=".php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</include>
|
||||
</coverage>
|
||||
</phpunit>
|
||||
|
||||
35
rector.php
Normal file
35
rector.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Php70\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector;
|
||||
use Rector\Set\ValueObject\SetList;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
|
||||
$parameters->set(Option::AUTO_IMPORT_NAMES, true);
|
||||
|
||||
$parameters->set(Option::SETS, [
|
||||
SetList::ACTION_INJECTION_TO_CONSTRUCTOR_INJECTION,
|
||||
SetList::ARRAY_STR_FUNCTIONS_TO_STATIC_CALL,
|
||||
SetList::CODE_QUALITY,
|
||||
SetList::PHP_53,
|
||||
SetList::PHP_54,
|
||||
SetList::PHP_56,
|
||||
SetList::PHP_70,
|
||||
SetList::PHP_71,
|
||||
SetList::PHP_72,
|
||||
SetList::PHP_73,
|
||||
SetList::PHPSTAN,
|
||||
SetList::PHPUNIT_CODE_QUALITY,
|
||||
SetList::SOLID,
|
||||
]);
|
||||
|
||||
$parameters->set(Option::PATHS, [__DIR__ . '/src', __DIR__ . '/tests']);
|
||||
$parameters->set(Option::EXCLUDE_RECTORS, [
|
||||
StaticCallOnNonStaticToInstanceCallRector::class,
|
||||
]);
|
||||
};
|
||||
16
rector.yaml
16
rector.yaml
@ -1,16 +0,0 @@
|
||||
# rector.yaml
|
||||
parameters:
|
||||
sets:
|
||||
- 'action-injection-to-constructor-injection'
|
||||
- 'array-str-functions-to-static-call'
|
||||
- 'celebrity'
|
||||
- 'doctrine'
|
||||
- 'phpstan'
|
||||
- 'phpunit-code-quality'
|
||||
- 'solid'
|
||||
- 'early-return'
|
||||
- 'doctrine-code-quality'
|
||||
- 'code-quality'
|
||||
- 'php71'
|
||||
- 'php72'
|
||||
- 'php73'
|
||||
@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use Pest\Console\Coverage;
|
||||
use Pest\Support\Str;
|
||||
use Pest\TestSuite;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Input\InputDefinition;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AddsCoverage
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const COVERAGE_OPTION = 'coverage';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const MIN_OPTION = 'min';
|
||||
|
||||
/**
|
||||
* Holds the coverage related options.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const OPTIONS = [self::COVERAGE_OPTION, self::MIN_OPTION];
|
||||
|
||||
/**
|
||||
* If any, adds the coverage params to the given original arguments.
|
||||
*
|
||||
* @param array<int, string> $originals
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function from(TestSuite $testSuite, array $originals): array
|
||||
{
|
||||
$arguments = array_merge([''], array_values(array_filter($originals, function ($original): bool {
|
||||
foreach (self::OPTIONS as $option) {
|
||||
if ($original === sprintf('--%s', $option) || Str::startsWith($original, sprintf('--%s=', $option))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
})));
|
||||
|
||||
$originals = array_flip($originals);
|
||||
foreach ($arguments as $argument) {
|
||||
unset($originals[$argument]);
|
||||
}
|
||||
$originals = array_flip($originals);
|
||||
|
||||
$inputs = [];
|
||||
$inputs[] = new InputOption(self::COVERAGE_OPTION, null, InputOption::VALUE_NONE);
|
||||
$inputs[] = new InputOption(self::MIN_OPTION, null, InputOption::VALUE_REQUIRED);
|
||||
|
||||
$input = new ArgvInput($arguments, new InputDefinition($inputs));
|
||||
if ((bool) $input->getOption(self::COVERAGE_OPTION)) {
|
||||
$testSuite->coverage = true;
|
||||
$originals[] = '--coverage-php';
|
||||
$originals[] = Coverage::getPath();
|
||||
}
|
||||
|
||||
if ($input->getOption(self::MIN_OPTION) !== null) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
$testSuite->coverageMin = (float) $input->getOption(self::MIN_OPTION);
|
||||
}
|
||||
|
||||
return $originals;
|
||||
}
|
||||
}
|
||||
@ -5,12 +5,16 @@ declare(strict_types=1);
|
||||
namespace Pest\Actions;
|
||||
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\Printer;
|
||||
use Pest\TeamCity;
|
||||
use PHPUnit\TextUI\DefaultResultPrinter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AddsDefaults
|
||||
{
|
||||
private const PRINTER = 'printer';
|
||||
|
||||
/**
|
||||
* Adds default arguments to the given `arguments` array.
|
||||
*
|
||||
@ -20,8 +24,12 @@ final class AddsDefaults
|
||||
*/
|
||||
public static function to(array $arguments): array
|
||||
{
|
||||
if (!array_key_exists('printer', $arguments)) {
|
||||
$arguments['printer'] = new Printer();
|
||||
if (!array_key_exists(self::PRINTER, $arguments)) {
|
||||
$arguments[self::PRINTER] = new Printer(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
|
||||
}
|
||||
|
||||
if ($arguments[self::PRINTER] === \PHPUnit\Util\Log\TeamCity::class) {
|
||||
$arguments[self::PRINTER] = new TeamCity($arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
|
||||
@ -6,8 +6,7 @@ namespace Pest\Actions;
|
||||
|
||||
use Pest\Exceptions\AttributeNotSupportedYet;
|
||||
use Pest\Exceptions\FileOrFolderNotFound;
|
||||
use PHPUnit\TextUI\Configuration\Configuration;
|
||||
use PHPUnit\TextUI\Configuration\Registry;
|
||||
use PHPUnit\TextUI\XmlConfiguration\Loader;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -30,9 +29,7 @@ final class ValidatesConfiguration
|
||||
throw new FileOrFolderNotFound('phpunit.xml');
|
||||
}
|
||||
|
||||
$configuration = Registry::getInstance()
|
||||
->get($arguments[self::CONFIGURATION_KEY])
|
||||
->phpunit();
|
||||
$configuration = (new Loader())->load($arguments[self::CONFIGURATION_KEY])->phpunit();
|
||||
|
||||
if ($configuration->processIsolation()) {
|
||||
throw new AttributeNotSupportedYet('processIsolation', 'true');
|
||||
|
||||
@ -19,7 +19,6 @@ final class ValidatesEnvironment
|
||||
*/
|
||||
private const NEEDED_FILES = [
|
||||
'composer.json',
|
||||
'tests',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@ -5,9 +5,11 @@ declare(strict_types=1);
|
||||
namespace Pest\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Pest\Expectation;
|
||||
use Pest\Support\ExceptionTrace;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Util\Test;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* To avoid inheritance conflicts, all the fields related
|
||||
@ -63,6 +65,11 @@ trait TestCase
|
||||
return $this->__description;
|
||||
}
|
||||
|
||||
public static function __getFileName(): string
|
||||
{
|
||||
return self::$__filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called before the first test of this test class is run.
|
||||
*/
|
||||
@ -87,6 +94,16 @@ trait TestCase
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expectation.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function expect($value): Expectation
|
||||
{
|
||||
return new Expectation($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets executed before the test.
|
||||
*/
|
||||
@ -129,16 +146,25 @@ trait TestCase
|
||||
|
||||
/**
|
||||
* Runs the test.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function __test(): void
|
||||
public function __test()
|
||||
{
|
||||
$this->__callClosure($this->__test, func_get_args());
|
||||
return $this->__callClosure($this->__test, func_get_args());
|
||||
}
|
||||
|
||||
private function __callClosure(Closure $closure, array $arguments): void
|
||||
/**
|
||||
* @return mixed
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
private function __callClosure(Closure $closure, array $arguments)
|
||||
{
|
||||
ExceptionTrace::ensure(function () use ($closure, $arguments) {
|
||||
call_user_func_array(Closure::bind($closure, $this, get_class($this)), $arguments);
|
||||
return ExceptionTrace::ensure(function () use ($closure, $arguments) {
|
||||
return call_user_func_array(Closure::bind($closure, $this, get_class($this)), $arguments);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -4,11 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace Pest\Console;
|
||||
|
||||
use Pest\Actions\AddsCoverage;
|
||||
use Pest\Actions\AddsDefaults;
|
||||
use Pest\Actions\AddsTests;
|
||||
use Pest\Actions\LoadStructure;
|
||||
use Pest\Actions\ValidatesConfiguration;
|
||||
use Pest\Contracts\Plugins\AddsOutput;
|
||||
use Pest\Contracts\Plugins\HandlesArguments;
|
||||
use Pest\Plugin\Loader;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Framework\TestSuite as BaseTestSuite;
|
||||
use PHPUnit\TextUI\Command as BaseCommand;
|
||||
@ -54,9 +56,14 @@ final class Command extends BaseCommand
|
||||
protected function handleArguments(array $argv): void
|
||||
{
|
||||
/*
|
||||
* First, let's handle pest is own `--coverage` param.
|
||||
* First, let's call all plugins that want to handle arguments
|
||||
*/
|
||||
$argv = AddsCoverage::from($this->testSuite, $argv);
|
||||
$plugins = Loader::getPlugins(HandlesArguments::class);
|
||||
|
||||
/** @var HandlesArguments $plugin */
|
||||
foreach ($plugins as $plugin) {
|
||||
$argv = $plugin->handleArguments($argv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Next, as usual, let's send the console arguments to PHPUnit.
|
||||
@ -81,6 +88,8 @@ final class Command extends BaseCommand
|
||||
*/
|
||||
$this->arguments = AddsDefaults::to($this->arguments);
|
||||
|
||||
LoadStructure::in($this->testSuite->rootPath);
|
||||
|
||||
$testRunner = new TestRunner($this->arguments['loader']);
|
||||
$testSuite = $this->arguments['test'];
|
||||
|
||||
@ -102,7 +111,6 @@ final class Command extends BaseCommand
|
||||
$this->arguments['test'] = $testSuite;
|
||||
}
|
||||
|
||||
LoadStructure::in($this->testSuite->rootPath);
|
||||
AddsTests::to($testSuite, $this->testSuite);
|
||||
|
||||
return $testRunner;
|
||||
@ -119,25 +127,14 @@ final class Command extends BaseCommand
|
||||
{
|
||||
$result = parent::run($argv, false);
|
||||
|
||||
if ($result === 0 && $this->testSuite->coverage) {
|
||||
if (!Coverage::isAvailable()) {
|
||||
$this->output->writeln(
|
||||
"\n <fg=white;bg=red;options=bold> ERROR </> No code coverage driver is available.</>",
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
/*
|
||||
* Let's call all plugins that want to add output after test execution
|
||||
*/
|
||||
$plugins = Loader::getPlugins(AddsOutput::class);
|
||||
|
||||
$coverage = Coverage::report($this->output);
|
||||
|
||||
$result = (int) ($coverage < $this->testSuite->coverageMin);
|
||||
|
||||
if ($result === 1) {
|
||||
$this->output->writeln(sprintf(
|
||||
"\n <fg=white;bg=red;options=bold> FAIL </> Code coverage below expected:<fg=red;options=bold> %s %%</>. Minimum:<fg=white;options=bold> %s %%</>.",
|
||||
number_format($coverage, 1),
|
||||
number_format($this->testSuite->coverageMin, 1)
|
||||
));
|
||||
}
|
||||
/** @var AddsOutput $plugin */
|
||||
foreach ($plugins as $plugin) {
|
||||
$result = $plugin->addOutput($result);
|
||||
}
|
||||
|
||||
exit($result);
|
||||
|
||||
@ -1,167 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Console;
|
||||
|
||||
use Pest\Exceptions\ShouldNotHappen;
|
||||
use SebastianBergmann\CodeCoverage\Node\Directory;
|
||||
use SebastianBergmann\CodeCoverage\Node\File;
|
||||
use SebastianBergmann\Environment\Runtime;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Terminal;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Coverage
|
||||
{
|
||||
/**
|
||||
* Returns the coverage path.
|
||||
*/
|
||||
public static function getPath(): string
|
||||
{
|
||||
return implode(DIRECTORY_SEPARATOR, [
|
||||
dirname(__DIR__, 2),
|
||||
'.temp',
|
||||
'coverage.php',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs true there is any code
|
||||
* coverage driver available.
|
||||
*/
|
||||
public static function isAvailable(): bool
|
||||
{
|
||||
return (new Runtime())->canCollectCodeCoverage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the code coverage report to the
|
||||
* console and returns the result in float.
|
||||
*/
|
||||
public static function report(OutputInterface $output): float
|
||||
{
|
||||
if (!file_exists($reportPath = self::getPath())) {
|
||||
throw ShouldNotHappen::fromMessage(sprintf('Coverage not found in path: %s.', $reportPath));
|
||||
}
|
||||
|
||||
/** @var \SebastianBergmann\CodeCoverage\CodeCoverage $codeCoverage */
|
||||
$codeCoverage = require $reportPath;
|
||||
unlink($reportPath);
|
||||
|
||||
$totalWidth = (new Terminal())->getWidth();
|
||||
|
||||
$dottedLineLength = $totalWidth <= 70 ? $totalWidth : 70;
|
||||
|
||||
$totalCoverage = $codeCoverage->getReport()->getLineExecutedPercent();
|
||||
|
||||
$output->writeln(
|
||||
sprintf(
|
||||
' <fg=white;options=bold>Cov: </><fg=default>%s</>',
|
||||
$totalCoverage
|
||||
)
|
||||
);
|
||||
|
||||
$output->writeln('');
|
||||
|
||||
/** @var Directory<File|Directory> $report */
|
||||
$report = $codeCoverage->getReport();
|
||||
|
||||
foreach ($report->getIterator() as $file) {
|
||||
if (!$file instanceof File) {
|
||||
continue;
|
||||
}
|
||||
$dirname = dirname($file->getId());
|
||||
$basename = basename($file->getId(), '.php');
|
||||
|
||||
$name = $dirname === '.' ? $basename : implode(DIRECTORY_SEPARATOR, [
|
||||
$dirname,
|
||||
$basename,
|
||||
]);
|
||||
$rawName = $dirname === '.' ? $basename : implode(DIRECTORY_SEPARATOR, [
|
||||
$dirname,
|
||||
$basename,
|
||||
]);
|
||||
|
||||
$linesExecutedTakenSize = 0;
|
||||
|
||||
if ($file->getLineExecutedPercent() != '0.00%') {
|
||||
$linesExecutedTakenSize = strlen($uncoveredLines = trim(implode(', ', self::getMissingCoverage($file)))) + 1;
|
||||
$name .= sprintf(' <fg=red>%s</>', $uncoveredLines);
|
||||
}
|
||||
|
||||
$percentage = $file->getNumExecutableLines() === 0
|
||||
? '100.0'
|
||||
: number_format((float) $file->getLineExecutedPercent(), 1, '.', '');
|
||||
|
||||
$takenSize = strlen($rawName . $percentage) + 4 + $linesExecutedTakenSize; // adding 3 space and percent sign
|
||||
|
||||
$percentage = sprintf(
|
||||
'<fg=%s>%s</>',
|
||||
$percentage === '100.0' ? 'green' : ($percentage === '0.0' ? 'red' : 'yellow'),
|
||||
$percentage
|
||||
);
|
||||
|
||||
$output->writeln(sprintf(' %s %s %s %%',
|
||||
$name,
|
||||
str_repeat('.', max($dottedLineLength - $takenSize, 1)),
|
||||
$percentage
|
||||
));
|
||||
}
|
||||
|
||||
return (float) $totalCoverage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array of missing coverage on the following format:.
|
||||
*
|
||||
* ```
|
||||
* ['11', '20..25', '50', '60...80'];
|
||||
* ```
|
||||
*
|
||||
* @param File $file
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function getMissingCoverage($file): array
|
||||
{
|
||||
$shouldBeNewLine = true;
|
||||
|
||||
$eachLine = function (array $array, array $tests, int $line) use (&$shouldBeNewLine): array {
|
||||
if (count($tests) > 0) {
|
||||
$shouldBeNewLine = true;
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
if ($shouldBeNewLine) {
|
||||
$array[] = (string) $line;
|
||||
$shouldBeNewLine = false;
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
$lastKey = count($array) - 1;
|
||||
|
||||
if (array_key_exists($lastKey, $array) && strpos($array[$lastKey], '..') !== false) {
|
||||
[$from] = explode('..', $array[$lastKey]);
|
||||
$array[$lastKey] = sprintf('%s..%s', $from, $line);
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
$array[$lastKey] = sprintf('%s..%s', $array[$lastKey], $line);
|
||||
|
||||
return $array;
|
||||
};
|
||||
|
||||
$array = [];
|
||||
foreach (array_filter($file->getCoverageData(), 'is_array') as $line => $tests) {
|
||||
$array = $eachLine($array, $tests, $line);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
66
src/Console/Thanks.php
Normal file
66
src/Console/Thanks.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Console;
|
||||
|
||||
use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Thanks
|
||||
{
|
||||
/** @var array<int, string> */
|
||||
private const FUNDING_MESSAGES = [
|
||||
'',
|
||||
' - Star or contribute to Pest:',
|
||||
' <options=bold>https://github.com/pestphp/pest</>',
|
||||
' - Tweet something about Pest on Twitter:',
|
||||
' <options=bold>https://twitter.com/pestphp</>',
|
||||
' - Sponsor the creator:',
|
||||
' <options=bold>https://github.com/sponsors/nunomaduro</>',
|
||||
];
|
||||
|
||||
/** @var OutputInterface */
|
||||
private $output;
|
||||
|
||||
public function __construct(OutputInterface $output)
|
||||
{
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the user to support Pest.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
$wantsToSupport = (new SymfonyQuestionHelper())->ask(
|
||||
new ArrayInput([]),
|
||||
$this->output,
|
||||
new ConfirmationQuestion(
|
||||
'Can you quickly <options=bold>star our GitHub repository</>? 🙏🏻',
|
||||
true,
|
||||
)
|
||||
);
|
||||
|
||||
if ($wantsToSupport === true) {
|
||||
if (PHP_OS_FAMILY == 'Darwin') {
|
||||
exec('open https://github.com/pestphp/pest');
|
||||
}
|
||||
if (PHP_OS_FAMILY == 'Windows') {
|
||||
exec('start https://github.com/pestphp/pest');
|
||||
}
|
||||
if (PHP_OS_FAMILY == 'Linux') {
|
||||
exec('xdg-open https://github.com/pestphp/pest');
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::FUNDING_MESSAGES as $message) {
|
||||
$this->output->writeln($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/Contracts/Plugins/AddsOutput.php
Normal file
16
src/Contracts/Plugins/AddsOutput.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts\Plugins;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface AddsOutput
|
||||
{
|
||||
/**
|
||||
* Allows to add custom output after the test suite was executed.
|
||||
*/
|
||||
public function addOutput(int $testReturnCode): int;
|
||||
}
|
||||
23
src/Contracts/Plugins/HandlesArguments.php
Normal file
23
src/Contracts/Plugins/HandlesArguments.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts\Plugins;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface HandlesArguments
|
||||
{
|
||||
/**
|
||||
* Allows to handle custom command line arguments.
|
||||
*
|
||||
* PLEASE NOTE: it is necessary to remove any custom argument from the array
|
||||
* because otherwise the application will complain about them
|
||||
*
|
||||
* @param array<int, string> $arguments
|
||||
*
|
||||
* @return array<int, string> the updated list of arguments
|
||||
*/
|
||||
public function handleArguments(array $arguments): array;
|
||||
}
|
||||
@ -74,12 +74,30 @@ final class Datasets
|
||||
$data = iterator_to_array($data);
|
||||
}
|
||||
|
||||
$namedData = [];
|
||||
$dataSetDescriptions = [];
|
||||
$dataSetValues = [];
|
||||
|
||||
foreach ($data as $values) {
|
||||
$values = is_array($values) ? $values : [$values];
|
||||
|
||||
$name = $description . self::getDataSetDescription($values);
|
||||
$namedData[$name] = $values;
|
||||
$dataSetDescriptions[] = $description . self::getDataSetDescription($values);
|
||||
$dataSetValues[] = $values;
|
||||
}
|
||||
|
||||
foreach (array_count_values($dataSetDescriptions) as $descriptionToCheck => $count) {
|
||||
if ($count > 1) {
|
||||
$index = 1;
|
||||
foreach ($dataSetDescriptions as $i => $dataSetDescription) {
|
||||
if ($dataSetDescription === $descriptionToCheck) {
|
||||
$dataSetDescriptions[$i] .= sprintf(' #%d', $index++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$namedData = [];
|
||||
foreach ($dataSetDescriptions as $i => $dataSetDescription) {
|
||||
$namedData[$dataSetDescription] = $dataSetValues[$i];
|
||||
}
|
||||
|
||||
return $namedData;
|
||||
|
||||
538
src/Expectation.php
Normal file
538
src/Expectation.php
Normal file
@ -0,0 +1,538 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest;
|
||||
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @property Expectation $not Creates the opposite expectation.
|
||||
*/
|
||||
final class Expectation
|
||||
{
|
||||
/**
|
||||
* The expectation value.
|
||||
*
|
||||
* @readonly
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Creates a new expectation.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expectation.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function and($value): Expectation
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the opposite expectation for the value.
|
||||
*/
|
||||
public function not(): OppositeExpectation
|
||||
{
|
||||
return new OppositeExpectation($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that two variables have the same type and
|
||||
* value. Used on objects, it asserts that two
|
||||
* variables reference the same object.
|
||||
*
|
||||
* @param mixed $expected
|
||||
*/
|
||||
public function toBe($expected): Expectation
|
||||
{
|
||||
Assert::assertSame($expected, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is empty.
|
||||
*/
|
||||
public function toBeEmpty(): Expectation
|
||||
{
|
||||
Assert::assertEmpty($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is true.
|
||||
*/
|
||||
public function toBeTrue(): Expectation
|
||||
{
|
||||
Assert::assertTrue($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is false.
|
||||
*/
|
||||
public function toBeFalse(): Expectation
|
||||
{
|
||||
Assert::assertFalse($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is greater than $expected.
|
||||
*
|
||||
* @param int|float $expected
|
||||
*/
|
||||
public function toBeGreaterThan($expected): Expectation
|
||||
{
|
||||
Assert::assertGreaterThan($expected, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is greater than or equal to $expected.
|
||||
*
|
||||
* @param int|float $expected
|
||||
*/
|
||||
public function toBeGreaterThanOrEqual($expected): Expectation
|
||||
{
|
||||
Assert::assertGreaterThanOrEqual($expected, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is less than or equal to $expected.
|
||||
*
|
||||
* @param int|float $expected
|
||||
*/
|
||||
public function toBeLessThan($expected): Expectation
|
||||
{
|
||||
Assert::assertLessThan($expected, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is less than $expected.
|
||||
*
|
||||
* @param int|float $expected
|
||||
*/
|
||||
public function toBeLessThanOrEqual($expected): Expectation
|
||||
{
|
||||
Assert::assertLessThanOrEqual($expected, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that $needle is an element of the value.
|
||||
*
|
||||
* @param mixed $needle
|
||||
*/
|
||||
public function toContain($needle): Expectation
|
||||
{
|
||||
if (is_string($this->value)) {
|
||||
Assert::assertStringContainsString($needle, $this->value);
|
||||
} else {
|
||||
Assert::assertContains($needle, $this->value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value starts with $expected.
|
||||
*/
|
||||
public function toStartWith(string $expected): Expectation
|
||||
{
|
||||
Assert::assertStringStartsWith($expected, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value ends with $expected.
|
||||
*/
|
||||
public function toEndWith(string $expected): Expectation
|
||||
{
|
||||
Assert::assertStringEndsWith($expected, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that $count matches the number of elements of the value.
|
||||
*/
|
||||
public function toHaveCount(int $count): Expectation
|
||||
{
|
||||
Assert::assertCount($count, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value contains the property $name.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function toHaveProperty(string $name, $value = null): Expectation
|
||||
{
|
||||
$this->toBeObject();
|
||||
|
||||
Assert::assertTrue(property_exists($this->value, $name));
|
||||
|
||||
if (func_num_args() > 1) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
Assert::assertEquals($value, $this->value->{$name});
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that two variables have the same value.
|
||||
*
|
||||
* @param mixed $expected
|
||||
*/
|
||||
public function toEqual($expected): Expectation
|
||||
{
|
||||
Assert::assertEquals($expected, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that two variables have the same value.
|
||||
* The contents of $expected and the $this->value are
|
||||
* canonicalized before they are compared. For instance, when the two
|
||||
* variables $expected and $this->value are arrays, then these arrays
|
||||
* are sorted before they are compared. When $expected and $this->value
|
||||
* are objects, each object is converted to an array containing all
|
||||
* private, protected and public attributes.
|
||||
*
|
||||
* @param mixed $expected
|
||||
*/
|
||||
public function toEqualCanonicalizing($expected): Expectation
|
||||
{
|
||||
Assert::assertEqualsCanonicalizing($expected, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the absolute difference between the value and $expected
|
||||
* is lower than $delta.
|
||||
*
|
||||
* @param mixed $expected
|
||||
*/
|
||||
public function toEqualWithDelta($expected, float $delta): Expectation
|
||||
{
|
||||
Assert::assertEqualsWithDelta($expected, $this->value, $delta);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is infinite.
|
||||
*/
|
||||
public function toBeInfinite(): Expectation
|
||||
{
|
||||
Assert::assertInfinite($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is an instance of $class.
|
||||
*
|
||||
* @param string $class
|
||||
*/
|
||||
public function toBeInstanceOf($class): Expectation
|
||||
{
|
||||
/* @phpstan-ignore-next-line */
|
||||
Assert::assertInstanceOf($class, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is an array.
|
||||
*/
|
||||
public function toBeArray(): Expectation
|
||||
{
|
||||
Assert::assertIsArray($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is of type bool.
|
||||
*/
|
||||
public function toBeBool(): Expectation
|
||||
{
|
||||
Assert::assertIsBool($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is of type callable.
|
||||
*/
|
||||
public function toBeCallable(): Expectation
|
||||
{
|
||||
Assert::assertIsCallable($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is of type float.
|
||||
*/
|
||||
public function toBeFloat(): Expectation
|
||||
{
|
||||
Assert::assertIsFloat($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is of type int.
|
||||
*/
|
||||
public function toBeInt(): Expectation
|
||||
{
|
||||
Assert::assertIsInt($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is of type iterable.
|
||||
*/
|
||||
public function toBeIterable(): Expectation
|
||||
{
|
||||
Assert::assertIsIterable($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is of type numeric.
|
||||
*/
|
||||
public function toBeNumeric(): Expectation
|
||||
{
|
||||
Assert::assertIsNumeric($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is of type object.
|
||||
*/
|
||||
public function toBeObject(): Expectation
|
||||
{
|
||||
Assert::assertIsObject($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is of type resource.
|
||||
*/
|
||||
public function toBeResource(): Expectation
|
||||
{
|
||||
Assert::assertIsResource($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is of type scalar.
|
||||
*/
|
||||
public function toBeScalar(): Expectation
|
||||
{
|
||||
Assert::assertIsScalar($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is of type string.
|
||||
*/
|
||||
public function toBeString(): Expectation
|
||||
{
|
||||
Assert::assertIsString($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is NAN.
|
||||
*/
|
||||
public function toBeNan(): Expectation
|
||||
{
|
||||
Assert::assertNan($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is null.
|
||||
*/
|
||||
public function toBeNull(): Expectation
|
||||
{
|
||||
Assert::assertNull($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value array has the provided $key.
|
||||
*
|
||||
* @param string|int $key
|
||||
*/
|
||||
public function toHaveKey($key): Expectation
|
||||
{
|
||||
Assert::assertArrayHasKey($key, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value array has the provided $keys.
|
||||
*
|
||||
* @param array<int, int|string> $keys
|
||||
*/
|
||||
public function toHaveKeys(array $keys): Expectation
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
$this->toHaveKey($key);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is a directory.
|
||||
*/
|
||||
public function toBeDirectory(): Expectation
|
||||
{
|
||||
Assert::assertDirectoryExists($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is a directory and is readable.
|
||||
*/
|
||||
public function toBeReadableDirectory(): Expectation
|
||||
{
|
||||
Assert::assertDirectoryIsReadable($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is a directory and is writable.
|
||||
*/
|
||||
public function toBeWritableDirectory(): Expectation
|
||||
{
|
||||
Assert::assertDirectoryIsWritable($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is a file.
|
||||
*/
|
||||
public function toBeFile(): Expectation
|
||||
{
|
||||
Assert::assertFileExists($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is a file and is readable.
|
||||
*/
|
||||
public function toBeReadableFile(): Expectation
|
||||
{
|
||||
Assert::assertFileIsReadable($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value is a file and is writable.
|
||||
*/
|
||||
public function toBeWritableFile(): Expectation
|
||||
{
|
||||
Assert::assertFileIsWritable($this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value object matches a subset
|
||||
* of the properties of an given object.
|
||||
*
|
||||
* @param array<string, mixed>|object $object
|
||||
*/
|
||||
public function toMatchObject($object): Expectation
|
||||
{
|
||||
foreach ((array) $object as $property => $value) {
|
||||
$this->toHaveProperty($property, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value matches a regular expression.
|
||||
*/
|
||||
public function toMatch(string $expression): Expectation
|
||||
{
|
||||
Assert::assertMatchesRegularExpression($expression, $this->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value matches a constraint.
|
||||
*/
|
||||
public function toMatchConstraint(Constraint $constraint): Expectation
|
||||
{
|
||||
Assert::assertThat($this->value, $constraint);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically calls methods on the class without any arguments.
|
||||
*
|
||||
* @return Expectation
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
/* @phpstan-ignore-next-line */
|
||||
return $this->{$name}();
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ use Closure;
|
||||
use Pest\Concerns;
|
||||
use Pest\Contracts\HasPrintableTestCaseName;
|
||||
use Pest\Datasets;
|
||||
use Pest\Exceptions\ShouldNotHappen;
|
||||
use Pest\Support\HigherOrderMessageCollection;
|
||||
use Pest\Support\NullClosure;
|
||||
use Pest\TestSuite;
|
||||
@ -39,9 +40,10 @@ final class TestCaseFactory
|
||||
/**
|
||||
* Holds the test description.
|
||||
*
|
||||
* @readonly
|
||||
* If the description is null, means that it
|
||||
* will be created with the given assertions.
|
||||
*
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
public $description;
|
||||
|
||||
@ -104,7 +106,7 @@ final class TestCaseFactory
|
||||
/**
|
||||
* Creates a new anonymous test case pending object.
|
||||
*/
|
||||
public function __construct(string $filename, string $description, Closure $closure = null)
|
||||
public function __construct(string $filename, string $description = null, Closure $closure = null)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
$this->description = $description;
|
||||
@ -122,14 +124,22 @@ final class TestCaseFactory
|
||||
*/
|
||||
public function build(TestSuite $testSuite): array
|
||||
{
|
||||
if ($this->description === null) {
|
||||
throw ShouldNotHappen::fromMessage('Description can not be empty.');
|
||||
}
|
||||
|
||||
$chains = $this->chains;
|
||||
$proxies = $this->proxies;
|
||||
$factoryTest = $this->test;
|
||||
|
||||
$test = function () use ($chains, $proxies, $factoryTest): void {
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
$test = function () use ($chains, $proxies, $factoryTest) {
|
||||
$proxies->proxy($this);
|
||||
$chains->chain($this);
|
||||
call_user_func(Closure::bind($factoryTest, $this, get_class($this)), ...func_get_args());
|
||||
|
||||
return call_user_func(Closure::bind($factoryTest, $this, get_class($this)), ...func_get_args());
|
||||
};
|
||||
|
||||
$className = $this->makeClassFromFilename($this->filename);
|
||||
@ -147,21 +157,37 @@ final class TestCaseFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a fully qualified class name
|
||||
* from the given filename.
|
||||
* Makes a fully qualified class name from the current filename.
|
||||
*/
|
||||
public function getClassName(): string
|
||||
{
|
||||
return $this->makeClassFromFilename($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a fully qualified class name from the given filename.
|
||||
*/
|
||||
public function makeClassFromFilename(string $filename): string
|
||||
{
|
||||
if ('\\' === DIRECTORY_SEPARATOR) {
|
||||
// In case Windows, strtolower drive name, like in UsesCall.
|
||||
$filename = (string) preg_replace_callback('~^(?P<drive>[a-z]+:\\\)~i', function ($match): string {
|
||||
return strtolower($match['drive']);
|
||||
}, $filename);
|
||||
}
|
||||
|
||||
$filename = (string) realpath($filename);
|
||||
$rootPath = TestSuite::getInstance()->rootPath;
|
||||
$relativePath = str_replace($rootPath . DIRECTORY_SEPARATOR, '', $filename);
|
||||
$relativePath = dirname(ucfirst($relativePath)) . DIRECTORY_SEPARATOR . basename($relativePath, '.php');
|
||||
$relativePath = str_replace(DIRECTORY_SEPARATOR, '\\', $relativePath);
|
||||
|
||||
// Strip out any %-encoded octets.
|
||||
$relativePath = (string) preg_replace('|%[a-fA-F0-9][a-fA-F0-9]|', '', $relativePath);
|
||||
|
||||
// Limit to A-Z, a-z, 0-9, '_', '-'.
|
||||
$relativePath = (string) preg_replace('/[^A-Za-z0-9.\/]/', '', $relativePath);
|
||||
|
||||
$classFQN = 'P\\' . basename(ucfirst(str_replace(DIRECTORY_SEPARATOR, '\\', $relativePath)), '.php');
|
||||
$relativePath = (string) preg_replace('/[^A-Za-z0-9.\\\]/', '', $relativePath);
|
||||
|
||||
$classFQN = 'P\\' . $relativePath;
|
||||
if (class_exists($classFQN)) {
|
||||
return $classFQN;
|
||||
}
|
||||
|
||||
@ -61,7 +61,8 @@ final class PestDatasetCommand extends Command
|
||||
$element = Str::singular($name);
|
||||
$contents = str_replace('{dataset_element}', $element, $contents);
|
||||
File::put($target, str_replace('{dataset_name}', $name, $contents));
|
||||
$message = sprintf('`%s` created successfully.', $relativePath);
|
||||
|
||||
$this->output->success(sprintf('`%s` created successfully.', $relativePath));
|
||||
$this->output->success($message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,9 @@ namespace Pest\Laravel\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Pest\Console\Thanks;
|
||||
use Pest\Exceptions\InvalidConsoleArgument;
|
||||
use Pest\Support\Str;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -36,6 +38,7 @@ final class PestInstallCommand extends Command
|
||||
$pest = base_path('tests/Pest.php');
|
||||
/* @phpstan-ignore-next-line */
|
||||
$helpers = base_path('tests/Helpers.php');
|
||||
$stubs = $this->isLumen() ? 'stubs/Lumen' : 'stubs/Laravel';
|
||||
|
||||
foreach ([$pest, $helpers] as $file) {
|
||||
if (File::exists($file)) {
|
||||
@ -45,17 +48,30 @@ final class PestInstallCommand extends Command
|
||||
|
||||
File::copy(implode(DIRECTORY_SEPARATOR, [
|
||||
dirname(__DIR__, 3),
|
||||
'stubs',
|
||||
$stubs,
|
||||
'Pest.php',
|
||||
]), $pest);
|
||||
|
||||
File::copy(implode(DIRECTORY_SEPARATOR, [
|
||||
dirname(__DIR__, 3),
|
||||
'stubs',
|
||||
$stubs,
|
||||
'Helpers.php',
|
||||
]), $helpers);
|
||||
|
||||
$this->output->success('`tests/Pest.php` created successfully.');
|
||||
$this->output->success('`tests/Helpers.php` created successfully.');
|
||||
|
||||
if (!(bool) $this->option('no-interaction')) {
|
||||
(new Thanks($this->output))();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this is a Lumen application.
|
||||
*/
|
||||
private function isLumen(): bool
|
||||
{
|
||||
/* @phpstan-ignore-next-line */
|
||||
return Str::startsWith(app()->version(), 'Lumen');
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +64,8 @@ final class PestTestCommand extends Command
|
||||
$name = Str::endsWith($name, 'test') ? mb_substr($name, 0, -4) : $name;
|
||||
|
||||
File::put($target, str_replace('{name}', $name, $contents));
|
||||
$message = sprintf('`%s` created successfully.', $relativePath);
|
||||
|
||||
$this->output->success(sprintf('`%s` created successfully.', $relativePath));
|
||||
$this->output->success($message);
|
||||
}
|
||||
}
|
||||
|
||||
100
src/OppositeExpectation.php
Normal file
100
src/OppositeExpectation.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest;
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
use SebastianBergmann\Exporter\Exporter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @mixin Expectation
|
||||
*/
|
||||
final class OppositeExpectation
|
||||
{
|
||||
/**
|
||||
* @var Expectation
|
||||
*/
|
||||
private $original;
|
||||
|
||||
/**
|
||||
* Creates a new opposite expectation.
|
||||
*/
|
||||
public function __construct(Expectation $original)
|
||||
{
|
||||
$this->original = $original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value array not has the provided $keys.
|
||||
*
|
||||
* @param array<int, int|string> $keys
|
||||
*/
|
||||
public function toHaveKeys(array $keys): Expectation
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
try {
|
||||
$this->original->toHaveKey($key);
|
||||
} catch (ExpectationFailedException $e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->throwExpectationFailedExpection('toHaveKey', [$key]);
|
||||
}
|
||||
|
||||
return $this->original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dynamic method calls into the original expectation.
|
||||
*
|
||||
* @param array<int, mixed> $arguments
|
||||
*/
|
||||
public function __call(string $name, array $arguments): Expectation
|
||||
{
|
||||
try {
|
||||
/* @phpstan-ignore-next-line */
|
||||
$this->original->{$name}(...$arguments);
|
||||
} catch (ExpectationFailedException $e) {
|
||||
return $this->original;
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
$this->throwExpectationFailedExpection($name, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dynamic properties gets into the original expectation.
|
||||
*/
|
||||
public function __get(string $name): Expectation
|
||||
{
|
||||
try {
|
||||
/* @phpstan-ignore-next-line */
|
||||
$this->original->{$name};
|
||||
} catch (ExpectationFailedException $e) {
|
||||
return $this->original;
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
$this->throwExpectationFailedExpection($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expectation failed exception
|
||||
* with a nice readable message.
|
||||
*
|
||||
* @param array<int, mixed> $arguments
|
||||
*/
|
||||
private function throwExpectationFailedExpection(string $name, array $arguments = []): void
|
||||
{
|
||||
$exporter = new Exporter();
|
||||
|
||||
$toString = function ($argument) use ($exporter): string {
|
||||
return $exporter->shortenedExport($argument);
|
||||
};
|
||||
|
||||
throw new ExpectationFailedException(sprintf('Expecting %s not %s %s.', $toString($this->original->value), strtolower((string) preg_replace('/(?<!\ )[A-Z]/', ' $0', $name)), implode(' ', array_map(function ($argument) use ($toString): string { return $toString($argument); }, $arguments))));
|
||||
}
|
||||
}
|
||||
@ -9,12 +9,25 @@ use Pest\Factories\TestCaseFactory;
|
||||
use Pest\Support\Backtrace;
|
||||
use Pest\Support\NullClosure;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Framework\ExecutionOrderDependency;
|
||||
use SebastianBergmann\Exporter\Exporter;
|
||||
|
||||
/**
|
||||
* @method \Pest\Expectation expect(mixed $value)
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class TestCall
|
||||
{
|
||||
/**
|
||||
* Holds the test suite.
|
||||
*
|
||||
* @readonly
|
||||
*
|
||||
* @var TestSuite
|
||||
*/
|
||||
private $testSuite;
|
||||
|
||||
/**
|
||||
* Holds the test case factory.
|
||||
*
|
||||
@ -24,14 +37,23 @@ final class TestCall
|
||||
*/
|
||||
private $testCaseFactory;
|
||||
|
||||
/**
|
||||
* If test call is descriptionLess.
|
||||
*
|
||||
* @readonly
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $descriptionLess = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of a pending test call.
|
||||
*/
|
||||
public function __construct(TestSuite $testSuite, string $filename, string $description, Closure $closure = null)
|
||||
public function __construct(TestSuite $testSuite, string $filename, string $description = null, Closure $closure = null)
|
||||
{
|
||||
$this->testCaseFactory = new TestCaseFactory($filename, $description, $closure);
|
||||
|
||||
$testSuite->tests->set($this->testCaseFactory);
|
||||
$this->testSuite = $testSuite;
|
||||
$this->descriptionLess = $description === null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,13 +62,13 @@ final class TestCall
|
||||
public function throws(string $exceptionClass, string $exceptionMessage = null): TestCall
|
||||
{
|
||||
$this->testCaseFactory
|
||||
->proxies
|
||||
->add(Backtrace::file(), Backtrace::line(), 'expectException', [$exceptionClass]);
|
||||
->proxies
|
||||
->add(Backtrace::file(), Backtrace::line(), 'expectException', [$exceptionClass]);
|
||||
|
||||
if (is_string($exceptionMessage)) {
|
||||
$this->testCaseFactory
|
||||
->proxies
|
||||
->add(Backtrace::file(), Backtrace::line(), 'expectExceptionMessage', [$exceptionMessage]);
|
||||
->proxies
|
||||
->add(Backtrace::file(), Backtrace::line(), 'expectExceptionMessage', [$exceptionMessage]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
@ -65,6 +87,24 @@ final class TestCall
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test depends.
|
||||
*/
|
||||
public function depends(string ...$tests): TestCall
|
||||
{
|
||||
$className = $this->testCaseFactory->getClassName();
|
||||
|
||||
$tests = array_map(function (string $test) use ($className): ExecutionOrderDependency {
|
||||
return ExecutionOrderDependency::createFromDependsAnnotation($className, $test);
|
||||
}, $tests);
|
||||
|
||||
$this->testCaseFactory
|
||||
->factoryProxies
|
||||
->add(Backtrace::file(), Backtrace::line(), 'setDependencies', [$tests]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the test suite only this test case.
|
||||
*/
|
||||
@ -76,13 +116,13 @@ final class TestCall
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test groups(s).
|
||||
* Sets the test group(s).
|
||||
*/
|
||||
public function group(string ...$groups): TestCall
|
||||
{
|
||||
$this->testCaseFactory
|
||||
->factoryProxies
|
||||
->add(Backtrace::file(), Backtrace::line(), 'addGroups', [$groups]);
|
||||
->factoryProxies
|
||||
->add(Backtrace::file(), Backtrace::line(), 'addGroups', [$groups]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -110,8 +150,8 @@ final class TestCall
|
||||
|
||||
if ($condition() !== false) {
|
||||
$this->testCaseFactory
|
||||
->chains
|
||||
->add(Backtrace::file(), Backtrace::line(), 'markTestSkipped', [$message]);
|
||||
->chains
|
||||
->add(Backtrace::file(), Backtrace::line(), 'markTestSkipped', [$message]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
@ -125,9 +165,26 @@ final class TestCall
|
||||
public function __call(string $name, array $arguments): self
|
||||
{
|
||||
$this->testCaseFactory
|
||||
->chains
|
||||
->add(Backtrace::file(), Backtrace::line(), $name, $arguments);
|
||||
->chains
|
||||
->add(Backtrace::file(), Backtrace::line(), $name, $arguments);
|
||||
|
||||
if ($this->descriptionLess) {
|
||||
$exporter = new Exporter();
|
||||
if ($this->testCaseFactory->description !== null) {
|
||||
$this->testCaseFactory->description .= ' → ';
|
||||
}
|
||||
$this->testCaseFactory->description .= sprintf('%s %s', $name, $exporter->shortenedRecursiveExport($arguments));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the current test case factory
|
||||
* to the tests repository.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->testSuite->tests->set($this->testCaseFactory);
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +59,17 @@ final class UsesCall
|
||||
public function in(string ...$targets): void
|
||||
{
|
||||
$targets = array_map(function ($path): string {
|
||||
return $path[0] === DIRECTORY_SEPARATOR
|
||||
$startChar = DIRECTORY_SEPARATOR;
|
||||
|
||||
if ('\\' === DIRECTORY_SEPARATOR || preg_match('~\A[A-Z]:(?![^/\\\\])~i', $path) > 0) {
|
||||
$path = (string) preg_replace_callback('~^(?P<drive>[a-z]+:\\\)~i', function ($match): string {
|
||||
return strtolower($match['drive']);
|
||||
}, $path);
|
||||
|
||||
$startChar = strtolower((string) preg_replace('~^([a-z]+:\\\).*$~i', '$1', __DIR__));
|
||||
}
|
||||
|
||||
return 0 === strpos($path, $startChar)
|
||||
? $path
|
||||
: implode(DIRECTORY_SEPARATOR, [
|
||||
dirname($this->filename),
|
||||
@ -68,12 +78,12 @@ final class UsesCall
|
||||
}, $targets);
|
||||
|
||||
$this->targets = array_map(function ($target): string {
|
||||
$realTarget = realpath($target);
|
||||
if ($realTarget === false) {
|
||||
$isValid = is_dir($target) || file_exists($target);
|
||||
if (!$isValid) {
|
||||
throw new InvalidUsesPath($target);
|
||||
}
|
||||
|
||||
return $realTarget;
|
||||
return (string) realpath($target);
|
||||
}, $targets);
|
||||
}
|
||||
|
||||
|
||||
10
src/Pest.php
Normal file
10
src/Pest.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest;
|
||||
|
||||
function version(): string
|
||||
{
|
||||
return '0.3.6';
|
||||
}
|
||||
39
src/Plugins/Version.php
Normal file
39
src/Plugins/Version.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Plugins;
|
||||
|
||||
use Pest\Contracts\Plugins\HandlesArguments;
|
||||
use function Pest\version;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Version implements HandlesArguments
|
||||
{
|
||||
/**
|
||||
* @var OutputInterface
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the plugin.
|
||||
*/
|
||||
public function __construct(OutputInterface $output)
|
||||
{
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
public function handleArguments(array $arguments): array
|
||||
{
|
||||
if (in_array('--version', $arguments, true)) {
|
||||
$this->output->writeln(
|
||||
sprintf('Pest %s', version()),
|
||||
);
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
@ -41,6 +41,12 @@ final class AfterEachRepository
|
||||
|
||||
return ChainableClosure::from(function (): void {
|
||||
if (class_exists(Mockery::class)) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
if ($container = Mockery::getContainer()) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
$this->addToAssertionCount($container->mockery_getExpectationCount());
|
||||
}
|
||||
|
||||
Mockery::close();
|
||||
}
|
||||
}, $afterEach);
|
||||
|
||||
@ -4,12 +4,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace Pest\Repositories;
|
||||
|
||||
use Pest\Exceptions\ShouldNotHappen;
|
||||
use Pest\Exceptions\TestAlreadyExist;
|
||||
use Pest\Exceptions\TestCaseAlreadyInUse;
|
||||
use Pest\Exceptions\TestCaseClassOrTraitNotFound;
|
||||
use Pest\Factories\TestCaseFactory;
|
||||
use Pest\Support\Str;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -51,10 +53,9 @@ final class TestRepository
|
||||
if ((!is_dir($path) && $filename === $path) || (is_dir($path) && $startsWith($filename, $path))) {
|
||||
foreach ($classOrTraits as $class) {
|
||||
if (class_exists($class)) {
|
||||
if ($testCase->class !== \PHPUnit\Framework\TestCase::class) {
|
||||
if ($testCase->class !== TestCase::class) {
|
||||
throw new TestCaseAlreadyInUse($testCase->class, $class, $filename);
|
||||
}
|
||||
|
||||
$testCase->class = $class;
|
||||
} elseif (trait_exists($class)) {
|
||||
$testCase->traits[] = $class;
|
||||
@ -62,9 +63,9 @@ final class TestRepository
|
||||
}
|
||||
|
||||
$testCase
|
||||
->factoryProxies
|
||||
// Consider set the real line here.
|
||||
->add($filename, 0, 'addGroups', [$groups]);
|
||||
->factoryProxies
|
||||
// Consider set the real line here.
|
||||
->add($filename, 0, 'addGroups', [$groups]);
|
||||
}
|
||||
};
|
||||
|
||||
@ -104,7 +105,14 @@ final class TestRepository
|
||||
}
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$this->uses[$path] = [$classOrTraits, $groups];
|
||||
if (array_key_exists($path, $this->uses)) {
|
||||
$this->uses[$path] = [
|
||||
array_merge($this->uses[$path][0], $classOrTraits),
|
||||
array_merge($this->uses[$path][1], $groups),
|
||||
];
|
||||
} else {
|
||||
$this->uses[$path] = [$classOrTraits, $groups];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,6 +121,10 @@ final class TestRepository
|
||||
*/
|
||||
public function set(TestCaseFactory $test): void
|
||||
{
|
||||
if ($test->description === null) {
|
||||
throw ShouldNotHappen::fromMessage('Trying to create a test without description.');
|
||||
}
|
||||
|
||||
if (array_key_exists(sprintf('%s@%s', $test->filename, $test->description), $this->state)) {
|
||||
throw new TestAlreadyExist($test->filename, $test->description);
|
||||
}
|
||||
|
||||
@ -4,17 +4,48 @@ declare(strict_types=1);
|
||||
|
||||
namespace Pest\Support;
|
||||
|
||||
use Pest\Exceptions\ShouldNotHappen;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Backtrace
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const FILE = 'file';
|
||||
|
||||
private const BACKTRACE_OPTIONS = DEBUG_BACKTRACE_IGNORE_ARGS;
|
||||
|
||||
/**
|
||||
* Returns the current test file.
|
||||
*/
|
||||
public static function testFile(): string
|
||||
{
|
||||
$current = null;
|
||||
|
||||
foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) {
|
||||
if (Str::endsWith($trace[self::FILE], (string) realpath('vendor/phpunit/phpunit/src/Util/FileLoader.php'))) {
|
||||
break;
|
||||
}
|
||||
|
||||
$current = $trace;
|
||||
}
|
||||
|
||||
if ($current === null) {
|
||||
throw ShouldNotHappen::fromMessage('Test file not found.');
|
||||
}
|
||||
|
||||
return $current[self::FILE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filename that called the current function/method.
|
||||
*/
|
||||
public static function file(): string
|
||||
{
|
||||
return debug_backtrace()[1]['file'];
|
||||
return debug_backtrace(self::BACKTRACE_OPTIONS)[1][self::FILE];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -22,7 +53,7 @@ final class Backtrace
|
||||
*/
|
||||
public static function dirname(): string
|
||||
{
|
||||
return dirname(debug_backtrace()[1]['file']);
|
||||
return dirname(debug_backtrace(self::BACKTRACE_OPTIONS)[1][self::FILE]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -30,6 +61,6 @@ final class Backtrace
|
||||
*/
|
||||
public static function line(): int
|
||||
{
|
||||
return debug_backtrace()[1]['line'];
|
||||
return debug_backtrace(self::BACKTRACE_OPTIONS)[1]['line'];
|
||||
}
|
||||
}
|
||||
|
||||
102
src/Support/Container.php
Normal file
102
src/Support/Container.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Support;
|
||||
|
||||
use Pest\Exceptions\ShouldNotHappen;
|
||||
use ReflectionClass;
|
||||
use ReflectionParameter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Container
|
||||
{
|
||||
/**
|
||||
* @var self
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private $instances = [];
|
||||
|
||||
/**
|
||||
* Gets a new or already existing container.
|
||||
*/
|
||||
public static function getInstance(): self
|
||||
{
|
||||
if (static::$instance === null) {
|
||||
static::$instance = new static();
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a dependency from the container.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get(string $id)
|
||||
{
|
||||
if (array_key_exists($id, $this->instances)) {
|
||||
return $this->instances[$id];
|
||||
}
|
||||
|
||||
$this->instances[$id] = $this->build($id);
|
||||
|
||||
return $this->instances[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given instance to the container.
|
||||
*
|
||||
* @param mixed $instance
|
||||
*/
|
||||
public function add(string $id, $instance): void
|
||||
{
|
||||
$this->instances[$id] = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to build the given instance.
|
||||
*/
|
||||
private function build(string $id): object
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
$reflectionClass = new ReflectionClass($id);
|
||||
|
||||
if ($reflectionClass->isInstantiable()) {
|
||||
$constructor = $reflectionClass->getConstructor();
|
||||
|
||||
if ($constructor !== null) {
|
||||
$params = array_map(
|
||||
function (ReflectionParameter $param) use ($id) {
|
||||
$candidate = Reflection::getParameterClassName($param);
|
||||
|
||||
if ($candidate === null) {
|
||||
$type = $param->getType();
|
||||
if ($type !== null && $type->isBuiltin()) {
|
||||
$candidate = $param->getName();
|
||||
} else {
|
||||
throw ShouldNotHappen::fromMessage(sprintf('The type of `$%s` in `%s` cannot be determined.', $id, $param->getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->get($candidate);
|
||||
},
|
||||
$constructor->getParameters()
|
||||
);
|
||||
|
||||
return $reflectionClass->newInstanceArgs($params);
|
||||
}
|
||||
|
||||
return $reflectionClass->newInstance();
|
||||
}
|
||||
|
||||
throw ShouldNotHappen::fromMessage(sprintf('A dependency with the name `%s` cannot be resolved.', $id));
|
||||
}
|
||||
}
|
||||
@ -17,11 +17,15 @@ final class ExceptionTrace
|
||||
/**
|
||||
* Ensures the given closure reports
|
||||
* the good execution context.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function ensure(Closure $closure): void
|
||||
public static function ensure(Closure $closure)
|
||||
{
|
||||
try {
|
||||
$closure();
|
||||
return $closure();
|
||||
} catch (Throwable $throwable) {
|
||||
if (Str::startsWith($message = $throwable->getMessage(), self::UNDEFINED_METHOD)) {
|
||||
$message = str_replace(self::UNDEFINED_METHOD, 'Call to undefined method ', $message);
|
||||
|
||||
@ -76,13 +76,24 @@ final class HigherOrderMessage
|
||||
Reflection::setPropertyValue($throwable, 'file', $this->filename);
|
||||
Reflection::setPropertyValue($throwable, 'line', $this->line);
|
||||
|
||||
if ($throwable->getMessage() === sprintf(self::UNDEFINED_METHOD, $this->methodName)) {
|
||||
/** @var \ReflectionClass $reflection */
|
||||
$reflection = (new ReflectionClass($target))->getParentClass();
|
||||
if ($throwable->getMessage() === self::getUndefinedMethodMessage($target, $this->methodName)) {
|
||||
/** @var ReflectionClass $reflection */
|
||||
$reflection = new ReflectionClass($target);
|
||||
/* @phpstan-ignore-next-line */
|
||||
$reflection = $reflection->getParentClass() ?: $reflection;
|
||||
Reflection::setPropertyValue($throwable, 'message', sprintf('Call to undefined method %s::%s()', $reflection->getName(), $this->methodName));
|
||||
}
|
||||
|
||||
throw $throwable;
|
||||
}
|
||||
}
|
||||
|
||||
private static function getUndefinedMethodMessage(object $target, string $methodName): string
|
||||
{
|
||||
if (\PHP_MAJOR_VERSION >= 8) {
|
||||
return sprintf(sprintf(self::UNDEFINED_METHOD, sprintf('%s::%s()', get_class($target), $methodName)));
|
||||
}
|
||||
|
||||
return sprintf(self::UNDEFINED_METHOD, $methodName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ final class HigherOrderMessageCollection
|
||||
public function chain(object $target): void
|
||||
{
|
||||
foreach ($this->messages as $message) {
|
||||
$target = $message->call($target);
|
||||
$target = $message->call($target) ?? $target;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Pest\Support;
|
||||
|
||||
use ReflectionClass;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -51,12 +52,12 @@ final class HigherOrderTapProxy
|
||||
try {
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->target->{$property};
|
||||
} catch (\Throwable $throwable) {
|
||||
} catch (Throwable $throwable) {
|
||||
Reflection::setPropertyValue($throwable, 'file', Backtrace::file());
|
||||
Reflection::setPropertyValue($throwable, 'line', Backtrace::line());
|
||||
|
||||
if (Str::startsWith($message = $throwable->getMessage(), self::UNDEFINED_PROPERTY)) {
|
||||
/** @var \ReflectionClass $reflection */
|
||||
/** @var ReflectionClass $reflection */
|
||||
$reflection = (new ReflectionClass($this->target))->getParentClass();
|
||||
Reflection::setPropertyValue($throwable, 'message', sprintf('Undefined property %s::$%s', $reflection->getName(), $property));
|
||||
}
|
||||
|
||||
@ -6,9 +6,12 @@ namespace Pest\Support;
|
||||
|
||||
use Closure;
|
||||
use Pest\Exceptions\ShouldNotHappen;
|
||||
use Pest\TestSuite;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionFunction;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionParameter;
|
||||
use ReflectionProperty;
|
||||
|
||||
/**
|
||||
@ -27,11 +30,25 @@ final class Reflection
|
||||
{
|
||||
$reflectionClass = new ReflectionClass($object);
|
||||
|
||||
$reflectionMethod = $reflectionClass->getMethod($method);
|
||||
try {
|
||||
$reflectionMethod = $reflectionClass->getMethod($method);
|
||||
|
||||
$reflectionMethod->setAccessible(true);
|
||||
$reflectionMethod->setAccessible(true);
|
||||
|
||||
return $reflectionMethod->invoke($object, ...$args);
|
||||
return $reflectionMethod->invoke($object, ...$args);
|
||||
} catch (ReflectionException $exception) {
|
||||
if (method_exists($object, '__call')) {
|
||||
return $object->__call($method, $args);
|
||||
}
|
||||
|
||||
if (is_callable($method)) {
|
||||
return Closure::fromCallable($method)->bindTo(
|
||||
TestSuite::getInstance()->test
|
||||
)(...$args);
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,4 +126,32 @@ final class Reflection
|
||||
$reflectionProperty->setAccessible(true);
|
||||
$reflectionProperty->setValue($object, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class name of the given parameter's type, if possible.
|
||||
*
|
||||
* @see https://github.com/laravel/framework/blob/v6.18.25/src/Illuminate/Support/Reflector.php
|
||||
*/
|
||||
public static function getParameterClassName(ReflectionParameter $parameter): ?string
|
||||
{
|
||||
$type = $parameter->getType();
|
||||
|
||||
if (!$type instanceof ReflectionNamedType || $type->isBuiltin()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$name = $type->getName();
|
||||
|
||||
if (($class = $parameter->getDeclaringClass()) instanceof ReflectionClass) {
|
||||
if ($name === 'self') {
|
||||
return $class->getName();
|
||||
}
|
||||
|
||||
if ($name === 'parent' && ($parent = $class->getParentClass()) instanceof ReflectionClass) {
|
||||
return $parent->getName();
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
235
src/TeamCity.php
Normal file
235
src/TeamCity.php
Normal file
@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest;
|
||||
|
||||
use function getmypid;
|
||||
use Pest\Concerns\TestCase;
|
||||
use PHPUnit\Framework\AssertionFailedError;
|
||||
use PHPUnit\Framework\Test;
|
||||
use PHPUnit\Framework\TestResult;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\Framework\Warning;
|
||||
use PHPUnit\TextUI\DefaultResultPrinter;
|
||||
use function round;
|
||||
use function str_replace;
|
||||
use Throwable;
|
||||
|
||||
final class TeamCity extends DefaultResultPrinter
|
||||
{
|
||||
private const PROTOCOL = 'pest_qn://';
|
||||
private const NAME = 'name';
|
||||
private const LOCATION_HINT = 'locationHint';
|
||||
private const DURATION = 'duration';
|
||||
private const TEST_SUITE_STARTED = 'testSuiteStarted';
|
||||
private const TEST_SUITE_FINISHED = 'testSuiteFinished';
|
||||
private const TEST_FAILED = 'testFailed';
|
||||
|
||||
/** @var int */
|
||||
private $flowId;
|
||||
|
||||
/** @var bool */
|
||||
private $isSummaryTestCountPrinted = false;
|
||||
|
||||
/** @var \PHPUnit\Util\Log\TeamCity */
|
||||
private $phpunitTeamCity;
|
||||
|
||||
public function __construct(bool $verbose, string $colors)
|
||||
{
|
||||
parent::__construct(null, $verbose, $colors, false, 80, false);
|
||||
$this->phpunitTeamCity = new \PHPUnit\Util\Log\TeamCity(
|
||||
null,
|
||||
$verbose,
|
||||
$colors,
|
||||
false,
|
||||
80,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public function printResult(TestResult $result): void
|
||||
{
|
||||
$this->printHeader($result);
|
||||
$this->printFooter($result);
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
public function startTestSuite(TestSuite $suite): void
|
||||
{
|
||||
$this->flowId = getmypid();
|
||||
|
||||
if (!$this->isSummaryTestCountPrinted) {
|
||||
$this->printEvent(
|
||||
'testCount',
|
||||
['count' => $suite->count()]
|
||||
);
|
||||
$this->isSummaryTestCountPrinted = true;
|
||||
}
|
||||
|
||||
$suiteName = $suite->getName();
|
||||
|
||||
if (file_exists($suiteName) || !method_exists($suiteName, '__getFileName')) {
|
||||
$this->printEvent(
|
||||
self::TEST_SUITE_STARTED, [
|
||||
self::NAME => $suiteName,
|
||||
self::LOCATION_HINT => self::PROTOCOL . $suiteName,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$fileName = $suiteName::__getFileName();
|
||||
|
||||
$this->printEvent(
|
||||
self::TEST_SUITE_STARTED, [
|
||||
self::NAME => substr($suiteName, 2),
|
||||
self::LOCATION_HINT => self::PROTOCOL . $fileName,
|
||||
]);
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
public function endTestSuite(TestSuite $suite): void
|
||||
{
|
||||
$suiteName = $suite->getName();
|
||||
|
||||
if (file_exists($suiteName) || !method_exists($suiteName, '__getFileName')) {
|
||||
$this->printEvent(
|
||||
self::TEST_SUITE_FINISHED, [
|
||||
self::NAME => $suiteName,
|
||||
self::LOCATION_HINT => self::PROTOCOL . $suiteName,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->printEvent(
|
||||
self::TEST_SUITE_FINISHED, [
|
||||
self::NAME => substr($suiteName, 2),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Test|TestCase $test
|
||||
*/
|
||||
public function startTest(Test $test): void
|
||||
{
|
||||
if (!TeamCity::isPestTest($test)) {
|
||||
$this->phpunitTeamCity->startTest($test);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->printEvent('testStarted', [
|
||||
self::NAME => $test->getName(),
|
||||
/* @phpstan-ignore-next-line */
|
||||
self::LOCATION_HINT => self::PROTOCOL . $test->toString(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Test|TestCase $test
|
||||
*/
|
||||
public function endTest(Test $test, float $time): void
|
||||
{
|
||||
if (!TeamCity::isPestTest($test)) {
|
||||
$this->phpunitTeamCity->endTest($test, $time);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->printEvent('testFinished', [
|
||||
self::NAME => $test->getName(),
|
||||
self::DURATION => self::toMilliseconds($time),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Test|TestCase $test
|
||||
*/
|
||||
public function addError(Test $test, Throwable $t, float $time): void
|
||||
{
|
||||
if (!TeamCity::isPestTest($test)) {
|
||||
$this->phpunitTeamCity->addError($test, $t, $time);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->printEvent(
|
||||
self::TEST_FAILED, [
|
||||
self::NAME => $test->getName(),
|
||||
'message' => $t->getMessage(),
|
||||
'details' => $t->getTraceAsString(),
|
||||
self::DURATION => self::toMilliseconds($time),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-ignore-next-line
|
||||
*
|
||||
* @param Test|TestCase $test
|
||||
*/
|
||||
public function addWarning(Test $test, Warning $e, float $time): void
|
||||
{
|
||||
if (!TeamCity::isPestTest($test)) {
|
||||
$this->phpunitTeamCity->addWarning($test, $e, $time);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->printEvent(
|
||||
self::TEST_FAILED, [
|
||||
self::NAME => $test->getName(),
|
||||
'message' => $e->getMessage(),
|
||||
'details' => $e->getTraceAsString(),
|
||||
self::DURATION => self::toMilliseconds($time),
|
||||
]);
|
||||
}
|
||||
|
||||
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
|
||||
{
|
||||
$this->phpunitTeamCity->addFailure($test, $e, $time);
|
||||
}
|
||||
|
||||
protected function writeProgress(string $progress): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string|int> $params
|
||||
*/
|
||||
private function printEvent(string $eventName, array $params = []): void
|
||||
{
|
||||
$this->write("\n##teamcity[{$eventName}");
|
||||
|
||||
if ($this->flowId !== 0) {
|
||||
$params['flowId'] = $this->flowId;
|
||||
}
|
||||
|
||||
foreach ($params as $key => $value) {
|
||||
$escapedValue = self::escapeValue((string) $value);
|
||||
$this->write(" {$key}='{$escapedValue}'");
|
||||
}
|
||||
|
||||
$this->write("]\n");
|
||||
}
|
||||
|
||||
private static function escapeValue(string $text): string
|
||||
{
|
||||
return str_replace(
|
||||
['|', "'", "\n", "\r", ']', '['],
|
||||
['||', "|'", '|n', '|r', '|]', '|['],
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
private static function toMilliseconds(float $time): int
|
||||
{
|
||||
return (int) round($time * 1000);
|
||||
}
|
||||
|
||||
private static function isPestTest(Test $test): bool
|
||||
{
|
||||
return in_array(TestCase::class, class_uses($test), true);
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@ use Pest\Repositories\AfterEachRepository;
|
||||
use Pest\Repositories\BeforeAllRepository;
|
||||
use Pest\Repositories\BeforeEachRepository;
|
||||
use Pest\Repositories\TestRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -19,7 +20,7 @@ final class TestSuite
|
||||
/**
|
||||
* Holds the current test case.
|
||||
*
|
||||
* @var \PHPUnit\Framework\TestCase|null
|
||||
* @var TestCase|null
|
||||
*/
|
||||
public $test;
|
||||
|
||||
@ -30,20 +31,6 @@ final class TestSuite
|
||||
*/
|
||||
public $tests;
|
||||
|
||||
/**
|
||||
* Whether should show the coverage or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $coverage = false;
|
||||
|
||||
/**
|
||||
* The minimum coverage.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
public $coverageMin = 0.0;
|
||||
|
||||
/**
|
||||
* Holds the before each repository.
|
||||
*
|
||||
@ -97,7 +84,7 @@ final class TestSuite
|
||||
$this->afterEach = new AfterEachRepository();
|
||||
$this->afterAll = new AfterAllRepository();
|
||||
|
||||
$this->rootPath = $rootPath;
|
||||
$this->rootPath = (string) realpath($rootPath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Pest\Datasets;
|
||||
use Pest\Expectation;
|
||||
use Pest\PendingObjects\AfterEachCall;
|
||||
use Pest\PendingObjects\BeforeEachCall;
|
||||
use Pest\PendingObjects\TestCall;
|
||||
@ -13,7 +14,7 @@ use Pest\TestSuite;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Runs the given closure after all tests in the current file.
|
||||
* Runs the given closure before all tests in the current file.
|
||||
*/
|
||||
function beforeAll(Closure $closure): void
|
||||
{
|
||||
@ -43,8 +44,8 @@ function dataset(string $name, $dataset): void
|
||||
}
|
||||
|
||||
/**
|
||||
* The uses function adds the binds the
|
||||
* given arguments to test closures.
|
||||
* The uses function binds the given
|
||||
* arguments to test closures.
|
||||
*/
|
||||
function uses(string ...$classAndTraits): UsesCall
|
||||
{
|
||||
@ -66,7 +67,7 @@ function test(string $description = null, Closure $closure = null)
|
||||
return new HigherOrderTapProxy(TestSuite::getInstance()->test);
|
||||
}
|
||||
|
||||
$filename = Backtrace::file();
|
||||
$filename = Backtrace::testFile();
|
||||
|
||||
return new TestCall(TestSuite::getInstance(), $filename, $description, $closure);
|
||||
}
|
||||
@ -80,9 +81,9 @@ function test(string $description = null, Closure $closure = null)
|
||||
*/
|
||||
function it(string $description, Closure $closure = null): TestCall
|
||||
{
|
||||
$filename = Backtrace::file();
|
||||
$description = sprintf('it %s', $description);
|
||||
|
||||
return new TestCall(TestSuite::getInstance(), $filename, sprintf('it %s', $description), $closure);
|
||||
return test($description, $closure);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,3 +105,15 @@ function afterAll(Closure $closure = null): void
|
||||
{
|
||||
TestSuite::getInstance()->afterAll->set($closure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expectation.
|
||||
*
|
||||
* @param mixed $value the Value
|
||||
*
|
||||
* @return Expectation
|
||||
*/
|
||||
function expect($value)
|
||||
{
|
||||
return test()->expect($value);
|
||||
}
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Set the currently logged in user for the application.
|
||||
*/
|
||||
function actingAs(Authenticatable $user, string $driver = null): TestCase
|
||||
{
|
||||
return test()->actingAs($user, $driver);
|
||||
}
|
||||
11
stubs/Laravel/Helpers.php
Normal file
11
stubs/Laravel/Helpers.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
/**
|
||||
* A basic assert example.
|
||||
*/
|
||||
function assertExample(): void
|
||||
{
|
||||
test()->assertTrue(true);
|
||||
}
|
||||
@ -12,10 +12,10 @@
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./app</directory>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</include>
|
||||
</coverage>
|
||||
</phpunit>
|
||||
11
stubs/Lumen/Helpers.php
Normal file
11
stubs/Lumen/Helpers.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
/**
|
||||
* A basic assert example.
|
||||
*/
|
||||
function assertExample(): void
|
||||
{
|
||||
test()->assertTrue(true);
|
||||
}
|
||||
3
stubs/Lumen/Pest.php
Normal file
3
stubs/Lumen/Pest.php
Normal file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
uses(TestCase::class)->in(__DIR__);
|
||||
17
stubs/Lumen/phpunit.xml
Normal file
17
stubs/Lumen/phpunit.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Application Test Suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./app</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
</phpunit>
|
||||
7
tests/.snapshots/allows-to-run-a-directory.txt
Normal file
7
tests/.snapshots/allows-to-run-a-directory.txt
Normal file
@ -0,0 +1,7 @@
|
||||
PASS Tests\Fixtures\DirectoryWithTests\ExampleTest
|
||||
✓ it example 1
|
||||
|
||||
PASS Tests\Fixtures\ExampleTest
|
||||
✓ it example 2
|
||||
|
||||
Tests: 2 passed
|
||||
4
tests/.snapshots/allows-to-run-a-single-test.txt
Normal file
4
tests/.snapshots/allows-to-run-a-single-test.txt
Normal file
@ -0,0 +1,4 @@
|
||||
PASS Tests\Fixtures\DirectoryWithTests\ExampleTest
|
||||
✓ it example 1
|
||||
|
||||
Tests: 1 passed
|
||||
60
tests/.snapshots/coverage.txt
Normal file
60
tests/.snapshots/coverage.txt
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
PASS Tests\Playground
|
||||
✓ basic
|
||||
|
||||
Tests: 1 passed
|
||||
Time: 0.20s
|
||||
Cov: 6.49%
|
||||
|
||||
Actions/AddsDefaults ........................................... 0.0 %
|
||||
Actions/AddsTests .............................................. 0.0 %
|
||||
Actions/LoadStructure .......................................... 0.0 %
|
||||
Actions/ValidatesConfiguration ................................. 0.0 %
|
||||
Actions/ValidatesEnvironment ................................... 0.0 %
|
||||
Concerns/TestCase 40..54, 71..88, 123..126, 147 ............... 44.4 %
|
||||
Console/Command ................................................ 0.0 %
|
||||
Contracts/HasPrintableTestCaseName ............................. 0.0 %
|
||||
Contracts/Plugins/AddsOutput ................................ 100.0 %
|
||||
Contracts/Plugins/HandlesArguments .......................... 100.0 %
|
||||
Datasets ....................................................... 0.0 %
|
||||
Exceptions/AfterAllAlreadyExist ................................ 0.0 %
|
||||
Exceptions/AfterEachAlreadyExist ............................... 0.0 %
|
||||
Exceptions/AttributeNotSupportedYet ............................ 0.0 %
|
||||
Exceptions/BeforeEachAlreadyExist .............................. 0.0 %
|
||||
Exceptions/DatasetAlreadyExist ................................. 0.0 %
|
||||
Exceptions/DatasetDoesNotExist ................................. 0.0 %
|
||||
Exceptions/FileOrFolderNotFound ................................ 0.0 %
|
||||
Exceptions/InvalidConsoleArgument .............................. 0.0 %
|
||||
Exceptions/InvalidPestCommand .................................. 0.0 %
|
||||
Exceptions/InvalidUsesPath ..................................... 0.0 %
|
||||
Exceptions/ShouldNotHappen ..................................... 0.0 %
|
||||
Exceptions/TestAlreadyExist .................................... 0.0 %
|
||||
Exceptions/TestCaseAlreadyInUse ................................ 0.0 %
|
||||
Exceptions/TestCaseClassOrTraitNotFound ........................ 0.0 %
|
||||
Factories/TestCaseFactory 111..133, 141..204 ................... 8.2 %
|
||||
Laravel/Commands/PestDatasetCommand ............................ 0.0 %
|
||||
Laravel/Commands/PestInstallCommand ............................ 0.0 %
|
||||
Laravel/Commands/PestTestCommand ............................... 0.0 %
|
||||
Laravel/PestServiceProvider .................................... 0.0 %
|
||||
PendingObjects/AfterEachCall ................................... 0.0 %
|
||||
PendingObjects/BeforeEachCall .................................. 0.0 %
|
||||
PendingObjects/TestCall ........................................ 0.0 %
|
||||
PendingObjects/UsesCall ........................................ 0.0 %
|
||||
Plugin ......................................................... 0.0 %
|
||||
Repositories/AfterAllRepository ................................ 0.0 %
|
||||
Repositories/AfterEachRepository 28..33 ....................... 60.0 %
|
||||
Repositories/BeforeAllRepository ............................... 0.0 %
|
||||
Repositories/BeforeEachRepository 26..31 ...................... 20.0 %
|
||||
Repositories/TestRepository .................................... 0.0 %
|
||||
Support/Backtrace .............................................. 0.0 %
|
||||
Support/ChainableClosure .................................... 100.0 %
|
||||
Support/Container .............................................. 0.0 %
|
||||
Support/ExceptionTrace 25..32 ................................. 28.6 %
|
||||
Support/HigherOrderMessage ..................................... 0.0 %
|
||||
Support/HigherOrderMessageCollection 24..25, 33, 43 ........... 50.0 %
|
||||
Support/HigherOrderTapProxy .................................... 0.0 %
|
||||
Support/NullClosure ......................................... 100.0 %
|
||||
Support/Reflection ............................................. 0.0 %
|
||||
Support/Str .................................................... 0.0 %
|
||||
TestSuite 80..87, 95..101, 105 ................................ 20.0 %
|
||||
globals ........................................................ 0.0 %
|
||||
5
tests/.snapshots/disable-decorating-printer.txt
Normal file
5
tests/.snapshots/disable-decorating-printer.txt
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
PASS Tests\Fixtures\DirectoryWithTests\ExampleTest
|
||||
✓ [2mit example 1[22m
|
||||
|
||||
Tests: 1 passed
|
||||
5
tests/.snapshots/has-ascii-chars.txt
Normal file
5
tests/.snapshots/has-ascii-chars.txt
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
[30;42;1m PASS [39;49;22m[39m Tests\Fixtures\DirectoryWithTests\ExampleTest[39m
|
||||
[32;1m✓[39;22m[39m [2mit example 1[22m[39m
|
||||
|
||||
[37;1mTests: [39;22m[32;1m1 passed[39;22m
|
||||
@ -1,7 +1,222 @@
|
||||
|
||||
PASS Tests\CustomTestCase\PhpunitTest
|
||||
PASS Tests\CustomTestCase\ExecutedTest
|
||||
✓ that gets executed
|
||||
|
||||
PASS Tests\Expect\not
|
||||
✓ not property calls
|
||||
|
||||
PASS Tests\Expect\toBe
|
||||
✓ expect true → toBeTrue → and false → toBeFalse
|
||||
✓ strict comparisons
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeArray
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeBool
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeCallable
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeDirectory
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeEmpty
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeFalse
|
||||
✓ strict comparisons
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeFile
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeFloat
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeGreatherThan
|
||||
✓ passes
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeGreatherThanOrEqual
|
||||
✓ passes
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeInfinite
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeInstanceOf
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeInt
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeIterable
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeLessThan
|
||||
✓ passes
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeLessThanOrEqual
|
||||
✓ passes
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeNAN
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeNull
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeNumeric
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeObject
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeReadableDirectory
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeReadableFile
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeResource
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeScalar
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeString
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeTrue
|
||||
✓ strict comparisons
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeWritableDirectory
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toBeWritableFile
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toContain
|
||||
✓ passes strings
|
||||
✓ passes arrays
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toEndWith
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toEqual
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toEqualCanonicalizing
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toEqualWithDelta
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toHaveCount
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toHaveKey
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toHaveKeys
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toHaveProperty
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toMatch
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toMatchConstraint
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toMatchObject
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Expect\toStartWith
|
||||
✓ pass
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\AfterAll
|
||||
✓ deletes file after all
|
||||
|
||||
@ -24,7 +239,7 @@
|
||||
✓ it sets arrays
|
||||
✓ it gets bound to test case object with ('a')
|
||||
✓ it gets bound to test case object with ('b')
|
||||
✓ it truncates the description with (' fooo fooo fooo fooo fooo fooo fooo f...oo fooo')
|
||||
✓ it truncates the description with ('FoooFoooFoooFoooFoooFoooFoooF...ooFooo')
|
||||
✓ lazy datasets with (1)
|
||||
✓ lazy datasets with (2)
|
||||
✓ lazy datasets did the job right
|
||||
@ -40,10 +255,17 @@
|
||||
✓ eager wrapped registered datasets with (1)
|
||||
✓ eager wrapped registered datasets with (2)
|
||||
✓ eager registered wrapped datasets did the job right
|
||||
✓ lazy named datasets with ( bar object (...))
|
||||
✓ lazy named datasets with (Bar Object (...))
|
||||
✓ it creates unique test case names with ('Name 1', Pest\Plugin Object (), true) #1
|
||||
✓ it creates unique test case names with ('Name 1', Pest\Plugin Object (), true) #2
|
||||
✓ it creates unique test case names with ('Name 1', Pest\Plugin Object (), false)
|
||||
✓ it creates unique test case names with ('Name 2', Pest\Plugin Object (), false)
|
||||
✓ it creates unique test case names with ('Name 2', Pest\Plugin Object (), true)
|
||||
✓ it creates unique test case names with ('Name 1', Pest\Plugin Object (), true) #3
|
||||
✓ it creates unique test case names - count
|
||||
|
||||
PASS Tests\Features\Exceptions
|
||||
✓ it gives access the the underlying expect exception
|
||||
✓ it gives access the the underlying expectException
|
||||
✓ it catch exceptions
|
||||
✓ it catch exceptions and messages
|
||||
|
||||
@ -52,41 +274,52 @@
|
||||
✓ it throws error if property do not exist
|
||||
✓ it allows to call underlying protected/private methods
|
||||
✓ it throws error if method do not exist
|
||||
✓ it can forward unexpected calls to any global function
|
||||
|
||||
PASS Tests\Features\HigherOrderMessages
|
||||
PASS Tests\Features\HigherOrderTests
|
||||
✓ it proxies calls to object
|
||||
✓ it is capable doing multiple assertions
|
||||
|
||||
PASS Tests\Features\It
|
||||
✓ it is a test
|
||||
✓ it is a higher order message test
|
||||
|
||||
PASS Tests\Features\Macro
|
||||
✓ it can call chained macro method
|
||||
✓ it will throw exception from call if no macro exists
|
||||
|
||||
PASS Tests\Features\Mocks
|
||||
✓ it has bar
|
||||
|
||||
PASS Tests\Features\PendingHigherOrderTests
|
||||
✓ get 'foo' → get 'bar' → expect true → toBeTrue
|
||||
✓ get 'foo' → expect true → toBeTrue
|
||||
|
||||
WARN Tests\Features\Skip
|
||||
✓ it do not skips
|
||||
s it skips with truthy
|
||||
s it skips with truthy condition by default
|
||||
s it skips with message → skipped because bar
|
||||
s it skips with truthy closure condition
|
||||
- it skips with truthy
|
||||
- it skips with truthy condition by default
|
||||
- it skips with message → skipped because bar
|
||||
- it skips with truthy closure condition
|
||||
✓ it do not skips with falsy closure condition
|
||||
s it skips with condition and messsage → skipped because foo
|
||||
- it skips with condition and message → skipped because foo
|
||||
- it skips when skip after assertion
|
||||
|
||||
PASS Tests\Features\Test
|
||||
✓ a test
|
||||
✓ higher order message test
|
||||
|
||||
PASS Tests\Fixtures\DirectoryWithTests\ExampleTest
|
||||
✓ it example
|
||||
✓ it example 1
|
||||
|
||||
PASS Tests\Fixtures\ExampleTest
|
||||
✓ it example
|
||||
✓ it example 2
|
||||
|
||||
PASS Tests\PHPUnit\CustomTestCase\UsesPerDirectory
|
||||
✓ closure was bound to custom test case
|
||||
✓ closure was bound to CustomTestCase
|
||||
|
||||
PASS Tests\PHPUnit\CustomTestCaseInSubFolders\SubFolder\SubFolder\UsesPerSubDirectory
|
||||
✓ closure was bound to custom test case
|
||||
✓ closure was bound to CustomTestCase
|
||||
|
||||
PASS Tests\PHPUnit\CustomTestCaseInSubFolders\SubFolder2\UsesPerFile
|
||||
✓ custom traits can be used
|
||||
@ -97,10 +330,7 @@
|
||||
|
||||
PASS Tests\Plugins\Traits
|
||||
✓ it allows global uses
|
||||
|
||||
PASS Tests\Unit\Actions\AddsCoverage
|
||||
✓ it adds coverage if --coverage exist
|
||||
✓ it adds coverage if --min exist
|
||||
✓ it allows multiple global uses registered in the same path
|
||||
|
||||
PASS Tests\Unit\Actions\AddsDefaults
|
||||
✓ it sets defaults
|
||||
@ -115,12 +345,22 @@
|
||||
✓ it throws exception when `process isolation` is true
|
||||
✓ it do not throws exception when `process isolation` is false
|
||||
|
||||
PASS Tests\Unit\Console\Coverage
|
||||
✓ it generates coverage based on file input
|
||||
PASS Tests\Unit\Plugins\Version
|
||||
✓ it outputs the version when --version is used
|
||||
✓ it do not outputs version when --version is not used
|
||||
|
||||
PASS Tests\Unit\Support\Backtrace
|
||||
✓ it gets file name from called file
|
||||
|
||||
PASS Tests\Unit\Support\Container
|
||||
✓ it exists
|
||||
✓ it gets an instance
|
||||
✓ autowire
|
||||
✓ it creates an instance and resolves parameters
|
||||
✓ it creates an instance and resolves also sub parameters
|
||||
✓ it can resolve builtin value types
|
||||
✓ it cannot resolve a parameter without type
|
||||
|
||||
PASS Tests\Unit\Support\Reflection
|
||||
✓ it gets file name from closure
|
||||
✓ it gets property values
|
||||
@ -131,9 +371,19 @@
|
||||
PASS Tests\Visual\SingleTestOrDirectory
|
||||
✓ allows to run a single test
|
||||
✓ allows to run a directory
|
||||
✓ it has ascii chars
|
||||
✓ it disable decorating printer when colors is set to never
|
||||
|
||||
WARN Tests\Visual\Success
|
||||
s visual snapshot of test suite on success
|
||||
- visual snapshot of test suite on success
|
||||
|
||||
Tests: 6 skipped, 70 passed
|
||||
Time: 2.68s
|
||||
PASS Tests\Features\Depends
|
||||
✓ first
|
||||
✓ second
|
||||
✓ depends
|
||||
✓ depends with ...params
|
||||
✓ depends with defined arguments
|
||||
✓ depends run test only once
|
||||
|
||||
Tests: 7 skipped, 227 passed
|
||||
|
||||
@ -8,8 +8,22 @@ trait PluginTrait
|
||||
{
|
||||
public function assertPluginTraitGotRegistered(): void
|
||||
{
|
||||
assertTrue(true);
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
trait SecondPluginTrait
|
||||
{
|
||||
public function assertSecondPluginTraitGotRegistered(): void
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
Pest\Plugin::uses(PluginTrait::class);
|
||||
Pest\Plugin::uses(SecondPluginTrait::class);
|
||||
|
||||
function _assertThat()
|
||||
{
|
||||
expect(true)->toBeTrue();
|
||||
}
|
||||
|
||||
10
tests/Expect/not.php
Normal file
10
tests/Expect/not.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
test('not property calls', function () {
|
||||
expect(true)
|
||||
->toBeTrue()
|
||||
->not()->toBeFalse()
|
||||
->not->toBeFalse
|
||||
->and(false)
|
||||
->toBeFalse();
|
||||
});
|
||||
20
tests/Expect/toBe.php
Normal file
20
tests/Expect/toBe.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
expect(true)->toBeTrue()->and(false)->toBeFalse();
|
||||
|
||||
test('strict comparisons', function () {
|
||||
$nuno = new stdClass();
|
||||
$dries = new stdClass();
|
||||
|
||||
expect($nuno)->toBe($nuno)->not->toBe($dries);
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(1)->toBe(2);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(1)->not->toBe(1);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeArray.php
Normal file
16
tests/Expect/toBeArray.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect([1, 2, 3])->toBeArray();
|
||||
expect('1, 2, 3')->not->toBeArray();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(null)->toBeArray();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(['a', 'b', 'c'])->not->toBeArray();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeBool.php
Normal file
16
tests/Expect/toBeBool.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(true)->toBeBool();
|
||||
expect(0)->not->toBeBool();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(null)->toBeBool();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(false)->not->toBeBool();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
18
tests/Expect/toBeCallable.php
Normal file
18
tests/Expect/toBeCallable.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(function () {})->toBeCallable();
|
||||
expect(null)->not->toBeCallable();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
$hello = 5;
|
||||
|
||||
expect($hello)->toBeCallable();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(function () { return 42; })->not->toBeCallable();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
17
tests/Expect/toBeDirectory.php
Normal file
17
tests/Expect/toBeDirectory.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
$temp = sys_get_temp_dir();
|
||||
|
||||
expect($temp)->toBeDirectory();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect('/random/path/whatever')->toBeDirectory();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect('.')->not->toBeDirectory();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
18
tests/Expect/toBeEmpty.php
Normal file
18
tests/Expect/toBeEmpty.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect([])->toBeEmpty();
|
||||
expect(null)->toBeEmpty();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect([1, 2])->toBeEmpty();
|
||||
expect(' ')->toBeEmpty();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect([])->not->toBeEmpty();
|
||||
expect(null)->not->toBeEmpty();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
15
tests/Expect/toBeFalse.php
Normal file
15
tests/Expect/toBeFalse.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('strict comparisons', function () {
|
||||
expect(false)->toBeFalse();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect('')->toBeFalse();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(false)->not->toBe(false);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
23
tests/Expect/toBeFile.php
Normal file
23
tests/Expect/toBeFile.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
beforeEach(function () {
|
||||
touch($this->tempFile = sys_get_temp_dir() . '/fake.file');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
unlink($this->tempFile);
|
||||
});
|
||||
|
||||
test('pass', function () {
|
||||
expect($this->tempFile)->toBeFile();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect('/random/path/whatever.file')->toBeFile();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect($this->tempFile)->not->toBeFile();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeFloat.php
Normal file
16
tests/Expect/toBeFloat.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(1.0)->toBeFloat();
|
||||
expect(1)->not->toBeFloat();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(42)->toBeFloat();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(log(3))->not->toBeFloat();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeGreatherThan.php
Normal file
16
tests/Expect/toBeGreatherThan.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('passes', function () {
|
||||
expect(42)->toBeGreaterThan(41);
|
||||
expect(4)->toBeGreaterThan(3.9);
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(4)->toBeGreaterThan(4);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(5)->not->toBeGreaterThan(4);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeGreatherThanOrEqual.php
Normal file
16
tests/Expect/toBeGreatherThanOrEqual.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('passes', function () {
|
||||
expect(42)->toBeGreaterThanOrEqual(41);
|
||||
expect(4)->toBeGreaterThanOrEqual(4);
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(4)->toBeGreaterThanOrEqual(4.1);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(5)->not->toBeGreaterThanOrEqual(5);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeInfinite.php
Normal file
16
tests/Expect/toBeInfinite.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(log(0))->toBeInfinite();
|
||||
expect(log(1))->not->toBeInfinite();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(asin(2))->toBeInfinite();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(INF)->not->toBeInfinite();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeInstanceOf.php
Normal file
16
tests/Expect/toBeInstanceOf.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(new Exception())->toBeInstanceOf(Exception::class);
|
||||
expect(new Exception())->not->toBeInstanceOf(RuntimeException::class);
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(new Exception())->toBeInstanceOf(RuntimeException::class);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(new Exception())->not->toBeInstanceOf(Exception::class);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeInt.php
Normal file
16
tests/Expect/toBeInt.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(42)->toBeInt();
|
||||
expect(42.0)->not->toBeInt();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(42.0)->toBeInt();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(6 * 7)->not->toBeInt();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
23
tests/Expect/toBeIterable.php
Normal file
23
tests/Expect/toBeIterable.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect([])->toBeIterable();
|
||||
expect(null)->not->toBeIterable();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(42)->toBeIterable();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
function gen(): iterable
|
||||
{
|
||||
yield 1;
|
||||
yield 2;
|
||||
yield 3;
|
||||
}
|
||||
|
||||
expect(gen())->not->toBeIterable();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeLessThan.php
Normal file
16
tests/Expect/toBeLessThan.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('passes', function () {
|
||||
expect(41)->toBeLessThan(42);
|
||||
expect(4)->toBeLessThan(5);
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(4)->toBeLessThan(4);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(5)->not->toBeLessThan(6);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeLessThanOrEqual.php
Normal file
16
tests/Expect/toBeLessThanOrEqual.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('passes', function () {
|
||||
expect(41)->toBeLessThanOrEqual(42);
|
||||
expect(4)->toBeLessThanOrEqual(4);
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(4)->toBeLessThanOrEqual(3.9);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(5)->not->toBeLessThanOrEqual(5);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeNAN.php
Normal file
16
tests/Expect/toBeNAN.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(asin(2))->toBeNan();
|
||||
expect(log(0))->not->toBeNan();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(1)->toBeNan();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(acos(1.5))->not->toBeNan();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeNull.php
Normal file
16
tests/Expect/toBeNull.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(null)->toBeNull();
|
||||
expect('')->not->toBeNull();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect('hello')->toBeNull();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(null)->not->toBeNull();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeNumeric.php
Normal file
16
tests/Expect/toBeNumeric.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(42)->toBeNumeric();
|
||||
expect('A')->not->toBeNumeric();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(null)->toBeNumeric();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(6 * 7)->not->toBeNumeric();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeObject.php
Normal file
16
tests/Expect/toBeObject.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect((object) ['a' => 1])->toBeObject();
|
||||
expect(['a' => 1])->not->toBeObject();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(null)->toBeObject();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect((object) 'ciao')->not->toBeObject();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
15
tests/Expect/toBeReadableDirectory.php
Normal file
15
tests/Expect/toBeReadableDirectory.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(sys_get_temp_dir())->toBeReadableDirectory();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect('/random/path/whatever')->toBeReadableDirectory();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(sys_get_temp_dir())->not->toBeReadableDirectory();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
23
tests/Expect/toBeReadableFile.php
Normal file
23
tests/Expect/toBeReadableFile.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
beforeEach(function () {
|
||||
touch($this->tempFile = sys_get_temp_dir() . '/fake.file');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
unlink($this->tempFile);
|
||||
});
|
||||
|
||||
test('pass', function () {
|
||||
expect($this->tempFile)->toBeReadableFile();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect('/random/path/whatever.file')->toBeReadableFile();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect($this->tempFile)->not->toBeReadableFile();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
22
tests/Expect/toBeResource.php
Normal file
22
tests/Expect/toBeResource.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
$resource = tmpfile();
|
||||
|
||||
afterAll(function () use ($resource) {
|
||||
fclose($resource);
|
||||
});
|
||||
|
||||
test('pass', function () use ($resource) {
|
||||
expect($resource)->toBeResource();
|
||||
expect(null)->not->toBeResource();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(null)->toBeResource();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () use ($resource) {
|
||||
expect($resource)->not->toBeResource();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
15
tests/Expect/toBeScalar.php
Normal file
15
tests/Expect/toBeScalar.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(1.1)->toBeScalar();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(null)->toBeScalar();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(42)->not->toBeScalar();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toBeString.php
Normal file
16
tests/Expect/toBeString.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect('1.1')->toBeString();
|
||||
expect(1.1)->not->toBeString();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(null)->toBeString();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect('42')->not->toBeString();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
15
tests/Expect/toBeTrue.php
Normal file
15
tests/Expect/toBeTrue.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('strict comparisons', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect('')->toBeTrue();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(false)->not->toBe(false);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
15
tests/Expect/toBeWritableDirectory.php
Normal file
15
tests/Expect/toBeWritableDirectory.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(sys_get_temp_dir())->toBeWritableDirectory();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect('/random/path/whatever')->toBeWritableDirectory();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(sys_get_temp_dir())->not->toBeWritableDirectory();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
23
tests/Expect/toBeWritableFile.php
Normal file
23
tests/Expect/toBeWritableFile.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
beforeEach(function () {
|
||||
touch($this->tempFile = sys_get_temp_dir() . '/fake.file');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
unlink($this->tempFile);
|
||||
});
|
||||
|
||||
test('pass', function () {
|
||||
expect($this->tempFile)->toBeWritableFile();
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect('/random/path/whatever.file')->toBeWritableFile();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect($this->tempFile)->not->toBeWritableFile();
|
||||
})->throws(ExpectationFailedException::class);
|
||||
19
tests/Expect/toContain.php
Normal file
19
tests/Expect/toContain.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('passes strings', function () {
|
||||
expect([1, 2, 42])->toContain(42);
|
||||
});
|
||||
|
||||
test('passes arrays', function () {
|
||||
expect('Nuno')->toContain('Nu');
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect([1, 2, 42])->toContain(3);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect([1, 2, 42])->not->toContain(42);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
15
tests/Expect/toEndWith.php
Normal file
15
tests/Expect/toEndWith.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect('username')->toEndWith('name');
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect('username')->toEndWith('password');
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect('username')->not->toEndWith('name');
|
||||
})->throws(ExpectationFailedException::class);
|
||||
15
tests/Expect/toEqual.php
Normal file
15
tests/Expect/toEqual.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect('00123')->toEqual(123);
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(['a', 'b', 'c'])->toEqual(['a', 'b']);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect('042')->not->toEqual(42);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
16
tests/Expect/toEqualCanonicalizing.php
Normal file
16
tests/Expect/toEqualCanonicalizing.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect([1, 2, 3])->toEqualCanonicalizing([3, 1, 2]);
|
||||
expect(['g', 'a', 'z'])->not->toEqualCanonicalizing(['a', 'z']);
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect([3, 2, 1])->toEqualCanonicalizing([1, 2]);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(['a', 'b', 'c'])->not->toEqualCanonicalizing(['b', 'a', 'c']);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
15
tests/Expect/toEqualWithDelta.php
Normal file
15
tests/Expect/toEqualWithDelta.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('pass', function () {
|
||||
expect(1.0)->toEqualWithDelta(1.3, .4);
|
||||
});
|
||||
|
||||
test('failures', function () {
|
||||
expect(1.0)->toEqualWithDelta(1.5, .1);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(1.0)->not->toEqualWithDelta(1.6, .7);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user