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 ""`
+
+
*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 "@example.com"
+ And I fill in "Username" with ""
+ When I press "Create new account"
+ Then an email has been sent to "@example.com" with the subject "Account details for "
+
+ Scenario: Create the second user
+ Given I am at "/user/register"
+ And I fill in "Email address" with "@example.com"
+ And I fill in "Username" with ""
+ When I press "Create new account"
+ Then an email has been sent to "@example.com" with the subject "Account details for "
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 @@
+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 @@
+)#';
+
+ /**
+ * 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 = [];
+ }
+}