Compare commits

...

310 Commits

Author SHA1 Message Date
1bc0f79508 release: 2.33.3 2024-02-02 16:51:42 +00:00
cb0f256791 release: 2.33.2 2024-02-02 16:50:40 +00:00
923970a117 chore: bumps versioning 2024-02-01 11:51:11 +00:00
b3db7dfd4c chore: fixes type checking 2024-02-01 11:45:19 +00:00
b303f9f818 Merge pull request #1082 from nhaynes/fix-ci-flag
fix: updates Only plugin to check for CI environment
2024-01-30 14:17:03 +00:00
d29997d5b0 fix: updates Only plugin to check for CI environment 2024-01-29 19:03:06 -06:00
13f340a742 feat: improves badge coloring 2024-01-29 23:00:30 +00:00
eeade88ad2 Fixes kernel throwing all kind of errors 2024-01-29 12:50:00 +00:00
06280ef75d chore: updates snaphosts 2024-01-29 11:54:23 +00:00
aa46f73888 Merge pull request #1081 from nuernbergerA/track-vendor-changes
[2.x] Track vendor changes
2024-01-29 11:39:34 +00:00
3660865e5e update snapshot 2024-01-29 08:58:07 +01:00
13695d597b Merge branch '2.x' into track-vendor-changes 2024-01-29 08:56:41 +01:00
fab2de833f Merge pull request #1080 from nuernbergerA/test-junit
[2.x] Add test for junit implementation
2024-01-28 23:49:06 +00:00
5b630bcdff possible implementation 2024-01-28 09:48:37 +01:00
e70edbfa38 normalize path for windows 2024-01-28 09:11:06 +01:00
b1558ddde5 update snapshot 2024-01-28 09:04:42 +01:00
582529377b add test for junit output 2024-01-28 08:53:20 +01:00
88714598b6 Merge pull request #1076 from pestphp/fixing-version
[2.x] Fixing Version `2.33` for New Release
2024-01-27 13:01:06 +00:00
AJ
5136267bbe fixing version for new release 2024-01-26 23:43:52 -03:00
19e748f0d4 chore: adjusts snapshots 2024-01-26 01:58:03 +00:00
a53a9d03cf fix: exiting 2024-01-26 00:12:36 +00:00
edaa045283 feat: exists after kernel shutdown 2024-01-26 00:04:52 +00:00
c5ce355f3c feat: improves fatal exception handling 2024-01-25 21:47:31 +00:00
62d8459627 Merge pull request #1075 from luismgsantos/fix/docker-build-image
fix: build failing to run
2024-01-25 17:56:50 +00:00
a5bf6a3fcb fix: --cache-directory being used on phpunit file 2024-01-25 17:56:24 +00:00
7a46514df8 fix: removes process-isolation from --help output 2024-01-25 17:32:02 +00:00
cb1735f4d8 fix: removes process-isolation from --help output 2024-01-25 17:27:03 +00:00
607a4906ac Merge pull request #1006 from JonPurvis/to-be-backed-enum-expectation
[2.x] Add `toBeStringBackedEnum()` and `toBeIntBackedEnum()` Architecture Expectations
2024-01-25 17:09:23 +00:00
317ea0356e fix: build failing to run 2024-01-25 18:06:35 +01:00
1153531104 Merge pull request #1055 from mapon-com/feature/string-comparison-expectations
[2.x] Allow string type in greaterThan/lessThan expectations
2024-01-25 16:48:27 +00:00
cfb724cd77 Merge pull request #1060 from calebdw/bugfix-code_coverage
[2.x] fix: warn if no code coverage driver
2024-01-25 16:42:03 +00:00
0060b6f955 Merge pull request #1069 from davybaccaert/improve_coverage_message_on_failing_minimum_requirements
[2.x] Improve coverage output message on failing minimum requirements
2024-01-25 16:39:30 +00:00
95cd550524 fix: pipes not allowing to modify original value 2024-01-25 16:10:16 +00:00
887bed3d45 fix: adjusts backtrace for pest's internal test suite 2024-01-25 15:00:04 +00:00
79da02c500 Merge pull request #972 from Carnicero90/bugfix-backtrace-naming-conflicts
[2.x] Fixing Backtrace not found error if project dirname endswith pest
2024-01-25 14:54:58 +00:00
0aecd5d5d7 Merge pull request #974 from erikgaal/expect-to-contain-equals
[2.x] Add `toContainEquals` expectation
2024-01-25 14:38:55 +00:00
e95c4ee636 feat(toContainEqual): adds method name 2024-01-25 14:38:44 +00:00
2e7fec6be5 Merge pull request #961 from bastien-phi/allow_multiple_hook_per_directory
[2.x] Allow define multiple hooks per directory
2024-01-25 14:31:29 +00:00
4be7082de5 chore: updates snapshots 2024-01-25 14:31:17 +00:00
fb90f778b9 Update snapshots 2024-01-25 14:28:37 +00:00
9d58e1a77e Add ability to define multiple hooks for the same directory in Pest.php 2024-01-25 14:23:41 +00:00
9c077ed352 refacto: moves function to being used on internal test suite only 2024-01-25 14:13:18 +00:00
2562d36518 feat: clarfies that high order testing does not support bound datasets 2024-01-25 14:12:01 +00:00
1d2fe2de2d fix: doNotThrowsExceptions being marked as incomplete 2024-01-25 14:12:01 +00:00
2d82ee2837 chore: fixes types 2024-01-25 14:12:01 +00:00
1eee9df679 Merge pull request #981 from salehhashemi1992/refactor/remove-ansi-sequences
[2.x] Refactor: Extract ANSI Escape Sequence Removal to a Function
2024-01-25 14:11:51 +00:00
8c57cc1731 fix: --watch plugin access to original arguments 2024-01-25 12:33:20 +00:00
4febd8a11b Merge pull request #1073 from nuernbergerA/fix-junit-parallel
Fix junit parallel
2024-01-25 10:17:36 +00:00
880b003bee apply cs 2024-01-24 21:50:52 +01:00
e0f9d0bccf just override the phpunit file 2024-01-24 21:33:40 +01:00
d4853feecd drop own implementation 2024-01-24 21:33:17 +01:00
86e812284d remove plugin to ensure argument reaches paratest 2024-01-24 21:32:49 +01:00
f75063c420 release: 2.32.2 2024-01-23 18:12:07 +00:00
1f8e6e4e9f fix: helper access 2024-01-23 17:40:37 +00:00
bb593846e5 release: 2.32.1 2024-01-23 17:04:48 +00:00
108d181a05 Improve coverage output message on failing minimum requirements 2024-01-20 15:29:35 +01:00
ac5d6c1f67 chore: fixes constrains no workflow 2024-01-20 13:48:00 +00:00
5aa3b91d56 chore: fixes windows builds 2024-01-20 13:36:31 +00:00
9a01504b76 chore: fixes workflow 2024-01-20 13:32:21 +00:00
0ab636e436 chore: fixes workflow 2024-01-20 13:28:43 +00:00
b9d2be87a2 fix: missing things on junit 2024-01-20 13:21:57 +00:00
fef02594db release: 2.32.0 2024-01-20 11:44:11 +00:00
e135e2671f style 2024-01-20 11:44:11 +00:00
6d74965727 chore: bump dependencies 2024-01-20 11:44:11 +00:00
146e141b2a Merge pull request #887 from nuernbergerA/fix-junit-output
[2.x] Junit support
2024-01-20 11:43:20 +00:00
6fed7545c0 Merge pull request #990 from rudashi/patch-1
[2.x] Fix typo in `toHaveProperties` PHPDoc block
2024-01-13 01:44:16 +00:00
be407ac904 fix: warn if no code coverage driver 2024-01-11 10:20:35 -06:00
5332858782 chore: fixes snapshots 2024-01-11 15:46:50 +00:00
3457841a9b release: v2.31.0 2024-01-11 15:33:20 +00:00
5258e569c1 feat: adds skipOnPHP 2024-01-11 15:33:12 +00:00
abb416c2ff chore: bumps dependencies 2024-01-11 15:32:44 +00:00
b1c59ec2e6 feat: allow string type in gt/lt expectations 2024-01-05 16:21:02 +02:00
dc1e4f040d docs: adds sponsor 2024-01-04 18:26:20 +00:00
5e1e701ce5 Merge pull request #1051 from krencl/fix-cache-directory-config-override
Fix cache directory config override
2024-01-02 14:33:48 +00:00
f004591c5a fix: checking existing argument with equal sign 2024-01-02 15:03:46 +01:00
86a96dd157 fix: overriding cli argument --cache-directory 2024-01-02 15:01:13 +01:00
97dc32f9d2 release: v2.30.0 2023-12-28 10:36:40 +00:00
a3ab065343 chore: coding style 2023-12-28 10:36:30 +00:00
c390721ac3 chore: update snapshots 2023-12-28 10:34:22 +00:00
f83d758d4b feat: adds fails 2023-12-28 10:31:39 +00:00
e00aba539a release: v2.29.1 2023-12-27 15:27:07 +00:00
7799500d06 release: v2.29.0 2023-12-27 11:12:01 +00:00
c099991cd9 Merge pull request #1044 from nhrrs/fix-typo
Fix typo in `toBeClass`
2023-12-23 02:03:57 +00:00
e27d2e7394 Fix typo in toBeClass 2023-12-23 00:36:41 +00:00
14fb992ef2 unify converter 2023-12-19 06:29:28 +01:00
4550a344d3 overwrite phpunit junit logging with noop 2023-12-19 06:29:28 +01:00
8efd25ef65 remove debug output 2023-12-19 06:29:28 +01:00
117694f210 cleanup 2023-12-19 06:29:28 +01:00
e5dc6f0ae2 junit support 2023-12-19 06:29:28 +01:00
8f738f5d49 Revert "Merge pull request #919 from WendellAdriel/feature/coverage-errors-only-flag-2"
This reverts commit 1e2ca40c5b, reversing
changes made to 4522cb5dcb.
2023-12-17 22:03:15 +00:00
1e2ca40c5b Merge pull request #919 from WendellAdriel/feature/coverage-errors-only-flag-2
[2.x] Print only files below the min coverage
2023-12-17 21:56:14 +00:00
4522cb5dcb Merge pull request #1014 from mjsafarali/chore/docker-file-optimization
[2.x] Dockerfile Optimization
2023-12-17 21:39:38 +00:00
9ee4191020 release: v2.28.1 2023-12-15 11:42:34 +00:00
cc65009d0a chore: adds "phpunit/phpunit": "^10.5.3" support 2023-12-15 11:42:23 +00:00
453133d382 chore: code style changes 2023-12-15 11:42:09 +00:00
dd0dddffd4 docs: updates sponsors 2023-12-11 12:05:58 +00:00
9a8f6e6414 release: v2.28.0 2023-12-05 19:06:22 +00:00
4ece95a040 tests: uses arch function 2023-12-05 19:06:11 +00:00
0cc09380bc chore: bumps dependencies 2023-12-05 19:06:03 +00:00
809fb855de release: v2.27.0 2023-12-04 11:11:35 +00:00
aa14f2e200 chore: uses specific symfony versions 2023-12-04 11:08:41 +00:00
e319bdb6d3 chore: fixes missing caret on workflow 2023-12-04 11:04:08 +00:00
fb7340b556 chore: fixes exclude key and add fail-fast 2023-12-04 11:02:41 +00:00
0528fec083 chore: fixes duplicated key name on workflow 2023-12-04 10:59:58 +00:00
1cbaaf6e12 chore: allows symfony 7 on composer 2023-12-04 10:55:34 +00:00
dc862f60b2 chore: adjusts workflow 2023-12-04 10:54:11 +00:00
ff04d54247 chore: adjusts workflow name 2023-12-04 10:40:29 +00:00
330cf05177 chore: adjusts workflow 2023-12-04 10:38:37 +00:00
42b5fa914c Fixes integration tests 2023-12-04 10:15:55 +00:00
3b1026b7d7 chore: fixes workflow name 2023-12-04 10:14:51 +00:00
b6151e0d01 chore: tests against Symonfy 7 2023-12-04 10:10:36 +00:00
d6db2c13c1 Merge pull request #1025 from xiCO2k/fix/allow-todo-argument
[2.x] Allow `--todo` argument.
2023-11-30 10:47:10 +00:00
07b6ff6c04 Update bin/pest
Co-authored-by: Owen Voke <development@voke.dev>
2023-11-30 07:49:24 +00:00
ac5da9e3f7 feat: Allow --todo argument. 2023-11-30 00:32:23 +00:00
90fb8c602c release: v2.26.0 2023-11-29 09:09:09 +00:00
3974a65a18 Merge pull request #1017 from markhuot/patch-2
[2.x] Add `toSnapshot` early return
2023-11-29 08:50:28 +00:00
2a54b5819d #1017 adds early return toSnapshot test 2023-11-28 20:59:45 -05:00
8be46b57a0 Update toHaveProperties() $names param 2023-11-24 09:16:13 +01:00
7177791f1e Merge pull request #1020 from allanmcarvalho/2.x
Update Expectation.php
2023-11-23 17:42:51 +00:00
c743b10a87 Update Expectation.php
Removed @internal phpdoc
2023-11-23 13:15:50 -03:00
83f8de17c8 release: v2.25.0 2023-11-22 07:17:30 +00:00
da20a62e49 Add toSnapshot() early return
Sometimes objects need native toString() and toArray() methods that are different from what you want to snapshot.

