Skip to content

Commit

Permalink
Merge pull request #473 from jhedstrom/195-email-testing
Browse files Browse the repository at this point in the history
Reroll: 392 Support for testing emails sent from Drupal
  • Loading branch information
jhedstrom authored Mar 19, 2018
2 parents 078292e + 016a454 commit b58d468
Show file tree
Hide file tree
Showing 8 changed files with 656 additions and 5 deletions.
18 changes: 14 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
## [4.0.0 alpha4] 2018-03-19
### Added
* [#195](https://github.com/jhedstrom/drupalextension/issues/195): Support for testing emails sent from Drupal.
* [#477](https://github.com/jhedstrom/drupalextension/pull/477): Adds a `composer test` script.
### Changed
* Switches from `1.x` to `2.x` version of the DrupalDriver. This was necessary for the email testing in
[#195](https://github.com/jhedstrom/drupalextension/issues/195).
### Fixed
* [#464](https://github.com/jhedstrom/drupalextension/pull/464): Remove outdated `sudo` installation instructions.

## [4.0.0 alpha3]
## [4.0.0 alpha3] 2018-02-26
### Added
* [#467](https://github.com/jhedstrom/drupalextension/pull/467): Allows
AJAX timeout to be overridden via an `ajax_timeout` parameter in `behat.yml`.
Expand All @@ -23,14 +32,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
* [#462](https://github.com/jhedstrom/drupalextension/pull/462): The version of DrupalDriver is now pinned to a stable
release instead of `dev-master`.

## [4.0.0 alpha2]
## [4.0.0 alpha2] 2017-12-14
### Added
* [#448](https://github.com/jhedstrom/drupalextension/pull/448): Additional use of the fast logout method.
### Fixed
* [#447](https://github.com/jhedstrom/drupalextension/pull/447): Only reset session if it is started during fast
logout.

## [4.0.0 alpha1]
## [4.0.0 alpha1] 2017-12-06
### Added
* [#268](https://github.com/jhedstrom/drupalextension/pull/268): Added a `BatchContext` for testing queue-runners and
other batch-related functionality.
Expand Down Expand Up @@ -60,7 +69,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
* [#437](https://github.com/jhedstrom/drupalextension/pull/437): Radio button selector fix.
* [#439](https://github.com/jhedstrom/drupalextension/pull/439): Symfony 3 compatibility follow-up fix.

[Unreleased]: https://github.com/jhedstrom/drupalextension/compare/v4.0.0alpha2...HEAD
[Unreleased]: https://github.com/jhedstrom/drupalextension/compare/v4.0.0alpha4...HEAD
[4.0.0 alpha4]:https://github.com/jhedstrom/drupalextension/compare/v4.0.0alpha3...v4.0.0alpha4
[4.0.0 alpha3]:https://github.com/jhedstrom/drupalextension/compare/v4.0.0alpha2...v4.0.0alpha3
[4.0.0 alpha2]:https://github.com/jhedstrom/drupalextension/compare/v4.0.0alpha1...v4.0.0alpha2
[4.0.0 alpha1]:https://github.com/jhedstrom/drupalextension/compare/v3.4.0...v4.0.0alpha1
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
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"behat/mink-extension": "~2.0",
"behat/mink-goutte-driver": "~1.0",
"behat/mink-selenium2-driver": "~1.1",
"drupal/drupal-driver": "^1.3.2",
"drupal/drupal-driver": "^2.0.0",
"symfony/dependency-injection": "~3.0",
"symfony/event-dispatcher": "~3.0"
},
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 | fred@example.com |
| subject | test |
| body | test body |
And Drupal sends a mail:
| to | jane@example.com |
| 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 | jack@example.com |
| 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 | fred@example.com |
| subject | test 1 |
And Drupal sends a mail:
| to | jane@example.com |
| 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 | fred@example.com |
| subject | test |
And Drupal sends a mail:
| to | jane@example.com |
| subject | test |
And Drupal sends a mail:
| to | jane@example.com |
| 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 | fred@example.com |
| 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 | fred@example.com |
| subject | test |
| body | test body |
And Drupal sends a mail:
| to | jane@example.com |
| subject | test |
| body | test body 2 |
Then mails have been sent:
| to | subject | body |
| jane | test | body 2 |
| fred | | test body |
179 changes: 179 additions & 0 deletions src/Drupal/DrupalExtension/Context/MailContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
<?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

0 comments on commit b58d468

Please sign in to comment.