diff --git a/overrides/TextUI/Output/Default/ProgressPrinter/TestSkippedSubscriber.php b/overrides/TextUI/Output/Default/ProgressPrinter/TestSkippedSubscriber.php index 4e0521a3..80de0225 100644 --- a/overrides/TextUI/Output/Default/ProgressPrinter/TestSkippedSubscriber.php +++ b/overrides/TextUI/Output/Default/ProgressPrinter/TestSkippedSubscriber.php @@ -61,9 +61,11 @@ final class TestSkippedSubscriber extends Subscriber implements SkippedSubscribe */ public function notify(Skipped $event): void { - str_contains($event->message(), '__TODO__') - ? $this->printTodoItem() - : $this->printer()->testSkipped(); + if (str_contains($event->message(), '__TODO__')) { + $this->printTodoItem(); + } + + $this->printer()->testSkipped(); } /** diff --git a/phpunit.xml b/phpunit.xml index c7653639..c4d4343e 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -12,12 +12,6 @@ stopOnError="false" stopOnFailure="false" backupStaticProperties="false" - displayDetailsOnIncompleteTests="true" - displayDetailsOnSkippedTests="true" - displayDetailsOnTestsThatTriggerDeprecations="true" - displayDetailsOnTestsThatTriggerErrors="true" - displayDetailsOnTestsThatTriggerNotices="true" - displayDetailsOnTestsThatTriggerWarnings="true" > diff --git a/src/Plugins/Parallel/Paratest/ResultPrinter.php b/src/Plugins/Parallel/Paratest/ResultPrinter.php index 71134d1f..a0e0033e 100644 --- a/src/Plugins/Parallel/Paratest/ResultPrinter.php +++ b/src/Plugins/Parallel/Paratest/ResultPrinter.php @@ -12,6 +12,7 @@ use function fread; use function fseek; use function ftell; use function fwrite; +use NunoMaduro\Collision\Adapters\Phpunit\State; use ParaTest\Options; use Pest\Plugins\Parallel\Support\CompactPrinter; use Pest\Support\StateGenerator; @@ -27,11 +28,21 @@ use Symfony\Component\Console\Output\OutputInterface; /** @internal */ final class ResultPrinter { + /** + * If the test should be marked as todo. + */ + public bool $lastWasTodo = false; + /** * The "native" printer. */ public readonly Printer $printer; + /** + * The state. + */ + public int $passedTests = 0; + /** * The "compact" printer. */ @@ -140,7 +151,7 @@ final class ResultPrinter return; } - $state = (new StateGenerator())->fromPhpUnitTestResult($testResult); + $state = (new StateGenerator())->fromPhpUnitTestResult($this->passedTests, $testResult); $this->compactPrinter->errors($state); $this->compactPrinter->recap($state, $testResult, $duration, $this->options); @@ -148,6 +159,20 @@ final class ResultPrinter private function printFeedbackItem(string $item): void { + if ($this->lastWasTodo) { + $this->lastWasTodo = false; + + return; + } + + if ($item === 'T') { + $this->lastWasTodo = true; + } + + if ($item === '.') { + $this->passedTests++; + } + $this->compactPrinter->descriptionItem($item); } diff --git a/src/Plugins/Parallel/Support/CompactPrinter.php b/src/Plugins/Parallel/Support/CompactPrinter.php index 16ae2388..95aec81d 100644 --- a/src/Plugins/Parallel/Support/CompactPrinter.php +++ b/src/Plugins/Parallel/Support/CompactPrinter.php @@ -36,8 +36,8 @@ final class CompactPrinter '.' => ['gray', '.'], 'S' => ['yellow', 's'], 'T' => ['cyan', 't'], - 'I' => ['yellow', 'i'], - 'N' => ['yellow', 'i'], + 'I' => ['yellow', '!'], + 'N' => ['yellow', '!'], 'D' => ['yellow', '!'], 'R' => ['yellow', '!'], 'W' => ['yellow', '!'], @@ -106,7 +106,9 @@ final class CompactPrinter */ public function errors(State $state): void { - $this->style->writeErrorsSummary($state, false); + $this->output->writeln(''); + + $this->style->writeErrorsSummary($state); } /** diff --git a/src/Support/HigherOrderTapProxy.php b/src/Support/HigherOrderTapProxy.php index 8df75e3a..151b2b80 100644 --- a/src/Support/HigherOrderTapProxy.php +++ b/src/Support/HigherOrderTapProxy.php @@ -6,15 +6,12 @@ namespace Pest\Support; use PHPUnit\Framework\TestCase; use ReflectionClass; -use Throwable; /** * @internal */ final class HigherOrderTapProxy { - private const UNDEFINED_PROPERTY = 'Undefined property: P\\'; // @phpstan-ignore-line - /** * Create a new tap proxy instance. */ @@ -40,16 +37,18 @@ final class HigherOrderTapProxy public function __get(string $property) { if (property_exists($this->target, $property)) { - return $this->target->{$property}; + return $this->target->{$property}; // @phpstan-ignore-line } $className = (new ReflectionClass($this->target))->getName(); - if (str_starts_with($className, "P\\")) { + if (str_starts_with($className, 'P\\')) { $className = substr($className, 2); } trigger_error(sprintf('Undefined property %s::$%s', $className, $property), E_USER_WARNING); + + return null; } /** diff --git a/src/Support/StateGenerator.php b/src/Support/StateGenerator.php index 7d06b7b4..84065703 100644 --- a/src/Support/StateGenerator.php +++ b/src/Support/StateGenerator.php @@ -11,6 +11,7 @@ use PHPUnit\Event\Code\TestMethod; use PHPUnit\Event\Code\Throwable; use PHPUnit\Event\Test\Errored; use PHPUnit\Event\TestData\TestDataCollection; +use PHPUnit\Framework\Exception; use PHPUnit\Framework\IncompleteTestError; use PHPUnit\Framework\SkippedWithMessageException; use PHPUnit\Metadata\MetadataCollection; @@ -18,7 +19,7 @@ use PHPUnit\TestRunner\TestResult\TestResult as PHPUnitTestResult; final class StateGenerator { - public function fromPhpUnitTestResult(PHPUnitTestResult $testResult): State + public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testResult): State { $state = new State(); @@ -74,16 +75,69 @@ final class StateGenerator )); } - $numberOfPassedTests = $testResult->numberOfTestsRun() - - $testResult->numberOfTestErroredEvents() - - $testResult->numberOfTestFailedEvents() - - $testResult->numberOfTestSkippedEvents() - - $testResult->numberOfTestsWithTestConsideredRiskyEvents() - - $testResult->numberOfTestMarkedIncompleteEvents(); + foreach ($testResult->testTriggeredDeprecationEvents() as $testResultEvent) { + $testResultEvent = $testResultEvent[0]; - for ($i = 0; $i < $numberOfPassedTests; $i++) { $state->add(TestResult::fromTestCase( + $testResultEvent->test(), + TestResult::DEPRECATED, + Throwable::from(new Exception($testResultEvent->message())) + )); + } + foreach ($testResult->testTriggeredPhpDeprecationEvents() as $testResultEvent) { + $testResultEvent = $testResultEvent[0]; + + $state->add(TestResult::fromTestCase( + $testResultEvent->test(), + TestResult::DEPRECATED, + Throwable::from(new Exception($testResultEvent->message())) + )); + } + + foreach ($testResult->testTriggeredNoticeEvents() as $testResultEvent) { + $testResultEvent = $testResultEvent[0]; + + $state->add(TestResult::fromTestCase( + $testResultEvent->test(), + TestResult::NOTICE, + Throwable::from(new Exception($testResultEvent->message())) + )); + } + + foreach ($testResult->testTriggeredPhpNoticeEvents() as $testResultEvent) { + $testResultEvent = $testResultEvent[0]; + + $state->add(TestResult::fromTestCase( + $testResultEvent->test(), + TestResult::NOTICE, + Throwable::from(new Exception($testResultEvent->message())) + )); + } + + foreach ($testResult->testTriggeredWarningEvents() as $testResultEvent) { + $testResultEvent = $testResultEvent[0]; + + $state->add(TestResult::fromTestCase( + $testResultEvent->test(), + TestResult::WARN, + Throwable::from(new Exception($testResultEvent->message())) + )); + } + + foreach ($testResult->testTriggeredPhpWarningEvents() as $testResultEvent) { + $testResultEvent = $testResultEvent[0]; + + $state->add(TestResult::fromTestCase( + $testResultEvent->test(), + TestResult::WARN, + Throwable::from(new Exception($testResultEvent->message())) + )); + } + + // for each test that passed, we need to add it to the state + for ($i = 0; $i < $passedTests; $i++) { + $state->add(TestResult::fromTestCase( new TestMethod( /** @phpstan-ignore-next-line */ "$i", diff --git a/tests/.snapshots/Failure.php.inc b/tests/.snapshots/Failure.php.inc index cde5f942..cc3837bd 100644 --- a/tests/.snapshots/Failure.php.inc +++ b/tests/.snapshots/Failure.php.inc @@ -18,7 +18,3 @@ ##teamcity[testIgnored name='build this one.' message='This test was ignored.' details='' flowId='1234'] ##teamcity[testFinished name='build this one.' duration='100000' flowId='1234'] ##teamcity[testSuiteFinished name='Tests/tests/Failure' flowId='1234'] - - Tests: 2 failed, 1 risky, 2 todos, 1 skipped (2 assertions) - Duration: 1.00s - diff --git a/tests/.snapshots/SuccessOnly.php.inc b/tests/.snapshots/SuccessOnly.php.inc index 21e8a362..5629be43 100644 --- a/tests/.snapshots/SuccessOnly.php.inc +++ b/tests/.snapshots/SuccessOnly.php.inc @@ -4,7 +4,3 @@ ##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[testSuiteFinished name='Tests/tests/SuccessOnly' flowId='1234'] - - Tests: 2 passed (2 assertions) - Duration: 1.00s - diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index 81ae5e23..d28df786 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -139,6 +139,10 @@ ✓ it is a test ✓ it uses correct parent class + DEPR Tests\Features\Deprecated + ! deprecated → str_contains(): Passing null to parameter #2 ($needle) of type string is deprecated + ! user deprecated → Since foo 1.0: This is a deprecation description + PASS Tests\Features\Exceptions ✓ it gives access the the underlying expectException ✓ it catch exceptions @@ -646,9 +650,9 @@ ✓ it skips with falsy closure condition ✓ it can be used in higher order tests - PASS Tests\Features\Helpers + WARN Tests\Features\Helpers ✓ it can set/get properties on $this - ✓ it throws error if property do not exist + ! it gets null if property do not exist → Undefined property Tests\Features\Helpers::$wqdwqdqw ✓ it allows to call underlying protected/private methods ✓ it throws error if method do not exist ✓ it can forward unexpected calls to any global function @@ -678,6 +682,9 @@ ✓ it is a test ✓ it is a higher order message test + NOTI Tests\Features\Notices + ! notice → This is a notice description + PASS Tests\Features\PendingHigherOrderTests ✓ get 'foo' ✓ get 'foo' → get 'bar' → expect true → toBeTrue @@ -750,6 +757,10 @@ ↓ something todo later chained and with function body ✓ it does something within a file with a todo + WARN Tests\Features\Warnings + ! warning → Undefined property: P\Tests\Features\Warnings::$fooqwdfwqdfqw + ! user warning → This is a warning description + PASS Tests\Fixtures\DirectoryWithTests\ExampleTest ✓ it example 1 @@ -917,4 +928,4 @@ PASS Tests\Visual\Version ✓ visual snapshot of help command output - Tests: 4 incomplete, 4 todos, 18 skipped, 634 passed (1560 assertions) \ No newline at end of file + Tests: 2 deprecated, 3 warnings, 4 incomplete, 1 notice, 4 todos, 18 skipped, 633 passed (1564 assertions) \ No newline at end of file diff --git a/tests/Features/Notices.php b/tests/Features/Notices.php new file mode 100644 index 00000000..dd245450 --- /dev/null +++ b/tests/Features/Notices.php @@ -0,0 +1,7 @@ +toBeTrue(); +}); diff --git a/tests/Visual/Parallel.php b/tests/Visual/Parallel.php index be8b5c0b..66726d7b 100644 --- a/tests/Visual/Parallel.php +++ b/tests/Visual/Parallel.php @@ -15,6 +15,6 @@ $run = function () { }; test('parallel', function () use ($run) { - expect($run())->toContain('Tests: 4 incomplete, 4 todos, 15 skipped, 627 passed (1551 assertions)') + expect($run())->toContain('Tests: 2 deprecated, 3 warnings, 4 incomplete, 1 notice, 4 todos, 15 skipped, 626 passed (1555 assertions)') ->toContain('Parallel: 3 processes'); });