This adds an explicit toSnapshot() method that will be called first (when set) allowing for better snapshot values than the generic methods offer.
2023-11-21 22:56:21 -05:00
c8d3e1a9fa Merge pull request #1012 from nahime0/2.x
[2.x] Added onlyOn* methods to run the test only on a specific OS
2023-11-21 01:01:24 +11:00
f7705fe1c1 feat: onlyOn* methods, removed private onlyOn, rely instead on skipOn* methods 2023-11-20 14:51:38 +01:00
4f35dbc607 chore: optimized version of the Dockerfile 2023-11-18 14:57:03 +03:30
2e01776272 add to be backed enum expectation 2023-11-18 03:31:35 +00:00
cf23dfa477 feat: onlyOn* methods now use the private onlyOn method 2023-11-17 16:16:48 +01:00
ab4787c667 feat: added onlyOn* methods to run the test only on a specific OS 2023-11-17 15:03:28 +01:00
bd6b166a62 Merge pull request #1002 from faissaloux/remove-double-plus
Remove double plus
2023-11-08 10:01:57 +00:00
17340947b3 remove double plus 2023-11-08 10:52:33 +01:00
f235d84d95 release: v2.24.3 2023-11-08 09:47:14 +00:00
3c0d780696 Merge pull request #1001 from faissaloux/fix-html-in-descriptions-or-datasets
Fix html in descriptions or datasets
2023-11-08 09:40:48 +00:00
16768fca9f update snapshots/paralell test 2023-11-07 17:46:00 +01:00
95ec0a82b2 fix html in tests descriptions and datasets 2023-11-07 17:35:42 +01:00
15cd7187e9 Update toContainEquals.php 2023-11-06 10:31:48 +01:00
0a680dd06e release: v2.24.2 2023-11-01 19:10:11 -04:00
152892cc38 chore: bumps paratest 2023-11-01 19:06:05 -04:00
9aad417fb2 Merge pull request #996 from CalebDW/phpstan
Create PHPStan extension and add `HigherOrderTapProxy` to `universalObjectCratesClasses`
2023-10-30 20:49:55 -04:00
b58e0cba66 Add Expectation to universalObjectCratesClasses 2023-10-30 14:48:23 -05:00
74864c60e1 Create phpstan extension 2023-10-30 11:55:26 -05:00
fd4f161edd release: v2.24.1 2023-10-26 11:02:35 -04:00
e0939e3e99 chore: adds phpunit 10.4.2 support 2023-10-26 11:02:26 -04:00
2cbecd10e6 Fix typo in toHaveProperties() PHPDoc block 2023-10-23 11:23:53 +02:00
2cdd5e3ba0 fix: infer generic type from expectation 2023-10-21 11:06:26 +01:00
811ef27ee4 release: v2.24.0 2023-10-17 10:07:18 +01:00
22a7fd0656 chore: adjusts snapshots 2023-10-17 10:07:08 +01:00
698c276cbe chore: fixes style 2023-10-17 10:06:58 +01:00
6340656ece chore: bumps dependencies 2023-10-17 10:06:48 +01:00
2d5840f947 Merge pull request #933 from hungthai1401/throws_unless
[2.x] Add `throwsUnless`
2023-10-17 10:03:16 +01:00
b8bb3684a3 Merge pull request #983 from Muhammad-Sarfaraz/patch-1
Polishing Up "TestDox.php' PHPDoc Blocks for Clarity
2023-10-17 10:02:08 +01:00
b8cd563569 Update src/Factories/Annotations/TestDox.php
Co-authored-by: Owen Voke <development@voke.dev>
2023-10-16 16:23:09 +06:00
9fb64599de Polishing Up "TestDox.php' PHPDoc Blocks for Clarity
Added the missing parenthesis and period for proper punctuation and formatted the doc block to meet PHPDocumentor standards.
2023-10-16 10:56:06 +06:00
502f37d280 chore: updates links 2023-10-15 12:10:07 +01:00
29cfa8ec35 chore: updates sponsors 2023-10-14 11:34:00 +01:00
86c107ae5e Extract ANSI escape sequence to a function 2023-10-13 20:16:46 +03:30
a63cd2e4f5 Merge pull request #980 from salehhashemi1992/fix/lifecycle-hook-scope
Fix TestCycle hook scope
2023-10-13 15:27:37 +01:00
7249b59e52 fix lifecycle hook scope 2023-10-13 17:51:02 +03:30
5c94d9994e Merge pull request #979 from salehhashemi1992/ci/checkout-update
Update actions/checkout to v4
2023-10-13 00:36:11 +01:00
bb0a5d8323 update checkout to v4 2023-10-12 21:32:46 +03:30
b126e8e6e4 release: v2.23.2 2023-10-10 16:40:34 +01:00
677129d23d chore: uses paratest 7.3.0 2023-10-10 16:39:04 +01:00
cef5c36885 release: v2.23.1 2023-10-10 15:57:14 +01:00
a343ba4a29 chore: adds PHPUnit 10.4.1 support 2023-10-10 15:57:07 +01:00
21b30b22a7 release: v2.23.0 2023-10-10 15:41:56 +01:00
449c4b6c5e chore: adds collision v8 support 2023-10-10 15:37:25 +01:00
6513ad6ced release: v2.22.1 2023-10-10 14:59:16 +01:00
12421c846e chore: adds termwind v2 support 2023-10-10 14:55:43 +01:00
a312cecede release: v2.22.0 2023-10-10 08:45:41 +01:00
4be97ed314 Merge pull request #977 from JonPurvis/to-be-url-expectation
[2.x] Adds `toBeUrl()` Expectation
2023-10-09 20:06:52 +01:00
5101b9dce3 add to be url expectation 2023-10-09 20:02:11 +01:00
2ffafd445d release: v2.21.0 2023-10-06 13:33:39 +01:00
6068ef6150 feat: adds support for PHPUnit 10.4 2023-10-06 13:33:31 +01:00
8c0b933fcd chore: bumps dependencies 2023-10-05 18:32:07 +01:00
991e02649a chore: bumps paratest 2023-10-05 09:42:56 +01:00
79f5973e5a Add tests 2023-10-03 11:09:26 +02:00
37c40cb735 Add toContainEquals expectation 2023-10-03 10:55:57 +02:00
28ee2917f1 Fixing Backtrace not found error if project dirname endswith pest 2023-09-30 00:05:42 +02:00
a8b785f69e release: v2.20.0 2023-09-29 19:05:52 +01:00
56610d886d Merge pull request #968 from JonPurvis/add-to-be-between-expectation
[2.x] Add `toBeBetween` expectation
2023-09-29 19:01:05 +01:00
be0d9e964b add toBeBetween() expectation 2023-09-22 01:55:06 +01:00
6bc9da3fe1 chore: bumps collision 2023-09-19 11:48:16 +01:00
6f54462070 fix: sync wrapper runner with paratest 2023-09-19 11:27:09 +01:00
876629b744 release: v2.19.1 2023-09-19 11:01:29 +01:00
5e74e5a19d release: v2.19.0 2023-09-19 10:48:34 +01:00
0d114e21fd chore: updates snapshots 2023-09-19 10:48:23 +01:00
95b65fe72b Merge pull request #962 from JonPurvis/construct-destruct-expectations
add toHaveConstructor() and toHaveDestructor() expectations
2023-09-18 11:37:46 +01:00
bc08f2cb55 fix style issues 2023-09-18 01:13:51 +01:00
6c73a3d90b initial commit 2023-09-18 01:00:50 +01:00
c08f33638a chore: updates release 2023-09-13 23:16:44 +01:00
6c93390c9c chore: removes changelog.md 2023-09-13 23:15:56 +01:00
b53e396aac release: v2.18.2 2023-09-13 23:14:31 +01:00
8b327aa8b4 chore: adds phpunit 10.3.4 support 2023-09-13 23:14:22 +01:00
d0c6f9bc60 Merge pull request #957 from adevade/patch-1
Switch mixed indentation to spaces only in Laravel stub for `phpunit.xml.stub`
2023-09-12 16:15:39 +01:00
b5e066939b Whoops 2023-09-12 09:04:30 +02:00
7892237408 Update Laravel phpunit.xml.stub indentation 2023-09-12 09:03:41 +02:00
74df53c72b release: v2.18.1 2023-09-11 11:38:47 +01:00
ee26457705 Merge pull request #956 from Itemshopp/phpunit-xml-stub-update
[2.x] Update phpunit.xml stub file
2023-09-11 11:32:34 +01:00
MHO
09e6a0944a Removed self closing coverage tag from phpunit xml stub files 2023-09-11 11:03:25 +02:00
MHO
bdee46043a Reformatted php unit xml tag attributes in both init stubs files 2023-09-10 13:37:26 +02:00
MHO
3e25168777 Corrected incorrect indentation in laravel phpunit xml slug 2023-09-10 13:00:55 +02:00
MHO
21b8507252 Updated Laravel init phpunit.xml stub file 2023-09-08 16:40:25 +02:00
MHO
d8e283777e Updated phpunit.xml stub file 2023-09-08 15:39:44 +02:00
2b0aa4b9c9 release: v2.18.0 2023-09-07 19:00:46 +01:00
040eb8142d chore: phpunit 10.3.3 support 2023-09-07 19:00:26 +01:00
d1aeabc9da chore: style changes 2023-09-06 12:19:27 +01:00
e4ec2b3efa chore: updates snapshots 2023-09-06 11:58:48 +01:00
dedcc6b887 Merge pull request #950 from hungthai1401/wrong_comment
[2.x] Correct some  comment messages in `OppositeExpectation`
2023-09-06 11:55:17 +01:00
2b0ed2bc45 Merge pull request #948 from hungthai1401/to_be_uuid
[2.x] Add `toBeUuid` expectation
2023-09-06 11:54:05 +01:00
9c859ae7c4 Merge branch '2.x' into to_be_uuid 2023-09-06 11:53:58 +01:00
ae0a230046 chore: improves readability 2023-09-06 11:48:53 +01:00
644fade478 Merge pull request #949 from pestphp/fix-depends-with-describe
[2.x] Fix the Usage of `depends` With `describe`
2023-09-06 11:47:34 +01:00
c9e919dd40 fix: correct comment message in OppositeExpectation 2023-09-06 08:34:48 +07:00
42323e27b1 fix: correct method name 2023-09-06 08:21:42 +07:00
3927177b23 finishing the code 2023-09-05 20:36:18 -03:00
038fd80428 feat: toBeUUID expectation 2023-09-05 08:25:02 +07:00
cc6c5bf199 docs: updates changelog 2023-09-04 00:20:57 +01:00
b88d9e8ff2 tests: update snapshots 2023-09-03 23:24:52 +01:00
0fc232bbc7 Merge pull request #934 from hungthai1401/to_have_attribute_expectation
[2.x] Add `toHaveAttribute` expectation
2023-09-03 23:18:47 +01:00
7dcd42d113 chore: prepares release 2023-09-03 21:39:21 +01:00
e79ffc6bad tests: adjusts snapshots 2023-09-03 21:36:48 +01:00
8ea425b266 Merge pull request #947 from ludoguenet/2.x
[2.x] Add `toHaveMethod` arch expectation
2023-09-03 21:32:01 +01:00
3a0f6a1d09 chore: prepares release 2023-09-03 13:37:26 +01:00
b9b90295fa Update Expectation.php
Typo
2023-09-01 18:39:37 +02:00
9dabecacbf Add toHaveMethod arch expectation 2023-09-01 18:32:40 +02:00
04fa6b6372 Merge pull request #943 from fabio-ivona/datasets-in-pest-file
fix directory separator for windows
2023-08-29 10:36:58 +01:00
a0d2856f51 docs: update changelog 2023-08-29 10:36:06 +01:00
55b9266648 release: v2.16.1 2023-08-29 10:30:36 +01:00
4313a1ef20 chore: bump dependencies 2023-08-29 10:30:28 +01:00
005ef03845 chore: bumps dependencies 2023-08-29 10:17:07 +01:00
bbac28c9f4 fix directory separator for windows 2023-08-29 11:14:23 +02:00
eb56483ba2 Merge pull request #942 from fabio-ivona/datasets-in-pest-file
[fix] missing datasets when defined in Pest.php file
2023-08-29 09:54:09 +01:00
5d6b717c9a fix missing datasets when defined in Pest.php file 2023-08-29 10:49:17 +02:00
e888f3613b refactor: change falsy to false 2023-08-24 16:40:30 +07:00
e69899559d refactor: generic attribute 2023-08-24 15:23:13 +07:00
e6fe968d44 fix: pint 2023-08-24 14:45:11 +07:00
678898efe7 feat: toHaveAttribute expectation 2023-08-24 14:26:54 +07:00
6c3d8829ce feat: throwsUnless method 2023-08-24 09:28:47 +07:00
14859a4c89 Merge pull request #930 from pestphp/feature/same-size-arg
[2.x] chore: resolve `toHaveSameSize` parameter
2023-08-23 12:01:34 +01:00
8a44d3f136 chore: resolve toHaveSameSize parameter 2023-08-23 11:28:41 +01:00
be71d6918d chore: bump dependencies 2023-08-23 10:35:06 +01:00
afb3dd459a Merge pull request #924 from hungthai1401/to_have_same_size_expectation
[2.x] Add `toHaveSameSize` expectation
2023-08-23 10:14:17 +01:00
b6e3ffafa7 fix: phpstan 2023-08-23 08:14:27 +07:00
6c95f3d8cf Merge pull request #923 from hungthai1401/inconsistent_type_have_count_exception
[2.x] Inconsistent type have count exception
2023-08-22 10:37:59 +01:00
2192373bec test: toHaveSameSize 2023-08-22 11:10:38 +07:00
dfcdaa3f8e feat: toHaveSameSize expectation 2023-08-22 11:10:25 +07:00
79bc9e677f test: toHaveCount with invalid type 2023-08-22 10:36:10 +07:00
60b615ea6a fix: inconsistent type in InvalidExpectationValue exception at toHaveCount expectation 2023-08-22 10:35:07 +07:00
8787481e40 docs: updates changelog 2023-08-21 09:53:42 +01:00
c24406259f docs: updates changelog 2023-08-21 09:51:12 +01:00
cbd6a65057 release: v2.16.0 2023-08-21 09:42:07 +01:00
175004baf3 chore: adds testing on native functions 2023-08-21 09:40:04 +01:00
6d9c0483a6 chore: improves type checking 2023-08-21 09:39:55 +01:00
2dc413cba0 tests: update snapshots 2023-08-19 10:42:42 +01:00
206548af2b Merge pull request #895 from cerbero90/feature/traversable-sequence
[2.x] Add support for nested traversable in sequence
2023-08-19 09:38:46 +00:00
af6de422e9 Merge pull request #921 from leMaur/feat/string-case-expectations
feat: add string case expectations
2023-08-19 09:38:31 +00:00
1c7b254395 Merge branch '2.x' into feat/string-case-expectations 2023-08-19 10:28:48 +02:00
de1c721cd9 chore: improve error messages 2023-08-19 10:27:21 +02:00
f8dd286213 chore: skip array list 2023-08-19 10:27:02 +02:00
e11337df2d Merge branch '2.x' into feature/traversable-sequence 2023-08-19 00:34:30 +02:00
2f90d4ccd7 tests: update snapshots 2023-08-18 12:16:15 +01:00
2db15af24a Merge branch '2.x' into feature/traversable-sequence 2023-08-18 12:33:09 +02:00
8ea7b2b802 Add errors-only flag 2023-08-18 10:13:28 +01:00
c9e3932637 Merge pull request #911 from devajmeireles/feature/add-to-be-digits-expectation
[2.x] Introducing `toBeDigits` Expectation
2023-08-18 03:13:59 +00:00
d218afaf77 introducing new proposal of the PR template 2023-08-17 18:50:23 -03:00
19739ff814 Merge pull request #915 from pestphp/adapting-phpunit-xml-stubs
[2.x] Adapting `phpunit.xml` stubs with PhpUnit
2023-08-17 18:56:50 +00:00
478144fb35 feat: add toHaveStudlyCaseKeys 2023-08-17 20:51:26 +02:00
5d81cf0d4c feat: add toHaveCamelCaseKeys 2023-08-17 20:51:14 +02:00
0b115230f9 feat: add toHaveKebabCaseKeys 2023-08-17 20:50:51 +02:00
0b246f7a76 feat: add toHaveSnakeCaseKeys 2023-08-17 20:50:26 +02:00
7914224ff7 introducing https://schema.phpunit.de/10.3/phpunit.xsd 2023-08-17 15:50:15 -03:00
997b0e9368 feat: add toBeStudlyCase 2023-08-17 20:49:40 +02:00
a76414aeee feat: add toBeCamelCase 2023-08-17 20:49:21 +02:00
d2096df82a feat: add toBeKebabCase 2023-08-17 20:48:51 +02:00
4951b1b0f9 feat: add toBeSnakeCase 2023-08-17 20:48:18 +02:00
f2e31452f2 Merge pull request #912 from devajmeireles/issue-template
Introducing Issue Template
2023-08-17 11:04:27 -03:00
c2985ffb31 release: v2.15.0 2023-08-17 11:28:55 +01:00
492f797dd5 chore: style changes 2023-08-17 11:24:16 +01:00
0b261ef97b feat: adds php@8.3 support 2023-08-17 11:19:43 +01:00
f19692a72f chore: changes phpstan settings 2023-08-17 11:19:11 +01:00
0787b37f2c chore: style changes 2023-08-17 11:18:59 +01:00
f0223b50d0 introducing sample repository input 2023-08-16 15:50:40 -03:00
0263fcb2ac wip 2023-08-16 14:18:09 -03:00
c0a234317b introducing issue template 2023-08-16 14:16:18 -03:00
72100075d2 docs: updates changelog 2023-08-16 09:49:07 +01:00
a7aa923241 release: v2.14.1 2023-08-16 09:47:05 +01:00
e012517b16 chore: bumps phpunit 2023-08-16 09:46:51 +01:00
b1dd18af8a chore: style changes 2023-08-16 09:46:31 +01:00
398e3ff3b5 introducing toBeDigits 2023-08-14 17:10:58 -03:00
03648f580c docs: update changelog 2023-08-14 09:44:14 +01:00
df2212055b release: v2.14.0 2023-08-14 09:41:14 +01:00
b1a137c513 chore: updates snapshot tests 2023-08-14 09:41:05 +01:00
62267dfd3e Merge pull request #906 from JonPurvis/extra-expectations
add expectations for uppercase, lowercase, alpha and alphanumeric
2023-08-13 08:44:17 +00:00
f996a48dfa fix refacto check 2023-08-12 18:14:38 +01:00
54e00dd4dc add expectations for uppercase, lowercase, alpha and alphanumeric 2023-08-12 16:41:15 +01:00
f1414a0beb docs: changelog 2023-08-09 12:16:21 +01:00
47f2ae32c1 release: v2.13.0 2023-08-09 12:14:39 +01:00
306b7eb2a6 feat: adds ddWhen and ddUnless 2023-08-09 12:14:32 +01:00
02f72aabb2 Merge pull request #860 from devajmeireles/feature/add-dd-conditionally
Feature: Introducing The Ability to Dump Conditionally
2023-08-09 10:50:53 +00:00
e3a21384e6 release: v2.12.2 2023-08-07 10:29:25 +01:00
331381eed5 release: v2.12.1 2023-08-07 10:26:55 +01:00
75a7d77a80 Updates snapshots 2023-08-07 10:22:58 +01:00
cc242a50d1 chore: bump dependencies 2023-08-07 09:39:13 +01:00
704acbf6de Merge pull request #898 from dylanbr/allow_tests_to_be_extended
TestSuiteLoader will always consider classes from the current file
2023-08-06 22:59:52 +00:00
7baa48e068 TestSuiteLoader will always consider classes from the current file 2023-08-05 13:06:00 +02:00
d94a6580f5 fix: type check 2023-08-02 20:49:27 +02:00
fb75b712d3 chore: update snapshot 2023-08-02 20:49:05 +02:00
6ead2a4e8b feat(sequence): Add support for nested traversable 2023-08-02 20:31:53 +02:00
b00bc4d5ea applying enhancement to use single dd function 2023-07-17 19:11:06 -03:00
8abc0d1920 applying enhancement to use ddWhen inside ddUnless 2023-07-17 14:12:54 -03:00
ea967b439f Feature: Introducing The Ability to Dump Conditionally 2023-07-17 11:08:00 -03:00
166 changed files with 3170 additions and 702 deletions

66
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,66 @@
name: Bug Report
description: Report an Issue or Bug with the Pest
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
We're sorry to hear you have a problem. Can you help us solve it by providing the following details.
- type: textarea
id: what-happened
attributes:
label: What Happened
description: What did you expect to happen?
placeholder: When I use expect()->toBeTrue() in my tests, I get an error
validations:
required: true
- type: textarea
id: how-to-reproduce
attributes:
label: How to Reproduce
description: How did this occur, please add any config values used and provide a set of reliable steps if possible.
placeholder: Install a fresh Laravel app, add Pest, add a test that uses expect()->toBeTrue()
validations:
required: true
- type: input
id: repository-sample
attributes:
label: Sample Repository
description: If possible, please provide a sample repository that reproduces the issue.
placeholder: https://github.com.br/your-username/your-repository
- type: input
id: pest-version
attributes:
label: Pest Version
description: What version of our Package are you running? Please be as specific as possible
placeholder: 2.14.1
validations:
required: true
- type: input
id: php-version
attributes:
label: PHP Version
description: What version of PHP are you running? Please be as specific as possible
placeholder: 8.1.20
validations:
required: true
- type: dropdown
id: operating-systems
attributes:
label: Operation System
description: On which operating systems does the problem occur? You can select more than one.
multiple: true
options:
- macOS
- Windows
- Linux
validations:
required: true
- type: textarea
id: notes
attributes:
label: Notes
description: Use this field to provide any other notes that you feel might be relevant to the issue.
validations:
required: false

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. -->

View File

@ -1,42 +0,0 @@
name: Integration Tests
on:
push:
schedule:
- cron: '0 0 * * *'
jobs:
ci:
if: github.event_name != 'schedule' || github.repository == 'pestphp/pest'
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
php: ['8.1', '8.2']
dependency-version: [prefer-lowest, prefer-stable]
name: PHP ${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer:v2
coverage: none
- name: Setup Problem Matches
run: |
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: Install PHP dependencies
run: composer update --${{ matrix.dependency-version }} --no-interaction --no-progress --ansi
- name: Integration Tests
run: composer test:integration

View File

@ -13,12 +13,13 @@ jobs:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
dependency-version: [prefer-lowest, prefer-stable]
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2

View File

@ -6,22 +6,27 @@ on:
schedule:
- cron: '0 0 * * *'
jobs:
ci:
tests:
if: github.event_name != 'schedule' || github.repository == 'pestphp/pest'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
php: ['8.1', '8.2']
dependency-version: [prefer-lowest, prefer-stable]
symfony: ['6.4', '7.0']
php: ['8.1', '8.2', '8.3']
dependency_version: [prefer-lowest, prefer-stable]
exclude:
- php: '8.1'
symfony: '7.0'
name: PHP ${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }}
name: PHP ${{ matrix.php }} - Symfony ^${{ matrix.symfony }} - ${{ matrix.os }} - ${{ matrix.dependency_version }}
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
@ -36,11 +41,13 @@ jobs:
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: Install PHP dependencies
run: composer update --${{ matrix.dependency-version }} --no-interaction --no-progress --ansi
run: composer update --${{ matrix.dependency_version }} --no-interaction --no-progress --ansi --with="symfony/console:~${{ matrix.symfony }}"
- name: Unit Tests
run: composer test:unit
- name: Unit Tests in Parallel
- name: Parallel Tests
run: composer test:parallel
- name: Integration Tests
run: composer test:integration

View File

