From 1838fae41cff34a0a5ff8637013b3e2febb27972 Mon Sep 17 00:00:00 2001 From: Aaron Gustavo Nieves <64917965+TavoNiievez@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:27:35 -0500 Subject: [PATCH] test: cover session login and add time, translation, twig, validator asserts --- .gitignore | 7 +- codeception.yml | 7 + composer.json | 14 +- readme.md | 4 + src/Codeception/Lib/Connector/Symfony.php | 6 +- .../Module/Symfony/BrowserAssertionsTrait.php | 19 +- .../Module/Symfony/RouterAssertionsTrait.php | 5 +- .../Module/Symfony/SessionAssertionsTrait.php | 2 +- tests/BrowserAssertionsTest.php | 69 ++++++ tests/BrowserKitConnectorTest.php | 26 +++ tests/ConsoleAssertionsTest.php | 38 ++++ tests/DomCrawlerAssertionsTest.php | 52 +++++ tests/FormAssertionsTest.php | 43 ++++ tests/Functional.suite.yml | 8 + tests/Functional/BrowserCest.php | 39 ++++ tests/Functional/ConsoleCest.php | 13 ++ tests/Functional/DomCrawlerCest.php | 24 +++ tests/Functional/ExampleCest.php | 14 ++ tests/Functional/FormCest.php | 15 ++ tests/Functional/LoggerCest.php | 13 ++ tests/Functional/MailerCest.php | 37 ++++ tests/Functional/MimeCest.php | 26 +++ tests/Functional/ParameterCest.php | 12 ++ tests/Functional/RouterCest.php | 19 ++ tests/Functional/SecurityCest.php | 23 ++ tests/Functional/ServicesCest.php | 15 ++ tests/Functional/SessionCest.php | 47 +++++ tests/Functional/TimeCest.php | 13 ++ tests/Functional/TranslationCest.php | 20 ++ tests/Functional/TwigCest.php | 16 ++ tests/Functional/ValidatorCest.php | 23 ++ tests/LoggerAssertionsTest.php | 64 ++++++ tests/MailerAssertionsTest.php | 84 ++++++++ tests/MimeAssertionsTest.php | 71 +++++++ tests/ParameterAssertionsTest.php | 46 ++++ tests/RouterAssertionsTest.php | 59 ++++++ tests/SecurityAssertionsTest.php | 73 +++++++ tests/ServicesAssertionsTest.php | 59 ++++++ tests/SessionAssertionsTest.php | 87 ++++++++ tests/TimeAssertionsTest.php | 64 ++++++ tests/TranslationAssertionsTest.php | 71 +++++++ tests/TwigAssertionsTest.php | 67 ++++++ tests/ValidatorAssertionsTest.php | 63 ++++++ tests/_app/HelloCommand.php | 24 +++ tests/_app/TestKernel.php | 198 ++++++++++++++++++ tests/_app/TestUser.php | 33 +++ tests/_app/ValidEntity.php | 18 ++ tests/_app/templates/home.html.twig | 2 + tests/_app/templates/layout.html.twig | 6 + tests/_app/translations/messages.en.yaml | 1 + tests/_support/.gitkeep | 0 51 files changed, 1745 insertions(+), 14 deletions(-) create mode 100644 codeception.yml create mode 100644 tests/BrowserAssertionsTest.php create mode 100644 tests/BrowserKitConnectorTest.php create mode 100644 tests/ConsoleAssertionsTest.php create mode 100644 tests/DomCrawlerAssertionsTest.php create mode 100644 tests/FormAssertionsTest.php create mode 100644 tests/Functional.suite.yml create mode 100644 tests/Functional/BrowserCest.php create mode 100644 tests/Functional/ConsoleCest.php create mode 100644 tests/Functional/DomCrawlerCest.php create mode 100644 tests/Functional/ExampleCest.php create mode 100644 tests/Functional/FormCest.php create mode 100644 tests/Functional/LoggerCest.php create mode 100644 tests/Functional/MailerCest.php create mode 100644 tests/Functional/MimeCest.php create mode 100644 tests/Functional/ParameterCest.php create mode 100644 tests/Functional/RouterCest.php create mode 100644 tests/Functional/SecurityCest.php create mode 100644 tests/Functional/ServicesCest.php create mode 100644 tests/Functional/SessionCest.php create mode 100644 tests/Functional/TimeCest.php create mode 100644 tests/Functional/TranslationCest.php create mode 100644 tests/Functional/TwigCest.php create mode 100644 tests/Functional/ValidatorCest.php create mode 100644 tests/LoggerAssertionsTest.php create mode 100644 tests/MailerAssertionsTest.php create mode 100644 tests/MimeAssertionsTest.php create mode 100644 tests/ParameterAssertionsTest.php create mode 100644 tests/RouterAssertionsTest.php create mode 100644 tests/SecurityAssertionsTest.php create mode 100644 tests/ServicesAssertionsTest.php create mode 100644 tests/SessionAssertionsTest.php create mode 100644 tests/TimeAssertionsTest.php create mode 100644 tests/TranslationAssertionsTest.php create mode 100644 tests/TwigAssertionsTest.php create mode 100644 tests/ValidatorAssertionsTest.php create mode 100644 tests/_app/HelloCommand.php create mode 100644 tests/_app/TestKernel.php create mode 100644 tests/_app/TestUser.php create mode 100644 tests/_app/ValidEntity.php create mode 100644 tests/_app/templates/home.html.twig create mode 100644 tests/_app/templates/layout.html.twig create mode 100644 tests/_app/translations/messages.en.yaml create mode 100644 tests/_support/.gitkeep diff --git a/.gitignore b/.gitignore index a816930..763daa1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,9 @@ /vendor/ /composer.lock /framework-tests -/.php-cs-fixer.cache \ No newline at end of file +/.php-cs-fixer.cache +tests/_output/ +tests/_support/_generated/ +tests/_support/FunctionalTester.php +var/ + diff --git a/codeception.yml b/codeception.yml new file mode 100644 index 0000000..aa0f0cb --- /dev/null +++ b/codeception.yml @@ -0,0 +1,7 @@ +namespace: Tests +actor_suffix: Tester +paths: + tests: tests + output: tests/_output + data: tests/_data + support: tests/_support diff --git a/composer.json b/composer.json index 03ca498..76790d8 100644 --- a/composer.json +++ b/composer.json @@ -24,15 +24,16 @@ "require": { "php": "^8.2", "ext-json": "*", - "codeception/codeception": "^5.3", "codeception/lib-innerbrowser": "^3.1 | ^4.0" }, "require-dev": { + "codeception/codeception": "^5.3", "codeception/module-asserts": "^3.0", "codeception/module-doctrine": "^3.1", "doctrine/orm": "^3.5", "friendsofphp/php-cs-fixer": "^3.85", "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.5", "symfony/browser-kit": "^5.4 | ^6.4 | ^7.3", "symfony/cache": "^5.4 | ^6.4 | ^7.3", "symfony/config": "^5.4 | ^6.4 | ^7.3", @@ -72,6 +73,17 @@ "Codeception\\": "src/Codeception/" } }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + }, + "files": [ + "tests/_app/TestKernel.php", + "tests/_app/HelloCommand.php", + "tests/_app/TestUser.php", + "tests/_app/ValidEntity.php" + ] + }, "config": { "sort-packages": true }, diff --git a/readme.md b/readme.md index 34cac75..25af797 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,7 @@ # Codeception Module Symfony A Codeception module for Symfony framework. +It can be used with Codeception or as a standalone Symfony BrowserKit client. [![Actions Status](https://github.com/Codeception/module-symfony/workflows/CI/badge.svg)](https://github.com/Codeception/module-symfony/actions) [![Latest Stable Version](https://poser.pugx.org/codeception/module-symfony/v/stable)](https://github.com/Codeception/module-symfony/releases) @@ -18,6 +19,9 @@ A Codeception module for Symfony framework. composer require "codeception/module-symfony" --dev ``` +To use the connector without Codeception, require the package and instantiate +`Codeception\\Lib\\Connector\\Symfony` with your kernel. + ## Documentation See [the module documentation](https://codeception.com/docs/modules/Symfony). diff --git a/src/Codeception/Lib/Connector/Symfony.php b/src/Codeception/Lib/Connector/Symfony.php index bf18eb2..e0aff60 100644 --- a/src/Codeception/Lib/Connector/Symfony.php +++ b/src/Codeception/Lib/Connector/Symfony.php @@ -17,7 +17,7 @@ use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\Profiler\Profiler; -use function codecept_debug; +use function function_exists; /** * @property KernelInterface $kernel @@ -73,7 +73,9 @@ public function rebootKernel(): void try { $this->container->set($name, $service); } catch (InvalidArgumentException $e) { - codecept_debug("[Symfony] Can't set persistent service {$name}: {$e->getMessage()}"); + if (function_exists('codecept_debug')) { + codecept_debug("[Symfony] Can't set persistent service {$name}: {$e->getMessage()}"); + } } } diff --git a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php index 8bd940b..d9eff8b 100644 --- a/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/BrowserAssertionsTrait.php @@ -309,8 +309,8 @@ public function rebootClientKernel(): void public function seePageIsAvailable(?string $url = null): void { if ($url !== null) { - $this->amOnPage($url); - $this->seeInCurrentUrl($url); + $this->getClient()->request('GET', $url); + $this->assertStringContainsString($url, $this->getClient()->getRequest()->getRequestUri()); } $this->assertResponseIsSuccessful(); @@ -328,12 +328,12 @@ public function seePageRedirectsTo(string $page, string $redirectsTo): void { $client = $this->getClient(); $client->followRedirects(false); - $this->amOnPage($page); + $client->request('GET', $page); $this->assertThatForResponse(new ResponseIsRedirected(), 'The response is not a redirection.'); $client->followRedirect(); - $this->seeInCurrentUrl($redirectsTo); + $this->assertStringContainsString($redirectsTo, $client->getRequest()->getRequestUri()); } /** @@ -363,9 +363,16 @@ public function submitSymfonyForm(string $name, array $fields): void $params[$fixedKey] = $value; } - $button = sprintf('%s_submit', $name); + if (method_exists($this, 'submitForm')) { // @phpstan-ignore-line + $button = sprintf('%s_submit', $name); + $this->submitForm($selector, $params, $button); + return; + } - $this->submitForm($selector, $params, $button); + $node = $this->getClient()->getCrawler()->filter($selector); + $this->assertNotEmpty($node, sprintf('Form "%s" not found.', $selector)); + $form = $node->form(); + $this->getClient()->submit($form, $params); } protected function assertThatForClient(Constraint $constraint, string $message = ''): void diff --git a/src/Codeception/Module/Symfony/RouterAssertionsTrait.php b/src/Codeception/Module/Symfony/RouterAssertionsTrait.php index cdbd41e..85ec9aa 100644 --- a/src/Codeception/Module/Symfony/RouterAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/RouterAssertionsTrait.php @@ -112,8 +112,7 @@ private function getCurrentRouteMatch(string $routeName): array { $this->assertRouteExists($routeName); - $url = $this->grabFromCurrentUrl(); - Assert::assertIsString($url, 'Unable to obtain current URL.'); + $url = $this->getClient()->getRequest()->getRequestUri(); $path = (string) parse_url($url, PHP_URL_PATH); /** @var array $match */ @@ -143,7 +142,7 @@ private function assertRouteExists(string $routeName): void /** @param array $params */ private function openRoute(string $routeName, array $params = []): void { - $this->amOnPage($this->grabRouterService()->generate($routeName, $params)); + $this->getClient()->request('GET', $this->grabRouterService()->generate($routeName, $params)); } protected function grabRouterService(): RouterInterface diff --git a/src/Codeception/Module/Symfony/SessionAssertionsTrait.php b/src/Codeception/Module/Symfony/SessionAssertionsTrait.php index 7052894..47137f1 100644 --- a/src/Codeception/Module/Symfony/SessionAssertionsTrait.php +++ b/src/Codeception/Module/Symfony/SessionAssertionsTrait.php @@ -82,7 +82,7 @@ public function dontSeeInSession(string $attribute, mixed $value = null): void */ public function goToLogoutPath(): void { - $this->amOnPage($this->getLogoutUrlGenerator()->getLogoutPath()); + $this->getClient()->request('GET', $this->getLogoutUrlGenerator()->getLogoutPath()); } /** diff --git a/tests/BrowserAssertionsTest.php b/tests/BrowserAssertionsTest.php new file mode 100644 index 0000000..2cec5d7 --- /dev/null +++ b/tests/BrowserAssertionsTest.php @@ -0,0 +1,69 @@ +client = new KernelBrowser(self::$kernel); + $this->client->getCookieJar()->set(new Cookie('browser_cookie', 'value')); + } + + protected function tearDown(): void + { + parent::tearDown(); + restore_exception_handler(); + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + public function testBrowserAssertions(): void + { + $this->client->request('GET', '/sample'); + + $this->assertBrowserHasCookie('browser_cookie'); + $this->assertBrowserCookieValueSame('browser_cookie', 'value'); + $this->assertBrowserNotHasCookie('missing_cookie'); + + $this->assertRequestAttributeValueSame('foo', 'bar'); + + $this->assertResponseHasCookie('response_cookie'); + $this->assertResponseCookieValueSame('response_cookie', 'yum'); + $this->assertResponseNotHasCookie('other_cookie'); + + $this->assertResponseHasHeader('X-Test'); + $this->assertResponseHeaderSame('X-Test', '1'); + $this->assertResponseHeaderNotSame('X-Test', '2'); + $this->assertResponseNotHasHeader('X-None'); + + $this->assertResponseFormatSame('html'); + $this->assertResponseIsSuccessful(); + $this->assertResponseStatusCodeSame(200); + $this->assertRouteSame('sample'); + + $this->seePageIsAvailable('/sample'); + $this->seePageRedirectsTo('/redirect', '/sample'); + + $this->client->request('GET', '/unprocessable'); + $this->assertResponseIsUnprocessable(); + } +} diff --git a/tests/BrowserKitConnectorTest.php b/tests/BrowserKitConnectorTest.php new file mode 100644 index 0000000..163f634 --- /dev/null +++ b/tests/BrowserKitConnectorTest.php @@ -0,0 +1,26 @@ +boot(); + $browser = new SymfonyConnector($kernel); + + $browser->request('GET', '/'); + + $this->assertSame(200, $browser->getResponse()->getStatusCode()); + $this->assertSame('OK', $browser->getResponse()->getContent()); + } + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } +} diff --git a/tests/ConsoleAssertionsTest.php b/tests/ConsoleAssertionsTest.php new file mode 100644 index 0000000..f3d2c97 --- /dev/null +++ b/tests/ConsoleAssertionsTest.php @@ -0,0 +1,38 @@ +get($serviceId); + } + + protected function unpersistService(string $serviceName): void + { + // no-op for tests + } + + public function testRunSymfonyConsoleCommand(): void + { + $output = $this->runSymfonyConsoleCommand('app:hello', ['name' => 'Codeception']); + $this->assertStringContainsString('Hello Codeception', $output); + } + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } +} diff --git a/tests/DomCrawlerAssertionsTest.php b/tests/DomCrawlerAssertionsTest.php new file mode 100644 index 0000000..de50d8c --- /dev/null +++ b/tests/DomCrawlerAssertionsTest.php @@ -0,0 +1,52 @@ +client = new KernelBrowser(self::$kernel); + $this->client->request('GET', '/sample'); + } + + protected function tearDown(): void + { + parent::tearDown(); + restore_exception_handler(); + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + public function testDomCrawlerAssertions(): void + { + $this->assertCheckboxChecked('agree'); + $this->assertCheckboxNotChecked('subscribe'); + $this->assertInputValueSame('username', 'john'); + $this->assertInputValueNotSame('username', 'doe'); + $this->assertPageTitleContains('Test'); + $this->assertPageTitleSame('Test Page'); + $this->assertSelectorExists('#greeting'); + $this->assertSelectorNotExists('#missing'); + $this->assertSelectorTextContains('#greeting', 'Hello'); + $this->assertSelectorTextNotContains('#greeting', 'Bye'); + $this->assertSelectorTextSame('#greeting', 'Hello World'); + } +} diff --git a/tests/FormAssertionsTest.php b/tests/FormAssertionsTest.php new file mode 100644 index 0000000..cf31d11 --- /dev/null +++ b/tests/FormAssertionsTest.php @@ -0,0 +1,43 @@ +client = new KernelBrowser(self::$kernel); + $this->client->request('GET', '/sample'); + } + + protected function tearDown(): void + { + parent::tearDown(); + restore_exception_handler(); + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + public function testFormAssertions(): void + { + $this->assertFormValue('#testForm', 'field1', 'value1'); + $this->assertNoFormValue('#testForm', 'missing_field'); + } +} diff --git a/tests/Functional.suite.yml b/tests/Functional.suite.yml new file mode 100644 index 0000000..e010b19 --- /dev/null +++ b/tests/Functional.suite.yml @@ -0,0 +1,8 @@ +actor: FunctionalTester +modules: + enabled: + - Symfony: + app_path: tests/_app + environment: 'test' + kernel_class: TestKernel + - Asserts diff --git a/tests/Functional/BrowserCest.php b/tests/Functional/BrowserCest.php new file mode 100644 index 0000000..76437f2 --- /dev/null +++ b/tests/Functional/BrowserCest.php @@ -0,0 +1,39 @@ +setCookie('browser_cookie', 'value'); + $I->amOnPage('/sample'); + + $I->assertBrowserHasCookie('browser_cookie'); + $I->assertBrowserCookieValueSame('browser_cookie', 'value'); + $I->assertBrowserNotHasCookie('missing_cookie'); + + $I->assertRequestAttributeValueSame('foo', 'bar'); + + $I->assertResponseHasCookie('response_cookie'); + $I->assertResponseCookieValueSame('response_cookie', 'yum'); + $I->assertResponseNotHasCookie('other_cookie'); + + $I->assertResponseHasHeader('X-Test'); + $I->assertResponseHeaderSame('X-Test', '1'); + $I->assertResponseHeaderNotSame('X-Test', '2'); + $I->assertResponseNotHasHeader('X-None'); + + $I->assertResponseFormatSame('html'); + $I->assertResponseIsSuccessful(); + $I->assertResponseStatusCodeSame(200); + $I->assertRouteSame('sample'); + + $I->seePageIsAvailable('/sample'); + $I->seePageRedirectsTo('/redirect', '/sample'); + + $I->amOnPage('/unprocessable'); + $I->assertResponseIsUnprocessable(); + } +} diff --git a/tests/Functional/ConsoleCest.php b/tests/Functional/ConsoleCest.php new file mode 100644 index 0000000..1bd0ffa --- /dev/null +++ b/tests/Functional/ConsoleCest.php @@ -0,0 +1,13 @@ +runSymfonyConsoleCommand('app:hello', ['name' => 'Codeception']); + $I->assertStringContainsString('Hello Codeception', $output); + } +} diff --git a/tests/Functional/DomCrawlerCest.php b/tests/Functional/DomCrawlerCest.php new file mode 100644 index 0000000..938499c --- /dev/null +++ b/tests/Functional/DomCrawlerCest.php @@ -0,0 +1,24 @@ +amOnPage('/sample'); + + $I->assertCheckboxChecked('agree'); + $I->assertCheckboxNotChecked('subscribe'); + $I->assertInputValueSame('username', 'john'); + $I->assertInputValueNotSame('username', 'doe'); + $I->assertPageTitleContains('Test'); + $I->assertPageTitleSame('Test Page'); + $I->assertSelectorExists('#greeting'); + $I->assertSelectorNotExists('#missing'); + $I->assertSelectorTextContains('#greeting', 'Hello'); + $I->assertSelectorTextNotContains('#greeting', 'Bye'); + $I->assertSelectorTextSame('#greeting', 'Hello World'); + } +} diff --git a/tests/Functional/ExampleCest.php b/tests/Functional/ExampleCest.php new file mode 100644 index 0000000..ea74f95 --- /dev/null +++ b/tests/Functional/ExampleCest.php @@ -0,0 +1,14 @@ +amOnPage('/'); + $I->seeResponseCodeIs(200); + $I->see('OK'); + } +} diff --git a/tests/Functional/FormCest.php b/tests/Functional/FormCest.php new file mode 100644 index 0000000..0116a03 --- /dev/null +++ b/tests/Functional/FormCest.php @@ -0,0 +1,15 @@ +amOnPage('/sample'); + + $I->assertFormValue('#testForm', 'field1', 'value1'); + $I->assertNoFormValue('#testForm', 'missing_field'); + } +} diff --git a/tests/Functional/LoggerCest.php b/tests/Functional/LoggerCest.php new file mode 100644 index 0000000..8a5d453 --- /dev/null +++ b/tests/Functional/LoggerCest.php @@ -0,0 +1,13 @@ +amOnPage('/sample'); + $I->dontSeeDeprecations(); + } +} diff --git a/tests/Functional/MailerCest.php b/tests/Functional/MailerCest.php new file mode 100644 index 0000000..cfe4140 --- /dev/null +++ b/tests/Functional/MailerCest.php @@ -0,0 +1,37 @@ +grabService('mailer.message_logger_listener'); + $logger->reset(); + $I->dontSeeEmailIsSent(); + + $queuedEmail = (new Email())->from('queued@example.com')->to('queued@example.com'); + $envelope = new Envelope(new Address('queued@example.com'), [new Address('queued@example.com')]); + $queuedEvent = new MessageEvent($queuedEmail, $envelope, 'smtp', true); + $logger->onMessage($queuedEvent); + + $I->assertQueuedEmailCount(1); + $I->assertEmailIsQueued($queuedEvent); + + $I->amOnRoute('send_email'); + + $I->assertEmailCount(1); + $I->seeEmailIsSent(); + $I->grabLastSentEmail(); + $I->grabSentEmails(); + $event = $I->getMailerEvent(1); + $I->assertEmailIsNotQueued($event); + } +} diff --git a/tests/Functional/MimeCest.php b/tests/Functional/MimeCest.php new file mode 100644 index 0000000..ca89fea --- /dev/null +++ b/tests/Functional/MimeCest.php @@ -0,0 +1,26 @@ +grabService('mailer.message_logger_listener'); + $logger->reset(); + + $I->amOnRoute('send_email'); + + $I->assertEmailAddressContains('To', 'jane_doe@example.com'); + $I->assertEmailAttachmentCount(1); + $I->assertEmailHasHeader('To'); + $I->assertEmailHeaderSame('To', 'jane_doe@example.com'); + $I->assertEmailHeaderNotSame('To', 'john_doe@example.com'); + $I->assertEmailHtmlBodyContains('HTML body'); + $I->assertEmailHtmlBodyNotContains('password'); + $I->assertEmailNotHasHeader('Bcc'); + $I->assertEmailTextBodyContains('Example text body'); + $I->assertEmailTextBodyNotContains('Secret'); + } +} diff --git a/tests/Functional/ParameterCest.php b/tests/Functional/ParameterCest.php new file mode 100644 index 0000000..d5ef8eb --- /dev/null +++ b/tests/Functional/ParameterCest.php @@ -0,0 +1,12 @@ +assertSame('value', $I->grabParameter('app.param')); + } +} diff --git a/tests/Functional/RouterCest.php b/tests/Functional/RouterCest.php new file mode 100644 index 0000000..62e2e56 --- /dev/null +++ b/tests/Functional/RouterCest.php @@ -0,0 +1,19 @@ +amOnRoute('sample'); + $I->seeCurrentRouteIs('sample'); + $I->seeInCurrentRoute('sample'); + $I->seeCurrentActionIs('TestKernel::sample'); + + $I->amOnAction('TestKernel::index'); + $I->seeCurrentRouteIs('index'); + $I->invalidateCachedRouter(); + } +} diff --git a/tests/Functional/SecurityCest.php b/tests/Functional/SecurityCest.php new file mode 100644 index 0000000..439162b --- /dev/null +++ b/tests/Functional/SecurityCest.php @@ -0,0 +1,23 @@ +dontSeeAuthentication(); + $hasher = $I->grabService('security.password_hasher'); + $hashed = $hasher->hashPassword(new \TestUser('tmp', ''), 'password'); + $user = new \TestUser('john@example.com', $hashed, ['ROLE_USER', 'ROLE_ADMIN']); + $I->amLoggedInAs($user); + + $I->seeAuthentication(); + + $I->seeUserHasRole('ROLE_ADMIN'); + $I->seeUserHasRoles(['ROLE_USER', 'ROLE_ADMIN']); + $I->seeUserPasswordDoesNotNeedRehash(); + } +} diff --git a/tests/Functional/ServicesCest.php b/tests/Functional/ServicesCest.php new file mode 100644 index 0000000..794b98b --- /dev/null +++ b/tests/Functional/ServicesCest.php @@ -0,0 +1,15 @@ +grabService('router'); + $I->persistService('router'); + $I->persistPermanentService('router'); + $I->unpersistService('router'); + } +} diff --git a/tests/Functional/SessionCest.php b/tests/Functional/SessionCest.php new file mode 100644 index 0000000..450fb00 --- /dev/null +++ b/tests/Functional/SessionCest.php @@ -0,0 +1,47 @@ +grabService('service_container'); + $factory = $I->grabService('session.factory'); + $session = $factory->createSession(); + $container->set('session', $session); + $session->set('key1', 'value1'); + $session->set('key2', 'value2'); + $session->save(); + + $I->seeInSession('key1'); + $I->seeInSession('key1', 'value1'); + $I->dontSeeInSession('missing'); + $I->dontSeeInSession('key1', 'other'); + $I->seeSessionHasValues(['key1', 'key2']); + $I->seeSessionHasValues(['key1' => 'value1', 'key2' => 'value2']); + } + + public function loginAndLogoutAssertions(FunctionalTester $I): void + { + $user = new InMemoryUser('john@example.com', null, ['ROLE_USER']); + + $I->amLoggedInAs($user); + $I->seeInSession('_security_main'); + $I->logout(); + $I->dontSeeInSession('_security_main'); + + $token = new UsernamePasswordToken($user, 'main', ['ROLE_USER']); + $I->amLoggedInWithToken($token); + $I->seeInSession('_security_main'); + $I->goToLogoutPath(); + + $I->amLoggedInAs($user); + $I->seeInSession('_security_main'); + $I->logoutProgrammatically(); + $I->dontSeeInSession('_security_main'); + } +} diff --git a/tests/Functional/TimeCest.php b/tests/Functional/TimeCest.php new file mode 100644 index 0000000..3b901e5 --- /dev/null +++ b/tests/Functional/TimeCest.php @@ -0,0 +1,13 @@ +amOnRoute('sample'); + $I->seeRequestTimeIsLessThan(500); + } +} diff --git a/tests/Functional/TranslationCest.php b/tests/Functional/TranslationCest.php new file mode 100644 index 0000000..63f9cc5 --- /dev/null +++ b/tests/Functional/TranslationCest.php @@ -0,0 +1,20 @@ +amOnRoute('translation'); + $I->dontSeeMissingTranslations(); + $I->dontSeeFallbackTranslations(); + $I->assertGreaterThanOrEqual(0, $I->grabDefinedTranslationsCount()); + $I->seeAllTranslationsDefined(); + $I->seeDefaultLocaleIs('en'); + $I->seeFallbackLocalesAre(['es']); + $I->seeFallbackTranslationsCountLessThan(1); + $I->seeMissingTranslationsCountLessThan(1); + } +} diff --git a/tests/Functional/TwigCest.php b/tests/Functional/TwigCest.php new file mode 100644 index 0000000..74515dd --- /dev/null +++ b/tests/Functional/TwigCest.php @@ -0,0 +1,16 @@ +amOnRoute('twig'); + $I->seeRenderedTemplate('home.html.twig'); + $I->seeRenderedTemplate('layout.html.twig'); + $I->dontSeeRenderedTemplate('other.html.twig'); + $I->seeCurrentTemplateIs('home.html.twig'); + } +} diff --git a/tests/Functional/ValidatorCest.php b/tests/Functional/ValidatorCest.php new file mode 100644 index 0000000..3da2732 --- /dev/null +++ b/tests/Functional/ValidatorCest.php @@ -0,0 +1,23 @@ +seeViolatedConstraint($invalid); + $I->seeViolatedConstraint($invalid, 'name'); + $I->seeViolatedConstraint($invalid, 'short', Assert\Length::class); + $I->seeViolatedConstraintsCount(2, $invalid); + $I->seeViolatedConstraintsCount(1, $invalid, 'name'); + $I->seeViolatedConstraintMessage('too short', $invalid, 'short'); + $I->dontSeeViolatedConstraint($valid); + $I->dontSeeViolatedConstraint($invalid, 'short', Assert\NotBlank::class); + } +} diff --git a/tests/LoggerAssertionsTest.php b/tests/LoggerAssertionsTest.php new file mode 100644 index 0000000..b9c0c1c --- /dev/null +++ b/tests/LoggerAssertionsTest.php @@ -0,0 +1,64 @@ +client = new KernelBrowser(self::$kernel); + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected function grabService(string $serviceId): object + { + return self::getContainer()->get($serviceId); + } + + protected function unpersistService(string $serviceName): void + { + // no-op for tests + } + + public function testDontSeeDeprecations(): void + { + $this->client->request('GET', '/sample'); + $this->dontSeeDeprecations(); + } + + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } + + protected function grabCollector(DataCollectorName $name, string $function): DataCollectorInterface + { + /** @var Profiler $profiler */ + $profiler = self::getContainer()->get('profiler'); + $profile = $profiler->collect($this->client->getRequest(), $this->client->getResponse()); + return $profile->getCollector($name->value); + } +} diff --git a/tests/MailerAssertionsTest.php b/tests/MailerAssertionsTest.php new file mode 100644 index 0000000..552413b --- /dev/null +++ b/tests/MailerAssertionsTest.php @@ -0,0 +1,84 @@ +kernel = new \TestKernel('test', true); + $this->kernel->boot(); + $this->client = new KernelBrowser($this->kernel); + $this->getService('mailer.message_logger_listener')->reset(); + } + + protected function tearDown(): void + { + $this->kernel->shutdown(); + restore_exception_handler(); + parent::tearDown(); + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected function getService(string $serviceId): object + { + $container = $this->kernel->getContainer(); + if ($container->has('test.service_container')) { + $container = $container->get('test.service_container'); + } + return $container->get($serviceId); + } + + public function testMailerAssertions(): void + { + $this->dontSeeEmailIsSent(); + + $queuedEmail = (new Email()) + ->from('queued@example.com') + ->to('queued@example.com'); + $envelope = new Envelope(new Address('queued@example.com'), [new Address('queued@example.com')]); + $queuedEvent = new MessageEvent($queuedEmail, $envelope, 'smtp', true); + /** @var MessageLoggerListener $logger */ + $logger = $this->getService('mailer.message_logger_listener'); + $logger->onMessage($queuedEvent); + + $this->assertQueuedEmailCount(1); + $this->assertEmailIsQueued($queuedEvent); + + $mailer = $this->getService('mailer'); + $mailer->send((new Email()) + ->from('john_doe@example.com') + ->to('jane_doe@example.com') + ->subject('Test') + ->text('Example text body') + ->html('

