+++ /dev/null
-<?php
-
-namespace Drupal\DrupalExtension\Context;
-
-use Behat\MinkExtension\Context\RawMinkContext;
-use Behat\Mink\Exception\DriverException;
-use Behat\Testwork\Hook\HookDispatcher;
-
-use Drupal\DrupalDriverManager;
-
-use Drupal\DrupalExtension\Hook\Scope\AfterLanguageEnableScope;
-use Drupal\DrupalExtension\Hook\Scope\AfterNodeCreateScope;
-use Drupal\DrupalExtension\Hook\Scope\AfterTermCreateScope;
-use Drupal\DrupalExtension\Hook\Scope\AfterUserCreateScope;
-use Drupal\DrupalExtension\Hook\Scope\BaseEntityScope;
-use Drupal\DrupalExtension\Hook\Scope\BeforeLanguageEnableScope;
-use Drupal\DrupalExtension\Hook\Scope\BeforeNodeCreateScope;
-use Drupal\DrupalExtension\Hook\Scope\BeforeUserCreateScope;
-use Drupal\DrupalExtension\Hook\Scope\BeforeTermCreateScope;
-
-
-/**
- * Provides the raw functionality for interacting with Drupal.
- */
-class RawDrupalContext extends RawMinkContext implements DrupalAwareInterface {
-
- /**
- * Drupal driver manager.
- *
- * @var \Drupal\DrupalDriverManager
- */
- private $drupal;
-
- /**
- * Test parameters.
- *
- * @var array
- */
- private $drupalParameters;
-
- /**
- * Event dispatcher object.
- *
- * @var \Behat\Testwork\Hook\HookDispatcher
- */
- protected $dispatcher;
-
- /**
- * Keep track of nodes so they can be cleaned up.
- *
- * @var array
- */
- protected $nodes = array();
-
- /**
- * Current authenticated user.
- *
- * A value of FALSE denotes an anonymous user.
- *
- * @var \stdClass|bool
- */
- public $user = FALSE;
-
- /**
- * Keep track of all users that are created so they can easily be removed.
- *
- * @var array
- */
- protected $users = array();
-
- /**
- * Keep track of all terms that are created so they can easily be removed.
- *
- * @var array
- */
- protected $terms = array();
-
- /**
- * Keep track of any roles that are created so they can easily be removed.
- *
- * @var array
- */
- protected $roles = array();
-
- /**
- * Keep track of any languages that are created so they can easily be removed.
- *
- * @var array
- */
- protected $languages = array();
-
- /**
- * {@inheritDoc}
- */
- public function setDrupal(DrupalDriverManager $drupal) {
- $this->drupal = $drupal;
- }
-
- /**
- * {@inheritDoc}
- */
- public function getDrupal() {
- return $this->drupal;
- }
-
- /**
- * {@inheritDoc}
- */
- public function setDispatcher(HookDispatcher $dispatcher) {
- $this->dispatcher = $dispatcher;
- }
-
- /**
- * Set parameters provided for Drupal.
- */
- public function setDrupalParameters(array $parameters) {
- $this->drupalParameters = $parameters;
- }
-
- /**
- * Returns a specific Drupal parameter.
- *
- * @param string $name
- * Parameter name.
- *
- * @return mixed
- */
- public function getDrupalParameter($name) {
- return isset($this->drupalParameters[$name]) ? $this->drupalParameters[$name] : NULL;
- }
-
- /**
- * Returns a specific Drupal text value.
- *
- * @param string $name
- * Text value name, such as 'log_out', which corresponds to the default 'Log
- * out' link text.
- * @throws \Exception
- * @return
- */
- public function getDrupalText($name) {
- $text = $this->getDrupalParameter('text');
- if (!isset($text[$name])) {
- throw new \Exception(sprintf('No such Drupal string: %s', $name));
- }
- return $text[$name];
- }
-
- /**
- * Returns a specific css selector.
- *
- * @param $name
- * string CSS selector name
- */
- public function getDrupalSelector($name) {
- $text = $this->getDrupalParameter('selectors');
- if (!isset($text[$name])) {
- throw new \Exception(sprintf('No such selector configured: %s', $name));
- }
- return $text[$name];
- }
-
- /**
- * Get active Drupal Driver.
- *
- * @return \Drupal\Driver\DrupalDriver
- */
- public function getDriver($name = NULL) {
- return $this->getDrupal()->getDriver($name);
- }
-
- /**
- * Get driver's random generator.
- */
- public function getRandom() {
- return $this->getDriver()->getRandom();
- }
-
- /**
- * Massage node values to match the expectations on different Drupal versions.
- *
- * @beforeNodeCreate
- */
- public static function alterNodeParameters(BeforeNodeCreateScope $scope) {
- $node = $scope->getEntity();
-
- // Get the Drupal API version if available. This is not available when
- // using e.g. the BlackBoxDriver or DrushDriver.
- $api_version = NULL;
- $driver = $scope->getContext()->getDrupal()->getDriver();
- if ($driver instanceof \Drupal\Driver\DrupalDriver) {
- $api_version = $scope->getContext()->getDrupal()->getDriver()->version;
- }
-
- // On Drupal 8 the timestamps should be in UNIX time.
- switch ($api_version) {
- case 8:
- foreach (array('changed', 'created', 'revision_timestamp') as $field) {
- if (!empty($node->$field) && !is_numeric($node->$field)) {
- $node->$field = strtotime($node->$field);
- }
- }
- break;
- }
- }
-
- /**
- * Remove any created nodes.
- *
- * @AfterScenario
- */
- public function cleanNodes() {
- // Remove any nodes that were created.
- foreach ($this->nodes as $node) {
- $this->getDriver()->nodeDelete($node);
- }
- $this->nodes = array();
- }
-
- /**
- * Remove any created users.
- *
- * @AfterScenario
- */
- public function cleanUsers() {
- // Remove any users that were created.
- if (!empty($this->users)) {
- foreach ($this->users as $user) {
- $this->getDriver()->userDelete($user);
- }
- $this->getDriver()->processBatch();
- $this->users = array();
- $this->user = FALSE;
- if ($this->loggedIn()) {
- $this->logout();
- }
- }
- }
-
- /**
- * Remove any created terms.
- *
- * @AfterScenario
- */
- public function cleanTerms() {
- // Remove any terms that were created.
- foreach ($this->terms as $term) {
- $this->getDriver()->termDelete($term);
- }
- $this->terms = array();
- }
-
- /**
- * Remove any created roles.
- *
- * @AfterScenario
- */
- public function cleanRoles() {
- // Remove any roles that were created.
- foreach ($this->roles as $rid) {
- $this->getDriver()->roleDelete($rid);
- }
- $this->roles = array();
- }
-
- /**
- * Remove any created languages.
- *
- * @AfterScenario
- */
- public function cleanLanguages() {
- // Delete any languages that were created.
- foreach ($this->languages as $language) {
- $this->getDriver()->languageDelete($language);
- unset($this->languages[$language->langcode]);
- }
- }
-
- /**
- * Clear static caches.
- *
- * @AfterScenario @api
- */
- public function clearStaticCaches() {
- $this->getDriver()->clearStaticCaches();
- }
-
- /**
- * Dispatch scope hooks.
- *
- * @param string $scope
- * The entity scope to dispatch.
- * @param \stdClass $entity
- * The entity.
- */
- protected function dispatchHooks($scopeType, \stdClass $entity) {
- $fullScopeClass = 'Drupal\\DrupalExtension\\Hook\\Scope\\' . $scopeType;
- $scope = new $fullScopeClass($this->getDrupal()->getEnvironment(), $this, $entity);
- $callResults = $this->dispatcher->dispatchScopeHooks($scope);
-
- // The dispatcher suppresses exceptions, throw them here if there are any.
- foreach ($callResults as $result) {
- if ($result->hasException()) {
- $exception = $result->getException();
- throw $exception;
- }
- }
- }
-
- /**
- * Create a node.
- *
- * @return object
- * The created node.
- */
- public function nodeCreate($node) {
- $this->dispatchHooks('BeforeNodeCreateScope', $node);
- $this->parseEntityFields('node', $node);
- $saved = $this->getDriver()->createNode($node);
- $this->dispatchHooks('AfterNodeCreateScope', $saved);
- $this->nodes[] = $saved;
- return $saved;
- }
-
- /**
- * Parse multi-value fields. Possible formats:
- * A, B, C
- * A - B, C - D, E - F
- *
- * @param string $entity_type
- * The entity type.
- * @param \stdClass $entity
- * An object containing the entity properties and fields as properties.
- */
- public function parseEntityFields($entity_type, \stdClass $entity) {
- $multicolumn_field = '';
- $multicolumn_fields = array();
-
- foreach (clone $entity as $field => $field_value) {
- // Reset the multicolumn field if the field name does not contain a column.
- if (strpos($field, ':') === FALSE) {
- $multicolumn_field = '';
- }
- // Start tracking a new multicolumn field if the field name contains a ':'
- // which is preceded by at least 1 character.
- elseif (strpos($field, ':', 1) !== FALSE) {
- list($multicolumn_field, $multicolumn_column) = explode(':', $field);
- }
- // If a field name starts with a ':' but we are not yet tracking a
- // multicolumn field we don't know to which field this belongs.
- elseif (empty($multicolumn_field)) {
- throw new \Exception('Field name missing for ' . $field);
- }
- // Update the column name if the field name starts with a ':' and we are
- // already tracking a multicolumn field.
- else {
- $multicolumn_column = substr($field, 1);
- }
-
- $is_multicolumn = $multicolumn_field && $multicolumn_column;
- $field_name = $multicolumn_field ?: $field;
- if ($this->getDriver()->isField($entity_type, $field_name)) {
- // Split up multiple values in multi-value fields.
- $values = array();
- foreach (explode(', ', $field_value) as $key => $value) {
- $columns = $value;
- // Split up field columns if the ' - ' separator is present.
- if (strstr($value, ' - ') !== FALSE) {
- $columns = array();
- foreach (explode(' - ', $value) as $column) {
- // Check if it is an inline named column.
- if (!$is_multicolumn && strpos($column, ': ', 1) !== FALSE) {
- list ($key, $column) = explode(': ', $column);
- $columns[$key] = $column;
- }
- else {
- $columns[] = $column;
- }
- }
- }
- // Use the column name if we are tracking a multicolumn field.
- if ($is_multicolumn) {
- $multicolumn_fields[$multicolumn_field][$key][$multicolumn_column] = $columns;
- unset($entity->$field);
- }
- else {
- $values[] = $columns;
- }
- }
- // Replace regular fields inline in the entity after parsing.
- if (!$is_multicolumn) {
- $entity->$field_name = $values;
- // Don't specify any value if the step author has left it blank.
- if ($field_value === '') {
- unset($entity->$field_name);
- }
- }
- }
- }
-
- // Add the multicolumn fields to the entity.
- foreach ($multicolumn_fields as $field_name => $columns) {
- // Don't specify any value if the step author has left it blank.
- if (count(array_filter($columns, function ($var) {
- return ($var !== '');
- })) > 0) {
- $entity->$field_name = $columns;
- }
- }
- }
-
- /**
- * Create a user.
- *
- * @return object
- * The created user.
- */
- public function userCreate($user) {
- $this->dispatchHooks('BeforeUserCreateScope', $user);
- $this->parseEntityFields('user', $user);
- $this->getDriver()->userCreate($user);
- $this->dispatchHooks('AfterUserCreateScope', $user);
- $this->users[$user->name] = $this->user = $user;
- return $user;
- }
-
- /**
- * Create a term.
- *
- * @return object
- * The created term.
- */
- public function termCreate($term) {
- $this->dispatchHooks('BeforeTermCreateScope', $term);
- $this->parseEntityFields('taxonomy_term', $term);
- $saved = $this->getDriver()->createTerm($term);
- $this->dispatchHooks('AfterTermCreateScope', $saved);
- $this->terms[] = $saved;
- return $saved;
- }
-
- /**
- * Creates a language.
- *
- * @param \stdClass $language
- * An object with the following properties:
- * - langcode: the langcode of the language to create.
- *
- * @return object|FALSE
- * The created language, or FALSE if the language was already created.
- */
- public function languageCreate(\stdClass $language) {
- $this->dispatchHooks('BeforeLanguageCreateScope', $language);
- $language = $this->getDriver()->languageCreate($language);
- if ($language) {
- $this->dispatchHooks('AfterLanguageCreateScope', $language);
- $this->languages[$language->langcode] = $language;
- }
- return $language;
- }
-
- /**
- * Log-in the current user.
- */
- public function login() {
- // Check if logged in.
- if ($this->loggedIn()) {
- $this->logout();
- }
-
- if (!$this->user) {
- throw new \Exception('Tried to login without a user.');
- }
-
- $this->getSession()->visit($this->locatePath('/user'));
- $element = $this->getSession()->getPage();
- $element->fillField($this->getDrupalText('username_field'), $this->user->name);
- $element->fillField($this->getDrupalText('password_field'), $this->user->pass);
- $submit = $element->findButton($this->getDrupalText('log_in'));
- if (empty($submit)) {
- throw new \Exception(sprintf("No submit button at %s", $this->getSession()->getCurrentUrl()));
- }
-
- // Log in.
- $submit->click();
-
- if (!$this->loggedIn()) {
- if (isset($this->user->role)) {
- throw new \Exception(sprintf("Unable to determine if logged in because 'log_out' link cannot be found for user '%s' with role '%s'", $this->user->name, $this->user->role));
- }
- else {
- throw new \Exception(sprintf("Unable to determine if logged in because 'log_out' link cannot be found for user '%s'", $this->user->name));
- }
- }
- }
-
- /**
- * Logs the current user out.
- */
- public function logout() {
- $this->getSession()->visit($this->locatePath('/user/logout'));
- }
-
- /**
- * Determine if the a user is already logged in.
- *
- * @return boolean
- * Returns TRUE if a user is logged in for this session.
- */
- public function loggedIn() {
- $session = $this->getSession();
-
- // If the session has not been started yet, or no page has yet been loaded,
- // then this is a brand new test session and the user is not logged in.
- if (!$session->isStarted() || !$page = $session->getPage()) {
- return FALSE;
- }
-
- // Look for a css selector to determine if a user is logged in.
- // Default is the logged-in class on the body tag.
- // Which should work with almost any theme.
- try {
- if ($page->has('css', $this->getDrupalSelector('logged_in_selector'))) {
- return TRUE;
- }
- } catch (DriverException $e) {
- // This test may fail if the driver did not load any site yet.
- }
-
- // Some themes do not add that class to the body, so lets check if the
- // login form is displayed on /user/login.
- $session->visit($this->locatePath('/user/login'));
- if (!$page->has('css', $this->getDrupalSelector('login_form_selector'))) {
- return TRUE;
- }
-
- $session->visit($this->locatePath('/'));
-
- // As a last resort, if a logout link is found, we are logged in. While not
- // perfect, this is how Drupal SimpleTests currently work as well.
- return $page->findLink($this->getDrupalText('log_out'));
- }
-
- /**
- * User with a given role is already logged in.
- *
- * @param string $role
- * A single role, or multiple comma-separated roles in a single string.
- *
- * @return boolean
- * Returns TRUE if the current logged in user has this role (or roles).
- */
- public function loggedInWithRole($role) {
- return $this->loggedIn() && $this->user && isset($this->user->role) && $this->user->role == $role;
- }
-
-}