diff --git a/behat.yml.dist b/behat.yml.dist index 0fff888b..000eaeb6 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -115,6 +115,7 @@ drupal8: - Drupal\DrupalExtension\Context\MarkupContext - Drupal\DrupalExtension\Context\MessageContext - Drupal\DrupalExtension\Context\MailContext + - Drupal\DrupalExtension\Context\RandomContext filters: tags: "@d8&&~@d8wip" extensions: diff --git a/doc/contexts.rst b/doc/contexts.rst index 6a722135..43dd5ebe 100644 --- a/doc/contexts.rst +++ b/doc/contexts.rst @@ -31,6 +31,11 @@ following contexts: Step-definitions that are specific to Drupal messages that get displayed (notice, warning, and error). +*RandomContext* + Contains transforms that allow for use of placeholders such as `` that will be replaced with a random string + when the scenario is run: `Given I am viewing an "Article" with the title "<title>"` + + *DrushContext* Allows steps to directly call drush commands. diff --git a/features/random.feature b/features/random.feature new file mode 100644 index 00000000..f7da6729 --- /dev/null +++ b/features/random.feature @@ -0,0 +1,20 @@ +@api @d8 @random +Feature: RandomContext functionality + In order to prove the RandomContext is functional at transforming variables + As a developer + I need to use random variables in scenarios + + # This will fail on the second scenario if random transforms are not functional. + Scenario: Create a first user + Given I am at "/user/register" + And I fill in "Email address" with "<user>@example.com" + And I fill in "Username" with "<user>" + When I press "Create new account" + Then an email has been sent to "<user>@example.com" with the subject "Account details for <user>" + + Scenario: Create the second user + Given I am at "/user/register" + And I fill in "Email address" with "<user>@example.com" + And I fill in "Username" with "<user>" + When I press "Create new account" + Then an email has been sent to "<user>@example.com" with the subject "Account details for <user>" diff --git a/spec/Drupal/DrupalExtension/Context/RandomContextSpec.php b/spec/Drupal/DrupalExtension/Context/RandomContextSpec.php new file mode 100644 index 00000000..7390b02a --- /dev/null +++ b/spec/Drupal/DrupalExtension/Context/RandomContextSpec.php @@ -0,0 +1,15 @@ +<?php + +namespace spec\Drupal\DrupalExtension\Context; + +use Drupal\DrupalExtension\Context\RandomContext; +use PhpSpec\ObjectBehavior; +use Prophecy\Argument; + +class RandomContextSpec extends ObjectBehavior +{ + function it_is_initializable() + { + $this->shouldHaveType(RandomContext::class); + } +} diff --git a/src/Drupal/DrupalExtension/Context/RandomContext.php b/src/Drupal/DrupalExtension/Context/RandomContext.php new file mode 100644 index 00000000..482d199d --- /dev/null +++ b/src/Drupal/DrupalExtension/Context/RandomContext.php @@ -0,0 +1,88 @@ +<?php + +namespace Drupal\DrupalExtension\Context; + +use Behat\Behat\Hook\Scope\AfterScenarioScope; +use Behat\Behat\Hook\Scope\BeforeScenarioScope; + +/** + * Class RandomContext + * @package Drupal\DrupalExtension\Context + * + * Transform tokens into random variables. + */ +class RandomContext extends RawDrupalContext +{ + /** + * Tracks variable names for consistent replacement during a given scenario. + * + * @var array + */ + protected $values = []; + + /** + * The regex to use for variable replacement. + */ + const VARIABLE_REGEX = '#(\<.*?\>)#'; + + /** + * Transform random variables. + * + * @Transform #([^<]*\<.*\>[^>]*)# + */ + public function transformVariables($message) + { + $patterns = []; + $replacements = []; + + preg_match_all(static::VARIABLE_REGEX, $message, $matches); + foreach ($matches[0] as $variable) { + $replacements[] = $this->values[$variable]; + $patterns[] = '#' . $variable . '#'; + } + $message = preg_replace($patterns, $replacements, $message); + + return $message; + } + + /** + * Set values for each random variable found in the current scenario. + * + * @BeforeScenario + */ + public function beforeScenarioSetVariables(BeforeScenarioScope $scope) + { + $steps = []; + if ($scope->getFeature()->hasBackground()) { + $steps = $scope->getFeature()->getBackground()->getSteps(); + } + $steps = array_merge($steps, $scope->getScenario()->getSteps()); + foreach ($steps as $step) { + preg_match_all(static::VARIABLE_REGEX, $step->getText(), $matches); + $variables_found = $matches[0]; + // Find variables in are TableNodes or PyStringNodes. + $step_argument = $step->getArguments(); + if (!empty($step_argument) && $step_argument[0] instanceof TableNode) { + preg_match_all(static::VARIABLE_REGEX, $step_argument[0]->getTableAsString(), $matches); + $variables_found = array_filter(array_merge($variables_found, $matches[0])); + } + foreach ($variables_found as $variable_name) { + if (!isset($this->values[$variable_name])) { + $value = $this->getDriver()->getRandom()->name(10); + // Value forced to lowercase to ensure it is machine-readable. + $this->values[$variable_name] = strtolower($value); + } + } + } + } + + /** + * Reset variables after the scenario. + * + * @AfterScenario + */ + public function afterScenarioResetVariables(AfterScenarioScope $scope) + { + $this->values = []; + } +}