Completed
Pull Request — master (#307)
by Pieter
04:19
created

RawDrupalContext::getDrupalText()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 7
Ratio 100 %

Importance

Changes 0
Metric Value
dl 7
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace Drupal\DrupalExtension\Context;
4
5
use Behat\MinkExtension\Context\RawMinkContext;
6
use Behat\Mink\Exception\DriverException;
7
use Behat\Testwork\Hook\HookDispatcher;
8
9
use Drupal\DrupalDriverManager;
10
use Drupal\DrupalUserManagerInterface;
11
12
use Drupal\DrupalExtension\Hook\Scope\AfterLanguageEnableScope;
13
use Drupal\DrupalExtension\Hook\Scope\AfterNodeCreateScope;
14
use Drupal\DrupalExtension\Hook\Scope\AfterTermCreateScope;
15
use Drupal\DrupalExtension\Hook\Scope\AfterUserCreateScope;
16
use Drupal\DrupalExtension\Hook\Scope\BaseEntityScope;
17
use Drupal\DrupalExtension\Hook\Scope\BeforeLanguageEnableScope;
18
use Drupal\DrupalExtension\Hook\Scope\BeforeNodeCreateScope;
19
use Drupal\DrupalExtension\Hook\Scope\BeforeUserCreateScope;
20
use Drupal\DrupalExtension\Hook\Scope\BeforeTermCreateScope;
21
22
23
/**
24
 * Provides the raw functionality for interacting with Drupal.
25
 */
26
class RawDrupalContext extends RawMinkContext implements DrupalAwareInterface {
27
28
  /**
29
   * Drupal driver manager.
30
   *
31
   * @var \Drupal\DrupalDriverManager
32
   */
33
  private $drupal;
34
35
  /**
36
   * Test parameters.
37
   *
38
   * @var array
39
   */
40
  private $drupalParameters;
41
42
  /**
43
   * Event dispatcher object.
44
   *
45
   * @var \Behat\Testwork\Hook\HookDispatcher
46
   */
47
  protected $dispatcher;
48
49
  /**
50
   * Drupal user manager.
51
   *
52
   * @var \Drupal\DrupalUserManagerInterface
53
   */
54
  protected $userManager;
55
56
  /**
57
   * Keep track of nodes so they can be cleaned up.
58
   *
59
   * @var array
60
   */
61
  protected $nodes = array();
62
63
  /**
64
   * Keep track of all users that are created so they can easily be removed.
65
   *
66
   * @var array
67
   */
68
  protected $users = array();
69
70
  /**
71
   * Keep track of all terms that are created so they can easily be removed.
72
   *
73
   * @var array
74
   */
75
  protected $terms = array();
76
77
  /**
78
   * Keep track of any roles that are created so they can easily be removed.
79
   *
80
   * @var array
81
   */
82
  protected $roles = array();
83
84
  /**
85
   * Keep track of any languages that are created so they can easily be removed.
86
   *
87
   * @var array
88
   */
89
  protected $languages = array();
90
91
  /**
92
   * {@inheritDoc}
93
   */
94
  public function setDrupal(DrupalDriverManager $drupal) {
95
    $this->drupal = $drupal;
96
  }
97
98
  /**
99
   * {@inheritDoc}
100
   */
101
  public function getDrupal() {
102
    return $this->drupal;
103
  }
104
105
  /**
106
   * {@inheritDoc}
107
   */
108
  public function setUserManager(DrupalUserManagerInterface $userManager) {
109
    $this->userManager = $userManager;
110
  }
111
112
  /**
113
   * {@inheritdoc}
114
   */
115
  public function getUserManager() {
116
    return $this->userManager;
117
  }
118
119
  /**
120
   * Magic setter.
121
   */
122
  public function __set($name, $value) {
123
    if ($name === 'user') {
124
      // Set the user on the user manager service, so it is shared between all
125
      // contexts.
126
      $this->getUserManager()->setUser($value);
127
    }
128
  }
129
130
  /**
131
   * Magic getter.
132
   */
133
  public function __get($name) {
134
    if ($name === 'user') {
135
      // Returns the current user from the user manager service. This is shared
136
      // between all contexts.
137
      return $this->getUserManager()->getUser();
138
    }
139
  }
140
141
  /**
142
   * {@inheritdoc}
143
   */
144
  public function setDispatcher(HookDispatcher $dispatcher) {
145
    $this->dispatcher = $dispatcher;
146
  }
147
148
  /**
149
   * Set parameters provided for Drupal.
150
   */
151
  public function setDrupalParameters(array $parameters) {
152
    $this->drupalParameters = $parameters;
153
  }
154
155
  /**
156
   * Returns a specific Drupal parameter.
157
   *
158
   * @param string $name
159
   *   Parameter name.
160
   *
161
   * @return mixed
162
   */
163
  public function getDrupalParameter($name) {
164
    return isset($this->drupalParameters[$name]) ? $this->drupalParameters[$name] : NULL;
165
  }
166
167
  /**
168
   * Returns a specific Drupal text value.
169
   *
170
   * @param string $name
171
   *   Text value name, such as 'log_out', which corresponds to the default 'Log
172
   *   out' link text.
173
   * @throws \Exception
174
   * @return
175
   */
176 View Code Duplication
  public function getDrupalText($name) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
177
    $text = $this->getDrupalParameter('text');
178
    if (!isset($text[$name])) {
179
      throw new \Exception(sprintf('No such Drupal string: %s', $name));
180
    }
181
    return $text[$name];
182
  }
183
184
  /**
185
   * Returns a specific css selector.
186
   *
187
   * @param $name
188
   *   string CSS selector name
189
   */
190 View Code Duplication
  public function getDrupalSelector($name) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
191
    $text = $this->getDrupalParameter('selectors');
192
    if (!isset($text[$name])) {
193
      throw new \Exception(sprintf('No such selector configured: %s', $name));
194
    }
195
    return $text[$name];
196
  }
