mirror of
https://github.com/pestphp/pest.git
synced 2026-03-09 09:17:23 +01:00
feat: pr and issue
This commit is contained in:
@ -50,7 +50,7 @@ final class Laravel extends AbstractPreset
|
||||
->classes()
|
||||
->toHaveMethod('handle');
|
||||
|
||||
$this->expectations[] = expect('App\Models')
|
||||
$this->expectations[] = expect('App\Models') // @phpstan-ignore-line
|
||||
->classes()
|
||||
->toExtend('Illuminate\Database\Eloquent\Model')
|
||||
->not->toHaveSuffix('Model');
|
||||
@ -62,7 +62,7 @@ final class Laravel extends AbstractPreset
|
||||
$this->expectations[] = expect('App\Http\Requests')
|
||||
->classes()
|
||||
->toHaveSuffix('Request')
|
||||
->toExtend('Illuminate\Foundation\Http\FormRequest') // @phpstan-ignore-line
|
||||
->toExtend('Illuminate\Foundation\Http\FormRequest')
|
||||
->toHaveMethod('rules');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
@ -72,7 +72,7 @@ final class Laravel extends AbstractPreset
|
||||
$this->expectations[] = expect('App\Console\Commands')
|
||||
->classes()
|
||||
->toHaveSuffix('Command')
|
||||
->toExtend('Illuminate\Console\Command') // @phpstan-ignore-line
|
||||
->toExtend('Illuminate\Console\Command')
|
||||
->toHaveMethod('handle');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
@ -81,7 +81,7 @@ final class Laravel extends AbstractPreset
|
||||
|
||||
$this->expectations[] = expect('App\Mail')
|
||||
->classes()
|
||||
->toExtend('Illuminate\Mail\Mailable'); // @phpstan-ignore-line
|
||||
->toExtend('Illuminate\Mail\Mailable');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toExtend('Illuminate\Mail\Mailable')
|
||||
@ -101,7 +101,7 @@ final class Laravel extends AbstractPreset
|
||||
->toHaveMethod('handle');
|
||||
|
||||
$this->expectations[] = expect('App\Notifications')
|
||||
->toExtend('Illuminate\Notifications\Notification'); // @phpstan-ignore-line
|
||||
->toExtend('Illuminate\Notifications\Notification');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toExtend('Illuminate\Notifications\Notification')
|
||||
@ -109,10 +109,10 @@ final class Laravel extends AbstractPreset
|
||||
|
||||
$this->expectations[] = expect('App\Providers') // @phpstan-ignore-line
|
||||
->toHaveSuffix('ServiceProvider')
|
||||
->toExtend('Illuminate\Support\ServiceProvider') // @phpstan-ignore-line
|
||||
->toExtend('Illuminate\Support\ServiceProvider')
|
||||
->not->toBeUsed();
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
$this->expectations[] = expect('App') // @phpstan-ignore-line
|
||||
->not->toExtend('Illuminate\Support\ServiceProvider')
|
||||
->not->toHaveSuffix('ServiceProvider')
|
||||
->ignoring('App\Providers');
|
||||
|
||||
@ -29,15 +29,29 @@ trait Testable
|
||||
*/
|
||||
private string $__description;
|
||||
|
||||
/**
|
||||
* The test's latest description.
|
||||
*/
|
||||
private static string $__latestDescription;
|
||||
|
||||
/**
|
||||
* The test's notes.
|
||||
*/
|
||||
private static array $__latestNotes = [];
|
||||
|
||||
/**
|
||||
* The test's latest description.
|
||||
* The test's issues.
|
||||
*
|
||||
* @var array<int, int>
|
||||
*/
|
||||
private static string $__latestDescription;
|
||||
private static array $__latestIssues = [];
|
||||
|
||||
/**
|
||||
* The test's PRs.
|
||||
*
|
||||
* @var array<int, int>
|
||||
*/
|
||||
private static array $__latestPrs = [];
|
||||
|
||||
/**
|
||||
* The test's describing, if any.
|
||||
@ -79,27 +93,6 @@ 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.
|
||||
*/
|
||||
public static function flush(): void
|
||||
{
|
||||
self::$__beforeAll = null;
|
||||
self::$__afterAll = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Test Case instance.
|
||||
*/
|
||||
@ -113,11 +106,34 @@ trait Testable
|
||||
$method = $test->getMethod($name);
|
||||
$this->__description = self::$__latestDescription = $method->description;
|
||||
self::$__latestNotes = $method->notes;
|
||||
self::$__latestIssues = $method->issues;
|
||||
self::$__latestPrs = $method->prs;
|
||||
$this->__describing = $method->describing;
|
||||
$this->__test = $method->getClosure($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the test case static properties.
|
||||
*/
|
||||
public static function flush(): void
|
||||
{
|
||||
self::$__beforeAll = null;
|
||||
self::$__afterAll = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new "setUpBeforeClass" to the Test Case.
|
||||
*/
|
||||
@ -243,6 +259,8 @@ trait Testable
|
||||
|
||||
$this->__description = self::$__latestDescription = $description;
|
||||
self::$__latestNotes = $method->notes;
|
||||
self::$__latestIssues = $method->issues;
|
||||
self::$__latestPrs = $method->prs;
|
||||
|
||||
parent::setUp();
|
||||
|
||||
@ -432,4 +450,20 @@ trait Testable
|
||||
{
|
||||
return self::$__latestNotes;
|
||||
}
|
||||
|
||||
/**
|
||||
* The latest printable test case issues.
|
||||
*/
|
||||
public static function getPrintableTestCaseMethodIssues(): array
|
||||
{
|
||||
return self::$__latestIssues;
|
||||
}
|
||||
|
||||
/**
|
||||
* The latest printable test case PRs.
|
||||
*/
|
||||
public static function getPrintableTestCaseMethodPrs(): array
|
||||
{
|
||||
return self::$__latestPrs;
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,4 +75,12 @@ final class Configuration
|
||||
{
|
||||
return new Configuration\Theme();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the context configuration.
|
||||
*/
|
||||
public function context(): Configuration\Context
|
||||
{
|
||||
return new Configuration\Context();
|
||||
}
|
||||
}
|
||||
|
||||
67
src/Configuration/Context.php
Normal file
67
src/Configuration/Context.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Configuration;
|
||||
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\Printers\DefaultPrinter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final readonly class Context
|
||||
{
|
||||
/**
|
||||
* Sets the test context to GitHub.
|
||||
*/
|
||||
public function github(string $project): self
|
||||
{
|
||||
DefaultPrinter::linkIssuesWith("https://github.com/{$project}/issues/%s");
|
||||
DefaultPrinter::linkPrsWith("https://github.com/{$project}/pull/%s");
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test context to GitLab.
|
||||
*/
|
||||
public function gitlab(string $project): self
|
||||
{
|
||||
DefaultPrinter::linkIssuesWith("https://gitlab.com/{$project}/issues/%s");
|
||||
DefaultPrinter::linkPrsWith("https://gitlab.com/{$project}/merge_requests/%s");
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test context to Bitbucket.
|
||||
*/
|
||||
public function bitbucket(string $project): self
|
||||
{
|
||||
DefaultPrinter::linkIssuesWith('https://bitbucket.org/{$project}/issues/%s');
|
||||
DefaultPrinter::linkPrsWith("https://bitbucket.org/{$project}/pull-requests/%s");
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test context to Jira.
|
||||
*/
|
||||
public function jira(string $namespace, string $project): self
|
||||
{
|
||||
DefaultPrinter::linkIssuesWith("https://{$namespace}.atlassian.net/browse/{$project}-%s");
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test context to custom.
|
||||
*/
|
||||
public function using(string $issues, string $prs): self
|
||||
{
|
||||
DefaultPrinter::linkIssuesWith($issues);
|
||||
DefaultPrinter::linkPrsWith($prs);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@ -585,8 +585,6 @@ final class Expectation
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target to be subclass of the given class.
|
||||
*
|
||||
* @param class-string $class
|
||||
*/
|
||||
public function toExtend(string $class): ArchExpectation
|
||||
{
|
||||
@ -660,7 +658,7 @@ final class Expectation
|
||||
/**
|
||||
* Asserts that the given expectation target to only implement the given interfaces.
|
||||
*
|
||||
* @param array<int, class-string>|class-string $interfaces
|
||||
* @param array<int, string>|string $interfaces
|
||||
*/
|
||||
public function toOnlyImplement(array|string $interfaces): ArchExpectation
|
||||
{
|
||||
@ -704,7 +702,7 @@ final class Expectation
|
||||
/**
|
||||
* Asserts that the given expectation target to implement the given interfaces.
|
||||
*
|
||||
* @param array<int, class-string>|class-string $interfaces
|
||||
* @param array<int, string>|string $interfaces
|
||||
*/
|
||||
public function toImplement(array|string $interfaces): ArchExpectation
|
||||
{
|
||||
@ -888,8 +886,6 @@ final class Expectation
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target to have the given attribute.
|
||||
*
|
||||
* @param class-string<Attribute> $attribute
|
||||
*/
|
||||
public function toHaveAttribute(string $attribute): ArchExpectation
|
||||
{
|
||||
|
||||
@ -226,8 +226,6 @@ final class OppositeExpectation
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target to be not subclass of the given class.
|
||||
*
|
||||
* @param class-string $class
|
||||
*/
|
||||
public function toExtend(string $class): ArchExpectation
|
||||
{
|
||||
@ -288,7 +286,7 @@ final class OppositeExpectation
|
||||
/**
|
||||
* Asserts that the given expectation target not to implement the given interfaces.
|
||||
*
|
||||
* @param array<int, class-string>|string $interfaces
|
||||
* @param array<int, string>|string $interfaces
|
||||
*/
|
||||
public function toImplement(array|string $interfaces): ArchExpectation
|
||||
{
|
||||
@ -421,8 +419,6 @@ final class OppositeExpectation
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target not to have the given attribute.
|
||||
*
|
||||
* @param class-string<Attribute> $attribute
|
||||
*/
|
||||
public function toHaveAttribute(string $attribute): ArchExpectation
|
||||
{
|
||||
|
||||
@ -49,6 +49,20 @@ final class TestCaseMethodFactory
|
||||
*/
|
||||
public bool $todo = false;
|
||||
|
||||
/**
|
||||
* The associated issue numbers.
|
||||
*
|
||||
* @var array<int, int>
|
||||
*/
|
||||
public array $issues = [];
|
||||
|
||||
/**
|
||||
* The associated PRs numbers.
|
||||
*
|
||||
* @var array<int, int>
|
||||
*/
|
||||
public array $prs = [];
|
||||
|
||||
/**
|
||||
* The test's notes.
|
||||
*
|
||||
|
||||
@ -18,6 +18,11 @@ final class DescribeCall
|
||||
*/
|
||||
private static ?string $describing = null;
|
||||
|
||||
/**
|
||||
* The describe "before each" call.
|
||||
*/
|
||||
private ?BeforeEachCall $currentBeforeEachCall = null;
|
||||
|
||||
/**
|
||||
* Creates a new Pending Call.
|
||||
*/
|
||||
@ -43,6 +48,8 @@ final class DescribeCall
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
unset($this->currentBeforeEachCall);
|
||||
|
||||
self::$describing = $this->description;
|
||||
|
||||
try {
|
||||
@ -57,14 +64,18 @@ final class DescribeCall
|
||||
*
|
||||
* @param array<int, mixed> $arguments
|
||||
*/
|
||||
public function __call(string $name, array $arguments): BeforeEachCall
|
||||
public function __call(string $name, array $arguments): self
|
||||
{
|
||||
$filename = Backtrace::file();
|
||||
|
||||
$beforeEachCall = new BeforeEachCall(TestSuite::getInstance(), $filename);
|
||||
if (! $this->currentBeforeEachCall instanceof \Pest\PendingCalls\BeforeEachCall) {
|
||||
$this->currentBeforeEachCall = new BeforeEachCall(TestSuite::getInstance(), $filename);
|
||||
|
||||
$beforeEachCall->describing = $this->description;
|
||||
$this->currentBeforeEachCall->describing = $this->description;
|
||||
}
|
||||
|
||||
return $beforeEachCall->{$name}(...$arguments); // @phpstan-ignore-line
|
||||
$this->currentBeforeEachCall->{$name}(...$arguments); // @phpstan-ignore-line
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,6 +364,48 @@ final class TestCall
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates the test with the given issue(s).
|
||||
*
|
||||
* @param array<int, string|int>|string|int $number
|
||||
*/
|
||||
public function issue(array|string|int $number): self
|
||||
{
|
||||
$number = is_array($number) ? $number : [$number];
|
||||
|
||||
$number = array_map(fn (string|int $number): int => (int) ltrim((string) $number, '#'), $number);
|
||||
|
||||
$this->testCaseMethod->issues = array_merge($this->testCaseMethod->issues, $number);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates the test with the given ticket(s). (Alias for `issue`)
|
||||
*
|
||||
* @param array<int, string|int>|string|int $number
|
||||
*/
|
||||
public function ticket(array|string|int $number): self
|
||||
{
|
||||
return $this->issue($number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates the test with the given pull request(s).
|
||||
*
|
||||
* @param array<int, string|int>|string|int $number
|
||||
*/
|
||||
public function pr(array|string|int $number): self
|
||||
{
|
||||
$number = is_array($number) ? $number : [$number];
|
||||
|
||||
$number = array_map(fn (string|int $number): int => (int) ltrim((string) $number, '#'), $number);
|
||||
|
||||
$this->testCaseMethod->prs = array_merge($this->testCaseMethod->issues, $number);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a note to the test.
|
||||
*
|
||||
|
||||
@ -21,7 +21,7 @@ trait HandleArguments
|
||||
return true;
|
||||
}
|
||||
|
||||
if (str_starts_with((string) $arg, "$argument=")) {
|
||||
if (str_starts_with((string) $arg, "$argument=")) { // @phpstan-ignore-line
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +126,18 @@ final class Help implements HandlesArguments
|
||||
], [
|
||||
'arg' => '--notes',
|
||||
'desc' => 'Output to standard output tests with notes',
|
||||
], [
|
||||
], [
|
||||
'arg' => '--issue',
|
||||
'desc' => 'Output to standard output tests with the given issue number',
|
||||
], [
|
||||
], [
|
||||
'arg' => '--pr',
|
||||
'desc' => 'Output to standard output tests with the given pull request number',
|
||||
], [
|
||||
], [
|
||||
'arg' => '--pull-request',
|
||||
'desc' => 'Output to standard output tests with the given pull request number (alias for --pr)',
|
||||
], [
|
||||
'arg' => '--retry',
|
||||
'desc' => 'Run non-passing tests first and stop execution upon first error or failure',
|
||||
|
||||
@ -34,7 +34,7 @@ final class Parallel implements HandlesArguments
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private const UNSUPPORTED_ARGUMENTS = ['--todo', '--todos', '--retry', '--notes'];
|
||||
private const UNSUPPORTED_ARGUMENTS = ['--todo', '--todos', '--retry', '--notes', '--issue', '--pr', '--pull-request'];
|
||||
|
||||
/**
|
||||
* Whether the given command line arguments indicate that the test suite should be run in parallel.
|
||||
|
||||
@ -198,6 +198,8 @@ final class Coverage
|
||||
|
||||
$array = [];
|
||||
foreach (array_filter($file->lineCoverageData(), is_array(...)) as $line => $tests) {
|
||||
assert(is_array($tests));
|
||||
|
||||
$array = $eachLine($array, $tests, $line);
|
||||
}
|
||||
|
||||
|
||||
27
src/TestCaseMethodFilters/IssueTestCaseFilter.php
Normal file
27
src/TestCaseMethodFilters/IssueTestCaseFilter.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\TestCaseMethodFilters;
|
||||
|
||||
use Pest\Contracts\TestCaseMethodFilter;
|
||||
use Pest\Factories\TestCaseMethodFactory;
|
||||
|
||||
final readonly class IssueTestCaseFilter implements TestCaseMethodFilter
|
||||
{
|
||||
/**
|
||||
* Create a new filter instance.
|
||||
*/
|
||||
public function __construct(private int $number)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the test case methods.
|
||||
*/
|
||||
public function accept(TestCaseMethodFactory $factory): bool
|
||||
{
|
||||
return in_array($this->number, $factory->issues, true);
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@ namespace Pest\TestCaseMethodFilters;
|
||||
use Pest\Contracts\TestCaseMethodFilter;
|
||||
use Pest\Factories\TestCaseMethodFactory;
|
||||
|
||||
final class NotesTestCaseFilter implements TestCaseMethodFilter
|
||||
final readonly class NotesTestCaseFilter implements TestCaseMethodFilter
|
||||
{
|
||||
public function accept(TestCaseMethodFactory $factory): bool
|
||||
{
|
||||
|
||||
27
src/TestCaseMethodFilters/PrTestCaseFilter.php
Normal file
27
src/TestCaseMethodFilters/PrTestCaseFilter.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\TestCaseMethodFilters;
|
||||
|
||||
use Pest\Contracts\TestCaseMethodFilter;
|
||||
use Pest\Factories\TestCaseMethodFactory;
|
||||
|
||||
final readonly class PrTestCaseFilter implements TestCaseMethodFilter
|
||||
{
|
||||
/**
|
||||
* Create a new filter instance.
|
||||
*/
|
||||
public function __construct(private int $number)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the test case methods.
|
||||
*/
|
||||
public function accept(TestCaseMethodFactory $factory): bool
|
||||
{
|
||||
return in_array($this->number, $factory->prs, true);
|
||||
}
|
||||
}
|
||||
@ -7,8 +7,11 @@ namespace Pest\TestCaseMethodFilters;
|
||||
use Pest\Contracts\TestCaseMethodFilter;
|
||||
use Pest\Factories\TestCaseMethodFactory;
|
||||
|
||||
final class TodoTestCaseFilter implements TestCaseMethodFilter
|
||||
final readonly class TodoTestCaseFilter implements TestCaseMethodFilter
|
||||
{
|
||||
/**
|
||||
* Filter the test case methods.
|
||||
*/
|
||||
public function accept(TestCaseMethodFactory $factory): bool
|
||||
{
|
||||
return $factory->todo;
|
||||
|
||||
Reference in New Issue
Block a user