Compare commits

..

2 Commits

Author SHA1 Message Date
27fbe3efcc docs: updates changelog 2020-08-06 21:18:26 +01:00
d0eb2f15cd chore: keeps phpunit 9.2 for pest 0.2 2020-08-06 21:15:46 +01:00
82 changed files with 2281 additions and 1005 deletions

View File

@ -14,5 +14,5 @@ trim_trailing_whitespace = true
[*.md] [*.md]
trim_trailing_whitespace = false trim_trailing_whitespace = false
[*.{yml,yaml}] [*.yml]
indent_size = 2 indent_size = 2

2
.github/FUNDING.yml vendored
View File

@ -1,5 +1,5 @@
# These are supported funding model platforms # These are supported funding model platforms
github: [nunomaduro,owenvoke,olivernybroe,octoper] github: nunomaduro
patreon: nunomaduro patreon: nunomaduro
custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L

View File

@ -5,7 +5,6 @@ on:
branches: [ master ] branches: [ master ]
paths: paths:
- CHANGELOG.md - CHANGELOG.md
- .github/workflows/changelog.yml
pull_request: pull_request:
branches: [ master ] branches: [ master ]
paths: paths:
@ -14,25 +13,19 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'pestphp/pest'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Checkout website repository - name: Checkout website repository
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
token: ${{ secrets.CHANGELOG_KEY }} token: ${{ secrets.CHANGELOG_KEY }}
repository: pestphp/docs repository: pestphp/website
path: pestphp-docs path: pestphp-website
ref: master
- name: Read CHANGELOG.md - name: Read CHANGELOG.md
id: package id: package
uses: juliangruber/read-file-action@v1 uses: juliangruber/read-file-action@v1
with: with:
path: ./CHANGELOG.md path: ./CHANGELOG.md
- name: Add file headers - name: Add file headers
uses: DamianReeves/write-file-action@v1.0 uses: DamianReeves/write-file-action@v1.0
with: with:
@ -41,23 +34,21 @@ jobs:
--- ---
title: Changelog title: Changelog
description: Changelog description: Changelog
extends: _layouts.documentation
section: content
--- ---
${{ steps.package.outputs.content }} ${{ steps.package.outputs.content }}
----
Next section: [Upgrade Guide →](/docs/upgrade-guide) Next section: [Upgrade Guide →](/docs/upgrade-guide)
write-mode: overwrite write-mode: overwrite
- name: Copy CHANGELOG to website repository - name: Copy CHANGELOG to website repository
run: cp CHANGELOG.md pestphp-docs/changelog.md run: cp CHANGELOG.md pestphp-website/source/docs/changelog.md
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v3 uses: peter-evans/create-pull-request@v2
with: with:
token: ${{ secrets.CHANGELOG_KEY }} token: ${{ secrets.CHANGELOG_KEY }}
commit-message: Update changelog.md commit-message: Update changelog.md
committer: GitHub Action <noreply@github.com> committer: GitHub Action <noreply@github.com>
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
title: 'Update changelog.md' title: 'Update changelog.md'
path: ./pestphp-docs path: ./pestphp-website

46
.github/workflows/formats.yml vendored Normal file
View File