197
198
  /**
199
   * Get active Drupal Driver.
200
   *
201
   * @return \Drupal\Driver\DrupalDriver
202
   */
203
  public function getDriver($name = NULL) {
204
    return $this->getDrupal()->getDriver($name);
205
  }
206
207
  /**
208
   * Get driver's random generator.
209
   */
210
  public function getRandom() {
211
    return $this->getDriver()->getRandom();
212
  }
213
214
  /**
215
   * Massage node values to match the expectations on different Drupal versions.
216
   *
217
   * @beforeNodeCreate
218
   */
219
  public function alterNodeParameters(BeforeNodeCreateScope $scope) {
220
    $node = $scope->getEntity();
221
222
    // Get the Drupal API version if available. This is not available when
223
    // using e.g. the BlackBoxDriver or DrushDriver.
224
    $api_version = NULL;
225
    $driver = $scope->getContext()->getDrupal()->getDriver();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Behat\Context\Context as the method getDrupal() does only exist in the following implementations of said interface: Drupal\DrupalExtension\Context\ConfigContext, Drupal\DrupalExtension\Context\DrupalContext, Drupal\DrupalExtension\C...xt\DrupalSubContextBase, Drupal\DrupalExtension\Context\DrushContext, Drupal\DrupalExtension\Context\MessageContext, Drupal\DrupalExtension\Context\RawDrupalContext, FeatureContext, FooFoo.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
226
    if ($driver instanceof \Drupal\Driver\DrupalDriver) {
227
      $api_version = $scope->getContext()->getDrupal()->getDriver()->version;
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Behat\Context\Context as the method getDrupal() does only exist in the following implementations of said interface: Drupal\DrupalExtension\Context\ConfigContext, Drupal\DrupalExtension\Context\DrupalContext, Drupal\DrupalExtension\C...xt\DrupalSubContextBase, Drupal\DrupalExtension\Context\DrushContext, Drupal\DrupalExtension\Context\MessageContext, Drupal\DrupalExtension\Context\RawDrupalContext, FeatureContext, FooFoo.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
228
    }
229
230
    // On Drupal 8 the timestamps should be in UNIX time.
231
    switch ($api_version) {
232
      case 8:
233
        foreach (array('changed', 'created', 'revision_timestamp') as $field) {
234
          if (!empty($node->$field) && !is_numeric($node->$field)) {
235
            $node->$field = strtotime($node->$field);
236
          }
237
        }
238
      break;
239
    }
240
  }
241
242
  /**
243
   * Remove any created nodes.
244
   *
245
   * @AfterScenario
246
   */