HTML body

') + ->attach('Attachment content', 'test.txt') + ); + + $this->assertEmailCount(1); + $this->seeEmailIsSent(); + $this->grabLastSentEmail(); + $this->grabSentEmails(); + $event = $this->getMailerEvent(1); + $this->assertEmailIsNotQueued($event); + } +} diff --git a/tests/MimeAssertionsTest.php b/tests/MimeAssertionsTest.php new file mode 100644 index 0000000..8c55086 --- /dev/null +++ b/tests/MimeAssertionsTest.php @@ -0,0 +1,71 @@ +kernel = new \TestKernel('test', true); + $this->kernel->boot(); + $this->client = new KernelBrowser($this->kernel); + $this->getService('mailer.message_logger_listener')->reset(); + + $mailer = $this->getService('mailer'); + $mailer->send((new Email()) + ->from('john_doe@example.com') + ->to('jane_doe@example.com') + ->subject('Test') + ->text('Example text body') + ->html('

HTML body

') + ->attach('Attachment content', 'test.txt') + ); + } + + protected function tearDown(): void + { + $this->kernel->shutdown(); + restore_exception_handler(); + parent::tearDown(); + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected function getService(string $serviceId): object + { + $container = $this->kernel->getContainer(); + if ($container->has('test.service_container')) { + $container = $container->get('test.service_container'); + } + return $container->get($serviceId); + } + + public function testMimeAssertions(): void + { + $this->assertEmailAddressContains('To', 'jane_doe@example.com'); + $this->assertEmailAttachmentCount(1); + $this->assertEmailHasHeader('To'); + $this->assertEmailHeaderSame('To', 'jane_doe@example.com'); + $this->assertEmailHeaderNotSame('To', 'john_doe@example.com'); + $this->assertEmailHtmlBodyContains('HTML body'); + $this->assertEmailHtmlBodyNotContains('password'); + $this->assertEmailNotHasHeader('Bcc'); + $this->assertEmailTextBodyContains('Example text body'); + $this->assertEmailTextBodyNotContains('Secret'); + } +} diff --git a/tests/ParameterAssertionsTest.php b/tests/ParameterAssertionsTest.php new file mode 100644 index 0000000..398005b --- /dev/null +++ b/tests/ParameterAssertionsTest.php @@ -0,0 +1,46 @@ +client = new KernelBrowser(self::$kernel); + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected function grabService(string $serviceId): object + { + return self::getContainer()->get($serviceId); + } + + public function testGrabParameter(): void + { + $this->assertSame('value', $this->grabParameter('app.param')); + } + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } +} diff --git a/tests/RouterAssertionsTest.php b/tests/RouterAssertionsTest.php new file mode 100644 index 0000000..5d68527 --- /dev/null +++ b/tests/RouterAssertionsTest.php @@ -0,0 +1,59 @@ +client = new KernelBrowser(self::$kernel); + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected function grabService(string $serviceId): object + { + return self::getContainer()->get($serviceId); + } + + protected function unpersistService(string $serviceName): void + { + // no-op for tests + } + + public function testRouterAssertions(): void + { + $this->amOnRoute('sample'); + $this->seeCurrentRouteIs('sample'); + $this->seeInCurrentRoute('sample'); + $this->seeCurrentActionIs('TestKernel::sample'); + + $this->amOnAction('TestKernel::index'); + $this->seeCurrentRouteIs('index'); + + $this->invalidateCachedRouter(); + } + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } +} diff --git a/tests/SecurityAssertionsTest.php b/tests/SecurityAssertionsTest.php new file mode 100644 index 0000000..072049b --- /dev/null +++ b/tests/SecurityAssertionsTest.php @@ -0,0 +1,73 @@ +client = new KernelBrowser(self::$kernel); + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected function grabService(string $serviceId): object + { + return self::getContainer()->get($serviceId); + } + + protected function getService(string $serviceId): object + { + return self::getContainer()->get($serviceId); + } + + protected function grabSecurityService() + { + return new Security(self::getContainer()); + } + + public function testSecurityAssertions(): void + { + $this->dontSeeAuthentication(); + $this->dontSeeRememberedAuthentication(); + + $hasher = $this->grabService('security.password_hasher'); + $hashed = $hasher->hashPassword(new \TestUser('tmp', ''), 'password'); + $user = new \TestUser('john@example.com', $hashed, ['ROLE_USER', 'ROLE_ADMIN']); + $this->getClient()->loginUser($user); + + $this->seeAuthentication(); + + $this->getClient()->getCookieJar()->set(new Cookie('REMEMBERME', 'test')); + $this->seeRememberedAuthentication(); + + $this->seeUserHasRole('ROLE_ADMIN'); + $this->seeUserHasRoles(['ROLE_USER', 'ROLE_ADMIN']); + $this->seeUserPasswordDoesNotNeedRehash(); + } + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } +} diff --git a/tests/ServicesAssertionsTest.php b/tests/ServicesAssertionsTest.php new file mode 100644 index 0000000..3972cfe --- /dev/null +++ b/tests/ServicesAssertionsTest.php @@ -0,0 +1,59 @@ +client = new KernelBrowser(self::$kernel); + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + protected function _getContainer(): ContainerInterface + { + return self::getContainer(); + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + public function testServicesAssertions(): void + { + $this->grabService('router'); + $this->persistService('router'); + $this->assertArrayHasKey('router', $this->persistentServices); + + $this->persistPermanentService('router'); + $this->assertArrayHasKey('router', $this->permanentServices); + + $this->unpersistService('router'); + $this->assertArrayNotHasKey('router', $this->persistentServices); + $this->assertArrayNotHasKey('router', $this->permanentServices); + } + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } +} diff --git a/tests/SessionAssertionsTest.php b/tests/SessionAssertionsTest.php new file mode 100644 index 0000000..b277762 --- /dev/null +++ b/tests/SessionAssertionsTest.php @@ -0,0 +1,87 @@ +client = new KernelBrowser(self::$kernel); + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected function grabService(string $serviceId): object + { + return self::getContainer()->get($serviceId); + } + + protected function _getContainer(): ContainerInterface + { + return self::getContainer(); + } + + public function testSessionAssertions(): void + { + $container = self::getContainer(); + $factory = $container->get('session.factory'); + $session = $factory->createSession(); + $container->set('session', $session); + $session->set('key1', 'value1'); + $session->set('key2', 'value2'); + $session->save(); + + $this->seeInSession('key1'); + $this->seeInSession('key1', 'value1'); + $this->dontSeeInSession('missing'); + $this->dontSeeInSession('key1', 'other'); + $this->seeSessionHasValues(['key1', 'key2']); + $this->seeSessionHasValues(['key1' => 'value1', 'key2' => 'value2']); + } + + public function testLoginAndLogoutAssertions(): void + { + $user = new InMemoryUser('john@example.com', null, ['ROLE_USER']); + + $this->amLoggedInAs($user); + $this->seeInSession('_security_main'); + $this->logout(); + $this->dontSeeInSession('_security_main'); + + $token = new UsernamePasswordToken($user, 'main', ['ROLE_USER']); + $this->amLoggedInWithToken($token); + $this->seeInSession('_security_main'); + $this->goToLogoutPath(); + + $this->amLoggedInAs($user); + $this->seeInSession('_security_main'); + $this->logoutProgrammatically(); + $this->dontSeeInSession('_security_main'); + } + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } +} diff --git a/tests/TimeAssertionsTest.php b/tests/TimeAssertionsTest.php new file mode 100644 index 0000000..12a7c32 --- /dev/null +++ b/tests/TimeAssertionsTest.php @@ -0,0 +1,64 @@ +client = new KernelBrowser(self::$kernel); + $this->client->request('GET', '/sample'); + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected function grabService(string $serviceId): object + { + return self::getContainer()->get($serviceId); + } + + protected function _getContainer(): ContainerInterface + { + return self::getContainer(); + } + + public function testRequestTime(): void + { + $this->seeRequestTimeIsLessThan(500); + } + + protected function grabCollector(DataCollectorName $name, string $function): DataCollectorInterface + { + /** @var Profiler $profiler */ + $profiler = self::getContainer()->get('profiler'); + $profile = $profiler->collect($this->client->getRequest(), $this->client->getResponse()); + return $profile->getCollector($name->value); + } + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } +} diff --git a/tests/TranslationAssertionsTest.php b/tests/TranslationAssertionsTest.php new file mode 100644 index 0000000..6a91c28 --- /dev/null +++ b/tests/TranslationAssertionsTest.php @@ -0,0 +1,71 @@ +client = new KernelBrowser(self::$kernel); + $this->client->request('GET', '/translation'); + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected function grabService(string $serviceId): object + { + return self::getContainer()->get($serviceId); + } + + protected function _getContainer(): ContainerInterface + { + return self::getContainer(); + } + + public function testTranslationAssertions(): void + { + $this->dontSeeMissingTranslations(); + $this->dontSeeFallbackTranslations(); + $this->assertGreaterThanOrEqual(0, $this->grabDefinedTranslationsCount()); + $this->seeAllTranslationsDefined(); + $this->seeDefaultLocaleIs('en'); + $this->seeFallbackLocalesAre(['es']); + $this->seeFallbackTranslationsCountLessThan(1); + $this->seeMissingTranslationsCountLessThan(1); + } + + protected function grabCollector(DataCollectorName $name, string $function): DataCollectorInterface + { + /** @var Profiler $profiler */ + $profiler = self::getContainer()->get('profiler'); + $profile = $profiler->collect($this->client->getRequest(), $this->client->getResponse()); + return $profile->getCollector($name->value); + } + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } +} diff --git a/tests/TwigAssertionsTest.php b/tests/TwigAssertionsTest.php new file mode 100644 index 0000000..0bdaccd --- /dev/null +++ b/tests/TwigAssertionsTest.php @@ -0,0 +1,67 @@ +client = new KernelBrowser(self::$kernel); + $this->client->request('GET', '/twig'); + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected function grabService(string $serviceId): object + { + return self::getContainer()->get($serviceId); + } + + protected function _getContainer(): ContainerInterface + { + return self::getContainer(); + } + + public function testTwigAssertions(): void + { + $this->seeRenderedTemplate('home.html.twig'); + $this->seeRenderedTemplate('layout.html.twig'); + $this->dontSeeRenderedTemplate('other.html.twig'); + $this->seeCurrentTemplateIs('home.html.twig'); + } + + protected function grabCollector(DataCollectorName $name, string $function): DataCollectorInterface + { + /** @var Profiler $profiler */ + $profiler = self::getContainer()->get('profiler'); + $profile = $profiler->collect($this->client->getRequest(), $this->client->getResponse()); + return $profile->getCollector($name->value); + } + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } +} diff --git a/tests/ValidatorAssertionsTest.php b/tests/ValidatorAssertionsTest.php new file mode 100644 index 0000000..5aa2bcc --- /dev/null +++ b/tests/ValidatorAssertionsTest.php @@ -0,0 +1,63 @@ +client = new KernelBrowser(self::$kernel); + } + + protected static function getKernelClass(): string + { + return \TestKernel::class; + } + + protected function getClient(): KernelBrowser + { + return $this->client; + } + + protected function grabService(string $serviceId): object + { + return self::getContainer()->get($serviceId); + } + + protected function _getContainer(): ContainerInterface + { + return self::getContainer(); + } + + public function testValidatorAssertions(): void + { + $invalid = new \ValidEntity(); + $valid = new \ValidEntity('John', 'abcd'); + + $this->seeViolatedConstraint($invalid); + $this->seeViolatedConstraint($invalid, 'name'); + $this->seeViolatedConstraint($invalid, 'short', Assert\Length::class); + $this->seeViolatedConstraintsCount(2, $invalid); + $this->seeViolatedConstraintsCount(1, $invalid, 'name'); + $this->seeViolatedConstraintMessage('too short', $invalid, 'short'); + $this->dontSeeViolatedConstraint($valid); + $this->dontSeeViolatedConstraint($invalid, 'short', Assert\NotBlank::class); + } + + protected function tearDown(): void + { + restore_exception_handler(); + parent::tearDown(); + } +} diff --git a/tests/_app/HelloCommand.php b/tests/_app/HelloCommand.php new file mode 100644 index 0000000..f583d40 --- /dev/null +++ b/tests/_app/HelloCommand.php @@ -0,0 +1,24 @@ +setDescription('Greets the user') + ->addArgument('name', InputArgument::OPTIONAL, 'Name to greet', 'World'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $name = (string) $input->getArgument('name'); + $output->writeln('Hello ' . $name); + return Command::SUCCESS; + } +} diff --git a/tests/_app/TestKernel.php b/tests/_app/TestKernel.php new file mode 100644 index 0000000..d8dc71e --- /dev/null +++ b/tests/_app/TestKernel.php @@ -0,0 +1,198 @@ +extension('framework', [ + 'secret' => 'test', + 'test' => true, + 'profiler' => ['enabled' => true, 'collect' => true, 'collect_serializer_data' => true], + 'property_info' => ['with_constructor_extractor' => false], + 'session' => [ + 'handler_id' => null, + 'storage_factory_id' => 'session.storage.factory.mock_file', + ], + 'mailer' => ['dsn' => 'null://null'], + 'default_locale' => 'en', + 'translator' => [ + 'default_path' => __DIR__ . '/translations', + 'fallbacks' => ['es'], + ], + 'validation' => ['enabled' => true], + ]); + + $container->extension('twig', [ + 'default_path' => __DIR__ . '/templates', + ]); + + $container->extension('security', [ + 'password_hashers' => [ + PasswordAuthenticatedUserInterface::class => 'auto', + ], + 'providers' => [ + 'users_in_memory' => [ + 'memory' => [ + 'users' => [], + ], + ], + ], + 'firewalls' => [ + 'main' => [ + 'lazy' => true, + 'provider' => 'users_in_memory', + 'remember_me' => ['secret' => 'test'], + 'logout' => ['path' => 'logout'], + ], + ], + ]); + + $container->parameters()->set('app.param', 'value'); + + $services = $container->services(); + $services->set(HelloCommand::class, HelloCommand::class) + ->tag('console.command', ['command' => 'app:hello']) + ->public(); + $services->set(Security::class) + ->public() + ->arg('$container', service('test.service_container')); + $services->alias('security.helper', Security::class)->public(); + $services->set('mailer.message_logger_listener', MessageLoggerListener::class) + ->tag('kernel.event_subscriber') + ->public(); + } + + public function registerBundles(): iterable + { + return [ + new FrameworkBundle(), + new SecurityBundle(), + new TwigBundle(), + ]; + } + + protected function configureRoutes(RoutingConfigurator $routes): void + { + $routes->add('index', '/') + ->controller(self::class . '::index'); + $routes->add('sample', '/sample') + ->controller(self::class . '::sample'); + $routes->add('redirect', '/redirect') + ->controller(self::class . '::redirect'); + $routes->add('unprocessable', '/unprocessable') + ->controller(self::class . '::unprocessable'); + $routes->add('session', '/session') + ->controller(self::class . '::session'); + $routes->add('deprecated', '/deprecated') + ->controller(self::class . '::deprecated'); + $routes->add('send_email', '/send-email') + ->controller(self::class . '::sendEmail'); + $routes->add('translation', '/translation') + ->controller(self::class . '::translation'); + $routes->add('twig', '/twig') + ->controller(self::class . '::twig'); + $routes->add('logout', '/logout') + ->controller(self::class . '::index'); + } + + public function index(): Response + { + return new Response('OK'); + } + + public function sample(Request $request): Response + { + $request->attributes->set('foo', 'bar'); + $html = << + Test Page + + + + + +
+ +
+
Hello World
+ + +HTML; + $response = new Response($html, 200, ['X-Test' => '1']); + $response->headers->setCookie(new Cookie('response_cookie', 'yum')); + return $response; + } + + public function redirect(): RedirectResponse + { + return new RedirectResponse('/sample'); + } + + public function unprocessable(): Response + { + return new Response('Unprocessable', 422); + } + + public function session(Request $request): Response + { + $session = $request->getSession(); + $session->set('key1', 'value1'); + $session->set('key2', 'value2'); + return new Response('Session'); + } + + public function deprecated(): Response + { + trigger_error('Deprecated endpoint', E_USER_DEPRECATED); + return new Response('Deprecated'); + } + + public function sendEmail(MailerInterface $mailer): Response + { + $email = (new Email()) + ->from('john_doe@example.com') + ->to('jane_doe@example.com') + ->subject('Test') + ->text('Example text body') + ->html('

HTML body

') + ->attach('Attachment content', 'test.txt'); + + $mailer->send($email); + + return new Response('Email sent'); + } + + public function translation(TranslatorInterface $translator): Response + { + $translator->trans('defined_message'); + return new Response('Translation'); + } + + public function twig(Environment $twig): Response + { + return new Response($twig->render('home.html.twig')); + } +} diff --git a/tests/_app/TestUser.php b/tests/_app/TestUser.php new file mode 100644 index 0000000..69caf2f --- /dev/null +++ b/tests/_app/TestUser.php @@ -0,0 +1,33 @@ +userIdentifier; + } + + public function getRoles(): array + { + return $this->roles; + } + + public function getPassword(): string + { + return $this->password; + } + + public function eraseCredentials(): void + { + } +} diff --git a/tests/_app/ValidEntity.php b/tests/_app/ValidEntity.php new file mode 100644 index 0000000..2729a7d --- /dev/null +++ b/tests/_app/ValidEntity.php @@ -0,0 +1,18 @@ +name = $name; + $this->short = $short; + } +} diff --git a/tests/_app/templates/home.html.twig b/tests/_app/templates/home.html.twig new file mode 100644 index 0000000..d2f72c4 --- /dev/null +++ b/tests/_app/templates/home.html.twig @@ -0,0 +1,2 @@ +{% extends "layout.html.twig" %} +{% block content %}Home{% endblock %} diff --git a/tests/_app/templates/layout.html.twig b/tests/_app/templates/layout.html.twig new file mode 100644 index 0000000..82f27ae --- /dev/null +++ b/tests/_app/templates/layout.html.twig @@ -0,0 +1,6 @@ + + + +{% block content %}{% endblock %} + + diff --git a/tests/_app/translations/messages.en.yaml b/tests/_app/translations/messages.en.yaml new file mode 100644 index 0000000..e209d18 --- /dev/null +++ b/tests/_app/translations/messages.en.yaml @@ -0,0 +1 @@ +defined_message: "Hello" diff --git a/tests/_support/.gitkeep b/tests/_support/.gitkeep new file mode 100644 index 0000000..e69de29