diff --git a/bin/pest b/bin/pest index f73a728d..875bb9a5 100755 --- a/bin/pest +++ b/bin/pest @@ -4,6 +4,7 @@ use Pest\Kernel; use Pest\Panic; use Pest\TestCaseFilters\GitDirtyTestCaseFilter; +use Pest\TestCaseMethodFilters\NotesTestCaseFilter; use Pest\TestCaseMethodFilters\TodoTestCaseFilter; use Pest\TestSuite; use Symfony\Component\Console\Input\ArgvInput; @@ -17,6 +18,7 @@ use Symfony\Component\Console\Output\ConsoleOutput; $dirty = false; $todo = false; + $notes = false; foreach ($arguments as $key => $value) { if ($value === '--compact') { @@ -43,6 +45,11 @@ use Symfony\Component\Console\Output\ConsoleOutput; unset($arguments[$key]); } + if ($value === '--notes') { + $notes = true; + unset($arguments[$key]); + } + if (str_contains($value, '--teamcity')) { unset($arguments[$key]); $arguments[] = '--no-output'; @@ -81,6 +88,10 @@ use Symfony\Component\Console\Output\ConsoleOutput; $testSuite->tests->addTestCaseMethodFilter(new TodoTestCaseFilter()); } + if ($notes) { + $testSuite->tests->addTestCaseMethodFilter(new NotesTestCaseFilter()); + } + $isDecorated = $input->getParameterOption('--colors', 'always') !== 'never'; $output = new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, $isDecorated); diff --git a/src/Concerns/Testable.php b/src/Concerns/Testable.php index 0560cbb3..a204b728 100644 --- a/src/Concerns/Testable.php +++ b/src/Concerns/Testable.php @@ -29,6 +29,11 @@ trait Testable */ private string $__description; + /** + * The test's notes. + */ + private static array $__latestNotes = []; + /** * The test's latest description. */ @@ -74,6 +79,18 @@ trait Testable */ private array $__snapshotChanges = []; + /** + * Adds a new "note" to the Test Case. + */ + public function note(array|string $note): self + { + $note = is_array($note) ? $note : [$note]; + + self::$__latestNotes = array_merge(self::$__latestNotes, $note); + + return $this; + } + /** * Resets the test case static properties. */ @@ -95,6 +112,7 @@ trait Testable if ($test->hasMethod($name)) { $method = $test->getMethod($name); $this->__description = self::$__latestDescription = $method->description; + self::$__latestNotes = $method->notes; $this->__describing = $method->describing; $this->__test = $method->getClosure($this); } @@ -224,6 +242,7 @@ trait Testable } $this->__description = self::$__latestDescription = $description; + self::$__latestNotes = $method->notes; parent::setUp(); @@ -405,4 +424,12 @@ trait Testable { return self::$__latestDescription; } + + /** + * The latest printable test case notes. + */ + public static function getPrintableTestCaseMethodNotes(): array + { + return self::$__latestNotes; + } } diff --git a/src/Factories/TestCaseMethodFactory.php b/src/Factories/TestCaseMethodFactory.php index 81cb40fb..299e9bae 100644 --- a/src/Factories/TestCaseMethodFactory.php +++ b/src/Factories/TestCaseMethodFactory.php @@ -49,6 +49,13 @@ final class TestCaseMethodFactory */ public bool $todo = false; + /** + * The test's notes. + * + * @var array + */ + public array $notes = []; + /** * The test's datasets. * diff --git a/src/PendingCalls/TestCall.php b/src/PendingCalls/TestCall.php index fb8f72c8..2ae319cf 100644 --- a/src/PendingCalls/TestCall.php +++ b/src/PendingCalls/TestCall.php @@ -364,6 +364,20 @@ final class TestCall return $this; } + /** + * Adds a note to the test. + * + * @param array|string $note + */ + public function note(array|string $note): self + { + $notes = is_array($note) ? $note : [$note]; + + $this->testCaseMethod->notes = array_merge($this->testCaseMethod->notes, $notes); + + return $this; + } + /** * Sets the covered classes or methods. */ diff --git a/src/Plugins/Help.php b/src/Plugins/Help.php index 205610f0..b7d48d62 100644 --- a/src/Plugins/Help.php +++ b/src/Plugins/Help.php @@ -123,6 +123,9 @@ final class Help implements HandlesArguments ], [ 'arg' => '--todos', 'desc' => 'Output to standard output the list of todos', + ], [ + 'arg' => '--notes', + 'desc' => 'Output to standard output tests with notes', ], [ 'arg' => '--retry', 'desc' => 'Run non-passing tests first and stop execution upon first error or failure', diff --git a/src/Plugins/Parallel.php b/src/Plugins/Parallel.php index 3c906522..81fa4d6c 100644 --- a/src/Plugins/Parallel.php +++ b/src/Plugins/Parallel.php @@ -34,7 +34,7 @@ final class Parallel implements HandlesArguments /** * @var string[] */ - private const UNSUPPORTED_ARGUMENTS = ['--todo', '--todos', '--retry']; + private const UNSUPPORTED_ARGUMENTS = ['--todo', '--todos', '--retry', '--notes']; /** * Whether the given command line arguments indicate that the test suite should be run in parallel. @@ -42,6 +42,7 @@ final class Parallel implements HandlesArguments public static function isEnabled(): bool { $argv = new ArgvInput(); + if ($argv->hasParameterOption('--parallel')) { return true; } diff --git a/src/Repositories/TestRepository.php b/src/Repositories/TestRepository.php index b8896f77..466d3226 100644 --- a/src/Repositories/TestRepository.php +++ b/src/Repositories/TestRepository.php @@ -118,7 +118,7 @@ final class TestRepository */ public function get(string $filename): ?TestCaseFactory { - return $this->testCases[$filename]; + return $this->testCases[$filename] ?? null; } /** diff --git a/src/Support/ExceptionTrace.php b/src/Support/ExceptionTrace.php index 0f6dc10b..5ab37f65 100644 --- a/src/Support/ExceptionTrace.php +++ b/src/Support/ExceptionTrace.php @@ -31,7 +31,7 @@ final class ExceptionTrace $message = str_replace(self::UNDEFINED_METHOD, 'Call to undefined method ', $message); if (class_exists((string) $class) && (is_countable(class_parents($class)) ? count(class_parents($class)) : 0) > 0 && array_values(class_parents($class))[0] === TestCase::class) { // @phpstan-ignore-line - $message .= '. Did you forget to use the [uses()] function? Read more at: https://pestphp.com/docs/configuring-tests'; + $message .= '. Did you forget to use the [pest()->uses()] function? Read more at: https://pestphp.com/docs/configuring-tests'; } Reflection::setPropertyValue($throwable, 'message', $message); diff --git a/src/TestCaseMethodFilters/NotesTestCaseFilter.php b/src/TestCaseMethodFilters/NotesTestCaseFilter.php new file mode 100644 index 00000000..6678b659 --- /dev/null +++ b/src/TestCaseMethodFilters/NotesTestCaseFilter.php @@ -0,0 +1,16 @@ +notes) > 0; + } +} diff --git a/tests/Features/Note.php b/tests/Features/Note.php new file mode 100644 index 00000000..1878a024 --- /dev/null +++ b/tests/Features/Note.php @@ -0,0 +1,36 @@ +note('This is before each runtime note'); +})->note('This is before each static note'); + +it('may have a static note', function () { + expect(true)->toBeTrue(); +})->note('This is a note'); + +it('may have a runtime note', function () { + expect(true)->toBeTrue(true); + + $this->note('This is a runtime note'); +}); + +it('may have static note and runtime note', function () { + expect(true)->toBeTrue(true); + + $this->note('This is a runtime note'); +})->note('This is a static note'); + +describe('nested', function () { + it('may have static note and runtime note', function () { + expect(true)->toBeTrue(true); + + $this->note('This is a runtime note within describe'); + })->note('This is a static note within describe'); +})->note('This is describe static note'); + +test('multiple notes', function () { + $this->note([ + 'This is a runtime note', + 'This is another runtime note', + ]); +});