From e11bf68e2e720bccfc1d449bf86e4bc7155c0b2a Mon Sep 17 00:00:00 2001 From: Aaron Gustavo Nieves <64917965+TavoNiievez@users.noreply.github.com> Date: Tue, 25 Mar 2025 16:17:07 -0500 Subject: [PATCH 1/6] Document new Symfony assertions (#208) --- .../Module/Symfony/BrowserAssertionsTrait.php | 87 ++++++++++++++++++- .../Symfony/DomCrawlerAssertionsTrait.php | 55 ++++++++++++ .../Module/Symfony/FormAssertionsTrait.php | 17 +++- .../Symfony/HttpClientAssertionsTrait.php | 23 ++++- .../Module/Symfony/MailerAssertionsTrait.php | 59 +++++++++++-- 5 files changed, 226 insertions(+), 15 deletions(-) diff --git a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php index 488e1976..fbd8a075 100644 --- a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php @@ -25,6 +25,11 @@ trait BrowserAssertionsTrait { /** * Asserts that the given cookie in the test client is set to the expected value. + * + * ```php + * assertBrowserCookieValueSame('cookie_name', 'expected_value'); + * ``` */ public function assertBrowserCookieValueSame(string $name, string $expectedValue, bool $raw = false, string $path = '/', ?string $domain = null, string $message = ''): void { @@ -35,6 +40,11 @@ public function assertBrowserCookieValueSame(string $name, string $expectedValue /** * Asserts that the test client has the specified cookie set. * This indicates that the cookie was set by any response during the test. + * + * ``` + * assertBrowserHasCookie('cookie_name'); + * ``` */ public function assertBrowserHasCookie(string $name, string $path = '/', ?string $domain = null, string $message = ''): void { @@ -44,6 +54,11 @@ public function assertBrowserHasCookie(string $name, string $path = '/', ?string /** * Asserts that the test client does not have the specified cookie set. * This indicates that the cookie was not set by any response during the test. + * + * ```php + * assertBrowserNotHasCookie('cookie_name'); + * ``` */ public function assertBrowserNotHasCookie(string $name, string $path = '/', ?string $domain = null, string $message = ''): void { @@ -52,6 +67,11 @@ public function assertBrowserNotHasCookie(string $name, string $path = '/', ?str /** * Asserts that the specified request attribute matches the expected value. + * + * ```php + * assertRequestAttributeValueSame('attribute_name', 'expected_value'); + * ``` */ public function assertRequestAttributeValueSame(string $name, string $expectedValue, string $message = ''): void { @@ -60,6 +80,11 @@ public function assertRequestAttributeValueSame(string $name, string $expectedVa /** * Asserts that the specified response cookie is present and matches the expected value. + * + * ```php + * assertResponseCookieValueSame('cookie_name', 'expected_value'); + * ``` */ public function assertResponseCookieValueSame(string $name, string $expectedValue, string $path = '/', ?string $domain = null, string $message = ''): void { @@ -69,6 +94,11 @@ public function assertResponseCookieValueSame(string $name, string $expectedValu /** * Asserts that the response format matches the expected format. This checks the format returned by the `Response::getFormat()` method. + * + * ```php + * assertResponseFormatSame('json'); + * ``` */ public function assertResponseFormatSame(?string $expectedFormat, string $message = ''): void { @@ -77,6 +107,11 @@ public function assertResponseFormatSame(?string $expectedFormat, string $messag /** * Asserts that the specified cookie is present in the response. Optionally, it can check for a specific cookie path or domain. + * + * ```php + * assertResponseHasCookie('cookie_name'); + * ``` */ public function assertResponseHasCookie(string $name, string $path = '/', ?string $domain = null, string $message = ''): void { @@ -86,6 +121,11 @@ public function assertResponseHasCookie(string $name, string $path = '/', ?strin /** * Asserts that the specified header is available in the response. * For example, use `assertResponseHasHeader('content-type');`. + * + * ```php + * assertResponseHasHeader('content-type'); + * ``` */ public function assertResponseHasHeader(string $headerName, string $message = ''): void { @@ -95,6 +135,11 @@ public function assertResponseHasHeader(string $headerName, string $message = '' /** * Asserts that the specified header does not contain the expected value in the response. * For example, use `assertResponseHeaderNotSame('content-type', 'application/octet-stream');`. + * + * ```php + * assertResponseHeaderNotSame('content-type', 'application/json'); + * ``` */ public function assertResponseHeaderNotSame(string $headerName, string $expectedValue, string $message = ''): void { @@ -104,6 +149,11 @@ public function assertResponseHeaderNotSame(string $headerName, string $expected /** * Asserts that the specified header contains the expected value in the response. * For example, use `assertResponseHeaderSame('content-type', 'application/octet-stream');`. + * + * ```php + * assertResponseHeaderSame('content-type', 'application/json'); + * ``` */ public function assertResponseHeaderSame(string $headerName, string $expectedValue, string $message = ''): void { @@ -112,6 +162,11 @@ public function assertResponseHeaderSame(string $headerName, string $expectedVal /** * Asserts that the response was successful (HTTP status code is in the 2xx range). + * + * ```php + * assertResponseIsSuccessful(); + * ``` */ public function assertResponseIsSuccessful(string $message = '', bool $verbose = true): void { @@ -120,6 +175,11 @@ public function assertResponseIsSuccessful(string $message = '', bool $verbose = /** * Asserts that the response is unprocessable (HTTP status code is 422). + * + * ```php + * assertResponseIsUnprocessable(); + * ``` */ public function assertResponseIsUnprocessable(string $message = '', bool $verbose = true): void { @@ -128,6 +188,11 @@ public function assertResponseIsUnprocessable(string $message = '', bool $verbos /** * Asserts that the specified cookie is not present in the response. Optionally, it can check for a specific cookie path or domain. + * + * ```php + * assertResponseNotHasCookie('cookie_name'); + * ``` */ public function assertResponseNotHasCookie(string $name, string $path = '/', ?string $domain = null, string $message = ''): void { @@ -136,7 +201,11 @@ public function assertResponseNotHasCookie(string $name, string $path = '/', ?st /** * Asserts that the specified header is not available in the response. - * For example, use `assertResponseNotHasHeader('content-type');`. + * + * ```php + * assertResponseNotHasHeader('content-type'); + * ``` */ public function assertResponseNotHasHeader(string $headerName, string $message = ''): void { @@ -146,6 +215,12 @@ public function assertResponseNotHasHeader(string $headerName, string $message = /** * Asserts that the response is a redirect. Optionally, you can check the target location and status code. * The expected location can be either an absolute or a relative path. + * + * ```php + * assertResponseRedirects('/login', 302); + * ``` */ public function assertResponseRedirects(?string $expectedLocation = null, ?int $expectedCode = null, string $message = '', bool $verbose = true): void { @@ -165,6 +240,11 @@ public function assertResponseRedirects(?string $expectedLocation = null, ?int $ /** * Asserts that the response status code matches the expected code. + * + * ```php + * assertResponseStatusCodeSame(200); + * ``` */ public function assertResponseStatusCodeSame(int $expectedCode, string $message = '', bool $verbose = true): void { @@ -173,6 +253,11 @@ public function assertResponseStatusCodeSame(int $expectedCode, string $message /** * Asserts the request matches the given route and optionally route parameters. + * + * ```php + * assertRouteSame('profile', ['id' => 123]); + * ``` */ public function assertRouteSame(string $expectedRoute, array $parameters = [], string $message = ''): void { $request = $this->getClient()->getRequest(); diff --git a/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php b/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php index 643e3fd1..8786be4c 100644 --- a/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php @@ -15,6 +15,11 @@ trait DomCrawlerAssertionsTrait { /** * Asserts that the checkbox with the given name is checked. + * + * ```php + * assertCheckboxChecked('agree_terms'); + * ``` */ public function assertCheckboxChecked(string $fieldName, string $message = ''): void { @@ -23,6 +28,11 @@ public function assertCheckboxChecked(string $fieldName, string $message = ''): /** * Asserts that the checkbox with the given name is not checked. + * + * ```php + * assertCheckboxNotChecked('subscribe'); + * ``` */ public function assertCheckboxNotChecked(string $fieldName, string $message = ''): void { @@ -33,6 +43,11 @@ public function assertCheckboxNotChecked(string $fieldName, string $message = '' /** * Asserts that the value of the form input with the given name does not equal the expected value. + * + * ```php + * assertInputValueNotSame('username', 'admin'); + * ``` */ public function assertInputValueNotSame(string $fieldName, string $expectedValue, string $message = ''): void { @@ -44,6 +59,11 @@ public function assertInputValueNotSame(string $fieldName, string $expectedValue /** * Asserts that the value of the form input with the given name equals the expected value. + * + * ```php + * assertInputValueSame('username', 'johndoe'); + * ``` */ public function assertInputValueSame(string $fieldName, string $expectedValue, string $message = ''): void { @@ -56,6 +76,11 @@ public function assertInputValueSame(string $fieldName, string $expectedValue, s /** * Asserts that the `` element contains the given title. + * + * ```php + * <?php + * $I->assertPageTitleContains('Welcome'); + * ``` */ public function assertPageTitleContains(string $expectedTitle, string $message = ''): void { @@ -64,6 +89,11 @@ public function assertPageTitleContains(string $expectedTitle, string $message = /** * Asserts that the `<title>` element equals the given title. + * + * ```php + * <?php + * $I->assertPageTitleSame('Home Page'); + * ``` */ public function assertPageTitleSame(string $expectedTitle, string $message = ''): void { @@ -72,6 +102,11 @@ public function assertPageTitleSame(string $expectedTitle, string $message = '') /** * Asserts that the given selector matches at least one element in the response. + * + * ```php + * <?php + * $I->assertSelectorExists('.main-content'); + * ``` */ public function assertSelectorExists(string $selector, string $message = ''): void { @@ -80,6 +115,11 @@ public function assertSelectorExists(string $selector, string $message = ''): vo /** * Asserts that the given selector does not match at least one element in the response. + * + * ```php + * <?php + * $I->assertSelectorNotExists('.error'); + * ``` */ public function assertSelectorNotExists(string $selector, string $message = ''): void { @@ -88,6 +128,11 @@ public function assertSelectorNotExists(string $selector, string $message = ''): /** * Asserts that the first element matching the given selector contains the expected text. + * + * ```php + * <?php + * $I->assertSelectorTextContains('h1', 'Dashboard'); + * ``` */ public function assertSelectorTextContains(string $selector, string $text, string $message = ''): void { @@ -97,6 +142,11 @@ public function assertSelectorTextContains(string $selector, string $text, strin /** * Asserts that the first element matching the given selector does not contain the expected text. + * + * ```php + * <?php + * $I->assertSelectorTextNotContains('p', 'error'); + * ``` */ public function assertSelectorTextNotContains(string $selector, string $text, string $message = ''): void { @@ -106,6 +156,11 @@ public function assertSelectorTextNotContains(string $selector, string $text, st /** * Asserts that the text of the first element matching the given selector equals the expected text. + * + * ```php + * <?php + * $I->assertSelectorTextSame('h1', 'Dashboard'); + * ``` */ public function assertSelectorTextSame(string $selector, string $text, string $message = ''): void { diff --git a/src/Codeception/Module/Symfony/FormAssertionsTrait.php b/src/Codeception/Module/Symfony/FormAssertionsTrait.php index cdebbb64..f77403bd 100644 --- a/src/Codeception/Module/Symfony/FormAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/FormAssertionsTrait.php @@ -14,6 +14,11 @@ trait FormAssertionsTrait { /** * Asserts that value of the field of the first form matching the given selector does equal the expected value. + * + * ```php + * <?php + * $I->assertFormValue('#loginForm', 'username', 'john_doe'); + * ``` */ public function assertFormValue(string $formSelector, string $fieldName, string $value, string $message = ''): void { @@ -25,7 +30,12 @@ public function assertFormValue(string $formSelector, string $fieldName, string } /** - * Asserts that value of the field of the first form matching the given selector does equal the expected value. + * Asserts that the field of the first form matching the given selector does not have a value. + * + * ```php + * <?php + * $I->assertNoFormValue('#registrationForm', 'middle_name'); + * ``` */ public function assertNoFormValue(string $formSelector, string $fieldName, string $message = ''): void { @@ -128,7 +138,6 @@ public function seeFormErrorMessage(string $field, ?string $message = null): voi * If you want to specify the error messages, you can do so * by sending an associative array instead, with the key being * the name of the field and the error message the value. - * * This method will validate that the expected error message * is contained in the actual error message, that is, * you can specify either the entire error message or just a part of it: @@ -136,7 +145,7 @@ public function seeFormErrorMessage(string $field, ?string $message = null): voi * ```php * <?php * $I->seeFormErrorMessages([ - * 'address' => 'The address is too long' + * 'address' => 'The address is too long', * 'telephone' => 'too short', // the full error message is 'The telephone is too short' * ]); * ``` @@ -191,4 +200,4 @@ protected function grabFormCollector(string $function): FormDataCollector { return $this->grabCollector('form', $function); } -} \ No newline at end of file +} diff --git a/src/Codeception/Module/Symfony/HttpClientAssertionsTrait.php b/src/Codeception/Module/Symfony/HttpClientAssertionsTrait.php index 9ac3a6e4..f6f322eb 100644 --- a/src/Codeception/Module/Symfony/HttpClientAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/HttpClientAssertionsTrait.php @@ -12,7 +12,18 @@ trait HttpClientAssertionsTrait { /** * Asserts that the given URL has been called using, if specified, the given method body and headers. - * By default, it will check on the HttpClient, but you can also pass a specific HttpClient ID. (It will succeed if the request has been called multiple times.) + * By default, it will check on the HttpClient, but you can also pass a specific HttpClient ID. + * (It will succeed if the request has been called multiple times.) + * + * ```php + * <?php + * $I->assertHttpClientRequest( + * 'https://example.com/api', + * 'POST', + * '{"data": "value"}', + * ['Authorization' => 'Bearer token'] + * ); + * ``` */ public function assertHttpClientRequest(string $expectedUrl, string $expectedMethod = 'GET', string|array|null $expectedBody = null, array $expectedHeaders = [], string $httpClientId = 'http_client'): void { @@ -77,6 +88,11 @@ public function assertHttpClientRequest(string $expectedUrl, string $expectedMet /** * Asserts that the given number of requests has been made on the HttpClient. * By default, it will check on the HttpClient, but you can also pass a specific HttpClient ID. + * + * ```php + * <?php + * $I->assertHttpClientRequestCount(3); + * ``` */ public function assertHttpClientRequestCount(int $count, string $httpClientId = 'http_client'): void { @@ -88,6 +104,11 @@ public function assertHttpClientRequestCount(int $count, string $httpClientId = /** * Asserts that the given URL has not been called using GET or the specified method. * By default, it will check on the HttpClient, but a HttpClient id can be specified. + * + * ```php + * <?php + * $I->assertNotHttpClientRequest('https://example.com/unexpected', 'GET'); + * ``` */ public function assertNotHttpClientRequest(string $unexpectedUrl, string $expectedMethod = 'GET', string $httpClientId = 'http_client'): void { diff --git a/src/Codeception/Module/Symfony/MailerAssertionsTrait.php b/src/Codeception/Module/Symfony/MailerAssertionsTrait.php index df2fd0f1..007e02af 100644 --- a/src/Codeception/Module/Symfony/MailerAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/MailerAssertionsTrait.php @@ -15,6 +15,11 @@ trait MailerAssertionsTrait { /** * Asserts that the expected number of emails was sent. + * + * ```php + * <?php + * $I->assertEmailCount(2, 'smtp'); + * ``` */ public function assertEmailCount(int $count, ?string $transport = null, string $message = ''): void { @@ -24,6 +29,12 @@ public function assertEmailCount(int $count, ?string $transport = null, string $ /** * Asserts that the given mailer event is not queued. * Use `getMailerEvent(int $index = 0, ?string $transport = null)` to retrieve a mailer event by index. + * + * ```php + * <?php + * $event = $I->getMailerEvent(); + * $I->assertEmailIsNotQueued($event); + * ``` */ public function assertEmailIsNotQueued(MessageEvent $event, string $message = ''): void { @@ -33,6 +44,12 @@ public function assertEmailIsNotQueued(MessageEvent $event, string $message = '' /** * Asserts that the given mailer event is queued. * Use `getMailerEvent(int $index = 0, ?string $transport = null)` to retrieve a mailer event by index. + * + * ```php + * <?php + * $event = $I->getMailerEvent(); + * $I->assertEmailIsQueued($event); + * ``` */ public function assertEmailIsQueued(MessageEvent $event, string $message = ''): void { @@ -41,6 +58,11 @@ public function assertEmailIsQueued(MessageEvent $event, string $message = ''): /** * Asserts that the expected number of emails was queued (e.g. using the Messenger component). + * + * ```php + * <?php + * $I->assertQueuedEmailCount(1, 'smtp'); + * ``` */ public function assertQueuedEmailCount(int $count, ?string $transport = null, string $message = ''): void { @@ -50,7 +72,13 @@ public function assertQueuedEmailCount(int $count, ?string $transport = null, st /** * Checks that no email was sent. * The check is based on `\Symfony\Component\Mailer\EventListener\MessageLoggerListener`, which means: - * If your app performs an HTTP redirect, you need to suppress it using [stopFollowingRedirects()](https://codeception.com/docs/modules/Symfony#stopFollowingRedirects) first; otherwise this check will *always* pass. + * If your app performs an HTTP redirect, you need to suppress it using [stopFollowingRedirects()](https://codeception.com/docs/modules/Symfony#stopFollowingRedirects) first; + * otherwise this check will *always* pass. + * + * ```php + * <?php + * $I->dontSeeEmailIsSent(); + * ``` */ public function dontSeeEmailIsSent(): void { @@ -114,18 +142,31 @@ public function seeEmailIsSent(int $expectedCount = 1): void $this->assertThat($this->getMessageMailerEvents(), new MailerConstraint\EmailCount($expectedCount)); } + /** + * Returns the mailer event at the specified index. + * + * ```php + * <?php + * $event = $I->getMailerEvent(); + * ``` + */ + public function getMailerEvent(int $index = 0, ?string $transport = null): ?MessageEvent + { + $mailerEvents = $this->getMessageMailerEvents(); + $events = $mailerEvents->getEvents($transport); + return $events[$index] ?? null; + } + protected function getMessageMailerEvents(): MessageEvents { - if ($messageLogger = $this->getService('mailer.message_logger_listener')) { - /** @var MessageLoggerListener $messageLogger */ - return $messageLogger->getEvents(); + if ($mailer = $this->getService('mailer.message_logger_listener')) { + /** @var MessageLoggerListener $mailer */ + return $mailer->getEvents(); } - - if ($messageLogger = $this->getService('mailer.logger_message_listener')) { - /** @var MessageLoggerListener $messageLogger */ - return $messageLogger->getEvents(); + if ($mailer = $this->getService('mailer.logger_message_listener')) { + /** @var MessageLoggerListener $mailer */ + return $mailer->getEvents(); } - $this->fail("Emails can't be tested without Symfony Mailer service."); } } From 15fd14260b5041ec245bb9ffc146f4887f379e43 Mon Sep 17 00:00:00 2001 From: prophetz <kpuzuc@gmail.com> Date: Wed, 9 Apr 2025 23:48:38 +0300 Subject: [PATCH 2/6] Fix parameter name in exception for case when Kernel has custom namespace (#199) --- src/Codeception/Module/Symfony.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Codeception/Module/Symfony.php b/src/Codeception/Module/Symfony.php index 0e41e787..3ac2bc79 100644 --- a/src/Codeception/Module/Symfony.php +++ b/src/Codeception/Module/Symfony.php @@ -321,7 +321,7 @@ protected function getKernelClass(): string throw new ModuleRequireException( self::class, "Kernel class was not found.\n" - . 'Specify directory where file with Kernel class for your application is located with `app_path` parameter.' + . 'Specify directory where file with Kernel class for your application is located with `kernel_class` parameter.' ); } From d9084f562015e5749a66647b977be0710cdc65d4 Mon Sep 17 00:00:00 2001 From: Thomas Landauer <thomas@landauer.at> Date: Thu, 10 Apr 2025 00:03:00 +0200 Subject: [PATCH 3/6] Update MailerAssertionsTrait.php: Adding Mailpit (#204) --- .../Module/Symfony/MailerAssertionsTrait.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Codeception/Module/Symfony/MailerAssertionsTrait.php b/src/Codeception/Module/Symfony/MailerAssertionsTrait.php index 007e02af..5a31e6d8 100644 --- a/src/Codeception/Module/Symfony/MailerAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/MailerAssertionsTrait.php @@ -72,8 +72,7 @@ public function assertQueuedEmailCount(int $count, ?string $transport = null, st /** * Checks that no email was sent. * The check is based on `\Symfony\Component\Mailer\EventListener\MessageLoggerListener`, which means: - * If your app performs an HTTP redirect, you need to suppress it using [stopFollowingRedirects()](https://codeception.com/docs/modules/Symfony#stopFollowingRedirects) first; - * otherwise this check will *always* pass. + * If your app performs an HTTP redirect, you need to suppress it using [stopFollowingRedirects()](#stopFollowingRedirects) first; otherwise this check will *always* pass. * * ```php * <?php @@ -88,7 +87,7 @@ public function dontSeeEmailIsSent(): void /** * Returns the last sent email. * The function is based on `\Symfony\Component\Mailer\EventListener\MessageLoggerListener`, which means: - * If your app performs an HTTP redirect after sending the email, you need to suppress it using [stopFollowingRedirects()](https://codeception.com/docs/modules/Symfony#stopFollowingRedirects) first. + * If your app performs an HTTP redirect after sending the email, you need to suppress it using [stopFollowingRedirects()](#stopFollowingRedirects) first. * See also: [grabSentEmails()](https://codeception.com/docs/modules/Symfony#grabSentEmails) * * ```php @@ -110,7 +109,7 @@ public function grabLastSentEmail(): ?Email /** * Returns an array of all sent emails. * The function is based on `\Symfony\Component\Mailer\EventListener\MessageLoggerListener`, which means: - * If your app performs an HTTP redirect after sending the email, you need to suppress it using [stopFollowingRedirects()](https://codeception.com/docs/modules/Symfony#stopFollowingRedirects) first. + * If your app performs an HTTP redirect after sending the email, you need to suppress it using [stopFollowingRedirects()](#stopFollowingRedirects) first. * See also: [grabLastSentEmail()](https://codeception.com/docs/modules/Symfony#grabLastSentEmail) * * ```php @@ -128,7 +127,12 @@ public function grabSentEmails(): array /** * Checks if the given number of emails was sent (default `$expectedCount`: 1). * The check is based on `\Symfony\Component\Mailer\EventListener\MessageLoggerListener`, which means: - * If your app performs an HTTP redirect after sending the email, you need to suppress it using [stopFollowingRedirects()](https://codeception.com/docs/modules/Symfony#stopFollowingRedirects) first. + * If your app performs an HTTP redirect after sending the email, you need to suppress it using [stopFollowingRedirects()](#stopFollowingRedirects) first. + * + * Limitation: + * If your mail is sent in a Symfony console command and you start that command in your test with [$I->runShellCommand()](https://codeception.com/docs/modules/Cli#runShellCommand), + * Codeception will not notice it. + * As a more professional alternative, we recommend Mailpit (see [Addons](https://codeception.com/addons)), which also lets you test the content of the mail. * * ```php * <?php From c3a1c42f8dfe15e049bfc6763a962371a93e376b Mon Sep 17 00:00:00 2001 From: Aaron Gustavo Nieves <64917965+TavoNiievez@users.noreply.github.com> Date: Wed, 28 May 2025 13:25:49 -0500 Subject: [PATCH 4/6] Remove PHP 8.1 Support (#211) --- .github/workflows/main.yml | 2 +- composer.json | 19 +++-- .../Module/Symfony/BrowserAssertionsTrait.php | 9 ++- .../Module/Symfony/ConsoleAssertionsTrait.php | 74 ++++++++++--------- .../Symfony/DomCrawlerAssertionsTrait.php | 16 ++-- .../Module/Symfony/MimeAssertionsTrait.php | 6 +- .../Symfony/ServicesAssertionsTrait.php | 7 +- .../Symfony/TranslationAssertionsTrait.php | 2 +- .../Module/Symfony/TwigAssertionsTrait.php | 2 +- .../Symfony/ValidatorAssertionsTrait.php | 16 ++-- 10 files changed, 84 insertions(+), 69 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 85b0d49d..43762ad0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: [8.1, 8.2, 8.3, 8.4] + php: [8.2, 8.3, 8.4] symfony: ["5.4.*", "6.4.*", "7.2.*"] exclude: - php: 8.1 diff --git a/composer.json b/composer.json index 747a5941..f8023eb7 100644 --- a/composer.json +++ b/composer.json @@ -1,13 +1,17 @@ { "name": "codeception/module-symfony", "description": "Codeception module for Symfony framework", - "license": "MIT", "type": "library", + "license": "MIT", "keywords": [ "codeception", "functional testing", "symfony" ], + "homepage": "https://codeception.com/", + "support": { + "docs": "https://codeception.com/docs/modules/Symfony" + }, "authors": [ { "name": "Michael Bodnarchuk" @@ -17,17 +21,16 @@ "homepage": "https://medium.com/@ganieves" } ], - "homepage": "https://codeception.com/", "require": { - "php": "^8.1", + "php": "^8.2", "ext-json": "*", - "codeception/codeception": "^5.0.8", - "codeception/lib-innerbrowser": "^3.1.1 | ^4.0" + "codeception/codeception": "^5.3", + "codeception/lib-innerbrowser": "^3.1 | ^4.0" }, "require-dev": { "codeception/module-asserts": "^3.0", "codeception/module-doctrine": "^3.1", - "doctrine/orm": "^2.10", + "doctrine/orm": "^2.20", "symfony/browser-kit": "^5.4 | ^6.4 | ^7.2", "symfony/cache": "^5.4 | ^6.4 | ^7.2", "symfony/config": "^5.4 | ^6.4 | ^7.2", @@ -62,12 +65,12 @@ "codeception/module-asserts": "Include traditional PHPUnit assertions in your tests", "symfony/web-profiler-bundle": "Tool that gives information about the execution of requests" }, - "minimum-stability": "RC", "autoload": { "classmap": ["src/"] }, "config": { "classmap-authoritative": true, "sort-packages": true - } + }, + "minimum-stability": "RC" } diff --git a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php index fbd8a075..760f6cc7 100644 --- a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php @@ -258,13 +258,16 @@ public function assertResponseStatusCodeSame(int $expectedCode, string $message * <?php * $I->assertRouteSame('profile', ['id' => 123]); * ``` + * + * @param array<string, bool|float|int|null|string> $parameters */ - public function assertRouteSame(string $expectedRoute, array $parameters = [], string $message = ''): void { + public function assertRouteSame(string $expectedRoute, array $parameters = [], string $message = ''): void + { $request = $this->getClient()->getRequest(); $this->assertThat($request, new RequestAttributeValueSame('_route', $expectedRoute)); foreach ($parameters as $key => $value) { - $this->assertThat($request, new RequestAttributeValueSame($key, $value), $message); + $this->assertThat($request, new RequestAttributeValueSame($key, (string)$value), $message); } } @@ -349,7 +352,7 @@ public function seePageRedirectsTo(string $page, string $redirectsTo): void * ]); * ``` * - * @param string $name The `name` attribute of the `<form>`. You cannot use an array as a selector here. + * @param string $name The `name` attribute of the `<form>`. You cannot use an array as a selector here. * @param array<string, mixed> $fields The form fields to submit. */ public function submitSymfonyForm(string $name, array $fields): void diff --git a/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php b/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php index 18e20173..763f977e 100644 --- a/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/ConsoleAssertionsTrait.php @@ -9,6 +9,9 @@ use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\HttpKernel\KernelInterface; +use function in_array; +use function sprintf; + trait ConsoleAssertionsTrait { /** @@ -20,40 +23,42 @@ trait ConsoleAssertionsTrait * $result = $I->runSymfonyConsoleCommand('hello:world', ['arg' => 'argValue', 'opt1' => 'optValue'], ['input']); * ``` * - * @param string $command The console command to execute - * @param array $parameters Parameters (arguments and options) to pass to the command - * @param array $consoleInputs Console inputs (e.g. used for interactive questions) - * @param int $expectedExitCode The expected exit code of the command - * @return string Returns the console output of the command + * @param string $command The console command to execute. + * @param array<string, int|string> $parameters Arguments and options passed to the command + * @param list<string> $consoleInputs Inputs for interactive questions. + * @param int $expectedExitCode Expected exit code. + * @return string Console output (stdout). */ - public function runSymfonyConsoleCommand(string $command, array $parameters = [], array $consoleInputs = [], int $expectedExitCode = 0): string - { - $kernel = $this->grabKernelService(); - $application = new Application($kernel); - $consoleCommand = $application->find($command); - $commandTester = new CommandTester($consoleCommand); + public function runSymfonyConsoleCommand( + string $command, + array $parameters = [], + array $consoleInputs = [], + int $expectedExitCode = 0 + ): string { + $kernel = $this->grabKernelService(); + $application = new Application($kernel); + $consoleCommand = $application->find($command); + $commandTester = new CommandTester($consoleCommand); $commandTester->setInputs($consoleInputs); - $input = ['command' => $command] + $parameters; - $options = $this->configureOptions($parameters); - + $input = ['command' => $command] + $parameters; + $options = $this->configureOptions($parameters); $exitCode = $commandTester->execute($input, $options); - $output = $commandTester->getDisplay(); + $output = $commandTester->getDisplay(); $this->assertSame( $expectedExitCode, $exitCode, - sprintf( - 'Command did not exit with code %d but with %d: %s', - $expectedExitCode, - $exitCode, - $output - ) + sprintf('Command exited with %d instead of expected %d. Output: %s', $exitCode, $expectedExitCode, $output) ); return $output; } + /** + * @param array<string, int|string|bool> $parameters + * @return array<string, mixed> Options array supported by CommandTester. + */ private function configureOptions(array $parameters): array { $options = []; @@ -69,27 +74,24 @@ private function configureOptions(array $parameters): array } if (in_array('--quiet', $parameters, true) || in_array('-q', $parameters, true)) { - $options['verbosity'] = OutputInterface::VERBOSITY_QUIET; + $options['verbosity'] = OutputInterface::VERBOSITY_QUIET; $options['interactive'] = false; } - if ( - in_array('-vvv', $parameters, true) || - in_array('--verbose=3', $parameters, true) || - (isset($parameters["--verbose"]) && $parameters["--verbose"] === 3) + if (in_array('-vvv', $parameters, true) + || in_array('--verbose=3', $parameters, true) + || (isset($parameters['--verbose']) && $parameters['--verbose'] === 3) ) { $options['verbosity'] = OutputInterface::VERBOSITY_DEBUG; - } elseif ( - in_array('-vv', $parameters, true) || - in_array('--verbose=2', $parameters, true) || - (isset($parameters["--verbose"]) && $parameters["--verbose"] === 2) + } elseif (in_array('-vv', $parameters, true) + || in_array('--verbose=2', $parameters, true) + || (isset($parameters['--verbose']) && $parameters['--verbose'] === 2) ) { $options['verbosity'] = OutputInterface::VERBOSITY_VERY_VERBOSE; - } elseif ( - in_array('-v', $parameters, true) || - in_array('--verbose=1', $parameters, true) || - in_array('--verbose', $parameters, true) || - (isset($parameters["--verbose"]) && $parameters["--verbose"] === 1) + } elseif (in_array('-v', $parameters, true) + || in_array('--verbose=1', $parameters, true) + || in_array('--verbose', $parameters, true) + || (isset($parameters['--verbose']) && $parameters['--verbose'] === 1) ) { $options['verbosity'] = OutputInterface::VERBOSITY_VERBOSE; } @@ -101,4 +103,4 @@ protected function grabKernelService(): KernelInterface { return $this->grabService('kernel'); } -} \ No newline at end of file +} diff --git a/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php b/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php index 8786be4c..f25f9bf3 100644 --- a/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php @@ -36,9 +36,11 @@ public function assertCheckboxChecked(string $fieldName, string $message = ''): */ public function assertCheckboxNotChecked(string $fieldName, string $message = ''): void { - $this->assertThatCrawler(new LogicalNot( - new CrawlerSelectorExists("input[name=\"$fieldName\"]:checked") - ), $message); + $this->assertThatCrawler( + new LogicalNot( + new CrawlerSelectorExists("input[name=\"$fieldName\"]:checked") + ), $message + ); } /** @@ -52,9 +54,11 @@ public function assertCheckboxNotChecked(string $fieldName, string $message = '' public function assertInputValueNotSame(string $fieldName, string $expectedValue, string $message = ''): void { $this->assertThatCrawler(new CrawlerSelectorExists("input[name=\"$fieldName\"]"), $message); - $this->assertThatCrawler(new LogicalNot( - new CrawlerSelectorAttributeValueSame("input[name=\"$fieldName\"]", 'value', $expectedValue) - ), $message); + $this->assertThatCrawler( + new LogicalNot( + new CrawlerSelectorAttributeValueSame("input[name=\"$fieldName\"]", 'value', $expectedValue) + ), $message + ); } /** diff --git a/src/Codeception/Module/Symfony/MimeAssertionsTrait.php b/src/Codeception/Module/Symfony/MimeAssertionsTrait.php index ba2ee9ac..d48df3d4 100644 --- a/src/Codeception/Module/Symfony/MimeAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/MimeAssertionsTrait.php @@ -4,9 +4,9 @@ namespace Codeception\Module\Symfony; +use PHPUnit\Framework\Assert; use PHPUnit\Framework\Constraint\LogicalNot; use Symfony\Component\Mime\Email; -use Symfony\Component\Mime\RawMessage; use Symfony\Component\Mime\Test\Constraint as MimeConstraint; trait MimeAssertionsTrait @@ -171,8 +171,8 @@ private function verifyEmailObject(?Email $email, string $function): Email { $email = $email ?: $this->grabLastSentEmail(); $errorMsgTemplate = "There is no email to verify. An Email object was not specified when invoking '%s' and the application has not sent one."; - return $email ?: $this->fail( + return $email ?? Assert::fail( sprintf($errorMsgTemplate, $function) ); } -} \ No newline at end of file +} diff --git a/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php b/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php index bd9140c0..b4c1a5dd 100644 --- a/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php @@ -5,6 +5,7 @@ namespace Codeception\Module\Symfony; use Codeception\Lib\Connector\Symfony as SymfonyConnector; +use PHPUnit\Framework\Assert; trait ServicesAssertionsTrait { @@ -24,8 +25,10 @@ trait ServicesAssertionsTrait public function grabService(string $serviceId): object { if (!$service = $this->getService($serviceId)) { - $this->fail("Service `{$serviceId}` is required by Codeception, but not loaded by Symfony since you're not using it anywhere in your app.\n - Recommended solution: Set it to `public` in your `config/services_test.php`/`.yaml`, see https://symfony.com/doc/current/service_container/alias_private.html#marking-services-as-public-private"); + Assert::fail( + "Service `{$serviceId}` is required by Codeception, but not loaded by Symfony since you're not using it anywhere in your app.\n + Recommended solution: Set it to `public` in your `config/services_test.php`/`.yaml`, see https://symfony.com/doc/current/service_container/alias_private.html#marking-services-as-public-private" + ); } return $service; diff --git a/src/Codeception/Module/Symfony/TranslationAssertionsTrait.php b/src/Codeception/Module/Symfony/TranslationAssertionsTrait.php index 7c4c385a..5fa91725 100644 --- a/src/Codeception/Module/Symfony/TranslationAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/TranslationAssertionsTrait.php @@ -109,7 +109,7 @@ public function seeDefaultLocaleIs(string $expectedLocale): void * $I->seeFallbackLocalesAre(['es', 'fr']); * ``` * - * @param array $expectedLocales The expected fallback locales + * @param string[] $expectedLocales The expected fallback locales */ public function seeFallbackLocalesAre(array $expectedLocales): void { diff --git a/src/Codeception/Module/Symfony/TwigAssertionsTrait.php b/src/Codeception/Module/Symfony/TwigAssertionsTrait.php index 1bfba3ec..e664932c 100644 --- a/src/Codeception/Module/Symfony/TwigAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/TwigAssertionsTrait.php @@ -79,4 +79,4 @@ protected function grabTwigCollector(string $function): TwigDataCollector { return $this->grabCollector('twig', $function); } -} \ No newline at end of file +} diff --git a/src/Codeception/Module/Symfony/ValidatorAssertionsTrait.php b/src/Codeception/Module/Symfony/ValidatorAssertionsTrait.php index ca82e196..508cfa5e 100644 --- a/src/Codeception/Module/Symfony/ValidatorAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/ValidatorAssertionsTrait.php @@ -20,7 +20,7 @@ trait ValidatorAssertionsTrait * $I->dontSeeViolatedConstraint($subject, 'propertyName', 'Symfony\Validator\ConstraintClass'); * ``` */ - public function dontSeeViolatedConstraint(mixed $subject, ?string $propertyPath = null, ?string $constraint = null): void + public function dontSeeViolatedConstraint(object $subject, ?string $propertyPath = null, ?string $constraint = null): void { $violations = $this->getViolationsForSubject($subject, $propertyPath, $constraint); $this->assertCount(0, $violations, 'Constraint violations found.'); @@ -37,7 +37,7 @@ public function dontSeeViolatedConstraint(mixed $subject, ?string $propertyPath * $I->seeViolatedConstraint($subject, 'propertyName', 'Symfony\Validator\ConstraintClass'); * ``` */ - public function seeViolatedConstraint(mixed $subject, ?string $propertyPath = null, ?string $constraint = null): void + public function seeViolatedConstraint(object $subject, ?string $propertyPath = null, ?string $constraint = null): void { $violations = $this->getViolationsForSubject($subject, $propertyPath, $constraint); $this->assertNotCount(0, $violations, 'No constraint violations found.'); @@ -52,7 +52,7 @@ public function seeViolatedConstraint(mixed $subject, ?string $propertyPath = nu * $I->seeViolatedConstraintsCount(2, $subject, 'propertyName'); * ``` */ - public function seeViolatedConstraintsCount(int $expected, mixed $subject, ?string $propertyPath = null, ?string $constraint = null): void + public function seeViolatedConstraintsCount(int $expected, object $subject, ?string $propertyPath = null, ?string $constraint = null): void { $violations = $this->getViolationsForSubject($subject, $propertyPath, $constraint); $this->assertCount($expected, $violations); @@ -66,12 +66,12 @@ public function seeViolatedConstraintsCount(int $expected, mixed $subject, ?stri * $I->seeViolatedConstraintMessage('too short', $user, 'address'); * ``` */ - public function seeViolatedConstraintMessage(string $expected, mixed $subject, string $propertyPath): void + public function seeViolatedConstraintMessage(string $expected, object $subject, string $propertyPath): void { $violations = $this->getViolationsForSubject($subject, $propertyPath); $containsExpected = false; foreach ($violations as $violation) { - if ($violation->getPropertyPath() === $propertyPath && str_contains($violation->getMessage(), $expected)) { + if ($violation->getPropertyPath() === $propertyPath && str_contains((string)$violation->getMessage(), $expected)) { $containsExpected = true; break; } @@ -81,7 +81,7 @@ public function seeViolatedConstraintMessage(string $expected, mixed $subject, s } /** @return ConstraintViolationInterface[] */ - protected function getViolationsForSubject(mixed $subject, ?string $propertyPath = null, ?string $constraint = null): array + protected function getViolationsForSubject(object $subject, ?string $propertyPath = null, ?string $constraint = null): array { $validator = $this->getValidatorService(); $violations = $propertyPath ? $validator->validateProperty($subject, $propertyPath) : $validator->validate($subject); @@ -89,9 +89,9 @@ protected function getViolationsForSubject(mixed $subject, ?string $propertyPath $violations = iterator_to_array($violations); if ($constraint !== null) { - return array_filter( + return (array)array_filter( $violations, - static fn($violation): bool => $violation->getConstraint()::class === $constraint && + static fn(ConstraintViolationInterface $violation): bool => get_class((object)$violation->getConstraint()) === $constraint && ($propertyPath === null || $violation->getPropertyPath() === $propertyPath) ); } From d8a8943d06b303fa9a2feb9064eeeabb5bb36386 Mon Sep 17 00:00:00 2001 From: Thomas Landauer <thomas@landauer.at> Date: Wed, 28 May 2025 20:28:12 +0200 Subject: [PATCH 5/6] Update ServicesAssertionsTrait.php: Adding another hint about private services (#210) --- .../Module/Symfony/ServicesAssertionsTrait.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php b/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php index b4c1a5dd..1286e252 100644 --- a/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/ServicesAssertionsTrait.php @@ -11,9 +11,9 @@ trait ServicesAssertionsTrait { /** * Grabs a service from the Symfony dependency injection container (DIC). - * In "test" environment, Symfony uses a special `test.service_container`. + * In the "test" environment, Symfony uses a special `test.service_container`. * See the "[Public Versus Private Services](https://symfony.com/doc/current/service_container/alias_private.html#marking-services-as-public-private)" documentation. - * Services that aren't injected somewhere into your app, need to be defined as `public` to be accessible by Codeception. + * Services that aren't injected anywhere in your app, need to be defined as `public` to be accessible by Codeception. * * ```php * <?php @@ -25,10 +25,10 @@ trait ServicesAssertionsTrait public function grabService(string $serviceId): object { if (!$service = $this->getService($serviceId)) { - Assert::fail( - "Service `{$serviceId}` is required by Codeception, but not loaded by Symfony since you're not using it anywhere in your app.\n - Recommended solution: Set it to `public` in your `config/services_test.php`/`.yaml`, see https://symfony.com/doc/current/service_container/alias_private.html#marking-services-as-public-private" - ); + Assert::fail("Service `{$serviceId}` is required by Codeception, but not loaded by Symfony. Possible solutions:\n + In your `config/packages/framework.php`/`.yaml`, set `test` to `true` (when in test environment), see https://symfony.com/doc/current/reference/configuration/framework.html#test\n + If you're still getting this message, you're not using that service in your app, so Symfony isn't loading it at all.\n + Solution: Set it to `public` in your `config/services.php`/`.yaml`, see https://symfony.com/doc/current/service_container/alias_private.html#marking-services-as-public-private\n"); } return $service; From c2adb5cdcff816a4190c4bfd5be6acc1a5a9c4c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20M=C3=BCller?= <9016208+Seros@users.noreply.github.com> Date: Fri, 30 May 2025 10:07:26 +0200 Subject: [PATCH 6/6] Replace kernel reboot with actual boot to reset services (#209) * Replace kernel reboot with actual boot to reset services * Ensure congruence with the flow in the Symfony KernelTestCase * Add 6.4wApi branch to CI --------- Co-authored-by: TavoNiievez <ganieves@outlook.com> --- .github/workflows/main.yml | 50 +++++++++++++++++------ src/Codeception/Lib/Connector/Symfony.php | 11 ++++- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 43762ad0..c95a2224 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,10 +7,7 @@ jobs: strategy: matrix: php: [8.2, 8.3, 8.4] - symfony: ["5.4.*", "6.4.*", "7.2.*"] - exclude: - - php: 8.1 - symfony: "7.2.*" + symfony: ["5.4.*", "6.4.*", "6.4wApi", "7.2.*"] steps: - name: Checkout code @@ -25,11 +22,26 @@ jobs: coverage: none - name: Set Symfony version reference - run: echo "SF_REF=${MATRIX_SYMFONY%.*}" >> $GITHUB_ENV env: MATRIX_SYMFONY: ${{ matrix.symfony }} + run: | + if [[ "$MATRIX_SYMFONY" == *'*' ]]; then + echo "SF_REF=${MATRIX_SYMFONY%.*}" >> "$GITHUB_ENV" + else + echo "SF_REF=$MATRIX_SYMFONY" >> "$GITHUB_ENV" + fi + + - name: Set Composer Symfony constraint + env: + MATRIX_SYMFONY: ${{ matrix.symfony }} + run: | + if [[ "$MATRIX_SYMFONY" == "6.4wApi" ]]; then + echo "COMP_SYMFONY=6.4.*" >> "$GITHUB_ENV" + else + echo "COMP_SYMFONY=$MATRIX_SYMFONY" >> "$GITHUB_ENV" + fi - - name: Checkout Symfony ${{ env.SF_REF }} Sample + - name: Checkout Symfony ${{ env.SF_REF }} sample uses: actions/checkout@v4 with: repository: Codeception/symfony-module-tests @@ -51,17 +63,24 @@ jobs: run: composer require --dev --no-update "phpunit/phpunit=^10.0" - name: Install dependencies + env: + MATRIX_SYMFONY: ${{ matrix.symfony }} run: | - composer require symfony/finder=${{ matrix.symfony }} --no-update - composer require symfony/yaml=${{ matrix.symfony }} --no-update - composer require symfony/console=${{ matrix.symfony }} --no-update - composer require symfony/event-dispatcher=${{ matrix.symfony }} --no-update - composer require symfony/css-selector=${{ matrix.symfony }} --no-update - composer require symfony/dom-crawler=${{ matrix.symfony }} --no-update - composer require symfony/browser-kit=${{ matrix.symfony }} --no-update + composer require symfony/finder=${{ env.COMP_SYMFONY }} --no-update + composer require symfony/yaml=${{ env.COMP_SYMFONY }} --no-update + composer require symfony/console=${{ env.COMP_SYMFONY }} --no-update + composer require symfony/event-dispatcher=${{ env.COMP_SYMFONY }} --no-update + composer require symfony/css-selector=${{ env.COMP_SYMFONY }} --no-update + composer require symfony/dom-crawler=${{ env.COMP_SYMFONY }} --no-update + composer require symfony/browser-kit=${{ env.COMP_SYMFONY }} --no-update composer require vlucas/phpdotenv --no-update composer require codeception/module-asserts="3.*" --no-update composer require codeception/module-doctrine="3.*" --no-update + + if [[ "$MATRIX_SYMFONY" == "6.4wApi" ]]; then + composer require codeception/module-rest="3.*" --no-update + fi + composer update --prefer-dist --no-progress --no-dev - name: Validate Composer files @@ -84,6 +103,11 @@ jobs: php bin/console doctrine:fixtures:load --quiet working-directory: framework-tests + - name: Generate JWT keypair + if: ${{ matrix.symfony == '6.4wApi' }} + run: php bin/console lexik:jwt:generate-keypair --skip-if-exists + working-directory: framework-tests + - name: Run tests run: | php vendor/bin/codecept build -c framework-tests diff --git a/src/Codeception/Lib/Connector/Symfony.php b/src/Codeception/Lib/Connector/Symfony.php index 44d7595a..019317af 100644 --- a/src/Codeception/Lib/Connector/Symfony.php +++ b/src/Codeception/Lib/Connector/Symfony.php @@ -49,7 +49,7 @@ protected function doRequest(object $request): Response } /** - * Reboot kernel + * Reboots the kernel. * * Services from the list of persistent services * are updated from service container before kernel shutdown @@ -66,7 +66,8 @@ public function rebootKernel(): void } $this->persistDoctrineConnections(); - $this->kernel->reboot(null); + $this->ensureKernelShutdown(); + $this->kernel->boot(); $this->container = $this->getContainer(); foreach ($this->persistentServices as $serviceName => $service) { @@ -82,6 +83,12 @@ public function rebootKernel(): void } } + protected function ensureKernelShutdown(): void + { + $this->kernel->boot(); + $this->kernel->shutdown(); + } + private function getContainer(): ?ContainerInterface { /** @var ContainerInterface $container */