@ -1,210 +0,0 @@
# Release Notes for 2.x
## Unreleased
## [v2.12.0 (2023-08-02)](https://github.com/pestphp/pest/compare/v2.11.0...v2.12.0)
### Added
- Allows multiple `toMatchSnapshot` per test ([#881](https://github.com/pestphp/pest/pull/881))
### Changed
- Bumps PHPUnit to `^10.2.7` ([43107c1](https://github.com/pestphp/pest/commit/43107c17436e41e23018ae31705c688168c14784))
## [v2.11.0 (2023-08-01)](https://github.com/pestphp/pest/compare/v2.10.1...v2.11.0)
### Added
- `toBeInvokable`expectation ([#891](https://github.com/pestphp/pest/pull/891))
## [v2.10.1 (2023-07-31)](https://github.com/pestphp/pest/compare/v2.10.0...v2.10.1)
### Fixed
- `not->toHaveSuffix` and `toHavePrefix` expectations ([#888](https://github.com/pestphp/pest/pull/888))
## [v2.10.0 (2023-07-31)](https://github.com/pestphp/pest/compare/v2.9.5...v2.10.0)
### Added
- `repeat` feature ([f3f35a2](https://github.com/pestphp/pest/commit/f3f35a2ed119f63eefd323a8c66d3387e908df3f))
### Fixed
- `-v` option ([86a6b32](https://github.com/pestphp/pest/commit/86a6b3271518742dc39761228687a5107551d279))
## [v2.9.5 (2023-07-22)](https://github.com/pestphp/pest/compare/v2.9.4...v2.9.5)
### Fixed
- Assertions count on arch expectations ([632ffc2](https://github.com/pestphp/pest/commit/632ffc2f8e1fe45f739b12b818426ae14700079e))
## [v2.9.4 (2023-07-22)](https://github.com/pestphp/pest/compare/v2.9.3...v2.9.4)
### Fixed
- Output on `describe` `beforeEach` failure ([5637dfa](https://github.com/pestphp/pest/commit/5637dfa75d1a331adc810935536cde7c3196af06))
## [v2.9.3 (2023-07-20)](https://github.com/pestphp/pest/compare/v2.9.2...v2.9.3)
### Fixed
- Snapshots directory on Windows environments ([cf52752](https://github.com/pestphp/pest/commit/cf5275293fe693ec2cf4dbadbadae01daaa08169))
## [v2.9.2 (2023-07-20)](https://github.com/pestphp/pest/compare/v2.9.1...v2.9.2)
### Fixed
- `beforeEach` on Windows environments ([a37a3b9](https://github.com/pestphp/pest/commit/a37a3b9f9931bc1ab1ea9e1d6d38dfb55dde3f74))
## [v2.9.1 (2023-07-20)](https://github.com/pestphp/pest/compare/v2.9.0...v2.9.1)
### Chore
- Bumps PHPUnit to `^10.2.6` ([8fdb0b3](https://github.com/pestphp/pest/commit/8fdb0b3d32ce9ee12bd182f22751c2b41a53e97b))
## [v2.9.0 (2023-07-19)](https://github.com/pestphp/pest/compare/v2.8.1...v2.9.0)
> "Spicy Summer" is the codename assigned to Pest 2.9, for full details check our announcement: [https://pestphp.com/docs/pest-spicy-summer-release](https://pestphp.com/docs/pest-spicy-summer-release)
### Added
- Built-in Snapshot Testing ([c828756](https://github.com/pestphp/pest/commit/c8287567eb8c3dbea5845b2a6f70804b094b4b60))
- Describe Blocks ([c828756](https://github.com/pestphp/pest/commit/c8287567eb8c3dbea5845b2a6f70804b094b4b60))
- Architectural Testing++ ([c828756](https://github.com/pestphp/pest/commit/c8287567eb8c3dbea5845b2a6f70804b094b4b60))
- Type Coverage Plugin ([c828756](https://github.com/pestphp/pest/commit/c8287567eb8c3dbea5845b2a6f70804b094b4b60))
- Drift Plugin ([c828756](https://github.com/pestphp/pest/commit/c8287567eb8c3dbea5845b2a6f70804b094b4b60))
## [v2.8.1 (2023-06-20)](https://github.com/pestphp/pest/compare/v2.8.0...v2.8.1)
### Fixed
- Fixes "Cannot find TestCase object on call stack" ([eb7bb34](https://github.com/pestphp/pest/commit/eb7bb348253f412e806a6ba6f0df46c0435d0dfe))
## [v2.8.0 (2023-06-19)](https://github.com/pestphp/pest/compare/v2.7.0...v2.8.0)
### Added
- Support for `globs` in `uses` ([#829](https://github.com/pestphp/pest/pull/829))
## [v2.7.0 (2023-06-15)](https://github.com/pestphp/pest/compare/v2.6.3...v2.7.0)
### Added
- Support for unexpected output on printer ([eb9f31e](https://github.com/pestphp/pest/commit/eb9f31edeb00a88c449874f3d48156128a00fff8))
### Chore
- Bumps PHPUnit to `^10.2.2` ([0e5470b](https://github.com/pestphp/pest/commit/0e5470b192b259ba2db7c02a50371216c98fc0a6))
## [v2.6.3 (2023-06-07)](https://github.com/pestphp/pest/compare/v2.6.2...v2.6.3)
### Chore
- Bumps PHPUnit to `^10.2.1` ([73a859e](https://github.com/pestphp/pest/commit/73a859ee563fe96944ba39b191dceca28ef703c2))
## [v2.6.2 (2023-06-02)](https://github.com/pestphp/pest/compare/v2.6.1...v2.6.2)
### Chore
- Bumps PHPUnit to `^10.2.0` ([a0041f1](https://github.com/pestphp/pest/commit/a0041f139cba94fe5d15318c38e275f2e2fb3350))
## [v2.6.1 (2023-04-12)](https://github.com/pestphp/pest/compare/v2.6.0...v2.6.1)
### Fixes
- PHPStorm issue output problem for tests throwing an exception before the first assertion ([#809](https://github.com/pestphp/pest/pull/809))
- Allow traits to be covered ([#804](https://github.com/pestphp/pest/pull/804))
### Chore
- Bumps PHPUnit to `^10.1.3` ([c993252](https://github.com/pestphp/pest/commit/c99325275acf1fd3759b487b93ec50473f706709))
## [v2.6.0 (2023-04-05)](https://github.com/pestphp/pest/compare/v2.5.2...v2.6.0)
### Adds
- Allows `toThrow` to be used against an exception instance ([#797](https://github.com/pestphp/pest/pull/797))
## [v2.5.2 (2023-04-19)](https://github.com/pestphp/pest/compare/v2.5.1...v2.5.2)
### Chore
- Removes `myclabs/php-enuma` dependency ([1a05df1](https://github.com/pestphp/pest/commit/1a05df14d0ce7d12583df26ff716807db6f81f13))
## [v2.5.1 (2023-04-18)](https://github.com/pestphp/pest/compare/v2.5.0...v2.5.1)
### Chore
- Bumps PHPUnit to `^10.1.1` ([ec6a817](https://github.com/pestphp/pest/commit/ec6a81735af19f5463d24545df97535d77697ec6))
## [v2.5.0 (2023-04-14)](https://github.com/pestphp/pest/compare/v2.4.0...v2.5.0)
### Chore
- Bumps PHPUnit to `^10.1.0` ([#780](https://github.com/pestphp/pest/pull/780))
## [v2.4.0 (2023-04-03)](https://github.com/pestphp/pest/compare/v2.3.0...v2.4.0)
### Added
- `skipOnWindows()`, `skipOnMac()`, and `skipOnLinux()` ([#757](https://github.com/pestphp/pest/pull/757))
- source architecture testing violation ([#1](https://github.com/pestphp/pest-plugin-arch/pull/1))([8e66263](https://github.com/pestphp/pest-plugin-arch/commit/8e66263104304e99e3d6ceda25c7ed679b27fb03))
- `toHaveProperties` may now also check values ([#760](https://github.com/pestphp/pest/pull/760))
### Fixed
- Tests on `tests/Helpers` directory not being executed ([#753](https://github.com/pestphp/pest/pull/753))
- Teamcity count ([#747](https://github.com/pestphp/pest/pull/747))
- Parallel execution when class extends class with same name ([#748](https://github.com/pestphp/pest/pull/748))
- Wording on `uses()` hint ([#745](https://github.com/pestphp/pest/pull/745/files))
## [v2.3.0 (2023-03-28)](https://github.com/pestphp/pest/compare/v2.2.3...v2.3.0)
### Added
- Better error handler about missing uses ([#743](https://github.com/pestphp/pest/pull/743))
### Fixed
- Inconsistent spelling of `dataset` ([#739](https://github.com/pestphp/pest/pull/739))
### Chore
- Bumps PHPUnit to `^10.0.19` ([3d7e621](https://github.com/pestphp/pest/commit/3d7e621b7dfc03f0b2d9dcf6eb06c26bc383f502))
## [v2.2.3 (2023-03-24)](https://github.com/pestphp/pest/compare/v2.2.2...v2.2.3)
### Fixed
- Unnecessary dataset on dataset arguments mismatch ([#736](https://github.com/pestphp/pest/pull/736))
- Parallel arguments on plugins order ([#703](https://github.com/pestphp/pest/pull/703))
- Arch plugin runtime exceptions on bad phpdocs ([2f2b51c](https://github.com/pestphp/pest/commit/2f2b51ce3d1b000be9d6add0e785fd0044931b3b))
## [v2.2.2 (2023-03-23)](https://github.com/pestphp/pest/compare/v2.2.1...v2.2.2)
### Fixed
- Edge case in parallel execution test description ([3ce6408](https://github.com/pestphp/pest/commit/3ce640819541ca6022b250e000f336d87c3e7889))
## [v2.2.1 (2023-03-22)](https://github.com/pestphp/pest/compare/v2.2.0...v2.2.1)
### Fixed
- Collision between tests names with underscores ([#724](https://github.com/pestphp/pest/pull/724))
### Chore
- Bumps PHPUnit to `^10.0.18` ([1408cff](https://github.com/pestphp/pest/commit/1408cffc028690057e44f00038f9390f776e6bfb))
## [v2.2.0 (2023-03-22)](https://github.com/pestphp/pest/compare/v2.1.0...v2.2.0)
### Added
- Improved error messages on dataset arguments mismatch ([#698](https://github.com/pestphp/pest/pull/698))
- Allows the usage of `DateTimeInterface` on multiple expectations ([#716](https://github.com/pestphp/pest/pull/716))
### Fixed
- `--dirty` option on Windows environments ([#721](https://github.com/pestphp/pest/pull/721))
- Parallel exit code when `phpunit.xml` is outdated ([14dd5cb](https://github.com/pestphp/pest/commit/14dd5cb57b9432300ac4e8095f069941cb43bdb5))
## [v2.1.0 (2023-03-21)](https://github.com/pestphp/pest/compare/v2.0.2...v2.1.0)
### Added
- `only` test case method ([bcd1503](https://github.com/pestphp/pest/commit/bcd1503cade938853a55c1283b02b6b820ea0b69))
### Fixed
- Issues with different characters on test names ([715](https://github.com/pestphp/pest/pull/715))
## [v2.0.2 (2023-03-20)](https://github.com/pestphp/pest/compare/v2.0.1...v2.0.2)
### Fixed
- `Pest.php` not being loaded in certain scenarios ([b887116](https://github.com/pestphp/pest/commit/b887116e5ce9a69403ad620cad20f0a029474eb5))
## [v2.0.1 (2023-03-20)](https://github.com/pestphp/pest/compare/v2.0.0...v2.0.1)
### Fixed
- Wrong `version` configuration key on `composer.json` ([8f91f40](https://github.com/pestphp/pest/commit/8f91f40e8ea8b35e04b7989bed6a8f9439e2a2d6))
## [v2.0.0 (2023-03-20)](https://github.com/pestphp/pest/compare/v1.22.6...v2.0.0)
Please consult the [upgrade guide](https://pestphp.com/docs/upgrade-guide) and [release notes](https://pestphp.com/docs/announcing-pest2) in the official Pest documentation.

View File

@ -22,18 +22,17 @@ We cannot thank our sponsors enough for their incredible support in funding Pest
### Platinum Sponsors
- **[Forge](https://forge.laravel.com)**
- **[LoadForge](https://loadforge.com)**
- **[Spatie](https://spatie.be)**
- **[Worksome](https://www.worksome.com/)**
### Premium Sponsors
- [Akaunting](https://akaunting.com)
- [Codecourse](https://codecourse.com/)
- [Laracasts](https://laracasts.com/)
- [Localazy](https://localazy.com)
- [Fathom Analytics](https://usefathom.com/)
- [Meema](https://meema.io)
- [Zapiet](https://www.zapiet.com)
- [Akaunting](https://akaunting.com/?ref=pestphp)
- [Codecourse](https://codecourse.com/?ref=pestphp)
- [Laracasts](https://laracasts.com/?ref=pestphp)
- [Laradir](https://laradir.com/?ref=pestphp)
- [Localazy](https://localazy.com/?ref=pestphp)
- [Stormlikes](https://www.stormlikes.net/?ref=pestphp)
- [Zapiet](https://www.zapiet.com/?ref=pestphp)
Pest is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.

View File

@ -5,10 +5,10 @@ When releasing a new version of Pest there are some checks and updates that need
> **For Pest v1 you should use the `1.x` branch instead.**
- Clear your local repository with: `git add . && git reset --hard && git checkout 2.x`
- On the GitHub repository, check the contents of [github.com/pestphp/pest/compare/{latest_version}...2.x](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}...2.x](https://github.com/pestphp/pest/compare/{latest_version}...2.x)
- Update the version number in [src/Pest.php](src/Pest.php)
- Run the tests locally using: `composer test`
- Commit the CHANGELOG and Pest file with the message: `git commit -m "release: vX.X.X"`
- Commit the Pest file with the message: `git commit -m "release: vX.X.X"`
- Push the changes to GitHub
- Check that the CI is passing as expected: [github.com/pestphp/pest/actions](https://github.com/pestphp/pest/actions)
- Tag and push the tag with `git tag vX.X.X && git push --tags`

View File

@ -13,39 +13,39 @@ use Symfony\Component\Console\Output\ConsoleOutput;
// Ensures Collision's Printer is registered.
$_SERVER['COLLISION_PRINTER'] = 'DefaultPrinter';
$args = $_SERVER['argv'];
$arguments = $originalArguments = $_SERVER['argv'];
$dirty = false;
$todo = false;
foreach ($args as $key => $value) {
foreach ($arguments as $key => $value) {
if ($value === '--compact') {
$_SERVER['COLLISION_PRINTER_COMPACT'] = 'true';
unset($args[$key]);
unset($arguments[$key]);
}
if ($value === '--profile') {
$_SERVER['COLLISION_PRINTER_PROFILE'] = 'true';
unset($args[$key]);
unset($arguments[$key]);
}
if (str_contains($value, '--test-directory')) {
unset($args[$key]);
unset($arguments[$key]);
}
if ($value === '--dirty') {
$dirty = true;
unset($args[$key]);
unset($arguments[$key]);
}
if ($value === '--todos') {
if (in_array($value, ['--todo', '--todos'], true)) {
$todo = true;
unset($args[$key]);
unset($arguments[$key]);
}
if (str_contains($value, '--teamcity')) {
unset($args[$key]);
$args[] = '--no-output';
unset($arguments[$key]);
$arguments[] = '--no-output';
unset($_SERVER['COLLISION_PRINTER']);
}
}
@ -88,9 +88,9 @@ use Symfony\Component\Console\Output\ConsoleOutput;
try {
$kernel = Kernel::boot($testSuite, $input, $output);
$result = $kernel->handle($args);
$result = $kernel->handle($originalArguments, $arguments);
$kernel->shutdown();
$kernel->terminate();
} catch (Throwable|Error $e) {
Panic::with($e);
}

View File

@ -81,6 +81,7 @@ $bootPest = (static function (): void {
$getopt['teamcity-file'] ?? null,
$getopt['testdox-file'] ?? null,
isset($getopt['testdox-color']),
$getopt['testdox-columns'] ?? null,
);
while (true) {

View File

@ -18,16 +18,17 @@
],
"require": {
"php": "^8.1.0",
"brianium/paratest": "^7.2.4",
"nunomaduro/collision": "^7.8.0",
"nunomaduro/termwind": "^1.15.1",
"pestphp/pest-plugin": "^2.0.1",
"pestphp/pest-plugin-arch": "^2.2.3",
"phpunit/phpunit": "^10.3.1"
"brianium/paratest": "^7.4.0",
"nunomaduro/collision": "^7.10.0|^8.1.0",
"nunomaduro/termwind": "^1.15.1|^2.0.0",
"pestphp/pest-plugin": "^2.1.1",
"pestphp/pest-plugin-arch": "^2.7.0",
"phpunit/phpunit": "^10.5.9"
},
"conflict": {
"webmozart/assert": "<1.11.0",
"phpunit/phpunit": ">10.3.1"
"phpunit/phpunit": ">10.5.9",
"sebastian/exporter": "<5.1.0",
"webmozart/assert": "<1.11.0"
},
"autoload": {
"psr-4": {
@ -50,9 +51,9 @@
]
},
"require-dev": {
"pestphp/pest-dev-tools": "^2.14.0",
"pestphp/pest-plugin-type-coverage": "^2.0.0",
"symfony/process": "^6.3.2"
"pestphp/pest-dev-tools": "^2.16.0",
"pestphp/pest-plugin-type-coverage": "^2.8.0",
"symfony/process": "^6.4.0|^7.0.3"
},
"minimum-stability": "dev",
"prefer-stable": true,
@ -75,7 +76,7 @@
"test:type:coverage": "php bin/pest --type-coverage --min=100",
"test:unit": "php bin/pest --colors=always --exclude-group=integration --compact",
"test:inline": "php bin/pest --colors=always --configuration=phpunit.inline.xml",
"test:parallel": "php bin/pest --colors=always --exclude-group=integration --parallel --processes=10",
"test:parallel": "php bin/pest --colors=always --exclude-group=integration --parallel --processes=3",
"test:integration": "php bin/pest --colors=always --group=integration",
"update:snapshots": "REBUILD_SNAPSHOTS=true php bin/pest --colors=always --update-snapshots",
"test": [
@ -108,6 +109,11 @@
"Pest\\Plugins\\Version",
"Pest\\Plugins\\Parallel"
]
},
"phpstan": {
"includes": [
"extension.neon"
]
}
}
}

View File

@ -1,21 +1,14 @@
ARG PHP=8.1
FROM php:${PHP}-cli-alpine
RUN apk update \
&& apk add zip libzip-dev icu-dev git
RUN apk update && apk add \
zip libzip-dev icu-dev git
RUN docker-php-ext-configure zip
RUN docker-php-ext-install zip
RUN docker-php-ext-enable zip
RUN docker-php-ext-install zip intl
RUN docker-php-ext-configure intl
RUN docker-php-ext-install intl
RUN docker-php-ext-enable intl
RUN apk add --no-cache $PHPIZE_DEPS linux-headers
RUN apk add --no-cache linux-headers autoconf build-base
RUN pecl install xdebug
RUN docker-php-ext-enable xdebug
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html

4
extension.neon Normal file
View File

@ -0,0 +1,4 @@
parameters:
universalObjectCratesClasses:
- Pest\Support\HigherOrderTapProxy
- Pest\Expectation

View File

@ -0,0 +1,459 @@
<?php
declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Logging\JUnit;
use DOMDocument;
use DOMElement;
use PHPUnit\Event\Code\Test;
use PHPUnit\Event\Code\TestMethod;
use PHPUnit\Event\EventFacadeIsSealedException;
use PHPUnit\Event\Facade;
use PHPUnit\Event\InvalidArgumentException;
use PHPUnit\Event\Telemetry\HRTime;
use PHPUnit\Event\Telemetry\Info;
use PHPUnit\Event\Test\Errored;
use PHPUnit\Event\Test\Failed;
use PHPUnit\Event\Test\Finished;
use PHPUnit\Event\Test\MarkedIncomplete;
use PHPUnit\Event\Test\PreparationStarted;
use PHPUnit\Event\Test\Prepared;
use PHPUnit\Event\Test\Skipped;
use PHPUnit\Event\TestSuite\Started;
use PHPUnit\Event\UnknownSubscriberTypeException;
use PHPUnit\TextUI\Output\Printer;
use PHPUnit\Util\Xml;
use function assert;
use function basename;
use function is_int;
use function sprintf;
use function str_replace;
use function trim;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class JunitXmlLogger
{
private readonly Printer $printer;
private readonly \Pest\Logging\Converter $converter; // pest-added
private DOMDocument $document;
private DOMElement $root;
/**
* @var DOMElement[]
*/
private array $testSuites = [];
/**
* @psalm-var array<int,int>
*/
private array $testSuiteTests = [0];
/**
* @psalm-var array<int,int>
*/
private array $testSuiteAssertions = [0];
/**
* @psalm-var array<int,int>
*/
private array $testSuiteErrors = [0];
/**
* @psalm-var array<int,int>
*/
private array $testSuiteFailures = [0];
/**
* @psalm-var array<int,int>
*/
private array $testSuiteSkipped = [0];
/**
* @psalm-var array<int,int>
*/
private array $testSuiteTimes = [0];
private int $testSuiteLevel = 0;
private ?DOMElement $currentTestCase = null;
private ?HRTime $time = null;
private bool $prepared = false;
private bool $preparationFailed = false;
/**
* @throws EventFacadeIsSealedException
* @throws UnknownSubscriberTypeException
*/
public function __construct(Printer $printer, Facade $facade)
{
$this->printer = $printer;
$this->converter = new \Pest\Logging\Converter(\Pest\Support\Container::getInstance()->get(\Pest\TestSuite::class)->rootPath); // pest-added
$this->registerSubscribers($facade);
$this->createDocument();
}
public function flush(): void
{
$this->printer->print($this->document->saveXML());
$this->printer->flush();
}
public function testSuiteStarted(Started $event): void
{
$testSuite = $this->document->createElement('testsuite');
$testSuite->setAttribute('name', $this->converter->getTestSuiteName($event->testSuite())); // pest-changed
if ($event->testSuite()->isForTestClass()) {
$testSuite->setAttribute('file', $this->converter->getTestSuiteLocation($event->testSuite()) ?? ''); // pest-changed
}
if ($this->testSuiteLevel > 0) {
$this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
} else {
$this->root->appendChild($testSuite);
}
$this->testSuiteLevel++;
$this->testSuites[$this->testSuiteLevel] = $testSuite;
$this->testSuiteTests[$this->testSuiteLevel] = 0;
$this->testSuiteAssertions[$this->testSuiteLevel] = 0;
$this->testSuiteErrors[$this->testSuiteLevel] = 0;
$this->testSuiteFailures[$this->testSuiteLevel] = 0;
$this->testSuiteSkipped[$this->testSuiteLevel] = 0;
$this->testSuiteTimes[$this->testSuiteLevel] = 0;
}
public function testSuiteFinished(): void
{
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'tests',
(string) $this->testSuiteTests[$this->testSuiteLevel],
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'assertions',
(string) $this->testSuiteAssertions[$this->testSuiteLevel],
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'errors',
(string) $this->testSuiteErrors[$this->testSuiteLevel],
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'failures',
(string) $this->testSuiteFailures[$this->testSuiteLevel],
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'skipped',
(string) $this->testSuiteSkipped[$this->testSuiteLevel],
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'time',
sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel]),
);
if ($this->testSuiteLevel > 1) {
$this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel];
$this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel];
$this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel];
$this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel];
$this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel];
$this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel];
}
$this->testSuiteLevel--;
}
/**
* @throws InvalidArgumentException
*/
public function testPreparationStarted(PreparationStarted $event): void
{
$this->createTestCase($event);
}
/**
* @throws InvalidArgumentException
*/
public function testPreparationFailed(): void
{
$this->preparationFailed = true;
}
/**
* @throws InvalidArgumentException
*/
public function testPrepared(): void
{
$this->prepared = true;
}
/**
* @throws InvalidArgumentException
*/
public function testFinished(Finished $event): void
{
if ($this->preparationFailed) {
return;
}
$this->handleFinish($event->telemetryInfo(), $event->numberOfAssertionsPerformed());
}
/**
* @throws InvalidArgumentException
*/
public function testMarkedIncomplete(MarkedIncomplete $event): void
{
$this->handleIncompleteOrSkipped($event);
}
/**
* @throws InvalidArgumentException
*/
public function testSkipped(Skipped $event): void
{
$this->handleIncompleteOrSkipped($event);
}
/**
* @throws InvalidArgumentException
*/
public function testErrored(Errored $event): void
{
$this->handleFault($event, 'error');
$this->testSuiteErrors[$this->testSuiteLevel]++;
}
/**
* @throws InvalidArgumentException
*/
public function testFailed(Failed $event): void
{
$this->handleFault($event, 'failure');
$this->testSuiteFailures[$this->testSuiteLevel]++;
}
/**
* @throws InvalidArgumentException
*/
private function handleFinish(Info $telemetryInfo, int $numberOfAssertionsPerformed): void
{
assert($this->currentTestCase !== null);
assert($this->time !== null);
$time = $telemetryInfo->time()->duration($this->time)->asFloat();
$this->testSuiteAssertions[$this->testSuiteLevel] += $numberOfAssertionsPerformed;
$this->currentTestCase->setAttribute(
'assertions',
(string) $numberOfAssertionsPerformed,
);
$this->currentTestCase->setAttribute(
'time',
sprintf('%F', $time),
);
$this->testSuites[$this->testSuiteLevel]->appendChild(
$this->currentTestCase,
);
$this->testSuiteTests[$this->testSuiteLevel]++;
$this->testSuiteTimes[$this->testSuiteLevel] += $time;
$this->currentTestCase = null;
$this->time = null;
$this->prepared = false;
}
/**
* @throws EventFacadeIsSealedException
* @throws UnknownSubscriberTypeException
*/
private function registerSubscribers(Facade $facade): void
{
$facade->registerSubscribers(
new TestSuiteStartedSubscriber($this),
new TestSuiteFinishedSubscriber($this),
new TestPreparationStartedSubscriber($this),
new TestPreparationFailedSubscriber($this),
new TestPreparedSubscriber($this),
new TestFinishedSubscriber($this),
new TestErroredSubscriber($this),
new TestFailedSubscriber($this),
new TestMarkedIncompleteSubscriber($this),
new TestSkippedSubscriber($this),
new TestRunnerExecutionFinishedSubscriber($this),
);
}
private function createDocument(): void
{
$this->document = new DOMDocument('1.0', 'UTF-8');
$this->document->formatOutput = true;
$this->root = $this->document->createElement('testsuites');
$this->document->appendChild($this->root);
}
/**
* @throws InvalidArgumentException
*/
private function handleFault(Errored|Failed $event, string $type): void
{
if (! $this->prepared) {
$this->createTestCase($event);
}
assert($this->currentTestCase !== null);
$buffer = $this->converter->getTestCaseMethodName($event->test()); // pest-changed
$throwable = $event->throwable();
$buffer .= trim(
$this->converter->getExceptionMessage($throwable).PHP_EOL. // pest-changed
$this->converter->getExceptionDetails($throwable), // pest-changed
);
$fault = $this->document->createElement(
$type,
Xml::prepareString($buffer),
);
$fault->setAttribute('type', $throwable->className());
$this->currentTestCase->appendChild($fault);
if (! $this->prepared) {
$this->handleFinish($event->telemetryInfo(), 0);
}
}
/**
* @throws InvalidArgumentException
*/
private function handleIncompleteOrSkipped(MarkedIncomplete|Skipped $event): void
{
if (! $this->prepared) {
$this->createTestCase($event);
}
assert($this->currentTestCase !== null);
$skipped = $this->document->createElement('skipped');
$this->currentTestCase->appendChild($skipped);
$this->testSuiteSkipped[$this->testSuiteLevel]++;
if (! $this->prepared) {
$this->handleFinish($event->telemetryInfo(), 0);
}
}
/**
* @throws InvalidArgumentException
*/
private function testAsString(Test $test): string
{
if ($test->isPhpt()) {
return basename($test->file());
}
assert($test instanceof TestMethod);
return sprintf(
'%s::%s%s',
$test->className(),
$this->name($test),
PHP_EOL,
);
}
/**
* @throws InvalidArgumentException
*/
private function name(Test $test): string
{
if ($test->isPhpt()) {
return basename($test->file());
}
assert($test instanceof TestMethod);
if (! $test->testData()->hasDataFromDataProvider()) {
return $test->methodName();
}
$dataSetName = $test->testData()->dataFromDataProvider()->dataSetName();
if (is_int($dataSetName)) {
return sprintf(
'%s with data set #%d',
$test->methodName(),
$dataSetName,
);
}
return sprintf(
'%s with data set "%s"',
$test->methodName(),
$dataSetName,
);
}
/**
* @throws InvalidArgumentException
*
* @psalm-assert !null $this->currentTestCase
*/
private function createTestCase(Errored|Failed|MarkedIncomplete|PreparationStarted|Prepared|Skipped $event): void
{
$testCase = $this->document->createElement('testcase');
$test = $event->test();
$file = $this->converter->getTestCaseLocation($test); // pest-added
$testCase->setAttribute('name', $this->converter->getTestCaseMethodName($test)); // pest-changed
$testCase->setAttribute('file', $file); // pest-changed
if ($test->isTestMethod()) {
assert($test instanceof TestMethod);
//$testCase->setAttribute('line', (string) $test->line()); // pest-removed
$className = $this->converter->getTrimmedTestClassName($test); // pest-added
$testCase->setAttribute('class', $className); // pest-changed
$testCase->setAttribute('classname', str_replace('\\', '.', $className)); // pest-changed
}
$this->currentTestCase = $testCase;
$this->time = $event->telemetryInfo()->time();
}
}

View File

@ -34,17 +34,18 @@
namespace PHPUnit\Runner\Filter;
use function end;
use Exception;
use function implode;
use Pest\Contracts\HasPrintableTestCaseName;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use function preg_match;
use RecursiveFilterIterator;
use RecursiveIterator;
use function end;
use function implode;
use function preg_match;
use function sprintf;
use function str_replace;

View File

@ -45,9 +45,15 @@ declare(strict_types=1);
namespace PHPUnit\Runner\ResultCache;
use const DIRECTORY_SEPARATOR;
use PHPUnit\Framework\TestStatus\TestStatus;
use PHPUnit\Runner\DirectoryCannotBeCreatedException;
use PHPUnit\Runner\Exception;
use PHPUnit\Util\Filesystem;
use function array_keys;
use function assert;
use const DIRECTORY_SEPARATOR;
use function dirname;
use function file_get_contents;
use function file_put_contents;
@ -57,10 +63,6 @@ use function is_file;
use function json_decode;
use function json_encode;
use function Pest\version;
use PHPUnit\Framework\TestStatus\TestStatus;
use PHPUnit\Runner\DirectoryCannotBeCreatedException;
use PHPUnit\Runner\Exception;
use PHPUnit\Util\Filesystem;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
@ -89,7 +91,7 @@ final class DefaultResultCache implements ResultCache
*/
private array $times = [];
public function __construct(string $filepath = null)
public function __construct(?string $filepath = null)
{
if ($filepath !== null && is_dir($filepath)) {
$filepath .= DIRECTORY_SEPARATOR.self::DEFAULT_RESULT_CACHE_FILENAME;

View File

@ -36,18 +36,19 @@ declare(strict_types=1);
namespace PHPUnit\Runner;
use function array_diff;
use function array_values;
use function basename;
use function class_exists;
use Exception;
use function get_declared_classes;
use Pest\Contracts\HasPrintableTestCaseName;
use Pest\TestCases\IgnorableTestCase;
use Pest\TestSuite;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionException;
use function array_diff;
use function array_values;
use function basename;
use function class_exists;
use function get_declared_classes;
use function substr;
/**
@ -60,6 +61,11 @@ final class TestSuiteLoader
*/
private static array $loadedClasses = [];
/**
* @psalm-var array<string, array<class-string>>
*/
private static array $loadedClassesByFilename = [];
/**
* @psalm-var list<class-string>
*/
@ -97,6 +103,17 @@ final class TestSuiteLoader
self::$loadedClasses = array_merge($loadedClasses, self::$loadedClasses);
foreach ($loadedClasses as $loadedClass) {
$reflection = new ReflectionClass($loadedClass);
$filename = $reflection->getFileName();
self::$loadedClassesByFilename[$filename] = [
$loadedClass,
...self::$loadedClassesByFilename[$filename] ?? [],
];
}
$loadedClasses = array_merge(self::$loadedClassesByFilename[$suiteClassFile] ?? [], $loadedClasses);
if (empty($loadedClasses)) {
return $this->exceptionFor($suiteClassName, $suiteClassFile);
}
@ -115,7 +132,7 @@ final class TestSuiteLoader
continue;
}
if ($class->isAbstract() || ($class->getFileName() !== $suiteClassFile)) {
if ($class->isAbstract() || ($suiteClassFile !== $class->getFileName())) {
if (! str_contains($class->getFileName(), 'TestCaseFactory.php')) {
continue;
}

View File

@ -45,7 +45,6 @@ declare(strict_types=1);
namespace PHPUnit\TextUI;
use function array_map;
use Pest\Plugins\Only;
use PHPUnit\Event;
use PHPUnit\Framework\TestSuite;
@ -53,6 +52,8 @@ use PHPUnit\Runner\Filter\Factory;
use PHPUnit\TextUI\Configuration\Configuration;
use PHPUnit\TextUI\Configuration\FilterNotConfiguredException;
use function array_map;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/

View File

@ -10,7 +10,7 @@
?>
<div class="my-1">
<span class="ml-2 px-1 bg-<?php echo $bgBadgeColor ?>-600 font-bold"><?php echo htmlspecialchars($bgBadgeText) ?></span>
<span class="ml-2 px-1 bg-<?php echo $bgBadgeColor ?> font-bold"><?php echo htmlspecialchars($bgBadgeText) ?></span>
<span class="ml-1">
<?php echo htmlspecialchars($content) ?>
</span>

View File

@ -7,12 +7,13 @@ namespace Pest\Bootstrappers;
use Pest\Contracts\Bootstrapper;
use Pest\Support\DatasetInfo;
use Pest\Support\Str;
use function Pest\testDirectory;
use Pest\TestSuite;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SebastianBergmann\FileIterator\Facade as PhpUnitFileIterator;
use function Pest\testDirectory;
/**
* @internal
*/
@ -77,6 +78,8 @@ final class BootFiles implements Bootstrapper
private function bootDatasets(string $testsPath): void
{
assert(strlen($testsPath) > 0);
$files = (new PhpUnitFileIterator)->getFilesAsArray($testsPath, '.php');
foreach ($files as $file) {

View File

@ -15,16 +15,17 @@ final class BootOverrides implements Bootstrapper
/**
* The list of files to be overridden.
*
* @var array<int, string>
* @var array<string, string>
*/
private const FILES = [
'Runner/Filter/NameFilterIterator.php',
'Runner/ResultCache/DefaultResultCache.php',
'Runner/TestSuiteLoader.php',
'TextUI/Command/WarmCodeCoverageCacheCommand.php',
'TextUI/Output/Default/ProgressPrinter/TestSkippedSubscriber.php',
'TextUI/TestSuiteFilterProcessor.php',
'Event/Value/ThrowableBuilder.php',
public const FILES = [
'c7b9c8a96006dea314204a8f09a8764e51ce0b9b79aadd58da52e8c328db4870' => 'Runner/Filter/NameFilterIterator.php',
'52b2574e96269aca1bb2d41bbf418c3bcf23dd21d14c66f90789025c309e39df' => 'Runner/ResultCache/DefaultResultCache.php',
'bc8718c89264f65800beabc23e51c6d3bcff87dfc764a12179ef5dbfde272c8b' => 'Runner/TestSuiteLoader.php',
'f41e48d6cb546772a7de4f8e66b6b7ce894a5318d063eb52e354d206e96c701c' => 'TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php',
'cb7519f2d82893640b694492cf7ec9528da80773cc1d259634181b5d393528b5' => 'TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php',
'6db25ee539e9b12b1fb4e044a0a93410e015bc983ecdd3909cd394fe44ae8c95' => 'TextUI/TestSuiteFilterProcessor.php',
'ef64a657ed9c0067791483784944107827bf227c7e3200f212b6751876b99e25' => 'Event/Value/ThrowableBuilder.php',
'c78f96e34b98ed01dd8106539d59b8aa8d67f733274118b827c01c5c4111c033' => 'Logging/JUnit/JunitXmlLogger.php',
];
/**

View File

@ -60,7 +60,7 @@ trait Pipeable
}
/**
* Get th list of pipes by the given name.
* Get the list of pipes by the given name.
*
* @return array<int, Closure>
*/

View File

@ -193,6 +193,7 @@ trait Testable
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
$description = $this->dataName() ? $method->description.' with '.$this->dataName() : $method->description;
$description = htmlspecialchars(html_entity_decode($description), ENT_NOQUOTES);
if ($method->repetitions > 1) {
$matches = [];
@ -289,7 +290,7 @@ trait Testable
return $arguments;
}
if (in_array($testParameterTypes[0], [Closure::class, 'callable'])) {
if (isset($testParameterTypes[0]) && in_array($testParameterTypes[0], [Closure::class, 'callable'])) {
return $arguments;
}

View File

@ -23,9 +23,10 @@ final class Thanks
* @var array<string, string>
*/
private const FUNDING_MESSAGES = [
'Star the project on GitHub' => 'https://github.com/pestphp/pest',
'Tweet about the project' => 'https://twitter.com/pestphp',
'Sponsor the project' => 'https://github.com/sponsors/nunomaduro',
'Star' => 'https://github.com/pestphp/pest',
'News' => 'https://twitter.com/pestphp',
'Videos' => 'https://youtube.com/@nunomaduro',
'Sponsor' => 'https://github.com/sponsors/nunomaduro',
];
/**

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Pest\Contracts\Plugins;
/**
* @internal
*/
interface HandlesOriginalArguments
{
/**
* Adds original arguments before the Test Suite execution.
*
* @param array<int, string> $arguments
*/
public function handleOriginalArguments(array $arguments): void;
}

View File

@ -7,10 +7,10 @@ namespace Pest\Contracts\Plugins;
/**
* @internal
*/
interface Shutdownable
interface Terminable
{
/**
* Shutdowns the plugin.
* Terminates the plugin.
*/
public function shutdown(): void;
public function terminate(): void;
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Pest\Exceptions;
use NunoMaduro\Collision\Contracts\RenderlessTrace;
use RuntimeException;
/**
* @internal
*/
final class FatalException extends RuntimeException implements RenderlessTrace
{
//
}

View File

@ -14,7 +14,7 @@ use Symfony\Component\Console\Output\OutputInterface;
/**
* @internal
*/
final class NoDirtyTestsFound extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace, Panicable
final class NoDirtyTestsFound extends InvalidArgumentException implements ExceptionInterface, Panicable, RenderlessEditor, RenderlessTrace
{
/**
* Renders the panic on the given output.

View File

@ -4,8 +4,11 @@ declare(strict_types=1);
namespace Pest;
use Attribute;
use BadMethodCallException;
use Closure;
use InvalidArgumentException;
use OutOfRangeException;
use Pest\Arch\Contracts\ArchExpectation;
use Pest\Arch\Expectations\Targeted;
use Pest\Arch\Expectations\ToBeUsedIn;
@ -28,12 +31,10 @@ use Pest\Expectations\OppositeExpectation;
use Pest\Matchers\Any;
use Pest\Support\ExpectationPipeline;
use PHPUnit\Architecture\Elements\ObjectDescription;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\ExpectationFailedException;
use ReflectionEnum;
/**
* @internal
*
* @template TValue
*
* @property OppositeExpectation $not Creates the opposite expectation.
@ -127,6 +128,40 @@ final class Expectation
exit(1);
}
/**
* Dump the expectation value when the result of the condition is truthy.
*
* @param (\Closure(TValue): bool)|bool $condition
* @return self<TValue>
*/
public function ddWhen(Closure|bool $condition, mixed ...$arguments): Expectation
{
$condition = $condition instanceof Closure ? $condition($this->value) : $condition;
if (! $condition) {
return $this;
}
$this->dd(...$arguments);
}
/**
* Dump the expectation value when the result of the condition is falsy.
*
* @param (\Closure(TValue): bool)|bool $condition
* @return self<TValue>
*/
public function ddUnless(Closure|bool $condition, mixed ...$arguments): Expectation
{
$condition = $condition instanceof Closure ? $condition($this->value) : $condition;
if ($condition) {
return $this;
}
$this->dd(...$arguments);
}
/**
* Send the expectation value to Ray along with all given arguments.
*
@ -156,7 +191,7 @@ final class Expectation
*
* @return EachExpectation<TValue>
*/
public function each(callable $callback = null): EachExpectation
public function each(?callable $callback = null): EachExpectation
{
if (! is_iterable($this->value)) {
throw new BadMethodCallException('Expectation value is not iterable.');
@ -185,30 +220,26 @@ final class Expectation
throw new BadMethodCallException('Expectation value is not iterable.');
}
$value = is_array($this->value) ? $this->value : iterator_to_array($this->value);
$keys = array_keys($value);
$values = array_values($value);
$callbacksCount = count($callbacks);
$index = 0;
while (count($callbacks) < count($values)) {
$callbacks[] = $callbacks[$index];
$index = $index < count($values) - 1 ? $index + 1 : 0;
if (count($callbacks) == 0) {
throw new InvalidArgumentException('No sequence expectations defined.');
}
if ($callbacksCount > count($values)) {
Assert::assertLessThanOrEqual(count($value), count($callbacks));
}
$index = $valuesCount = 0;
foreach ($values as $key => $item) {
if ($callbacks[$key] instanceof Closure) {
call_user_func($callbacks[$key], new self($item), new self($keys[$key]));
foreach ($this->value as $key => $value) {
$valuesCount++;
continue;
if ($callbacks[$index] instanceof Closure) {
$callbacks[$index](new self($value), new self($key));
} else {
(new self($value))->toEqual($callbacks[$index]);
}
(new self($item))->toEqual($callbacks[$key]);
$index = isset($callbacks[$index + 1]) ? $index + 1 : 0;
}
if ($valuesCount < count($callbacks)) {
throw new OutOfRangeException('Sequence expectations are more than the iterable items.');
}
return $this;
@ -317,9 +348,15 @@ final class Expectation
return new HigherOrderExpectation($this, call_user_func_array($this->value->$method(...), $parameters));
}
ExpectationPipeline::for($this->getExpectationClosure($method))
$closure = $this->getExpectationClosure($method);
$reflectionClosure = new \ReflectionFunction($closure);
$expectation = $reflectionClosure->getClosureThis();
assert(is_object($expectation));
ExpectationPipeline::for($closure)
->send(...$parameters)
->through($this->pipes($method, $this, Expectation::class))
->through($this->pipes($method, $expectation, Expectation::class))
->run();
return $this;
@ -470,6 +507,19 @@ final class Expectation
);
}
/**
* Asserts that the given expectation target has a specific method.
*/
public function toHaveMethod(string $method): ArchExpectation
{
return Targeted::make(
$this,
fn (ObjectDescription $object): bool => $object->reflectionClass->hasMethod($method),
'to have method',
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
);
}
/**
* Asserts that the given expectation target is enum.
*/
@ -492,7 +542,7 @@ final class Expectation
}
/**
* Asserts that the given expectation targets is an class.
* Asserts that the given expectation target is a class.
*/
public function toBeClass(): ArchExpectation
{
@ -706,4 +756,178 @@ final class Expectation
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class'))
);
}
/**
* Asserts that the given expectation is iterable and contains snake_case keys.
*
* @return self<TValue>
*/
public function toHaveSnakeCaseKeys(string $message = ''): self
{
if (! is_iterable($this->value)) {
InvalidExpectationValue::expected('iterable');
}
foreach ($this->value as $k => $item) {
if (is_string($k)) {
$this->and($k)->toBeSnakeCase($message);
}
if (is_array($item)) {
$this->and($item)->toHaveSnakeCaseKeys($message);
}
}
return $this;
}
/**
* Asserts that the given expectation is iterable and contains kebab-case keys.
*
* @return self<TValue>
*/
public function toHaveKebabCaseKeys(string $message = ''): self
{
if (! is_iterable($this->value)) {
InvalidExpectationValue::expected('iterable');
}
foreach ($this->value as $k => $item) {
if (is_string($k)) {
$this->and($k)->toBeKebabCase($message);
}
if (is_array($item)) {
$this->and($item)->toHaveKebabCaseKeys($message);
}
}
return $this;
}
/**
* Asserts that the given expectation is iterable and contains camelCase keys.
*
* @return self<TValue>
*/
public function toHaveCamelCaseKeys(string $message = ''): self
{
if (! is_iterable($this->value)) {
InvalidExpectationValue::expected('iterable');
}
foreach ($this->value as $k => $item) {
if (is_string($k)) {
$this->and($k)->toBeCamelCase($message);
}
if (is_array($item)) {
$this->and($item)->toHaveCamelCaseKeys($message);
}
}
return $this;
}
/**
* Asserts that the given expectation is iterable and contains StudlyCase keys.
*
* @return self<TValue>
*/
public function toHaveStudlyCaseKeys(string $message = ''): self
{
if (! is_iterable($this->value)) {
InvalidExpectationValue::expected('iterable');
}
foreach ($this->value as $k => $item) {
if (is_string($k)) {
$this->and($k)->toBeStudlyCase($message);
}
if (is_array($item)) {
$this->and($item)->toHaveStudlyCaseKeys($message);
}
}
return $this;
}
/**
* Asserts that the given expectation target to have the given attribute.
*
* @param class-string<Attribute> $attribute
*/
public function toHaveAttribute(string $attribute): ArchExpectation
{
return Targeted::make(
$this,
fn (ObjectDescription $object): bool => $object->reflectionClass->getAttributes($attribute) !== [],
"to have attribute '{$attribute}'",
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
);
}
/**
* Asserts that the given expectation target has a constructor method.
*/
public function toHaveConstructor(): ArchExpectation
{
return $this->toHaveMethod('__construct');
}
/**
* Asserts that the given expectation target has a destructor method.
*/
public function toHaveDestructor(): ArchExpectation
{
return $this->toHaveMethod('__destruct');
}
/**
* Asserts that the given expectation target is a backed enum of given type.
*/
private function toBeBackedEnum(string $backingType): ArchExpectation
{
return Targeted::make(
$this,
fn (ObjectDescription $object): bool => $object->reflectionClass->isEnum()
&& (new ReflectionEnum($object->name))->isBacked() // @phpstan-ignore-line
&& (string) (new ReflectionEnum($object->name))->getBackingType() === $backingType, // @phpstan-ignore-line
'to be '.$backingType.' backed enum',
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
);
}
/**
* Asserts that the given expectation targets are string backed enums.
*/
public function toBeStringBackedEnums(): ArchExpectation
{
return $this->toBeStringBackedEnum();
}
/**
* Asserts that the given expectation targets are int backed enums.
*/
public function toBeIntBackedEnums(): ArchExpectation
{
return $this->toBeIntBackedEnum();
}
/**
* Asserts that the given expectation target is a string backed enum.
*/
public function toBeStringBackedEnum(): ArchExpectation
{
return $this->toBeBackedEnum('string');
}
/**
* Asserts that the given expectation target is an int backed enum.
*/
public function toBeIntBackedEnum(): ArchExpectation
{
return $this->toBeBackedEnum('int');
}
}

View File

@ -4,9 +4,10 @@ declare(strict_types=1);
namespace Pest\Expectations;
use function expect;
use Pest\Expectation;
use function expect;
/**
* @internal
*

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Pest\Expectations;
use Attribute;
use Pest\Arch\Contracts\ArchExpectation;
use Pest\Arch\Expectations\Targeted;
use Pest\Arch\Expectations\ToBeUsedIn;
@ -149,6 +150,19 @@ final class OppositeExpectation
);
}
/**
* Asserts that the given expectation target does not have a specific method.
*/
public function toHaveMethod(string $method): ArchExpectation
{
return Targeted::make(
$this->original,
fn (ObjectDescription $object): bool => ! $object->reflectionClass->hasMethod($method),
'to not have method',
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
);
}
/**
* Asserts that the given expectation target is not enum.
*/
@ -171,7 +185,7 @@ final class OppositeExpectation
}
/**
* Asserts that the given expectation targets is an class.
* Asserts that the given expectation targets is not class.
*/
public function toBeClass(): ArchExpectation
{
@ -213,7 +227,7 @@ final class OppositeExpectation
}
/**
* Asserts that the given expectation target to be subclass of the given class.
* Asserts that the given expectation target to be not subclass of the given class.
*
* @param class-string $class
*/
@ -378,6 +392,21 @@ final class OppositeExpectation
);
}
/**
* Asserts that the given expectation target not to have the given attribute.
*
* @param class-string<Attribute> $attribute
*/
public function toHaveAttribute(string $attribute): ArchExpectation
{
return Targeted::make(
$this->original,
fn (ObjectDescription $object): bool => $object->reflectionClass->getAttributes($attribute) === [],
"to not have attribute '{$attribute}'",
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class'))
);
}
/**
* Handle dynamic method calls into the original expectation.
*
@ -440,4 +469,67 @@ final class OppositeExpectation
implode(' ', array_map(fn (mixed $argument): string => $toString($argument), $arguments)),
));
}
/**
* Asserts that the given expectation target does not have a constructor method.
*/
public function toHaveConstructor(): ArchExpectation
{
return $this->toHaveMethod('__construct');
}
/**
* Asserts that the given expectation target does not have a destructor method.
*/
public function toHaveDestructor(): ArchExpectation
{
return $this->toHaveMethod('__destruct');
}
/**
* Asserts that the given expectation target is not a backed enum of given type.
*/
private function toBeBackedEnum(string $backingType): ArchExpectation
{
return Targeted::make(
$this->original,
fn (ObjectDescription $object): bool => ! $object->reflectionClass->isEnum()
|| ! (new \ReflectionEnum($object->name))->isBacked() // @phpstan-ignore-line
|| (string) (new \ReflectionEnum($object->name))->getBackingType() !== $backingType, // @phpstan-ignore-line
'not to be '.$backingType.' backed enum',
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
);
}
/**
* Asserts that the given expectation targets are not string backed enums.
*/
public function toBeStringBackedEnums(): ArchExpectation
{
return $this->toBeStringBackedEnum();
}
/**
* Asserts that the given expectation targets are not int backed enums.
*/
public function toBeIntBackedEnums(): ArchExpectation
{
return $this->toBeIntBackedEnum();
}
/**
* Asserts that the given expectation target is not a string backed enum.
*/
public function toBeStringBackedEnum(): ArchExpectation
{
return $this->toBeBackedEnum('string');
}
/**
* Asserts that the given expectation target is not an int backed enum.
*/
public function toBeIntBackedEnum(): ArchExpectation
{
return $this->toBeBackedEnum('int');
}
}

View File

@ -19,7 +19,7 @@ final class Depends implements AddsAnnotations
public function __invoke(TestCaseMethodFactory $method, array $annotations): array
{
foreach ($method->depends as $depend) {
$depend = Str::evaluable($depend);
$depend = Str::evaluable($method->describing !== null ? Str::describe($method->describing, $depend) : $depend);
$annotations[] = "@depends $depend";
}

View File

@ -15,12 +15,11 @@ final class TestDox implements AddsAnnotations
public function __invoke(TestCaseMethodFactory $method, array $annotations): array
{
/*
* escapes docblock according to
* https://manual.phpdoc.org/HTMLframesConverter/default/phpDocumentor/tutorial_phpDocumentor.howto.pkg.html#basics.desc
*
* note: '@' escaping is not needed as it cannot be the first character of the line (it always starts with @testdox
*/
* Escapes docblock according to
* https://manual.phpdoc.org/HTMLframesConverter/default/phpDocumentor/tutorial_phpDocumentor.howto.pkg.html#basics.desc
*
* Note: '@' escaping is not needed as it cannot be the first character of the line (it always starts with @testdox).
*/
assert($method->description !== null);
$methodDescription = str_replace('*/', '{@*}', $method->description);

View File

@ -20,7 +20,7 @@ abstract class Attribute
* @param array<int, string> $attributes
* @return array<int, string>
*/
public function __invoke(TestCaseMethodFactory $method, array $attributes): array // @phpstan-ignore-line
public function __invoke(TestCaseMethodFactory $method, array $attributes): array // @phpstan-ignore-line
{
return $attributes;
}

View File

@ -142,7 +142,7 @@ final class TestCaseFactory
$namespace = implode('\\', $partsFQN);
$baseClass = sprintf('\%s', $this->class);
if ('' === trim($className)) {
if (trim($className) === '') {
$className = 'InvalidTestName'.Str::random();
}
@ -241,7 +241,7 @@ final class TestCaseFactory
throw ShouldNotHappen::fromMessage('The test description may not be empty.');
}
if (Str::evaluable($method->description) === $methodName) {
if ($methodName === Str::evaluable($method->description)) {
return true;
}
}
@ -259,7 +259,7 @@ final class TestCaseFactory
throw ShouldNotHappen::fromMessage('The test description may not be empty.');
}
if (Str::evaluable($method->description) === $methodName) {
if ($methodName === Str::evaluable($method->description)) {
return $method;
}
}

View File

@ -73,7 +73,7 @@ final class TestCaseMethodFactory
public ?Closure $closure,
) {
$this->closure ??= function (): void {
Assert::getCount() > 0 ?: self::markTestIncomplete(); // @phpstan-ignore-line
(Assert::getCount() > 0 || $this->doesNotPerformAssertions()) ?: self::markTestIncomplete(); // @phpstan-ignore-line
};
$this->bootHigherOrderable();

View File

@ -55,7 +55,7 @@ if (! function_exists('beforeEach')) {
*
* @return HigherOrderTapProxy<Expectable|TestCall|TestCase>|Expectable|TestCall|TestCase|mixed
*/
function beforeEach(Closure $closure = null): BeforeEachCall
function beforeEach(?Closure $closure = null): BeforeEachCall
{
$filename = Backtrace::file();
@ -116,7 +116,7 @@ if (! function_exists('test')) {
*
* @return Expectable|TestCall|TestCase|mixed
*/
function test(string $description = null, Closure $closure = null): HigherOrderTapProxy|TestCall
function test(?string $description = null, ?Closure $closure = null): HigherOrderTapProxy|TestCall
{
if ($description === null && TestSuite::getInstance()->test instanceof \PHPUnit\Framework\TestCase) {
return new HigherOrderTapProxy(TestSuite::getInstance()->test);
@ -136,7 +136,7 @@ if (! function_exists('it')) {
*
* @return Expectable|TestCall|TestCase|mixed
*/
function it(string $description, Closure $closure = null): TestCall
function it(string $description, ?Closure $closure = null): TestCall
{
$description = sprintf('it %s', $description);
@ -171,7 +171,7 @@ if (! function_exists('afterEach')) {
*
* @return Expectable|HigherOrderTapProxy<Expectable|TestCall|TestCase>|TestCall|mixed
*/
function afterEach(Closure $closure = null): AfterEachCall
function afterEach(?Closure $closure = null): AfterEachCall
{
$filename = Backtrace::file();

View File

@ -4,18 +4,25 @@ declare(strict_types=1);
namespace Pest;
use NunoMaduro\Collision\Writer;
use Pest\Contracts\Bootstrapper;
use Pest\Exceptions\FatalException;
use Pest\Exceptions\NoDirtyTestsFound;
use Pest\Plugins\Actions\CallsAddsOutput;
use Pest\Plugins\Actions\CallsBoot;
use Pest\Plugins\Actions\CallsHandleArguments;
use Pest\Plugins\Actions\CallsShutdown;
use Pest\Plugins\Actions\CallsHandleOriginalArguments;
use Pest\Plugins\Actions\CallsTerminable;
use Pest\Support\Container;
use Pest\Support\Reflection;
use Pest\Support\View;
use PHPUnit\TestRunner\TestResult\Facade;
use PHPUnit\TextUI\Application;
use PHPUnit\TextUI\Configuration\Registry;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;
use Whoops\Exception\Inspector;
/**
* @internal
@ -43,7 +50,7 @@ final class Kernel
private readonly Application $application,
private readonly OutputInterface $output,
) {
register_shutdown_function(fn () => $this->shutdown());
//
}
/**
@ -59,6 +66,13 @@ final class Kernel
->add(OutputInterface::class, $output)
->add(Container::class, $container);
$kernel = new self(
new Application(),
$output,
);
register_shutdown_function(fn () => $kernel->shutdown());
foreach (self::BOOTSTRAPPERS as $bootstrapper) {
$bootstrapper = Container::getInstance()->get($bootstrapper);
assert($bootstrapper instanceof Bootstrapper);
@ -68,11 +82,6 @@ final class Kernel
CallsBoot::execute();
$kernel = new self(
new Application(),
$output,
);
Container::getInstance()->add(self::class, $kernel);
return $kernel;
@ -81,14 +90,17 @@ final class Kernel
/**
* Runs the application, and returns the exit code.
*
* @param array<int, string> $args
* @param array<int, string> $originalArguments
* @param array<int, string> $arguments
*/
public function handle(array $args): int
public function handle(array $originalArguments, array $arguments): int
{
$args = CallsHandleArguments::execute($args);
CallsHandleOriginalArguments::execute($originalArguments);
$arguments = CallsHandleArguments::execute($arguments);
try {
$this->application->run($args);
$this->application->run($arguments);
} catch (NoDirtyTestsFound) {
$this->output->writeln([
'',
@ -106,16 +118,54 @@ final class Kernel
}
/**
* Shutdown the Kernel.
* Terminate the Kernel.
*/
public function shutdown(): void
public function terminate(): void
{
$preBufferOutput = Container::getInstance()->get(KernelDump::class);
assert($preBufferOutput instanceof KernelDump);
$preBufferOutput->shutdown();
$preBufferOutput->terminate();
CallsShutdown::execute();
CallsTerminable::execute();
}
/**
* Shutdowns unexpectedly the Kernel.
*/
public function shutdown(): void
{
$this->terminate();
if (is_array($error = error_get_last())) {
if (! in_array($error['type'], [E_ERROR, E_CORE_ERROR], true)) {
return;
}
$message = $error['message'];
$file = $error['file'];
$line = $error['line'];
try {
$writer = new Writer(null, $this->output);
$throwable = new FatalException($message);
Reflection::setPropertyValue($throwable, 'line', $line);
Reflection::setPropertyValue($throwable, 'file', $file);
$inspector = new Inspector($throwable);
$writer->write($inspector);
} catch (Throwable) { // @phpstan-ignore-line
View::render('components.badge', [
'type' => 'ERROR',
'content' => sprintf('%s in %s:%d', $message, $file, $line),
]);
}
exit(1);
}
}
}

View File

@ -48,9 +48,9 @@ final class KernelDump
}
/**
* Shutdown the output buffering.
* Terminate the output buffering.
*/
public function shutdown(): void
public function terminate(): void
{
$this->disable();
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Pest\Logging\TeamCity;
namespace Pest\Logging;
use NunoMaduro\Collision\Adapters\Phpunit\State;
use Pest\Exceptions\ShouldNotHappen;
@ -150,6 +150,14 @@ final class Converter
return Str::after($name, self::PREFIX);
}
/**
* Gets the trimmed test class name.
*/
public function getTrimmedTestClassName(TestMethod $test): string
{
return Str::after($test->className(), self::PREFIX);
}
/**
* Gets the test suite location.
*/

View File

@ -63,7 +63,7 @@ final class ServiceMessage
}
/**
* @param int $duration in milliseconds
* @param int $duration in milliseconds
*/
public static function testFinished(string $name, int $duration): self
{
@ -106,7 +106,7 @@ final class ServiceMessage
]);
}
public static function testIgnored(string $name, string $message, string $details = null): self
public static function testIgnored(string $name, string $message, ?string $details = null): self
{
return new self('testIgnored', [
'name' => $name,

View File

@ -6,6 +6,7 @@ namespace Pest\Logging\TeamCity;
use NunoMaduro\Collision\Adapters\Phpunit\Style;
use Pest\Exceptions\ShouldNotHappen;
use Pest\Logging\Converter;
use Pest\Logging\TeamCity\Subscriber\TestConsideredRiskySubscriber;
use Pest\Logging\TeamCity\Subscriber\TestErroredSubscriber;
use Pest\Logging\TeamCity\Subscriber\TestExecutionFinishedSubscriber;

View File

@ -6,6 +6,7 @@ namespace Pest\Mixins;
use BadMethodCallException;
use Closure;
use Countable;
use DateTimeInterface;
use Error;
use InvalidArgumentException;
@ -15,6 +16,7 @@ use Pest\Matchers\Any;
use Pest\Support\Arr;
use Pest\Support\Exporter;
use Pest\Support\NullClosure;
use Pest\Support\Str;
use Pest\TestSuite;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\Constraint\Constraint;
@ -129,7 +131,7 @@ final class Expectation
*
* @return self<TValue>
*/
public function toBeGreaterThan(int|float|DateTimeInterface $expected, string $message = ''): self
public function toBeGreaterThan(int|float|string|DateTimeInterface $expected, string $message = ''): self
{
Assert::assertGreaterThan($expected, $this->value, $message);
@ -141,7 +143,7 @@ final class Expectation
*
* @return self<TValue>
*/
public function toBeGreaterThanOrEqual(int|float|DateTimeInterface $expected, string $message = ''): self
public function toBeGreaterThanOrEqual(int|float|string|DateTimeInterface $expected, string $message = ''): self
{
Assert::assertGreaterThanOrEqual($expected, $this->value, $message);
@ -153,7 +155,7 @@ final class Expectation
*
* @return self<TValue>
*/
public function toBeLessThan(int|float|DateTimeInterface $expected, string $message = ''): self
public function toBeLessThan(int|float|string|DateTimeInterface $expected, string $message = ''): self
{
Assert::assertLessThan($expected, $this->value, $message);
@ -165,7 +167,7 @@ final class Expectation
*
* @return self<TValue>
*/
public function toBeLessThanOrEqual(int|float|DateTimeInterface $expected, string $message = ''): self
public function toBeLessThanOrEqual(int|float|string|DateTimeInterface $expected, string $message = ''): self
{
Assert::assertLessThanOrEqual($expected, $this->value, $message);
@ -194,6 +196,24 @@ final class Expectation
return $this;
}
/**
* Asserts that $needle equal an element of the value.
*
* @return self<TValue>
*/
public function toContainEqual(mixed ...$needles): self
{
if (! is_iterable($this->value)) {
InvalidExpectationValue::expected('iterable');
}
foreach ($needles as $needle) {
Assert::assertContainsEquals($needle, $this->value);
}
return $this;
}
/**
* Asserts that the value starts with $expected.
*
@ -264,7 +284,7 @@ final class Expectation
public function toHaveCount(int $count, string $message = ''): self
{
if (! is_countable($this->value) && ! is_iterable($this->value)) {
InvalidExpectationValue::expected('string');
InvalidExpectationValue::expected('countable|iterable');
}
Assert::assertCount($count, $this->value, $message);
@ -272,6 +292,23 @@ final class Expectation
return $this;
}
/**
* Asserts that the size of the value and $expected are the same.
*
* @param Countable|iterable<mixed> $expected
* @return self<TValue>
*/
public function toHaveSameSize(Countable|iterable $expected, string $message = ''): self
{
if (! is_countable($this->value) && ! is_iterable($this->value)) {
InvalidExpectationValue::expected('countable|iterable');
}
Assert::assertSameSize($expected, $this->value, $message);
return $this;
}
/**
* Asserts that the value contains the property $name.
*
@ -295,13 +332,13 @@ final class Expectation
/**
* Asserts that the value contains the provided properties $names.
*
* @param iterable<array-key, string> $names
* @param iterable<string, mixed>|iterable<int, string> $names
* @return self<TValue>
*/
public function toHaveProperties(iterable $names, string $message = ''): self
{
foreach ($names as $name => $value) {
is_int($name) ? $this->toHaveProperty($value, message: $message) : $this->toHaveProperty($name, $value, $message);
is_int($name) ? $this->toHaveProperty($value, message: $message) : $this->toHaveProperty($name, $value, $message); // @phpstan-ignore-line
}
return $this;
@ -502,6 +539,18 @@ final class Expectation
return $this;
}
/**
* Asserts that the value contains only digits.
*
* @return self<TValue>
*/
public function toBeDigits(string $message = ''): self
{
Assert::assertTrue(ctype_digit((string) $this->value), $message);
return $this;
}
/**
* Asserts that the value is of type object.
*
@ -813,6 +862,7 @@ final class Expectation
$string = match (true) {
is_string($this->value) => $this->value,
is_object($this->value) && method_exists($this->value, 'toSnapshot') => $this->value->toSnapshot(),
is_object($this->value) && method_exists($this->value, '__toString') => $this->value->__toString(),
is_object($this->value) && method_exists($this->value, 'toString') => $this->value->toString(),
$this->value instanceof \Illuminate\Testing\TestResponse => $this->value->getContent(), // @phpstan-ignore-line
@ -888,7 +938,7 @@ final class Expectation
* @param (Closure(Throwable): mixed)|string $exception
* @return self<TValue>
*/
public function toThrow(callable|string|Throwable $exception, string $exceptionMessage = null, string $message = ''): self
public function toThrow(callable|string|Throwable $exception, ?string $exceptionMessage = null, string $message = ''): self
{
$callback = NullClosure::create();
@ -896,7 +946,7 @@ final class Expectation
$callback = $exception;
$parameters = (new ReflectionFunction($exception))->getParameters();
if (1 !== count($parameters)) {
if (count($parameters) !== 1) {
throw new InvalidArgumentException('The given closure must have a single parameter type-hinted as the class string.');
}
@ -920,7 +970,7 @@ final class Expectation
}
if (! class_exists($exception)) {
if ($e instanceof Error && $e->getMessage() === "Class \"$exception\" not found") {
if ($e instanceof Error && "Class \"$exception\" not found" === $e->getMessage()) {
Assert::assertTrue(true);
throw $e;
@ -936,6 +986,7 @@ final class Expectation
}
Assert::assertInstanceOf($exception, $e, $message);
$callback($e);
return $this;
@ -961,4 +1012,169 @@ final class Expectation
return $this->exporter->shortenedExport($value);
}
/**
* Asserts that the value is uppercase.
*
* @return self<TValue>
*/
public function toBeUppercase(string $message = ''): self
{
Assert::assertTrue(ctype_upper((string) $this->value), $message);
return $this;
}
/**
* Asserts that the value is lowercase.
*
* @return self<TValue>
*/
public function toBeLowercase(string $message = ''): self
{
Assert::assertTrue(ctype_lower((string) $this->value), $message);
return $this;
}
/**
* Asserts that the value is alphanumeric.
*
* @return self<TValue>
*/
public function toBeAlphaNumeric(string $message = ''): self
{
Assert::assertTrue(ctype_alnum((string) $this->value), $message);
return $this;
}
/**
* Asserts that the value is alpha.
*
* @return self<TValue>
*/
public function toBeAlpha(string $message = ''): self
{
Assert::assertTrue(ctype_alpha((string) $this->value), $message);
return $this;
}
/**
* Asserts that the value is snake_case.
*
* @return self<TValue>
*/
public function toBeSnakeCase(string $message = ''): self
{
$value = (string) $this->value;
if ($message === '') {
$message = "Failed asserting that {$value} is snake_case.";
}
Assert::assertTrue((bool) preg_match('/^[\p{Ll}_]+$/u', $value), $message);
return $this;
}
/**
* Asserts that the value is kebab-case.
*
* @return self<TValue>
*/
public function toBeKebabCase(string $message = ''): self
{
$value = (string) $this->value;
if ($message === '') {
$message = "Failed asserting that {$value} is kebab-case.";
}
Assert::assertTrue((bool) preg_match('/^[\p{Ll}-]+$/u', $value), $message);
return $this;
}
/**
* Asserts that the value is camelCase.
*
* @return self<TValue>
*/
public function toBeCamelCase(string $message = ''): self
{
$value = (string) $this->value;
if ($message === '') {
$message = "Failed asserting that {$value} is camelCase.";
}
Assert::assertTrue((bool) preg_match('/^\p{Ll}[\p{Ll}\p{Lu}]+$/u', $value), $message);
return $this;
}
/**
* Asserts that the value is StudlyCase.
*
* @return self<TValue>
*/
public function toBeStudlyCase(string $message = ''): self
{
$value = (string) $this->value;
if ($message === '') {
$message = "Failed asserting that {$value} is StudlyCase.";
}
Assert::assertTrue((bool) preg_match('/^\p{Lu}+\p{Ll}[\p{Ll}\p{Lu}]+$/u', $value), $message);
return $this;
}
/**
* Asserts that the value is UUID.
*
* @return self<TValue>
*/
public function toBeUuid(string $message = ''): self
{
if (! is_string($this->value)) {
InvalidExpectationValue::expected('string');
}
Assert::assertTrue(Str::isUuid($this->value), $message);
return $this;
}
/**
* Asserts that the value is between 2 specified values
*
* @return self<TValue>
*/
public function toBeBetween(int|float|DateTimeInterface $lowestValue, int|float|DateTimeInterface $highestValue, string $message = ''): self
{
Assert::assertGreaterThanOrEqual($lowestValue, $this->value, $message);
Assert::assertLessThanOrEqual($highestValue, $this->value, $message);
return $this;
}
/**
* Asserts that the value is a url
*
* @return self<TValue>
*/
public function toBeUrl(string $message = ''): self
{
if ($message === '') {
$message = "Failed asserting that {$this->value} is a url.";
}
Assert::assertTrue(Str::isUrl((string) $this->value), $message);
return $this;
}
}

View File

@ -35,7 +35,7 @@ final class AfterEachCall
public function __construct(
private readonly TestSuite $testSuite,
private readonly string $filename,
Closure $closure = null
?Closure $closure = null
) {
$this->closure = $closure instanceof Closure ? $closure : NullClosure::create();

View File

@ -40,7 +40,7 @@ final class BeforeEachCall
public function __construct(
public readonly TestSuite $testSuite,
private readonly string $filename,
Closure $closure = null
?Closure $closure = null
) {
$this->closure = $closure instanceof Closure ? $closure : NullClosure::create();

View File

@ -16,7 +16,9 @@ use Pest\Support\Backtrace;
use Pest\Support\Exporter;
use Pest\Support\HigherOrderCallables;
use Pest\Support\NullClosure;
use Pest\Support\Str;
use Pest\TestSuite;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\TestCase;
/**
@ -44,8 +46,8 @@ final class TestCall
public function __construct(
private readonly TestSuite $testSuite,
private readonly string $filename,
string $description = null,
Closure $closure = null
?string $description = null,
?Closure $closure = null
) {
$this->testCaseMethod = new TestCaseMethodFactory($filename, $description, $closure);
@ -56,10 +58,18 @@ final class TestCall
$this->testSuite->beforeEach->get($this->filename)[0]($this);
}
/**
* Asserts that the test fails with the given message.
*/
public function fails(?string $message = null): self
{
return $this->throws(AssertionFailedError::class, $message);
}
/**
* Asserts that the test throws the given `$exceptionClass` when called.
*/
public function throws(string|int $exception, string $exceptionMessage = null, int $exceptionCode = null): self
public function throws(string|int $exception, ?string $exceptionMessage = null, ?int $exceptionCode = null): self
{
if (is_int($exception)) {
$exceptionCode = $exception;
@ -91,7 +101,7 @@ final class TestCall
*
* @param (callable(): bool)|bool $condition
*/
public function throwsIf(callable|bool $condition, string|int $exception, string $exceptionMessage = null, int $exceptionCode = null): self
public function throwsIf(callable|bool $condition, string|int $exception, ?string $exceptionMessage = null, ?int $exceptionCode = null): self
{
$condition = is_callable($condition)
? $condition
@ -104,6 +114,24 @@ final class TestCall
return $this;
}
/**
* Asserts that the test throws the given `$exceptionClass` when called if the given condition is false.
*
* @param (callable(): bool)|bool $condition
*/
public function throwsUnless(callable|bool $condition, string|int $exception, ?string $exceptionMessage = null, ?int $exceptionCode = null): self
{
$condition = is_callable($condition)
? $condition
: static fn (): bool => $condition;
if (! $condition()) {
return $this->throws($exception, $exceptionMessage, $exceptionCode);
}
return $this;
}
/**
* Runs the current test multiple times with
* each item of the given `iterable`.
@ -180,12 +208,37 @@ final class TestCall
return $this;
}
/**
* Skips the current test on the given PHP version.
*/
public function skipOnPhp(string $version): self
{
if (mb_strlen($version) < 2) {
throw new InvalidArgumentException('The version must start with [<] or [>].');
}
if (str_starts_with($version, '>=') || str_starts_with($version, '<=')) {
$operator = substr($version, 0, 2);
$version = substr($version, 2);
} elseif (str_starts_with($version, '>') || str_starts_with($version, '<')) {
$operator = $version[0];
$version = substr($version, 1);
// ensure starts with number:
} elseif (is_numeric($version[0])) {
$operator = '==';
} else {
throw new InvalidArgumentException('The version must start with [<, >, <=, >=] or a number.');
}
return $this->skip(version_compare(PHP_VERSION, $version, $operator), sprintf('This test is skipped on PHP [%s%s].', $operator, $version));
}
/**
* Skips the current test if the given test is running on Windows.
*/
public function skipOnWindows(): self
{
return $this->skipOn('Windows', 'This test is skipped on [Windows].');
return $this->skipOnOs('Windows', 'This test is skipped on [Windows].');
}
/**
@ -193,7 +246,7 @@ final class TestCall
*/
public function skipOnMac(): self
{
return $this->skipOn('Darwin', 'This test is skipped on [Mac].');
return $this->skipOnOs('Darwin', 'This test is skipped on [Mac].');
}
/**
@ -201,19 +254,43 @@ final class TestCall
*/
public function skipOnLinux(): self
{
return $this->skipOn('Linux', 'This test is skipped on [Linux].');
return $this->skipOnOs('Linux', 'This test is skipped on [Linux].');
}
/**
* Skips the current test if the given test is running on the given operating systems.
*/
private function skipOn(string $osFamily, string $message): self
private function skipOnOs(string $osFamily, string $message): self
{
return PHP_OS_FAMILY === $osFamily
return $osFamily === PHP_OS_FAMILY
? $this->skip($message)
: $this;
}
/**
* Skips the current test unless the given test is running on Windows.
*/
public function onlyOnWindows(): self
{
return $this->skipOnMac()->skipOnLinux();
}
/**
* Skips the current test unless the given test is running on Mac.
*/
public function onlyOnMac(): self
{
return $this->skipOnWindows()->skipOnLinux();
}
/**
* Skips the current test unless the given test is running on Linux.
*/
public function onlyOnLinux(): self
{
return $this->skipOnWindows()->skipOnMac();
}
/**
* Repeats the current test the given number of times.
*/
@ -332,7 +409,7 @@ final class TestCall
*
* @param array<int, mixed>|null $arguments
*/
private function addChain(string $file, int $line, string $name, array $arguments = null): self
private function addChain(string $file, int $line, string $name, ?array $arguments = null): self
{
$exporter = Exporter::default();
@ -361,7 +438,7 @@ final class TestCall
{
if (! is_null($this->describing)) {
$this->testCaseMethod->describing = $this->describing;
$this->testCaseMethod->description = sprintf('`%s` → %s', $this->describing, $this->testCaseMethod->description);
$this->testCaseMethod->description = Str::describe($this->describing, $this->testCaseMethod->description); // @phpstan-ignore-line
}
$this->testSuite->tests->set($this->testCaseMethod);

View File

@ -6,10 +6,10 @@ namespace Pest;
function version(): string
{
return '2.12.0';
return '2.33.3';
}
function testDirectory(string $file = ''): string
{
return TestSuite::getInstance()->testPath.'/'.$file;
return TestSuite::getInstance()->testPath.DIRECTORY_SEPARATOR.$file;
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Pest\Plugins\Actions;
use Pest\Contracts\Plugins;
use Pest\Plugin\Loader;
/**
* @internal
*/
final class CallsHandleOriginalArguments
{
/**
* Executes the Plugin action.
*
* Transform the input arguments by passing it to the relevant plugins.
*
* @param array<int, string> $argv
*/
public static function execute(array $argv): void
{
$plugins = Loader::getPlugins(Plugins\HandlesOriginalArguments::class);
/** @var Plugins\HandlesOriginalArguments $plugin */
foreach ($plugins as $plugin) {
$plugin->handleOriginalArguments($argv);
}
}
}

View File

@ -10,20 +10,20 @@ use Pest\Plugin\Loader;
/**
* @internal
*/
final class CallsShutdown
final class CallsTerminable
{
/**
* Executes the Plugin action.
*
* Provides an opportunity for any plugins to shutdown.
* Provides an opportunity for any plugins to terminate.
*/
public static function execute(): void
{
$plugins = Loader::getPlugins(Plugins\Shutdownable::class);
$plugins = Loader::getPlugins(Plugins\Terminable::class);
/** @var Plugins\Shutdownable $plugin */
/** @var Plugins\Terminable $plugin */
foreach ($plugins as $plugin) {
$plugin->shutdown();
$plugin->terminate();
}
}
}

View File

@ -6,6 +6,10 @@ namespace Pest\Plugins;
use Pest\Contracts\Plugins\HandlesArguments;
use Pest\Plugins\Concerns\HandleArguments;
use PHPUnit\TextUI\CliArguments\Builder as CliConfigurationBuilder;
use PHPUnit\TextUI\CliArguments\XmlConfigurationFileFinder;
use PHPUnit\TextUI\XmlConfiguration\DefaultConfiguration;
use PHPUnit\TextUI\XmlConfiguration\Loader;
/**
* @internal
@ -30,10 +34,21 @@ final class Cache implements HandlesArguments
*/
public function handleArguments(array $arguments): array
{
$arguments = $this->pushArgument(
sprintf('--cache-directory=%s', realpath(self::TEMPORARY_FOLDER)),
$arguments
);
if (! $this->hasArgument('--cache-directory', $arguments)) {
$cliConfiguration = (new CliConfigurationBuilder)->fromParameters([]);
$configurationFile = (new XmlConfigurationFileFinder)->find($cliConfiguration);
$xmlConfiguration = DefaultConfiguration::create();
if (is_string($configurationFile)) {
$xmlConfiguration = (new Loader)->load($configurationFile);
}
if (! $xmlConfiguration->phpunit()->hasCacheDirectory()) {
$arguments = $this->pushArgument('--cache-directory', $arguments);
$arguments = $this->pushArgument((string) realpath(self::TEMPORARY_FOLDER), $arguments);
}
}
if (! $this->hasArgument('--parallel', $arguments)) {
return $this->pushArgument('--cache-result', $arguments);

View File

@ -16,7 +16,17 @@ trait HandleArguments
*/
public function hasArgument(string $argument, array $arguments): bool
{
return in_array($argument, $arguments, true);
foreach ($arguments as $arg) {
if ($arg === $argument) {
return true;
}
if (str_starts_with($arg, "$argument=")) {
return true;
}
}
return false;
}
/**

View File

@ -128,9 +128,9 @@ final class Coverage implements AddsOutput, HandlesArguments
if ($exitCode === 1) {
$this->output->writeln(sprintf(
"\n <fg=white;bg=red;options=bold> FAIL </> Code coverage below expected:<fg=red;options=bold> %s %%</>. Minimum:<fg=white;options=bold> %s %%</>.",
number_format($coverage, 1),
number_format($this->coverageMin, 1)
"\n <fg=white;bg=red;options=bold> FAIL </> Code coverage below expected <fg=white;options=bold> %s %%</>, currently <fg=red;options=bold> %s %%</>.",
number_format($this->coverageMin, 1),
number_format($coverage, 1)
));
}

View File

@ -45,7 +45,7 @@ final class Environment implements HandlesArguments
/**
* Gets the environment name.
*/
public static function name(string $name = null): string
public static function name(?string $name = null): string
{
if (is_string($name)) {
self::$name = $name;

View File

@ -6,10 +6,11 @@ namespace Pest\Plugins;
use Pest\Contracts\Plugins\HandlesArguments;
use Pest\Support\View;
use function Pest\version;
use PHPUnit\TextUI\Help as PHPUnitHelp;
use Symfony\Component\Console\Output\OutputInterface;
use function Pest\version;
/**
* @internal
*/
@ -60,6 +61,10 @@ final class Help implements HandlesArguments
assert(is_string($argument));
if (trim($argument) === '--process-isolation') {
continue;
}
View::render('components.two-column-detail', [
'left' => $this->colorizeOptions($argument),
'right' => preg_replace(['/</', '/>/'], ['[', ']'], $description),
@ -92,10 +97,9 @@ final class Help implements HandlesArguments
*/
private function getContent(): array
{
$helpReflection = new \ReflectionClass(PHPUnitHelp::class);
$helpReflection = new PHPUnitHelp();
/** @var array<string, array<int, array{arg: string, desc: string}>> $content */
$content = $helpReflection->getConstant('HELP_TEXT');
$content = (fn (): array => $this->elements())->call($helpReflection);
$content['Configuration'] = [...[[
'arg' => '--init',

View File

@ -4,13 +4,13 @@ declare(strict_types=1);
namespace Pest\Plugins;
use Pest\Contracts\Plugins\Shutdownable;
use Pest\Contracts\Plugins\Terminable;
use Pest\PendingCalls\TestCall;
/**
* @internal
*/
final class Only implements Shutdownable
final class Only implements Terminable
{
/**
* The temporary folder.
@ -26,7 +26,7 @@ final class Only implements Shutdownable
/**
* {@inheritDoc}
*/
public function shutdown(): void
public function terminate(): void
{
$lockFile = self::TEMPORARY_FOLDER.DIRECTORY_SEPARATOR.'only.lock';
@ -40,6 +40,10 @@ final class Only implements Shutdownable
*/
public static function enable(TestCall $testCall): void
{
if (Environment::name() == Environment::CI) {
return;
}
$testCall->group('__pest_only');
$lockFile = self::TEMPORARY_FOLDER.DIRECTORY_SEPARATOR.'only.lock';

View File

@ -13,11 +13,12 @@ use Pest\Plugins\Parallel\Paratest\CleanConsoleOutput;
use Pest\Support\Arr;
use Pest\Support\Container;
use Pest\TestSuite;
use function Pest\version;
use Stringable;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use function Pest\version;
final class Parallel implements HandlesArguments
{
use HandleArguments;
@ -33,7 +34,7 @@ final class Parallel implements HandlesArguments
/**
* @var string[]
*/
private const UNSUPPORTED_ARGUMENTS = ['--todos', '--retry'];
private const UNSUPPORTED_ARGUMENTS = ['--todo', '--todos', '--retry'];
/**
* Whether the given command line arguments indicate that the test suite should be run in parallel.

View File

@ -4,6 +4,16 @@ declare(strict_types=1);
namespace Pest\Plugins\Parallel\Paratest;
use ParaTest\Options;
use Pest\Plugins\Parallel\Support\CompactPrinter;
use Pest\Support\StateGenerator;
use PHPUnit\TestRunner\TestResult\TestResult;
use PHPUnit\TextUI\Output\Printer;
use SebastianBergmann\Timer\Duration;
use SplFileInfo;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Output\OutputInterface;
use function assert;
use function fclose;
use function feof;
@ -12,16 +22,7 @@ use function fread;
use function fseek;
use function ftell;
use function fwrite;
use ParaTest\Options;
use Pest\Plugins\Parallel\Support\CompactPrinter;
use Pest\Support\StateGenerator;
use PHPUnit\TestRunner\TestResult\TestResult;
use PHPUnit\TextUI\Output\Printer;
use SebastianBergmann\Timer\Duration;
use SplFileInfo;
use function strlen;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @internal

View File

@ -4,15 +4,8 @@ declare(strict_types=1);
namespace Pest\Plugins\Parallel\Paratest;
use function array_merge;
use function array_merge_recursive;
use function array_shift;
use function assert;
use function count;
use const DIRECTORY_SEPARATOR;
use function dirname;
use function file_get_contents;
use function max;
use NunoMaduro\Collision\Adapters\Phpunit\Support\ResultReflection;
use ParaTest\Coverage\CoverageMerger;
use ParaTest\JUnit\LogMerger;
@ -30,11 +23,20 @@ use PHPUnit\TestRunner\TestResult\Facade as TestResultFacade;
use PHPUnit\TestRunner\TestResult\TestResult;
use PHPUnit\TextUI\Configuration\CodeCoverageFilterRegistry;
use PHPUnit\Util\ExcludeList;
use function realpath;
use SebastianBergmann\Timer\Timer;
use SplFileInfo;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\PhpExecutableFinder;
use function array_merge;
use function array_merge_recursive;
use function array_shift;
use function assert;
use function count;
use function dirname;
use function file_get_contents;
use function max;
use function realpath;
use function unlink;
use function unserialize;
use function usleep;
@ -252,11 +254,11 @@ final class WrapperRunner implements RunnerInterface
private function destroyWorker(int $token): void
{
// Mutation Testing tells us that the following `unset()` already destroys
// the `WrapperWorker`, which destroys the Symfony's `Process`, which
// automatically calls `Process::stop` within `Process::__destruct()`.
// But we prefer to have an explicit stops.
$this->workers[$token]->stop();
// We need to wait for ApplicationForWrapperWorker::end to end
while ($this->workers[$token]->isRunning()) {
usleep(self::CYCLE_SLEEP);
}
unset($this->workers[$token]);
}
@ -295,6 +297,7 @@ final class WrapperRunner implements RunnerInterface
array_merge_recursive($testResultSum->phpDeprecations(), $testResult->phpDeprecations()),
array_merge_recursive($testResultSum->phpNotices(), $testResult->phpNotices()),
array_merge_recursive($testResultSum->phpWarnings(), $testResult->phpWarnings()),
$testResultSum->numberOfIssuesIgnoredByBaseline() + $testResult->numberOfIssuesIgnoredByBaseline(),
);
}
@ -323,6 +326,8 @@ final class WrapperRunner implements RunnerInterface
$testResultSum->phpDeprecations(),
$testResultSum->phpNotices(),
$testResultSum->phpWarnings(),
$testResultSum->numberOfIssuesIgnoredByBaseline(),
);
$this->printer->printResults(
@ -358,6 +363,15 @@ final class WrapperRunner implements RunnerInterface
$this->codeCoverageFilterRegistry,
false,
);
if (! $coverageManager->isActive()) {
$this->output->writeln([
'',
' <fg=black;bg=yellow;options=bold> WARN </> No code coverage driver is available.</>',
'',
]);
return;
}
$coverageMerger = new CoverageMerger($coverageManager->codeCoverage());
foreach ($this->coverageFiles as $coverageFile) {
$coverageMerger->addCoverageFromFile($coverageFile);

View File

@ -16,8 +16,9 @@ use PHPUnit\TestRunner\TestResult\TestResult as PHPUnitTestResult;
use SebastianBergmann\Timer\Duration;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use function Termwind\render;
use Termwind\Terminal;
use function Termwind\render;
use function Termwind\terminal;
/**

View File

@ -6,6 +6,7 @@ namespace Pest\Plugins;
use Pest\Contracts\Plugins\HandlesArguments;
use Pest\Support\View;
use function Pest\version;
/**

View File

@ -10,9 +10,10 @@ use Pest\Exceptions\DatasetAlreadyExists;
use Pest\Exceptions\DatasetDoesNotExist;
use Pest\Exceptions\ShouldNotHappen;
use Pest\Support\Exporter;
use function sprintf;
use Traversable;
use function sprintf;
/**
* @internal
*/

View File

@ -25,17 +25,17 @@ final class TestRepository
private array $testCases = [];
/**
* @var array<string, array{0: array<int, string>, 1: array<int, string>, 2: array<int, string|Closure>}>
* @var array<string, array{0: array<int, string>, 1: array<int, string>, 2: array<int, array<int, string|Closure>>}>
*/
private array $uses = [];
/**
* @var array<int, TestCaseFilter>
* @var array<int, TestCaseFilter>
*/
private array $testCaseFilters = [];
/**
* @var array<int, TestCaseMethodFilter>
* @var array<int, TestCaseMethodFilter>
*/
private array $testCaseMethodFilters = [];
@ -77,12 +77,17 @@ final class TestRepository
throw new TestCaseClassOrTraitNotFound($classOrTrait);
}
$hooks = array_map(fn (Closure $hook): array => [$hook], $hooks);
foreach ($paths as $path) {
if (array_key_exists($path, $this->uses)) {
$this->uses[$path] = [
[...$this->uses[$path][0], ...$classOrTraits],
[...$this->uses[$path][1], ...$groups],
$this->uses[$path][2] + $hooks,
array_map(
fn (int $index): array => [...$this->uses[$path][2][$index] ?? [], ...($hooks[$index] ?? [])],
range(0, 3),
),
];
} else {
$this->uses[$path] = [$classOrTraits, $groups, $hooks];
@ -189,10 +194,11 @@ final class TestRepository
$method->groups = [...$groups, ...$method->groups];
}
$testCase->factoryProxies->add($testCase->filename, 0, '__addBeforeAll', [$hooks[0] ?? null]);
$testCase->factoryProxies->add($testCase->filename, 0, '__addBeforeEach', [$hooks[1] ?? null]);
$testCase->factoryProxies->add($testCase->filename, 0, '__addAfterEach', [$hooks[2] ?? null]);
$testCase->factoryProxies->add($testCase->filename, 0, '__addAfterAll', [$hooks[3] ?? null]);
foreach (['__addBeforeAll', '__addBeforeEach', '__addAfterEach', '__addAfterAll'] as $index => $name) {
foreach ($hooks[$index] ?? [null] as $hook) {
$testCase->factoryProxies->add($testCase->filename, 0, $name, [$hook]);
}
}
}
}

View File

@ -54,8 +54,8 @@ final class Result
$returnCode = self::FAILURE_EXIT;
}
$warnings = $result->numberOfTestsWithTestTriggeredPhpunitWarningEvents() +
+count($result->warnings())
$warnings = $result->numberOfTestsWithTestTriggeredPhpunitWarningEvents()
+ count($result->warnings())
+ count($result->phpWarnings());
if ($configuration->failOnWarning() && $warnings > 0) {

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Pest\Subscribers;
use Pest\Logging\TeamCity\Converter;
use Pest\Logging\Converter;
use Pest\Logging\TeamCity\TeamCityLogger;
use Pest\TestSuite;
use PHPUnit\Event\TestRunner\Configured;

View File

@ -115,7 +115,11 @@ final class Backtrace
continue;
}
if (str_contains($trace['file'], 'pest'.DIRECTORY_SEPARATOR.'src')) {
if (($GLOBALS['__PEST_INTERNAL_TEST_SUITE'] ?? false) && str_contains($trace['file'], 'pest'.DIRECTORY_SEPARATOR.'src')) {
continue;
}
if (str_contains($trace['file'], DIRECTORY_SEPARATOR.'pestphp'.DIRECTORY_SEPARATOR.'pest'.DIRECTORY_SEPARATOR.'src')) {
continue;
}

View File

@ -10,6 +10,7 @@ use SebastianBergmann\CodeCoverage\Node\Directory;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\Environment\Runtime;
use Symfony\Component\Console\Output\OutputInterface;
use function Termwind\render;
use function Termwind\renderUsing;
use function Termwind\terminal;

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Pest\Support;
use function Pest\testDirectory;
/**
* @internal
*/
@ -25,6 +27,10 @@ final class DatasetInfo
public static function scope(string $file): string
{
if (Str::endsWith($file, testDirectory('Pest.php'))) {
return dirname($file);
}
if (self::isInsideADatasetsDirectory($file)) {
return dirname($file, 2);
}

View File

@ -41,7 +41,7 @@ final class Exporter
*
* @param array<int|string, mixed> $data
*/
public function shortenedRecursiveExport(array &$data, Context $context = null): string
public function shortenedRecursiveExport(array &$data, ?Context $context = null): string
{
$result = [];
$array = $data;

View File

@ -58,7 +58,7 @@ final class HigherOrderMessageCollection
/**
* Count the number of messages with the given name.
*
* @param string $name A higher order message name (usually a method name)
* @param string $name A higher order message name (usually a method name)
*/
public function count(string $name): int
{

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Pest\Support;
use Closure;
use InvalidArgumentException;
use Pest\Exceptions\ShouldNotHappen;
use Pest\TestSuite;
use ReflectionClass;
@ -66,9 +67,17 @@ final class Reflection
{
$test = TestSuite::getInstance()->test;
return $test instanceof \PHPUnit\Framework\TestCase
? Closure::fromCallable($callable)->bindTo($test)(...$test->providedData())
: self::bindCallable($callable);
if (! $test instanceof \PHPUnit\Framework\TestCase) {
return self::bindCallable($callable);
}
foreach ($test->providedData() as $value) {
if ($value instanceof Closure) {
throw new InvalidArgumentException('Bound datasets are not supported while doing high order testing.');
}
}
return Closure::fromCallable($callable)->bindTo($test)(...$test->providedData());
}
/**
@ -190,7 +199,7 @@ final class Reflection
}
$arguments[$parameter->getName()] = implode('|', array_map(
static fn (ReflectionNamedType $type): string => $type->getName(),
static fn (ReflectionNamedType $type): string => $type->getName(), // @phpstan-ignore-line
($types instanceof ReflectionNamedType)
? [$types] // NOTE: normalize as list of to handle unions
: $types->getTypes(),

View File

@ -24,7 +24,7 @@ final class Str
* Create a (unsecure & non-cryptographically safe) random alpha-numeric
* string value.
*
* @param int $length the length of the resulting randomized string
* @param int $length the length of the resulting randomized string
*
* @see https://github.com/laravel/framework/blob/4.2/src/Illuminate/Support/Str.php#L240-L242
*/
@ -51,7 +51,7 @@ final class Str
return true;
}
return substr($target, -$length) === $search;
return $search === substr($target, -$length);
}
/**
@ -92,4 +92,28 @@ final class Str
{
return $search === '' ? $subject : array_reverse(explode($search, $subject, 2))[0];
}
/**
* Determine if a given value is a valid UUID.
*/
public static function isUuid(string $value): bool
{
return preg_match('/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iD', $value) > 0;
}
/**
* Creates a describe block as `$describeDescription` → `$testDescription` format.
*/
public static function describe(string $describeDescription, string $testDescription): string
{
return sprintf('`%s` → %s', $describeDescription, $testDescription);
}
/**
* Determine if a given value is a valid URL.
*/
public static function isUrl(string $value): bool
{
return (bool) filter_var($value, FILTER_VALIDATE_URL);
}
}

View File

@ -5,9 +5,10 @@ declare(strict_types=1);
namespace Pest\Support;
use Symfony\Component\Console\Output\OutputInterface;
use Termwind\Termwind;
use function Termwind\render;
use function Termwind\renderUsing;
use Termwind\Termwind;
/**
* @internal

View File

@ -87,8 +87,8 @@ final class TestSuite
* Returns the current instance of the test suite.
*/
public static function getInstance(
string $rootPath = null,
string $testPath = null,
?string $rootPath = null,
?string $testPath = null,
): TestSuite {
if (is_string($rootPath) && is_string($testPath)) {
self::$instance = new TestSuite($rootPath, $testPath);

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.3/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
@ -12,11 +12,6 @@
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory suffix=".php">./app</directory>
</include>
</coverage>
<php>
<env name="APP_ENV" value="testing"/>
<env name="BCRYPT_ROUNDS" value="4"/>
@ -28,4 +23,9 @@
<env name="SESSION_DRIVER" value="array"/>
<env name="TELESCOPE_ENABLED" value="false"/>
</php>
<source>
<include>
<directory suffix=".php">./app</directory>
</include>
</source>
</phpunit>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.3/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
@ -9,10 +9,10 @@
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<coverage>
<source>
<include>
<directory suffix=".php">./app</directory>
<directory suffix=".php">./src</directory>
</include>
</coverage>
</source>
</phpunit>

View File

@ -0,0 +1 @@
{"version":"pest_2.32.2","defects":[],"times":{"P\\Tests\\Playground::__pest_evaluable_basic":0.005}}

View File

@ -0,0 +1 @@
<input type="hidden" name="_token" value="1" />

View File

@ -0,0 +1,3 @@
{
"key": " <div class=\"container\">\n <div class=\"row\">\n <div class=\"col-md-12\">\n <h1>Snapshot<\/h1>\n <\/div>\n <\/div>\n <\/div>"
}

View File

@ -1,5 +1,5 @@
Pest Testing Framework 2.12.0.
Pest Testing Framework 2.33.3.
USAGE: pest <file> [options]
@ -14,6 +14,9 @@
--cache-directory [dir] ............................ Specify cache directory
--generate-configuration Generate configuration file with suggested settings
--migrate-configuration ....... Migrate configuration file to current format
--generate-baseline [file] .................... Generate baseline for issues
--use-baseline [file] ........................ Use baseline to ignore issues
--ignore-baseline ..................... Do not use baseline to ignore issues
SELECTION OPTIONS:
--bail ........................... Stop execution upon first not-passed test
@ -35,7 +38,6 @@
EXECUTION OPTIONS:
--parallel ........................................... Run tests in parallel
--update-snapshots Update snapshots for tests using the "toMatchSnapshot" expectation
--process-isolation ................ Run each test in a separate PHP process
--globals-backup ................. Backup and restore $GLOBALS for each test
--static-backup ......... Backup and restore static properties for each test
--strict-coverage ................... Be strict about code coverage metadata
@ -81,6 +83,7 @@
--reverse-list .............................. Print defects in reverse order
--teamcity . Replace default progress and result output with TeamCity format
--testdox ................ Replace default result output with TestDox format
--debug Replace default progress and result output with debugging information
--compact ................ Replace default result output with Compact format
LOGGING OPTIONS:
@ -89,7 +92,7 @@
--testdox-html [file] .. Write test results in TestDox format (HTML) to file
--testdox-text [file] Write test results in TestDox format (plain text) to file
--log-events-text [file] ............... Stream events as plain text to file
--log-events-verbose-text [file] Stream events as plain text (with telemetry information) to file
--log-events-verbose-text [file] Stream events as plain text with extended information to file
--no-logging ....... Ignore logging configured in the XML configuration file
CODE COVERAGE OPTIONS:

View File

@ -1,3 +1,3 @@
Pest Testing Framework 2.12.0.
Pest Testing Framework 2.33.3.

View File

@ -154,6 +154,10 @@
✓ it can correctly resolve a bound dataset that returns an array with (Closure)
✓ it can correctly resolve a bound dataset that returns an array but wants to be spread with (Closure)
↓ forbids to define tests in Datasets dirs and Datasets.php files
✓ it may be used with high order with dataset "formal"
✓ it may be used with high order with dataset "informal"
✓ it may be used with high order even when bound with dataset "formal"
✓ it may be used with high order even when bound with dataset "informal"
PASS Tests\Features\Depends
✓ first
@ -187,6 +191,10 @@
✓ with with (1)
✓ with on hook → value with (2)
✓ with on describe → value with (3)
✓ depends on describe → foo
✓ depends on describe → bar
✓ depends on describe using with → foo with (3)
✓ depends on describe using with → bar with (3)
PASS Tests\Features\DescriptionLess
✓ get 'foo'
@ -211,6 +219,14 @@
✓ it can just define the code if given condition is true
✓ it can just define the message if given condition is 1
✓ it can just define the code if given condition is 1
✓ it not catch exceptions if given condition is true
✓ it catch exceptions if given condition is false
✓ it catch exceptions and messages if given condition is false
✓ it catch exceptions, messages and code if given condition is false
✓ it can just define the message if given condition is false
✓ it can just define the code if given condition is false
✓ it can just define the message if given condition is 0
✓ it can just define the code if given condition is 0
PASS Tests\Features\Expect\HigherOrder\methods
✓ it can access methods
@ -297,23 +313,49 @@
PASS Tests\Features\Expect\sequence
✓ an exception is thrown if the the type is not iterable
✓ an exception is thrown if there are no expectations
✓ allows for sequences of checks to be run on iterable data
✓ loops back to the start if it runs out of sequence items
✓ fails if the number of iterable items is greater than the number of expectations
✓ fails if the number of iterable items is less than the number of expectations
✓ it works with associative arrays
✓ it can be passed non-callable values
✓ it can be passed a mixture of value types
✓ it works with traversables
PASS Tests\Features\Expect\toBe
✓ strict comparisons
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeAlpha
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeAlphaNumeric
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeArray
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeBetween
✓ passes with int
✓ passes with float
✓ passes with float and int
✓ passes with DateTime
✓ failure with int
✓ failure with float
✓ failure with float and int
✓ failure with DateTime
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeBool
@ -326,6 +368,18 @@
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeCamelCase
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeDigits
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeDirectory
@ -376,6 +430,7 @@
PASS Tests\Features\Expect\toBeGreaterThan
✓ passes
✓ passes with DateTime and DateTimeImmutable
✓ passes with strings
✓ failures
✓ failures with custom message
✓ not failures
@ -383,6 +438,7 @@
PASS Tests\Features\Expect\toBeGreaterThanOrEqual
✓ passes
✓ passes with DateTime and DateTimeImmutable
✓ passes with strings
✓ failures
✓ failures with custom message
✓ not failures
@ -411,6 +467,10 @@
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeIntBackedEnum
✓ enum is backed by int
✓ enum is not backed by int
PASS Tests\Features\Expect\toBeInvokable
✓ class is invokable
✓ opposite class is invokable
@ -429,11 +489,18 @@
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeKebabCase
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeLessThan
✓ passes
✓ passes with DateTime and DateTimeImmutable
✓ passes with strings
✓ failures
✓ failures with custom message
✓ not failures
@ -441,6 +508,13 @@
PASS Tests\Features\Expect\toBeLessThanOrEqual
✓ passes
✓ passes with DateTime and DateTimeImmutable
✓ passes with strings
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeLowercase
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
@ -491,12 +565,28 @@
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeSnakeCase
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeString
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeStringBackedEnum
✓ enum is backed by string
✓ enum is not backed by string
PASS Tests\Features\Expect\toBeStudlyCase
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeTrue
@ -518,6 +608,26 @@
✓ passes as not truthy with ('0')
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeUppercase
✓ pass
✓ failures
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toBeUrl
✓ pass
✓ failures
✓ failures with custom message
✓ failures with default message
✓ not failures
PASS Tests\Features\Expect\toBeUuid
✓ failures with wrong type
✓ pass
✓ failures
✓ failures with message
✓ not failures
PASS Tests\Features\Expect\toBeWritableDirectory
@ -543,6 +653,16 @@
✓ failures with multiple needles (some failing)
✓ not failures
✓ not failures with multiple needles (all failing)
✓ not failures with multiple needles (some failing)
PASS Tests\Features\Expect\toContainEqual
✓ passes arrays
✓ passes arrays with multiple needles
✓ failures
✓ failures with multiple needles (all failing)
✓ failures with multiple needles (some failing)
✓ not failures
✓ not failures with multiple needles (all failing)
✓ not failures with multiple needles (some failing)
PASS Tests\Features\Expect\toContainOnlyInstancesOf
@ -574,8 +694,34 @@
✓ failures with custom message
✓ not failures
PASS Tests\Features\Expect\toHaveAttribute
✓ class has attribute
✓ opposite class has attribute
✓ class not has attribute
PASS Tests\Features\Expect\toHaveCamelCaseKeys
✓ pass
✓ failures
✓ failures with message
✓ not failures
PASS Tests\Features\Expect\toHaveConstructor
✓ class has constructor
✓ class has no constructor
PASS Tests\Features\Expect\toHaveCount
✓ pass
✓ failures with invalid type
✓ failures
✓ failures with message
✓ not failures
PASS Tests\Features\Expect\toHaveDestructor
✓ class has destructor
✓ class has no destructor
PASS Tests\Features\Expect\toHaveKebabCaseKeys
✓ pass
✓ failures
✓ failures with message
✓ not failures
@ -626,10 +772,12 @@
✓ it fails with message
PASS Tests\Features\Expect\toHaveMethod
pass
failures
failures with message
not failures
class has method
opposite class has method
class has method via a parent class
class has method via a trait
✓ failure when the class has no method
✓ class has no method
PASS Tests\Features\Expect\toHaveMethods
✓ pass
@ -654,6 +802,25 @@
✓ failures
✓ failures with message
✓ failures with message and Any matcher
✓ not failures
PASS Tests\Features\Expect\toHaveSameSize
✓ failures with wrong type
✓ pass
✓ failures
✓ failures with message
✓ not failures
PASS Tests\Features\Expect\toHaveSnakeCaseKeys
✓ pass
✓ failures
✓ failures with message
✓ not failures
PASS Tests\Features\Expect\toHaveStudlyCaseKeys
✓ pass
✓ failures
✓ failures with message
✓ not failures
PASS Tests\Features\Expect\toHaveSuffix
@ -689,12 +856,14 @@
PASS Tests\Features\Expect\toMatchSnapshot
✓ pass
✓ pass using pipes
✓ pass with __toString
✓ pass with toString
✓ pass with dataset with ('my-datas-set-value')
✓ within describe → pass with dataset with ('my-datas-set-value')
✓ pass with toArray
✓ pass with array
✓ pass with toSnapshot
✓ failures
✓ failures with custom message
✓ not failures
@ -756,6 +925,14 @@
✓ it skips with falsy closure condition
✓ it can be used in higher order tests
PASS Tests\Features\Fail
✓ it may fail
✓ it may fail with the given message
PASS Tests\Features\Fails
✓ it may fail
✓ it may fail with the given message
WARN Tests\Features\Helpers
✓ it can set/get properties on $this
! it gets null if property do not exist → Undefined property Tests\Features\Helpers::$wqdwqdqw
@ -927,6 +1104,9 @@
✓ uses dataset with (1)
✓ uses dataset with (2)
✓ the right dataset is taken
✓ it can see datasets defined in Pest.php file with ('A')
✓ it can see datasets defined in Pest.php file with ('B')
✓ Pest.php dataset is taken
WARN Tests\Features\Skip
✓ it do not skips
@ -940,6 +1120,11 @@
- it can use something in the test case as a condition → This test was skipped
- it can user higher order callables and skip
WARN Tests\Features\SkipOnPhp
✓ it can run on php version
✓ it can run on specific php version
- it can skip on php versions depending on constraint → This test is skipped on PHP [>=7.4.0].
PASS Tests\Features\Test
✓ a test
✓ higher order message test
@ -976,20 +1161,22 @@
PASS Tests\Helpers\TestInHelpers
✓ it executes tests in the Helpers directory
PASS Tests\Hooks\AfterAllTest
✓ global afterAll execution order
✓ it only gets called once per file
PASS Tests\Hooks\AfterEachTest
✓ global afterEach execution order
PASS Tests\Hooks\BeforeAllTest
✓ global beforeAll execution order
✓ it only gets called once per file
PASS Tests\Hooks\BeforeEachTest
✓ global beforeEach execution order
PASS Tests\Overrides\VersionsTest
✓ versions with dataset "Runner/Filter/NameFilterIterator.php"
✓ versions with dataset "Runner/ResultCache/DefaultResultCache.php"
✓ versions with dataset "Runner/TestSuiteLoader.php"
✓ versions with dataset "TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php"
✓ versions with dataset "TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php"
✓ versions with dataset "TextUI/TestSuiteFilterProcessor.php"
✓ versions with dataset "Event/Value/ThrowableBuilder.php"
✓ versions with dataset "Logging/JUnit/JunitXmlLogger.php"
PASS Tests\PHPUnit\CustomAffixes\InvalidTestName
✓ it runs file names like @#$%^&()-_=+.php
@ -1015,9 +1202,15 @@
PASS Tests\PHPUnit\CustomAffixes\snakecasespec
✓ it runs file names like snake_case_spec.php
PASS Tests\CustomTestCase\ChildTest
✓ override method
PASS Tests\CustomTestCase\ExecutedTest
✓ that gets executed
PASS Tests\CustomTestCase\ParentTest
✓ override method
PASS Tests\PHPUnit\CustomTestCase\UsesPerDirectory
✓ closure was bound to CustomTestCase
@ -1052,6 +1245,7 @@
✓ it show the correct description for mixed named and not-named datasets
✓ it shows the correct description for long texts with newlines
✓ it shows the correct description for arrays with many elements
✓ it shows the correct description of datasets with html
PASS Tests\Unit\Expectations\OppositeExpectation
✓ it throw expectation failed exception with string argument
@ -1060,6 +1254,14 @@
PASS Tests\Unit\Overrides\ThrowableBuilder
✓ collision editor can be added to the stack trace
PASS Tests\Unit\Plugins\Concerns\HandleArguments
✓ method hasArgument with ('--long-argument', true)
✓ method hasArgument with ('-a', true)
✓ method hasArgument with ('--with-equal-sign', true)
✓ method hasArgument with ('someValue', true)
✓ method hasArgument with ('--a', false)
✓ method hasArgument with ('--undefined-argument', false)
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
@ -1187,6 +1389,10 @@
PASS Tests\Visual\Help
✓ visual snapshot of help command output
PASS Tests\Visual\JUnit
✓ junit output
✓ junit with parallel
PASS Tests\Visual\Parallel
✓ parallel
✓ a parallel test can extend another test with same name
@ -1204,10 +1410,12 @@
- visual snapshot of team city with ('SuccessOnly.php')
PASS Tests\Visual\Todo
✓ todos
✓ todos in parallel
✓ todo
✓ todo in parallel
WARN Tests\Visual\Version
- visual snapshot of help command output
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 13 todos, 19 skipped, 860 passed (1975 assertions)
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 13 todos, 20 skipped, 1009 passed (2395 assertions)

View File

@ -19,7 +19,13 @@
↓ something todo later chained
↓ something todo later chained and with function body
PASS Tests\CustomTestCase\ChildTest
✓ override method
PASS Tests\CustomTestCase\ExecutedTest
✓ that gets executed
Tests: 13 todos, 1 passed (1 assertions)
PASS Tests\CustomTestCase\ParentTest
✓ override method
Tests: 13 todos, 3 passed (3 assertions)

View File

@ -0,0 +1,31 @@
TODO Tests\Features\BeforeEachProxiesToTestCallWithTodo - 4 todos
↓ is marked as todo 1
↓ is marked as todo 2
↓ is marked as todo 3
↓ shouldBeMarkedAsTodo
TODO Tests\Features\DatasetsTests - 1 todo
↓ forbids to define tests in Datasets dirs and Datasets.php files
TODO Tests\Features\Describe - 5 todos
↓ todo
↓ todo on hook → should not fail
↓ todo on hook → should run
↓ todo on describe → should not fail
↓ todo on describe → should run
TODO Tests\Features\Todo - 3 todos
↓ something todo later
↓ something todo later chained
↓ something todo later chained and with function body
PASS Tests\CustomTestCase\ChildTest
✓ override method
PASS Tests\CustomTestCase\ExecutedTest
✓ that gets executed
PASS Tests\CustomTestCase\ParentTest
✓ override method
Tests: 13 todos, 3 passed (3 assertions)

View File

@ -2,12 +2,12 @@
use Pest\Expectation;
test('globals')
->expect(['dd', 'dump', 'ray'])
arch('globals')
->expect(['dd', 'dump', 'ray', 'die', 'var_dump', 'sleep'])
->not->toBeUsed()
->ignoring(Expectation::class);
test('dependencies')
arch('dependencies')
->expect('Pest')
->toOnlyUse([
'dd',
@ -24,7 +24,7 @@ test('dependencies')
'Symfony\Component\Process',
])->ignoring(['Composer', 'PHPUnit', 'SebastianBergmann']);
test('contracts')
arch('contracts')
->expect('Pest\Contracts')
->toOnlyUse([
'NunoMaduro\Collision\Contracts',

View File

@ -361,3 +361,23 @@ it('can correctly resolve a bound dataset that returns an array but wants to be
]);
todo('forbids to define tests in Datasets dirs and Datasets.php files');
dataset('greeting-string', [
'formal' => 'Evening',
'informal' => 'yo',
]);
it('may be used with high order')
->with('greeting-string')
->expect(fn (string $greeting) => $greeting)
->throwsNoExceptions();
dataset('greeting-bound', [
'formal' => fn () => 'Evening',
'informal' => fn () => 'yo',
]);
it('may be used with high order even when bound')
->with('greeting-bound')
->expect(fn (string $greeting) => $greeting)
->throws(InvalidArgumentException::class);

View File

@ -76,3 +76,23 @@ describe('with on describe', function () {
expect($foo)->toBe(3);
});
})->with([3]);
describe('depends on describe', function () {
test('foo', function () {
expect('foo')->toBe('foo');
});
test('bar', function () {
expect('bar')->toBe('bar');
})->depends('foo');
});
describe('depends on describe using with', function () {
test('foo', function ($foo) {
expect($foo)->toBe(3);
});
test('bar', function ($foo) {
expect($foo + $foo)->toBe(6);
})->depends('foo');
})->with([3]);

View File

@ -59,3 +59,37 @@ it('can just define the message if given condition is 1', function () {
it('can just define the code if given condition is 1', function () {
throw new Exception('Something bad happened', 1);
})->throwsIf(1, 1);
it('not catch exceptions if given condition is true', function () {
$this->assertTrue(true);
})->throwsUnless(true, Exception::class);
it('catch exceptions if given condition is false', function () {
throw new Exception('Something bad happened');
})->throwsUnless(function () {
return false;
}, Exception::class);
it('catch exceptions and messages if given condition is false', function () {
throw new Exception('Something bad happened');
})->throwsUnless(false, Exception::class, 'Something bad happened');
it('catch exceptions, messages and code if given condition is false', function () {
throw new Exception('Something bad happened', 1);
})->throwsUnless(false, Exception::class, 'Something bad happened', 1);
it('can just define the message if given condition is false', function () {
throw new Exception('Something bad happened');
})->throwsUnless(false, 'Something bad happened');
it('can just define the code if given condition is false', function () {
throw new Exception('Something bad happened', 1);
})->throwsUnless(false, 1);
it('can just define the message if given condition is 0', function () {
throw new Exception('Something bad happened');
})->throwsUnless(0, 'Something bad happened');
it('can just define the code if given condition is 0', function () {
throw new Exception('Something bad happened', 1);
})->throwsUnless(0, 1);

View File

@ -1,11 +1,13 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('an exception is thrown if the the type is not iterable', function () {
expect('Foobar')->each->sequence();
})->throws(BadMethodCallException::class, 'Expectation value is not iterable.');
test('an exception is thrown if there are no expectations', function () {
expect(['Foobar'])->sequence();
})->throws(InvalidArgumentException::class, 'No sequence expectations defined.');
test('allows for sequences of checks to be run on iterable data', function () {
expect([1, 2, 3])
->sequence(
@ -40,7 +42,7 @@ test('loops back to the start if it runs out of sequence items', function () {
expect(static::getCount())->toBe(16);
});
test('fails if the number of iterable items is greater than the number of expectations', function () {
test('fails if the number of iterable items is less than the number of expectations', function () {
expect([1, 2])
->sequence(
function ($expectation) {
@ -53,7 +55,7 @@ test('fails if the number of iterable items is greater than the number of expect
$expectation->toBeInt()->toEqual(3);
},
);
})->throws(ExpectationFailedException::class);
})->throws(OutOfRangeException::class, 'Sequence expectations are more than the iterable items.');
test('it works with associative arrays', function () {
expect(['foo' => 'bar', 'baz' => 'boom'])
@ -86,3 +88,26 @@ test('it can be passed a mixture of value types', function () {
expect(static::getCount())->toBe(4);
});
test('it works with traversables', function () {
$generator = (function () {
yield 'one' => (fn () => yield from [1, 2, 3])();
yield 'two' => (fn () => yield from [4, 5, 6])();
yield 'three' => (fn () => yield from [7, 8, 9])();
})();
expect($generator)->sequence(
fn ($value, $key) => $key->toBe('one')
->and($value)
->toBeInstanceOf(Generator::class)
->sequence(1, 2, 3),
fn ($value, $key) => $key->toBe('two')
->and($value)
->toBeInstanceOf(Generator::class)
->sequence(4, 5, 6),
fn ($value, $key) => $key->toBe('three')
->and($value)
->toBeInstanceOf(Generator::class)
->sequence(7, 8, 9),
);
});

View File

@ -0,0 +1,20 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('pass', function () {
expect('abc')->toBeAlpha();
expect('123')->not->toBeAlpha();
});
test('failures', function () {
expect('123')->toBeAlpha();
})->throws(ExpectationFailedException::class);
test('failures with custom message', function () {
expect('123')->toBeAlpha('oh no!');
})->throws(ExpectationFailedException::class, 'oh no!');
test('not failures', function () {
expect('abc')->not->toBeAlpha();
})->throws(ExpectationFailedException::class);

View File

@ -0,0 +1,20 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('pass', function () {
expect('abc123')->toBeAlphaNumeric();
expect('-')->not->toBeAlphaNumeric();
});
test('failures', function () {
expect('-')->toBeAlphaNumeric();
})->throws(ExpectationFailedException::class);
test('failures with custom message', function () {
expect('-')->toBeAlphaNumeric('oh no!');
})->throws(ExpectationFailedException::class, 'oh no!');
test('not failures', function () {
expect('abc123')->not->toBeAlphaNumeric();
})->throws(ExpectationFailedException::class);

View File

@ -0,0 +1,43 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('passes with int', function () {
expect(2)->toBeBetween(1, 3);
});
test('passes with float', function () {
expect(1.5)->toBeBetween(1.25, 1.75);
});
test('passes with float and int', function () {
expect(1.5)->toBeBetween(1, 2);
});
test('passes with DateTime', function () {
expect(new DateTime('2023-09-22'))->toBeBetween(new DateTime('2023-09-21'), new DateTime('2023-09-23'));
});
test('failure with int', function () {
expect(4)->toBeBetween(1, 3);
})->throws(ExpectationFailedException::class);
test('failure with float', function () {
expect(2)->toBeBetween(1.5, 1.75);
})->throws(ExpectationFailedException::class);
test('failure with float and int', function () {
expect(2.1)->toBeBetween(1, 2);
})->throws(ExpectationFailedException::class);
test('failure with DateTime', function () {
expect(new DateTime('2023-09-20'))->toBeBetween(new DateTime('2023-09-21'), new DateTime('2023-09-23'));
})->throws(ExpectationFailedException::class);
test('failures with custom message', function () {
expect(4)->toBeBetween(1, 3, 'oh no!');
})->throws(ExpectationFailedException::class, 'oh no!');
test('not failures', function () {
expect(2)->not->toBeBetween(1, 3);
})->throws(ExpectationFailedException::class);

View File

@ -0,0 +1,23 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('pass', function () {
expect('abc')->toBeCamelCase();
expect('abcDef')->toBeCamelCase();
expect('abc-def')->not->toBeCamelCase();
expect('abc-def')->not->toBeCamelCase();
expect('AbcDef')->not->toBeCamelCase();
});
test('failures', function () {
expect('Abc')->toBeCamelCase();
})->throws(ExpectationFailedException::class);
test('failures with custom message', function () {
expect('Abc')->toBeCamelCase('oh no!');
})->throws(ExpectationFailedException::class, 'oh no!');
test('not failures', function () {
expect('abcDef')->not->toBeCamelCase();
})->throws(ExpectationFailedException::class);

View File

@ -0,0 +1,20 @@
<?php
use PHPUnit\Framework\ExpectationFailedException;
test('pass', function () {
expect('123')->toBeDigits();
expect('123.14')->not->toBeDigits();
});
test('failures', function () {
expect('123.14')->toBeDigits();
})->throws(ExpectationFailedException::class);
test('failures with custom message', function () {
expect('123.14')->toBeDigits('oh no!');
})->throws(ExpectationFailedException::class, 'oh no!');
test('not failures', function () {
expect('445')->not->toBeDigits();
})->throws(ExpectationFailedException::class);

View File

@ -16,6 +16,11 @@ test('passes with DateTime and DateTimeImmutable', function () {
expect($past)->not->toBeGreaterThan($now);
});
test('passes with strings', function () {
expect('b')->toBeGreaterThan('a');
expect('a')->not->toBeGreaterThan('a');
});
test('failures', function () {
expect(4)->toBeGreaterThan(4);
})->throws(ExpectationFailedException::class);

View File

@ -18,6 +18,11 @@ test('passes with DateTime and DateTimeImmutable', function () {
expect($past)->not->toBeGreaterThanOrEqual($now);
});
test('passes with strings', function () {
expect('b')->toBeGreaterThanOrEqual('a');
expect('a')->toBeGreaterThanOrEqual('a');
});
test('failures', function () {
expect(4)->toBeGreaterThanOrEqual(4.1);
})->throws(ExpectationFailedException::class);

View File

@ -0,0 +1,9 @@
<?php
test('enum is backed by int')
->expect('Tests\Fixtures\Arch\ToBeIntBackedEnum\HasIntBacking')
->toBeIntBackedEnum();
test('enum is not backed by int')
->expect('Tests\Fixtures\Arch\ToBeIntBackedEnum\HasStringBacking')
->not->toBeIntBackedEnum();

Some files were not shown because too many files have changed in this diff Show More