From 2bf75b27951faca265042ba2ae28d73eb10b5d34 Mon Sep 17 00:00:00 2001 From: Nathan Boiron Date: Wed, 3 Dec 2025 22:41:51 +0100 Subject: [PATCH] =?UTF-8?q?D=C3=A9coupage=20du=20context=20Behat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La classe de context commence à devenir un peu difficile à gérer avec les ajouts récents de phrases. La recommandation officielle est de découper la classe avec des traits. --- behat.yml | 2 +- composer.json | 1 + tests/behat/bootstrap/ApiContext.php | 40 ++ tests/behat/bootstrap/AuthContext.php | 48 ++ tests/behat/bootstrap/EmailContext.php | 182 ++++++++ tests/behat/bootstrap/FeatureContext.php | 544 +---------------------- tests/behat/bootstrap/FormContext.php | 106 +++++ tests/behat/bootstrap/PdfContext.php | 70 +++ tests/behat/bootstrap/TimeContext.php | 34 ++ 9 files changed, 502 insertions(+), 525 deletions(-) create mode 100644 tests/behat/bootstrap/ApiContext.php create mode 100644 tests/behat/bootstrap/AuthContext.php create mode 100644 tests/behat/bootstrap/EmailContext.php create mode 100644 tests/behat/bootstrap/FormContext.php create mode 100644 tests/behat/bootstrap/PdfContext.php create mode 100644 tests/behat/bootstrap/TimeContext.php diff --git a/behat.yml b/behat.yml index 308835e10..69e684bd8 100644 --- a/behat.yml +++ b/behat.yml @@ -6,7 +6,7 @@ default: paths: - '%paths.base%/tests/behat' contexts: - - FeatureContext + - Afup\Tests\Behat\Bootstrap\FeatureContext - Behat\MinkExtension\Context\MinkContext extensions: Behat\MinkExtension: diff --git a/composer.json b/composer.json index e94f8b041..227911674 100644 --- a/composer.json +++ b/composer.json @@ -117,6 +117,7 @@ "Afup\\Site\\Tests\\": "tests/unit/Afup/", "Afup\\Tests\\Support\\": "tests/support/", "Afup\\Tests\\Stubs\\PHPStan\\": "tests/stubs/phpstan/", + "Afup\\Tests\\Behat\\Bootstrap\\": "tests/behat/bootstrap/", "AppBundle\\Tests\\": "tests/unit/AppBundle/", "AppBundle\\IntegrationTests\\": "tests/integration/AppBundle/", "PlanetePHP\\IntegrationTests\\": "tests/integration/PlanetePHP/" diff --git a/tests/behat/bootstrap/ApiContext.php b/tests/behat/bootstrap/ApiContext.php new file mode 100644 index 000000000..aa402db24 --- /dev/null +++ b/tests/behat/bootstrap/ApiContext.php @@ -0,0 +1,40 @@ +assertResponseHeaderEquals('Content-Type', 'application/json'); + Assert::assertJson($this->minkContext->getSession()->getPage()->getContent()); + } + + #[Then('/^the json response has the key "(?P[^"]*)" with value "(?P(?:[^"]|\\")*)"$/')] + public function assertResponseHasJsonKeyAndValue(string $key, string $value): void + { + $crawler = new JsonCrawler($this->minkContext->getSession()->getPage()->getContent()); + + $foundValue = $crawler->find($key); + + Assert::assertCount(1, $foundValue); + Assert::assertSame($value, $foundValue[0]); + } + + #[Then('/^the json response has no key "(?P[^"]*)"$/')] + public function assertResponseHasNoJsonKey(string $key): void + { + $crawler = new JsonCrawler($this->minkContext->getSession()->getPage()->getContent()); + + $foundValue = $crawler->find($key); + + Assert::assertEmpty($foundValue); + } +} diff --git a/tests/behat/bootstrap/AuthContext.php b/tests/behat/bootstrap/AuthContext.php new file mode 100644 index 000000000..effc52fa2 --- /dev/null +++ b/tests/behat/bootstrap/AuthContext.php @@ -0,0 +1,48 @@ +iAmLoggedInAsAdmin(); + $this->minkContext->clickLink('Administration'); + } + + #[Given('I am logged in as admin')] + public function iAmLoggedInAsAdmin(): void + { + $this->iAmLoggedInWithTheUserAndThePassword('admin', 'admin'); + } + + #[Given('I am logged-in with the user :username and the password :password')] + public function iAmLoggedInWithTheUserAndThePassword(string $username, string $password): void + { + $this->minkContext->visitPath('/admin/login'); + $this->minkContext->fillField('utilisateur', $username); + $this->minkContext->fillField('mot_de_passe', $password); + $this->minkContext->pressButton('Se connecter'); + $this->minkContext->assertPageContainsText('Espace membre'); + } + + #[When('I request a password reset for :arg1')] + public function iRequestAPasswordReset(string $arg1): void + { + $this->minkContext->iAmOnHomepage(); + $this->minkContext->assertPageContainsText("Tous les trois mois, des nouvelles de L'AFUP"); + $this->minkContext->clickLink("Se connecter"); + $this->minkContext->assertPageContainsText("Email ou nom d'utilisateur"); + $this->minkContext->clickLink("Mot de passe perdu"); + $this->minkContext->assertPageContainsText("Mot de passe perdu"); + $this->minkContext->fillField("form_email", $arg1); + $this->minkContext->pressButton("Demander un nouveau mot de passe"); + $this->minkContext->assertPageContainsText("Votre demande a été prise en compte. Si un compte correspond à cet email vous recevez un nouveau mot de passe rapidement."); + } +} diff --git a/tests/behat/bootstrap/EmailContext.php b/tests/behat/bootstrap/EmailContext.php new file mode 100644 index 000000000..728df8d26 --- /dev/null +++ b/tests/behat/bootstrap/EmailContext.php @@ -0,0 +1,182 @@ +remove(Event::getInscriptionAttachmentDir()); + } + + #[BeforeScenario('@clearAllSponsorFiles')] + public function beforeScenarioClearAllSponsorFiles(): void + { + $filesystem = new Filesystem(); + $filesystem->remove(Event::getSponsorFileDir()); + } + + #[BeforeScenario('@clearEmails')] + public function clearEmails(): void + { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, self::MAILCATCHER_URL . '/messages'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); + + curl_exec($ch); + if (curl_errno($ch) !== 0) { + throw new \RuntimeException('Error : ' . curl_error($ch)); + } + + curl_close($ch); + } + + #[Then('I should only receive the following emails:')] + public function theFollowingEmailsShouldBeReceived(TableNode $expectedEmails): void + { + $expectedEmailsArray = []; + foreach ($expectedEmails as $expectedEmail) { + $expectedEmailsArray[] = [ + 'to' => $expectedEmail['to'], + 'subject' => $expectedEmail['subject'], + ]; + } + + + $content = file_get_contents(self::MAILCATCHER_URL . '/messages'); + $decodedContent = json_decode($content, true); + + $foundEmails = []; + foreach ($decodedContent as $mail) { + $foundEmails[] = [ + 'to' => implode(',', $mail['recipients']), + 'subject' => $mail['subject'], + ]; + } + + if ($foundEmails !== $expectedEmailsArray) { + throw new ExpectationException( + sprintf( + 'The emails are not the expected ones "%s" (expected "%s")', + var_export($foundEmails, true), + var_export($expectedEmailsArray, true), + ), + $this->minkContext->getSession()->getDriver(), + ); + } + } + + #[Then('the checksum of the attachment :filename of the message of id :id should be :md5sum')] + public function theChecksumOfTheAttachmentOfTheMessageOfIdShouldBe(string $filename, string $id, string $md5sum): void + { + $infos = json_decode(file_get_contents(self::MAILCATCHER_URL . '/messages/' . $id . '.json'), true); + + $cid = null; + foreach ($infos['attachments'] as $attachment) { + if ($attachment['filename'] === $filename) { + $cid = $attachment['cid']; + } + } + + if (null === $cid) { + throw new ExpectationException( + sprintf('Attachment with name %s not found', $filename), + $this->minkContext->getSession()->getDriver(), + ); + } + + $attachmentContent = file_get_contents(self::MAILCATCHER_URL . '/messages/' . $id . '/parts/' . $cid); + $actualMd5sum = md5($attachmentContent); + + if ($actualMd5sum !== $md5sum) { + throw new ExpectationException( + sprintf('The md5sum of %s, if not %s (found %s)', $filename, $md5sum, $actualMd5sum), + $this->minkContext->getSession()->getDriver(), + ); + } + } + + #[Then('the plain text content of the message of id :id should be :')] + public function thePlainTextContentOfTheMessageOfIdShouldBe(string $id, PyStringNode $expectedContent): void + { + $content = file_get_contents(self::MAILCATCHER_URL . '/messages/' . $id . '.plain'); + $expectedContentString = $expectedContent->getRaw(); + + $content = str_replace("\r\n", "\n", $content); + + if ($content !== $expectedContentString) { + throw new ExpectationException( + sprintf( + "The content \n%s\nis not the expected one \n%s\n", + var_export($content, true), + var_export($expectedContentString, true), + ), + $this->minkContext->getSession()->getDriver(), + ); + } + } + + #[Then('I should receive an email')] + public function iShouldReceiveAnEmail(): void + { + $content = file_get_contents(self::MAILCATCHER_URL . '/messages'); + $emails = json_decode($content, true); + + if (count($emails) !== 1) { + throw new ExpectationException( + 'The email has not been received.', + $this->minkContext->getSession()->getDriver(), + ); + } + } + + #[Then('the email should contain a full URL starting with :arg1')] + public function theEmailShouldContainAFullUrlStartingWith(string $arg1): void + { + $content = file_get_contents(self::MAILCATCHER_URL . '/messages'); + $decodedContent = json_decode($content, true); + + $foundEmails = []; + foreach ($decodedContent as $mail) { + $foundEmails[] = [ + 'id' => $mail['id'], + 'to' => $mail['to'] ?? $mail['recipients'][0], + 'subject' => $mail['subject'], + ]; + } + + if (count($foundEmails) !== 1) { + throw new ExpectationException( + 'The email has not been received.', + $this->minkContext->getSession()->getDriver(), + ); + } + + $content = file_get_contents(self::MAILCATCHER_URL . '/messages/' . $foundEmails[0]['id'] . '.plain'); + if (!str_contains($content, $arg1)) { + throw new ExpectationException( + sprintf( + 'The email content does not contain the expected URL "%s" (expected "%s")', + $content, + $arg1, + ), $this->minkContext->getSession()->getDriver(), + ); + } + } +} diff --git a/tests/behat/bootstrap/FeatureContext.php b/tests/behat/bootstrap/FeatureContext.php index 627224617..23b6afa0e 100644 --- a/tests/behat/bootstrap/FeatureContext.php +++ b/tests/behat/bootstrap/FeatureContext.php @@ -2,32 +2,29 @@ declare(strict_types=1); +namespace Afup\Tests\Behat\Bootstrap; + use Afup\Tests\Support\DatabaseManager; -use AppBundle\Event\Model\Event; -use AppBundle\Listener\DetectClockMockingListener; use Behat\Behat\Context\Context; use Behat\Behat\Hook\Scope\BeforeScenarioScope; use Behat\Gherkin\Node\PyStringNode; -use Behat\Gherkin\Node\TableNode; use Behat\Hook\BeforeScenario; use Behat\Mink\Exception\ExpectationException; use Behat\MinkExtension\Context\MinkContext; -use Behat\Step\Given; use Behat\Step\Then; use Behat\Step\When; -use PHPUnit\Framework\Assert; -use Smalot\PdfParser\Parser; -use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\JsonPath\JsonCrawler; class FeatureContext implements Context { - private const MAILCATCHER_URL = 'http://mailcatcher:1080'; + use ApiContext; + use AuthContext; + use EmailContext; + use FormContext; + use PdfContext; + use TimeContext; private MinkContext $minkContext; - private array $pdfPages = []; - private readonly DatabaseManager $databaseManager; public function __construct() @@ -35,98 +32,19 @@ public function __construct() $this->databaseManager = new DatabaseManager(true); } - /** - * @BeforeScenario - */ - public function gatherContexts(BeforeScenarioScope $scope): void + #[BeforeScenario] + public function fetchMinkContext(BeforeScenarioScope $scope): void { - $environment = $scope->getEnvironment(); - - $this->minkContext = $environment->getContext(MinkContext::class); + $this->minkContext = $scope->getEnvironment()->getContext(MinkContext::class); } - /** - * @BeforeScenario @reloadDbWithTestData - */ + #[BeforeScenario('@reloadDbWithTestData')] public function beforeScenarioReloadDatabase(): void { $this->databaseManager->reloadDatabase(); } - /** - * @BeforeScenario @clearAllMailInscriptionAttachments - */ - public function beforeScenarioClearAllMailInscriptionAttachments(): void - { - $filesystem = new Filesystem(); - $filesystem->remove(Event::getInscriptionAttachmentDir()); - } - - /** - * @BeforeScenario @clearAllSponsorFiles - */ - public function beforeScenarioClearAllSponsorFiles(): void - { - $filesystem = new Filesystem(); - $filesystem->remove(Event::getSponsorFileDir()); - } - - #[BeforeScenario] - public function clearTestClock(): void - { - $this->minkContext->getSession()->getDriver()->setRequestHeader(DetectClockMockingListener::HEADER, ''); - } - - /** - * @Given I am logged in as admin and on the Administration - */ - public function iAmLoggedInAsAdminAndOnTheAdministration(): void - { - $this->iAmLoggedInAsAdmin(); - $this->minkContext->clickLink('Administration'); - } - - /** - * @Given I am logged in as admin - */ - public function iAmLoggedInAsAdmin(): void - { - $this->iAmLoggedInWithTheUserAndThePassword('admin', 'admin'); - } - - /** - * @Given I am logged-in with the user :username and the password :password - */ - public function iAmLoggedInWithTheUserAndThePassword(string $username, string $password): void - { - $this->minkContext->visitPath('/admin/login'); - $this->minkContext->fillField('utilisateur', $username); - $this->minkContext->fillField('mot_de_passe', $password); - $this->minkContext->pressButton('Se connecter'); - $this->minkContext->assertPageContainsText('Espace membre'); - } - - /** - * @Then I submit the form with name :formName - * @throws ExpectationException - */ - public function submitFormWithName(string $formName): void - { - $form = $this->minkContext->getSession()->getPage()->find('xpath', "//form[@name='$formName']"); - - if (null === $form) { - throw new ExpectationException( - sprintf('The form named "%s" not found', $formName), - $this->minkContext->getSession()->getDriver(), - ); - } - - $form->submit(); - } - - /** - * @Then simulate the Paybox callback - */ + #[Then('simulate the Paybox callback')] public function simulateThePayboxCallback(): void { $url = $this->minkContext->getSession()->getCurrentUrl(); @@ -138,139 +56,20 @@ public function simulateThePayboxCallback(): void curl_exec($curl); } - /** - * @Then The :field field should only contain the follow values :expectedValuesJson - * @throws ExpectationException - */ - public function selectHasValues(string $field, string $expectedValuesJson): void - { - $node = $this->minkContext->assertSession()->fieldExists($field); - $options = $node->findAll('css', 'option'); - - $expectedValues = json_decode($expectedValuesJson, true); - - $foundValues = []; - foreach ($options as $option) { - $foundValues[] = $option->getText(); - } - - if ($foundValues !== $expectedValues) { - throw new ExpectationException( - sprintf( - 'The select has the following values %s (expected %s)', - json_encode($foundValues, JSON_UNESCAPED_UNICODE), - $expectedValuesJson, - ), - $this->minkContext->getSession()->getDriver(), - ); - } - } - - /** - * @Then The :field field should have the following selected value :expectedValue - * @throws ExpectationException - */ - public function selectHasForCurrentSelectedValue(string $field, string $expectedValue): void - { - $node = $this->minkContext->assertSession()->fieldExists($field); - $options = $node->findAll('css', 'option'); - - $selectedValue = null; - foreach ($options as $option) { - if ($option->isSelected()) { - $selectedValue = $option->getValue(); - break; - } - } - - if ($selectedValue !== $expectedValue) { - throw new ExpectationException( - sprintf('The select has the following value "%s" (expected "%s")', $selectedValue, $expectedValue), - $this->minkContext->getSession()->getDriver(), - ); - } - } - - /** - * @Then The :field field should have the following selected text :expectedValue - * @throws ExpectationException - */ - public function selectHasForCurrentSelectedText(string $field, string $expectedValue): void - { - $node = $this->minkContext->assertSession()->fieldExists($field); - $options = $node->findAll('css', 'option'); - - $selectedValue = null; - foreach ($options as $option) { - if ($option->isSelected()) { - $selectedValue = $option->getText(); - break; - } - } - - if ($selectedValue !== $expectedValue) { - throw new ExpectationException( - sprintf('The select has the following text "%s" (expected "%s")', $selectedValue, $expectedValue), - $this->minkContext->getSession()->getDriver(), - ); - } - } - - - /** - * @Then the response header :arg1 should equal :arg2 - * @throws ExpectationException - */ + #[Then('the response header :arg1 should equal :arg2')] public function assertResponseHeaderEquals(string $headerName, string $expectedValue): void { $this->minkContext->assertSession()->responseHeaderEquals($headerName, $expectedValue); } - /** - * @Then the response header :arg1 should match :arg2 - * @throws ExpectationException - */ + #[Then('the response header :arg1 should match :arg2')] public function assertResponseHeaderMatch(string $headerName, string $regExpExpectedValue): void { $this->minkContext->assertSession()->responseHeaderMatches($headerName, $regExpExpectedValue); } - #[Then('/^the response should be in json$/')] - public function assertResponseShouldBeInJson(): void - { - $this->assertResponseHeaderEquals('Content-Type', 'application/json'); - Assert::assertJson($this->minkContext->getSession()->getPage()->getContent()); - } - - /** - * @Then /^the json response has the key "(?P[^"]*)" with value "(?P(?:[^"]|\\")*)"$/ - */ - public function assertResponseHasJsonKeyAndValue(string $key, string $value): void - { - $crawler = new JsonCrawler($this->minkContext->getSession()->getPage()->getContent()); - - $foundValue = $crawler->find($key); - - Assert::assertCount(1, $foundValue); - Assert::assertSame($value, $foundValue[0]); - } - - /** - * @Then /^the json response has no key "(?P[^"]*)"$/ - */ - public function assertResponseHasNoJsonKey(string $key): void - { - $crawler = new JsonCrawler($this->minkContext->getSession()->getPage()->getContent()); - - $foundValue = $crawler->find($key); - - Assert::assertEmpty($foundValue); - } - - /** - * @Then /^the response should contain the html "(?P(?:[^"]|\\")*)"$/ - * @Then /^the response should contain the html$/ - */ + #[Then('/^the response should contain the html "(?P(?:[^"]|\\")*)"$/')] + #[Then('/^the response should contain the html$/')] public function assertResponseHasHtml(PyStringNode|string $html): void { if ($html instanceof PyStringNode) { @@ -280,10 +79,7 @@ public function assertResponseHasHtml(PyStringNode|string $html): void $this->minkContext->assertResponseContains($html); } - /** - * @Then the current URL should match :arg1 - * @throws ExpectationException - */ + #[Then('the current URL should match :arg1')] public function assertCurrentUrlContains(string $regex): void { $currentUrl = $this->minkContext->getSession()->getCurrentUrl(); @@ -296,10 +92,7 @@ public function assertCurrentUrlContains(string $regex): void } } - /** - * @When I follow the button of tooltip :arg1 - * @throws ExpectationException - */ + #[When('I follow the button of tooltip :arg1')] public function clickLinkOfTooltip(string $tooltip): void { $link = $this->minkContext->getSession()->getPage()->find('css', sprintf('a[data-tooltip="%s"]', $tooltip)); @@ -314,181 +107,7 @@ public function clickLinkOfTooltip(string $tooltip): void $link->click(); } - - /** - * @BeforeScenario @clearEmails - */ - public function clearEmails(): void - { - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_URL, self::MAILCATCHER_URL . '/messages'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); - - curl_exec($ch); - if (curl_errno($ch) !== 0) { - throw new \RuntimeException('Error : ' . curl_error($ch)); - } - - curl_close($ch); - } - - - /** - * @Then I should only receive the following emails: - * @throws ExpectationException - */ - public function theFollowingEmailsShouldBeReceived(TableNode $expectedEmails): void - { - $expectedEmailsArray = []; - foreach ($expectedEmails as $expectedEmail) { - $expectedEmailsArray[] = [ - 'to' => $expectedEmail['to'], - 'subject' => $expectedEmail['subject'], - ]; - } - - - $content = file_get_contents(self::MAILCATCHER_URL . '/messages'); - $decodedContent = json_decode($content, true); - - $foundEmails = []; - foreach ($decodedContent as $mail) { - $foundEmails[] = [ - 'to' => implode(',', $mail['recipients']), - 'subject' => $mail['subject'], - ]; - } - - if ($foundEmails !== $expectedEmailsArray) { - throw new ExpectationException( - sprintf( - 'The emails are not the expected ones "%s" (expected "%s")', - var_export($foundEmails, true), - var_export($expectedEmailsArray, true), - ), - $this->minkContext->getSession()->getDriver(), - ); - } - } - - /** - * @Then the checksum of the attachment :filename of the message of id :id should be :md5sum - * @throws ExpectationException - */ - public function theChecksumOfTheAttachmentOfTheMessageOfIdShouldBe(string $filename, string $id, string $md5sum): void - { - $infos = json_decode(file_get_contents(self::MAILCATCHER_URL . '/messages/' . $id . '.json'), true); - - $cid = null; - foreach ($infos['attachments'] as $attachment) { - if ($attachment['filename'] === $filename) { - $cid = $attachment['cid']; - } - } - - if (null === $cid) { - throw new ExpectationException( - sprintf('Attachment with name %s not found', $filename), - $this->minkContext->getSession()->getDriver(), - ); - } - - $attachmentContent = file_get_contents(self::MAILCATCHER_URL . '/messages/' . $id . '/parts/' . $cid); - $actualMd5sum = md5($attachmentContent); - - if ($actualMd5sum !== $md5sum) { - throw new ExpectationException( - sprintf('The md5sum of %s, if not %s (found %s)', $filename, $md5sum, $actualMd5sum), - $this->minkContext->getSession()->getDriver(), - ); - } - } - - /** - * @Then the plain text content of the message of id :id should be : - * @throws ExpectationException - */ - public function thePlainTextContentOfTheMessageOfIdShouldBe(string $id, PyStringNode $expectedContent): void - { - $content = file_get_contents(self::MAILCATCHER_URL . '/messages/' . $id . '.plain'); - $expectedContentString = $expectedContent->getRaw(); - - $content = str_replace("\r\n", "\n", $content); - - if ($content !== $expectedContentString) { - throw new ExpectationException( - sprintf( - "The content \n%s\nis not the expected one \n%s\n", - var_export($content, true), - var_export($expectedContentString, true), - ), - $this->minkContext->getSession()->getDriver(), - ); - } - } - - /** - * @When I parse the pdf downloaded content - */ - public function iParseThePdfContent(): void - { - $pageContent = $this->minkContext->getSession()->getPage()->getContent(); - - $parser = new Parser(); - $pdf = $parser->parseContent($pageContent); - $pages = $pdf->getPages(); - - $this->pdfPages = []; - foreach ($pages as $i => $page) { - $this->pdfPages[++$i] = $page->getText(); - } - } - - /** - * @Then The page :page of the PDF should contain :content - * @throws ExpectationException - */ - public function thePageOfThePdfShouldContain(string $page, string $expectedContent): void - { - $pageContent = $this->pdfPages[$page] ?? null; - - if (!str_contains((string) $pageContent, $expectedContent)) { - throw new ExpectationException( - sprintf('The content "%s" was not found in the content "%s"', $expectedContent, $pageContent), - $this->minkContext->getSession()->getDriver(), - ); - } - } - - /** - * @Then The page :page of the PDF should not contain :content - * @throws ExpectationException - */ - public function thePageOfThePdfShouldNotContain(string $page, string $expectedContent): void - { - if (!isset($this->pdfPages[$page])) { - throw new ExpectationException( - sprintf('The page %d does not exists', $page), - $this->minkContext->getSession()->getDriver(), - ); - } - - $pageContent = $this->pdfPages[$page]; - - if (str_contains($pageContent, $expectedContent)) { - throw new ExpectationException( - sprintf('The content "%s" was not found in the content "%s"', $expectedContent, $pageContent), - $this->minkContext->getSession()->getDriver(), - ); - } - } - - /** - * @Then the checksum of the response content should be :md5 - * @throws ExpectationException - */ + #[Then('the checksum of the response content should be :md5')] public function checksumOfTheResponseContentShouldBe(string $expectedChecksum): void { $content = $this->minkContext->getSession()->getPage()->getContent(); @@ -503,117 +122,6 @@ public function checksumOfTheResponseContentShouldBe(string $expectedChecksum): } } - /** - * @Then print last PDF content - */ - public function printLastPDFResponse(): void - { - echo implode("######\n", $this->pdfPages); - } - - /** - * @Then print last response headers - */ - public function printLastResponseHeaders(): void - { - $headers = []; - foreach ($this->minkContext->getSession()->getResponseHeaders() as $name => $values) { - foreach ($values as $value) { - $headers[] = sprintf('%s : %s', $name, $value); - } - } - - echo implode("\n", $headers); - } - - /** - * @When I request a password reset for :arg1 - */ - public function iRequestAPasswordReset(string $arg1): void - { - $this->minkContext->iAmOnHomepage(); - $this->minkContext->assertPageContainsText("Tous les trois mois, des nouvelles de L'AFUP"); - $this->minkContext->clickLink("Se connecter"); - $this->minkContext->assertPageContainsText("Email ou nom d'utilisateur"); - $this->minkContext->clickLink("Mot de passe perdu"); - $this->minkContext->assertPageContainsText("Mot de passe perdu"); - $this->minkContext->fillField("form_email", $arg1); - $this->minkContext->pressButton("Demander un nouveau mot de passe"); - $this->minkContext->assertPageContainsText("Votre demande a été prise en compte. Si un compte correspond à cet email vous recevez un nouveau mot de passe rapidement."); - } - - /** - * @Then I should receive an email - */ - public function iShouldReceiveAnEmail(): void - { - $content = file_get_contents(self::MAILCATCHER_URL . '/messages'); - $emails = json_decode($content, true); - - if (count($emails) !== 1) { - throw new ExpectationException( - 'The email has not been received.', - $this->minkContext->getSession()->getDriver(), - ); - } - } - - /** - * @Then the email should contain a full URL starting with :arg1 - */ - public function theEmailShouldContainAFullUrlStartingWith(string $arg1): void - { - $content = file_get_contents(self::MAILCATCHER_URL . '/messages'); - $decodedContent = json_decode($content, true); - - $foundEmails = []; - foreach ($decodedContent as $mail) { - $foundEmails[] = [ - 'id' => $mail['id'], - 'to' => $mail['to'] ?? $mail['recipients'][0], - 'subject' => $mail['subject'], - ]; - } - - if (count($foundEmails) !== 1) { - throw new ExpectationException( - 'The email has not been received.', - $this->minkContext->getSession()->getDriver(), - ); - } - - $content = file_get_contents(self::MAILCATCHER_URL . '/messages/' . $foundEmails[0]['id'] . '.plain'); - if (!str_contains($content, $arg1)) { - throw new ExpectationException( - sprintf( - 'The email content does not contain the expected URL "%s" (expected "%s")', - $content, - $arg1, - ), $this->minkContext->getSession()->getDriver(), - ); - } - } - - #[Then('the response should contain date :arg1')] - #[Then('the response should contain date :arg1 with format :arg2')] - public function responseShouldContainsDate(string $datetimeFormat, string $withFormat = DateTimeInterface::ATOM): void - { - $this->minkContext->assertResponseContains((new DateTimeImmutable($datetimeFormat))->format($withFormat)); - } - - #[When('/^(?:|I )fill hidden field "(?P(?:[^"]|\\")*)" with "(?P(?:[^"]|\\")*)"$/')] - #[When('/^(?:|I )fill hidden field "(?P(?:[^"]|\\")*)" with:$/')] - #[When('/^(?:|I )fill hidden field "(?P(?:[^"]|\\")*)" for "(?P(?:[^"]|\\")*)"$/')] - public function fillHiddenField($field, $value): void - { - $this->minkContext->getSession()->getPage() - ->find('css', 'input[name="' . $field . '"]') - ?->setValue($value); - } - - /** - * @throws ExpectationException - */ #[Then('/^(?:|I )should see tooltip "(?P(?:[^"]|\\")*)"$/')] public function shouldSeeTooltip(string $tooltip): void { @@ -640,9 +148,6 @@ public function shouldNotSeeTooltip(string $tooltip): void } } - /** - * @throws ExpectationException - */ #[Then('/^the downloaded file should be the same as "(?P(?:[^"]|\\")*)"$/')] public function assertDownloadedFile(string $filename): void { @@ -700,13 +205,4 @@ public function shouldSeeImage(string $source): void ); } } - - #[Given('/^the current date is "(?P[^"]*)"$/')] - public function theCurrentDateIs(string $date): void - { - $this->minkContext->getSession()->getDriver()->setRequestHeader( - DetectClockMockingListener::HEADER, - $date, - ); - } } diff --git a/tests/behat/bootstrap/FormContext.php b/tests/behat/bootstrap/FormContext.php new file mode 100644 index 000000000..824be5abd --- /dev/null +++ b/tests/behat/bootstrap/FormContext.php @@ -0,0 +1,106 @@ +minkContext->getSession()->getPage()->find('xpath', "//form[@name='$formName']"); + + if (null === $form) { + throw new ExpectationException( + sprintf('The form named "%s" not found', $formName), + $this->minkContext->getSession()->getDriver(), + ); + } + + $form->submit(); + } + + #[Then('The :field field should only contain the follow values :expectedValuesJson')] + public function selectHasValues(string $field, string $expectedValuesJson): void + { + $node = $this->minkContext->assertSession()->fieldExists($field); + $options = $node->findAll('css', 'option'); + + $expectedValues = json_decode($expectedValuesJson, true); + + $foundValues = []; + foreach ($options as $option) { + $foundValues[] = $option->getText(); + } + + if ($foundValues !== $expectedValues) { + throw new ExpectationException( + sprintf( + 'The select has the following values %s (expected %s)', + json_encode($foundValues, JSON_UNESCAPED_UNICODE), + $expectedValuesJson, + ), + $this->minkContext->getSession()->getDriver(), + ); + } + } + + #[Then('The :field field should have the following selected value :expectedValue')] + public function selectHasForCurrentSelectedValue(string $field, string $expectedValue): void + { + $node = $this->minkContext->assertSession()->fieldExists($field); + $options = $node->findAll('css', 'option'); + + $selectedValue = null; + foreach ($options as $option) { + if ($option->isSelected()) { + $selectedValue = $option->getValue(); + break; + } + } + + if ($selectedValue !== $expectedValue) { + throw new ExpectationException( + sprintf('The select has the following value "%s" (expected "%s")', $selectedValue, $expectedValue), + $this->minkContext->getSession()->getDriver(), + ); + } + } + + #[Then('The :field field should have the following selected text :expectedValue')] + public function selectHasForCurrentSelectedText(string $field, string $expectedValue): void + { + $node = $this->minkContext->assertSession()->fieldExists($field); + $options = $node->findAll('css', 'option'); + + $selectedValue = null; + foreach ($options as $option) { + if ($option->isSelected()) { + $selectedValue = $option->getText(); + break; + } + } + + if ($selectedValue !== $expectedValue) { + throw new ExpectationException( + sprintf('The select has the following text "%s" (expected "%s")', $selectedValue, $expectedValue), + $this->minkContext->getSession()->getDriver(), + ); + } + } + + #[When('/^(?:|I )fill hidden field "(?P(?:[^"]|\\")*)" with "(?P(?:[^"]|\\")*)"$/')] + #[When('/^(?:|I )fill hidden field "(?P(?:[^"]|\\")*)" with:$/')] + #[When('/^(?:|I )fill hidden field "(?P(?:[^"]|\\")*)" for "(?P(?:[^"]|\\")*)"$/')] + public function fillHiddenField($field, $value): void + { + $this->minkContext->getSession()->getPage() + ->find('css', 'input[name="' . $field . '"]') + ?->setValue($value); + } +} diff --git a/tests/behat/bootstrap/PdfContext.php b/tests/behat/bootstrap/PdfContext.php new file mode 100644 index 000000000..f5bf3b4f7 --- /dev/null +++ b/tests/behat/bootstrap/PdfContext.php @@ -0,0 +1,70 @@ +pdfPages = []; + } + + #[When('I parse the pdf downloaded content')] + public function iParseThePdfContent(): void + { + $pageContent = $this->minkContext->getSession()->getPage()->getContent(); + + $parser = new Parser(); + $pdf = $parser->parseContent($pageContent); + $pages = $pdf->getPages(); + + $this->pdfPages = []; + foreach ($pages as $i => $page) { + $this->pdfPages[++$i] = $page->getText(); + } + } + + #[Then('The page :page of the PDF should contain :content')] + public function thePageOfThePdfShouldContain(string $page, string $expectedContent): void + { + $pageContent = $this->pdfPages[$page] ?? null; + + if (!str_contains((string) $pageContent, $expectedContent)) { + throw new ExpectationException( + sprintf('The content "%s" was not found in the content "%s"', $expectedContent, $pageContent), + $this->minkContext->getSession()->getDriver(), + ); + } + } + + #[Then('The page :page of the PDF should not contain :content')] + public function thePageOfThePdfShouldNotContain(string $page, string $expectedContent): void + { + if (!isset($this->pdfPages[$page])) { + throw new ExpectationException( + sprintf('The page %d does not exists', $page), + $this->minkContext->getSession()->getDriver(), + ); + } + + $pageContent = $this->pdfPages[$page]; + + if (str_contains($pageContent, $expectedContent)) { + throw new ExpectationException( + sprintf('The content "%s" was not found in the content "%s"', $expectedContent, $pageContent), + $this->minkContext->getSession()->getDriver(), + ); + } + } +} diff --git a/tests/behat/bootstrap/TimeContext.php b/tests/behat/bootstrap/TimeContext.php new file mode 100644 index 000000000..4fc8f64d0 --- /dev/null +++ b/tests/behat/bootstrap/TimeContext.php @@ -0,0 +1,34 @@ +minkContext->getSession()->getDriver()->setRequestHeader(DetectClockMockingListener::HEADER, ''); + } + + #[Given('/^the current date is "(?P[^"]*)"$/')] + public function theCurrentDateIs(string $date): void + { + $this->minkContext->getSession()->getDriver()->setRequestHeader(DetectClockMockingListener::HEADER, $date); + } + + #[Then('the response should contain date :arg1')] + #[Then('the response should contain date :arg1 with format :arg2')] + public function responseShouldContainsDate(string $datetimeFormat, string $withFormat = DateTimeInterface::ATOM): void + { + $this->minkContext->assertResponseContains((new DateTimeImmutable($datetimeFormat))->format($withFormat)); + } +}