diff --git a/bin/pest b/bin/pest index 79d897c8..7419393f 100755 --- a/bin/pest +++ b/bin/pest @@ -4,6 +4,7 @@ use Pest\Kernel; use Pest\Panic; use Pest\TestCaseFilters\GitDirtyTestCaseFilter; +use Pest\TestCaseMethodFilters\AssigneeTestCaseFilter; use Pest\TestCaseMethodFilters\IssueTestCaseFilter; use Pest\TestCaseMethodFilters\NotesTestCaseFilter; use Pest\TestCaseMethodFilters\PrTestCaseFilter; @@ -59,6 +60,16 @@ use Symfony\Component\Console\Output\ConsoleOutput; unset($arguments[$key]); } + if (str_contains($value, '--assignee=')) { + unset($arguments[$key]); + } else if ($value === '--assignee') { + unset($arguments[$key]); + + if (isset($arguments[$key + 1])) { + unset($arguments[$key + 1]); + } + } + if (str_contains($value, '--issue=')) { unset($arguments[$key]); } else if ($value === '--issue') { @@ -142,6 +153,10 @@ use Symfony\Component\Console\Output\ConsoleOutput; $testSuite->tests->addTestCaseMethodFilter(new NotesTestCaseFilter()); } + if ($assignee = $input->getParameterOption('--assignee')) { + $testSuite->tests->addTestCaseMethodFilter(new AssigneeTestCaseFilter((string) $assignee)); + } + if ($issue = $input->getParameterOption('--issue')) { $testSuite->tests->addTestCaseMethodFilter(new IssueTestCaseFilter((int) $issue)); } diff --git a/src/Collision/Events.php b/src/Collision/Events.php index 4810167d..0f83100f 100644 --- a/src/Collision/Events.php +++ b/src/Collision/Events.php @@ -41,22 +41,32 @@ final class Events renderUsing(self::$output); [ + 'assignees' => $assignees, 'issues' => $issues, 'prs' => $prs, ] = $context; - if ((($link = Context::getInstance()->issues) !== '' && ($link = Context::getInstance()->issues) !== '0')) { + if (($link = Context::getInstance()->issues) !== '') { $issuesDescription = array_map(fn (int $issue): string => sprintf('#%s', sprintf($link, $issue), $issue), $issues); } - if ((($link = Context::getInstance()->prs) !== '' && ($link = Context::getInstance()->prs) !== '0')) { + if (($link = Context::getInstance()->prs) !== '') { $prsDescription = array_map(fn (int $pr): string => sprintf('#%s', sprintf($link, $pr), $pr), $prs); } - if (count($issues) > 0 || count($prs) > 0) { + if (($link = Context::getInstance()->assignees) !== '' && count($assignees) > 0) { + $assigneesDescription = array_map(fn (string $assignee): string => sprintf( + '@%s', + sprintf($link, $assignee), + $assignee, + ), $assignees); + } + + if (count($assignees) > 0 || count($issues) > 0 || count($prs) > 0) { $description .= ' '.implode(', ', array_merge( $issuesDescription ?? [], $prsDescription ?? [], + isset($assigneesDescription) ? ['['.implode(', ', $assigneesDescription).']'] : [], )); } diff --git a/src/Concerns/Testable.php b/src/Concerns/Testable.php index 4db2887d..74c499b0 100644 --- a/src/Concerns/Testable.php +++ b/src/Concerns/Testable.php @@ -34,6 +34,11 @@ trait Testable */ private static string $__latestDescription; + /** + * The test's assignees. + */ + private static array $__latestAssignees = []; + /** * The test's notes. */ @@ -105,6 +110,7 @@ trait Testable if ($test->hasMethod($name)) { $method = $test->getMethod($name); $this->__description = self::$__latestDescription = $method->description; + self::$__latestAssignees = $method->assignees; self::$__latestNotes = $method->notes; self::$__latestIssues = $method->issues; self::$__latestPrs = $method->prs; @@ -258,6 +264,7 @@ trait Testable } $this->__description = self::$__latestDescription = $description; + self::$__latestAssignees = $method->assignees; self::$__latestNotes = $method->notes; self::$__latestIssues = $method->issues; self::$__latestPrs = $method->prs; @@ -449,6 +456,7 @@ trait Testable public static function getPrintableContext(): array { return [ + 'assignees' => self::$__latestAssignees, 'issues' => self::$__latestIssues, 'prs' => self::$__latestPrs, 'notes' => self::$__latestNotes, diff --git a/src/Configuration/Context.php b/src/Configuration/Context.php index 97ae4189..d9098f8c 100644 --- a/src/Configuration/Context.php +++ b/src/Configuration/Context.php @@ -9,6 +9,13 @@ namespace Pest\Configuration; */ final class Context { + /** + * The assignees link. + * + * @internal + */ + public string $assignees = ''; + /** * The issues link. * @@ -44,6 +51,8 @@ final class Context $this->issues = "https://github.com/{$project}/issues/%s"; $this->prs = "https://github.com/{$project}/pull/%s"; + $this->assignees = 'https://github.com/%s'; + return $this; } @@ -55,6 +64,8 @@ final class Context $this->issues = "https://gitlab.com/{$project}/issues/%s"; $this->prs = "https://gitlab.com/{$project}/merge_requests/%s"; + $this->assignees = 'https://gitlab.com/%s'; + return $this; } @@ -66,6 +77,8 @@ final class Context $this->issues = 'https://bitbucket.org/{$project}/issues/%s'; $this->prs = "https://bitbucket.org/{$project}/pull-requests/%s"; + $this->assignees = 'https://bitbucket.org/%s'; + return $this; } @@ -76,17 +89,21 @@ final class Context { $this->issues = "https://{$namespace}.atlassian.net/browse/{$project}-%s"; + $this->assignees = "https://{$namespace}.atlassian.net/secure/ViewProfile?name=%s"; + return $this; } /** * Sets the test context to custom. */ - public function using(string $issues, string $prs): self + public function using(string $issues, string $prs, string $assignees): self { $this->issues = $issues; $this->prs = $prs; + $this->assignees = $assignees; + return $this; } } diff --git a/src/Factories/TestCaseMethodFactory.php b/src/Factories/TestCaseMethodFactory.php index 7374a5a7..7c2e4248 100644 --- a/src/Factories/TestCaseMethodFactory.php +++ b/src/Factories/TestCaseMethodFactory.php @@ -56,6 +56,13 @@ final class TestCaseMethodFactory */ public array $issues = []; + /** + * The test assignees. + * + * @var array + */ + public array $assignees = []; + /** * The associated PRs numbers. * diff --git a/src/PendingCalls/TestCall.php b/src/PendingCalls/TestCall.php index 7b2d7f85..50b041c6 100644 --- a/src/PendingCalls/TestCall.php +++ b/src/PendingCalls/TestCall.php @@ -390,6 +390,18 @@ final class TestCall return $this->issue($number); } + /** + * Sets the test assignee(s). + */ + public function assignee(array|string $assignee): self + { + $assignees = is_array($assignee) ? $assignee : [$assignee]; + + $this->testCaseMethod->assignees = array_unique(array_merge($this->testCaseMethod->assignees, $assignees)); + + return $this; + } + /** * Associates the test with the given pull request(s). * @@ -401,7 +413,7 @@ final class TestCall $number = array_map(fn (string|int $number): int => (int) ltrim((string) $number, '#'), $number); - $this->testCaseMethod->prs = array_merge($this->testCaseMethod->issues, $number); + $this->testCaseMethod->prs = array_unique(array_merge($this->testCaseMethod->issues, $number)); return $this; } @@ -415,7 +427,7 @@ final class TestCall { $notes = is_array($note) ? $note : [$note]; - $this->testCaseMethod->notes = array_merge($this->testCaseMethod->notes, $notes); + $this->testCaseMethod->notes = array_unique(array_merge($this->testCaseMethod->notes, $notes)); return $this; } diff --git a/src/TestCaseMethodFilters/AssigneeTestCaseFilter.php b/src/TestCaseMethodFilters/AssigneeTestCaseFilter.php new file mode 100644 index 00000000..2a775693 --- /dev/null +++ b/src/TestCaseMethodFilters/AssigneeTestCaseFilter.php @@ -0,0 +1,27 @@ +assignees, fn ($assignee): bool => str_starts_with($assignee, $this->assignee)) !== []; + } +} diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index a42466d5..f868a173 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -28,6 +28,11 @@ ✓ it does not get executed before the test ✓ it gets executed after the test + PASS Tests\Features\Assignee + ✓ it may be associated with an assignee [@nunomaduro, @taylorotwell] + ✓ nested → it may be associated with an assignee [@nunomaduro, @jamesbrooks, @joedixon, @taylorotwell] + // an note between an the assignee + PASS Tests\Features\BeforeAll ✓ it gets executed before tests ✓ it do not get executed before each test @@ -1537,4 +1542,4 @@ WARN Tests\Visual\Version - visual snapshot of help command output - Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 13 todos, 24 skipped, 1076 passed (2628 assertions) \ No newline at end of file + Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 13 todos, 24 skipped, 1078 passed (2632 assertions) \ No newline at end of file diff --git a/tests/Features/Assignee.php b/tests/Features/Assignee.php new file mode 100644 index 00000000..7995cb5a --- /dev/null +++ b/tests/Features/Assignee.php @@ -0,0 +1,15 @@ +toBeTrue(); +})->assignee('nunomaduro'); + +it('may be associated with an assignee', function () { + expect(true)->toBeTrue(); +})->assignee('taylorotwell'); + +describe('nested', function () { + it('may be associated with an assignee', function () { + expect(true)->toBeTrue(); + })->assignee('taylorotwell'); +})->assignee('nunomaduro')->note('an note between an the assignee')->assignee(['jamesbrooks', 'joedixon']); diff --git a/tests/Visual/Parallel.php b/tests/Visual/Parallel.php index 53be829f..a6bbb0d3 100644 --- a/tests/Visual/Parallel.php +++ b/tests/Visual/Parallel.php @@ -16,7 +16,7 @@ $run = function () { test('parallel', function () use ($run) { expect($run('--exclude-group=integration')) - ->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 13 todos, 19 skipped, 1062 passed (2596 assertions)') + ->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 13 todos, 19 skipped, 1064 passed (2600 assertions)') ->toContain('Parallel: 3 processes'); })->skipOnWindows();