247
  public function cleanNodes() {
248
    // Remove any nodes that were created.
249
    foreach ($this->nodes as $node) {
250
      $this->getDriver()->nodeDelete($node);
251
    }
252
    $this->nodes = array();
253
  }
254
255
  /**
256
   * Remove any created users.
257
   *
258
   * @AfterScenario
259
   */
260
  public function cleanUsers() {
261
    // Remove any users that were created.
262
    if (!empty($this->users)) {
263
      foreach ($this->users as $user) {
264
        $this->getDriver()->userDelete($user);
265
      }
266
      $this->getDriver()->processBatch();
267
      $this->users = array();
268
      $this->user = FALSE;
0 ignored issues
show
Documentation introduced by
The property user does not exist on object<Drupal\DrupalExte...ntext\RawDrupalContext>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
269
      if ($this->loggedIn()) {
270
        $this->logout();
271
      }
272
    }
273
  }
274
275
  /**
276
   * Remove any created terms.
277
   *
278
   * @AfterScenario
279
   */
280
  public function cleanTerms() {
281
    // Remove any terms that were created.
282
    foreach ($this->terms as $term) {
283
      $this->getDriver()->termDelete($term);
284
    }
285
    $this->terms = array();
286
  }
287
288
  /**
289
   * Remove any created roles.
290
   *
291
   * @AfterScenario
292
   */
293
  public function cleanRoles() {
294
    // Remove any roles that were created.
295
    foreach ($this->roles as $rid) {
296
      $this->getDriver()->roleDelete($rid);
297
    }
298
    $this->roles = array();
299
  }
300
301
  /**
302
   * Remove any created languages.
303
   *
304
   * @AfterScenario
305
   */
306
  public function cleanLanguages() {
307
    // Delete any languages that were created.
308
    foreach ($this->languages as $language) {
309
      $this->getDriver()->languageDelete($language);
310
      unset($this->languages[$language->langcode]);
311
    }
312
  }
313
314
  /**
315
   * Clear static caches.
316
   *
317
   * @AfterScenario @api
318
   */
319
  public function clearStaticCaches() {
320
    $this->getDriver()->clearStaticCaches();
321
  }
322
323
  /**
324
   * Dispatch scope hooks.
325
   *
326
   * @param string $scope
0 ignored issues
show
Bug introduced by
There is no parameter named $scope. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
327
   *   The entity scope to dispatch.
328
   * @param \stdClass $entity
329
   *   The entity.
330
   */
331
  protected function dispatchHooks($scopeType, \stdClass $entity) {
332
    $fullScopeClass = 'Drupal\\DrupalExtension\\Hook\\Scope\\' . $scopeType;
333
    $scope = new $fullScopeClass($this->getDrupal()->getEnvironment(), $this, $entity);
334
    $callResults = $this->dispatcher->dispatchScopeHooks($scope);
335
336
    // The dispatcher suppresses exceptions, throw them here if there are any.
337
    foreach ($callResults as $result) {
338
      if ($result->hasException()) {
339
        $exception = $result->getException();
340
        throw $exception;
341
      }
342
    }
343
  }
344
345
  /**
346
   * Create a node.
347
   *
348
   * @return object
349
   *   The created node.
350
   */
351 View Code Duplication
  public function nodeCreate($node) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
352
    $this->dispatchHooks('BeforeNodeCreateScope', $node);
353
    $this->parseEntityFields('node', $node);
354
    $saved = $this->getDriver()->createNode($node);
355
    $this->dispatchHooks('AfterNodeCreateScope', $saved);
356
    $this->nodes[] = $saved;
357
    return $saved;
358
  }
359
360
  /**
361
   * Parse multi-value fields. Possible formats:
362
   *    A, B, C
363
   *    A - B, C - D, E - F
364
   *
365
   * @param string $entity_type
366
   *   The entity type.
367
   * @param \stdClass $entity
368
   *   An object containing the entity properties and fields as properties.
369
   */
