Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for testing emails sent from Drupal #392

Merged
merged 13 commits into from
Mar 19, 2018
1 change: 1 addition & 0 deletions behat.yml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ drupal8:
- Drupal\DrupalExtension\Context\MinkContext
- Drupal\DrupalExtension\Context\MarkupContext
- Drupal\DrupalExtension\Context\MessageContext
- Drupal\DrupalExtension\Context\MailContext
filters:
tags: "@d8&&~@d8wip"
extensions:
Expand Down
91 changes: 91 additions & 0 deletions features/mail.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
@api @d8
Feature: MailContext
In order to prove the Mail context is working properly
As a developer
I need to use the step definitions of this context

Scenario: Mail is sent
When Drupal sends an email:
| to | [email protected] |
| subject | test |
| body | test body |
And Drupal sends a mail:
| to | [email protected] |
| subject | test |
| body | test body 2 |
Then mails have been sent:
| to | subject | body |
| fred | | test body |
| jane | test | body 2 |
When Drupal sends a mail:
| to | [email protected] |
| subject | for jack |
| body | test body with many words |
Then new email is sent:
| to | body | body |
| jack | test | many words |
And a mail has been sent to "[email protected]"
And a mail has been sent to "[email protected]":
| subject |
| test |
And an email has been sent with the subject "test"
And emails have been sent with the subject "test":
| to |
| fred |
| jane |
And a mail has been sent to "fred" with the subject "test"
And emails have been sent to "fred" with the subject "test":
| body |
| test body |

Scenario: New mail is sent to someone
When Drupal sends a mail:
| to | [email protected] |
| subject | test 1 |
And Drupal sends a mail:
| to | [email protected] |
| subject | test 2 |
Then new mail is sent to fred:
| subject |
| test 1 |


Scenario: No mail is sent
Then no mail has been sent

Scenario: Count sent mail
When Drupal sends an email:
| to | [email protected] |
| subject | test |
And Drupal sends a mail:
| to | [email protected] |
| subject | test |
And Drupal sends a mail:
| to | [email protected] |
| subject | something else |
Then 2 new emails are sent with the subject "test"
And 1 mail has been sent to "jane" with the subject "something else"
And no new emails are sent
And no mail has been sent to "hans"

Scenario: I follow link in mail
When Drupal sends a mail:
| to | [email protected] |
| subject | test link |
| body | A link to Google: http://www.Google.com |
And I follow the link to "google" from the mail with the subject "test link"
Then I should see "Search"

Scenario: We try to be order insensitive
When Drupal sends an email:
| to | [email protected] |
| subject | test |
| body | test body |
And Drupal sends a mail:
| to | [email protected] |
| subject | test |
| body | test body 2 |
Then mails have been sent:
| to | subject | body |
| jane | test | body 2 |
| fred | | test body |
170 changes: 170 additions & 0 deletions src/Drupal/DrupalExtension/Context/MailContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<?php

namespace Drupal\DrupalExtension\Context;

use Behat\Gherkin\Node\TableNode;