@ -0,0 +1,46 @@
name: Formats
on: ['push', 'pull_request']
jobs:
ci:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest]
php: [7.4]
dependency-version: [prefer-lowest, prefer-stable]
name: Formats P${{ matrix.php }} - ${{ matrix.os }} - ${{ 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: dom, 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

View File

@ -1,51 +0,0 @@
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 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

View File

@ -6,39 +6,43 @@ jobs:
ci: ci:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: true
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
php: ['7.3', '7.4', '8.0'] php: [7.3, 7.4]
dependency-version: [prefer-lowest, prefer-stable] dependency-version: [prefer-lowest, prefer-stable]
name: PHP ${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }} name: Tests P${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 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 - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
tools: composer:v2 extensions: dom, mbstring, zip
coverage: none coverage: none
- name: Setup Problem Matches - name: Install Composer dependencies
run: | run: composer update --${{ matrix.dependency-version }} --no-interaction --prefer-dist
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 - name: Unit Tests
run: php bin/pest --colors=always --exclude-group=integration run: php bin/pest --colors=always --exclude-group=integration
- name: Integration Tests - name: Integration Tests
run: php bin/pest --colors=always --group=integration run: php bin/pest --colors=always --group=integration
- name: Setup problem matchers for PHP
run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
- name: Setup problem matchers for Pest
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"

View File

@ -3,6 +3,7 @@
$finder = PhpCsFixer\Finder::create() $finder = PhpCsFixer\Finder::create()
->in(__DIR__ . DIRECTORY_SEPARATOR . 'tests') ->in(__DIR__ . DIRECTORY_SEPARATOR . 'tests')
->in(__DIR__ . DIRECTORY_SEPARATOR . 'bin') ->in(__DIR__ . DIRECTORY_SEPARATOR . 'bin')
->in(__DIR__ . DIRECTORY_SEPARATOR . 'compiled')
->in(__DIR__ . DIRECTORY_SEPARATOR . 'scripts') ->in(__DIR__ . DIRECTORY_SEPARATOR . 'scripts')
->in(__DIR__ . DIRECTORY_SEPARATOR . 'stubs') ->in(__DIR__ . DIRECTORY_SEPARATOR . 'stubs')
->in(__DIR__ . DIRECTORY_SEPARATOR . 'src') ->in(__DIR__ . DIRECTORY_SEPARATOR . 'src')

View File

@ -4,124 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## [v1.0.2 (2021-02-04)](https://github.com/pestphp/pest/compare/v1.0.1...v1.0.2) ## [Unreleased]
### Added
- Support for PHPUnit 9.5.2 ([#267](https://github.com/pestphp/pest/pull/267))
## [v1.0.1 (2021-01-18)](https://github.com/pestphp/pest/compare/v1.0.0...v1.0.1)
### Added
- Support for PHPUnit 9.5.1 ([#261](https://github.com/pestphp/pest/pull/261))
### Fixed
- Fix `TestCase@expect` PHPDoc tag ([#251](https://github.com/pestphp/pest/pull/251))
## [v1.0.0 (2021-01-03)](https://github.com/pestphp/pest/compare/v0.3.19...v1.0.0)
### Added
- `pest:test --dusk` option ([#245](https://github.com/pestphp/pest/pull/245))
## [v0.2.4 (2020-08-06)](https://github.com/pestphp/pest/compare/v0.2.3...v0.2.4)
### Changed ### Changed
- Stable version - uses `"phpunit/phpunit": "~9.2.0"` as dependency ([d0eb2f1](https://github.com/pestphp/pest/commit/d0eb2f15cd44b558ebb47002a7ff5b7af6fbbc07))
- Updates init structure ([#240](https://github.com/pestphp/pest/pull/240))
## [v0.3.19 (2020-12-27)](https://github.com/pestphp/pest/compare/v0.3.18...v0.3.19)
### Fixed
- Fix binary path in `pest:dusk` command ([#239](https://github.com/pestphp/pest/pull/239))
## [v0.3.18 (2020-12-26)](https://github.com/pestphp/pest/compare/v0.3.17...v0.3.18)
### Added
- `toBeJson()` expectation ([plugin-expectations#2](https://github.com/pestphp/pest-plugin-expectations/pull/2))
## [v0.3.17 (2020-12-20)](https://github.com/pestphp/pest/compare/v0.3.16...v0.3.17)
### Fixed
- Class inheritance with `depends()` ([#236](https://github.com/pestphp/pest/pull/236))
## [v0.3.16 (2020-12-13)](https://github.com/pestphp/pest/compare/v0.3.15...v0.3.16)
### Changed
- Moves expectation API for external plugin ([5d7f262](https://github.com/pestphp/pest/commit/5d7f262f4ab280a660a85900f402eebb23abfda8))
## [v0.3.15 (2020-12-04)](https://github.com/pestphp/pest/compare/v0.3.14...v0.3.15)
### Added
- Support for PHPUnit 9.5.0 ([#234](https://github.com/pestphp/pest/pull/234))
- Support for extending expectation API ([#232](https://github.com/pestphp/pest/pull/232))
### Fixed
- Static analysis while using string as key for datasets ([#233](https://github.com/pestphp/pest/pull/233))
## [v0.3.14 (2020-11-28)](https://github.com/pestphp/pest/compare/v0.3.13...v0.3.14)
### Added
- `pest:dusk` command ([#223](https://github.com/pestphp/pest/pull/223))
- Better feedback on errors in `toMatchArray` and `toMatchObject` ([#231](https://github.com/pestphp/pest/pull/231))
## [v0.3.13 (2020-11-23)](https://github.com/pestphp/pest/compare/v0.3.12...v0.3.13)
### Added
- `toMatchArray` expectation ([7bea51f](https://github.com/pestphp/pest/commit/7bea51fe09dd2eca7093e4c34cf2dab2e8d39fa5), [3fd24d9](https://github.com/pestphp/pest/commit/3fd24d96d3145dcebdb0aab40aa8b76faa8b6979))
- Add Pest options to `--help` output ([#217](https://github.com/pestphp/pest/pull/217))
### Fixed
- Resolve issue with name resolution in `depends()` ([#216](https://github.com/pestphp/pest/pull/216))
## [v0.3.12 (2020-11-11)](https://github.com/pestphp/pest/compare/v0.3.11...v0.3.12)
### Added
- Add support for PHPUnit 9.4.3 ([#219](https://github.com/pestphp/pest/pull/219))
## [v0.3.11 (2020-11-09)](https://github.com/pestphp/pest/compare/v0.3.10...v0.3.11)
### Changed
- Improved the exception output for the TeamCity printer (usage with phpstorm plugin) ([#215](https://github.com/pestphp/pest/pull/215))
## [v0.3.10 (2020-11-01)](https://github.com/pestphp/pest/compare/v0.3.9...v0.3.10)
### Added
- Add support for PHPUnit 9.4.2 ([d177ab5](https://github.com/pestphp/pest/commit/d177ab5ec2030c5bb8e418d10834c370c94c433d))
## [v0.3.9 (2020-10-13)](https://github.com/pestphp/pest/compare/v0.3.8...v0.3.9)
### Added
- Add support for named datasets in description output ([#134](https://github.com/pestphp/pest/pull/134))
- Add Pest version to `--help` output ([#203](https://github.com/pestphp/pest/pull/203))
- Add support for PHPUnit 9.4.1 ([#207](https://github.com/pestphp/pest/pull/207))
## [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) ## [v0.2.3 (2020-07-01)](https://github.com/pestphp/pest/compare/v0.2.2...v0.2.3)
### Added ### Added

View File

@ -1,7 +1,7 @@
<p align="center"> <p align="center">
<img src="https://raw.githubusercontent.com/pestphp/art/master/readme.png" width="600" alt="PEST"> <img src="https://raw.githubusercontent.com/pestphp/art/master/readme.png" width="600" alt="PEST">
<p align="center"> <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/Tests/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/Continuous Integration/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="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="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> <a href="https://packagist.org/packages/pestphp/pest"><img alt="License" src="https://img.shields.io/packagist/l/pestphp/pest"></a>
@ -15,13 +15,4 @@
- Follow us on Twitter: **[@pestphp »](https://twitter.com/pestphp)** - Follow us on Twitter: **[@pestphp »](https://twitter.com/pestphp)**
- Join us on the Discord Server: **[discord.gg/bMAJv82 »](https://discord.gg/bMAJv82)** - 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://scoutapm.com)**
- **[Akaunting](https://akaunting.com)**
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)**. 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)**.

View File

@ -1,16 +0,0 @@
# Release process
When releasing a new version of Pest there are some checks and updates that need to be done:
- Clear your local repository with: `git add . && git reset --hard && git checkout master`
- On the GitHub repository, check the contents of [github.com/pestphp/pest/compare/{latest_version}...master](https://github.com/pestphp/pest/compare/{latest_version}...master) and update the [changelog](CHANGELOG.md) file with the main changes for this release
- Update the version number in [src/Pest.php](src/Pest.php)
- Run the tests locally using: `composer test`
- Commit the CHANGELOG and Pest file with the message: `git commit -m "docs: update changelog"`
- Push the changes to GitHub
- Check that the CI is passing as expected: [github.com/pestphp/pest/actions](https://github.com/pestphp/pest/actions)
- Tag and push the tag with `git tag vX.X.X && git push --tags`
### Plugins
Plugins should be versioned using the same major (or minor for `0.x` releases) version as Pest core.

View File

@ -6,7 +6,6 @@ use Pest\Actions\ValidatesEnvironment;
use Pest\Console\Command; use Pest\Console\Command;
use Pest\Support\Container; use Pest\Support\Container;
use Pest\TestSuite; use Pest\TestSuite;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
@ -19,21 +18,16 @@ use Symfony\Component\Console\Output\OutputInterface;
if (file_exists($vendorPath)) { if (file_exists($vendorPath)) {
include_once $vendorPath; include_once $vendorPath;
$autoloadPath = $vendorPath;
} else { } else {
include_once $localPath; include_once $localPath;
$autoloadPath = $localPath;
} }
(new Provider())->register(); (new Provider())->register();
// get $rootPath based on $autoloadPath $rootPath = getcwd();
$rootPath = dirname($autoloadPath, 2);
$testSuite = TestSuite::getInstance($rootPath); $testSuite = TestSuite::getInstance($rootPath);
$output = new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, true);
$isDecorated = (new ArgvInput())->getParameterOption('--colors', 'always') !== 'never';
$output = new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, $isDecorated);
$container = Container::getInstance(); $container = Container::getInstance();
$container->add(TestSuite::class, $testSuite); $container->add(TestSuite::class, $testSuite);

0
compiled/.gitkeep Normal file
View File

1926
compiled/globals.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -17,21 +17,22 @@
} }
], ],
"require": { "require": {
"php": "^7.3 || ^8.0", "php": "^7.3",
"nunomaduro/collision": "^5.0", "nunomaduro/collision": "^5.0.0-BETA3",
"pestphp/pest-plugin": "^1.0", "pestphp/pest-plugin": "^0.2",
"pestphp/pest-plugin-coverage": "^1.0", "pestphp/pest-plugin-coverage": "^0.2",
"pestphp/pest-plugin-expectations": "^1.0", "pestphp/pest-plugin-init": "^0.2",
"pestphp/pest-plugin-init": "^1.0", "phpunit/phpunit": "~9.2.0",
"phpunit/phpunit": ">= 9.3.7 <= 9.5.2" "sebastian/environment": "^5.1"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Pest\\": "src/" "Pest\\": "src/"
}, },
"files": [ "files": [
"src/Functions.php", "src/globals.php",
"src/Pest.php" "src/Pest.php",
"compiled/globals.php"
] ]
}, },
"autoload-dev": { "autoload-dev": {
@ -43,11 +44,16 @@
] ]
}, },
"require-dev": { "require-dev": {
"illuminate/console": "^8.0", "ergebnis/phpstan-rules": "^0.15.0",
"illuminate/support": "^8.0", "friendsofphp/php-cs-fixer": "^2.16.3",
"laravel/dusk": "^6.9.1", "illuminate/console": "^7.16.1",
"mockery/mockery": "^1.4.1", "illuminate/support": "^7.16.1",
"pestphp/pest-dev-tools": "dev-master" "mockery/mockery": "^1.4.0",
"phpstan/phpstan": "^0.12.30",
"phpstan/phpstan-strict-rules": "^0.12.2",
"rector/rector": "^0.7.37",
"symfony/var-dumper": "^5.1.2",
"thecodingmachine/phpstan-strict-rules": "^0.12.0"
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"prefer-stable": true, "prefer-stable": true,
@ -59,8 +65,9 @@
"bin/pest" "bin/pest"
], ],
"scripts": { "scripts": {
"lint": "php-cs-fixer fix -v", "compile": "@php ./scripts/compile.php",
"test:lint": "php-cs-fixer fix -v --dry-run", "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 --memory-limit=0", "test:types": "phpstan analyse --ansi --memory-limit=0",
"test:unit": "php bin/pest --colors=always --exclude-group=integration", "test:unit": "php bin/pest --colors=always --exclude-group=integration",
"test:integration": "php bin/pest --colors=always --group=integration", "test:integration": "php bin/pest --colors=always --group=integration",
@ -74,7 +81,7 @@
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.x-dev" "dev-master": "0.2.x-dev"
}, },
"pest": { "pest": {
"plugins": [ "plugins": [

View File

@ -7,26 +7,17 @@ parameters:
level: max level: max
paths: paths:
- src - src
excludes_analyse:
- src/globals.php
checkMissingIterableValueType: true checkMissingIterableValueType: true
checkGenericClassInNonGenericObjectType: false checkGenericClassInNonGenericObjectType: false
reportUnmatchedIgnoredErrors: true reportUnmatchedIgnoredErrors: true
ignoreErrors: ignoreErrors:
- "#type mixed is not subtype of native#"
- "#Undefined variable: \\$this#" - "#Undefined variable: \\$this#"
- "#is not allowed to extend#" - "#is not allowed to extend#"
- "#Language construct eval#" - "#Language construct eval#"
- "# with null as default value#" - "# with null as default value#"
- "#has parameter \\$closure with default value.#" - "#has parameter \\$closure with default value.#"
- "#has parameter \\$description 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

View File

@ -8,9 +8,9 @@
<directory suffix=".php">./tests</directory> <directory suffix=".php">./tests</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>
<coverage processUncoveredFiles="true"> <filter>
<include> <whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src</directory> <directory suffix=".php">./src</directory>
</include> </whitelist>
</coverage> </filter>
</phpunit> </phpunit>

16
rector.yaml Normal file
View File

@ -0,0 +1,16 @@
# 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'

View File

@ -5,16 +5,12 @@ declare(strict_types=1);
namespace Pest\Actions; namespace Pest\Actions;
use NunoMaduro\Collision\Adapters\Phpunit\Printer; use NunoMaduro\Collision\Adapters\Phpunit\Printer;
use Pest\TeamCity;
use PHPUnit\TextUI\DefaultResultPrinter;
/** /**
* @internal * @internal
*/ */
final class AddsDefaults final class AddsDefaults
{ {
private const PRINTER = 'printer';
/** /**
* Adds default arguments to the given `arguments` array. * Adds default arguments to the given `arguments` array.
* *
@ -24,12 +20,8 @@ final class AddsDefaults
*/ */
public static function to(array $arguments): array public static function to(array $arguments): array
{ {
if (!array_key_exists(self::PRINTER, $arguments)) { if (!array_key_exists('printer', $arguments)) {
$arguments[self::PRINTER] = new Printer(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS); $arguments['printer'] = new Printer(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? '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; return $arguments;

View File

@ -21,7 +21,6 @@ final class LoadStructure
* @var array<int, string> * @var array<int, string>
*/ */
private const STRUCTURE = [ private const STRUCTURE = [
'Expectations.php',
'Datasets.php', 'Datasets.php',
'Helpers.php', 'Helpers.php',
'Pest.php', 'Pest.php',

View File

@ -6,7 +6,8 @@ namespace Pest\Actions;
use Pest\Exceptions\AttributeNotSupportedYet; use Pest\Exceptions\AttributeNotSupportedYet;
use Pest\Exceptions\FileOrFolderNotFound; use Pest\Exceptions\FileOrFolderNotFound;
use PHPUnit\TextUI\XmlConfiguration\Loader; use PHPUnit\TextUI\Configuration\Configuration;
use PHPUnit\TextUI\Configuration\Registry;
/** /**
* @internal * @internal
@ -29,7 +30,9 @@ final class ValidatesConfiguration
throw new FileOrFolderNotFound('phpunit.xml'); throw new FileOrFolderNotFound('phpunit.xml');
} }
$configuration = (new Loader())->load($arguments[self::CONFIGURATION_KEY])->phpunit(); $configuration = Registry::getInstance()
->get($arguments[self::CONFIGURATION_KEY])
->phpunit();
if ($configuration->processIsolation()) { if ($configuration->processIsolation()) {
throw new AttributeNotSupportedYet('processIsolation', 'true'); throw new AttributeNotSupportedYet('processIsolation', 'true');

View File

@ -7,9 +7,7 @@ namespace Pest\Concerns;
use Closure; use Closure;
use Pest\Support\ExceptionTrace; use Pest\Support\ExceptionTrace;
use Pest\TestSuite; use Pest\TestSuite;
use PHPUnit\Framework\ExecutionOrderDependency;
use PHPUnit\Util\Test; use PHPUnit\Util\Test;
use Throwable;
/** /**
* To avoid inheritance conflicts, all the fields related * To avoid inheritance conflicts, all the fields related
@ -55,24 +53,6 @@ trait TestCase
$this->setGroups($groups); $this->setGroups($groups);
} }
/**
* Add dependencies to the test case and map them to instances of ExecutionOrderDependency.
*/
public function addDependencies(array $tests): void
{
$className = get_class($this);
$tests = array_map(function (string $test) use ($className): ExecutionOrderDependency {
if (strpos($test, '::') === false) {
$test = "{$className}::{$test}";
}
return new ExecutionOrderDependency($test, null, '');
}, $tests);
$this->setDependencies($tests);
}
/** /**
* Returns the test case name. Note that, in Pest * Returns the test case name. Note that, in Pest
* we ignore withDataset argument as the description * we ignore withDataset argument as the description
@ -83,11 +63,6 @@ trait TestCase
return $this->__description; 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. * This method is called before the first test of this test class is run.
*/ */
@ -157,7 +132,7 @@ trait TestCase
* *
* @return mixed * @return mixed
* *
* @throws Throwable * @throws \Throwable
*/ */
public function __test() public function __test()
{ {
@ -167,7 +142,7 @@ trait TestCase
/** /**
* @return mixed * @return mixed
* *
* @throws Throwable * @throws \Throwable
*/ */
private function __callClosure(Closure $closure, array $arguments) private function __callClosure(Closure $closure, array $arguments)
{ {

View File

@ -11,8 +11,6 @@ use Pest\Actions\ValidatesConfiguration;
use Pest\Contracts\Plugins\AddsOutput; use Pest\Contracts\Plugins\AddsOutput;
use Pest\Contracts\Plugins\HandlesArguments; use Pest\Contracts\Plugins\HandlesArguments;
use Pest\Plugin\Loader; use Pest\Plugin\Loader;
use Pest\Plugins\Version;
use Pest\Support\Container;
use Pest\TestSuite; use Pest\TestSuite;
use PHPUnit\Framework\TestSuite as BaseTestSuite; use PHPUnit\Framework\TestSuite as BaseTestSuite;
use PHPUnit\TextUI\Command as BaseCommand; use PHPUnit\TextUI\Command as BaseCommand;
@ -141,14 +139,4 @@ final class Command extends BaseCommand
exit($result); exit($result);
} }
protected function showHelp(): void
{
/** @var Version $version */
$version = Container::getInstance()->get(Version::class);
$version->handleArguments(['--version']);
parent::showHelp();
(new Help($this->output))();
}
} }

View File

@ -1,37 +0,0 @@
<?php
declare(strict_types=1);
namespace Pest\Console;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @internal
*/
final class Help
{
/** @var array<int, string> */
private const HELP_MESSAGES = [
'<comment>Pest Options:</comment>',
' <info>--init</info> Initialise a standard Pest configuration',
' <info>--coverage</info> Enable coverage and output to standard output',
' <info>--min=<fg=cyan><N></></info> Set the minimum required coverage percentage (<N>), and fail if not met',
' <info>--group=<fg=cyan><name></></info> Only runs tests from the specified group(s)',
];
/** @var OutputInterface */
private $output;
public function __construct(OutputInterface $output)
{
$this->output = $output;
}
public function __invoke(): void
{
foreach (self::HELP_MESSAGES as $message) {
$this->output->writeln($message);
}
}
}

View File

@ -18,14 +18,14 @@ final class Datasets
/** /**
* Holds the datasets. * Holds the datasets.
* *
* @var array<int|string, Closure|iterable<int|string, mixed>> * @var array<string, \Closure|iterable<int, mixed>>
*/ */
private static $datasets = []; private static $datasets = [];
/** /**
* Sets the given. * Sets the given.
* *
* @param Closure|iterable<int|string, mixed> $data * @param Closure|iterable<int, mixed> $data
*/ */
public static function set(string $name, $data): void public static function set(string $name, $data): void
{ {
@ -37,7 +37,7 @@ final class Datasets
} }
/** /**
* @return Closure|iterable<int|string, mixed> * @return Closure|iterable<int, mixed>
*/ */
public static function get(string $name) public static function get(string $name)
{ {
@ -51,7 +51,7 @@ final class Datasets
/** /**
* Resolves the current dataset to an array value. * Resolves the current dataset to an array value.
* *
* @param Traversable<int|string, mixed>|Closure|iterable<int|string, mixed>|string|null $data * @param Traversable<int, mixed>|Closure|iterable<int, mixed>|string|null $data
* *
* @return array<string, mixed> * @return array<string, mixed>
*/ */
@ -77,10 +77,10 @@ final class Datasets
$dataSetDescriptions = []; $dataSetDescriptions = [];
$dataSetValues = []; $dataSetValues = [];
foreach ($data as $key => $values) { foreach ($data as $values) {
$values = is_array($values) ? $values : [$values]; $values = is_array($values) ? $values : [$values];
$dataSetDescriptions[] = $description . self::getDataSetDescription($key, $values); $dataSetDescriptions[] = $description . self::getDataSetDescription($values);
$dataSetValues[] = $values; $dataSetValues[] = $values;
} }
@ -104,15 +104,12 @@ final class Datasets
} }
/** /**
* @param int|string $key
* @param array<int, mixed> $data * @param array<int, mixed> $data
*/ */
private static function getDataSetDescription($key, array $data): string private static function getDataSetDescription(array $data): string
{ {
$exporter = new Exporter(); $exporter = new Exporter();
$nameInsert = is_string($key) ? \sprintf('data set "%s" ', $key) : ''; return \sprintf(' with (%s)', $exporter->shortenedRecursiveExport($data));
return \sprintf(' with %s(%s)', $nameInsert, $exporter->shortenedRecursiveExport($data));
} }
} }

View File

@ -59,7 +59,7 @@ final class TestCaseFactory
/** /**
* Holds the dataset, if any. * Holds the dataset, if any.
* *
* @var Closure|iterable<int|string, mixed>|string|null * @var Closure|iterable<int, mixed>|string|null
*/ */
public $dataset; public $dataset;
@ -157,7 +157,8 @@ final class TestCaseFactory
} }
/** /**
* Makes a fully qualified class name from the given filename. * Makes a fully qualified class name
* from the given filename.
*/ */
public function makeClassFromFilename(string $filename): string public function makeClassFromFilename(string $filename): string
{ {

View File

@ -61,8 +61,7 @@ final class PestDatasetCommand extends Command
$element = Str::singular($name); $element = Str::singular($name);
$contents = str_replace('{dataset_element}', $element, $contents); $contents = str_replace('{dataset_element}', $element, $contents);
File::put($target, str_replace('{dataset_name}', $name, $contents)); File::put($target, str_replace('{dataset_name}', $name, $contents));
$message = sprintf('`%s` created successfully.', $relativePath);
$this->output->success($message); $this->output->success(sprintf('`%s` created successfully.', $relativePath));
} }
} }

View File

@ -1,41 +0,0 @@
<?php
declare(strict_types=1);
namespace Pest\Laravel\Commands;
use Laravel\Dusk\Console\DuskCommand;
/**
* @internal
*/
final class PestDuskCommand extends DuskCommand
{
/**
* The console command name.
*
* @var string
*/
protected $signature = 'pest:dusk {--without-tty : Disable output to TTY}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Run the Dusk tests for the application with Pest';
/**
* Get the PHP binary to execute.
*
* @return array<string>
*/
protected function binary()
{
if ('phpdbg' === PHP_SAPI) {
return [PHP_BINARY, '-qrr', 'vendor/pestphp/pest/bin/pest'];
}
return [PHP_BINARY, 'vendor/pestphp/pest/bin/pest'];
}
}

View File

@ -6,8 +6,8 @@ namespace Pest\Laravel\Commands;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Pest\Console\Thanks;
use Pest\Exceptions\InvalidConsoleArgument; use Pest\Exceptions\InvalidConsoleArgument;
use Pest\Support\Str;
/** /**
* @internal * @internal
@ -35,10 +35,14 @@ final class PestInstallCommand extends Command
{ {
/* @phpstan-ignore-next-line */ /* @phpstan-ignore-next-line */
$pest = base_path('tests/Pest.php'); $pest = base_path('tests/Pest.php');
$stubs = 'stubs/Laravel'; /* @phpstan-ignore-next-line */
$helpers = base_path('tests/Helpers.php');
$stubs = $this->isLumen() ? 'stubs/Lumen' : 'stubs/Laravel';
if (File::exists($pest)) { foreach ([$pest, $helpers] as $file) {
throw new InvalidConsoleArgument(sprintf('%s already exist', $pest)); if (File::exists($file)) {
throw new InvalidConsoleArgument(sprintf('%s already exist', $file));
}
} }
File::copy(implode(DIRECTORY_SEPARATOR, [ File::copy(implode(DIRECTORY_SEPARATOR, [
@ -47,10 +51,24 @@ final class PestInstallCommand extends Command
'Pest.php', 'Pest.php',
]), $pest); ]), $pest);
$this->output->success('`tests/Pest.php` created successfully.'); File::copy(implode(DIRECTORY_SEPARATOR, [
dirname(__DIR__, 3),
$stubs,
'Helpers.php',
]), $helpers);
if (!(bool) $this->option('no-interaction')) { $this->output->success('`tests/Pest.php` created successfully.');
(new Thanks($this->output))(); $this->output->success('`tests/Helpers.php` created successfully.');
}
(new \Pest\Console\Thanks($this->output))();
}
/**
* Determine if this is a Lumen application.
*/
private function isLumen(): bool
{
/* @phpstan-ignore-next-line */
return Str::startsWith(app()->version(), 'Lumen');
} }
} }

View File

@ -19,7 +19,7 @@ final class PestTestCommand extends Command
* *
* @var string * @var string
*/ */
protected $signature = 'pest:test {name : The name of the file} {--unit : Create a unit test} {--dusk : Create a Dusk test}'; protected $signature = 'pest:test {name : The name of the file} {--unit : Create a unit test}';
/** /**
* The console command description. * The console command description.
@ -36,7 +36,7 @@ final class PestTestCommand extends Command
/** @var string $name */ /** @var string $name */
$name = $this->argument('name'); $name = $this->argument('name');
$type = ((bool) $this->option('unit')) ? 'Unit' : (((bool) $this->option('dusk')) ? 'Browser' : 'Feature'); $type = ((bool) $this->option('unit')) ? 'Unit' : 'Feature';
$relativePath = sprintf('tests/%s/%s.php', $relativePath = sprintf('tests/%s/%s.php',
$type, $type,
@ -64,8 +64,7 @@ final class PestTestCommand extends Command
$name = Str::endsWith($name, 'test') ? mb_substr($name, 0, -4) : $name; $name = Str::endsWith($name, 'test') ? mb_substr($name, 0, -4) : $name;
File::put($target, str_replace('{name}', $name, $contents)); File::put($target, str_replace('{name}', $name, $contents));
$message = sprintf('`%s` created successfully.', $relativePath);
$this->output->success($message); $this->output->success(sprintf('`%s` created successfully.', $relativePath));
} }
} }

View File

@ -5,9 +5,7 @@ declare(strict_types=1);
namespace Pest\Laravel; namespace Pest\Laravel;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Console\DuskCommand;
use Pest\Laravel\Commands\PestDatasetCommand; use Pest\Laravel\Commands\PestDatasetCommand;
use Pest\Laravel\Commands\PestDuskCommand;
use Pest\Laravel\Commands\PestInstallCommand; use Pest\Laravel\Commands\PestInstallCommand;
use Pest\Laravel\Commands\PestTestCommand; use Pest\Laravel\Commands\PestTestCommand;
@ -24,12 +22,6 @@ final class PestServiceProvider extends ServiceProvider
PestTestCommand::class, PestTestCommand::class,
PestDatasetCommand::class, PestDatasetCommand::class,
]); ]);
if (class_exists(DuskCommand::class)) {
$this->commands([
PestDuskCommand::class,
]);
}
} }
} }
} }

View File

@ -12,8 +12,6 @@ use Pest\TestSuite;
use SebastianBergmann\Exporter\Exporter; use SebastianBergmann\Exporter\Exporter;
/** /**
* @method \Pest\Expectations\Expectation expect(mixed $value)
*
* @internal * @internal
*/ */
final class TestCall final class TestCall
@ -77,7 +75,7 @@ final class TestCall
* Runs the current test multiple times with * Runs the current test multiple times with
* each item of the given `iterable`. * each item of the given `iterable`.
* *
* @param \Closure|iterable<int|string, mixed>|string $data * @param \Closure|iterable<int, mixed>|string $data
*/ */
public function with($data): TestCall public function with($data): TestCall
{ {
@ -93,7 +91,7 @@ final class TestCall
{ {
$this->testCaseFactory $this->testCaseFactory
->factoryProxies ->factoryProxies
->add(Backtrace::file(), Backtrace::line(), 'addDependencies', [$tests]); ->add(Backtrace::file(), Backtrace::line(), 'setDependencies', [$tests]);
return $this; return $this;
} }

View File

@ -6,5 +6,5 @@ namespace Pest;
function version(): string function version(): string
{ {
return '1.0.2'; return '0.2.2';
} }

View File

@ -11,7 +11,6 @@ use Pest\Exceptions\TestCaseClassOrTraitNotFound;
use Pest\Factories\TestCaseFactory; use Pest\Factories\TestCaseFactory;
use Pest\Support\Str; use Pest\Support\Str;
use Pest\TestSuite; use Pest\TestSuite;
use PHPUnit\Framework\TestCase;
/** /**
* @internal * @internal
@ -53,7 +52,7 @@ final class TestRepository
if ((!is_dir($path) && $filename === $path) || (is_dir($path) && $startsWith($filename, $path))) { if ((!is_dir($path) && $filename === $path) || (is_dir($path) && $startsWith($filename, $path))) {
foreach ($classOrTraits as $class) { foreach ($classOrTraits as $class) {
if (class_exists($class)) { if (class_exists($class)) {
if ($testCase->class !== TestCase::class) { if ($testCase->class !== \PHPUnit\Framework\TestCase::class) {
throw new TestCaseAlreadyInUse($testCase->class, $class, $filename); throw new TestCaseAlreadyInUse($testCase->class, $class, $filename);
} }
$testCase->class = $class; $testCase->class = $class;

View File

@ -75,15 +75,14 @@ final class Container
if ($constructor !== null) { if ($constructor !== null) {
$params = array_map( $params = array_map(
function (ReflectionParameter $param) use ($id) { function (ReflectionParameter $param) use ($id) {
$candidate = Reflection::getParameterClassName($param); $candidate = null;
if ($candidate === null) { if ($param->getType() !== null && $param->getType()->isBuiltin()) {
$type = $param->getType(); $candidate = $param->getName();
if ($type !== null && $type->isBuiltin()) { } elseif ($param->getClass() !== null) {
$candidate = $param->getName(); $candidate = $param->getClass()->getName();
} else { } else {
throw ShouldNotHappen::fromMessage(sprintf('The type of `$%s` in `%s` cannot be determined.', $id, $param->getName())); throw ShouldNotHappen::fromMessage(sprintf('The type of `$%s` in `%s` cannot be determined.', $id, $param->getName()));
}
} }
return $this->get($candidate); return $this->get($candidate);

View File

@ -20,7 +20,7 @@ final class ExceptionTrace
* *
* @return mixed * @return mixed
* *
* @throws Throwable * @throws \Throwable
*/ */
public static function ensure(Closure $closure) public static function ensure(Closure $closure)
{ {

View File

@ -76,8 +76,8 @@ final class HigherOrderMessage
Reflection::setPropertyValue($throwable, 'file', $this->filename); Reflection::setPropertyValue($throwable, 'file', $this->filename);
Reflection::setPropertyValue($throwable, 'line', $this->line); Reflection::setPropertyValue($throwable, 'line', $this->line);
if ($throwable->getMessage() === self::getUndefinedMethodMessage($target, $this->methodName)) { if ($throwable->getMessage() === sprintf(self::UNDEFINED_METHOD, $this->methodName)) {
/** @var ReflectionClass $reflection */ /** @var \ReflectionClass $reflection */
$reflection = new ReflectionClass($target); $reflection = new ReflectionClass($target);
/* @phpstan-ignore-next-line */ /* @phpstan-ignore-next-line */
$reflection = $reflection->getParentClass() ?: $reflection; $reflection = $reflection->getParentClass() ?: $reflection;
@ -87,13 +87,4 @@ final class HigherOrderMessage
throw $throwable; 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);
}
} }

View File

@ -30,7 +30,7 @@ final class HigherOrderMessageCollection
public function chain(object $target): void public function chain(object $target): void
{ {
foreach ($this->messages as $message) { foreach ($this->messages as $message) {
$target = $message->call($target) ?? $target; $target = $message->call($target);
} }
} }

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Pest\Support; namespace Pest\Support;
use ReflectionClass; use ReflectionClass;
use Throwable;
/** /**
* @internal * @internal
@ -52,12 +51,12 @@ final class HigherOrderTapProxy
try { try {
// @phpstan-ignore-next-line // @phpstan-ignore-next-line
return $this->target->{$property}; return $this->target->{$property};
} catch (Throwable $throwable) { } catch (\Throwable $throwable) {
Reflection::setPropertyValue($throwable, 'file', Backtrace::file()); Reflection::setPropertyValue($throwable, 'file', Backtrace::file());
Reflection::setPropertyValue($throwable, 'line', Backtrace::line()); Reflection::setPropertyValue($throwable, 'line', Backtrace::line());
if (Str::startsWith($message = $throwable->getMessage(), self::UNDEFINED_PROPERTY)) { if (Str::startsWith($message = $throwable->getMessage(), self::UNDEFINED_PROPERTY)) {
/** @var ReflectionClass $reflection */ /** @var \ReflectionClass $reflection */
$reflection = (new ReflectionClass($this->target))->getParentClass(); $reflection = (new ReflectionClass($this->target))->getParentClass();
Reflection::setPropertyValue($throwable, 'message', sprintf('Undefined property %s::$%s', $reflection->getName(), $property)); Reflection::setPropertyValue($throwable, 'message', sprintf('Undefined property %s::$%s', $reflection->getName(), $property));
} }

View File

@ -6,12 +6,9 @@ namespace Pest\Support;
use Closure; use Closure;
use Pest\Exceptions\ShouldNotHappen; use Pest\Exceptions\ShouldNotHappen;
use Pest\TestSuite;
use ReflectionClass; use ReflectionClass;
use ReflectionException; use ReflectionException;
use ReflectionFunction; use ReflectionFunction;
use ReflectionNamedType;
use ReflectionParameter;
use ReflectionProperty; use ReflectionProperty;
/** /**
@ -41,12 +38,6 @@ final class Reflection
return $object->__call($method, $args); return $object->__call($method, $args);
} }
if (is_callable($method)) {
return Closure::fromCallable($method)->bindTo(
TestSuite::getInstance()->test
)(...$args);
}
throw $exception; throw $exception;
} }
} }
@ -126,32 +117,4 @@ final class Reflection
$reflectionProperty->setAccessible(true); $reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($object, $value); $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;
}
} }

View File

@ -1,213 +0,0 @@
<?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';
/** @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 = (int) 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
{
$this->phpunitTeamCity->addError($test, $t, $time);
}
/**
* @phpstan-ignore-next-line
*
* @param Test|TestCase $test
*/
public function addWarning(Test $test, Warning $e, float $time): void
{
$this->phpunitTeamCity->addWarning($test, $e, $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
{
/** @var array<string, string> $uses */
$uses = class_uses($test);
return in_array(TestCase::class, $uses, true);
}
}

View File

@ -10,7 +10,6 @@ use Pest\Repositories\AfterEachRepository;
use Pest\Repositories\BeforeAllRepository; use Pest\Repositories\BeforeAllRepository;
use Pest\Repositories\BeforeEachRepository; use Pest\Repositories\BeforeEachRepository;
use Pest\Repositories\TestRepository; use Pest\Repositories\TestRepository;
use PHPUnit\Framework\TestCase;
/** /**
* @internal * @internal
@ -20,7 +19,7 @@ final class TestSuite
/** /**
* Holds the current test case. * Holds the current test case.
* *
* @var TestCase|null * @var \PHPUnit\Framework\TestCase|null
*/ */
public $test; public $test;

View File

@ -35,7 +35,7 @@ function beforeEach(Closure $closure = null): BeforeEachCall
/** /**
* Registers the given dataset. * Registers the given dataset.
* *
* @param Closure|iterable<int|string, mixed> $dataset * @param Closure|iterable $dataset
*/ */
function dataset(string $name, $dataset): void function dataset(string $name, $dataset): void
{ {
@ -43,8 +43,8 @@ function dataset(string $name, $dataset): void
} }
/** /**
* The uses function binds the given * The uses function adds the binds the
* arguments to test closures. * given arguments to test closures.
*/ */
function uses(string ...$classAndTraits): UsesCall function uses(string ...$classAndTraits): UsesCall
{ {
@ -62,7 +62,7 @@ function uses(string ...$classAndTraits): UsesCall
*/ */
function test(string $description = null, Closure $closure = null) function test(string $description = null, Closure $closure = null)
{ {
if ($description === null && TestSuite::getInstance()->test !== null) { if ($description === null && TestSuite::getInstance()->test) {
return new HigherOrderTapProxy(TestSuite::getInstance()->test); return new HigherOrderTapProxy(TestSuite::getInstance()->test);
} }
@ -100,7 +100,7 @@ function afterEach(Closure $closure = null): AfterEachCall
/** /**
* Runs the given closure after all tests in the current file. * Runs the given closure after all tests in the current file.
*/ */
function afterAll(Closure $closure): void function afterAll(Closure $closure = null): void
{ {
TestSuite::getInstance()->afterAll->set($closure); TestSuite::getInstance()->afterAll->set($closure);
} }

View File

@ -1,10 +0,0 @@
<?php
use Laravel\Dusk\Browser;
it('has {name} page', function () {
$this->browse(function (Browser $browser) {
$browser->visit('/{name}')
->assertSee('{name}');
});
});

11
stubs/Laravel/Helpers.php Normal file
View File

@ -0,0 +1,11 @@
<?php
namespace Tests;
/**
* A basic assert example.
*/
function assertExample(): void
{
test()->assertTrue(true);
}

View File

@ -1,45 +1,3 @@
<?php <?php
/*
|--------------------------------------------------------------------------
| Test Case
|--------------------------------------------------------------------------
|
| The closure you provide to your test functions is always bound to a specific PHPUnit test
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
| need to change it using the "uses()" function to bind a different classes or traits.
|
*/
uses(Tests\TestCase::class)->in('Feature'); uses(Tests\TestCase::class)->in('Feature');
/*
|--------------------------------------------------------------------------
| Expectations
|--------------------------------------------------------------------------
|
| When you're writing tests, you often need to check that values meet certain conditions. The
| "expect()" function gives you access to a set of "expectations" methods that you can use
| to assert different things. Of course, you may extend the Expectation API at any time.
|
*/
expect()->extend('toBeOne', function () {
return $this->toBe(1);
});
/*
|--------------------------------------------------------------------------
| Functions
|--------------------------------------------------------------------------
|
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
| project that you don't want to repeat in every file. Here you can also expose helpers as
| global functions to help you to reduce the number of lines of code in your test files.
|
*/
function something()
{
// ..
}

View File

@ -12,10 +12,10 @@
<directory suffix="Test.php">./tests/Feature</directory> <directory suffix="Test.php">./tests/Feature</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>
<coverage processUncoveredFiles="true"> <filter>
<include> <whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app</directory> <directory suffix=".php">./app</directory>
<directory suffix=".php">./src</directory> <directory suffix=".php">./src</directory>
</include> </whitelist>
</coverage> </filter>
</phpunit> </phpunit>

11
stubs/Lumen/Helpers.php Normal file
View 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
View File

@ -0,0 +1,3 @@
<?php
uses(TestCase::class)->in(__DIR__);

17
stubs/Lumen/phpunit.xml Normal file
View 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>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -1,5 +0,0 @@
Pest Options:
--init Initialise a standard Pest configuration
--coverage Enable coverage and output to standard output
--min=<N> Set the minimum required coverage percentage (<N>), and fail if not met
--group=<name> Only runs tests from the specified group(s)

View File

@ -40,9 +40,6 @@
✓ eager wrapped registered datasets with (1) ✓ eager wrapped registered datasets with (1)
✓ eager wrapped registered datasets with (2) ✓ eager wrapped registered datasets with (2)
✓ eager registered wrapped datasets did the job right ✓ eager registered wrapped datasets did the job right
✓ named datasets with data set "one" (1)
✓ named datasets with data set "two" (2)
✓ named 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) #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 (), true) #2
@ -52,6 +49,14 @@
✓ it creates unique test case names with ('Name 1', Pest\Plugin Object (), true) #3 ✓ it creates unique test case names with ('Name 1', Pest\Plugin Object (), true) #3
✓ it creates unique test case names - count ✓ it creates unique test case names - count
PASS Tests\Features\Depends
✓ first
✓ second
✓ depends
✓ depends with ...params
✓ depends with defined arguments
✓ depends run test only once
PASS Tests\Features\Exceptions PASS Tests\Features\Exceptions
✓ it gives access the the underlying expectException ✓ it gives access the the underlying expectException
✓ it catch exceptions ✓ it catch exceptions
@ -62,11 +67,9 @@
✓ it throws error if property do not exist ✓ it throws error if property do not exist
✓ it allows to call underlying protected/private methods ✓ it allows to call underlying protected/private methods
✓ it throws error if method do not exist ✓ it throws error if method do not exist
✓ it can forward unexpected calls to any global function
PASS Tests\Features\HigherOrderTests PASS Tests\Features\HigherOrderTests
✓ it proxies calls to object ✓ it proxies calls to object
✓ it is capable doing multiple assertions
PASS Tests\Features\It PASS Tests\Features\It
✓ it is a test ✓ it is a test
@ -80,8 +83,8 @@
✓ it has bar ✓ it has bar
PASS Tests\Features\PendingHigherOrderTests PASS Tests\Features\PendingHigherOrderTests
✓ get 'foo' → get 'bar' → expect true → toBeTrue ✓ get 'foo' → get 'bar' → assertTrue true
✓ get 'foo' → expect true → toBeTrue ✓ get 'foo' → assertTrue true
WARN Tests\Features\Skip WARN Tests\Features\Skip
✓ it do not skips ✓ it do not skips
@ -91,7 +94,6 @@
- it skips with truthy closure condition - it skips with truthy closure condition
✓ it do not skips with falsy closure condition ✓ it do not skips with falsy closure condition
- it skips with condition and message → skipped because foo - it skips with condition and message → skipped because foo
- it skips when skip after assertion
PASS Tests\Features\Test PASS Tests\Features\Test
✓ a test ✓ a test
@ -133,12 +135,6 @@
✓ it throws exception when `process isolation` is true ✓ it throws exception when `process isolation` is true
✓ it do not throws exception when `process isolation` is false ✓ it do not throws exception when `process isolation` is false
PASS Tests\Unit\Console\Help
✓ it outputs the help information when --help is used
PASS Tests\Unit\Datasets
✓ it show the names of named datasets in their description
PASS Tests\Unit\Plugins\Version PASS Tests\Unit\Plugins\Version
✓ it outputs the version when --version is used ✓ it outputs the version when --version is used
✓ it do not outputs version when --version is not used ✓ it do not outputs version when --version is not used
@ -162,9 +158,6 @@
PASS Tests\Unit\TestSuite PASS Tests\Unit\TestSuite
✓ it does not allow to add the same test description twice ✓ it does not allow to add the same test description twice
PASS Tests\Visual\Help
✓ visual snapshot of help command output
PASS Tests\Visual\SingleTestOrDirectory PASS Tests\Visual\SingleTestOrDirectory
✓ allows to run a single test ✓ allows to run a single test
✓ allows to run a directory ✓ allows to run a directory
@ -174,19 +167,5 @@
WARN Tests\Visual\Success WARN Tests\Visual\Success
- visual snapshot of test suite on success - visual snapshot of test suite on success
PASS Tests\Features\Depends Tests: 6 skipped, 96 passed
✓ first Time: 3.43s
✓ second
✓ it asserts true is true
✓ depends
✓ depends with ...params
✓ depends with defined arguments
✓ depends run test only once
✓ depends works with the correct test name
PASS Tests\Features\DependsInheritance
✓ it is a test
✓ it uses correct parent class
Tests: 7 skipped, 108 passed

View File

@ -8,7 +8,7 @@ trait PluginTrait
{ {
public function assertPluginTraitGotRegistered(): void public function assertPluginTraitGotRegistered(): void
{ {
$this->assertTrue(true); assertTrue(true);
} }
} }
@ -16,14 +16,9 @@ trait SecondPluginTrait
{ {
public function assertSecondPluginTraitGotRegistered(): void public function assertSecondPluginTraitGotRegistered(): void
{ {
$this->assertTrue(true); assertTrue(true);
} }
} }
Pest\Plugin::uses(PluginTrait::class); Pest\Plugin::uses(PluginTrait::class);
Pest\Plugin::uses(SecondPluginTrait::class); Pest\Plugin::uses(SecondPluginTrait::class);
function _assertThat()
{
expect(true)->toBeTrue();
}

View File

@ -8,8 +8,8 @@ afterAll(function () use ($file) {
test('deletes file after all', function () use ($file) { test('deletes file after all', function () use ($file) {
file_put_contents($file, 'foo'); file_put_contents($file, 'foo');
$this->assertFileExists($file); assertFileExists($file);
register_shutdown_function(function () use ($file) { register_shutdown_function(function () use ($file) {
$this->assertFileNotExists($file); assertFileNotExists($file);
}); });
}); });

View File

@ -6,15 +6,15 @@ beforeEach(function () use ($state) {
$this->state = $state; $this->state = $state;
}); });
afterEach(function () { afterEach(function () use ($state) {
$this->state->bar = 2; $this->state->bar = 2;
}); });
it('does not get executed before the test', function () { it('does not get executed before the test', function () {
expect($this->state)->not->toHaveProperty('bar'); assertFalse(property_exists($this->state, 'bar'));
}); });
it('gets executed after the test', function () { it('gets executed after the test', function () {
expect($this->state)->toHaveProperty('bar'); assertTrue(property_exists($this->state, 'bar'));
expect($this->state->bar)->toBe(2); assertEquals(2, $this->state->bar);
}); });

View File

@ -8,11 +8,11 @@ beforeAll(function () use ($foo) {
}); });
it('gets executed before tests', function () use ($foo) { it('gets executed before tests', function () use ($foo) {
expect($foo->bar)->toBe(1); assertEquals($foo->bar, 1);
$foo->bar = 'changed'; $foo->bar = 'changed';
}); });
it('do not get executed before each test', function () use ($foo) { it('do not get executed before each test', function () use ($foo) {
expect($foo->bar)->toBe('changed'); assertEquals($foo->bar, 'changed');
}); });

View File

@ -5,11 +5,11 @@ beforeEach(function () {
}); });
it('gets executed before each test', function () { it('gets executed before each test', function () {
expect($this->bar)->toBe(2); assertEquals($this->bar, 2);
$this->bar = 'changed'; $this->bar = 'changed';
}); });
it('gets executed before each test once again', function () { it('gets executed before each test once again', function () {
expect($this->bar)->toBe(2); assertEquals($this->bar, 2);
}); });

View File

@ -23,13 +23,13 @@ it('sets closures', function () {
yield [1]; yield [1];
}); });
expect(iterator_to_array(Datasets::get('foo')()))->toBe([[1]]); assertEquals([[1]], iterator_to_array(Datasets::get('foo')()));
}); });
it('sets arrays', function () { it('sets arrays', function () {
Datasets::set('bar', [[2]]); Datasets::set('bar', [[2]]);
expect(Datasets::get('bar'))->toBe([[2]]); assertEquals([[2]], Datasets::get('bar'));
}); });
it('gets bound to test case object', function () { it('gets bound to test case object', function () {
@ -37,7 +37,7 @@ it('gets bound to test case object', function () {
})->with([['a'], ['b']]); })->with([['a'], ['b']]);
test('it truncates the description', function () { test('it truncates the description', function () {
expect(true)->toBe(true); assertTrue(true);
// it gets tested by the integration test // it gets tested by the integration test
})->with([str_repeat('Fooo', 10000000)]); })->with([str_repeat('Fooo', 10000000)]);
@ -48,63 +48,51 @@ $datasets = [[1], [2]];
test('lazy datasets', function ($text) use ($state, $datasets) { test('lazy datasets', function ($text) use ($state, $datasets) {
$state->text .= $text; $state->text .= $text;
expect(in_array([$text], $datasets))->toBe(true); assertTrue(in_array([$text], $datasets));
})->with($datasets); })->with($datasets);
test('lazy datasets did the job right', function () use ($state) { test('lazy datasets did the job right', function () use ($state) {
expect($state->text)->toBe('12'); assertEquals('12', $state->text);
}); });
$state->text = ''; $state->text = '';
test('eager datasets', function ($text) use ($state, $datasets) { test('eager datasets', function ($text) use ($state, $datasets) {
$state->text .= $text; $state->text .= $text;
expect($datasets)->toContain([$text]); assertTrue(in_array([$text], $datasets));
})->with(function () use ($datasets) { })->with(function () use ($datasets) {
return $datasets; return $datasets;
}); });
test('eager datasets did the job right', function () use ($state) { test('eager datasets did the job right', function () use ($state) {
expect($state->text)->toBe('1212'); assertEquals('1212', $state->text);
}); });
test('lazy registered datasets', function ($text) use ($state, $datasets) { test('lazy registered datasets', function ($text) use ($state, $datasets) {
$state->text .= $text; $state->text .= $text;
expect($datasets)->toContain([$text]); assertTrue(in_array([$text], $datasets));
})->with('numbers.array'); })->with('numbers.array');
test('lazy registered datasets did the job right', function () use ($state) { test('lazy registered datasets did the job right', function () use ($state) {
expect($state->text)->toBe('121212'); assertEquals('121212', $state->text);
}); });
test('eager registered datasets', function ($text) use ($state, $datasets) { test('eager registered datasets', function ($text) use ($state, $datasets) {
$state->text .= $text; $state->text .= $text;
expect($datasets)->toContain([$text]); assertTrue(in_array([$text], $datasets));
})->with('numbers.closure'); })->with('numbers.closure');
test('eager registered datasets did the job right', function () use ($state) { test('eager registered datasets did the job right', function () use ($state) {
expect($state->text)->toBe('12121212'); assertEquals('12121212', $state->text);
}); });
test('eager wrapped registered datasets', function ($text) use ($state, $datasets) { test('eager wrapped registered datasets', function ($text) use ($state, $datasets) {
$state->text .= $text; $state->text .= $text;
expect($datasets)->toContain([$text]); assertTrue(in_array([$text], $datasets));
})->with('numbers.closure.wrapped'); })->with('numbers.closure.wrapped');
test('eager registered wrapped datasets did the job right', function () use ($state) { test('eager registered wrapped datasets did the job right', function () use ($state) {
expect($state->text)->toBe('1212121212'); assertEquals('1212121212', $state->text);
});
test('named datasets', function ($text) use ($state, $datasets) {
$state->text .= $text;
expect($datasets)->toContain([$text]);
})->with([
'one' => [1],
'two' => [2],
]);
test('named datasets did the job right', function () use ($state) {
expect($state->text)->toBe('121212121212');
}); });
class Bar class Bar
@ -116,14 +104,14 @@ $namedDatasets = [
new Bar(), new Bar(),
]; ];
test('lazy named datasets', function ($text) { test('lazy named datasets', function ($text) use ($state, $datasets) {
expect(true)->toBeTrue(); assertTrue(true);
})->with($namedDatasets); })->with($namedDatasets);
$counter = 0; $counter = 0;
it('creates unique test case names', function (string $name, Plugin $plugin, bool $bool) use (&$counter) { it('creates unique test case names', function (string $name, Plugin $plugin, bool $bool) use (&$counter) {
expect(true)->toBeTrue(); assertTrue(true);
$counter++; $counter++;
})->with([ })->with([
['Name 1', new Plugin(), true], ['Name 1', new Plugin(), true],
@ -135,5 +123,5 @@ it('creates unique test case names', function (string $name, Plugin $plugin, boo
]); ]);
it('creates unique test case names - count', function () use (&$counter) { it('creates unique test case names - count', function () use (&$counter) {
expect($counter)->toBe(6); assertEquals(6, $counter);
}); });

View File

@ -3,36 +3,38 @@
$runCounter = 0; $runCounter = 0;
test('first', function () use (&$runCounter) { test('first', function () use (&$runCounter) {
expect(true)->toBeTrue(); assertTrue(true);
$runCounter++; $runCounter++;
return 'first'; return 'first';
}); });
test('second', function () use (&$runCounter) { test('second', function () use (&$runCounter) {
expect(true)->toBeTrue(); assertTrue(true);
$runCounter++; $runCounter++;
return 'second'; return 'second';
}); });
test('depends', function () { test('depends', function () {
expect(func_get_args())->toBe(['first', 'second']); assertEquals(
['first', 'second'],
func_get_args()
);
})->depends('first', 'second'); })->depends('first', 'second');
test('depends with ...params', function (string ...$params) { test('depends with ...params', function (string ...$params) {
expect(func_get_args())->toBe($params); assertEquals(
['first', 'second'],
$params
);
})->depends('first', 'second'); })->depends('first', 'second');
test('depends with defined arguments', function (string $first, string $second) { test('depends with defined arguments', function (string $first, string $second) {
expect($first)->toBe('first'); assertEquals('first', $first);
expect($second)->toBe('second'); assertEquals('second', $second);
})->depends('first', 'second'); })->depends('first', 'second');
test('depends run test only once', function () use (&$runCounter) { test('depends run test only once', function () use (&$runCounter) {
expect($runCounter)->toBe(2); assertEquals(2, $runCounter);
})->depends('first', 'second'); })->depends('first', 'second');
// Regression tests. See https://github.com/pestphp/pest/pull/216
it('asserts true is true')->assertTrue(true);
test('depends works with the correct test name')->assertTrue(true)->depends('it asserts true is true');

View File

@ -1,22 +0,0 @@
<?php
use PHPUnit\Framework\TestCase;
class InheritanceTest extends TestCase
{
public function foo()
{
return 'bar';
}
}
uses(InheritanceTest::class);
it('is a test', function () {
expect(true)->toBeTrue();
});
it('uses correct parent class', function () {
expect(get_parent_class($this))->toEqual(InheritanceTest::class);
expect($this->foo())->toEqual('bar');
})->depends('it is a test');

View File

@ -7,7 +7,7 @@ function addUser()
it('can set/get properties on $this', function () { it('can set/get properties on $this', function () {
addUser(); addUser();
expect($this->user)->toBe('nuno'); assertEquals('nuno', $this->user);
}); });
it('throws error if property do not exist', function () { it('throws error if property do not exist', function () {
@ -27,18 +27,17 @@ function mockUser()
$mock = test()->createMock(User::class); $mock = test()->createMock(User::class);
$mock->method('getName') $mock->method('getName')
->willReturn('maduro'); ->willReturn('maduro');
return $mock; return $mock;
} }
it('allows to call underlying protected/private methods', function () { it('allows to call underlying protected/private methods', function () {
$user = mockUser(); $user = mockUser();
expect($user->getName())->toBe('maduro');
assertEquals('maduro', $user->getName());
}); });
it('throws error if method do not exist', function () { it('throws error if method do not exist', function () {
test()->name(); test()->name();
})->throws(\ReflectionException::class, 'Call to undefined method PHPUnit\Framework\TestCase::name()'); })->throws(\ReflectionException::class, 'Call to undefined method PHPUnit\Framework\TestCase::name()');
it('can forward unexpected calls to any global function')->_assertThat();

View File

@ -4,8 +4,4 @@ beforeEach()->assertTrue(true);
it('proxies calls to object')->assertTrue(true); it('proxies calls to object')->assertTrue(true);
it('is capable doing multiple assertions')
->assertTrue(true)
->assertFalse(false);
afterEach()->assertTrue(true); afterEach()->assertTrue(true);

View File

@ -1,7 +1,7 @@
<?php <?php
it('is a test', function () { it('is a test', function () {
$this->assertArrayHasKey('key', ['key' => 'foo']); assertArrayHasKey('key', ['key' => 'foo']);
}); });
it('is a higher order message test')->expect(true)->toBeTrue(); it('is a higher order message test')->assertTrue(true);

View File

@ -6,7 +6,7 @@ use PHPUnit\Framework\TestCase;
uses(Macroable::class); uses(Macroable::class);
beforeEach()->macro('bar', function () { beforeEach()->macro('bar', function () {
expect($this)->toBeInstanceOf(TestCase::class); assertInstanceOf(TestCase::class, $this);
}); });
it('can call chained macro method')->bar(); it('can call chained macro method')->bar();

View File

@ -1,12 +1,11 @@
<?php <?php
use Pest\PendingObjects\TestCall;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
uses(Gettable::class); uses(Gettable::class);
/** /**
* @return TestCase|TestCall|Gettable * @return TestCase|Gettable
*/ */
function get(string $route) function get(string $route)
{ {
@ -16,15 +15,15 @@ function get(string $route)
trait Gettable trait Gettable
{ {
/** /**
* @return TestCase|TestCall|Gettable * @return TestCase|Gettable
*/ */
public function get(string $route) public function get(string $route)
{ {
expect($route)->not->toBeEmpty(); assertNotEmpty($route);
return $this; return $this;
} }
} }
get('foo')->get('bar')->expect(true)->toBeTrue(); get('foo')->get('bar')->assertTrue(true);
get('foo')->expect(true)->toBeTrue(); get('foo')->assertTrue(true);

View File

@ -27,7 +27,3 @@ it('do not skips with falsy closure condition')
it('skips with condition and message') it('skips with condition and message')
->skip(true, 'skipped because foo') ->skip(true, 'skipped because foo')
->assertTrue(false); ->assertTrue(false);
it('skips when skip after assertion')
->assertTrue(true)
->skip();

View File

@ -1,7 +1,7 @@
<?php <?php
test('a test', function () { test('a test', function () {
$this->assertArrayHasKey('key', ['key' => 'foo']); assertArrayHasKey('key', ['key' => 'foo']);
}); });
test('higher order message test')->expect(true)->toBeTrue(); test('higher order message test')->assertTrue(true);

View File

@ -1,7 +1,5 @@
<?php <?php
use function PHPUnit\Framework\assertFalse;
$foo = new stdClass(); $foo = new stdClass();
$foo->beforeAll = false; $foo->beforeAll = false;
$foo->beforeEach = false; $foo->beforeEach = false;

View File

@ -10,6 +10,6 @@ class CustomTestCaseInSubFolder extends TestCase
{ {
public function assertCustomInSubFolderTrue() public function assertCustomInSubFolderTrue()
{ {
$this->assertTrue(true); assertTrue(true);
} }
} }

View File

@ -12,7 +12,7 @@ class MyCustomClass extends PHPUnit\Framework\TestCase
{ {
public function assertTrueIsTrue() public function assertTrueIsTrue()
{ {
$this->assertTrue(true); assertTrue(true);
} }
} }

View File

@ -1,5 +1,5 @@
<?php <?php
test('basic', function () { test('basic', function () {
expect(true)->toBeTrue(); assertTrue(true);
}); });

View File

@ -7,14 +7,14 @@ use PHPUnit\TextUI\DefaultResultPrinter;
it('sets defaults', function () { it('sets defaults', function () {
$arguments = AddsDefaults::to(['bar' => 'foo']); $arguments = AddsDefaults::to(['bar' => 'foo']);
expect($arguments['printer'])->toBeInstanceOf(Printer::class); assertInstanceOf(Printer::class, $arguments['printer']);
expect($arguments['bar'])->toBe('foo'); assertEquals($arguments['bar'], 'foo');
}); });
it('does not override options', function () { it('does not override options', function () {
$defaultResultPrinter = new DefaultResultPrinter(); $defaultResultPrinter = new DefaultResultPrinter();
expect(AddsDefaults::to(['printer' => $defaultResultPrinter]))->tobe([ assertEquals(AddsDefaults::to(['printer' => $defaultResultPrinter]), [
'printer' => $defaultResultPrinter, 'printer' => $defaultResultPrinter,
]); ]);
}); });

View File

@ -16,17 +16,17 @@ test('default php unit tests', function () {
$phpUnitTestCase = new class() extends PhpUnitTestCase { $phpUnitTestCase = new class() extends PhpUnitTestCase {
}; };
$testSuite->addTest($phpUnitTestCase); $testSuite->addTest($phpUnitTestCase);
expect($testSuite->tests())->toHaveCount(1); assertCount(1, $testSuite->tests());
AddsTests::to($testSuite, new \Pest\TestSuite(getcwd())); AddsTests::to($testSuite, new \Pest\TestSuite(getcwd()));
expect($testSuite->tests())->toHaveCount(1); assertCount(1, $testSuite->tests());
}); });
it('removes warnings', function () { it('removes warnings', function () use ($pestTestCase) {
$testSuite = new TestSuite(); $testSuite = new TestSuite();
$warningTestCase = new WarningTestCase('No tests found in class "Pest\TestCase".'); $warningTestCase = new WarningTestCase('No tests found in class "Pest\TestCase".');
$testSuite->addTest($warningTestCase); $testSuite->addTest($warningTestCase);
AddsTests::to($testSuite, new \Pest\TestSuite(getcwd())); AddsTests::to($testSuite, new \Pest\TestSuite(getcwd()));
expect($testSuite->tests())->toHaveCount(0); assertCount(0, $testSuite->tests());
}); });

View File

@ -38,5 +38,5 @@ it('do not throws exception when `process isolation` is false', function () {
'configuration' => $filename, 'configuration' => $filename,
]); ]);
expect(true)->toBeTrue(); assertTrue(true);
}); });

View File

@ -1,12 +0,0 @@
<?php
use Pest\Console\Help;
use Symfony\Component\Console\Output\BufferedOutput;
it('outputs the help information when --help is used', function () {
$output = new BufferedOutput();
$plugin = new Help($output);
$plugin();
expect($output->fetch())->toContain('Pest Options:');
});

View File

@ -1,13 +0,0 @@
<?php
use Pest\Datasets;
it('show the names of named datasets in their description', function () {
$descriptions = array_keys(Datasets::resolve('test description', [
'one' => [1],
'two' => [[2]],
]));
expect($descriptions[0])->toBe('test description with data set "one" (1)');
expect($descriptions[1])->toBe('test description with data set "two" (array(2))');
});

View File

@ -1,7 +1,6 @@
<?php <?php
use Pest\Plugins\Version; use Pest\Plugins\Version;
use function Pest\version;
use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\BufferedOutput;
it('outputs the version when --version is used', function () { it('outputs the version when --version is used', function () {
@ -9,7 +8,7 @@ it('outputs the version when --version is used', function () {
$plugin = new Version($output); $plugin = new Version($output);
$plugin->handleArguments(['foo', '--version']); $plugin->handleArguments(['foo', '--version']);
expect($output->fetch())->toContain('Pest ' . version()); assertStringContainsString('Pest 0.2.2', $output->fetch());
}); });
it('do not outputs version when --version is not used', function () { it('do not outputs version when --version is not used', function () {
@ -17,5 +16,5 @@ it('do not outputs version when --version is not used', function () {
$plugin = new Version($output); $plugin = new Version($output);
$plugin->handleArguments(['foo', 'bar']); $plugin->handleArguments(['foo', 'bar']);
expect($output->fetch())->toBe(''); assertEquals('', $output->fetch());
}); });

View File

@ -7,5 +7,5 @@ it('gets file name from called file', function () {
return Backtrace::file(); return Backtrace::file();
}; };
expect($a())->toBe(__FILE__); assertEquals(__FILE__, $a());
}); });

View File

@ -15,32 +15,32 @@ it('exists')
it('gets an instance', function () { it('gets an instance', function () {
$this->container->add(Container::class, $this->container); $this->container->add(Container::class, $this->container);
expect($this->container->get(Container::class))->toBe($this->container); assertSame($this->container, $this->container->get(Container::class));
}); });
test('autowire', function () { test('autowire', function () {
expect($this->container->get(Container::class))->toBeInstanceOf(Container::class); assertInstanceOf(Container::class, $this->container->get(Container::class));
}); });
it('creates an instance and resolves parameters', function () { it('creates an instance and resolves parameters', function () {
$this->container->add(Container::class, $this->container); $this->container->add(Container::class, $this->container);
$instance = $this->container->get(ClassWithDependency::class); $instance = $this->container->get(ClassWithDependency::class);
expect($instance)->toBeInstanceOf(ClassWithDependency::class); assertInstanceOf(ClassWithDependency::class, $instance);
}); });
it('creates an instance and resolves also sub parameters', function () { it('creates an instance and resolves also sub parameters', function () {
$this->container->add(Container::class, $this->container); $this->container->add(Container::class, $this->container);
$instance = $this->container->get(ClassWithSubDependency::class); $instance = $this->container->get(ClassWithSubDependency::class);
expect($instance)->toBeInstanceOf(ClassWithSubDependency::class); assertInstanceOf(ClassWithSubDependency::class, $instance);
}); });
it('can resolve builtin value types', function () { it('can resolve builtin value types', function () {
$this->container->add('rootPath', getcwd()); $this->container->add('rootPath', getcwd());
$instance = $this->container->get(TestSuite::class); $instance = $this->container->get(TestSuite::class);
expect($instance)->toBeInstanceOf(TestSuite::class); assertInstanceOf(TestSuite::class, $instance);
}); });
it('cannot resolve a parameter without type', function () { it('cannot resolve a parameter without type', function () {

View File

@ -3,10 +3,9 @@
use Pest\Support\Reflection; use Pest\Support\Reflection;
it('gets file name from closure', function () { it('gets file name from closure', function () {
$fileName = Reflection::getFileNameFromClosure(function () { $fileName = Reflection::getFileNameFromClosure(function () {});
});
expect($fileName)->toBe(__FILE__); assertEquals(__FILE__, $fileName);
}); });
it('gets property values', function () { it('gets property values', function () {
@ -16,5 +15,5 @@ it('gets property values', function () {
$value = Reflection::getPropertyValue($class, 'foo'); $value = Reflection::getPropertyValue($class, 'foo');
expect($value)->toBe('bar'); assertEquals('bar', $value);
}); });

View File

@ -1,27 +0,0 @@
<?php
use Pest\Console\Help;
use Symfony\Component\Console\Output\BufferedOutput;
test('visual snapshot of help command output', function () {
$snapshot = __DIR__ . '/../.snapshots/help-command.txt';
if (getenv('REBUILD_SNAPSHOTS')) {
$outputBuffer = new BufferedOutput();
$plugin = new Help($outputBuffer);
$plugin();
file_put_contents($snapshot, $outputBuffer->fetch());
}
$output = function () {
$process = (new Symfony\Component\Process\Process(['php', 'bin/pest', '--help']));
$process->run();
return preg_replace('#\\x1b[[][^A-Za-z]*[A-Za-z]#', '', $process->getOutput());
};
expect($output())->toContain(file_get_contents($snapshot));
})->skip(PHP_OS_FAMILY === 'Windows');

View File

@ -10,7 +10,7 @@ $run = function (string $target, $decorated = false) {
return $decorated ? $process->getOutput() : preg_replace('#\\x1b[[][^A-Za-z]*[A-Za-z]#', '', $process->getOutput()); return $decorated ? $process->getOutput() : preg_replace('#\\x1b[[][^A-Za-z]*[A-Za-z]#', '', $process->getOutput());
}; };
$snapshot = function ($name) { $snapshot = function ($name) {
$testsPath = dirname(__DIR__); $testsPath = dirname(__DIR__);
return file_get_contents(implode(DIRECTORY_SEPARATOR, [ return file_get_contents(implode(DIRECTORY_SEPARATOR, [
@ -21,15 +21,23 @@ $snapshot = function ($name) {
}; };
test('allows to run a single test', function () use ($run, $snapshot) { test('allows to run a single test', function () use ($run, $snapshot) {
expect($run('tests/Fixtures/DirectoryWithTests/ExampleTest.php'))->toContain($snapshot('allows-to-run-a-single-test')); assertStringContainsString(
$snapshot('allows-to-run-a-single-test'),
$run('tests/Fixtures/DirectoryWithTests/ExampleTest.php'));
})->skip(PHP_OS_FAMILY === 'Windows'); })->skip(PHP_OS_FAMILY === 'Windows');
test('allows to run a directory', function () use ($run, $snapshot) { test('allows to run a directory', function () use ($run, $snapshot) {
expect($run('tests/Fixtures'))->toContain($snapshot('allows-to-run-a-directory')); assertStringContainsString(
$snapshot('allows-to-run-a-directory'),
$run('tests/Fixtures')
);
})->skip(PHP_OS_FAMILY === 'Windows'); })->skip(PHP_OS_FAMILY === 'Windows');
it('has ascii chars', function () use ($run, $snapshot) { it('has ascii chars', function () use ($run, $snapshot) {
expect($run('tests/Fixtures/DirectoryWithTests/ExampleTest.php', true))->toContain($snapshot('has-ascii-chars')); assertStringContainsString(
$snapshot('has-ascii-chars'),
$run('tests/Fixtures/DirectoryWithTests/ExampleTest.php', true)
);
})->skip(PHP_OS_FAMILY === 'Windows'); })->skip(PHP_OS_FAMILY === 'Windows');
it('disable decorating printer when colors is set to never', function () use ($snapshot) { it('disable decorating printer when colors is set to never', function () use ($snapshot) {
@ -41,5 +49,9 @@ it('disable decorating printer when colors is set to never', function () use ($s
], dirname(__DIR__, 2)); ], dirname(__DIR__, 2));
$process->run(); $process->run();
$output = $process->getOutput(); $output = $process->getOutput();
expect($output)->toContain($snapshot('disable-decorating-printer'));
assertStringContainsString(
$snapshot('disable-decorating-printer'),
$output
);
})->skip(PHP_OS_FAMILY === 'Windows'); })->skip(PHP_OS_FAMILY === 'Windows');

View File

@ -17,16 +17,12 @@ test('visual snapshot of test suite on success', function () {
}; };
if (getenv('REBUILD_SNAPSHOTS')) { if (getenv('REBUILD_SNAPSHOTS')) {
// Strip time from end of snapshot file_put_contents($snapshot, $output());
$outputContent = preg_replace('/Time\: \s+\d+\.\d+s\s+/m', '', $output());
file_put_contents($snapshot, $outputContent);
} elseif (!getenv('EXCLUDE')) { } elseif (!getenv('EXCLUDE')) {
$output = explode("\n", $output()); $output = explode("\n", $output());
array_pop($output); array_pop($output);
array_pop($output); array_pop($output);
assertStringContainsString(implode("\n", $output), file_get_contents($snapshot));
expect(implode("\n", $output))->toContain(file_get_contents($snapshot));
} }
})->skip(!getenv('REBUILD_SNAPSHOTS') && getenv('EXCLUDE')) })->skip(!getenv('REBUILD_SNAPSHOTS') && getenv('EXCLUDE'))
->skip(PHP_OS_FAMILY === 'Windows'); ->skip(PHP_OS_FAMILY === 'Windows');