diff --git a/src/Factories/TestCaseMethodFactory.php b/src/Factories/TestCaseMethodFactory.php index fb763c75..a9bbb0ee 100644 --- a/src/Factories/TestCaseMethodFactory.php +++ b/src/Factories/TestCaseMethodFactory.php @@ -32,7 +32,7 @@ final class TestCaseMethodFactory /** * The test's describing, if any. * - * @var array + * @var array */ public array $describing = []; diff --git a/src/Functions.php b/src/Functions.php index 1e12fe7e..6a4f45ec 100644 --- a/src/Functions.php +++ b/src/Functions.php @@ -18,6 +18,7 @@ use Pest\Support\Backtrace; use Pest\Support\Container; use Pest\Support\DatasetInfo; +use Pest\Support\Description; use Pest\Support\HigherOrderTapProxy; use Pest\TestSuite; use PHPUnit\Framework\TestCase; @@ -95,7 +96,7 @@ function describe(string $description, Closure $tests): DescribeCall { $filename = Backtrace::testFile(); - return new DescribeCall(TestSuite::getInstance(), $filename, $description, $tests); + return new DescribeCall(TestSuite::getInstance(), $filename, new Description($description), $tests); } } diff --git a/src/PendingCalls/Concerns/Describable.php b/src/PendingCalls/Concerns/Describable.php index 06a7eab7..0208ea4b 100644 --- a/src/PendingCalls/Concerns/Describable.php +++ b/src/PendingCalls/Concerns/Describable.php @@ -12,14 +12,14 @@ trait Describable /** * Note: this is property is not used; however, it gets added automatically by rector php. * - * @var array + * @var array */ public array $__describing; /** * The describing of the test case. * - * @var array + * @var array */ public array $describing = []; } diff --git a/src/PendingCalls/DescribeCall.php b/src/PendingCalls/DescribeCall.php index b015595c..bf6a7f51 100644 --- a/src/PendingCalls/DescribeCall.php +++ b/src/PendingCalls/DescribeCall.php @@ -6,6 +6,7 @@ use Closure; use Pest\Support\Backtrace; +use Pest\Support\Description; use Pest\TestSuite; /** @@ -16,7 +17,7 @@ final class DescribeCall /** * The current describe call. * - * @var array + * @var array */ private static array $describing = []; @@ -31,7 +32,7 @@ final class DescribeCall public function __construct( public readonly TestSuite $testSuite, public readonly string $filename, - public readonly string $description, + public readonly Description $description, public readonly Closure $tests ) { // @@ -40,7 +41,7 @@ public function __construct( /** * What is the current describing. * - * @return array + * @return array */ public static function describing(): array { diff --git a/src/Support/Description.php b/src/Support/Description.php new file mode 100644 index 00000000..01fc45a6 --- /dev/null +++ b/src/Support/Description.php @@ -0,0 +1,15 @@ +description; + } +} diff --git a/src/Support/Str.php b/src/Support/Str.php index 0e654bc8..6bdcc555 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -104,7 +104,7 @@ public static function isUuid(string $value): bool /** * Creates a describe block as `$describeDescription` → `$testDescription` format. * - * @param array $describeDescriptions + * @param array $describeDescriptions */ public static function describe(array $describeDescriptions, string $testDescription): string { diff --git a/tests/.pest/snapshots/Visual/Todo/todo.snap b/tests/.pest/snapshots/Visual/Todo/todo.snap index 5ab4f7fa..c50794f7 100644 --- a/tests/.pest/snapshots/Visual/Todo/todo.snap +++ b/tests/.pest/snapshots/Visual/Todo/todo.snap @@ -15,7 +15,7 @@ ↓ todo on describe → should not fail ↓ todo on describe → should run - TODO Tests\Features\Todo - 28 todos + TODO Tests\Features\Todo - 29 todos ↓ something todo later ↓ something todo later chained ↓ something todo later chained and with function body @@ -45,6 +45,7 @@ // nested describe note // test note ↓ todo on describe → todo block → it should not execute + ↓ todo on describe with matching name → describe block → it should not execute ↓ todo on test after describe block ↓ todo with note on test after describe block // test note @@ -80,6 +81,6 @@ PASS Tests\CustomTestCase\ParentTest ✓ override method - Tests: 38 todos, 3 passed (20 assertions) + Tests: 39 todos, 3 passed (21 assertions) Duration: x.xxs diff --git a/tests/.pest/snapshots/Visual/Todo/todo_in_parallel.snap b/tests/.pest/snapshots/Visual/Todo/todo_in_parallel.snap index 5ab4f7fa..c50794f7 100644 --- a/tests/.pest/snapshots/Visual/Todo/todo_in_parallel.snap +++ b/tests/.pest/snapshots/Visual/Todo/todo_in_parallel.snap @@ -15,7 +15,7 @@ ↓ todo on describe → should not fail ↓ todo on describe → should run - TODO Tests\Features\Todo - 28 todos + TODO Tests\Features\Todo - 29 todos ↓ something todo later ↓ something todo later chained ↓ something todo later chained and with function body @@ -45,6 +45,7 @@ // nested describe note // test note ↓ todo on describe → todo block → it should not execute + ↓ todo on describe with matching name → describe block → it should not execute ↓ todo on test after describe block ↓ todo with note on test after describe block // test note @@ -80,6 +81,6 @@ PASS Tests\CustomTestCase\ParentTest ✓ override method - Tests: 38 todos, 3 passed (20 assertions) + Tests: 39 todos, 3 passed (21 assertions) Duration: x.xxs diff --git a/tests/.pest/snapshots/Visual/Todo/todos.snap b/tests/.pest/snapshots/Visual/Todo/todos.snap index 5ab4f7fa..c50794f7 100644 --- a/tests/.pest/snapshots/Visual/Todo/todos.snap +++ b/tests/.pest/snapshots/Visual/Todo/todos.snap @@ -15,7 +15,7 @@ ↓ todo on describe → should not fail ↓ todo on describe → should run - TODO Tests\Features\Todo - 28 todos + TODO Tests\Features\Todo - 29 todos ↓ something todo later ↓ something todo later chained ↓ something todo later chained and with function body @@ -45,6 +45,7 @@ // nested describe note // test note ↓ todo on describe → todo block → it should not execute + ↓ todo on describe with matching name → describe block → it should not execute ↓ todo on test after describe block ↓ todo with note on test after describe block // test note @@ -80,6 +81,6 @@ PASS Tests\CustomTestCase\ParentTest ✓ override method - Tests: 38 todos, 3 passed (20 assertions) + Tests: 39 todos, 3 passed (21 assertions) Duration: x.xxs diff --git a/tests/.pest/snapshots/Visual/Todo/todos_in_parallel.snap b/tests/.pest/snapshots/Visual/Todo/todos_in_parallel.snap index 5ab4f7fa..c50794f7 100644 --- a/tests/.pest/snapshots/Visual/Todo/todos_in_parallel.snap +++ b/tests/.pest/snapshots/Visual/Todo/todos_in_parallel.snap @@ -15,7 +15,7 @@ ↓ todo on describe → should not fail ↓ todo on describe → should run - TODO Tests\Features\Todo - 28 todos + TODO Tests\Features\Todo - 29 todos ↓ something todo later ↓ something todo later chained ↓ something todo later chained and with function body @@ -45,6 +45,7 @@ // nested describe note // test note ↓ todo on describe → todo block → it should not execute + ↓ todo on describe with matching name → describe block → it should not execute ↓ todo on test after describe block ↓ todo with note on test after describe block // test note @@ -80,6 +81,6 @@ PASS Tests\CustomTestCase\ParentTest ✓ override method - Tests: 38 todos, 3 passed (20 assertions) + Tests: 39 todos, 3 passed (21 assertions) Duration: x.xxs diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index 1eaf4598..ee3477c8 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -29,6 +29,12 @@ ✓ it gets executed after the test ✓ outer → inner → it does not get executed before the test ✓ outer → inner → it should call all parent afterEach functions + ✓ matching describe block names → outer → middle → inner → it does not get executed before the test + ✓ matching describe block names → outer → middle → inner → it should call all parent afterEach functions + ✓ matching describe block names → outer → middle → it does not get executed before the test + ✓ matching describe block names → outer → middle → it should not call afterEach functions for sibling describe blocks with the same name + ✓ matching describe block names → outer → inner → it does not get executed before the test + ✓ matching describe block names → outer → inner → it should not call afterEach functions for descendent of sibling describe blocks with the same name PASS Tests\Features\Assignee ✓ it may be associated with an assignee [@nunomaduro, @taylorotwell] @@ -45,6 +51,13 @@ ✓ outer → inner → it should call all parent beforeEach functions ✓ with expectations → nested block → test ✓ with expectations → test + ✓ matching describe block names → outer → middle → inner → it should call all parent beforeEach functions + ✓ matching describe block names → outer → middle → it should not call beforeEach functions for sibling describe blocks with the same name + ✓ matching describe block names → outer → inner → it should not call beforeEach functions for descendent of sibling describe blocks with the same name + ✓ matching name → it should call the before each + ✓ matching name → it should not call the before each on the describe block with the same name + ✓ called on all tests → beforeEach should be called + ✓ called on all tests → beforeEach should be called for all tests PASS Tests\Features\BeforeEachProxiesToTestCallWithExpectations ✓ runs 1 @@ -187,6 +200,11 @@ ✓ with on nested describe → nested → describe → it should include the with value from all parent describe blocks with (1) / (2) ✓ with on nested describe → nested → describe → should include the with value from all parent describe blocks and the test with (1) / (2) / (3) ✓ with on nested describe → nested → after inner describe block with (1) + ✓ matching describe block names → outer → before inner describe block with (1) + ✓ matching describe block names → outer → inner → it should include the with value from all parent describe blocks with (1) / (2) + ✓ matching describe block names → outer → inner → should include the with value from all parent describe blocks and the test with (1) / (2) / (3) + ✓ matching describe block names → outer → inner → it should not include the value from the other describe block with the same name with (1) + ✓ matching describe block names → outer → after inner describe block with (1) ✓ after describe block with (5) ✓ it may be used with high order after describe block with dataset "formal" ✓ it may be used with high order after describe block with dataset "informal" @@ -1104,6 +1122,25 @@ // This is before each describe runtime note // This is before each nested describe runtime note // This is a runtime note within a nested describe + ✓ matching describe names → describe block → it may have a static note and runtime note + // This is before each static note + // This is before each matching describe static note + // This is a nested matching static note + // This is a static note within a matching describe + // This is before each runtime note + // This is before each matching describe runtime note + // This is before each matching describe runtime note + // This is a runtime note within a matching describe + ✓ matching describe names → describe block → it may have a static note and runtime note, that are different than the matching describe block + // This is before each static note + // This is before each matching describe static note + // This is a nested matching static note, and should not contain the matching describe notes + // This is before each matching describe static note, and should not contain the matching describe notes + // This is a static note within a matching describe, and should not contain the matching describe notes + // This is before each runtime note + // This is before each matching describe runtime note + // This is before each matching describe runtime note, and should not contain the matching describe notes + // This is a runtime note within a matching describe, and should not contain the matching describe notes ✓ multiple notes // This is before each static note // This is before each runtime note @@ -1250,6 +1287,10 @@ ✓ describe blocks → describe with repeat → nested describe with repeat → test with no repeat should repeat the number of times specified in the parent describe block @ repetition 2 of 2 ✓ describe blocks → describe with repeat → nested describe with repeat → test with repeat should repeat the number of times specified in the test @ repetition 1 of 2 ✓ describe blocks → describe with repeat → nested describe with repeat → test with repeat should repeat the number of times specified in the test @ repetition 2 of 2 + ✓ matching describe blocks → describe block → it should repeat the number of times specified in the parent describe block @ repetition 1 of 3 + ✓ matching describe blocks → describe block → it should repeat the number of times specified in the parent describe block @ repetition 2 of 3 + ✓ matching describe blocks → describe block → it should repeat the number of times specified in the parent describe block @ repetition 3 of 3 + ✓ matching describe blocks → describe block → should not repeat the number of times of the describe block with the same name PASS Tests\Features\ScopedDatasets\Directory\NestedDirectory1\TestFileInNestedDirectoryWithDatasetsFile ✓ uses dataset with (1) @@ -1312,6 +1353,12 @@ - skip on beforeEach → skipped tests → nested inside skipped block → it should not execute - skip on beforeEach → skipped tests → it should not execute ✓ skip on beforeEach → it should execute + - matching describe with skip → describe block → it should not execute + ✓ matching describe with skip → describe block → it should execute a test in a describe block with the same name as a skipped describe block + ✓ matching describe with skip → it should execute + - matching describe with skip on beforeEach → describe block → it should not execute + ✓ matching describe with skip on beforeEach → describe block → it should execute a test in a describe block with the same name as a skipped describe block + ✓ matching describe with skip on beforeEach → it should execute ✓ it does not skip after the describe block - it can skip after the describe block @@ -1334,7 +1381,7 @@ ✓ nested → it may be associated with an ticket #1, #4, #5, #6, #3 // an note between an the ticket - PASS Tests\Features\Todo - 28 todos + PASS Tests\Features\Todo - 29 todos ↓ something todo later ↓ something todo later chained ↓ something todo later chained and with function body @@ -1366,6 +1413,9 @@ // test note ↓ todo on describe → todo block → it should not execute ✓ todo on describe → it should execute + ↓ todo on describe with matching name → describe block → it should not execute + ✓ todo on describe with matching name → describe block → it should execute a test in a describe block with the same name as a todo describe block + ✓ todo on describe with matching name → it should execute ↓ todo on test after describe block ↓ todo with note on test after describe block // test note @@ -1698,4 +1748,4 @@ WARN Tests\Visual\Version - visual snapshot of help command output - Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 38 todos, 33 skipped, 1144 passed (2736 assertions) \ No newline at end of file + Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 39 todos, 35 skipped, 1174 passed (2777 assertions) \ No newline at end of file diff --git a/tests/Features/AfterEach.php b/tests/Features/AfterEach.php index ea0dd7ea..aff68283 100644 --- a/tests/Features/AfterEach.php +++ b/tests/Features/AfterEach.php @@ -48,3 +48,55 @@ }); }); }); + +describe('matching describe block names', function () { + afterEach(function () { + $this->state->foo = 1; + }); + + describe('outer', function () { + afterEach(function () { + $this->state->foo++; + }); + + describe('middle', function () { + afterEach(function () { + $this->state->foo++; + }); + + describe('inner', function () { + afterEach(function () { + $this->state->foo++; + }); + + it('does not get executed before the test', function () { + expect($this)->not->toHaveProperty('foo'); + }); + + it('should call all parent afterEach functions', function () { + expect($this->state->foo)->toBe(4); + }); + }); + }); + + describe('middle', function () { + it('does not get executed before the test', function () { + expect($this)->not->toHaveProperty('foo'); + }); + + it('should not call afterEach functions for sibling describe blocks with the same name', function () { + expect($this)->not->toHaveProperty('foo'); + }); + }); + + describe('inner', function () { + it('does not get executed before the test', function () { + expect($this)->not->toHaveProperty('foo'); + }); + + it('should not call afterEach functions for descendent of sibling describe blocks with the same name', function () { + expect($this)->not->toHaveProperty('foo'); + }); + }); + }); +}); diff --git a/tests/Features/BeforeEach.php b/tests/Features/BeforeEach.php index 04f0dd13..a7c7befe 100644 --- a/tests/Features/BeforeEach.php +++ b/tests/Features/BeforeEach.php @@ -51,3 +51,78 @@ test('test', function () {}); }); + +describe('matching describe block names', function () { + beforeEach(function () { + $this->foo = 1; + }); + + describe('outer', function () { + beforeEach(function () { + $this->foo++; + }); + + describe('middle', function () { + beforeEach(function () { + $this->foo++; + }); + + describe('inner', function () { + beforeEach(function () { + $this->foo++; + }); + + it('should call all parent beforeEach functions', function () { + expect($this->foo)->toBe(4); + }); + }); + }); + + describe('middle', function () { + it('should not call beforeEach functions for sibling describe blocks with the same name', function () { + expect($this->foo)->toBe(2); + }); + }); + + describe('inner', function () { + it('should not call beforeEach functions for descendent of sibling describe blocks with the same name', function () { + expect($this->foo)->toBe(2); + }); + }); + }); +}); + +$matchingNameCalls = 0; +describe('matching name', function () use (&$matchingNameCalls) { + beforeEach(function () use (&$matchingNameCalls) { + $matchingNameCalls++; + }); + + it('should call the before each', function () use (&$matchingNameCalls) { + expect($matchingNameCalls)->toBe(1); + }); +}); + +describe('matching name', function () use (&$matchingNameCalls) { + it('should not call the before each on the describe block with the same name', function () use (&$matchingNameCalls) { + expect($matchingNameCalls)->toBe(1); + }); +}); + +beforeEach(function () { + $this->baz = 1; +}); + +describe('called on all tests', function () { + beforeEach(function () { + $this->baz++; + }); + + test('beforeEach should be called', function () { + expect($this->baz)->toBe(2); + }); + + test('beforeEach should be called for all tests', function () { + expect($this->baz)->toBe(2); + }); +}); diff --git a/tests/Features/DatasetsTests.php b/tests/Features/DatasetsTests.php index 54cb77a8..837b56f6 100644 --- a/tests/Features/DatasetsTests.php +++ b/tests/Features/DatasetsTests.php @@ -415,6 +415,34 @@ function () { })->with([1]); }); +describe('matching describe block names', function () { + describe('outer', function () { + test('before inner describe block', function (...$args) { + expect($args)->toBe([1]); + }); + + describe('inner', function () { + it('should include the with value from all parent describe blocks', function (...$args) { + expect($args)->toBe([1, 2]); + }); + + test('should include the with value from all parent describe blocks and the test', function (...$args) { + expect($args)->toBe([1, 2, 3]); + })->with([3]); + })->with([2]); + + describe('inner', function () { + it('should not include the value from the other describe block with the same name', function (...$args) { + expect($args)->toBe([1]); + }); + }); + + test('after inner describe block', function (...$args) { + expect($args)->toBe([1]); + }); + })->with([1]); +}); + test('after describe block', function (...$args) { expect($args)->toBe([5]); })->with([5]); diff --git a/tests/Features/Note.php b/tests/Features/Note.php index 571d7c85..22d73b4f 100644 --- a/tests/Features/Note.php +++ b/tests/Features/Note.php @@ -44,6 +44,36 @@ })->note('This is a nested describe static note'); })->note('This is describe static note'); +describe('matching describe names', function () { + beforeEach(function () { + $this->note('This is before each matching describe runtime note'); + })->note('This is before each matching describe static note'); + + describe('describe block', function () { + beforeEach(function () { + $this->note('This is before each matching describe runtime note'); + })->note('This is before each matching describe static note'); + + it('may have a static note and runtime note', function () { + expect(true)->toBeTrue(true); + + $this->note('This is a runtime note within a matching describe'); + })->note('This is a static note within a matching describe'); + })->note('This is a nested matching static note'); + + describe('describe block', function () { + beforeEach(function () { + $this->note('This is before each matching describe runtime note, and should not contain the matching describe notes'); + })->note('This is before each matching describe static note, and should not contain the matching describe notes'); + + it('may have a static note and runtime note, that are different than the matching describe block', function () { + expect(true)->toBeTrue(true); + + $this->note('This is a runtime note within a matching describe, and should not contain the matching describe notes'); + })->note('This is a static note within a matching describe, and should not contain the matching describe notes'); + })->note('This is a nested matching static note, and should not contain the matching describe notes'); +}); + test('multiple notes', function () { expect(true)->toBeTrue(true); diff --git a/tests/Features/Repeat.php b/tests/Features/Repeat.php index 5a3370cd..89d0c322 100644 --- a/tests/Features/Repeat.php +++ b/tests/Features/Repeat.php @@ -79,3 +79,17 @@ })->repeat(times: 2); })->repeat(times: 3); }); + +describe('matching describe blocks', function () { + describe('describe block', function () { + it('should repeat the number of times specified in the parent describe block', function () { + expect(true)->toBeTrue(); + }); + })->repeat(times: 3); + + describe('describe block', function () { + test('should not repeat the number of times of the describe block with the same name', function () { + expect(true)->toBeTrue(); + }); + }); +}); diff --git a/tests/Features/Skip.php b/tests/Features/Skip.php index 35218945..9c305d35 100644 --- a/tests/Features/Skip.php +++ b/tests/Features/Skip.php @@ -125,6 +125,74 @@ }); }); +describe('matching describe with skip', function () { + beforeEach(function () { + $this->ran = false; + }); + + afterEach(function () { + match ($this->name()) { + '__pest_evaluable__matching_describe_with_skip__→__describe_block__→_it_should_not_execute' => expect($this->ran)->toBe(false), + '__pest_evaluable__matching_describe_with_skip__→__describe_block__→_it_should_execute_a_test_in_a_describe_block_with_the_same_name_as_a_skipped_describe_block' => expect($this->ran)->toBe(true), + '__pest_evaluable__matching_describe_with_skip__→_it_should_execute' => expect($this->ran)->toBe(true), + default => $this->fail('Unexpected test name: '.$this->name()), + }; + }); + + describe('describe block', function () { + it('should not execute', function () { + $this->ran = true; + $this->fail(); + }); + })->skip(); + + describe('describe block', function () { + it('should execute a test in a describe block with the same name as a skipped describe block', function () { + $this->ran = true; + }); + }); + + it('should execute', function () { + $this->ran = true; + expect($this->ran)->toBe(true); + }); +}); + +describe('matching describe with skip on beforeEach', function () { + beforeEach(function () { + $this->ran = false; + }); + + afterEach(function () { + match ($this->name()) { + '__pest_evaluable__matching_describe_with_skip_on_beforeEach__→__describe_block__→_it_should_not_execute' => expect($this->ran)->toBe(false), + '__pest_evaluable__matching_describe_with_skip_on_beforeEach__→__describe_block__→_it_should_execute_a_test_in_a_describe_block_with_the_same_name_as_a_skipped_describe_block' => expect($this->ran)->toBe(true), + '__pest_evaluable__matching_describe_with_skip_on_beforeEach__→_it_should_execute' => expect($this->ran)->toBe(true), + default => $this->fail('Unexpected test name: '.$this->name()), + }; + }); + + describe('describe block', function () { + beforeEach()->skip(); + + it('should not execute', function () { + $this->ran = true; + $this->fail(); + }); + }); + + describe('describe block', function () { + it('should execute a test in a describe block with the same name as a skipped describe block', function () { + $this->ran = true; + }); + }); + + it('should execute', function () { + $this->ran = true; + expect($this->ran)->toBe(true); + }); +}); + it('does not skip after the describe block', function () { expect(true)->toBeTrue(); }); diff --git a/tests/Features/Todo.php b/tests/Features/Todo.php index f979a2ff..d6e8d978 100644 --- a/tests/Features/Todo.php +++ b/tests/Features/Todo.php @@ -108,6 +108,40 @@ }); }); +describe('todo on describe with matching name', function () { + beforeEach(function () { + $this->ran = false; + }); + + afterEach(function () { + match ($this->name()) { + '__pest_evaluable__todo_on_describe_with_matching_name__→__describe_block__→_it_should_not_execute' => expect($this->ran)->toBe(false), + '__pest_evaluable__todo_on_describe_with_matching_name__→__describe_block__→_it_should_execute_a_test_in_a_describe_block_with_the_same_name_as_a_todo_describe_block' => expect($this->ran)->toBe(true), + '__pest_evaluable__todo_on_describe_with_matching_name__→_it_should_execute' => expect($this->ran)->toBe(true), + + default => $this->fail('Unexpected test name: '.$this->name()), + }; + }); + + describe('describe block', function () { + it('should not execute', function () { + $this->ran = true; + $this->fail(); + }); + })->todo(); + + describe('describe block', function () { + it('should execute a test in a describe block with the same name as a todo describe block', function () { + $this->ran = true; + }); + }); + + it('should execute', function () { + $this->ran = true; + expect($this->ran)->toBe(true); + }); +}); + test('todo on test after describe block', function () { $this->fail(); })->todo(); diff --git a/tests/Visual/Parallel.php b/tests/Visual/Parallel.php index 313f8208..4190213c 100644 --- a/tests/Visual/Parallel.php +++ b/tests/Visual/Parallel.php @@ -16,7 +16,7 @@ test('parallel', function () use ($run) { expect($run('--exclude-group=integration')) - ->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 38 todos, 24 skipped, 1134 passed (2712 assertions)') + ->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 39 todos, 26 skipped, 1164 passed (2753 assertions)') ->toContain('Parallel: 3 processes'); })->skipOnWindows();