Compare commits

...

292 Commits

Author SHA1 Message Date
nuno maduro fb9332e444 chore: scope GITHUB_TOKEN to contents: read in workflows 2026-05-13 02:29:45 +01:00
nuno maduro 451e0f92a5 chore: scope GITHUB_TOKEN to contents: read in workflows 2026-05-13 02:29:44 +01:00
nuno maduro 3db72651e0 chore: scope GITHUB_TOKEN to contents: read in workflows 2026-05-13 02:29:43 +01:00
nuno maduro 20e75b97a7 chore: pin GitHub Actions to commit SHAs (#1696)
* chore: pin GitHub Actions to commit SHAs

* chore: pin GitHub Actions to commit SHAs

* chore: pin GitHub Actions to commit SHAs
2026-05-12 02:11:19 +01:00
AJ Meireles 2848059a77 introducing new proposal of the PR template 2023-08-17 18:50:42 -03:00
Nuno Maduro 5c56ad8772 release: v1.23.1 2023-07-12 20:42:47 +01:00
Nuno Maduro f05fbd4d34 chore: bumps phpunit 2023-07-12 20:42:08 +01:00
Nuno Maduro 061c9de301 release: v1.23.0 2023-04-19 21:10:22 +01:00
Nuno Maduro cda82d6772 chore: bumps dependencies 2023-04-19 21:09:37 +01:00
Nuno Maduro 9f6a882327 release: v1.22.6 2023-03-17 23:24:14 +00:00
Nuno Maduro fa7f930d50 chore: upgrades dependencies 2023-03-17 23:22:35 +00:00
Nuno Maduro 84bc028796 chore: upgrades dependencies 2023-03-17 23:21:15 +00:00
Nuno Maduro 7f0e83671b Merge pull request #690 from localheinz/fix/typo
Fix: Typo
2023-03-16 12:10:21 +00:00
Andreas Möller 5b9198dad9 Fix: Typo 2023-03-16 10:18:13 +01:00
Nuno Maduro a1a0f049cd release: v1.22.5 2023-03-03 10:16:59 +00:00
Nuno Maduro a7e231f64f chore: requires latest version of phpunit 2023-03-03 10:16:21 +00:00
Nuno Maduro 1856c15e67 release: v1.22.4 2023-02-03 13:05:47 +00:00
Nuno Maduro ee4ff5a909 chore: bumps dependencies 2023-02-03 13:03:07 +00:00
Nuno Maduro 7de10d52a7 feat: include 'RefreshDatabase' on uses 2023-02-03 12:56:42 +00:00
Nuno Maduro 660df6d8d1 chore: bump phpunit 2023-02-03 12:55:06 +00:00
Nuno Maduro d8666c8abf chore: coding style changes 2023-02-03 12:55:01 +00:00
Nuno Maduro b8ac4968ee chore: style changes 2023-01-01 20:27:38 +00:00
Nuno Maduro 78b3c41825 chore: bumps phpunit 2022-12-12 13:45:28 +00:00
Nuno Maduro b58a020423 release: v1.22.3 2022-12-07 14:31:55 +00:00
Nuno Maduro e337a52cda Merge pull request #612 from alexmanase/1.x-fix-ignored-description-for-layz-datasets
[1.x] Fixes ignored dataset description only for string description
2022-12-07 14:23:55 +00:00
Alex Manase 0a6873d8a6 wip 2022-11-17 16:14:03 +02:00
Alex Manase 09b7ab9a42 fixes ignored dataset description for string description 2022-11-17 15:43:30 +02:00
Nuno Maduro 339414e348 release: v1.22.2 2022-11-09 21:10:57 +00:00
Nuno Maduro bb56d32643 chore: fixes coding style issues 2022-11-09 21:08:19 +00:00
Nuno Maduro acd6bb2234 Merge pull request #602 from alexmanase/fix/iterator-to-array
[1.x] Fix storing of lazy datasets into internal array
2022-11-09 20:59:19 +00:00
Nuno Maduro cf93e316ff chore: bumps dependencies 2022-11-09 20:09:40 +00:00
Alex Manase 47cc88a6ab add tests for eager registered wrapped datasets with Generator functions 2022-10-30 01:35:46 +03:00
Alex Manase 45bf9e8650 ignore the keys returned by the datasets Generator for storing them into an array 2022-10-29 18:19:43 +03:00
Nuno Maduro 3c766c15d5 Updates dependencies 2022-09-18 14:21:38 +01:00
Fabio Ivona aa407f34d5 Merge pull request #575 from fabio-ivona/1.x-fix-tests
1.x fix tests
2022-09-12 18:21:07 +02:00
Fabio Ivona 077b5ffce0 fix tests 2022-09-12 18:18:52 +02:00
Nuno Maduro af6240b4ee release: v1.22.1 2022-08-29 11:42:13 +01:00
Nuno Maduro 78b328e970 release: v1.22.1 2022-08-29 11:42:07 +01:00
Nuno Maduro c92fa45ad7 Adds tests on PHP 8.2 2022-08-29 11:35:36 +01:00
Nuno Maduro d050ef8d54 Fixes support for PHP 8.2 2022-08-29 11:29:41 +01:00
Nuno Maduro 1c2f1facac release: v1.22.0 2022-08-28 22:32:39 +01:00
Nuno Maduro 8e695d62e9 Merge pull request #563 from fabio-ivona/1.x-dynamic-properties
[1.x] Dynamic properties handling
2022-08-25 13:22:45 +01:00
Fabio Ivona b9a301a13e ignore dynamic properties deprecation 2022-08-25 13:27:24 +02:00
Fabio Ivona 4b9da9e3c1 Merge pull request #562 from fabio-ivona/1.x-dynamic-properties
Fix tests
2022-08-25 12:50:09 +02:00
Fabio Ivona 239fceab9e lint 2022-08-25 09:46:32 +02:00
Fabio Ivona bedf033655 lint 2022-08-25 09:32:48 +02:00
Nuno Maduro 66f69617f1 release: 1.21.3 2022-05-12 20:10:25 +01:00
Nuno Maduro 5721e8c29a docs: updates release 2022-05-12 20:06:47 +01:00
Nuno Maduro 546d19fd84 chore: cs 2022-05-12 20:03:22 +01:00
Nuno Maduro c1b32b9ffb fix: debug in high order tests 2022-05-12 20:03:11 +01:00
Owen Voke 298b1e6784 fix: update branch alias for 1.x branch 2022-03-16 09:24:51 +00:00
Nuno Maduro 63f009fadf release: v1.21.2 2022-03-05 19:34:40 +00:00
Nuno Maduro 1066c2270d Merge pull request #487 from fabio-ivona/fix-throws-missing-exception
Fix throws missing exception
2022-03-04 08:57:50 +00:00
Fabio Ivona 984f237a92 phpstan 2022-03-03 15:45:41 +01:00
Fabio Ivona dccd8239dd handles toThrow exception with a "class not found" error 2022-02-18 18:01:09 +01:00
Fabio Ivona e103623ecb adds failing test 2022-02-18 18:00:55 +01:00
Nuno Maduro 542fc046d2 Merge pull request #462 from pestphp/revert-461-json_fix
Revert "Fixes `json` in Higher Order Expectations"
2022-01-21 16:48:35 +00:00
Nuno Maduro 4720e0655b Revert "Fixes json in Higher Order Expectations" 2022-01-21 16:48:26 +00:00
Nuno Maduro a533772fe2 Merge pull request #461 from pestphp/json_fix
Fixes `json` in Higher Order Expectations
2022-01-21 16:46:30 +00:00
Luke Downing 2218a0c137 Tweaks 2022-01-21 16:39:10 +00:00
Luke Downing 2831629f1b Fixes bug with json method call in Higher Order Expectations. 2022-01-21 16:35:21 +00:00
Owen Voke 1b7f1dc5b3 chore(deps): update nunomaduro/collision to ^5.10
This ensures that the minimum dependencies work with PHP 8.1
2021-12-13 20:57:27 +00:00
Luke Downing 92b8d32ef7 release: v1.21.1 2021-11-25 16:44:17 +00:00
Nuno Maduro 2969c7a5e3 Merge pull request #442 from pestphp/fix_for_sequence
[1.x] Fix for sequence
2021-11-25 16:40:08 +00:00
Luke Downing 63c1faa9f4 Prevents issues with callables in sequence 2021-11-25 16:37:23 +00:00
Nuno Maduro b5b14ef280 chore: updates changelog branch to 1.x 2021-11-17 11:03:56 +00:00
Nuno Maduro 11eb1903c2 release: v1.21.0 2021-11-17 10:54:00 +00:00
Nuno Maduro a110848f9b docs: updates changelog 2021-11-17 10:47:28 +00:00
Nuno Maduro da5c21de8f Update README.md 2021-11-09 01:42:10 +00:00
Nuno Maduro 408ae4cad8 docs: adds spatie.be as platinum sponsor 2021-11-08 18:31:48 +00:00
Nuno Maduro fc2484a28a Update README.md 2021-11-05 12:00:56 +01:00
Nuno Maduro de46ee0f64 fix: adds link to xdebug coverage mode 2021-10-10 15:33:27 +01:00
Nuno Maduro 1e011c7b40 fix: warns about xdebug modes 2021-10-10 15:25:02 +01:00
Nuno Maduro 04dcebf3aa Revert "Merge pull request #413 from def-studio/fix_skipping_tests_with_exception_asserting"
This reverts commit e853792a59, reversing
changes made to 205238fcbf.
2021-10-10 14:40:07 +01:00
Nuno Maduro e853792a59 Merge pull request #413 from def-studio/fix_skipping_tests_with_exception_asserting
Fix skipping tests with exception asserting
2021-10-02 12:01:04 +01:00
Fabio Ivona b0fbe54181 removes duplicated test 2021-10-02 09:15:52 +02:00
Fabio Ivona 2a649bdfc0 revert previous solution and invert chain and proxy calls in TestCaseFactory.php 2021-10-02 09:09:47 +02:00
Fabio Ivona e042bf7d3a removes expection expectations if test is marked as skipped 2021-10-01 15:52:45 +02:00
Fabio Ivona 3ff71a4563 adds failing test 2021-10-01 15:52:02 +02:00
Nuno Maduro 205238fcbf docs: updates release process 2021-09-25 13:56:06 +01:00
Nuno Maduro ba06c5a76d release: v1.20.0 2021-09-25 13:52:12 +01:00
Nuno Maduro 78ffc491e9 chore: exclude builds on PHP 8.1 on mac os or windows 2021-09-25 13:37:06 +01:00
Nuno Maduro 7f38de11b7 refactor: --ci option 2021-09-25 13:29:11 +01:00
Nuno Maduro a6e34d204c Merge pull request #405 from def-studio/add-new-ci-option-to-pest-bin
Add new --ci option to pest bin
2021-09-25 13:11:05 +01:00
Nuno Maduro 66d47e4922 fix: usage on PHP 7.3 2021-09-25 09:39:17 +01:00
Fabio Ivona 7d70b6e95a merge from master 2021-09-25 09:04:26 +02:00
Fabio Ivona 076dcab4c5 Merge remote-tracking branch 'origin/master' into add-new-ci-option-to-pest-bin
# Conflicts:
#	tests/.snapshots/success.txt
2021-09-25 09:04:07 +02:00
Fabio Ivona 6f42e336c9 merge conflict 2021-09-25 08:57:55 +02:00
Fabio Ivona 0d72b5197c merge conflict 2021-09-25 08:55:57 +02:00
Nuno Maduro 7691e3c602 Merge pull request #399 from mertasan/sequence
Fix: `sequence()` can generate false positives
2021-09-24 22:15:49 +01:00
Nuno Maduro b43a59868d feat: adds unless expectation 2021-09-24 22:12:08 +01:00
Nuno Maduro 457972716f refactor: expectation match method 2021-09-24 22:02:18 +01:00
Nuno Maduro ae029660e3 tests: fixes snapshots 2021-09-24 21:18:30 +01:00
Nuno Maduro dc12419078 Merge pull request #407 from mertasan/method-match
Adds new `match()` method
2021-09-24 21:16:48 +01:00
Nuno Maduro f0ddd10a54 Merge branch 'master' into method-match 2021-09-24 21:16:43 +01:00
Nuno Maduro 4daf7ee4ab refactor: throwsIf method 2021-09-24 21:15:31 +01:00
Nuno Maduro d60f320382 Merge pull request #371 from mertasan/throwsif
Add `throwsIf` exception
2021-09-24 21:10:56 +01:00
Nuno Maduro 3c3e6b160b refactor: expectation when 2021-09-24 21:10:02 +01:00
Mert Aşan c99f8f196e lint 2021-09-24 14:42:26 +03:00
Mert Aşan 9cc4ecd5ab Merge branch 'master' into method-match 2021-09-24 14:39:21 +03:00
Mert Aşan 8d96f975e0 update snapshots 2021-09-24 14:37:01 +03:00
Mert Aşan 7f214f9e12 use and() instead of new self 2021-09-24 13:59:00 +03:00
Mert Aşan da258fa89f remove the warning 2021-09-24 13:55:16 +03:00
Nuno Maduro f23f857903 Merge pull request #406 from mertasan/method-when
Adds new `when()` method
2021-09-24 11:23:21 +01:00
Mert Aşan fec11928cf update snapshots 2021-09-23 06:57:07 +03:00
Mert Aşan f6131d042b add tests
The filename is not accepted as `match.php`
2021-09-23 06:56:47 +03:00
Mert Aşan 543b9542ae add new match() method 2021-09-23 06:55:28 +03:00
Mert Aşan 1681c1f4f8 update snapshots 2021-09-23 03:35:56 +03:00
Mert Aşan f41c3ce9ba add tests 2021-09-23 03:35:46 +03:00
Mert Aşan 847b06e558 add when() method 2021-09-23 03:35:41 +03:00
Fabio Ivona 601c4b01fc refactors to use a Plugin to parse --ci option 2021-09-22 14:53:16 +02:00
Fabio Ivona 05c1c82ae2 lint 2021-09-22 11:05:04 +02:00
Fabio Ivona 1bde49b3c4 types fix 2021-09-22 11:00:41 +02:00
Fabio Ivona b22f5e0c85 adds test for CI env runs 2021-09-22 10:38:21 +02:00
Fabio Ivona dd643faa5c adds a new --ci option to pest binary 2021-09-22 10:17:44 +02:00
Nuno Maduro facbf05016 docs: adds missing entry to changelog 2021-09-20 19:38:02 +01:00
Nuno Maduro 58cff003d8 release: v1.19.0 2021-09-20 19:30:58 +01:00
Nuno Maduro ae997e6eee No need for --ignore-platform-req=php 2021-09-20 19:28:43 +01:00
Nuno Maduro e6c7d68def Adds php-81 support 2021-09-20 19:26:47 +01:00
Mert Aşan 0b19672963 Merge branch 'master' into throwsif 2021-09-19 01:47:07 +03:00
Mert Aşan a16a19e121 update snapshots 2021-09-16 18:10:15 +03:00
Mert Aşan 3dd10b3c7c change test 2021-09-16 18:09:59 +03:00
Mert Aşan 12e63c7376 add new assertion 2021-09-16 18:09:45 +03:00
Nuno Maduro 2f0cd7a4e3 Adds Fathom Analytics as sponsor 2021-09-15 10:03:05 +01:00
Nuno Maduro 447af55e7c Merge pull request #391 from gabbanaesteban/master
Add `toHaveProperties`
2021-09-03 09:52:39 +01:00
Esteban 253e9d10c8 Fix types 2021-09-03 04:44:10 -04:00
Esteban 536ce1eca0 Update snapshots 2021-09-03 04:40:48 -04:00
Esteban 4331b2aaf6 Add toHaveProperties 2021-09-03 04:26:57 -04:00
Nuno Maduro 8d99cacc95 Update README.md 2021-09-02 23:36:02 +01:00
Nuno Maduro 3c38facc8a Update README.md 2021-09-02 23:35:51 +01:00
Nuno Maduro ed389d35d0 Adds Auth0 as sponsor 2021-09-02 23:35:40 +01:00
Nuno Maduro 60c0636523 release: v1.18.0 2021-08-30 00:05:26 +01:00
Nuno Maduro 16b6f96b47 Merge pull request #389 from dansysanalyst/toHaveLength-mblen
Use mb_strlen instead of grapheme_strlen
2021-08-29 12:18:38 +01:00
Daniel Ang 042f2ec3f3 Use mb_strlen instead of grapheme_strlen
Due to inconsistent behave, mb_strlen will be used.
2021-08-29 12:55:37 +02:00
Nuno Maduro 851ce36010 Merge pull request #386 from dansysanalyst/expectation-toHaveLength
Adds toHaveLength() Expectation & Tests
2021-08-29 00:45:39 +01:00
Daniel Ang 4f386894bd Revert "Use toHaveCount"
This reverts commit 2289adade2.
2021-08-28 18:14:55 +02:00
Daniel Ang 2289adade2 Use toHaveCount
changes to toHaveCount
2021-08-28 17:54:29 +02:00
Nuno Maduro 29e21e3814 Merge pull request #385 from dansysanalyst/snapshot-contrib
Update snapshots
2021-08-28 16:47:25 +01:00
Daniel Ang 8367af22e7 Adds toHaveLength() Expectation & Tests
Adds toHaveLength() Expectation and its tests. toHaveLength checks if the given value has the informed length. Works with strings, array, object and collections.
2021-08-28 17:02:09 +02:00
Daniel Ang e3d678dc04 Update snapshots
Adds update:snapshots to CONTRIBUTING
2021-08-28 16:30:17 +02:00
Nuno Maduro 4ae482c707 feat: adds support for nunomaduro/collision:^6.0 2021-08-27 22:32:18 +01:00
Nuno Maduro 075c31bc78 release: v1.17.0 2021-08-26 21:17:03 +01:00
Nuno Maduro 2125bf9668 chore: adjusts tests 2021-08-26 21:14:56 +01:00
Nuno Maduro dbf3c0a8cf Merge pull request #361 from kbond/throw-expectation
Add `toThrow` expectation
2021-08-26 21:02:38 +01:00
Luke Downing eca5f89e59 release: v1.16.0 2021-08-19 17:07:45 +01:00
Luke Downing 0b0beac122 Script update 2021-08-16 22:12:13 +01:00
luke 578e97123d Changes -P to -p 2021-08-13 22:09:55 +01:00
Nuno Maduro 01d672d563 Merge pull request #369 from pestphp/parallel
Parallel testing support
2021-08-13 16:00:26 +01:00
luke 490b2d66e5 Fix 2021-08-13 13:41:00 +01:00
luke 0368c4846f Adds a composer test:parallel script 2021-08-13 13:40:07 +01:00
luke 4dfc02c5da Refactor 2021-08-13 11:19:03 +01:00
luke 5c84b0c6d3 The getFilenames method now respects only calls. 2021-08-13 11:07:52 +01:00
luke b6c06e8c30 Bugfix 2021-08-13 10:46:07 +01:00
luke c6435d5606 Adds a helpful message for users trying to run parallel without the plugin 2021-08-13 10:44:11 +01:00
luke 2887d212e3 CS fix 2021-08-13 10:30:26 +01:00
luke cadae52d5d Updates snapshots 2021-08-13 10:29:21 +01:00
luke d9749ca65b Adds a method for getting all filenames 2021-08-13 10:26:38 +01:00
luke c2070cd99d Refactor 2021-08-13 10:19:10 +01:00
luke 03d34e9a10 Removes isInParallel 2021-08-13 10:14:01 +01:00
luke 45e76a6df6 Typehint updates 2021-08-13 09:41:45 +01:00
luke 28dd3c2a03 Adds -P as a parallel shortcut 2021-08-13 09:39:56 +01:00
luke 5de981d923 Updates composer.json 2021-08-13 09:12:56 +01:00
Nuno Maduro a55b31e7c3 Update FUNDING.yml 2021-08-12 21:41:42 +01:00
Nuno Maduro 5f0bd8180e Update FUNDING.yml 2021-08-12 21:41:05 +01:00
luke e1f1fcccbe Removes unneeded dependency 2021-08-11 21:25:41 +01:00
luke ab04aef561 Refactors addOutput 2021-08-11 20:59:19 +01:00
luke 79ddb1f58e Composer change 2021-08-11 15:57:49 +01:00
luke c7a2e68941 Fixes a problem with the PhpUnit logger 2021-08-10 14:57:36 +01:00
luke 5c592928d4 Adds a new method, isInParallel, to the Testable trait to allow a test to determine its parallel status. 2021-08-10 14:34:10 +01:00
luke bcab4224fb Skips a test that doesn't support parallel. 2021-08-10 14:24:34 +01:00
luke 892f70b5b5 Refactor 2021-08-10 13:17:11 +01:00
luke 5c7de5ad75 Refactor 2021-08-10 12:04:15 +01:00
luke 995088b522 Refactor 2021-08-10 11:32:43 +01:00
luke ef503646ee Removes parallel classes. 2021-08-10 11:30:48 +01:00
luke a760470e48 Adds Pest output to parallel. 2021-08-09 18:57:20 +01:00
Mert Aşan 1d4c1a5359 update snapshots 2021-08-07 08:22:31 +03:00
Mert Aşan 8e32b88fc8 add tests 2021-08-07 08:22:26 +03:00
Mert Aşan 1a7baad338 add throwsIf exception 2021-08-07 08:22:16 +03:00
luke 31d1b1b91d Adds a little more spacing above the coverage output. 2021-08-06 15:15:09 +01:00
Luke Downing 7524c80af6 Merge pull request #370 from nuernbergerA/parallel
Parallel additions
2021-08-06 15:04:51 +01:00
Adrian Nürnberger 721d5134b7 replace str_starts_with to support pre php 8.0 2021-08-06 16:00:03 +02:00
Adrian Nürnberger 0b5321fdd7 only check for coverage driver if option is present 2021-08-06 15:48:38 +02:00
Adrian Nürnberger c86058fed1 use support class 2021-08-06 15:39:19 +02:00
Adrian Nürnberger 8b295b5e9d remove debug statements 2021-08-06 15:38:59 +02:00
Adrian Nürnberger 221248e691 introduced argument mapping, added pest coverage 2021-08-06 15:25:28 +02:00
luke 7621247bb7 Adds workflows for parallel 2021-08-05 17:45:50 +01:00
luke 463a50ebd4 Investigating bug fix 2021-08-05 17:39:23 +01:00
luke 62aabc6ae1 Bugfix. The TestCase is now aware of if it is running in parallel or not 😎 2021-08-05 17:09:45 +01:00
luke 1ca9aa5ca6 Further cleanup 2021-08-05 16:43:09 +01:00
luke 7a76f8dce2 Further cleanup 2021-08-05 16:21:06 +01:00
luke beca27599c Further cleanup 2021-08-05 16:18:47 +01:00
luke 256b167eaf Improvements 2021-08-05 15:31:09 +01:00
luke 5526d4c24d Cleanup 2021-08-05 13:51:55 +01:00
luke 7ea138c640 CS fixes 2021-08-05 13:41:04 +01:00
luke c4a659c3b5 Composer changes 2021-08-05 13:16:29 +01:00
luke 0a3991c314 Initial working draft 2021-08-05 13:13:53 +01:00
Nuno Maduro d1a9e0bbe3 release: v1.15.0 2021-08-04 23:02:18 +01:00
Nuno Maduro 17eacfdf95 Update RELEASE.md 2021-08-04 23:01:40 +01:00
Nuno Maduro 9ec0762d41 tests: refactors toBeTruthy and toBeFalsy 2021-08-04 22:56:18 +01:00
Nuno Maduro 30f39f1850 Merge pull request #367 from gabbanaesteban/add-to-be-falsy-and-to-be-truthy
Add `toBeTruthy` and `ToBeFalsy`
2021-08-04 22:45:39 +01:00
Esteban 8ee07330b3 Add toBeFalsy 2021-08-03 20:55:07 -04:00
Esteban cffde4564d Add toBeTruthy 2021-08-03 20:54:56 -04:00
Nuno Maduro ce7a7649a2 chore: removes scripts folder 2021-08-03 18:59:36 +01:00
Nuno Maduro eeed7e6a0a chore: removes scripts folder 2021-08-03 18:57:41 +01:00
Nuno Maduro d6844f5239 Delete compile.php 2021-08-03 18:55:53 +01:00
Nuno Maduro 06c4019e81 cs: removes comment 2021-08-03 18:22:15 +01:00
Nuno Maduro 7785a8cc58 chore: reverts changes regarding PHP 8.1 2021-08-03 18:00:42 +01:00
Nuno Maduro 663516c1e3 Merge pull request #366 from jeroenvanrensen/patch-1
Fix typography
2021-08-03 14:32:50 +01:00
luke e83667a20b docs: update changelog 2021-08-03 14:01:53 +01:00
Luke Downing aa96d75fb9 Merge pull request #364 from pestphp/bound-datasets
Datasets can now access the test case
2021-08-03 13:58:35 +01:00
Jeroen van Rensen a5af4bc5ed Fix typography 2021-08-03 15:04:03 +03:00
luke 57161ba5ad Merges master 2021-08-02 14:03:33 +01:00
luke b1a9254fc1 Merge branch 'master' into bound-datasets
# Conflicts:
#	tests/.snapshots/success.txt
2021-08-02 14:02:49 +01:00
luke e56e818659 Splits 8.1 OS jobs 2021-08-02 13:06:41 +01:00
luke 79de0d5875 Splits 8.1 OS jobs 2021-08-02 13:05:55 +01:00
luke f6f8140ebc Splits 8.1 OS jobs 2021-08-02 13:05:29 +01:00
luke eed221af46 Adds continue-on-error for php 8.1 2021-08-02 13:03:13 +01:00
luke 524457a4e6 Adds continue-on-error for php 8.1 2021-08-02 13:00:07 +01:00
luke 8e289b7a7d Adds continue-on-error for php 8.1 2021-08-02 12:59:07 +01:00
luke 172b69cf15 Adds continue-on-error for php 8.1 2021-08-02 12:57:57 +01:00
luke 475279a4fa Adds continue-on-error for php 8.1 2021-08-02 12:56:29 +01:00
luke 4b236bf9ff docs: update changelog 2021-08-02 12:50:44 +01:00
luke 50e2d10029 docs: update changelog 2021-08-02 12:50:28 +01:00
Luke Downing fa8a57f1ab Merge pull request #350 from pestphp/teamcity-styling
Teamcity styling
2021-08-02 12:47:54 +01:00
luke 715f8b420b Updates phpunit dependency version 2021-08-02 12:41:23 +01:00
Kevin Bond c776bcf86d add toThrow expectation 2021-08-01 12:33:09 -04:00
luke a0637d86ff Refactors 2021-08-01 17:00:01 +01:00
luke 1a941d7f92 Refactors 2021-08-01 16:45:06 +01:00
luke b5959aa3fa Refactors 2021-08-01 15:42:52 +01:00
luke 8861dd2401 Merges with master 2021-08-01 15:40:34 +01:00
luke da73f4b395 Merge branch 'master' into teamcity-styling
# Conflicts:
#	tests/.snapshots/success.txt
2021-08-01 15:36:53 +01:00
luke d5097d0fe5 Merge conflicts 2021-07-31 23:28:23 +01:00
luke 022ad4be0d Merge branch 'master' into bound-datasets
# Conflicts:
#	tests/.snapshots/success.txt
2021-07-31 23:27:38 +01:00
Nuno Maduro 2d2a83e9e8 chore: run tests against php 8.1 2021-07-31 22:56:33 +01:00
Nuno Maduro 67c7bee4fa Merge pull request #365 from def-studio/expect-toContain-with-multiple-needles
implements multiple needles in expect()->toContain()
2021-07-31 20:34:33 +01:00
Fabio Ivona 675b0f1ec8 implements multiple needles in expect()->toContain() 2021-07-31 18:29:03 +02:00
luke c2b86c3ab3 Removes pest from stack traces 2021-07-30 16:34:30 +01:00
luke 46337b8085 Removes pest from stack traces 2021-07-30 16:32:00 +01:00
luke df172d8eed Adds new lines 2021-07-30 15:36:38 +01:00
luke 24b9160b79 Removes new line from event printing 2021-07-30 15:34:31 +01:00
luke a7860b0b8e CS 2021-07-30 12:41:57 +01:00
luke 7471c224fa Output improvements. 2021-07-30 12:39:20 +01:00
luke 2996135155 Output improvements. 2021-07-30 12:38:13 +01:00
luke 1abab8d440 Output improvements. 2021-07-30 12:36:52 +01:00
luke e52c83e5be Output improvements. 2021-07-30 12:35:20 +01:00
luke 5ed4545737 Output improvements. 2021-07-30 12:34:10 +01:00
luke 8b39df68ce Output improvements. 2021-07-30 12:32:27 +01:00
luke 04d8a3762b Output improvements. 2021-07-30 12:31:03 +01:00
luke 0f7c8d00d6 Output improvements. 2021-07-30 12:29:39 +01:00
luke 12fb4f8639 Output improvements. 2021-07-30 12:24:23 +01:00
luke 6a84b825e6 Output improvements. 2021-07-30 12:23:24 +01:00
luke e8595c56b3 Merge branch 'master' into teamcity-styling 2021-07-30 11:46:57 +01:00
luke 4a9fb2fa74 Snapshot update 2021-07-28 10:41:47 +01:00
luke d8fae6d689 Datasets can now access the test case and are executed after the setup method has run. 2021-07-28 10:39:39 +01:00
Nuno Maduro 252f9a0e46 Merge pull request #321 from jordanbrauer/fix-missing-dataset-errors
Add user-friendly exception message for missing test inputs
2021-07-28 09:57:39 +01:00
jordanbrauer 8d24b4a217 chore: replace prop set/check with method call 2021-07-27 23:39:26 -05:00
jordanbrauer 43920f79a9 feat: add count method for checking message type presence 2021-07-27 23:36:09 -05:00
jordanbrauer 55bfc5856b Merge branch 'master' into fix-missing-dataset-errors 2021-07-27 23:02:12 -05:00
Nuno Maduro cd9d4acbc2 release: v1.13.0 2021-07-28 02:03:24 +01:00
Nuno Maduro 2b5355419a chore: fixes types 2021-07-28 01:59:47 +01:00
Nuno Maduro 22b822ce87 fix: skip with a false condition being ignored 2021-07-28 01:57:13 +01:00
Nuno Maduro b2c298b926 Merge pull request #363 from freekmurze/add-to-be-in
Add `toBeIn` expectation
2021-07-28 00:09:54 +01:00
freek 671f3df115 fix tests 2021-07-28 01:00:19 +02:00
freek 2dd77001b7 static analysis fix 2021-07-28 00:42:54 +02:00
freek 5f574ded81 add toBeIn 2021-07-28 00:36:43 +02:00
Nuno Maduro 6309e6818d Update README.md 2021-07-26 23:19:51 +01:00
Nuno Maduro 4813ab6ffb Add sponsor 2021-07-26 23:18:55 +01:00
jordanbrauer 00b4bb6305 Merge branch 'master' into fix-missing-dataset-errors 2021-07-21 21:26:33 -05:00
luke d10281f851 Adds test cases for loggers and removes use of str_starts_with. 2021-07-19 12:59:18 +01:00
luke 05f72f9b6d Aligns test case name with test names 2021-07-16 17:59:07 +01:00
luke f9de1b9c00 Refactor 2021-07-16 17:51:12 +01:00
luke 9516e56242 Improves test case name styling 2021-07-16 17:50:18 +01:00
luke 5f315fc899 Updates warning color 2021-07-16 17:46:30 +01:00
luke e59606818d More work on output 2021-07-16 17:26:48 +01:00
luke e16104350e More work on output 2021-07-16 16:45:35 +01:00
luke df31191f4e Merge branch 'master' into teamcity-assertion-fix 2021-07-16 16:45:16 +01:00
luke 5b310f6f93 More work on output 2021-07-16 15:58:44 +01:00
luke 0200f90e9a Start of work on better TeamCity output 2021-07-16 15:06:10 +01:00
Jordan Brauer dc75b34deb refactor: move logic into boolean method in TestCaseFactory 2021-06-30 09:51:49 -05:00
Jordan Brauer 8e22803797 Merge branch 'master' into fix-missing-dataset-errors 2021-06-30 09:04:20 -05:00
Jordan Brauer 82bd836ae9 Merge branch 'master' into fix-missing-dataset-errors 2021-06-21 19:50:15 -05:00
jordanbrauer e64856c664 test: use throws method instead of assert 2021-06-17 13:33:11 -05:00
jordanbrauer de86598c0d Merge branch 'master' into fix-missing-dataset-errors 2021-06-16 21:36:42 -05:00
jordanbrauer 553b45306f feat: handle unions (PHP 8) 2021-06-16 21:29:08 -05:00
jordanbrauer 2e7192ab95 chore: static analysis adjustments - deprecation RE: ReflectionType::__toString 2021-06-16 14:42:51 -05:00
jordanbrauer e21e3080e0 test: add new exception check & update snapshots 2021-06-16 14:04:50 -05:00
jordanbrauer 6a7ee90ff5 feat: throw user-friendly exception for missing argument data 2021-06-16 01:01:34 -05:00
jordanbrauer 9d66893d5a feat: add boolean property to signal dependency proxy calls 2021-06-16 01:00:23 -05:00
jordanbrauer 64e780cf72 fix: types 2021-06-16 00:58:46 -05:00
jordanbrauer 9bf141f698 style: formatting & linting 2021-06-16 00:54:22 -05:00
jordanbrauer 9904094590 feat: add new exception for missing datasets on tests with arguments 2021-06-16 00:53:17 -05:00
jordanbrauer 9e5b779abc feat: add helper function for mapping a function's arguments 2021-06-16 00:52:31 -05:00
79 changed files with 2367 additions and 357 deletions
+13 -7
View File
@@ -1,10 +1,16 @@
| 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. - Fill in the form below correctly. This will help the Pest team to understand the PR and also work on it.
--> -->
### What:
- [ ] Bug Fix
- [ ] New Feature
### Description:
<!-- describe what your PR is solving -->
### Related:
<!-- link to the issue(s) your PR is solving. If it doesn't exist, remove the "Related" section. -->
+10 -7
View File
@@ -2,14 +2,17 @@ name: Changelog
on: on:
push: push:
branches: [ master ] branches: [ 1.x ]
paths: paths:
- CHANGELOG.md - CHANGELOG.md
- .github/workflows/changelog.yml - .github/workflows/changelog.yml
pull_request: pull_request:
branches: [ master ] branches: [ 1.x ]
paths: paths:
- CHANGELOG.md - CHANGELOG.md
permissions:
contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -17,10 +20,10 @@ jobs:
if: github.repository == 'pestphp/pest' if: github.repository == 'pestphp/pest'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2
- name: Checkout website repository - name: Checkout website repository
uses: actions/checkout@v2 uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2
with: with:
token: ${{ secrets.CHANGELOG_KEY }} token: ${{ secrets.CHANGELOG_KEY }}
repository: pestphp/docs repository: pestphp/docs
@@ -29,12 +32,12 @@ jobs:
- name: Read CHANGELOG.md - name: Read CHANGELOG.md
id: package id: package
uses: juliangruber/read-file-action@v1 uses: juliangruber/read-file-action@271ff311a4947af354c6abcd696a306553b9ec18 # 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@e19fd875ed54f16fc583a3486e62547ce4a5dde8 # v1.0
with: with:
path: ./CHANGELOG.md path: ./CHANGELOG.md
contents: | contents: |
@@ -53,7 +56,7 @@ jobs:
run: cp CHANGELOG.md pestphp-docs/changelog.md run: cp CHANGELOG.md pestphp-docs/changelog.md
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v3 uses: peter-evans/create-pull-request@18f7dc018cc2cd597073088f7c7591b9d1c02672 # v3
with: with:
token: ${{ secrets.CHANGELOG_KEY }} token: ${{ secrets.CHANGELOG_KEY }}
commit-message: Update changelog.md commit-message: Update changelog.md
+7 -4
View File
@@ -2,6 +2,9 @@ name: Static Analysis
on: ['push', 'pull_request'] on: ['push', 'pull_request']
permissions:
contents: read
jobs: jobs:
cs: cs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -10,10 +13,10 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with: with:
php-version: 8.0 php-version: 8.0
tools: composer:v2 tools: composer:v2
@@ -35,10 +38,10 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with: with:
php-version: 8.0 php-version: 8.0
tools: composer:v2 tools: composer:v2
+20 -12
View File
@@ -2,43 +2,51 @@ name: Tests
on: ['push', 'pull_request'] on: ['push', 'pull_request']
permissions:
contents: read
jobs: jobs:
ci: ci:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
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', '8.0', '8.1', '8.2']
dependency-version: [prefer-lowest, prefer-stable] dependency-version: [prefer-lowest, prefer-stable]
parallel: ['', '--parallel']
exclude:
- php: 8.1
os: macos-latest
- php: 8.1
os: windows-latest
- php: 8.2
os: macos-latest
- php: 8.2
os: windows-latest
name: PHP ${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }} name: PHP ${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }} - ${{ matrix.parallel }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
tools: composer:v2 tools: composer:v2
coverage: none coverage: none
- name: Setup Problem Matches - name: Setup Problem Matchers
run: | run: |
echo "::add-matcher::${{ runner.tool_cache }}/php.json" echo "::add-matcher::${{ runner.tool_cache }}/php.json"
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: Install PHP 7 dependencies - name: Install PHP dependencies
run: composer update --${{ matrix.dependency-version }} --no-interaction --no-progress 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 ${{ matrix.parallel }}
- name: Integration Tests - name: Integration Tests
run: php bin/pest --colors=always --group=integration run: php bin/pest --colors=always --group=integration
+10 -6
View File
@@ -3,17 +3,16 @@
$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 . 'scripts')
->in(__DIR__ . DIRECTORY_SEPARATOR . 'stubs') ->in(__DIR__ . DIRECTORY_SEPARATOR . 'stubs')
->in(__DIR__ . DIRECTORY_SEPARATOR . 'src') ->in(__DIR__ . DIRECTORY_SEPARATOR . 'src')
->append(['.php-cs-fixer.dist.php']); ->append(['.php-cs-fixer.dist.php']);
$rules = [ $rules = [
'@Symfony' => true, '@Symfony' => true,
'phpdoc_no_empty_return' => false, 'phpdoc_no_empty_return' => false,
'array_syntax' => ['syntax' => 'short'], 'array_syntax' => ['syntax' => 'short'],
'yoda_style' => false, 'yoda_style' => false,
'binary_operator_spaces' => [ 'binary_operator_spaces' => [
'operators' => [ 'operators' => [
'=>' => 'align', '=>' => 'align',
'=' => 'align', '=' => 'align',
@@ -21,6 +20,11 @@ $rules = [
], ],
'concat_space' => ['spacing' => 'one'], 'concat_space' => ['spacing' => 'one'],
'not_operator_with_space' => false, 'not_operator_with_space' => false,
'global_namespace_import' => [
'import_classes' => true,
'import_functions' => true,
'import_constants' => true,
],
]; ];
$rules['increment_style'] = ['style' => 'post']; $rules['increment_style'] = ['style' => 'post'];
+93
View File
@@ -4,6 +4,99 @@ 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.23.0 (2023-04-19)](https://github.com/pestphp/pest/compare/v1.22.6...v1.23.0)
### Changed
- Bumps dependencies
## [v1.22.6 (2023-03-17)](https://github.com/pestphp/pest/compare/v1.22.5...v1.22.6)
### Changed
- Bumps dependencies
## [v1.22.5 (2023-03-03)](https://github.com/pestphp/pest/compare/v1.22.4...v1.22.5)
### Changed
- Bumps dependencies
## [v1.22.4 (2023-02-03)](https://github.com/pestphp/pest/compare/v1.22.3...v1.22.4)
### Changed
- Bumps dependencies
## [v1.22.3 (2022-12-07)](https://github.com/pestphp/pest/compare/v1.22.2...v1.22.3)
### Fixed
- Fixes ignoring datasets description on lazy datasets ([#612](https://github.com/pestphp/pest/pull/612))
## [v1.22.2 (2022-11-09)](https://github.com/pestphp/pest/compare/v1.22.1...v1.22.2)
### Fixed
- Fixes storing lazy datasets into internal array ([#602](https://github.com/pestphp/pest/pull/602))
## [v1.22.1 (2022-08-29)](https://github.com/pestphp/pest/compare/v1.22.0...v1.22.1)
### Fixed
- Initial PHP 8.2 support ([d050ef8](https://github.com/pestphp/pest/commit/d050ef8d54ffe8dfe015adeb1188aae2cca33c33))
## [v1.22.0 (2022-08-28)](https://github.com/pestphp/pest/compare/v1.21.3...v1.22.0)
### Added
- Initial PHP 8.2 support ([#563](https://github.com/pestphp/pest/pull/563))
## [v1.21.3 (2022-05-12)](https://github.com/pestphp/pest/compare/v1.21.2...v1.21.3)
### Fixed
- Debug of high order tests ([c1b32b9](https://github.com/pestphp/pest/commit/c1b32b9ffb5134803c490592454b11b8c05ea27d))
## [v1.21.2 (2022-03-05)](https://github.com/pestphp/pest/compare/v1.21.1...v1.21.2)
### Fixed
- `toThrow` expectation when exception does not exist ([#487](https://github.com/pestphp/pest/pull/487))
## [v1.21.1 (2021-11-25)](https://github.com/pestphp/pest/compare/v1.21.0...v1.21.1)
### Fixed
- sequence callables causing problems ([#442](https://github.com/pestphp/pest/pull/442))
## [v1.21.0 (2021-11-17)](https://github.com/pestphp/pest/compare/v1.20.0...v1.21.0)
### Added
- warn about xdebug modes ([1e011c](https://github.com/pestphp/pest/commit/1e011c7b4074d08f5dabab1f927d45383c85d210))
## [v1.20.0 (2021-09-25)](https://github.com/pestphp/pest/compare/v1.19.0...v1.20.0)
### Added
- `throwsIf` test call ([#371](https://github.com/pestphp/pest/pull/371))
- `--ci` CLI option to ignore development options like `->local()` ([#405](https://github.com/pestphp/pest/pull/405))
- `when` conditional expectation ([#406](https://github.com/pestphp/pest/pull/406))
- `unless` conditional expectation ([b43a598](https://github.com/pestphp/pest/commit/b43a59868d5b790a28cbb29c6110c9f068b0b812))
- `match` conditional expectation ([#407](https://github.com/pestphp/pest/pull/407))
### Fixed
- `sequence` with more expectations than iterable elements ([#399](https://github.com/pestphp/pest/pull/399))
## [v1.19.0 (2021-09-20)](https://github.com/pestphp/pest/compare/v1.18.0...v1.19.0)
### Added
- PHP 8.1 support ([e6c7d68](https://github.com/pestphp/pest/commit/e6c7d68defaec8efe01e71e15dd8d8c45b0cf60f))
- `toHaveProperties` expectation ([#391](https://github.com/pestphp/pest/pull/391))
## [v1.18.0 (2021-08-30)](https://github.com/pestphp/pest/compare/v1.17.0...v1.18.0)
### Added
- `toHaveLength` expectation ([#386](https://github.com/pestphp/pest/pull/386))
- `nunomaduro/collision:^6.0` support ([4ae482c](https://github.com/pestphp/pest/commit/4ae482c7073fb77782b8a4b5738ef1fcea0f82ab))
## [v1.17.0 (2021-08-26)](https://github.com/pestphp/pest/compare/v1.16.0...v1.17.0)
### Added
- `toThrow` expectation ([#361](https://github.com/pestphp/pest/pull/361))
## [v1.16.0 (2021-08-19)](https://github.com/pestphp/pest/compare/v1.15.0...v1.16.0)
### Added
- Support for new parallel options ([#369](https://github.com/pestphp/pest/pull/369))
## [v1.15.0 (2021-08-04)](https://github.com/pestphp/pest/compare/v1.14.0...v1.15.0)
### Added
- `toBeTruthy` and `toBeFalsy` ([#367](https://github.com/pestphp/pest/pull/367))
## [v1.14.0 (2021-08-03)](https://github.com/pestphp/pest/compare/v1.13.0...v1.14.0)
### Added
- A new bound closure that allows you to access the test case in Datasets ([#364](https://github.com/pestphp/pest/pull/364))
## [v1.13.0 (2021-08-02)](https://github.com/pestphp/pest/compare/v1.12.0...v1.13.0)
### Added
- A cleaner output when running the Pest runner in PhpStorm ([#350](https://github.com/pestphp/pest/pull/350))
- `toBeIn` expectation ([#363](https://github.com/pestphp/pest/pull/363))
### Fixed
- `skip` with false condition marking test as skipped ([22b822c](https://github.com/pestphp/pest/commit/22b822ce87a3d19d84960fa5c93eb286820b525d))
## [v1.12.0 (2021-07-26)](https://github.com/pestphp/pest/compare/v1.11.0...v1.12.0) ## [v1.12.0 (2021-07-26)](https://github.com/pestphp/pest/compare/v1.11.0...v1.12.0)
### Added ### Added
- `--force` option to override tests in `pest:test` artisan command ([#353](https://github.com/pestphp/pest/pull/353)) - `--force` option to override tests in `pest:test` artisan command ([#353](https://github.com/pestphp/pest/pull/353))
+4
View File
@@ -31,6 +31,10 @@ composer lint
``` ```
## Tests ## Tests
Update the snapshots:
```bash
composer update:snapshots
```
Run all tests: Run all tests:
```bash ```bash
composer test composer test
+12 -5
View File
@@ -19,11 +19,18 @@
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). 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).
### Platinum Sponsors
- **[Spatie](https://spatie.be)**
- **[Worksome](https://www.worksome.com/)**
### Premium Sponsors ### Premium Sponsors
- **[Akaunting](https://akaunting.com)** - [Akaunting](https://akaunting.com)
- **[Scout APM](https://scoutapm.com)** - [Auth0](https://auth0.com)
- **[Meema](https://meema.io/)** - [Codecourse](https://codecourse.com/)
- **[Spatie](https://spatie.be/)** - [Fathom Analytics](https://usefathom.com/)
- [Meema](https://meema.io)
- [Scout APM](https://scoutapm.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 is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
+4 -3
View File
@@ -2,14 +2,15 @@
When releasing a new version of Pest there are some checks and updates that need to be done: 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` - Clear your local repository with: `git add . && git reset --hard && git checkout 1.x`
- 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 - On the GitHub repository, check the contents of [github.com/pestphp/pest/compare/{latest_version}...1.x](https://github.com/pestphp/pest/compare/{latest_version}...1.x) 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) - Update the version number in [src/Pest.php](src/Pest.php)
- Run the tests locally using: `composer test` - Run the tests locally using: `composer test`
- Commit the CHANGELOG and Pest file with the message: `git commit -m "docs: update changelog"` - Commit the CHANGELOG and Pest file with the message: `git commit -m "release: vX.X.X"`
- Push the changes to GitHub - Push the changes to GitHub
- Check that the CI is passing as expected: [github.com/pestphp/pest/actions](https://github.com/pestphp/pest/actions) - 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` - Tag and push the tag with `git tag vX.X.X && git push --tags`
- Publish release here: [github.com/pestphp/pest/releases/new](https://github.com/pestphp/pest/releases/new).
### Plugins ### Plugins
+14 -6
View File
@@ -3,9 +3,9 @@
use NunoMaduro\Collision\Provider; use NunoMaduro\Collision\Provider;
use Pest\Actions\ValidatesEnvironment; use Pest\Actions\ValidatesEnvironment;
use Pest\Console\Command;
use Pest\Support\Container; use Pest\Support\Container;
use Pest\TestSuite; use Pest\TestSuite;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArgvInput; 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;
@@ -27,7 +27,7 @@ use Symfony\Component\Console\Output\OutputInterface;
(new Provider())->register(); (new Provider())->register();
// get $rootPath based on $autoloadPath // Get $rootPath based on $autoloadPath
$rootPath = dirname($autoloadPath, 2); $rootPath = dirname($autoloadPath, 2);
$argv = new ArgvInput(); $argv = new ArgvInput();
@@ -42,14 +42,22 @@ use Symfony\Component\Console\Output\OutputInterface;
ValidatesEnvironment::in($testSuite); ValidatesEnvironment::in($testSuite);
// lets remove any arguments that PHPUnit does not understand $args = $_SERVER['argv'];
// Let's remove any arguments that PHPUnit does not understand
if ($argv->hasParameterOption('--test-directory')) { if ($argv->hasParameterOption('--test-directory')) {
foreach ($_SERVER['argv'] as $key => $value) { foreach ($args as $key => $value) {
if (strpos($value, '--test-directory') !== false) { if (strpos($value, '--test-directory') !== false) {
unset($_SERVER['argv'][$key]); unset($args[$key]);
} }
} }
} }
exit($container->get(Command::class)->run($_SERVER['argv'])); if (($runInParallel = $argv->hasParameterOption(['--parallel', '-p'])) && !class_exists(\Pest\Parallel\Command::class)) {
$output->writeln("Parallel support requires the Pest Parallel plugin. Run <fg=yellow;options=bold>`composer require --dev pestphp/pest-plugin-parallel`</> first.");
exit(Command::FAILURE);
}
$command = $runInParallel ? \Pest\Parallel\Command::class : \Pest\Console\Command::class;
exit($container->get($command)->run($args));
})(); })();
+21 -14
View File
@@ -18,9 +18,9 @@
], ],
"require": { "require": {
"php": "^7.3 || ^8.0", "php": "^7.3 || ^8.0",
"nunomaduro/collision": "^5.4.0", "nunomaduro/collision": "^5.11.0|^6.4.0",
"pestphp/pest-plugin": "^1.0.0", "pestphp/pest-plugin": "^1.1.0",
"phpunit/phpunit": "^9.3.7" "phpunit/phpunit": "^9.6.10"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@@ -40,43 +40,50 @@
] ]
}, },
"require-dev": { "require-dev": {
"illuminate/console": "^8.47.0", "illuminate/console": "^8.83.27",
"illuminate/support": "^8.47.0", "illuminate/support": "^8.83.27",
"laravel/dusk": "^6.15.0", "laravel/dusk": "^6.25.2",
"pestphp/pest-dev-tools": "dev-master" "pestphp/pest-dev-tools": "^1.0.0",
"pestphp/pest-plugin-parallel": "^1.2.1"
}, },
"minimum-stability": "dev", "minimum-stability": "stable",
"prefer-stable": true, "prefer-stable": true,
"config": { "config": {
"sort-packages": true, "sort-packages": true,
"preferred-install": "dist" "preferred-install": "dist",
"allow-plugins": {
"pestphp/pest-plugin": true
}
}, },
"bin": [ "bin": [
"bin/pest" "bin/pest"
], ],
"scripts": { "scripts": {
"lint": "php-cs-fixer fix -v", "lint": "PHP_CS_FIXER_IGNORE_ENV=true php-cs-fixer fix -v",
"test:lint": "php-cs-fixer fix -v --dry-run", "test:lint": "PHP_CS_FIXER_IGNORE_ENV=true php-cs-fixer fix -v --dry-run",
"test:types": "phpstan analyse --ansi --memory-limit=0", "test:types": "phpstan analyse --ansi --memory-limit=-1",
"test:unit": "php bin/pest --colors=always --exclude-group=integration", "test:unit": "php bin/pest --colors=always --exclude-group=integration",
"test:parallel": "php bin/pest -p --colors=always --exclude-group=integration",
"test:integration": "php bin/pest --colors=always --group=integration", "test:integration": "php bin/pest --colors=always --group=integration",
"update:snapshots": "REBUILD_SNAPSHOTS=true php bin/pest --colors=always", "update:snapshots": "REBUILD_SNAPSHOTS=true php bin/pest --colors=always",
"test": [ "test": [
"@test:lint", "@test:lint",
"@test:types", "@test:types",
"@test:unit", "@test:unit",
"@test:parallel",
"@test:integration" "@test:integration"
] ]
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.x-dev" "dev-1.x": "1.x-dev"
}, },
"pest": { "pest": {
"plugins": [ "plugins": [
"Pest\\Plugins\\Coverage", "Pest\\Plugins\\Coverage",
"Pest\\Plugins\\Init", "Pest\\Plugins\\Init",
"Pest\\Plugins\\Version" "Pest\\Plugins\\Version",
"Pest\\Plugins\\Environment"
] ]
}, },
"laravel": { "laravel": {
+262
View File
@@ -0,0 +1,262 @@
parameters:
ignoreErrors:
-
message: "#^Cannot access an offset on mixed\\.$#"
count: 1
path: src/Actions/AddsDefaults.php
-
message: "#^Parameter \\#1 \\$out of class Pest\\\\Logging\\\\JUnit constructor expects string, mixed given\\.$#"
count: 1
path: src/Actions/AddsDefaults.php
-
message: "#^Parameter \\#2 \\$verbose of class NunoMaduro\\\\Collision\\\\Adapters\\\\Phpunit\\\\Printer constructor expects bool, mixed given\\.$#"
count: 1
path: src/Actions/AddsDefaults.php
-
message: "#^Parameter \\#2 \\$verbose of class Pest\\\\Logging\\\\TeamCity constructor expects bool, mixed given\\.$#"
count: 1
path: src/Actions/AddsDefaults.php
-
message: "#^Parameter \\#3 \\$colors of class NunoMaduro\\\\Collision\\\\Adapters\\\\Phpunit\\\\Printer constructor expects string, mixed given\\.$#"
count: 1
path: src/Actions/AddsDefaults.php
-
message: "#^Parameter \\#3 \\$colors of class Pest\\\\Logging\\\\TeamCity constructor expects string, mixed given\\.$#"
count: 1
path: src/Actions/AddsDefaults.php
-
message: "#^Parameter \\#1 \\$filename of function file_exists expects string, mixed given\\.$#"
count: 1
path: src/Actions/ValidatesConfiguration.php
-
message: "#^Parameter \\#1 \\$filename of method PHPUnit\\\\TextUI\\\\XmlConfiguration\\\\Loader\\:\\:load\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Actions/ValidatesConfiguration.php
-
message: "#^Parameter \\#1 \\$loader of class PHPUnit\\\\TextUI\\\\TestRunner constructor expects PHPUnit\\\\Runner\\\\TestSuiteLoader\\|null, mixed given\\.$#"
count: 1
path: src/Console/Command.php
-
message: "#^Parameter \\#1 \\$testSuite of static method Pest\\\\Actions\\\\AddsTests\\:\\:to\\(\\) expects iterable\\<PHPUnit\\\\Framework\\\\TestCase\\>&PHPUnit\\\\Framework\\\\TestSuite, mixed given\\.$#"
count: 1
path: src/Console/Command.php
-
message: "#^Parameter \\#2 \\$suffixes of method SebastianBergmann\\\\FileIterator\\\\Facade\\:\\:getFilesAsArray\\(\\) expects array\\|string, mixed given\\.$#"
count: 1
path: src/Console/Command.php
-
message: "#^Method Pest\\\\Datasets\\:\\:getDataSetsCombinations\\(\\) has parameter \\$combinations with no value type specified in iterable type array\\.$#"
count: 1
path: src/Datasets.php
-
message: "#^Method Pest\\\\Datasets\\:\\:getDataSetsCombinations\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Datasets.php
-
message: "#^Method Pest\\\\Datasets\\:\\:processDatasets\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Datasets.php
-
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
count: 1
path: src/Each.php
-
message: "#^Parameter \\#1 \\$ of callable callable\\(Pest\\\\Expectation\\<TValue\\>\\)\\: mixed expects Pest\\\\Expectation\\<TValue\\>, Pest\\\\Expectation\\<mixed\\> given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#1 \\$directory of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertDirectoryExists\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#1 \\$directory of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertDirectoryIsReadable\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#1 \\$directory of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertDirectoryIsWritable\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#1 \\$file of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertFileIsReadable\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#1 \\$file of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertFileIsWritable\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#1 \\$filename of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertFileExists\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#1 \\$json of function json_decode expects string, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#1 \\$needle of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringContainsString\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#1 \\$object_or_class of function property_exists expects object\\|string, mixed given\\.$#"
count: 2
path: src/Expectation.php
-
message: "#^Parameter \\#2 \\$haystack of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertContains\\(\\) expects iterable, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#2 \\$haystack of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertCount\\(\\) expects Countable\\|iterable, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#2 \\$string of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertMatchesRegularExpression\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#2 \\$string of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringEndsWith\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#2 \\$string of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringStartsWith\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Parameter \\#2 \\$value of method Pest\\\\Expectation\\<TValue\\>\\:\\:retrieve\\(\\) expects array\\<string, null\\>\\|object, mixed given\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Trying to invoke mixed but it's not a callable\\.$#"
count: 1
path: src/Expectation.php
-
message: "#^Method Pest\\\\Factories\\\\TestCaseFactory\\:\\:build\\(\\) should return array\\<int, PHPUnit\\\\Framework\\\\TestCase\\> but returns array\\<int, object\\>\\.$#"
count: 1
path: src/Factories/TestCaseFactory.php
-
message: "#^Function it\\(\\) should return Pest\\\\PendingObjects\\\\TestCall but returns mixed\\.$#"
count: 1
path: src/Functions.php
-
message: "#^Parameter \\#2 \\$classAndTraits of class Pest\\\\PendingObjects\\\\UsesCall constructor expects array\\<int, string\\>, array\\<int\\|string, string\\> given\\.$#"
count: 1
path: src/Functions.php
-
message: "#^Parameter \\#2 \\$value of method Pest\\\\HigherOrderExpectation\\:\\:retrieve\\(\\) expects array\\<string, null\\>\\|object, mixed given\\.$#"
count: 1
path: src/HigherOrderExpectation.php
-
message: "#^Dead catch \\- ReflectionException is never thrown in the try block\\.$#"
count: 1
path: src/Logging/JUnit.php
-
message: "#^Parameter \\#2 \\$value of method DOMElement\\:\\:setAttribute\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Logging/JUnit.php
-
message: "#^Parameter \\$test of method Pest\\\\Logging\\\\JUnit\\:\\:startTest\\(\\) has invalid type Pest\\\\Concerns\\\\Testable\\.$#"
count: 1
path: src/Logging/JUnit.php
-
message: "#^Parameter \\$test of method Pest\\\\Logging\\\\TeamCity\\:\\:endTest\\(\\) has invalid type Pest\\\\Concerns\\\\Testable\\.$#"
count: 1
path: src/Logging/TeamCity.php
-
message: "#^Parameter \\$test of method Pest\\\\Logging\\\\TeamCity\\:\\:startTest\\(\\) has invalid type Pest\\\\Concerns\\\\Testable\\.$#"
count: 1
path: src/Logging/TeamCity.php
-
message: "#^Dead catch \\- PHPUnit\\\\Framework\\\\ExpectationFailedException is never thrown in the try block\\.$#"
count: 1
path: src/OppositeExpectation.php
-
message: "#^Property Pest\\\\PendingObjects\\\\UsesCall\\:\\:\\$groups \\(array\\<int, string\\>\\) does not accept array\\<int\\|string, string\\>\\.$#"
count: 1
path: src/PendingObjects/UsesCall.php
-
message: "#^Cannot cast mixed to float\\.$#"
count: 1
path: src/Plugins/Coverage.php
-
message: "#^Static property Pest\\\\Plugins\\\\Environment\\:\\:\\$instance is unused\\.$#"
count: 1
path: src/Plugins/Environment.php
-
message: "#^Method Pest\\\\Support\\\\Container\\:\\:get\\(\\) should return object but returns mixed\\.$#"
count: 1
path: src/Support/Container.php
-
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
count: 1
path: src/Support/ExceptionTrace.php
-
message: "#^Parameter \\#2 \\$array of function key_exists expects array, mixed given\\.$#"
count: 1
path: src/Support/ExceptionTrace.php
-
message: "#^Method Pest\\\\Support\\\\HigherOrderCallables\\:\\:expect\\(\\) should return Pest\\\\Expectation\\<TValue\\> but returns Pest\\\\Expectation\\<mixed\\>\\.$#"
count: 1
path: src/Support/HigherOrderCallables.php
-
message: "#^Parameter \\#1 \\$target of method Pest\\\\Support\\\\HigherOrderMessage\\:\\:call\\(\\) expects object, mixed given\\.$#"
count: 1
path: src/Support/HigherOrderMessageCollection.php
-
message: "#^Constant Pest\\\\Support\\\\HigherOrderTapProxy\\:\\:UNDEFINED_PROPERTY is unused\\.$#"
count: 1
path: src/Support/HigherOrderTapProxy.php
-
message: "#^Dead catch \\- Throwable is never thrown in the try block\\.$#"
count: 1
path: src/Support/HigherOrderTapProxy.php
+1 -4
View File
@@ -2,12 +2,12 @@ includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon - vendor/phpstan/phpstan-strict-rules/rules.neon
- vendor/ergebnis/phpstan-rules/rules.neon - vendor/ergebnis/phpstan-rules/rules.neon
- vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon - vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon
- phpstan-baseline.neon
parameters: parameters:
level: max level: max
paths: paths:
- src - src
- scripts
checkMissingIterableValueType: true checkMissingIterableValueType: true
checkGenericClassInNonGenericObjectType: false checkGenericClassInNonGenericObjectType: false
@@ -24,9 +24,6 @@ parameters:
- -
message: '#Call to an undefined method PHPUnit\\Framework\\Test::getName\(\)#' message: '#Call to an undefined method PHPUnit\\Framework\\Test::getName\(\)#'
path: src/Logging path: src/Logging
-
message: '#invalid typehint type Pest\\Concerns\\Testable#'
path: src/Logging
- -
message: '#is not subtype of native type PHPUnit\\Framework\\Test#' message: '#is not subtype of native type PHPUnit\\Framework\\Test#'
path: src/Logging path: src/Logging
-37
View File
@@ -1,37 +0,0 @@
<?php
declare(strict_types=1);
$globalsFilePath = implode(DIRECTORY_SEPARATOR, [
dirname(__DIR__),
'vendor',
'phpunit',
'phpunit',
'src',
'Framework',
'Assert',
'Functions.php',
]);
$compiledFilePath = implode(DIRECTORY_SEPARATOR, [dirname(__DIR__), 'compiled', 'globals.php']);
/* @phpstan-ignore-next-line */
@unlink($compiledFilePath);
$replace = function ($contents, $string, $by) {
return str_replace($string, $by, $contents);
};
$remove = function ($contents, $string) {
return str_replace($string, '', $contents);
};
$contents = file_get_contents($globalsFilePath);
$contents = $replace($contents, 'namespace PHPUnit\Framework;', 'use PHPUnit\Framework\Assert;');
$contents = $remove($contents, 'use ArrayAccess;');
$contents = $remove($contents, 'use Countable;');
$contents = $remove($contents, 'use DOMDocument;');
$contents = $remove($contents, 'use DOMElement;');
$contents = $remove($contents, 'use Throwable;');
file_put_contents(implode(DIRECTORY_SEPARATOR, [dirname(__DIR__), 'compiled', 'globals.php']), $contents);
+1 -1
View File
@@ -30,7 +30,7 @@ final class AddsDefaults
} }
if ($arguments[self::PRINTER] === \PHPUnit\Util\Log\TeamCity::class) { if ($arguments[self::PRINTER] === \PHPUnit\Util\Log\TeamCity::class) {
$arguments[self::PRINTER] = new TeamCity($arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS); $arguments[self::PRINTER] = new TeamCity(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
} }
// Load our junit logger instead. // Load our junit logger instead.
-2
View File
@@ -22,8 +22,6 @@ final class AddsTests
{ {
self::removeTestClosureWarnings($testSuite); self::removeTestClosureWarnings($testSuite);
// @todo refactor this...
$testSuites = []; $testSuites = [];
$pestTestSuite->tests->build($pestTestSuite, function (TestCase $testCase) use (&$testSuites): void { $pestTestSuite->tests->build($pestTestSuite, function (TestCase $testCase) use (&$testSuites): void {
$testCaseClass = get_class($testCase); $testCaseClass = get_class($testCase);
+50
View File
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Pest\Actions;
use Pest\Contracts\Plugins\AddsOutput;
use Pest\Contracts\Plugins\HandlesArguments;
use Pest\Plugin\Loader;
/**
* @internal
*/
final class InteractsWithPlugins
{
/**
* Transform the input arguments by passing it to the relevant plugins.
*
* @param array<int, string> $argv
*
* @return array<int, string>
*/
public static function handleArguments(array $argv): array
{
$plugins = Loader::getPlugins(HandlesArguments::class);
/** @var HandlesArguments $plugin */
foreach ($plugins as $plugin) {
$argv = $plugin->handleArguments($argv);
}
return $argv;
}
/**
* Provides an opportunity for any plugins that want
* to provide additional output after test execution.
*/
public static function addOutput(int $result): int
{
$plugins = Loader::getPlugins(AddsOutput::class);
/** @var AddsOutput $plugin */
foreach ($plugins as $plugin) {
$result = $plugin->addOutput($result);
}
return $result;
}
}
+3 -1
View File
@@ -5,11 +5,12 @@ declare(strict_types=1);
namespace Pest\Actions; namespace Pest\Actions;
use Pest\Support\Str; use Pest\Support\Str;
use function Pest\testDirectory;
use PHPUnit\Util\FileLoader; use PHPUnit\Util\FileLoader;
use RecursiveDirectoryIterator; use RecursiveDirectoryIterator;
use RecursiveIteratorIterator; use RecursiveIteratorIterator;
use function Pest\testDirectory;
/** /**
* @internal * @internal
*/ */
@@ -50,6 +51,7 @@ final class LoadStructure
$directory = new RecursiveDirectoryIterator($filename); $directory = new RecursiveDirectoryIterator($filename);
$iterator = new RecursiveIteratorIterator($directory); $iterator = new RecursiveIteratorIterator($directory);
foreach ($iterator as $file) { foreach ($iterator as $file) {
/* @phpstan-ignore-next-line */
$filename = $file->__toString(); $filename = $file->__toString();
if (Str::endsWith($filename, '.php') && file_exists($filename)) { if (Str::endsWith($filename, '.php') && file_exists($filename)) {
require_once $filename; require_once $filename;
+33
View File
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Pest\Concerns\Logging;
/**
* @internal
*/
trait WritesToConsole
{
private function writeSuccess(string $message): void
{
$this->writePestTestOutput($message, 'fg-green, bold', '✓');
}
private function writeError(string $message): void
{
$this->writePestTestOutput($message, 'fg-red, bold', '');
}
private function writeWarning(string $message): void
{
$this->writePestTestOutput($message, 'fg-yellow, bold', '-');
}
private function writePestTestOutput(string $message, string $color, string $symbol): void
{
$this->writeWithColor($color, "$symbol ", false);
$this->write($message);
$this->writeNewLine();
}
}
+16 -2
View File
@@ -11,6 +11,8 @@ use Pest\TestSuite;
use PHPUnit\Framework\ExecutionOrderDependency; use PHPUnit\Framework\ExecutionOrderDependency;
use Throwable; use Throwable;
use function sprintf;
/** /**
* To avoid inheritance conflicts, all the fields related * To avoid inheritance conflicts, all the fields related
* to Pest only will be prefixed by double underscore. * to Pest only will be prefixed by double underscore.
@@ -257,7 +259,7 @@ trait Testable
*/ */
public function toString(): string public function toString(): string
{ {
return \sprintf( return sprintf(
'%s::%s', '%s::%s',
self::$__filename, self::$__filename,
$this->__description $this->__description
@@ -273,7 +275,19 @@ trait Testable
*/ */
public function __test() public function __test()
{ {
return $this->__callClosure($this->__test, func_get_args()); return $this->__callClosure($this->__test, $this->resolveTestArguments(func_get_args()));
}
/**
* Resolve the passed arguments. Any Closures will be bound to the testcase and resolved.
*
* @throws Throwable
*/
private function resolveTestArguments(array $arguments): array
{
return array_map(function ($data) {
return $data instanceof Closure ? $this->__callClosure($data, []) : $data;
}, $arguments);
} }
/** /**
+7 -27
View File
@@ -6,11 +6,9 @@ namespace Pest\Console;
use Pest\Actions\AddsDefaults; use Pest\Actions\AddsDefaults;
use Pest\Actions\AddsTests; use Pest\Actions\AddsTests;
use Pest\Actions\InteractsWithPlugins;
use Pest\Actions\LoadStructure; use Pest\Actions\LoadStructure;
use Pest\Actions\ValidatesConfiguration; use Pest\Actions\ValidatesConfiguration;
use Pest\Contracts\Plugins\AddsOutput;
use Pest\Contracts\Plugins\HandlesArguments;
use Pest\Plugin\Loader;
use Pest\Plugins\Version; use Pest\Plugins\Version;
use Pest\Support\Container; use Pest\Support\Container;
use Pest\TestSuite; use Pest\TestSuite;
@@ -20,6 +18,8 @@ use PHPUnit\TextUI\TestRunner;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use function is_dir;
/** /**
* @internal * @internal
*/ */
@@ -57,23 +57,12 @@ final class Command extends BaseCommand
*/ */
protected function handleArguments(array $argv): void protected function handleArguments(array $argv): void
{ {
/* $argv = InteractsWithPlugins::handleArguments($argv);
* First, let's call all plugins that want to handle arguments
*/
$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.
*/
parent::handleArguments($argv); parent::handleArguments($argv);
/* /*
* Finally, let's validate the configuration. Making * Let's validate the configuration. Making
* sure all options are yet supported by Pest. * sure all options are yet supported by Pest.
*/ */
ValidatesConfiguration::in($this->arguments); ValidatesConfiguration::in($this->arguments);
@@ -94,7 +83,7 @@ final class Command extends BaseCommand
$testSuite = $this->arguments['test']; $testSuite = $this->arguments['test'];
if (is_string($testSuite)) { if (is_string($testSuite)) {
if (\is_dir($testSuite)) { if (is_dir($testSuite)) {
/** @var string[] $files */ /** @var string[] $files */
$files = (new FileIteratorFacade())->getFilesAsArray( $files = (new FileIteratorFacade())->getFilesAsArray(
$testSuite, $testSuite,
@@ -128,16 +117,7 @@ final class Command extends BaseCommand
LoadStructure::in($this->testSuite->rootPath); LoadStructure::in($this->testSuite->rootPath);
$result = parent::run($argv, false); $result = parent::run($argv, false);
$result = InteractsWithPlugins::addOutput($result);
/*
* Let's call all plugins that want to add output after test execution
*/
$plugins = Loader::getPlugins(AddsOutput::class);
/** @var AddsOutput $plugin */
foreach ($plugins as $plugin) {
$result = $plugin->addOutput($result);
}
exit($result); exit($result);
} }
@@ -12,9 +12,6 @@ interface HandlesArguments
/** /**
* Allows to handle custom command line arguments. * 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 * @param array<int, string> $arguments
* *
* @return array<int, string> the updated list of arguments * @return array<int, string> the updated list of arguments
+5 -1
View File
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Pest; namespace Pest;
use Closure; use Closure;
use Generator;
use Pest\Exceptions\DatasetAlreadyExist; use Pest\Exceptions\DatasetAlreadyExist;
use Pest\Exceptions\DatasetDoesNotExist; use Pest\Exceptions\DatasetDoesNotExist;
use SebastianBergmann\Exporter\Exporter; use SebastianBergmann\Exporter\Exporter;
@@ -122,7 +123,10 @@ final class Datasets
} }
if ($datasets[$index] instanceof Traversable) { if ($datasets[$index] instanceof Traversable) {
$datasets[$index] = iterator_to_array($datasets[$index]); $preserveKeysForArrayIterator = $datasets[$index] instanceof Generator
&& is_string($datasets[$index]->key());
$datasets[$index] = iterator_to_array($datasets[$index], $preserveKeysForArrayIterator);
} }
foreach ($datasets[$index] as $key => $values) { foreach ($datasets[$index] as $key => $values) {
+36
View File
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Pest\Exceptions;
use BadFunctionCallException;
use NunoMaduro\Collision\Contracts\RenderlessEditor;
use NunoMaduro\Collision\Contracts\RenderlessTrace;
use Symfony\Component\Console\Exception\ExceptionInterface;
/**
* Creates a new instance of dataset is not present for test that has arguments.
*
* @internal
*/
final class DatasetMissing extends BadFunctionCallException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
{
/**
* Create new exception instance.
*
* @param array<string, string> $args A map of argument names to their typee
*/
public function __construct(string $file, string $name, array $args)
{
parent::__construct(sprintf(
"A test with the description '%s' has %d argument(s) ([%s]) and no dataset(s) provided in %s",
$name,
count($args),
implode(', ', array_map(static function (string $arg, string $type): string {
return sprintf('%s $%s', $type, $arg);
}, array_keys($args), $args)),
$file,
));
}
}
+237 -12
View File
@@ -5,13 +5,20 @@ declare(strict_types=1);
namespace Pest; namespace Pest;
use BadMethodCallException; use BadMethodCallException;
use Closure;
use Error;
use InvalidArgumentException;
use Pest\Concerns\Extendable; use Pest\Concerns\Extendable;
use Pest\Concerns\RetrievesValues; use Pest\Concerns\RetrievesValues;
use Pest\Support\Arr; use Pest\Support\Arr;
use Pest\Support\NullClosure;
use PHPUnit\Framework\Assert; use PHPUnit\Framework\Assert;
use PHPUnit\Framework\Constraint\Constraint; use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\ExpectationFailedException;
use ReflectionFunction;
use ReflectionNamedType;
use SebastianBergmann\Exporter\Exporter; use SebastianBergmann\Exporter\Exporter;
use Throwable;
/** /**
* @internal * @internal
@@ -102,7 +109,6 @@ final class Expectation
public function ray(...$arguments): self public function ray(...$arguments): self
{ {
if (function_exists('ray')) { if (function_exists('ray')) {
// @phpstan-ignore-next-line
ray($this->value, ...$arguments); ray($this->value, ...$arguments);
} }
@@ -148,9 +154,10 @@ final class Expectation
throw new BadMethodCallException('Expectation value is not iterable.'); throw new BadMethodCallException('Expectation value is not iterable.');
} }
$value = is_array($this->value) ? $this->value : iterator_to_array($this->value); $value = is_array($this->value) ? $this->value : iterator_to_array($this->value);
$keys = array_keys($value); $keys = array_keys($value);
$values = array_values($value); $values = array_values($value);
$callbacksCount = count($callbacks);
$index = 0; $index = 0;
@@ -159,8 +166,12 @@ final class Expectation
$index = $index < count($values) - 1 ? $index + 1 : 0; $index = $index < count($values) - 1 ? $index + 1 : 0;
} }
if ($callbacksCount > count($values)) {
Assert::assertLessThanOrEqual(count($value), count($callbacks));
}
foreach ($values as $key => $item) { foreach ($values as $key => $item) {
if (is_callable($callbacks[$key])) { if ($callbacks[$key] instanceof Closure) {
call_user_func($callbacks[$key], new self($item), new self($keys[$key])); call_user_func($callbacks[$key], new self($item), new self($keys[$key]));
continue; continue;
} }
@@ -171,6 +182,88 @@ final class Expectation
return $this; return $this;
} }
/**
* If the subject matches one of the given "expressions", the expression callback will run.
*
* @template TMatchSubject of array-key
*
* @param callable(): TMatchSubject|TMatchSubject $subject
* @param array<TMatchSubject, (callable(Expectation<TValue>): mixed)|TValue> $expressions
*/
public function match($subject, array $expressions): Expectation
{
$subject = is_callable($subject)
? $subject
: function () use ($subject) {
return $subject;
};
$subject = $subject();
$matched = false;
foreach ($expressions as $key => $callback) {
if ($subject != $key) {
continue;
}
$matched = true;
if (is_callable($callback)) {
$callback(new self($this->value));
continue;
}
$this->and($this->value)->toEqual($callback);
break;
}
if ($matched === false) {
throw new ExpectationFailedException('Unhandled match value.');
}
return $this;
}
/**
* Apply the callback if the given "condition" is falsy.
*
* @param (callable(): bool)|bool $condition
* @param callable(Expectation<TValue>): mixed $callback
*/
public function unless($condition, callable $callback): Expectation
{
$condition = is_callable($condition)
? $condition
: static function () use ($condition): bool {
return (bool) $condition; // @phpstan-ignore-line
};
return $this->when(!$condition(), $callback);
}
/**
* Apply the callback if the given "condition" is truthy.
*
* @param (callable(): bool)|bool $condition
* @param callable(Expectation<TValue>): mixed $callback
*/
public function when($condition, callable $callback): Expectation
{
$condition = is_callable($condition)
? $condition
: static function () use ($condition): bool {
return (bool) $condition; // @phpstan-ignore-line
};
if ($condition()) {
$callback($this->and($this->value));
}
return $this;
}
/** /**
* Asserts that two variables have the same type and * Asserts that two variables have the same type and
* value. Used on objects, it asserts that two * value. Used on objects, it asserts that two
@@ -205,6 +298,16 @@ final class Expectation
return $this; return $this;
} }
/**
* Asserts that the value is truthy.
*/
public function toBeTruthy(): Expectation
{
Assert::assertTrue((bool) $this->value);
return $this;
}
/** /**
* Asserts that the value is false. * Asserts that the value is false.
*/ */
@@ -215,6 +318,16 @@ final class Expectation
return $this; return $this;
} }
/**
* Asserts that the value is falsy.
*/
public function toBeFalsy(): Expectation
{
Assert::assertFalse((bool) $this->value);
return $this;
}
/** /**
* Asserts that the value is greater than $expected. * Asserts that the value is greater than $expected.
* *
@@ -266,14 +379,16 @@ final class Expectation
/** /**
* Asserts that $needle is an element of the value. * Asserts that $needle is an element of the value.
* *
* @param mixed $needle * @param mixed $needles
*/ */
public function toContain($needle): Expectation public function toContain(...$needles): Expectation
{ {
if (is_string($this->value)) { foreach ($needles as $needle) {
Assert::assertStringContainsString($needle, $this->value); if (is_string($this->value)) {
} else { Assert::assertStringContainsString($needle, $this->value);
Assert::assertContains($needle, $this->value); } else {
Assert::assertContains($needle, $this->value);
}
} }
return $this; return $this;
@@ -299,6 +414,36 @@ final class Expectation
return $this; return $this;
} }
/**
* Asserts that $number matches value's Length.
*/
public function toHaveLength(int $number): Expectation
{
if (is_string($this->value)) {
Assert::assertEquals($number, mb_strlen($this->value));
return $this;
}
if (is_iterable($this->value)) {
return $this->toHaveCount($number);
}
if (is_object($this->value)) {
if (method_exists($this->value, 'toArray')) {
$array = $this->value->toArray();
} else {
$array = (array) $this->value;
}
Assert::assertCount($number, $array);
return $this;
}
throw new BadMethodCallException('Expectation value length is not countable.');
}
/** /**
* Asserts that $count matches the number of elements of the value. * Asserts that $count matches the number of elements of the value.
*/ */
@@ -328,6 +473,20 @@ final class Expectation
return $this; return $this;
} }
/**
* Asserts that the value contains the provided properties $names.
*
* @param iterable<array-key, string> $names
*/
public function toHaveProperties(iterable $names): Expectation
{
foreach ($names as $name) {
$this->toHaveProperty($name);
}
return $this;
}
/** /**
* Asserts that two variables have the same value. * Asserts that two variables have the same value.
* *
@@ -371,6 +530,18 @@ final class Expectation
return $this; return $this;
} }
/**
* Asserts that the value is one of the given values.
*
* @param iterable<int|string, mixed> $values
*/
public function toBeIn(iterable $values): Expectation
{
Assert::assertContains($this->value, $values);
return $this;
}
/** /**
* Asserts that the value is infinite. * Asserts that the value is infinite.
*/ */
@@ -552,7 +723,7 @@ final class Expectation
try { try {
Assert::assertTrue(Arr::has($array, $key)); Assert::assertTrue(Arr::has($array, $key));
/* @phpstan-ignore-next-line */ /* @phpstan-ignore-next-line */
} catch (ExpectationFailedException $exception) { } catch (ExpectationFailedException $exception) {
throw new ExpectationFailedException("Failed asserting that an array has the key '$key'", $exception->getComparisonFailure()); throw new ExpectationFailedException("Failed asserting that an array has the key '$key'", $exception->getComparisonFailure());
} }
@@ -715,6 +886,60 @@ final class Expectation
return $this; return $this;
} }
/**
* Asserts that executing value throws an exception.
*
* @param (Closure(Throwable): mixed)|string $exception
*/
public function toThrow($exception, string $exceptionMessage = null): Expectation
{
$callback = NullClosure::create();
if ($exception instanceof Closure) {
$callback = $exception;
$parameters = (new ReflectionFunction($exception))->getParameters();
if (1 !== count($parameters)) {
throw new InvalidArgumentException('The given closure must have a single parameter type-hinted as the class string.');
}
if (!($type = $parameters[0]->getType()) instanceof ReflectionNamedType) {
throw new InvalidArgumentException('The given closure\'s parameter must be type-hinted as the class string.');
}
$exception = $type->getName();
}
try {
($this->value)();
} catch (Throwable $e) {
if (!class_exists($exception)) {
if ($e instanceof Error && (bool) preg_match("/Class [\"']{$exception}[\"'] not found/", $e->getMessage())) {
throw $e;
}
Assert::assertStringContainsString($exception, $e->getMessage());
return $this;
}
if ($exceptionMessage !== null) {
Assert::assertStringContainsString($exceptionMessage, $e->getMessage());
}
Assert::assertInstanceOf($exception, $e);
$callback($e);
return $this;
}
if (!class_exists($exception)) {
throw new ExpectationFailedException("Exception with message \"{$exception}\" not thrown.");
}
throw new ExpectationFailedException("Exception \"{$exception}\" not thrown.");
}
/** /**
* Exports the given value. * Exports the given value.
* *
+10
View File
@@ -216,6 +216,7 @@ final class TestCaseFactory
eval(" eval("
namespace $namespace; namespace $namespace;
#[\AllowDynamicProperties]
final class $className extends $baseClass implements $hasPrintableTestCaseClassFQN { final class $className extends $baseClass implements $hasPrintableTestCaseClassFQN {
$traitsCode $traitsCode
@@ -228,4 +229,13 @@ final class TestCaseFactory
return $classFQN; return $classFQN;
} }
/**
* Determine if the test case will receive argument input from Pest, or not.
*/
public function receivesArguments(): bool
{
return count($this->datasets) > 0
|| $this->factoryProxies->count('addDependencies') > 0;
}
} }
+2 -1
View File
@@ -8,9 +8,10 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Pest\Exceptions\InvalidConsoleArgument; use Pest\Exceptions\InvalidConsoleArgument;
use function Pest\testDirectory;
use Pest\TestSuite; use Pest\TestSuite;
use function Pest\testDirectory;
/** /**
* @internal * @internal
*/ */
+2 -1
View File
@@ -8,9 +8,10 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Pest\Console\Thanks; use Pest\Console\Thanks;
use Pest\Exceptions\InvalidConsoleArgument; use Pest\Exceptions\InvalidConsoleArgument;
use function Pest\testDirectory;
use Pest\TestSuite; use Pest\TestSuite;
use function Pest\testDirectory;
/** /**
* @internal * @internal
*/ */
+2 -1
View File
@@ -8,9 +8,10 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Pest\Exceptions\InvalidConsoleArgument; use Pest\Exceptions\InvalidConsoleArgument;
use Pest\Support\Str; use Pest\Support\Str;
use function Pest\testDirectory;
use Pest\TestSuite; use Pest\TestSuite;
use function Pest\testDirectory;
/** /**
* @internal * @internal
*/ */
+5 -7
View File
@@ -12,12 +12,9 @@ declare(strict_types=1);
namespace Pest\Logging; namespace Pest\Logging;
use function class_exists;
use DOMDocument; use DOMDocument;
use DOMElement; use DOMElement;
use Exception; use Exception;
use function get_class;
use function method_exists;
use Pest\Concerns\Testable; use Pest\Concerns\Testable;
use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ExceptionWrapper; use PHPUnit\Framework\ExceptionWrapper;
@@ -32,9 +29,13 @@ use PHPUnit\Util\Printer;
use PHPUnit\Util\Xml; use PHPUnit\Util\Xml;
use ReflectionClass; use ReflectionClass;
use ReflectionException; use ReflectionException;
use Throwable;
use function class_exists;
use function get_class;
use function method_exists;
use function sprintf; use function sprintf;
use function str_replace; use function str_replace;
use Throwable;
use function trim; use function trim;
/** /**
@@ -173,7 +174,6 @@ final class JUnit extends Printer implements TestListener
$this->doAddSkipped(); $this->doAddSkipped();
} }
/** @phpstan-ignore-next-line */
public function startTestSuite(TestSuite $suite): void public function startTestSuite(TestSuite $suite): void
{ {
$testSuite = $this->document->createElement('testsuite'); $testSuite = $this->document->createElement('testsuite');
@@ -212,7 +212,6 @@ final class JUnit extends Printer implements TestListener
$this->testSuiteTimes[$this->testSuiteLevel] = 0; $this->testSuiteTimes[$this->testSuiteLevel] = 0;
} }
/** @phpstan-ignore-next-line */
public function endTestSuite(TestSuite $suite): void public function endTestSuite(TestSuite $suite): void
{ {
$this->testSuites[$this->testSuiteLevel]->setAttribute( $this->testSuites[$this->testSuiteLevel]->setAttribute(
@@ -283,7 +282,6 @@ final class JUnit extends Printer implements TestListener
$class = new ReflectionClass($test); $class = new ReflectionClass($test);
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
} catch (ReflectionException $e) { } catch (ReflectionException $e) {
// @phpstan-ignore-next-line
throw new Exception($e->getMessage(), (int) $e->getCode(), $e); throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
+206 -130
View File
@@ -4,28 +4,36 @@ declare(strict_types=1);
namespace Pest\Logging; namespace Pest\Logging;
use function getmypid; use Pest\Concerns\Logging\WritesToConsole;
use Pest\Concerns\Testable; use Pest\Concerns\Testable;
use Pest\Support\ExceptionTrace;
use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test; use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestResult; use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite; use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning; use PHPUnit\Framework\Warning;
use PHPUnit\Runner\PhptTestCase;
use PHPUnit\TextUI\DefaultResultPrinter; use PHPUnit\TextUI\DefaultResultPrinter;
use Throwable;
use function getmypid;
use function Pest\version;
use function round; use function round;
use function str_replace; use function str_replace;
use Throwable; use function strlen;
final class TeamCity extends DefaultResultPrinter final class TeamCity extends DefaultResultPrinter
{ {
use WritesToConsole;
private const PROTOCOL = 'pest_qn://'; private const PROTOCOL = 'pest_qn://';
private const NAME = 'name'; private const NAME = 'name';
private const LOCATION_HINT = 'locationHint'; private const LOCATION_HINT = 'locationHint';
private const DURATION = 'duration'; private const DURATION = 'duration';
private const TEST_SUITE_STARTED = 'testSuiteStarted'; private const TEST_SUITE_STARTED = 'testSuiteStarted';
private const TEST_SUITE_FINISHED = 'testSuiteFinished'; private const TEST_SUITE_FINISHED = 'testSuiteFinished';
private const TEST_COUNT = 'testCount';
private const TEST_STARTED = 'testStarted';
private const TEST_FINISHED = 'testFinished';
/** @var int */ /** @var int */
private $flowId; private $flowId;
@@ -36,154 +44,105 @@ final class TeamCity extends DefaultResultPrinter
/** @var \PHPUnit\Util\Log\TeamCity */ /** @var \PHPUnit\Util\Log\TeamCity */
private $phpunitTeamCity; private $phpunitTeamCity;
public function __construct(bool $verbose, string $colors) /**
* @param resource|string|null $out
*/
public function __construct($out, bool $verbose, string $colors)
{ {
parent::__construct(null, $verbose, $colors, false, 80, false); parent::__construct($out, $verbose, $colors);
$this->phpunitTeamCity = new \PHPUnit\Util\Log\TeamCity( $this->phpunitTeamCity = new \PHPUnit\Util\Log\TeamCity($out, $verbose, $colors);
null,
$verbose, $this->logo();
$colors, }
false,
80, private function logo(): void
false {
); $this->writeNewLine();
$this->write('Pest ' . version());
$this->writeNewLine();
} }
public function printResult(TestResult $result): void public function printResult(TestResult $result): void
{ {
$this->printHeader($result); $this->write('Tests: ');
$this->printFooter($result);
$results = [
'failed' => ['count' => $result->errorCount() + $result->failureCount(), 'color' => 'fg-red'],
'skipped' => ['count' => $result->skippedCount(), 'color' => 'fg-yellow'],
'warned' => ['count' => $result->warningCount(), 'color' => 'fg-yellow'],
'risked' => ['count' => $result->riskyCount(), 'color' => 'fg-yellow'],
'incomplete' => ['count' => $result->notImplementedCount(), 'color' => 'fg-yellow'],
'passed' => ['count' => $this->successfulTestCount($result), 'color' => 'fg-green'],
];
$filteredResults = array_filter($results, function ($item): bool {
return $item['count'] > 0;
});
foreach ($filteredResults as $key => $info) {
$this->writeWithColor($info['color'], $info['count'] . " $key", false);
/* @phpstan-ignore-next-line */
if ($key !== array_reverse(array_keys($filteredResults))[0]) {
$this->write(', ');
}
}
$this->writeNewLine();
$this->write("Assertions: $this->numAssertions");
$this->writeNewLine();
$this->write("Time: {$result->time()}s");
$this->writeNewLine();
}
private function successfulTestCount(TestResult $result): int
{
return $result->count()
- $result->failureCount()
- $result->errorCount()
- $result->skippedCount()
- $result->warningCount()
- $result->notImplementedCount()
- $result->riskyCount();
} }
/** @phpstan-ignore-next-line */
public function startTestSuite(TestSuite $suite): void public function startTestSuite(TestSuite $suite): void
{ {
$suiteName = $suite->getName();
if (static::isCompoundTestSuite($suite)) {
$this->writeWithColor('bold', ' ' . $suiteName);
} elseif (static::isPestTestSuite($suite)) {
$this->writeWithColor('fg-white, bold', ' ' . substr_replace($suiteName, '', 0, 2) . ' ');
} else {
$this->writeWithColor('fg-white, bold', ' ' . $suiteName);
}
$this->writeNewLine();
$this->flowId = (int) getmypid(); $this->flowId = (int) getmypid();
if (!$this->isSummaryTestCountPrinted) { if (!$this->isSummaryTestCountPrinted) {
$this->printEvent( $this->printEvent(self::TEST_COUNT, [
'testCount', 'count' => $suite->count(),
['count' => $suite->count()] ]);
);
$this->isSummaryTestCountPrinted = true; $this->isSummaryTestCountPrinted = true;
} }
$suiteName = $suite->getName(); $this->printEvent(self::TEST_SUITE_STARTED, [
self::NAME => static::isCompoundTestSuite($suite) ? $suiteName : substr($suiteName, 2),
if (file_exists($suiteName) || !method_exists($suiteName, '__getFileName')) { self::LOCATION_HINT => self::PROTOCOL . (static::isCompoundTestSuite($suite) ? $suiteName : $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|Testable $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|Testable $test
*/
public function endTest(Test $test, float $time): void
{
if (!TeamCity::isPestTest($test)) {
$this->phpunitTeamCity->endTest($test, $time);
return;
}
if ($test instanceof TestCase) {
$this->numAssertions += $test->getNumAssertions();
} elseif ($test instanceof PhptTestCase) {
$this->numAssertions++;
}
$this->printEvent('testFinished', [
self::NAME => $test->getName(),
self::DURATION => self::toMilliseconds($time),
]);
}
/**
* @param Test|Testable $test
*/
public function addError(Test $test, Throwable $t, float $time): void
{
$this->phpunitTeamCity->addError($test, $t, $time);
}
/**
* @phpstan-ignore-next-line
*
* @param Test|Testable $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 * @param array<string, string|int> $params
*/ */
private function printEvent(string $eventName, array $params = []): void private function printEvent(string $eventName, array $params = []): void
{ {
$this->write("\n##teamcity[{$eventName}"); $this->write("##teamcity[{$eventName}");
if ($this->flowId !== 0) { if ($this->flowId !== 0) {
$params['flowId'] = $this->flowId; $params['flowId'] = $this->flowId;
@@ -206,9 +165,55 @@ final class TeamCity extends DefaultResultPrinter
); );
} }
private static function toMilliseconds(float $time): int public function endTestSuite(TestSuite $suite): void
{ {
return (int) round($time * 1000); $suiteName = $suite->getName();
$this->writeNewLine();
$this->writeNewLine();
$this->printEvent(self::TEST_SUITE_FINISHED, [
self::NAME => static::isCompoundTestSuite($suite) ? $suiteName : substr($suiteName, 2),
self::LOCATION_HINT => self::PROTOCOL . (static::isCompoundTestSuite($suite) ? $suiteName : $suiteName::__getFileName()),
]);
}
/**
* @param Test|Testable $test
*/
public function startTest(Test $test): void
{
if (!TeamCity::isPestTest($test)) {
$this->phpunitTeamCity->startTest($test);
return;
}
$this->printEvent(self::TEST_STARTED, [
self::NAME => $test->getName(),
// @phpstan-ignore-next-line
self::LOCATION_HINT => self::PROTOCOL . $test->toString(),
]);
}
/**
* Verify that the given test suite is a valid Pest suite.
*
* @param TestSuite<Test> $suite
*/
private static function isPestTestSuite(TestSuite $suite): bool
{
return strncmp($suite->getName(), 'P\\', strlen('P\\')) === 0;
}
/**
* Determine if the test suite is made up of multiple smaller test suites.
*
* @param TestSuite<Test> $suite
*/
private static function isCompoundTestSuite(TestSuite $suite): bool
{
return file_exists($suite->getName()) || !method_exists($suite->getName(), '__getFileName');
} }
public static function isPestTest(Test $test): bool public static function isPestTest(Test $test): bool
@@ -218,4 +223,75 @@ final class TeamCity extends DefaultResultPrinter
return in_array(Testable::class, $uses, true); return in_array(Testable::class, $uses, true);
} }
/**
* @param Test|Testable $test
*/
public function endTest(Test $test, float $time): void
{
$this->printEvent(self::TEST_FINISHED, [
self::NAME => $test->getName(),
self::DURATION => self::toMilliseconds($time),
]);
if (!$this->lastTestFailed) {
$this->writeSuccess($test->getName());
}
$this->numAssertions += $test instanceof TestCase ? $test->getNumAssertions() : 1;
$this->lastTestFailed = false;
}
private static function toMilliseconds(float $time): int
{
return (int) round($time * 1000);
}
public function addError(Test $test, Throwable $t, float $time): void
{
$this->markAsFailure($t);
$this->writeError($test->getName());
$this->phpunitTeamCity->addError($test, $t, $time);
}
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->markAsFailure($e);
$this->writeError($test->getName());
$this->phpunitTeamCity->addFailure($test, $e, $time);
}
public function addWarning(Test $test, Warning $e, float $time): void
{
$this->markAsFailure($e);
$this->writeWarning($test->getName());
$this->phpunitTeamCity->addWarning($test, $e, $time);
}
public function addIncompleteTest(Test $test, Throwable $t, float $time): void
{
$this->markAsFailure($t);
$this->writeWarning($test->getName());
$this->phpunitTeamCity->addIncompleteTest($test, $t, $time);
}
public function addRiskyTest(Test $test, Throwable $t, float $time): void
{
$this->markAsFailure($t);
$this->writeWarning($test->getName());
$this->phpunitTeamCity->addRiskyTest($test, $t, $time);
}
public function addSkippedTest(Test $test, Throwable $t, float $time): void
{
$this->markAsFailure($t);
$this->writeWarning($test->getName());
$this->phpunitTeamCity->printIgnoredTest($test->getName(), $t, $time);
}
private function markAsFailure(Throwable $t): void
{
$this->lastTestFailed = true;
ExceptionTrace::removePestReferences($t);
}
} }
+6 -2
View File
@@ -51,6 +51,8 @@ final class OppositeExpectation
* Handle dynamic method calls into the original expectation. * Handle dynamic method calls into the original expectation.
* *
* @param array<int, mixed> $arguments * @param array<int, mixed> $arguments
*
* @return Expectation|never
*/ */
public function __call(string $name, array $arguments): Expectation public function __call(string $name, array $arguments): Expectation
{ {
@@ -61,12 +63,13 @@ final class OppositeExpectation
return $this->original; return $this->original;
} }
// @phpstan-ignore-next-line
$this->throwExpectationFailedException($name, $arguments); $this->throwExpectationFailedException($name, $arguments);
} }
/** /**
* Handle dynamic properties gets into the original expectation. * Handle dynamic properties gets into the original expectation.
*
* @return Expectation|never
*/ */
public function __get(string $name): Expectation public function __get(string $name): Expectation
{ {
@@ -77,7 +80,6 @@ final class OppositeExpectation
return $this->original; return $this->original;
} }
// @phpstan-ignore-next-line
$this->throwExpectationFailedException($name); $this->throwExpectationFailedException($name);
} }
@@ -85,6 +87,8 @@ final class OppositeExpectation
* Creates a new expectation failed exception with a nice readable message. * Creates a new expectation failed exception with a nice readable message.
* *
* @param array<int, mixed> $arguments * @param array<int, mixed> $arguments
*
* @return never
*/ */
private function throwExpectationFailedException(string $name, array $arguments = []): void private function throwExpectationFailedException(string $name, array $arguments = []): void
{ {
+29 -6
View File
@@ -78,11 +78,31 @@ final class TestCall
return $this; return $this;
} }
/**
* Asserts that the test throws the given `$exceptionClass` when called if the given condition is true.
*
* @param (callable(): bool)|bool $condition
*/
public function throwsIf($condition, string $exception, string $exceptionMessage = null): TestCall
{
$condition = is_callable($condition)
? $condition
: static function () use ($condition): bool {
return (bool) $condition; // @phpstan-ignore-line
};
if ($condition()) {
return $this->throws($exception, $exceptionMessage);
}
return $this;
}
/** /**
* 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 array<\Closure|iterable<int|string, mixed>|string> $data * @param array<Closure|iterable<int|string, mixed>|string> $data
*/ */
public function with(...$data): TestCall public function with(...$data): TestCall
{ {
@@ -140,7 +160,7 @@ final class TestCall
$condition = is_callable($condition) $condition = is_callable($condition)
? $condition ? $condition
: function () use ($condition) { /* @phpstan-ignore-line */ : function () use ($condition) {
return $condition; return $condition;
}; };
@@ -148,6 +168,9 @@ final class TestCall
? $conditionOrMessage ? $conditionOrMessage
: $message; : $message;
/** @var callable(): bool $condition */
$condition = $condition->bindTo(null);
$this->testCaseFactory $this->testCaseFactory
->chains ->chains
->addWhen($condition, Backtrace::file(), Backtrace::line(), 'markTestSkipped', [$message]); ->addWhen($condition, Backtrace::file(), Backtrace::line(), 'markTestSkipped', [$message]);
@@ -160,7 +183,7 @@ final class TestCall
*/ */
public function __get(string $name): self public function __get(string $name): self
{ {
return $this->addChain($name); return $this->addChain(Backtrace::file(), Backtrace::line(), $name);
} }
/** /**
@@ -170,7 +193,7 @@ final class TestCall
*/ */
public function __call(string $name, array $arguments): self public function __call(string $name, array $arguments): self
{ {
return $this->addChain($name, $arguments); return $this->addChain(Backtrace::file(), Backtrace::line(), $name, $arguments);
} }
/** /**
@@ -178,11 +201,11 @@ final class TestCall
* *
* @param array<int, mixed>|null $arguments * @param array<int, mixed>|null $arguments
*/ */
private function addChain(string $name, array $arguments = null): self private function addChain(string $file, int $line, string $name, array $arguments = null): self
{ {
$this->testCaseFactory $this->testCaseFactory
->chains ->chains
->add(Backtrace::file(), Backtrace::line(), $name, $arguments); ->add($file, $line, $name, $arguments);
if ($this->descriptionLess) { if ($this->descriptionLess) {
$exporter = new Exporter(); $exporter = new Exporter();
+1 -1
View File
@@ -6,7 +6,7 @@ namespace Pest;
function version(): string function version(): string
{ {
return '1.12.0'; return '1.23.1';
} }
function testDirectory(string $file = ''): string function testDirectory(string $file = ''): string
+67
View File
@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace Pest\Plugins;
use Pest\Contracts\Plugins\HandlesArguments;
/**
* @internal
*/
final class Environment implements HandlesArguments
{
/**
* The continuous integration environment.
*/
public const CI = 'ci';
/**
* The local environment.
*/
public const LOCAL = 'local';
/**
* @var \Pest\Plugins\Environment|null
*/
private static $instance;
/**
* The current environment.
*
* @var string|null
*/
private static $name;
/**
* Allows to handle custom command line arguments.
*
* @param array<int, string> $arguments
*
* @return array<int, string> the updated list of arguments
*/
public function handleArguments(array $arguments): array
{
foreach ($arguments as $index => $argument) {
if ($argument === '--ci') {
unset($arguments[$index]);
self::$name = self::CI;
}
}
return array_values($arguments);
}
/**
* Gets the environment name.
*/
public static function name(string $name = null): string
{
if (is_string($name)) {
self::$name = $name;
}
return self::$name ?? self::LOCAL;
}
}
+2 -1
View File
@@ -5,9 +5,10 @@ declare(strict_types=1);
namespace Pest\Plugins; namespace Pest\Plugins;
use Pest\Contracts\Plugins\HandlesArguments; use Pest\Contracts\Plugins\HandlesArguments;
use function Pest\version;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use function Pest\version;
/** /**
* @internal * @internal
*/ */
+43 -4
View File
@@ -5,11 +5,14 @@ declare(strict_types=1);
namespace Pest\Repositories; namespace Pest\Repositories;
use Closure; use Closure;
use Pest\Exceptions\DatasetMissing;
use Pest\Exceptions\ShouldNotHappen; use Pest\Exceptions\ShouldNotHappen;
use Pest\Exceptions\TestAlreadyExist; use Pest\Exceptions\TestAlreadyExist;
use Pest\Exceptions\TestCaseAlreadyInUse; use Pest\Exceptions\TestCaseAlreadyInUse;
use Pest\Exceptions\TestCaseClassOrTraitNotFound; use Pest\Exceptions\TestCaseClassOrTraitNotFound;
use Pest\Factories\TestCaseFactory; use Pest\Factories\TestCaseFactory;
use Pest\Plugins\Environment;
use Pest\Support\Reflection;
use Pest\Support\Str; use Pest\Support\Str;
use Pest\TestSuite; use Pest\TestSuite;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@@ -20,7 +23,7 @@ use PHPUnit\Framework\TestCase;
final class TestRepository final class TestRepository
{ {
/** /**
* @var string * @var non-empty-string
*/ */
private const SEPARATOR = '>>>'; private const SEPARATOR = '>>>';
@@ -42,6 +45,20 @@ final class TestRepository
return count($this->state); return count($this->state);
} }
/**
* Returns the filename of each test that should be executed in the suite.
*
* @return array<int, string>
*/
public function getFilenames(): array
{
$testsWithOnly = $this->testsUsingOnly();
return array_values(array_map(function (TestCaseFactory $factory): string {
return $factory->filename;
}, count($testsWithOnly) > 0 ? $testsWithOnly : $this->state));
}
/** /**
* Calls the given callable foreach test case. * Calls the given callable foreach test case.
*/ */
@@ -83,9 +100,7 @@ final class TestRepository
} }
} }
$onlyState = array_filter($this->state, function ($testFactory): bool { $onlyState = $this->testsUsingOnly();
return $testFactory->only;
});
$state = count($onlyState) > 0 ? $onlyState : $this->state; $state = count($onlyState) > 0 ? $onlyState : $this->state;
@@ -98,6 +113,22 @@ final class TestRepository
} }
} }
/**
* Return all tests that have called the only method.
*
* @return array<TestCaseFactory>
*/
private function testsUsingOnly(): array
{
if (Environment::name() === Environment::CI) {
return [];
}
return array_filter($this->state, function ($testFactory): bool {
return $testFactory->only;
});
}
/** /**
* Uses the given `$testCaseClass` on the given `$paths`. * Uses the given `$testCaseClass` on the given `$paths`.
* *
@@ -140,6 +171,14 @@ final class TestRepository
throw new TestAlreadyExist($test->filename, $test->description); throw new TestAlreadyExist($test->filename, $test->description);
} }
if (!$test->receivesArguments()) {
$arguments = Reflection::getFunctionArguments($test->test);
if (count($arguments) > 0) {
throw new DatasetMissing($test->filename, $test->description, $arguments);
}
}
$this->state[sprintf('%s%s%s', $test->filename, self::SEPARATOR, $test->description)] = $test; $this->state[sprintf('%s%s%s', $test->filename, self::SEPARATOR, $test->description)] = $test;
} }
} }
+16 -3
View File
@@ -26,6 +26,7 @@ final class Backtrace
$current = null; $current = null;
foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) { foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) {
assert(array_key_exists(self::FILE, $trace));
if (Str::endsWith($trace[self::FILE], (string) realpath('vendor/phpunit/phpunit/src/Util/FileLoader.php'))) { if (Str::endsWith($trace[self::FILE], (string) realpath('vendor/phpunit/phpunit/src/Util/FileLoader.php'))) {
break; break;
} }
@@ -45,7 +46,11 @@ final class Backtrace
*/ */
public static function file(): string public static function file(): string
{ {
return debug_backtrace(self::BACKTRACE_OPTIONS)[1][self::FILE]; $trace = debug_backtrace(self::BACKTRACE_OPTIONS)[1];
assert(array_key_exists(self::FILE, $trace));
return $trace[self::FILE];
} }
/** /**
@@ -53,7 +58,11 @@ final class Backtrace
*/ */
public static function dirname(): string public static function dirname(): string
{ {
return dirname(debug_backtrace(self::BACKTRACE_OPTIONS)[1][self::FILE]); $trace = debug_backtrace(self::BACKTRACE_OPTIONS)[1];
assert(array_key_exists(self::FILE, $trace));
return dirname($trace[self::FILE]);
} }
/** /**
@@ -61,6 +70,10 @@ final class Backtrace
*/ */
public static function line(): int public static function line(): int
{ {
return debug_backtrace(self::BACKTRACE_OPTIONS)[1]['line']; $trace = debug_backtrace(self::BACKTRACE_OPTIONS)[1];
assert(array_key_exists('line', $trace));
return $trace['line'];
} }
} }
+17 -2
View File
@@ -30,14 +30,21 @@ final class Coverage
} }
/** /**
* Runs true there is any code * Runs true there is any code coverage driver available.
* coverage driver available.
*/ */
public static function isAvailable(): bool public static function isAvailable(): bool
{ {
return (new Runtime())->canCollectCodeCoverage(); return (new Runtime())->canCollectCodeCoverage();
} }
/**
* If the user is using Xdebug.
*/
public static function usingXdebug(): bool
{
return (new Runtime())->hasXdebug();
}
/** /**
* Reports the code coverage report to the * Reports the code coverage report to the
* console and returns the result in float. * console and returns the result in float.
@@ -45,6 +52,14 @@ final class Coverage
public static function report(OutputInterface $output): float public static function report(OutputInterface $output): float
{ {
if (!file_exists($reportPath = self::getPath())) { if (!file_exists($reportPath = self::getPath())) {
if (self::usingXdebug()) {
$output->writeln(
" <fg=black;bg=yellow;options=bold> WARN </> Unable to get coverage using Xdebug. Did you set <href=https://xdebug.org/docs/code_coverage#mode>Xdebug's coverage mode</>?</>",
);
return 0.0;
}
throw ShouldNotHappen::fromMessage(sprintf('Coverage not found in path: %s.', $reportPath)); throw ShouldNotHappen::fromMessage(sprintf('Coverage not found in path: %s.', $reportPath));
} }
+27
View File
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Pest\Support; namespace Pest\Support;
use Closure; use Closure;
use ReflectionProperty;
use Throwable; use Throwable;
/** /**
@@ -36,4 +37,30 @@ final class ExceptionTrace
throw $throwable; throw $throwable;
} }
} }
/**
* Removes any item from the stack trace referencing Pest so as not to
* crowd the error log for the end user.
*/
public static function removePestReferences(Throwable $t): void
{
if (!property_exists($t, 'serializableTrace')) {
return;
}
$property = new ReflectionProperty($t, 'serializableTrace');
$property->setAccessible(true);
$trace = $property->getValue($t);
$cleanedTrace = [];
foreach ($trace as $item) {
if (key_exists('file', $item) && mb_strpos($item['file'], 'vendor/pestphp/pest/') > 0) {
continue;
}
$cleanedTrace[] = $item;
}
$property->setValue($t, $cleanedTrace);
}
} }
+3 -1
View File
@@ -8,6 +8,8 @@ use Closure;
use ReflectionClass; use ReflectionClass;
use Throwable; use Throwable;
use const PHP_MAJOR_VERSION;
/** /**
* @internal * @internal
*/ */
@@ -132,7 +134,7 @@ final class HigherOrderMessage
private static function getUndefinedMethodMessage(object $target, string $methodName): string private static function getUndefinedMethodMessage(object $target, string $methodName): string
{ {
if (\PHP_MAJOR_VERSION >= 8) { if (PHP_MAJOR_VERSION >= 8) {
return sprintf(sprintf(self::UNDEFINED_METHOD, sprintf('%s::%s()', get_class($target), $methodName))); return sprintf(sprintf(self::UNDEFINED_METHOD, sprintf('%s::%s()', get_class($target), $methodName)));
} }
@@ -53,4 +53,20 @@ final class HigherOrderMessageCollection
$message->call($target); $message->call($target);
} }
} }
/**
* Count the number of messages with the given name.
*
* @param string $name A higher order message name (usually a method name)
*/
public function count(string $name): int
{
return array_reduce(
$this->messages,
static function (int $total, HigherOrderMessage $message) use ($name): int {
return $total + (int) ($name === $message->name);
},
0,
);
}
} }
+34
View File
@@ -12,6 +12,7 @@ use ReflectionException;
use ReflectionFunction; use ReflectionFunction;
use ReflectionNamedType; use ReflectionNamedType;
use ReflectionParameter; use ReflectionParameter;
use ReflectionUnionType;
/** /**
* @internal * @internal
@@ -170,4 +171,37 @@ final class Reflection
return $name; return $name;
} }
/**
* Receive a map of function argument names to their types.
*
* @return array<string, string>
*/
public static function getFunctionArguments(Closure $function): array
{
$parameters = (new ReflectionFunction($function))->getParameters();
$arguments = [];
foreach ($parameters as $parameter) {
/** @var ReflectionNamedType|ReflectionUnionType|null $types */
$types = ($parameter->hasType()) ? $parameter->getType() : null;
if (is_null($types)) {
$arguments[$parameter->getName()] = 'mixed';
continue;
}
$arguments[$parameter->getName()] = implode('|', array_map(
static function (ReflectionNamedType $type): string {
return $type->getName();
},
($types instanceof ReflectionNamedType)
? [$types] // NOTE: normalize as list of to handle unions
: $types->getTypes(),
));
}
return $arguments;
}
} }
+2 -2
View File
@@ -91,8 +91,8 @@ final class TestSuite
$this->afterEach = new AfterEachRepository(); $this->afterEach = new AfterEachRepository();
$this->afterAll = new AfterAllRepository(); $this->afterAll = new AfterAllRepository();
$this->rootPath = (string) realpath($rootPath); $this->rootPath = (string) realpath($rootPath);
$this->testPath = $testPath; $this->testPath = $testPath;
} }
/** /**
+4 -1
View File
@@ -11,7 +11,10 @@
| |
*/ */
uses(Tests\TestCase::class)->in('Feature'); uses(
Tests\TestCase::class,
// Illuminate\Foundation\Testing\RefreshDatabase::class,
)->in('Feature');
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
+150 -2
View File
@@ -96,11 +96,29 @@
✓ more than two datasets with (2) / (4) / (5) ✓ more than two datasets with (2) / (4) / (5)
✓ more than two datasets with (2) / (4) / (6) ✓ more than two datasets with (2) / (4) / (6)
✓ more than two datasets did the job right ✓ more than two datasets did the job right
✓ eager registered wrapped datasets with Generator functions with (1)
✓ eager registered wrapped datasets with Generator functions with (2)
✓ eager registered wrapped datasets with Generator functions with (3)
✓ eager registered wrapped datasets with Generator functions with (4)
✓ eager registered wrapped datasets with Generator functions did the job right
✓ eager registered wrapped datasets with Generator functions display description with data set "taylor"
✓ eager registered wrapped datasets with Generator functions display description with data set "james"
✓ it can resolve a dataset after the test case is available with (Closure Object (...))
✓ it can resolve a dataset after the test case is available with shared yield sets with (Closure Object (...)) #1
✓ it can resolve a dataset after the test case is available with shared yield sets with (Closure Object (...)) #2
✓ it can resolve a dataset after the test case is available with shared array sets with (Closure Object (...)) #1
✓ it can resolve a dataset after the test case is available with shared array sets with (Closure Object (...)) #2
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
✓ it catch exceptions and messages ✓ it catch exceptions and messages
✓ it can just define the message
✓ it not catch exceptions if given condition is false
✓ it catch exceptions if given condition is true
✓ it catch exceptions and messages if given condition is true
✓ it can just define the message if given condition is true
✓ it can just define the message if given condition is 1
PASS Tests\Features\Expect\HigherOrder\methods PASS Tests\Features\Expect\HigherOrder\methods
✓ it can access methods ✓ it can access methods
@@ -112,11 +130,14 @@
✓ it works with sequence ✓ it works with sequence
✓ it can compose complex expectations ✓ it can compose complex expectations
✓ it can handle nested method calls ✓ it can handle nested method calls
✓ it works with higher order tests
PASS Tests\Features\Expect\HigherOrder\methodsAndProperties PASS Tests\Features\Expect\HigherOrder\methodsAndProperties
✓ it can access methods and properties ✓ it can access methods and properties
✓ it can handle nested methods and properties ✓ it can handle nested methods and properties
✓ it works with higher order tests
✓ it can start a new higher order expectation using the and syntax ✓ it can start a new higher order expectation using the and syntax
✓ it can start a new higher order expectation using the and syntax in higher order tests
PASS Tests\Features\Expect\HigherOrder\properties PASS Tests\Features\Expect\HigherOrder\properties
✓ it allows properties to be accessed from the value ✓ it allows properties to be accessed from the value
@@ -128,6 +149,7 @@
✓ it can compose complex expectations ✓ it can compose complex expectations
✓ it works with objects ✓ it works with objects
✓ it works with nested properties ✓ it works with nested properties
✓ it works with higher order tests
PASS Tests\Features\Expect\each PASS Tests\Features\Expect\each
✓ an exception is thrown if the the type is not iterable ✓ an exception is thrown if the the type is not iterable
@@ -148,6 +170,17 @@
✓ it properly parses json string ✓ it properly parses json string
✓ fails with broken json string ✓ fails with broken json string
PASS Tests\Features\Expect\matchExpectation
✓ it pass
✓ it failures
✓ it runs with truthy
✓ it runs with falsy
✓ it runs with truthy closure condition
✓ it runs with falsy closure condition
✓ it can be passed non-callable values
✓ it fails with unhandled match
✓ it can be used in higher order tests
PASS Tests\Features\Expect\not PASS Tests\Features\Expect\not
✓ not property calls ✓ not property calls
@@ -158,7 +191,7 @@
✓ an exception is thrown if the the type is not iterable ✓ an exception is thrown if the the type is not iterable
✓ allows for sequences of checks to be run on iterable data ✓ allows for sequences of checks to be run on iterable data
✓ loops back to the start if it runs out of sequence items ✓ loops back to the start if it runs out of sequence items
it works if the number of items in the iterable is smaller than the number of expectations fails if the number of iterable items is greater than the number of expectations
✓ it works with associative arrays ✓ it works with associative arrays
✓ it can be passed non-callable values ✓ it can be passed non-callable values
✓ it can be passed a mixture of value types ✓ it can be passed a mixture of value types
@@ -196,6 +229,20 @@
PASS Tests\Features\Expect\toBeFalse PASS Tests\Features\Expect\toBeFalse
✓ strict comparisons ✓ strict comparisons
✓ failures ✓ failures
✓ not failures
PASS Tests\Features\Expect\toBeFalsy
✓ passes as falsy with (false)
✓ passes as falsy with ('')
✓ passes as falsy with (null)
✓ passes as falsy with (0)
✓ passes as falsy with ('0')
✓ passes as not falsy with (true)
✓ passes as not falsy with (1) #1
✓ passes as not falsy with ('false')
✓ passes as not falsy with (1) #2
✓ passes as not falsy with (-1)
✓ failures
✓ not failures ✓ not failures
PASS Tests\Features\Expect\toBeFile PASS Tests\Features\Expect\toBeFile
@@ -216,6 +263,11 @@
PASS Tests\Features\Expect\toBeGreatherThanOrEqual PASS Tests\Features\Expect\toBeGreatherThanOrEqual
✓ passes ✓ passes
✓ failures ✓ failures
✓ not failures
PASS Tests\Features\Expect\toBeIn
✓ passes
✓ failures
✓ not failures ✓ not failures
PASS Tests\Features\Expect\toBeInfinite PASS Tests\Features\Expect\toBeInfinite
@@ -301,6 +353,20 @@
PASS Tests\Features\Expect\toBeTrue PASS Tests\Features\Expect\toBeTrue
✓ strict comparisons ✓ strict comparisons
✓ failures ✓ failures
✓ not failures
PASS Tests\Features\Expect\toBeTruthy
✓ passes as truthy with (true)
✓ passes as truthy with (1) #1
✓ passes as truthy with ('false')
✓ passes as truthy with (1) #2
✓ passes as truthy with (-1)
✓ passes as not truthy with (false)
✓ passes as not truthy with ('')
✓ passes as not truthy with (null)
✓ passes as not truthy with (0)
✓ passes as not truthy with ('0')
✓ failures
✓ not failures ✓ not failures
PASS Tests\Features\Expect\toBeWritableDirectory PASS Tests\Features\Expect\toBeWritableDirectory
@@ -315,9 +381,16 @@
PASS Tests\Features\Expect\toContain PASS Tests\Features\Expect\toContain
✓ passes strings ✓ passes strings
✓ passes strings with multiple needles
✓ passes arrays ✓ passes arrays
✓ passes arrays with multiple needles
✓ passes with array needles
✓ failures ✓ failures
✓ failures with multiple needles (all failing)
✓ failures with multiple needles (some failing)
✓ not failures ✓ not failures
✓ not failures with multiple needles (all failing)
✓ not failures with multiple needles (some failing)
PASS Tests\Features\Expect\toEndWith PASS Tests\Features\Expect\toEndWith
✓ pass ✓ pass
@@ -367,6 +440,25 @@
PASS Tests\Features\Expect\toHaveKeys PASS Tests\Features\Expect\toHaveKeys
✓ pass ✓ pass
✓ failures ✓ failures
✓ not failures
PASS Tests\Features\Expect\toHaveLength
✓ it passes with ('Fortaleza')
✓ it passes with ('Sollefteå')
✓ it passes with ('Ιεράπετρα')
✓ it passes with (stdClass Object (...))
✓ it passes with (Illuminate\Support\Collection Object (...))
✓ it passes with array
✓ it passes with *not*
✓ it properly fails with *not*
✓ it fails with (1)
✓ it fails with (1.5)
✓ it fails with (true)
✓ it fails with (null)
PASS Tests\Features\Expect\toHaveProperties
✓ pass
✓ failures
✓ not failures ✓ not failures
PASS Tests\Features\Expect\toHaveProperty PASS Tests\Features\Expect\toHaveProperty
@@ -399,6 +491,39 @@
✓ failures ✓ failures
✓ not failures ✓ not failures
PASS Tests\Features\Expect\toThrow
✓ passes
✓ failures 1
✓ failures 2
✓ failures 3
✓ failures 4
✓ failures 5
✓ failures 6
✓ failures 7
✓ not failures
✓ closure missing parameter
✓ closure missing type-hint
✓ it can handle a non-defined exception
✓ it can handle a class not found Error
PASS Tests\Features\Expect\unless
✓ it pass
✓ it failures
✓ it runs with truthy
✓ it skips with falsy
✓ it runs with truthy closure condition
✓ it skips with falsy closure condition
✓ it can be used in higher order tests
PASS Tests\Features\Expect\when
✓ it pass
✓ it failures
✓ it runs with truthy
✓ it skips with falsy
✓ it runs with truthy closure condition
✓ it skips with falsy closure condition
✓ it can be used in higher order tests
PASS Tests\Features\Helpers PASS Tests\Features\Helpers
✓ it can set/get properties on $this ✓ it can set/get properties on $this
✓ it throws error if property do not exist ✓ it throws error if property do not exist
@@ -411,7 +536,12 @@
✓ it proxies calls to object ✓ it proxies calls to object
✓ it is capable doing multiple assertions ✓ it is capable doing multiple assertions
✓ it resolves expect callables correctly ✓ it resolves expect callables correctly
✓ does not treat method names as callables
✓ it can tap into the test ✓ it can tap into the test
✓ it can pass datasets into the expect callables with (1, 2, 3)
✓ it can pass datasets into the tap callable with (1, 2, 3)
✓ it can pass shared datasets into callables with (1)
✓ it can pass shared datasets into callables with (2)
WARN Tests\Features\Incompleted WARN Tests\Features\Incompleted
… incompleted … incompleted
@@ -444,6 +574,8 @@
✓ 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 - it skips when skip after assertion
- it can use something in the test case as a condition → This test was skipped
- it can user higher order callables and skip
PASS Tests\Features\Test PASS Tests\Features\Test
✓ a test ✓ a test
@@ -457,12 +589,14 @@
PASS Tests\Hooks\AfterAllTest PASS Tests\Hooks\AfterAllTest
✓ global afterAll execution order ✓ global afterAll execution order
✓ it only gets called once per file
PASS Tests\Hooks\AfterEachTest PASS Tests\Hooks\AfterEachTest
✓ global afterEach execution order ✓ global afterEach execution order
PASS Tests\Hooks\BeforeAllTest PASS Tests\Hooks\BeforeAllTest
✓ global beforeAll execution order ✓ global beforeAll execution order
✓ it only gets called once per file
PASS Tests\Hooks\BeforeEachTest PASS Tests\Hooks\BeforeEachTest
✓ global beforeEach execution order ✓ global beforeEach execution order
@@ -532,6 +666,10 @@
✓ it show the actual dataset of multiple non-named datasets in their description ✓ it show the actual dataset of multiple non-named datasets in their description
✓ it show the correct description for mixed named and not-named datasets ✓ it show the correct description for mixed named and not-named datasets
PASS Tests\Unit\Plugins\Environment
✓ environment is set to CI when --ci option is used
✓ environment is set to Local when --ci option is not used
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
@@ -554,10 +692,17 @@
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
✓ it alerts users about tests with arguments but no input
✓ it can return an array of all test suite filenames
✓ it can filter the test suite filenames to those with the only method
✓ it does not filter the test suite filenames to those with the only method when working in CI pipeline
PASS Tests\Visual\Help PASS Tests\Visual\Help
✓ visual snapshot of help command output ✓ visual snapshot of help command output
PASS Tests\Visual\JUnit
✓ it is can successfully call all public methods
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
@@ -567,6 +712,9 @@
WARN Tests\Visual\Success WARN Tests\Visual\Success
- visual snapshot of test suite on success - visual snapshot of test suite on success
PASS Tests\Visual\TeamCity
✓ it is can successfully call all public methods
PASS Tests\Features\Depends PASS Tests\Features\Depends
✓ first ✓ first
✓ second ✓ second
@@ -581,5 +729,5 @@
✓ it is a test ✓ it is a test
✓ it uses correct parent class ✓ it uses correct parent class
Tests: 4 incompleted, 7 skipped, 365 passed Tests: 4 incompleted, 9 skipped, 487 passed
+11
View File
@@ -0,0 +1,11 @@
<?php
dataset('bound.closure', function () {
yield function () { return 1; };
yield function () { return 2; };
});
dataset('bound.array', [
function () { return 1; },
function () { return 2; },
]);
+17
View File
@@ -13,3 +13,20 @@ dataset('numbers.closure.wrapped', function () {
dataset('numbers.array', [[1], [2]]); dataset('numbers.array', [[1], [2]]);
dataset('numbers.array.wrapped', [1, 2]); dataset('numbers.array.wrapped', [1, 2]);
dataset('numbers.generators.wrapped', function () {
yield from firstSetOfNumber();
yield from secondSetOfNumbers();
});
function firstSetOfNumber(): Generator
{
yield 1;
yield 2;
}
function secondSetOfNumbers(): Generator
{
yield 3;
yield 4;
}
+1 -1
View File
@@ -8,7 +8,7 @@ use Symfony\Component\Console\Output\ConsoleOutput;
it('has plugin')->assertTrue(class_exists(CoveragePlugin::class)); it('has plugin')->assertTrue(class_exists(CoveragePlugin::class));
it('adds coverage if --coverage exist', function () { it('adds coverage if --coverage exist', function () {
$plugin = new CoveragePlugin(new ConsoleOutput()); $plugin = new CoveragePlugin(new ConsoleOutput());
$testSuite = TestSuite::getInstance(); $testSuite = TestSuite::getInstance();
expect($plugin->coverage)->toBeFalse(); expect($plugin->coverage)->toBeFalse();
+46
View File
@@ -5,6 +5,10 @@ use Pest\Exceptions\DatasetAlreadyExist;
use Pest\Exceptions\DatasetDoesNotExist; use Pest\Exceptions\DatasetDoesNotExist;
use Pest\Plugin; use Pest\Plugin;
beforeEach(function () {
$this->foo = 'bar';
});
it('throws exception if dataset does not exist', function () { it('throws exception if dataset does not exist', function () {
$this->expectException(DatasetDoesNotExist::class); $this->expectException(DatasetDoesNotExist::class);
$this->expectExceptionMessage("A dataset with the name `first` does not exist. You can create it using `dataset('first', ['a', 'b']);`."); $this->expectExceptionMessage("A dataset with the name `first` does not exist. You can create it using `dataset('first', ['a', 'b']);`.");
@@ -223,3 +227,45 @@ test('more than two datasets', function ($text_a, $text_b, $text_c) use ($state,
test('more than two datasets did the job right', function () use ($state) { test('more than two datasets did the job right', function () use ($state) {
expect($state->text)->toBe('121212121212131423241314232411122122111221221112212213142324135136145146235236245246'); expect($state->text)->toBe('121212121212131423241314232411122122111221221112212213142324135136145146235236245246');
}); });
$wrapped_generator_state = new stdClass();
$wrapped_generator_state->text = '';
$wrapped_generator_function_datasets = [1, 2, 3, 4];
test(
'eager registered wrapped datasets with Generator functions',
function (int $text) use (
$wrapped_generator_state,
$wrapped_generator_function_datasets
) {
$wrapped_generator_state->text .= $text;
expect(in_array($text, $wrapped_generator_function_datasets))->toBe(true);
}
)->with('numbers.generators.wrapped');
test('eager registered wrapped datasets with Generator functions did the job right', function () use ($wrapped_generator_state) {
expect($wrapped_generator_state->text)->toBe('1234');
});
test('eager registered wrapped datasets with Generator functions display description', function ($wrapped_generator_state_with_description) {
expect($wrapped_generator_state_with_description)->not->toBeEmpty();
})->with(function () {
yield 'taylor' => 'taylor@laravel.com';
yield 'james' => 'james@laravel.com';
});
it('can resolve a dataset after the test case is available', function ($result) {
expect($result)->toBe('bar');
})->with([
function () {
return $this->foo;
},
]);
it('can resolve a dataset after the test case is available with shared yield sets', function ($result) {
expect($result)->toBeInt()->toBeLessThan(3);
})->with('bound.closure');
it('can resolve a dataset after the test case is available with shared array sets', function ($result) {
expect($result)->toBeInt()->toBeLessThan(3);
})->with('bound.array');
+20
View File
@@ -17,3 +17,23 @@ it('catch exceptions and messages', function () {
it('can just define the message', function () { it('can just define the message', function () {
throw new Exception('Something bad happened'); throw new Exception('Something bad happened');
})->throws('Something bad happened'); })->throws('Something bad happened');
it('not catch exceptions if given condition is false', function () {
$this->assertTrue(true);
})->throwsIf(false, Exception::class);
it('catch exceptions if given condition is true', function () {
throw new Exception('Something bad happened');
})->throwsIf(function () { return true; }, Exception::class);
it('catch exceptions and messages if given condition is true', function () {
throw new Exception('Something bad happened');
})->throwsIf(true, Exception::class, 'Something bad happened');
it('can just define the message if given condition is true', function () {
throw new Exception('Something bad happened');
})->throwsIf(true, 'Something bad happened');
it('can just define the message if given condition is 1', function () {
throw new Exception('Something bad happened');
})->throwsIf(1, 'Something bad happened');
+148
View File
@@ -0,0 +1,148 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
beforeEach(function () {
$this->matched = null;
});
it('pass', function () {
expect('baz')
->match('foo', [
'bar' => function ($value) {
$this->matched = 'bar';
return $value->toEqual('bar');
},
'foo' => function ($value) {
$this->matched = 'baz';
return $value->toEqual('baz');
},
]
)
->toEqual($this->matched);
expect(static::getCount())->toBe(2);
});
it('failures', function () {
expect(true)
->match('foo', [
'bar' => function ($value) {
return $value->toBeTrue();
},
'foo' => function ($value) {
return $value->toBeFalse();
},
]
);
})->throws(ExpectationFailedException::class, 'true is false');
it('runs with truthy', function () {
expect('foo')
->match(1, [
'bar' => function ($value) {
$this->matched = 'bar';
return $value->toEqual('bar');
},
true => function ($value) {
$this->matched = 'foo';
return $value->toEqual('foo');
},
]
)
->toEqual($this->matched);
expect(static::getCount())->toBe(2);
});
it('runs with falsy', function () {
expect('foo')
->match(false, [
'bar' => function ($value) {
$this->matched = 'bar';
return $value->toEqual('bar');
},
false => function ($value) {
$this->matched = 'foo';
return $value->toEqual('foo');
},
]
)
->toEqual($this->matched);
expect(static::getCount())->toBe(2);
});
it('runs with truthy closure condition', function () {
expect('foo')
->match(
function () { return '1'; }, [
'bar' => function ($value) {
$this->matched = 'bar';
return $value->toEqual('bar');
},
true => function ($value) {
$this->matched = 'foo';
return $value->toEqual('foo');
},
]
)
->toEqual($this->matched);
expect(static::getCount())->toBe(2);
});
it('runs with falsy closure condition', function () {
expect('foo')
->match(
function () { return '0'; }, [
'bar' => function ($value) {
$this->matched = 'bar';
return $value->toEqual('bar');
},
false => function ($value) {
$this->matched = 'foo';
return $value->toEqual('foo');
},
]
)
->toEqual($this->matched);
expect(static::getCount())->toBe(2);
});
it('can be passed non-callable values', function () {
expect('foo')
->match('pest', [
'bar' => 'foo',
'pest' => 'baz',
]
);
})->throws(ExpectationFailedException::class, 'two strings are equal');
it('fails with unhandled match', function () {
expect('foo')->match('bar', []);
})->throws(ExpectationFailedException::class, 'Unhandled match value.');
it('can be used in higher order tests')
->expect(true)
->match(
function () { return true; }, [
false => function ($value) {
return $value->toBeFalse();
},
true => function ($value) {
return $value->toBeTrue();
},
]
);
+42 -16
View File
@@ -1,5 +1,7 @@
<?php <?php
use PHPUnit\Framework\ExpectationFailedException;
test('an exception is thrown if the the type is not iterable', function () { test('an exception is thrown if the the type is not iterable', function () {
expect('Foobar')->each->sequence(); expect('Foobar')->each->sequence();
})->throws(BadMethodCallException::class, 'Expectation value is not iterable.'); })->throws(BadMethodCallException::class, 'Expectation value is not iterable.');
@@ -7,9 +9,15 @@ test('an exception is thrown if the the type is not iterable', function () {
test('allows for sequences of checks to be run on iterable data', function () { test('allows for sequences of checks to be run on iterable data', function () {
expect([1, 2, 3]) expect([1, 2, 3])
->sequence( ->sequence(
function ($expectation) { $expectation->toBeInt()->toEqual(1); }, function ($expectation) {
function ($expectation) { $expectation->toBeInt()->toEqual(2); }, $expectation->toBeInt()->toEqual(1);
function ($expectation) { $expectation->toBeInt()->toEqual(3); }, },
function ($expectation) {
$expectation->toBeInt()->toEqual(2);
},
function ($expectation) {
$expectation->toBeInt()->toEqual(3);
},
); );
expect(static::getCount())->toBe(6); expect(static::getCount())->toBe(6);
@@ -18,30 +26,46 @@ test('allows for sequences of checks to be run on iterable data', function () {
test('loops back to the start if it runs out of sequence items', function () { test('loops back to the start if it runs out of sequence items', function () {
expect([1, 2, 3, 1, 2, 3, 1, 2]) expect([1, 2, 3, 1, 2, 3, 1, 2])
->sequence( ->sequence(
function ($expectation) { $expectation->toBeInt()->toEqual(1); }, function ($expectation) {
function ($expectation) { $expectation->toBeInt()->toEqual(2); }, $expectation->toBeInt()->toEqual(1);
function ($expectation) { $expectation->toBeInt()->toEqual(3); }, },
function ($expectation) {
$expectation->toBeInt()->toEqual(2);
},
function ($expectation) {
$expectation->toBeInt()->toEqual(3);
},
); );
expect(static::getCount())->toBe(16); expect(static::getCount())->toBe(16);
}); });
test('it works if the number of items in the iterable is smaller than the number of expectations', function () { test('fails if the number of iterable items is greater than the number of expectations', function () {
expect([1, 2]) expect([1, 2])
->sequence( ->sequence(
function ($expectation) { $expectation->toBeInt()->toEqual(1); }, function ($expectation) {
function ($expectation) { $expectation->toBeInt()->toEqual(2); }, $expectation->toBeInt()->toEqual(1);
function ($expectation) { $expectation->toBeInt()->toEqual(3); }, },
function ($expectation) {
$expectation->toBeInt()->toEqual(2);
},
function ($expectation) {
$expectation->toBeInt()->toEqual(3);
},
); );
})->throws(ExpectationFailedException::class);
expect(static::getCount())->toBe(4);
});
test('it works with associative arrays', function () { test('it works with associative arrays', function () {
expect(['foo' => 'bar', 'baz' => 'boom']) expect(['foo' => 'bar', 'baz' => 'boom'])
->sequence( ->sequence(
function ($expectation, $key) { $expectation->toEqual('bar'); $key->toEqual('foo'); }, function ($expectation, $key) {
function ($expectation, $key) { $expectation->toEqual('boom'); $key->toEqual('baz'); }, $expectation->toEqual('bar');
$key->toEqual('foo');
},
function ($expectation, $key) {
$expectation->toEqual('boom');
$key->toEqual('baz');
},
); );
}); });
@@ -54,7 +78,9 @@ test('it can be passed non-callable values', function () {
test('it can be passed a mixture of value types', function () { test('it can be passed a mixture of value types', function () {
expect(['foo', 'bar', 'baz'])->sequence( expect(['foo', 'bar', 'baz'])->sequence(
'foo', 'foo',
function ($expectation) { $expectation->toEqual('bar')->toBeString(); }, function ($expectation) {
$expectation->toEqual('bar')->toBeString();
},
'baz' 'baz'
); );
+1 -1
View File
@@ -5,7 +5,7 @@ use PHPUnit\Framework\ExpectationFailedException;
expect(true)->toBeTrue()->and(false)->toBeFalse(); expect(true)->toBeTrue()->and(false)->toBeFalse();
test('strict comparisons', function () { test('strict comparisons', function () {
$nuno = new stdClass(); $nuno = new stdClass();
$dries = new stdClass(); $dries = new stdClass();
expect($nuno)->toBe($nuno)->not->toBe($dries); expect($nuno)->toBe($nuno)->not->toBe($dries);
+19
View File
@@ -0,0 +1,19 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('passes as falsy', function ($value) {
expect($value)->toBeFalsy();
})->with([false, '', null, 0, '0']);
test('passes as not falsy', function ($value) {
expect($value)->not->toBeFalsy();
})->with([true, [1], 'false', 1, -1]);
test('failures', function () {
expect(1)->toBeFalsy();
})->throws(ExpectationFailedException::class);
test('not failures', function () {
expect(null)->not->toBeFalsy();
})->throws(ExpectationFailedException::class);
+16
View File
@@ -0,0 +1,16 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('passes', function () {
expect('a')->toBeIn(['a', 'b', 'c']);
expect('d')->not->toBeIn(['a', 'b', 'c']);
});
test('failures', function () {
expect('d')->toBeIn(['a', 'b', 'c']);
})->throws(ExpectationFailedException::class);
test('not failures', function () {
expect('a')->not->toBeIn(['a', 'b', 'c']);
})->throws(ExpectationFailedException::class);
+19
View File
@@ -0,0 +1,19 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('passes as truthy', function ($value) {
expect($value)->toBeTruthy();
})->with([true, [1], 'false', 1, -1]);
test('passes as not truthy', function ($value) {
expect($value)->not->toBeTruthy();
})->with([false, '', null, 0, '0']);
test('failures', function () {
expect(null)->toBeTruthy();
})->throws(ExpectationFailedException::class);
test('not failures', function () {
expect(1)->not->toBeTruthy();
})->throws(ExpectationFailedException::class);
+30 -2
View File
@@ -3,17 +3,45 @@
use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\ExpectationFailedException;
test('passes strings', function () { test('passes strings', function () {
expect([1, 2, 42])->toContain(42); expect('Nuno')->toContain('Nu');
});
test('passes strings with multiple needles', function () {
expect('Nuno')->toContain('Nu', 'no');
}); });
test('passes arrays', function () { test('passes arrays', function () {
expect('Nuno')->toContain('Nu'); expect([1, 2, 42])->toContain(42);
});
test('passes arrays with multiple needles', function () {
expect([1, 2, 42])->toContain(42, 2);
});
test('passes with array needles', function () {
expect([[1, 2, 3], 2, 42])->toContain(42, [1, 2, 3]);
}); });
test('failures', function () { test('failures', function () {
expect([1, 2, 42])->toContain(3); expect([1, 2, 42])->toContain(3);
})->throws(ExpectationFailedException::class); })->throws(ExpectationFailedException::class);
test('failures with multiple needles (all failing)', function () {
expect([1, 2, 42])->toContain(3, 4);
})->throws(ExpectationFailedException::class);
test('failures with multiple needles (some failing)', function () {
expect([1, 2, 42])->toContain(1, 3, 4);
})->throws(ExpectationFailedException::class);
test('not failures', function () { test('not failures', function () {
expect([1, 2, 42])->not->toContain(42); expect([1, 2, 42])->not->toContain(42);
})->throws(ExpectationFailedException::class); })->throws(ExpectationFailedException::class);
test('not failures with multiple needles (all failing)', function () {
expect([1, 2, 42])->not->toContain(42, 2);
})->throws(ExpectationFailedException::class);
test('not failures with multiple needles (some failing)', function () {
expect([1, 2, 42])->not->toContain(42, 1);
})->throws(ExpectationFailedException::class);
+27
View File
@@ -0,0 +1,27 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
it('passes', function ($value) {
expect($value)->toHaveLength(9);
})->with([
'Fortaleza', 'Sollefteå', 'Ιεράπετρα',
(object) [1, 2, 3, 4, 5, 6, 7, 8, 9],
collect([1, 2, 3, 4, 5, 6, 7, 8, 9]),
]);
it('passes with array', function () {
expect([1, 2, 3])->toHaveLength(3);
});
it('passes with *not*', function () {
expect('')->not->toHaveLength(1);
});
it('properly fails with *not*', function () {
expect('pest')->not->toHaveLength(4);
})->throws(ExpectationFailedException::class);
it('fails', function ($value) {
expect($value)->toHaveLength(1);
})->with([1, 1.5, true, null])->throws(BadMethodCallException::class);
@@ -0,0 +1,26 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('pass', function () {
$object = new stdClass();
$object->name = 'Jhon';
$object->age = 21;
expect($object)->toHaveProperties(['name', 'age']);
});
test('failures', function () {
$object = new stdClass();
$object->name = 'Jhon';
expect($object)->toHaveProperties(['name', 'age']);
})->throws(ExpectationFailedException::class);
test('not failures', function () {
$object = new stdClass();
$object->name = 'Jhon';
$object->age = 21;
expect($object)->not->toHaveProperties(['name', 'age']);
})->throws(ExpectationFailedException::class);
+72
View File
@@ -0,0 +1,72 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('passes', function () {
expect(function () { throw new RuntimeException(); })->toThrow(RuntimeException::class);
expect(function () { throw new RuntimeException(); })->toThrow(Exception::class);
expect(function () { throw new RuntimeException(); })->toThrow(function (RuntimeException $e) {});
expect(function () { throw new RuntimeException('actual message'); })->toThrow(function (Exception $e) {
expect($e->getMessage())->toBe('actual message');
});
expect(function () {})->not->toThrow(Exception::class);
expect(function () { throw new RuntimeException('actual message'); })->toThrow('actual message');
expect(function () { throw new Exception(); })->not->toThrow(RuntimeException::class);
expect(function () { throw new RuntimeException('actual message'); })->toThrow(RuntimeException::class, 'actual message');
expect(function () { throw new RuntimeException('actual message'); })->toThrow(function (RuntimeException $e) {}, 'actual message');
});
test('failures 1', function () {
expect(function () {})->toThrow(RuntimeException::class);
})->throws(ExpectationFailedException::class, 'Exception "' . RuntimeException::class . '" not thrown.');
test('failures 2', function () {
expect(function () {})->toThrow(function (RuntimeException $e) {});
})->throws(ExpectationFailedException::class, 'Exception "' . RuntimeException::class . '" not thrown.');
test('failures 3', function () {
expect(function () { throw new Exception(); })->toThrow(function (RuntimeException $e) {});
})->throws(ExpectationFailedException::class, 'Failed asserting that Exception Object');
test('failures 4', function () {
expect(function () { throw new Exception('actual message'); })
->toThrow(function (Exception $e) {
expect($e->getMessage())->toBe('expected message');
});
})->throws(ExpectationFailedException::class, 'Failed asserting that two strings are identical');
test('failures 5', function () {
expect(function () { throw new Exception('actual message'); })->toThrow('expected message');
})->throws(ExpectationFailedException::class, 'Failed asserting that \'actual message\' contains "expected message".');
test('failures 6', function () {
expect(function () {})->toThrow('actual message');
})->throws(ExpectationFailedException::class, 'Exception with message "actual message" not thrown');
test('failures 7', function () {
expect(function () { throw new RuntimeException('actual message'); })->toThrow(RuntimeException::class, 'expected message');
})->throws(ExpectationFailedException::class);
test('not failures', function () {
expect(function () { throw new RuntimeException(); })->not->toThrow(RuntimeException::class);
})->throws(ExpectationFailedException::class);
test('closure missing parameter', function () {
expect(function () {})->toThrow(function () {});
})->throws(InvalidArgumentException::class, 'The given closure must have a single parameter type-hinted as the class string.');
test('closure missing type-hint', function () {
expect(function () {})->toThrow(function ($e) {});
})->throws(InvalidArgumentException::class, 'The given closure\'s parameter must be type-hinted as the class string.');
it('can handle a non-defined exception', function () {
expect(function () {
throw new NonExistingException();
})->toThrow(NonExistingException::class);
})->throws(Error::class);
it('can handle a class not found Error', function () {
expect(function () {
throw new NonExistingException();
})->toThrow(Error::class);
});
+101
View File
@@ -0,0 +1,101 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
beforeEach(function () {
$this->unlessObject = new stdClass();
$this->unlessObject->trueValue = true;
$this->unlessObject->foo = 'foo';
});
it('pass', function () {
expect('foo')
->unless(
true,
function ($value) {
return $value->toEqual('bar');
}
)
->toEqual('foo');
expect(static::getCount())->toBe(1);
});
it('failures', function () {
expect('foo')
->unless(
false,
function ($value) {
return $value->toBeTrue();
}
)
->toEqual('foo');
})->throws(ExpectationFailedException::class, 'is true');
it('runs with truthy', function () {
expect($this->unlessObject)
->unless(
0,
function ($value) {
return $value->trueValue->toBeTrue();
}
)
->foo->toEqual('foo');
expect(static::getCount())->toBe(2);
});
it('skips with falsy', function () {
expect($this->unlessObject)
->unless(
1,
function ($value) {
return $value->trueValue->toBeFalse(); // fails
}
)
->unless(
true,
function ($value) {
return $value->trueValue->toBeFalse(); // fails
}
)
->foo->toEqual('foo');
expect(static::getCount())->toBe(1);
});
it('runs with truthy closure condition', function () {
expect($this->unlessObject)
->unless(
function () { return '0'; },
function ($value) {
return $value->trueValue->toBeTrue();
}
)
->foo->toEqual('foo');
expect(static::getCount())->toBe(2);
});
it('skips with falsy closure condition', function () {
expect($this->unlessObject)
->unless(
function () { return '1'; },
function ($value) {
return $value->trueValue->toBeFalse(); // fails
}
)
->foo->toEqual('foo');
expect(static::getCount())->toBe(1);
});
it('can be used in higher order tests')
->expect(true)
->unless(
function () { return false; },
function ($value) {
return $value->toBeFalse();
}
)
->throws(ExpectationFailedException::class, 'true is false');
+101
View File
@@ -0,0 +1,101 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
beforeEach(function () {
$this->whenObject = new stdClass();
$this->whenObject->trueValue = true;
$this->whenObject->foo = 'foo';
});
it('pass', function () {
expect('foo')
->when(
true,
function ($value) {
return $value->toEqual('foo');
}
)
->toEqual('foo');
expect(static::getCount())->toBe(2);
});
it('failures', function () {
expect('foo')
->when(
true,
function ($value) {
return $value->toBeTrue();
}
)
->toEqual('foo');
})->throws(ExpectationFailedException::class, 'is true');
it('runs with truthy', function () {
expect($this->whenObject)
->when(
1,
function ($value) {
return $value->trueValue->toBeTrue();
}
)
->foo->toEqual('foo');
expect(static::getCount())->toBe(2);
});
it('skips with falsy', function () {
expect($this->whenObject)
->when(
0,
function ($value) {
return $value->trueValue->toBeFalse(); // fails
}
)
->when(
false,
function ($value) {
return $value->trueValue->toBeFalse(); // fails
}
)
->foo->toEqual('foo');
expect(static::getCount())->toBe(1);
});
it('runs with truthy closure condition', function () {
expect($this->whenObject)
->when(
function () { return '1'; },
function ($value) {
return $value->trueValue->toBeTrue();
}
)
->foo->toEqual('foo');
expect(static::getCount())->toBe(2);
});
it('skips with falsy closure condition', function () {
expect($this->whenObject)
->when(
function () { return '0'; },
function ($value) {
return $value->trueValue->toBeFalse(); // fails
}
)
->foo->toEqual('foo');
expect(static::getCount())->toBe(1);
});
it('can be used in higher order tests')
->expect(false)
->when(
function () { return true; },
function ($value) {
return $value->toBeTrue();
}
)
->throws(ExpectationFailedException::class, 'false is true');
+1 -1
View File
@@ -7,7 +7,7 @@ global $globalHook;
// HACK: we have to determine our $globalHook->calls baseline. This is because // HACK: we have to determine our $globalHook->calls baseline. This is because
// two other tests are executed before this one due to filename ordering. // two other tests are executed before this one due to filename ordering.
$args = $_SERVER['argv'] ?? []; $args = $_SERVER['argv'] ?? [];
$single = isset($args[1]) && Str::endsWith(__FILE__, $args[1]); $single = (isset($args[1]) && Str::endsWith(__FILE__, $args[1])) || ($_SERVER['PEST_PARALLEL'] ?? false);
$offset = $single ? 0 : 2; $offset = $single ? 0 : 2;
uses()->beforeAll(function () use ($globalHook, $offset) { uses()->beforeAll(function () use ($globalHook, $offset) {
@@ -4,9 +4,10 @@ declare(strict_types=1);
namespace Tests\CustomTestCase; namespace Tests\CustomTestCase;
use function PHPUnit\Framework\assertTrue;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function PHPUnit\Framework\assertTrue;
class CustomTestCase extends TestCase class CustomTestCase extends TestCase
{ {
public function assertCustomTrue() public function assertCustomTrue()
@@ -4,9 +4,10 @@ declare(strict_types=1);
namespace Tests\CustomTestCase; namespace Tests\CustomTestCase;
use function PHPUnit\Framework\assertTrue;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function PHPUnit\Framework\assertTrue;
class ExecutedTest extends TestCase class ExecutedTest extends TestCase
{ {
public static $executed = false; public static $executed = false;
@@ -2,7 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Tests\SubFolder\SubFolder\SubFolder; namespace Tests\CustomTestCaseInSubFolders\SubFolder\SubFolder;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
-3
View File
@@ -1,3 +0,0 @@
<?php
uses(Tests\SubFolder\SubFolder\SubFolder\CustomTestCaseInSubFolder::class)->in('CustomTestCaseInSubFolders/SubFolder');
+4
View File
@@ -1,5 +1,9 @@
<?php <?php
use Tests\CustomTestCaseInSubFolders\SubFolder\SubFolder\CustomTestCaseInSubFolder;
uses(CustomTestCaseInSubFolder::class)->in('PHPUnit/CustomTestCaseInSubFolders/SubFolder/SubFolder');
uses()->group('integration')->in('Visual'); uses()->group('integration')->in('Visual');
// NOTE: global test value container to be mutated and checked across files, as needed // NOTE: global test value container to be mutated and checked across files, as needed
+1 -1
View File
@@ -23,7 +23,7 @@ test('default php unit tests', function () {
}); });
it('removes warnings', function () { it('removes warnings', function () {
$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);
+23
View File
@@ -0,0 +1,23 @@
<?php
use Pest\Plugins\Environment;
test('environment is set to CI when --ci option is used', function () {
$previousName = Environment::name();
$plugin = new Environment();
$plugin->handleArguments(['foo', '--ci', 'bar']);
expect(Environment::name())->toBe(Environment::CI);
Environment::name($previousName);
});
test('environment is set to Local when --ci option is not used', function () {
$plugin = new Environment();
$plugin->handleArguments(['foo', 'bar', 'baz']);
expect(Environment::name())->toBe(Environment::LOCAL);
});
+2 -1
View File
@@ -1,9 +1,10 @@
<?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;
use function Pest\version;
it('outputs the version when --version is used', function () { it('outputs the version when --version is used', function () {
$output = new BufferedOutput(); $output = new BufferedOutput();
$plugin = new Version($output); $plugin = new Version($output);
+1 -1
View File
@@ -10,7 +10,7 @@ it('gets file name from closure', function () {
}); });
it('gets property values', function () { it('gets property values', function () {
$class = new class() { $class = new class() {
private $foo = 'bar'; private $foo = 'bar';
}; };
+66 -5
View File
@@ -1,13 +1,74 @@
<?php <?php
use Pest\Exceptions\DatasetMissing;
use Pest\Exceptions\TestAlreadyExist; use Pest\Exceptions\TestAlreadyExist;
use Pest\Factories\TestCaseFactory;
use Pest\Plugins\Environment;
use Pest\TestSuite; use Pest\TestSuite;
it('does not allow to add the same test description twice', function () { it('does not allow to add the same test description twice', function () {
$testSuite = new TestSuite(getcwd(), 'tests'); $testSuite = new TestSuite(getcwd(), 'tests');
$test = function () {}; $test = function () {};
$testSuite->tests->set(new \Pest\Factories\TestCaseFactory(__FILE__, 'foo', $test)); $testSuite->tests->set(new TestCaseFactory(__FILE__, 'foo', $test));
$this->expectException(TestAlreadyExist::class); $testSuite->tests->set(new TestCaseFactory(__FILE__, 'foo', $test));
$this->expectExceptionMessage(sprintf('A test with the description `%s` already exist in the filename `%s`.', 'foo', __FILE__)); })->throws(
$testSuite->tests->set(new \Pest\Factories\TestCaseFactory(__FILE__, 'foo', $test)); TestAlreadyExist::class,
sprintf('A test with the description `%s` already exist in the filename `%s`.', 'foo', __FILE__),
);
it('alerts users about tests with arguments but no input', function () {
$testSuite = new TestSuite(getcwd(), 'tests');
$test = function (int $arg) {};
$testSuite->tests->set(new TestCaseFactory(__FILE__, 'foo', $test));
})->throws(
DatasetMissing::class,
sprintf("A test with the description '%s' has %d argument(s) ([%s]) and no dataset(s) provided in %s", 'foo', 1, 'int $arg', __FILE__),
);
it('can return an array of all test suite filenames', function () {
$testSuite = TestSuite::getInstance(getcwd(), 'tests');
$test = function () {};
$testSuite->tests->set(new TestCaseFactory(__FILE__, 'foo', $test));
$testSuite->tests->set(new TestCaseFactory(__FILE__, 'bar', $test));
expect($testSuite->tests->getFilenames())->toEqual([
__FILE__,
__FILE__,
]);
});
it('can filter the test suite filenames to those with the only method', function () {
$testSuite = new TestSuite(getcwd(), 'tests');
$test = function () {};
$testWithOnly = new TestCaseFactory(__FILE__, 'foo', $test);
$testWithOnly->only = true;
$testSuite->tests->set($testWithOnly);
$testSuite->tests->set(new TestCaseFactory('Baz/Bar/Boo.php', 'bar', $test));
expect($testSuite->tests->getFilenames())->toEqual([
__FILE__,
]);
});
it('does not filter the test suite filenames to those with the only method when working in CI pipeline', function () {
$previousEnvironment = Environment::name();
Environment::name(Environment::CI);
$testSuite = TestSuite::getInstance(getcwd(), 'tests');
$test = function () {};
$testWithOnly = new TestCaseFactory(__FILE__, 'foo', $test);
$testWithOnly->only = true;
$testSuite->tests->set($testWithOnly);
$testSuite->tests->set(new TestCaseFactory('Baz/Bar/Boo.php', 'bar', $test));
expect($testSuite->tests->getFilenames())->toEqual([
__FILE__,
'Baz/Bar/Boo.php',
]);
Environment::name($previousEnvironment);
}); });
+1 -1
View File
@@ -8,7 +8,7 @@ test('visual snapshot of help command output', function () {
if (getenv('REBUILD_SNAPSHOTS')) { if (getenv('REBUILD_SNAPSHOTS')) {
$outputBuffer = new BufferedOutput(); $outputBuffer = new BufferedOutput();
$plugin = new Help($outputBuffer); $plugin = new Help($outputBuffer);
$plugin(); $plugin();
+29
View File
@@ -0,0 +1,29 @@
<?php
use Pest\Logging\JUnit;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
beforeEach(function () {
file_put_contents(__DIR__ . '/junit.html', '');
});
it('is can successfully call all public methods', function () {
$junit = new JUnit(__DIR__ . '/junit.html');
$junit->startTestSuite(new TestSuite());
$junit->startTest($this);
$junit->addError($this, new Exception(), 0);
$junit->addFailure($this, new AssertionFailedError(), 0);
$junit->addWarning($this, new Warning(), 0);
$junit->addIncompleteTest($this, new Exception(), 0);
$junit->addRiskyTest($this, new Exception(), 0);
$junit->addSkippedTest($this, new Exception(), 0);
$junit->endTest($this, 0);
$junit->endTestSuite(new TestSuite());
$this->expectNotToPerformAssertions();
});
afterEach(function () {
unlink(__DIR__ . '/junit.html');
});
+2 -2
View File
@@ -2,14 +2,14 @@
test('visual snapshot of test suite on success', function () { test('visual snapshot of test suite on success', function () {
$testsPath = dirname(__DIR__); $testsPath = dirname(__DIR__);
$snapshot = implode(DIRECTORY_SEPARATOR, [ $snapshot = implode(DIRECTORY_SEPARATOR, [
$testsPath, $testsPath,
'.snapshots', '.snapshots',
'success.txt', 'success.txt',
]); ]);
$output = function () use ($testsPath) { $output = function () use ($testsPath) {
$process = (new Symfony\Component\Process\Process(['php', 'bin/pest'], dirname($testsPath), ['EXCLUDE' => 'integration', 'REBUILD_SNAPSHOTS' => false])); $process = (new Symfony\Component\Process\Process(['php', 'bin/pest'], dirname($testsPath), ['EXCLUDE' => 'integration', 'REBUILD_SNAPSHOTS' => false, 'PARATEST' => 0]));
$process->run(); $process->run();
+32
View File
@@ -0,0 +1,32 @@
<?php
use Pest\Logging\TeamCity;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\TextUI\DefaultResultPrinter;
beforeEach(function () {
file_put_contents(__DIR__ . '/output.txt', '');
});
it('is can successfully call all public methods', function () {
$teamCity = new TeamCity(__DIR__ . '/output.txt', false, DefaultResultPrinter::COLOR_ALWAYS);
expect($teamCity::isPestTest($this))->toBeTrue();
$teamCity->startTestSuite(new TestSuite());
$teamCity->startTest($this);
$teamCity->addError($this, new Exception('Don\'t worry about this error. Its purposeful.'), 0);
$teamCity->addFailure($this, new AssertionFailedError('Don\'t worry about this error. Its purposeful.'), 0);
$teamCity->addWarning($this, new Warning(), 0);
$teamCity->addIncompleteTest($this, new Exception(), 0);
$teamCity->addRiskyTest($this, new Exception(), 0);
$teamCity->addSkippedTest($this, new Exception(), 0);
$teamCity->endTest($this, 0);
$teamCity->printResult(new TestResult());
$teamCity->endTestSuite(new TestSuite());
});
afterEach(function () {
unlink(__DIR__ . '/output.txt');
});
View File