AutorÃa | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.use core\test\message;use Behat\Gherkin\Node\TableNode;use Behat\Mink\Exception\ExpectationException;use Moodle\BehatExtension\Exception\SkippedException;require_once(__DIR__ . '/../../behat/behat_base.php');/*** Steps definitions to assist with email testing.** @package core* @category test* @copyright Simey Lameze <simey@moodle.com>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class behat_email extends behat_base {/*** Check if email catcher is configured.** @return bool*/public static function is_email_catcher_configured(): bool {return defined('TEST_EMAILCATCHER_MAIL_SERVER') && defined('TEST_EMAILCATCHER_API_SERVER');}/*** Behat step to check if email catcher is configured.** @Given /^an email catcher server is configured$/*/public function email_catcher_is_configured(): void {if (!self::is_email_catcher_configured()) {throw new SkippedException('The TEST_EMAILCATCHER_MAIL_SERVER and TEST_EMAILCATCHER_API_SERVER constants must be ' .'defined in config.php to use the mailcatcher steps.');}}/*** Get the email catcher object or thrown a SkippedException if TEST_MAILPIT_SERVER is not defined.** @return \core\test\email_catcher|null*/private function get_catcher(): ?\core\test\email_catcher {if (self::is_email_catcher_configured()) {return new \core\test\mailpit_email_catcher(TEST_EMAILCATCHER_API_SERVER);}return null;}/*** Clean up the email inbox after each scenario.** @AfterScenario @behat_email*/public function reset_after_test(): void {$this->get_catcher()?->delete_all();}/*** Get the e-mail address of a user from the step input.** This could be an e-mail address, or a username.** @param string $input The input from the step.* @return string*/private function get_email_address_from_input(string $input): string {if (strpos($input, '@') !== false) {return $input;}$user = $this->get_user_by_identifier($input);if (!$user) {throw new ExpectationException("No user found with identifier {$input}", $this->getSession()->getDriver());}return $user->email;}/*** Get any message matching the supplied user and subject.** @param string $user The user to check for.* @param string $subject The subject to check for.* @return iterable<message>*/private function get_messages_matching_address_and_subject(string $user,string $subject,): iterable {$address = $this->get_email_address_from_input($user);return new \CallbackFilterIterator(iterator: $this->get_catcher()->get_messages(showdetails: true),callback: function (message $message) use ($address, $subject): bool {if (!$message->has_recipient($address)) {return false;}if (strpos($message->get_subject(), $subject) === false) {return false;}return true;},);}/*** Verifies the content of an email sent to a specific user and subject.** @Given the email to :user with subject containing :subject should contain :content** @param string $user The user to check for.* @param string $subject The subject to check for.* @param string $content The content to check for.*/public function verify_email_content(string $user, string $subject, string $content): void {$messages = $this->get_messages_matching_address_and_subject($user, $subject);$count = 0;foreach ($messages as $message) {$count++;$this->validate_data('content', $message, $content);}if ($count === 0) {throw new ExpectationException("No messages found with subject containing {$subject}",$this->getSession()->getDriver(),);}}/*** Custom Behat test to verify the number of emails for a user.** @Then user :address should have :count emails** @param string $address The user to check for.* @param int $expected The number of emails to check for.*/public function verify_email_count(string $address, int $expected): void {$address = $this->get_email_address_from_input($address);$messages = new \CallbackFilterIterator(iterator: $this->get_catcher()->get_messages(),callback: fn($message) => $message->has_recipient($address),);$count = iterator_count($messages);if ($count !== $expected) {throw new ExpectationException(sprintf('Expected %d messages, but found %d',$expected,$count,),$this->getSession(),);}}/*** Custom Behat test to empty the email inbox.** @When I empty the email inbox*/public function empty_email_inbox() {$this->get_catcher()->delete_all();}/*** Behat step to send emails.** @Given the following emails have been sent:** @param TableNode $table The table of emails to send.*/public function the_following_emails_have_been_sent(TableNode $table): void {if (!$rows = $table->getRows()) {return;}// Allowed fields.$allowedfields = ['to', 'subject', 'message'];// Create a map of header to index.$headers = array_flip($rows[0]);// Remove header row.unset($rows[0]);// Validate supplied headers.foreach ($headers as $header => $index) {if (!in_array($header, $allowedfields)) {throw new ExpectationException("Invalid header {$header} found in table", $this->getSession()->getDriver());}}foreach ($rows as $row) {// Check if the required headers are set in the $headers map.$to = isset($headers['to']) ? $row[$headers['to']] : 'userto@example.com';$subject = isset($headers['subject']) ? $row[$headers['subject']] : 'Default test subject';$message = isset($headers['message']) ? $row[$headers['message']] : 'Default test message';// Use no-reply user as dummy user to send emails from.$noreplyuser = \core_user::get_user(\core_user::NOREPLY_USER);// Create a dummy user to send emails to.$emailuserto = new stdClass();$emailuserto->id = -99;$emailuserto->email = $to;$emailuserto->firstname = 'Test';$emailuserto->lastname = 'User';// Send test email.email_to_user($emailuserto, $noreplyuser, $subject, $message);}}/*** Validate the emails expected and actual values.** @param string $field The field to validate.* @param message $message The expected value.* @param string $expected The actual value.*/private function validate_data(string $field,message $message,string $expected,): void {switch ($field) {case 'user':$actual = $message->get_recipients();foreach ($actual as $recipient) {if ($recipient->get_address() === $expected) {return;}}throw new ExpectationException(sprintf('Expected %s %s, but found %s',$expected,$field,$actual,),$this->getSession(),);case 'subject':$actual = $message->get_subject();if (str_contains($expected, $actual)) {return;}throw new ExpectationException(sprintf('Expected %s %s, but found %s',$expected,$field,$actual,),$this->getSession(),);case 'content':if (str_contains($expected, $message->get_body_text())) {return;}if (str_contains($expected, $message->get_body_html())) {return;}throw new ExpectationException(sprintf('Expected %s to contain %s, but it does not. Actual text was:\n%s\nActual HTML content was:\n%s\n',$field,$expected,$message->get_body_text(),$message->get_body_html(),),$this->getSession(),);default:throw new ExpectationException(sprintf('Unknown field to validate: %s',$field,),$this->getSession(),);}}}