Compare commits

...

21 Commits

Author SHA1 Message Date
e86bec3e68 release: 4.3.0 2025-12-30 14:48:33 -05:00
58b8f3cc5d Merge pull request #1598 from Willem-Jaap/willem-jaap/pest-only-file-level
feat: add pest only function to mark each test in a file as only
2025-12-30 14:40:04 -05:00
c157b661f2 style: format 2025-12-30 09:26:35 +01:00
be90610f17 feat: add pest only function to mark each test in a file as only 2025-12-30 09:24:05 +01:00
1701a306c3 Merge pull request #1590 from pestphp/feature/intl-exception
feat: show more useful exception when `intl` extension not found
2025-12-29 22:09:16 -05:00
064ab3fc2e Merge pull request #1595 from bilboque/dirty-flag-not-found-in-help
feat: add --dirty documentation in --help
2025-12-29 22:08:12 -05:00
leo
44e315df98 feat: add --dirty documentation in --help 2025-12-22 11:02:28 +01:00
62694c14b9 chore: style 2025-12-15 11:54:24 +00:00
7c43c1c583 Merge pull request #1586 from jackbayliss/bump-checkout-action
[4.x] Bump checkout version from 5 to 6
2025-12-15 11:49:28 +00:00
6a96aed654 feat: adds phpunit@12.5 support 2025-12-15 11:48:43 +00:00
b1c997a869 feat: show more useful exception when intl extension not found 2025-12-12 12:02:00 +00:00
b4172e2c2e bump checkout version from 5 to 6 2025-12-10 14:08:06 +00:00
ae419afd36 chore: support for symfony 8.0.0 components 2025-11-28 12:04:48 +00:00
27aa305897 Merge pull request #1576 from Chris53897/feature/ci
ci: bump actions/checkout 4 => 5
2025-11-25 11:18:28 +00:00
f5820bd670 release: 4.1.5 2025-11-24 12:46:38 +00:00
41fd831153 release: 4.1.4 2025-11-24 10:25:45 +00:00
51340439e8 ci: bump actions/checkout 4 => 5 2025-11-22 12:12:15 +01:00
1a39826935 ci: tests against php 8.5 2025-11-20 02:59:27 +00:00
00990efc97 Merge pull request #1544 from Se7en-RU/fix-testdox-columns-warning
Fix Undefined array key "testdox-columns" warning
2025-11-04 07:42:57 +00:00
477d20a54f release: 4.1.3 2025-10-29 22:45:27 +00:00
4105e33c39 Fix Undefined array key "testdox-columns" warning
Fix Undefined array key "testdox-columns" warning
2025-10-21 11:44:55 +03:00
53 changed files with 69 additions and 30 deletions

View File

@ -19,7 +19,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2

View File

