From f385a10f5e095367de0f67c247d0cd7881ee7f7b Mon Sep 17 00:00:00 2001 From: markhuot Date: Tue, 8 Oct 2024 14:38:09 -0400 Subject: [PATCH 1/6] cleans up and better console testing --- src/console/PestController.php | 9 +++++++ src/console/TestableResponse.php | 31 ++++++++++++++++++--- src/factories/Category.php | 1 + src/factories/Entry.php | 3 ++- src/factories/User.php | 1 + src/io/Buffer.php | 36 +++++++++++++++++++++++++ src/test/ExecuteConsoleCommands.php | 42 +++++++++++++++++++++++++++++ src/test/TestCase.php | 17 +----------- tests/ConsoleCommandTest.php | 9 +++++++ 9 files changed, 128 insertions(+), 21 deletions(-) create mode 100644 src/io/Buffer.php create mode 100644 src/test/ExecuteConsoleCommands.php create mode 100644 tests/ConsoleCommandTest.php diff --git a/src/console/PestController.php b/src/console/PestController.php index 4e46c2a..cdcfaf8 100644 --- a/src/console/PestController.php +++ b/src/console/PestController.php @@ -194,4 +194,13 @@ public function actionSeed($seeder = null): int return 0; } + + // Internal method for testing purposes, not public + public function actionInternal() + { + $this->stdout('stdout'); + $this->stderr('stderr'); + + return ExitCode::OK; + } } diff --git a/src/console/TestableResponse.php b/src/console/TestableResponse.php index 0b9314e..e9cff6e 100644 --- a/src/console/TestableResponse.php +++ b/src/console/TestableResponse.php @@ -7,13 +7,36 @@ class TestableResponse { public function __construct( - protected int $exitCode, - protected string $stdout, - protected string $stderr, + public int $exitCode, + public string $stdout, + public string $stderr, ) {} - public function assertSuccesful() + public function assertSuccesful(): self { Assert::assertSame(0, $this->exitCode); + + return $this; + } + + public function assertExitCode(int $exitCode): self + { + Assert::assertSame($exitCode, $this->exitCode); + + return $this; + } + + public function assertSee(string $text): self + { + Assert::assertStringContainsString($text, $this->stdout.$this->stderr); + + return $this; + } + + public function assertDontSee(string $text): self + { + Assert::assertStringNotContainsString($text, $this->stdout.$this->stderr); + + return $this; } } diff --git a/src/factories/Category.php b/src/factories/Category.php index dd8bf74..2324703 100644 --- a/src/factories/Category.php +++ b/src/factories/Category.php @@ -4,6 +4,7 @@ /** * @method self title(string $title) + * * @extends Factory<\craft\elements\Category> */ class Category extends Element diff --git a/src/factories/Entry.php b/src/factories/Entry.php index 0b363ec..e99a106 100644 --- a/src/factories/Entry.php +++ b/src/factories/Entry.php @@ -24,6 +24,7 @@ * @phpstan-ignore-next-line ignored because the file is generated * * @mixin FactoryFields + * * @extends Factory<\craft\elements\Entry> */ class Entry extends Element @@ -105,7 +106,7 @@ public function setDateField($key, $value) $this->attributes[$key] = $value; } - public function isProvisionalDraft($provisionalDraft=true): self + public function isProvisionalDraft($provisionalDraft = true): self { $this->attributes['isProvisionalDraft'] = $provisionalDraft; diff --git a/src/factories/User.php b/src/factories/User.php index 0899163..d989ed1 100644 --- a/src/factories/User.php +++ b/src/factories/User.php @@ -4,6 +4,7 @@ /** * @method self admin(bool $isAdmin) + * * @extends Factory<\craft\elements\User> */ class User extends Element diff --git a/src/io/Buffer.php b/src/io/Buffer.php new file mode 100644 index 0000000..7b4ac5c --- /dev/null +++ b/src/io/Buffer.php @@ -0,0 +1,36 @@ +streamName = match ($this->filtername) { + 'craftpest.buffer.stdout' => 'stdout', + 'craftpest.buffer.stderr' => 'stderr', + default => throw new \RuntimeException('Unknown stream'), + }; + + return true; + } + + public function filter($in, $out, &$consumed, $closing): int + { + while ($bucket = stream_bucket_make_writeable($in)) { + if ($this->streamName === 'stdout') { + test()->storeStdOut($bucket->data); + } else { + test()->storeStdErr($bucket->data); + } + + $bucket->data = ''; + $consumed += $bucket->datalen; + stream_bucket_append($out, $bucket); + } + + return PSFS_PASS_ON; + } +} diff --git a/src/test/ExecuteConsoleCommands.php b/src/test/ExecuteConsoleCommands.php new file mode 100644 index 0000000..99e4f80 --- /dev/null +++ b/src/test/ExecuteConsoleCommands.php @@ -0,0 +1,42 @@ +stdout .= $out; + } + + public function storeStdErr(string $err): void + { + $this->stderr .= $err; + } + + public function console(string $className, string $action, array $params = []): TestableResponse + { + $stdout = stream_filter_append(\STDOUT, 'craftpest.buffer.stdout'); + $stderr = stream_filter_append(\STDERR, 'craftpest.buffer.stderr'); + + $controller = new $className('id', \Craft::$app); + $exitCode = call_user_func_array([$controller, 'action'.ucfirst($action)], $params); + + stream_filter_remove($stdout); + stream_filter_remove($stderr); + + return new TestableResponse($exitCode, $this->stdout, $this->stderr); + } +} diff --git a/src/test/TestCase.php b/src/test/TestCase.php index 5eea605..d8d6fa7 100644 --- a/src/test/TestCase.php +++ b/src/test/TestCase.php @@ -6,7 +6,6 @@ use craft\helpers\App; use Illuminate\Support\Collection; use markhuot\craftpest\actions\CallSeeders; -use markhuot\craftpest\console\TestableResponse; use markhuot\craftpest\interfaces\RenderCompiledClassesInterface; use Symfony\Component\Process\Process; @@ -18,6 +17,7 @@ class TestCase extends \PHPUnit\Framework\TestCase CookieState, DatabaseAssertions, Dd, + ExecuteConsoleCommands, Mocks, RequestBuilders, SnapshotAssertions, @@ -233,21 +233,6 @@ public function seed(callable|string ...$seeders): self return $this; } - public function console(array|string $command) - { - if (! is_array($command)) { - $command = [$command]; - } - - $craft = getenv('CRAFT_EXE_PATH') ?: './craft'; - $process = new Process([$craft, ...$command]); - $exitCode = $process->run(); - $stdout = $process->getOutput(); - $stderr = $process->getErrorOutput(); - - return new TestableResponse($exitCode, $stdout, $stderr); - } - public function renderTemplate(...$args) { $content = Craft::$app->getView()->renderTemplate(...$args); diff --git a/tests/ConsoleCommandTest.php b/tests/ConsoleCommandTest.php new file mode 100644 index 0000000..d56c94f --- /dev/null +++ b/tests/ConsoleCommandTest.php @@ -0,0 +1,9 @@ +console(PestController::class, 'internal') + ->assertSee('stdout') + ->assertSee('stderr') + ->assertDontSee('missing'); From a53889b00773b20340ba3cb2898a7a1d53e50ec4 Mon Sep 17 00:00:00 2001 From: markhuot Date: Tue, 8 Oct 2024 14:39:56 -0400 Subject: [PATCH 2/6] more tests --- tests/ConsoleCommandTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/ConsoleCommandTest.php b/tests/ConsoleCommandTest.php index d56c94f..f193e17 100644 --- a/tests/ConsoleCommandTest.php +++ b/tests/ConsoleCommandTest.php @@ -4,6 +4,15 @@ it('executes console commands') ->console(PestController::class, 'internal') + ->assertSuccesful() ->assertSee('stdout') ->assertSee('stderr') ->assertDontSee('missing'); + +it('gets stdout and stderr', function () { + $response = $this->console(PestController::class, 'internal'); + + expect($response->exitCode)->toBe(0); + expect($response->stdout)->toContain('stdout'); + expect($response->stderr)->toContain('stderr'); +}); From e2e9bdcdcbb21a974d98665f338cd5b46e40e7cb Mon Sep 17 00:00:00 2001 From: markhuot Date: Tue, 8 Oct 2024 14:49:14 -0400 Subject: [PATCH 3/6] docs --- .github/workflows/docs.yml | 3 +- src/behaviors/TestableResponseBehavior.php | 2 +- src/console/TestableResponse.php | 48 ++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 441a54b..83eef8b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -41,10 +41,11 @@ jobs: php src/bin/generate-docs.php src/factories/Asset.php docs/factories/asset.md php src/bin/generate-docs.php src/dom/NodeList.php docs/dom/node-list.md php src/bin/generate-docs.php src/dom/Form.php docs/dom/forms.md - php src/bin/generate-docs.php src/behaviors/TestableResponseBehavior.php docs/assertions/response.md + php src/bin/generate-docs.php src/behaviors/TestableResponseBehavior.php docs/assertions/http-response.md php src/bin/generate-docs.php src/behaviors/TestableElementBehavior.php docs/assertions/element.md php src/bin/generate-docs.php src/test/DatabaseAssertions.php docs/assertions/database.md php src/bin/generate-docs.php src/test/RequestBuilders.php docs/making-requests.md + php src/bin/generate-docs.php src/console/TestableResponse.php docs/assertions/console-response.md php src/bin/generate-docs.php src/web/BenchmarkResult.php docs/assertions/benchmark.md php src/bin/generate-docs.php src/test/CookieState.php docs/cookies.md php src/bin/generate-docs.php src/test/ActingAs.php docs/logging-in.md diff --git a/src/behaviors/TestableResponseBehavior.php b/src/behaviors/TestableResponseBehavior.php index b921adb..b7d41a6 100644 --- a/src/behaviors/TestableResponseBehavior.php +++ b/src/behaviors/TestableResponseBehavior.php @@ -17,7 +17,7 @@ use yii\base\Behavior; /** - * # Response Assertions + * # HTTP Response Assertions * * A testable response is returned whenever you perform a HTTP request * with Pest. It is an extension of Craft's native Response with a diff --git a/src/console/TestableResponse.php b/src/console/TestableResponse.php index e9cff6e..9e63933 100644 --- a/src/console/TestableResponse.php +++ b/src/console/TestableResponse.php @@ -4,6 +4,12 @@ use PHPUnit\Framework\Assert; +/** + * # Console Response Assertions + * + * A testable response is returned when running a console command action. This class provides a fluent interface for + * asserting on the response. + */ class TestableResponse { public function __construct( @@ -12,6 +18,13 @@ public function __construct( public string $stderr, ) {} + /** + * Assert that the console command exited successfully (with a zero exit code). + * + * ```php + * $this->command(ConsoleController::class, 'actionName')->assertSuccessful(); + * ``` + */ public function assertSuccesful(): self { Assert::assertSame(0, $this->exitCode); @@ -19,6 +32,27 @@ public function assertSuccesful(): self return $this; } + /** + * Assert that the console command failed (with a non-zero exit code). + * + * ```php + * $this->command(ConsoleController::class, 'actionName')->assertFailed(); + * ``` + */ + public function assertFailed(): self + { + Assert::assertNotSame(0, $this->exitCode); + + return $this; + } + + /** + * Assert the integer exit code + * + * ```php + * $this->command(ConsoleController::class, 'actionName')->assertExitCode(1337); + * ``` + */ public function assertExitCode(int $exitCode): self { Assert::assertSame($exitCode, $this->exitCode); @@ -26,6 +60,13 @@ public function assertExitCode(int $exitCode): self return $this; } + /** + * Assert that the command contains the passed text in stdout or stderr + * + * ```php + * $this->command(ConsoleController::class, 'actionName')->assertSee('text output'); + * ``` + */ public function assertSee(string $text): self { Assert::assertStringContainsString($text, $this->stdout.$this->stderr); @@ -33,6 +74,13 @@ public function assertSee(string $text): self return $this; } + /** + * Assert that the command does not contain the passed text in stdout or stderr + * + * ```php + * $this->command(ConsoleController::class, 'actionName')->assertDontSee('text output'); + * ``` + */ public function assertDontSee(string $text): self { Assert::assertStringNotContainsString($text, $this->stdout.$this->stderr); From e91f0c036ae4813574633c3df80993567fa8af43 Mon Sep 17 00:00:00 2001 From: markhuot Date: Tue, 8 Oct 2024 18:49:38 +0000 Subject: [PATCH 4/6] GitHub action updating docs --- docs/assertions/console-response.md | 39 ++++ docs/assertions/http-response.md | 329 ++++++++++++++++++++++++++++ 2 files changed, 368 insertions(+) create mode 100644 docs/assertions/console-response.md create mode 100644 docs/assertions/http-response.md diff --git a/docs/assertions/console-response.md b/docs/assertions/console-response.md new file mode 100644 index 0000000..59d496e --- /dev/null +++ b/docs/assertions/console-response.md @@ -0,0 +1,39 @@ +# Console Response Assertions + +A testable response is returned when running a console command action. This class provides a fluent interface for +asserting on the response. + +## assertSuccesful() +Assert that the console command exited successfully (with a zero exit code). + +```php +$this->command(ConsoleController::class, 'actionName')->assertSuccessful(); +``` + +## assertFailed() +Assert that the console command failed (with a non-zero exit code). + +```php +$this->command(ConsoleController::class, 'actionName')->assertFailed(); +``` + +## assertExitCode(int $exitCode) +Assert the integer exit code + +```php +$this->command(ConsoleController::class, 'actionName')->assertExitCode(1337); +``` + +## assertSee(string $text) +Assert that the command contains the passed text in stdout or stderr + +```php +$this->command(ConsoleController::class, 'actionName')->assertSee('text output'); +``` + +## assertDontSee(string $text) +Assert that the command does not contain the passed text in stdout or stderr + +```php +$this->command(ConsoleController::class, 'actionName')->assertDontSee('text output'); +``` diff --git a/docs/assertions/http-response.md b/docs/assertions/http-response.md new file mode 100644 index 0000000..8b701e3 --- /dev/null +++ b/docs/assertions/http-response.md @@ -0,0 +1,329 @@ +# HTTP Response Assertions + +A testable response is returned whenever you perform a HTTP request +with Pest. It is an extension of Craft's native Response with a +number of convience methods added for testing. For example, most +tests will perform a `get()` and want to check that the response did +not return an error. You may use `->assertOk()` to check that the +status code was 200. + +## getRequest() +Get the requesr that triggered this reaponse. + +## getJsonContent() +Fetch the response body as a JSON array. This can be run through the expectation API +as well for a fluent chain, + +```php +$response->expect()->jsonContent->toBe(['foo' => 'bar']); +``` + +## querySelector(string $selector) +If the response returns HTML you can `querySelector()` to inspect the +HTML for specific content. The `querySelector()` method takes a +CSS selector to look for (just like in Javascript). + +The return from `querySelector()` is always a `NodeList` containing zero +or more nodes. You can interact with the `NodeList` regardless of the return +and you will get back a scalar value or a collection of values. + +```php +$response->querySelector('h1')->text; // returns the string contents of the h1 element +$response->querySelector('li')->text; // returns a collection containing the text of all list items +``` + +## form(?string $selector = NULL) +The entry point for interactions with forms. This returns a testable +implementaion of the [Symfony DomCrawler's Form](#) class. + +If a response only has one form you may call `->form()` without any parameters +to get the only form in the response. If the response contains more than +one form then you must pass in a selector matching a specific form. + +To submit the form use `->submit()` or `->click('.button-selector')`. + +## expectSelector(string $selector) +Runs the same `querySelector()` against the response's HTML but instead +of returning a `NodeList` it returns an expectation against the `NodeList`. +This allows you to use Pest's expectation API against the found nodes. + +```php +$response->expectSelector('h1')->text->toBe('Hello World!'); +``` + +## assertCacheTag(string $tags) +Check that a response contains the given cache tag header. This is commonly used on +edge-side CDNs to "tag" pages with a unique identifier so that they can be purged +from the cache by that unique tag later. In Craft this will usually be something like +the element ID of any entries being rendered. + +```php +$response->assertCacheTag('my-tag'); +$response->assertCacheTag('el1234'); +``` + +## assertCookie(string $name, ?string $value = NULL) +Checks that the response contains the given cookie. When not passed a value +the assertion only checks the presence of the cookie. When passed a value the +value will be checked for strict equality. + +```php +$response->assertCookie('cookieName'); // checks presence, with any value +$response->assertCookie('cookieName', 'cookie value'); // checks that the values match +``` + +## assertCookieExpired(string $name) +Checks that the given cookie has an expiration in the past. Cookies are sent in headers and if left +unset a cookie will persist from request to request. Therefore, the only way to "remove" a cookie +is to set its expiration to a date in the past (negative number). This is common when logging people out. + +```php +$response->assertCookieExpired('cookieName'); +``` + +## assertCookieNotExpired(string $name) +Checks that the given cookie has an expiration in the future. + +```php +$response->assertCookieNotExpired('cookieName'); +``` + +## assertCookieMissing(string $name) +Checks that the given cookie is not present in the response + +```php +$response->assertCookieMissing('cookieName'); +``` + +## assertCreated() +Checks that the response has a 201 Created status code + +```php +$response->assertCreated(); +``` + +## assertDontSee(string $text) +Checks that the given string does not appear in thr response. + +```php +$response->assertDontSee('text that should not be in the response'); +``` + +## assertDontSeeText(string $text) +Checks that the given string does not appear in the response after first stripping all non-text elements (like HTML) from the response. +For example, if the response contains `foo bar` you could check against the text `foo bar` because the `` will be stripped. + +```php +$response->assertDontSeeText('foo bar'); +``` + +## assertDownload(?string $filename = NULL) +Checks that the response contains a file download, optionally checking that the filename of the download +matches the given filename. + +```php +$response->assertDownload(); // checks that any download is returned +$response->assertDownload('file.jpg'); // checks that a download with the name `file.jpg` is returned +``` + +## assertExactJson(array $data) +Checks that the given JSON exactly matches the returned JSON using PHPUnit's "canonicalizing" logic to +validate the objects. + +```php +$response->assertExactJson(['foo' => 'bar']); +``` + +## assertForbidden() +Checks that the response has a 403 Forbidden status code + +```php +$response->assertForbidden(); +``` + +## assertHeader(string $name, ?string $expected = NULL) +Checks that the given header is present in the response and, if provided, that the value of the +header matches the given value. + +```php +$response->assertHeader('x-foo'); // checks for presence of header, with any value +$response->assertHeader('x-foo', 'bar'); // checks for header with matching value +``` + +## assertHeaderMissing(string $name) +Checks that the response headers do not contain the given header. + +```php +$response->assertHeaderMissing('x-foo'); +``` + +## assertLocation(string $location, ?array $checkParts = NULL) +Checks that the location header matches the given location + +```php +$response->assertLocation('/foo/bar'); +``` + +By default the full location will be checked including the path, +host, port, etc... If you would like to check only a portion of +the location you can pass in an array of keys in the second +parameter. The keys take their names from PHP's [`parse_url`](https://www.php.net/parse_url) +function. + +```php +$response->assertLocation('/foo', ['host', 'path']); +``` + +## assertLocationPath(string $uri) +Assert that the given path marches the path of the returned +`location` header. The other parts of the location, like the +host name, are ignored. + +```php +$response->assertLocationPath('/foo'); +``` + +## assertFlash(?string $message = NULL, ?string $key = NULL) +Check that the given message/key is present in the flashed data. + +```php +$response->assertFlash('The title is required'); +$response->assertFlash('Field is required', 'title'); +``` + +## assertNoContent($status = 204) +Check that the response has the given status code and no content. + +```php +$response->assertNoContent(); +``` + +## assertNotFound() +Check that the response returns a 404 Not Found status code + +```php +$response->assertNotFound(); +``` + +## assertOk() +Check that the response returns a 200 OK status code + +```php +$response->assertOk(); +``` + +## assertRedirect() +Check that the response returns a 300 status code + +```php +$response->assertRedirect(); +``` + +## assertRedirectTo(string $location) +A sugar method that checks the status code as well as the location of the redirect. + +```php +$response->assertRedirectTo('/foo/bar'); +``` + +## followRedirect() +For a 300 class response with a `Location` header, trigger a new +request for the redirected page. + +```php +$response->assertRedirect()->followRedirect()->assertOk(); +``` + +## followRedirects() +For a 300 class response with a `Location` header, trigger a new +request for the redirected page. If the redirected page also contains +a redirect, follow the resulting redirects until you reach a non-300 +response code. + +```php +$response->assertRedirect()->followRedirects()->assertOk(); +``` + +## assertSee(string $text) +Checks that the response contains the given text + +```php +$response->assertSee('foo bar'); +``` + +## assertSeeInOrder(array $texts) +Checks that the response contains the given text, in successive order + +```php +$response->assertSee(['first', 'second', 'third']); +``` + +## assertSeeText(string $text) +Checks that the response contains the given text stripping tags. This would +pass against source code of `foo bar` + +```php +$response->assertSeeText('foo bar'); +``` + +## assertSeeTextInOrder(array $texts) +Checks that the response contains the given text, in successive order +while stripping tags. + +```php +$response->assertSeeTextInOrder(['first', 'second', 'third']); +``` + +## assertStatus($code) +Asserts the given status code matches the response status code. + +```php +$response->assertStatus(404); +``` + +## assertSuccessful() +Asserts a successfull (200-class) response code. + +## assertTitle(string $title) +Assert the given title matches the title of the page. + +```php +$response->assertTitle('The Title'); +``` + +## assertUnauthorized() +Asserts that the response's status code is 401 + +## beginBenchmark() +Benchmarks are started on your test case by calling `->beginBenchmark()`. You are +free to start as many benchmarks as needed, however, note that starting a new +benchmark will clear out any existing benchmarks already in progress. + +> **Warning** +> In order to use a benchmark you must enable Craft's `devMode` (which +will enable the Yii Debug Bar). + +## endBenchmark() +Ending a benchmark returns a testable Benchmark class. You can end a benchmark +by calling `->endBenchmark()` on the test case or on a response. Either of the +following will work, + +```php +it('ends on the test case', function () { + $this->beginBenchmark(); + $this->get('/'); + $benchmark = $this->endBenchmark(); +}); +``` + +```php +it('ends on the response', function () { + $this->beginBenchmark() + ->get('/') + ->endBenchmark(); +}); +``` + +> **Note** +> Unlike the traditional Craft request/response lifecycle you are +free to make multiple requests in a single benchmark. From c48e8d990379c8754ab6cffff25349edbeb4b2d2 Mon Sep 17 00:00:00 2001 From: markhuot Date: Tue, 8 Oct 2024 15:00:49 -0400 Subject: [PATCH 5/6] phpstan fixes --- src/factories/Asset.php | 2 +- src/factories/Category.php | 2 +- src/factories/Element.php | 3 +++ src/factories/MatrixField.php | 4 +++- src/factories/User.php | 2 +- src/io/Buffer.php | 2 ++ 6 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/factories/Asset.php b/src/factories/Asset.php index 573823b..a767cdd 100644 --- a/src/factories/Asset.php +++ b/src/factories/Asset.php @@ -25,7 +25,7 @@ * > **Note** * > Any assets created during a test will be cleaned up and deleted after the test. * - * @extends Factory<\craft\elements\Asset> + * @extends Element<\craft\elements\Asset> */ class Asset extends Element { diff --git a/src/factories/Category.php b/src/factories/Category.php index 2324703..80f8008 100644 --- a/src/factories/Category.php +++ b/src/factories/Category.php @@ -5,7 +5,7 @@ /** * @method self title(string $title) * - * @extends Factory<\craft\elements\Category> + * @extends Element<\craft\elements\Category> */ class Category extends Element { diff --git a/src/factories/Element.php b/src/factories/Element.php index 90908a7..e4b1a21 100644 --- a/src/factories/Element.php +++ b/src/factories/Element.php @@ -10,6 +10,9 @@ use function markhuot\craftpest\helpers\base\array_wrap; use function markhuot\craftpest\helpers\base\collection_wrap; +/** + * @template T + */ abstract class Element extends Factory { use AddsMatrixBlocks; diff --git a/src/factories/MatrixField.php b/src/factories/MatrixField.php index 27142d0..75083b7 100644 --- a/src/factories/MatrixField.php +++ b/src/factories/MatrixField.php @@ -7,13 +7,15 @@ class MatrixField extends Field { - public static function factory() + public static function factory(): static { if (InstalledVersions::satisfies(new VersionParser, 'craftcms/cms', '~5.0')) { + // @phpstan-ignore-next-line return MatrixFieldEntries::factory(); } if (InstalledVersions::satisfies(new VersionParser, 'craftcms/cms', '~4.0')) { + // @phpstan-ignore-next-line return MatrixFieldBlocks::factory(); } diff --git a/src/factories/User.php b/src/factories/User.php index d989ed1..a22ea45 100644 --- a/src/factories/User.php +++ b/src/factories/User.php @@ -5,7 +5,7 @@ /** * @method self admin(bool $isAdmin) * - * @extends Factory<\craft\elements\User> + * @extends Element<\craft\elements\User> */ class User extends Element { diff --git a/src/io/Buffer.php b/src/io/Buffer.php index 7b4ac5c..fb128bf 100644 --- a/src/io/Buffer.php +++ b/src/io/Buffer.php @@ -2,6 +2,8 @@ namespace markhuot\craftpest\io; +use function markhuot\craftpest\helpers\test\test; + class Buffer extends \php_user_filter { protected string $streamName = 'stdout'; From f61c0486773ee1e882e55a5bb76ec4aaf794bc2e Mon Sep 17 00:00:00 2001 From: markhuot Date: Tue, 8 Oct 2024 15:36:15 -0400 Subject: [PATCH 6/6] fixing some messy recursion and phpstan issues --- src/factories/MatrixField.php | 2 +- src/factories/MatrixFieldBlocks.php | 7 ++++++- src/factories/MatrixFieldEntries.php | 7 ++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/factories/MatrixField.php b/src/factories/MatrixField.php index 75083b7..d0fb3c4 100644 --- a/src/factories/MatrixField.php +++ b/src/factories/MatrixField.php @@ -19,6 +19,6 @@ public static function factory(): static return MatrixFieldBlocks::factory(); } - throw new \RuntimeException('bad version'); + throw new \RuntimeException('Craft Pest is not compatible with this version of Craft CMS.'); } } diff --git a/src/factories/MatrixFieldBlocks.php b/src/factories/MatrixFieldBlocks.php index 3c44d46..a578d87 100644 --- a/src/factories/MatrixFieldBlocks.php +++ b/src/factories/MatrixFieldBlocks.php @@ -7,10 +7,15 @@ use function markhuot\craftpest\helpers\base\version_greater_than_or_equal_to; -class MatrixFieldBlocks extends Field +class MatrixFieldBlocks extends MatrixField { protected $blockTypes = []; + public static function factory(): static + { + return new static; + } + public function blockTypes(...$blockTypes) { if (is_array($blockTypes[0])) { diff --git a/src/factories/MatrixFieldEntries.php b/src/factories/MatrixFieldEntries.php index f7377e2..dcc4812 100644 --- a/src/factories/MatrixFieldEntries.php +++ b/src/factories/MatrixFieldEntries.php @@ -4,13 +4,18 @@ use craft\fields\Matrix; -class MatrixFieldEntries extends Field +class MatrixFieldEntries extends MatrixField { /** * @var EntryType[] */ protected $entryTypes = []; + public static function factory(): static + { + return new static; + } + public function entryTypes(...$entryTypes) { if (is_array($entryTypes[0])) {