Skip to content

Commit

Permalink
Adds a RandomContext for generating random strings in steps.
Browse files Browse the repository at this point in the history
```
Given I am viewing an "Article" with the title "<title>"
Then I should see the header "<title>"
```

When the `RandomContext` is included, then `<title>` will be replaced
with a random string during the scenario. It will have a consistent
value throughout a given scenario, but a new value in subsequent
scenarios.

- Fixes #293
  • Loading branch information
jhedstrom committed Mar 27, 2018
1 parent 8eafb07 commit 44f9eec
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 1 deletion.
1 change: 1 addition & 0 deletions behat.yml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"behat/mink-selenium2-driver": "~1.1",
"drupal/drupal-driver": "^2.0.0",
"symfony/dependency-injection": "~3.0",
"symfony/event-dispatcher": "~3.0"
"symfony/event-dispatcher": "~3.0",
},
"require-dev": {
"phpspec/phpspec": "~2.0 || ~4.0",
Expand Down
5 changes: 5 additions & 0 deletions doc/contexts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<title>` 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.

Expand Down
20 changes: 20 additions & 0 deletions features/random.feature
Original file line number Diff line number Diff line change
@@ -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>"
15 changes: 15 additions & 0 deletions spec/Drupal/DrupalExtension/Context/RandomContextSpec.php
Original file line number Diff line number Diff line change
@@ -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);
}
}
88 changes: 88 additions & 0 deletions src/Drupal/DrupalExtension/Context/RandomContext.php
Original file line number Diff line number Diff line change
@@ -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 = [];
}
}

0 comments on commit 44f9eec

Please sign in to comment.