370
  public function parseEntityFields($entity_type, \stdClass $entity) {
371
    $multicolumn_field = '';
372
    $multicolumn_fields = array();
373
374
    foreach (clone $entity as $field => $field_value) {
0 ignored issues
show
Bug introduced by
The expression clone $entity of type object<stdClass> is not traversable.
Loading history...
375
      // Reset the multicolumn field if the field name does not contain a column.
376
      if (strpos($field, ':') === FALSE) {
377
        $multicolumn_field = '';
378
      }
379
      // Start tracking a new multicolumn field if the field name contains a ':'
380
      // which is preceded by at least 1 character.
381
      elseif (strpos($field, ':', 1) !== FALSE) {
382
        list($multicolumn_field, $multicolumn_column) = explode(':', $field);
383
      }
384
      // If a field name starts with a ':' but we are not yet tracking a
385
      // multicolumn field we don't know to which field this belongs.
386
      elseif (empty($multicolumn_field)) {
387
        throw new \Exception('Field name missing for ' . $field);
388
      }
389
      // Update the column name if the field name starts with a ':' and we are
390
      // already tracking a multicolumn field.
391
      else {
392
        $multicolumn_column = substr($field, 1);
393
      }
394
395
      $is_multicolumn = $multicolumn_field && $multicolumn_column;
0 ignored issues
show
Bug introduced by
The variable $multicolumn_column does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
396
      $field_name = $multicolumn_field ?: $field;
397
      if ($this->getDriver()->isField($entity_type, $field_name)) {
398
        // Split up multiple values in multi-value fields.
399
        $values = array();
400
        foreach (explode(', ', $field_value) as $key => $value) {
401
          $columns = $value;
402
          // Split up field columns if the ' - ' separator is present.
403
          if (strstr($value, ' - ') !== FALSE) {
404
            $columns = array();
405
            foreach (explode(' - ', $value) as $column) {
406
              // Check if it is an inline named column.
407
              if (!$is_multicolumn && strpos($column, ': ', 1) !== FALSE) {
408
                list ($key, $column) = explode(': ', $column);
409
                $columns[$key] = $column;
410
              }
411
              else {
412
                $columns[] = $column;
413
              }
414
            }
415
          }
416
          // Use the column name if we are tracking a multicolumn field.
417
          if ($is_multicolumn) {
418
            $multicolumn_fields[$multicolumn_field][$key][$multicolumn_column] = $columns;
419
            unset($entity->$field);
420
          }
421
          else {
422
            $values[] = $columns;
423
          }
424
        }
425
        // Replace regular fields inline in the entity after parsing.
426
        if (!$is_multicolumn) {
427
          $entity->$field_name = $values;
428
        }
429
      }
430
    }
431
432
    // Add the multicolumn fields to the entity.
433
    foreach ($multicolumn_fields as $field_name => $columns) {
434
      $entity->$field_name = $columns;
435
    }
436
  }
437
438
  /**
439
   * Create a user.
440
   *
441
   * @return object
442
   *   The created user.
443
   */
444 View Code Duplication
  public function userCreate($user) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
445
    $this->dispatchHooks('BeforeUserCreateScope', $user);
446
    $this->parseEntityFields('user', $user);
447
    $this->getDriver()->userCreate($user);
448
    $this->dispatchHooks('AfterUserCreateScope', $user);
449
    $this->users[$user->name] = $this->user = $user;
0 ignored issues
show
Documentation introduced by
The property user does not exist on object<Drupal\DrupalExte...ntext\RawDrupalContext>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
450
    return $user;
451
  }
452
453
  /**
454
   * Create a term.
455
   *
456
   * @return object
457
   *   The created term.
458
   */
459 View Code Duplication
  public function termCreate($term) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
460
    $this->dispatchHooks('BeforeTermCreateScope', $term);
461
    $this->parseEntityFields('taxonomy_term', $term);
462
    $saved = $this->getDriver()->createTerm($term);
463
    $this->dispatchHooks('AfterTermCreateScope', $saved);
464
    $this->terms[] = $saved;
465
    return $saved;
466
  }
467
468
  /**
469
   * Creates a language.
470
   *
471
   * @param \stdClass $language
472
   *   An object with the following properties:
473
   *   - langcode: the langcode of the language to create.
474
   *
475
   * @return object|FALSE
476
   *   The created language, or FALSE if the language was already created.
477
   */