@ -14,14 +14,14 @@ jobs:
matrix: matrix:
os: [ubuntu-latest, macos-latest] # windows-latest os: [ubuntu-latest, macos-latest] # windows-latest
symfony: ['7.3'] symfony: ['7.3']
php: ['8.3', '8.4'] php: ['8.3', '8.4', '8.5']
dependency_version: [prefer-stable] dependency_version: [prefer-stable]
name: PHP ${{ matrix.php }} - Symfony ^${{ matrix.symfony }} - ${{ matrix.os }} - ${{ matrix.dependency_version }} name: PHP ${{ matrix.php }} - Symfony ^${{ matrix.symfony }} - ${{ matrix.os }} - ${{ matrix.dependency_version }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2

View File

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

View File

@ -18,19 +18,19 @@
], ],
"require": { "require": {
"php": "^8.3.0", "php": "^8.3.0",
"brianium/paratest": "^7.14.0", "brianium/paratest": "^7.16.0",
"nunomaduro/collision": "^8.8.2", "nunomaduro/collision": "^8.8.3",
"nunomaduro/termwind": "^2.3.1", "nunomaduro/termwind": "^2.3.3",
"pestphp/pest-plugin": "^4.0.0", "pestphp/pest-plugin": "^4.0.0",
"pestphp/pest-plugin-arch": "^4.0.0", "pestphp/pest-plugin-arch": "^4.0.0",
"pestphp/pest-plugin-mutate": "^4.0.1", "pestphp/pest-plugin-mutate": "^4.0.1",
"pestphp/pest-plugin-profanity": "^4.1.0", "pestphp/pest-plugin-profanity": "^4.2.1",
"phpunit/phpunit": "^12.4.0", "phpunit/phpunit": "^12.5.4",
"symfony/process": "^7.3.4" "symfony/process": "^7.4.0|^8.0.0"
}, },
"conflict": { "conflict": {
"filp/whoops": "<2.18.3", "filp/whoops": "<2.18.3",
"phpunit/phpunit": ">12.4.0", "phpunit/phpunit": ">12.5.4",
"sebastian/exporter": "<7.0.0", "sebastian/exporter": "<7.0.0",
"webmozart/assert": "<1.11.0" "webmozart/assert": "<1.11.0"
}, },
@ -57,8 +57,8 @@
"require-dev": { "require-dev": {
"pestphp/pest-dev-tools": "^4.0.0", "pestphp/pest-dev-tools": "^4.0.0",
"pestphp/pest-plugin-browser": "^4.1.1", "pestphp/pest-plugin-browser": "^4.1.1",
"pestphp/pest-plugin-type-coverage": "^4.0.2", "pestphp/pest-plugin-type-coverage": "^4.0.3",
"psy/psysh": "^0.12.12" "psy/psysh": "^0.12.18"
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"prefer-stable": true, "prefer-stable": true,

View File

@ -2,7 +2,10 @@
declare(strict_types=1); declare(strict_types=1);
use Rector\CodingStyle\Rector\ArrowFunction\ArrowFunctionDelegatingCallToFirstClassCallableRector;
use Rector\Config\RectorConfig; use Rector\Config\RectorConfig;
use Rector\DeadCode\Rector\ClassMethod\RemoveParentDelegatingConstructorRector;
use Rector\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector; use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;
return RectorConfig::configure() return RectorConfig::configure()
@ -12,6 +15,9 @@ return RectorConfig::configure()
->withSkip([ ->withSkip([
__DIR__.'/src/Plugins/Parallel/Paratest/WrapperRunner.php', __DIR__.'/src/Plugins/Parallel/Paratest/WrapperRunner.php',
ReturnNeverTypeRector::class, ReturnNeverTypeRector::class,
ArrowFunctionDelegatingCallToFirstClassCallableRector::class,
NarrowObjectReturnTypeRector::class,
RemoveParentDelegatingConstructorRector::class,
]) ])
->withPreparedSets( ->withPreparedSets(
deadCode: true, deadCode: true,

View File

@ -83,7 +83,7 @@ final class BootFiles implements Bootstrapper
private function bootDatasets(string $testsPath): void private function bootDatasets(string $testsPath): void
{ {
assert(strlen($testsPath) > 0); assert($testsPath !== '');
$files = (new PhpUnitFileIterator)->getFilesAsArray($testsPath, '.php'); $files = (new PhpUnitFileIterator)->getFilesAsArray($testsPath, '.php');

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Pest; namespace Pest;
use Pest\PendingCalls\BeforeEachCall;
use Pest\PendingCalls\UsesCall; use Pest\PendingCalls\UsesCall;
/** /**
@ -62,6 +63,14 @@ final readonly class Configuration
return (new UsesCall($this->filename, []))->group(...$groups); return (new UsesCall($this->filename, []))->group(...$groups);
} }
/**
* Marks all tests in the current file to be run exclusively.
*/
public function only(): void
{
(new BeforeEachCall(TestSuite::getInstance(), $this->filename))->only();
}
/** /**
* Depending on where is called, it will extend the given classes and traits globally or locally. * Depending on where is called, it will extend the given classes and traits globally or locally.
*/ */

View File

@ -397,7 +397,7 @@ final class Expectation
* *
* @return Expectation<TValue>|OppositeExpectation<TValue>|EachExpectation<TValue>|HigherOrderExpectation<Expectation<TValue>, TValue|null>|TValue * @return Expectation<TValue>|OppositeExpectation<TValue>|EachExpectation<TValue>|HigherOrderExpectation<Expectation<TValue>, TValue|null>|TValue
*/ */
public function __get(string $name) public function __get(string $name): mixed
{ {
if (! self::hasMethod($name)) { if (! self::hasMethod($name)) {
if (! is_object($this->value) && method_exists(PendingArchExpectation::class, $name)) { if (! is_object($this->value) && method_exists(PendingArchExpectation::class, $name)) {

View File

@ -15,6 +15,7 @@ use Pest\Arch\PendingArchExpectation;
use Pest\Arch\SingleArchExpectation; use Pest\Arch\SingleArchExpectation;
use Pest\Arch\Support\FileLineFinder; use Pest\Arch\Support\FileLineFinder;
use Pest\Exceptions\InvalidExpectation; use Pest\Exceptions\InvalidExpectation;
use Pest\Exceptions\MissingDependency;
use Pest\Expectation; use Pest\Expectation;
use Pest\Support\Arr; use Pest\Support\Arr;
use Pest\Support\Exporter; use Pest\Support\Exporter;
@ -284,6 +285,10 @@ final readonly class OppositeExpectation
*/ */
public function toHaveSuspiciousCharacters(): ArchExpectation public function toHaveSuspiciousCharacters(): ArchExpectation
{ {
if (! class_exists(Spoofchecker::class)) {
throw new MissingDependency(__FUNCTION__, 'ext-intl >= 2.0');
}
$checker = new Spoofchecker; $checker = new Spoofchecker;
/** @var Expectation<array<int, string>|string> $original */ /** @var Expectation<array<int, string>|string> $original */

View File

@ -71,7 +71,7 @@ final readonly class Kernel
$output, $output,
); );
register_shutdown_function(fn () => $kernel->shutdown()); register_shutdown_function($kernel->shutdown(...));
foreach (self::BOOTSTRAPPERS as $bootstrapper) { foreach (self::BOOTSTRAPPERS as $bootstrapper) {
$bootstrapper = Container::getInstance()->get($bootstrapper); $bootstrapper = Container::getInstance()->get($bootstrapper);

View File

@ -131,7 +131,7 @@ final readonly class Converter
// clean the paths of each frame. // clean the paths of each frame.
$frames = array_map( $frames = array_map(
fn (string $frame): string => $this->toRelativePath($frame), $this->toRelativePath(...),
$frames $frames
); );

View File

@ -6,7 +6,7 @@ namespace Pest;
function version(): string function version(): string
{ {
return '4.1.2'; return '4.3.0';
} }
function testDirectory(string $file = ''): string function testDirectory(string $file = ''): string

View File

@ -99,6 +99,7 @@ final readonly class Help implements HandlesArguments
{ {
$helpReflection = new PHPUnitHelp; $helpReflection = new PHPUnitHelp;
// @phpstan-ignore-next-line
$content = (fn (): array => $this->elements())->call($helpReflection); $content = (fn (): array => $this->elements())->call($helpReflection);
$content['Configuration'] = [...[[ $content['Configuration'] = [...[[
@ -141,6 +142,9 @@ final readonly class Help implements HandlesArguments
], [ ], [
'arg' => '--retry', 'arg' => '--retry',
'desc' => 'Run non-passing tests first and stop execution upon first error or failure', 'desc' => 'Run non-passing tests first and stop execution upon first error or failure',
], [
'arg' => '--dirty',
'desc' => 'Only run tests that have uncommitted changes according to Git',
], ...$content['Selection']]; ], ...$content['Selection']];
$content['Reporting'] = [...$content['Reporting'], ...[ $content['Reporting'] = [...$content['Reporting'], ...[

View File

@ -31,10 +31,8 @@ final class HigherOrderTapProxy
/** /**
* Dynamically pass properties gets to the target. * Dynamically pass properties gets to the target.
*
* @return mixed
*/ */
public function __get(string $property) public function __get(string $property): mixed
{ {
if (property_exists($this->target, $property)) { if (property_exists($this->target, $property)) {
return $this->target->{$property}; return $this->target->{$property};

View File

@ -19,7 +19,7 @@ test('pass with dataset', function ($data) {
[$filename] = TestSuite::getInstance()->snapshots->get(); [$filename] = TestSuite::getInstance()->snapshots->get();
expect($filename)->toStartWith('tests/.pest/snapshots-external/') expect($filename)->toStartWith('tests/.pest/snapshots-external/')
->toEndWith('pass_with_dataset_with_data_set_____my_datas_set_value___.snap') ->toEndWith('pass_with_dataset_with_data_set____my_datas_set_value___.snap')
->and($this->snapshotable)->toMatchSnapshot(); ->and($this->snapshotable)->toMatchSnapshot();
})->with(['my-datas-set-value']); })->with(['my-datas-set-value']);
@ -29,7 +29,7 @@ describe('within describe', function () {
[$filename] = TestSuite::getInstance()->snapshots->get(); [$filename] = TestSuite::getInstance()->snapshots->get();
expect($filename)->toStartWith('tests/.pest/snapshots-external/') expect($filename)->toStartWith('tests/.pest/snapshots-external/')
->toEndWith('pass_with_dataset_with_data_set_____my_datas_set_value___.snap') ->toEndWith('pass_with_dataset_with_data_set____my_datas_set_value___.snap')
->and($this->snapshotable)->toMatchSnapshot(); ->and($this->snapshotable)->toMatchSnapshot();
}); });
})->with(['my-datas-set-value']); })->with(['my-datas-set-value']);

View File

@ -0,0 +1,7 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>Snapshot</h1>
</div>
</div>
</div>

View File

@ -0,0 +1,7 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>Snapshot</h1>
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
Pest Testing Framework 4.1.2. Pest Testing Framework 4.3.0.
USAGE: pest <file> [options] USAGE: pest <file> [options]
@ -27,6 +27,8 @@
--pr .... Output to standard output tests with the given pull request number --pr .... Output to standard output tests with the given pull request number
--pull-request Output to standard output tests with the given pull request number (alias for --pr) --pull-request Output to standard output tests with the given pull request number (alias for --pr)
--retry Run non-passing tests first and stop execution upon first error or failure --retry Run non-passing tests first and stop execution upon first error or failure
--dirty ...... Only run tests that have uncommitted changes according to Git
--all .................... Ignore test selection from XML configuration file
--list-suites ................................... List available test suites --list-suites ................................... List available test suites
--testsuite [name] ......... Only run tests from the specified test suite(s) --testsuite [name] ......... Only run tests from the specified test suite(s)
--exclude-testsuite [name] .. Exclude tests from the specified test suite(s) --exclude-testsuite [name] .. Exclude tests from the specified test suite(s)
@ -138,6 +140,7 @@
--only-summary-for-coverage-text Option for code coverage report in text format: only show summary --only-summary-for-coverage-text Option for code coverage report in text format: only show summary
--show-uncovered-for-coverage-text Option for code coverage report in text format: show uncovered files --show-uncovered-for-coverage-text Option for code coverage report in text format: show uncovered files
--coverage-xml [dir] . Write code coverage report in XML format to directory --coverage-xml [dir] . Write code coverage report in XML format to directory
--exclude-source-from-xml-coverage Exclude [source] element from code coverage report in XML format
--warm-coverage-cache ........................... Warm static analysis cache --warm-coverage-cache ........................... Warm static analysis cache
--coverage-filter [dir] ........... Include [dir] in code coverage reporting --coverage-filter [dir] ........... Include [dir] in code coverage reporting
--path-coverage .......... Report path coverage in addition to line coverage --path-coverage .......... Report path coverage in addition to line coverage

View File

@ -1,3 +1,3 @@
Pest Testing Framework 4.1.2. Pest Testing Framework 4.3.0.

View File

@ -5,8 +5,8 @@
##teamcity[testStarted name='can also pass' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can also pass' flowId='1234'] ##teamcity[testStarted name='can also pass' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can also pass' flowId='1234']
##teamcity[testFinished name='can also pass' duration='100000' flowId='1234'] ##teamcity[testFinished name='can also pass' duration='100000' flowId='1234']
##teamcity[testSuiteStarted name='can pass with dataset' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can pass with dataset' flowId='1234'] ##teamcity[testSuiteStarted name='can pass with dataset' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can pass with dataset' flowId='1234']
##teamcity[testStarted name='can pass with dataset with data set "@(true)"' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can pass with dataset with data set "@(true)"' flowId='1234'] ##teamcity[testStarted name='can pass with dataset with data set "(true)"' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can pass with dataset with data set "(true)"' flowId='1234']
##teamcity[testFinished name='can pass with dataset with data set "@(true)"' duration='100000' flowId='1234'] ##teamcity[testFinished name='can pass with dataset with data set "(true)"' duration='100000' flowId='1234']
##teamcity[testSuiteFinished name='can pass with dataset' flowId='1234'] ##teamcity[testSuiteFinished name='can pass with dataset' flowId='1234']
##teamcity[testSuiteFinished name='Tests/tests/SuccessOnly' flowId='1234'] ##teamcity[testSuiteFinished name='Tests/tests/SuccessOnly' flowId='1234']

View File

@ -75,7 +75,7 @@ test('pass with dataset', function ($data) {
[$filename] = TestSuite::getInstance()->snapshots->get(); [$filename] = TestSuite::getInstance()->snapshots->get();
expect($filename)->toStartWith('tests/.pest/snapshots/') expect($filename)->toStartWith('tests/.pest/snapshots/')
->toEndWith('pass_with_dataset_with_data_set_____my_datas_set_value___.snap') ->toEndWith('pass_with_dataset_with_data_set____my_datas_set_value___.snap')
->and($this->snapshotable)->toMatchSnapshot(); ->and($this->snapshotable)->toMatchSnapshot();
})->with(['my-datas-set-value']); })->with(['my-datas-set-value']);
@ -85,7 +85,7 @@ describe('within describe', function () {
[$filename] = TestSuite::getInstance()->snapshots->get(); [$filename] = TestSuite::getInstance()->snapshots->get();
expect($filename)->toStartWith('tests/.pest/snapshots/') expect($filename)->toStartWith('tests/.pest/snapshots/')
->toEndWith('pass_with_dataset_with_data_set_____my_datas_set_value___.snap') ->toEndWith('pass_with_dataset_with_data_set____my_datas_set_value___.snap')
->and($this->snapshotable)->toMatchSnapshot(); ->and($this->snapshotable)->toMatchSnapshot();
}); });
})->with(['my-datas-set-value']); })->with(['my-datas-set-value']);

View File

@ -16,7 +16,7 @@ $run = function () {
test('parallel', function () use ($run) { test('parallel', function () use ($run) {
expect($run('--exclude-group=integration')) expect($run('--exclude-group=integration'))
->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 39 todos, 26 skipped, 1178 passed (2790 assertions)') ->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 39 todos, 26 skipped, 1177 passed (2790 assertions)')
->toContain('Parallel: 3 processes'); ->toContain('Parallel: 3 processes');
})->skipOnWindows(); })->skipOnWindows();