/**
* Provides pre-built step definitions for interacting with mail.
*/
class MailContext extends RawMailContext {

/**
* By default, prevent mail from being actually sent out during tests.
*
* @BeforeScenario
*/
public function disableMail($event) {
$tags = array_merge($event->getFeature()->getTags(), $event->getScenario()->getTags());
if (!in_array('sendmail', $tags) && !in_array('sendemail', $tags)) {
$this->getMailManager()->disableMail();
// Always reset mail count, in case the default mail manager is being used
// which enables mail collecting automatically when mail is disabled, making
//the use of the @mail tag optional in this case.
$this->mailCount = [];
}
}

/**
* Restore mail sending.
*
* @AfterScenario
*/
public function enableMail($event) {
$tags = array_merge($event->getFeature()->getTags(), $event->getScenario()->getTags());
if (!in_array('sendmail', $tags) && !in_array('sendemail', $tags)) {
$this->getMailManager()->enableMail();
}
}

/**
* Allow opting in to mail collection. When using the default mail manager
* service, it is not necessary to use this tag.
*
* @BeforeScenario @mail @email
*/
public function collectMail() {
$this->getMailManager()->startCollectingMail();
}

/**
* Stop collecting mail at scenario end.
*
* @AfterScenario @mail @email
*/
public function stopCollectingMail() {
$this->getMailManager()->stopCollectingMail();
}

/**
* This is mainly useful for testing this context.
*
* @When Drupal sends a/an (e)mail:
*/
public function DrupalSendsMail(TableNode $fields) {
$mail = [
'body' => $this->getRandom()->name(255),
'subject' => $this->getRandom()->name(20),
'to' => $this->getRandom()->name(10) . '@anonexample.com',
'langcode' => '',
];
foreach ($fields->getRowsHash() as $field => $value) {
$mail[$field] = $value;
}
$this->getDriver()->sendMail($mail['body'], $mail['subject'], $mail['to'], $mail['langcode']);
}

/**
* Check all mail sent during the scenario.
*
* @Then (a )(an )(e)mail(s) has/have been sent:
* @Then (a )(an )(e)mail(s) has/have been sent to :to:
* @Then (a )(an )(e)mail(s) has/have been sent with the subject :subject:
* @Then (a )(an )(e)mail(s) has/have been sent to :to with the subject :subject:
*/
public function mailHasBeenSent(TableNode $expectedMailTable, $to = '', $subject = '') {
$expectedMail = $expectedMailTable->getHash();
$actualMail = $this->getMail(['to' => $to, 'subject' => $subject], FALSE);
$this->compareMail($actualMail, $expectedMail);
}

/**
* Check mail sent since the last step that checked mail.
*
* @Then (a )(an )new (e)mail(s) is/are sent:
* @Then (a )(an )new (e)mail(s) is/are sent to :to:
* @Then (a )(an )new (e)mail(s) is/are sent with the subject :subject:
* @Then (a )(an )new (e)mail(s) is/are sent to :to with the subject :subject:
*/
public function newMailIsSent(TableNode $expectedMailTable, $to = '', $subject = '') {
$expectedMail = $expectedMailTable->getHash();
$actualMail = $this->getMail(['to' => $to, 'subject' => $subject], TRUE);
$this->compareMail($actualMail, $expectedMail);
}

/**
* Check all mail sent during the scenario.
*
* @Then :count (e)mail(s) has/have been sent
* @Then :count (e)mail(s) has/have been sent to :to
* @Then :count (e)mail(s) has/have been sent with the subject :subject
* @Then :count (e)mail(s) has/have been sent to :to with the subject :subject
*/
public function noMailHasBeenSent($count, $to = '', $subject = '') {
$actualMail = $this->getMail(['to' => $to, 'subject' => $subject], FALSE);
$count = $count === 'no' ? 0 : $count;
$count = $count === 'a' ? NULL : $count;
$count = $count === 'an' ? NULL : $count;
$this->assertMailCount($actualMail, $count);
}

/**
* Check mail sent since the last step that checked mail.
*
* @Then :count new (e)mail(s) is/are sent
* @Then :count new (e)mail(s) is/are sent to :to
* @Then :count new (e)mail(s) is/are sent with the subject :subject
* @Then :count new (e)mail(s) is/are sent to :to with the subject :subject
*/
public function noNewMailIsSent($count, $to = '', $subject = '') {
$actualMail = $this->getMail(['to' => $to, 'subject' => $subject], TRUE);
$count = $count === 'no' ? 0 : $count;
$count = $count === 'a' ? 1 : $count;
$count = $count === 'an' ? 1 : $count;
$this->assertMailCount($actualMail, $count);
}

/**
* @When I follow the link to :urlFragment from the (e)mail
* @When I follow the link to :urlFragment from the (e)mail to :to
* @When I follow the link to :urlFragment from the (e)mail with the subject :subject
* @When I follow the link to :urlFragment from the (e)mail to :to with the subject :subject
*/
public function followLinkInMail($urlFragment, $to = '', $subject = '') {
// Get the mail
$matches = ['to' => $to, 'subject' => $subject];
$mail = $this->getMail($matches, FALSE, -1);
if (count($mail) == 0) {
throw new \Exception('No such mail found.');
}
$body = $mail['body'];

// Find web URLs in the mail
$urlPattern = '`.*?((http|https)://[\w#$&+,\/:;[email protected]]+)[^\w#$&+,\/:;[email protected]]*?`i';
if (preg_match_all($urlPattern, $body, $urls)) {
// Visit the first url that contains the desired fragment.
foreach ($urls[1] as $url) {
$match = (strpos(strtolower($url), strtolower($urlFragment)) !== FALSE);
if ($match) {
$this->getMinkContext()->visitPath($url);
return;
}
}
throw new \Exception(sprintf('No URL in mail body contained "%s".', $urlFragment));
}
else {
throw new \Exception('No URL found in mail body.');
}
}

}
Loading