478
  public function languageCreate(\stdClass $language) {
479
    $this->dispatchHooks('BeforeLanguageCreateScope', $language);
480
    $language = $this->getDriver()->languageCreate($language);
481
    if ($language) {
482
      $this->dispatchHooks('AfterLanguageCreateScope', $language);
483
      $this->languages[$language->langcode] = $language;
484
    }
485
    return $language;
486
  }
487
488
  /**
489
   * Log-in the current user.
490
   */
491
  public function login() {
492
    // Check if logged in.
493
    if ($this->loggedIn()) {
494
      $this->logout();
495
    }
496
497
    if (!$this->user) {
0 ignored issues
show
Documentation introduced by
The property user does not exist on object<Drupal\DrupalExte...ntext\RawDrupalContext>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
498
      throw new \Exception('Tried to login without a user.');
499
    }
500
501
    $this->getSession()->visit($this->locatePath('/user'));
502
    $element = $this->getSession()->getPage();
503
    $element->fillField($this->getDrupalText('username_field'), $this->user->name);
0 ignored issues
show
Documentation introduced by
The property user does not exist on object<Drupal\DrupalExte...ntext\RawDrupalContext>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
504
    $element->fillField($this->getDrupalText('password_field'), $this->user->pass);
0 ignored issues
show
Documentation introduced by
The property user does not exist on object<Drupal\DrupalExte...ntext\RawDrupalContext>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
505
    $submit = $element->findButton($this->getDrupalText('log_in'));
506
    if (empty($submit)) {
507
      throw new \Exception(sprintf("No submit button at %s", $this->getSession()->getCurrentUrl()));
508
    }
509
510
    // Log in.
511
    $submit->click();
512
513
    if (!$this->loggedIn()) {
514
      if (isset($this->user->role)) {
0 ignored issues
show
Documentation introduced by
The property user does not exist on object<Drupal\DrupalExte...ntext\RawDrupalContext>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
515
        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));
0 ignored issues
show
Documentation introduced by
The property user does not exist on object<Drupal\DrupalExte...ntext\RawDrupalContext>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
516
      }
517
      else {
518
        throw new \Exception(sprintf("Unable to determine if logged in because 'log_out' link cannot be found for user '%s'", $this->user->name));
0 ignored issues
show
Documentation introduced by
The property user does not exist on object<Drupal\DrupalExte...ntext\RawDrupalContext>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
519
      }
520
    }
521
  }
522
523
  /**
524
   * Logs the current user out.
525
   */
526
  public function logout() {
527
    $this->getSession()->visit($this->locatePath('/user/logout'));
528
  }
529
530
  /**
531
   * Determine if the a user is already logged in.
532
   *
533
   * @return boolean
534
   *   Returns TRUE if a user is logged in for this session.
535
   */
536
  public function loggedIn() {
537
    $session = $this->getSession();
538
    $page = $session->getPage();
539
540
    // Look for a css selector to determine if a user is logged in.
541
    // Default is the logged-in class on the body tag.
542
    // Which should work with almost any theme.
543
    try {
544
      if ($page->has('css', $this->getDrupalSelector('logged_in_selector'))) {
545
        return TRUE;
546
      }
547
    } catch (DriverException $e) {
548
      // This test may fail if the driver did not load any site yet.
549
    }
550
551
    // Some themes do not add that class to the body, so lets check if the
552
    // login form is displayed on /user/login.
553
    $session->visit($this->locatePath('/user/login'));
554
    if (!$page->has('css', $this->getDrupalSelector('login_form_selector'))) {
555
      return TRUE;
556
    }
557
558
    $session->visit($this->locatePath('/'));
559
560
    // As a last resort, if a logout link is found, we are logged in. While not
561
    // perfect, this is how Drupal SimpleTests currently work as well.
562
    return $page->findLink($this->getDrupalText('log_out'));
563
  }
564
565
  /**
566
   * User with a given role is already logged in.
567
   *
568
   * @param string $role
569
   *   A single role, or multiple comma-separated roles in a single string.
570
   *
571
   * @return boolean
572
   *   Returns TRUE if the current logged in user has this role (or roles).
573
   */
574
  public function loggedInWithRole($role) {
575
    return $this->loggedIn() && $this->user && isset($this->user->role) && $this->user->role == $role;
0 ignored issues
show
Documentation introduced by
The property user does not exist on object<Drupal\DrupalExte...ntext\RawDrupalContext>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
576
  }
577
578
}
579