Completed
Push — master ( 9cf213...22c274 )
by Jonathan
18s
created

RawDrupalContext::cleanUsers()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 17
rs 8.8571
cc 5
eloc 10
nc 7
nop 0
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
use Behat\Behat\Context\Environment\InitializedContextEnvironment;
9
10
use Drupal\DrupalDriverManager;
11
use Drupal\DrupalExtension\DrupalParametersTrait;
12
use Drupal\DrupalExtension\Manager\DrupalAuthenticationManagerInterface;
13
use Drupal\DrupalExtension\Manager\DrupalUserManagerInterface;
14
15
use Drupal\DrupalExtension\Hook\Scope\AfterLanguageEnableScope;
16
use Drupal\DrupalExtension\Hook\Scope\AfterNodeCreateScope;
17
use Drupal\DrupalExtension\Hook\Scope\AfterTermCreateScope;
18
use Drupal\DrupalExtension\Hook\Scope\AfterUserCreateScope;
19
use Drupal\DrupalExtension\Hook\Scope\BaseEntityScope;
20
use Drupal\DrupalExtension\Hook\Scope\BeforeLanguageEnableScope;
21
use Drupal\DrupalExtension\Hook\Scope\BeforeNodeCreateScope;
22
use Drupal\DrupalExtension\Hook\Scope\BeforeUserCreateScope;
23
use Drupal\DrupalExtension\Hook\Scope\BeforeTermCreateScope;
24
use Drupal\DrupalExtension\Manager\FastLogoutInterface;
25
26
/**
27
 * Provides the raw functionality for interacting with Drupal.
28
 */
29
class RawDrupalContext extends RawMinkContext implements DrupalAwareInterface
30
{
31
32
    use DrupalParametersTrait;
33
34
  /**
35
   * Drupal driver manager.
36
   *
37
   * @var \Drupal\DrupalDriverManager
38
   */
39
    private $drupal;
40
41
  /**
42
   * Event dispatcher object.
43
   *
44
   * @var \Behat\Testwork\Hook\HookDispatcher
45
   */
46
    protected $dispatcher;
47
48
  /**
49
   * Drupal authentication manager.
50
   *
51
   * @var \Drupal\DrupalExtension\Manager\DrupalAuthenticationManagerInterface
52
   */
53
    protected $authenticationManager;
54
55
  /**
56
   * Drupal user manager.
57
   *
58
   * @var \Drupal\DrupalExtension\Manager\DrupalUserManagerInterface
59
   */
60
    protected $userManager;
61
62
  /**
63
   * Keep track of nodes so they can be cleaned up.
64
   *
65
   * @var array
66
   */
67
    protected $nodes = array();
68
69
  /**
70
   * Keep track of all terms that are created so they can easily be removed.
71
   *
72
   * @var array
73
   */
74
    protected $terms = array();
75
76
  /**
77
   * Keep track of any roles that are created so they can easily be removed.
78
   *
79
   * @var array
80
   */
81
    protected $roles = array();
82
83
  /**
84
   * Keep track of any languages that are created so they can easily be removed.
85
   *
86
   * @var array
87
   */
88
    protected $languages = array();
89
90
  /**
91
   * {@inheritDoc}
92
   */
93
    public function setDrupal(DrupalDriverManager $drupal)
94
    {
95
        $this->drupal = $drupal;
96
    }
97
98
  /**
99
   * {@inheritDoc}
100
   */
101
    public function getDrupal()
102
    {
103
        return $this->drupal;
104
    }
105
106
  /**
107
   * {@inheritDoc}
108
   */
109
    public function setUserManager(DrupalUserManagerInterface $userManager)
110
    {
111
        $this->userManager = $userManager;
112
    }
113
114
  /**
115
   * {@inheritdoc}
116
   */
117
    public function getUserManager()
118
    {
119
        return $this->userManager;
120
    }
121
122
  /**
123
   * {@inheritdoc}
124
   */
125
    public function setAuthenticationManager(DrupalAuthenticationManagerInterface $authenticationManager)
126
    {
127
        $this->authenticationManager = $authenticationManager;
128
    }
129
130
  /**
131
   * {@inheritdoc}
132
   */
133
    public function getAuthenticationManager()
134
    {
135
        return $this->authenticationManager;
136
    }
137
138
  /**
139
   * Magic setter.
140
   */
141
    public function __set($name, $value)
142
    {
143
        switch ($name) {
144
            case 'user':
145
                trigger_error('Interacting directly with the RawDrupalContext::$user property has been deprecated. Use RawDrupalContext::getUserManager->setCurrentUser() instead.', E_USER_DEPRECATED);
146
                // Set the user on the user manager service, so it is shared between all
147
                // contexts.
148
                $this->getUserManager()->setCurrentUser($value);
149
                break;
150
151
            case 'users':
152
                trigger_error('Interacting directly with the RawDrupalContext::$users property has been deprecated. Use RawDrupalContext::getUserManager->addUser() instead.', E_USER_DEPRECATED);
153
                // Set the user on the user manager service, so it is shared between all
154
                // contexts.
155
                if (empty($value)) {
156
                    $this->getUserManager()->clearUsers();
157
                } else {
158
                    foreach ($value as $user) {
159
                        $this->getUserManager()->addUser($user);
160
                    }
161
                }
162
                break;
163
        }
164
    }
165
166
  /**
167
   * Magic getter.
168
   */
169
    public function __get($name)
170
    {
171
        switch ($name) {
172
            case 'user':
173
                trigger_error('Interacting directly with the RawDrupalContext::$user property has been deprecated. Use RawDrupalContext::getUserManager->getCurrentUser() instead.', E_USER_DEPRECATED);
174
                // Returns the current user from the user manager service. This is shared
175
                // between all contexts.
176
                return $this->getUserManager()->getCurrentUser();
177
178
            case 'users':
179
                trigger_error('Interacting directly with the RawDrupalContext::$users property has been deprecated. Use RawDrupalContext::getUserManager->getUsers() instead.', E_USER_DEPRECATED);
180
                // Returns the current user from the user manager service. This is shared
181
                // between all contexts.
182
                return $this->getUserManager()->getUsers();
183
        }
184
    }
185
186
  /**
187
   * {@inheritdoc}
188
   */
189
    public function setDispatcher(HookDispatcher $dispatcher)
190
    {
191
        $this->dispatcher = $dispatcher;
192
    }
193
194
  /**
195
   * Get active Drupal Driver.
196
   *
197
   * @return \Drupal\Driver\DrupalDriver
198
   */
199
    public function getDriver($name = null)
200
    {
201
        return $this->getDrupal()->getDriver($name);
202
    }
203
204
  /**
205
   * Get driver's random generator.
206
   */
207
    public function getRandom()
208
    {
209
        return $this->getDriver()->getRandom();
210
    }
211
212
  /**
213
   * Massage node values to match the expectations on different Drupal versions.
214
   *
215
   * @beforeNodeCreate
216
   */
217
    public static function alterNodeParameters(BeforeNodeCreateScope $scope)
218
    {
219
        $node = $scope->getEntity();
220
221
        // Get the Drupal API version if available. This is not available when
222
        // using e.g. the BlackBoxDriver or DrushDriver.
223
        $api_version = null;
224
        $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...
225
        if ($driver instanceof \Drupal\Driver\DrupalDriver) {
226
            $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...
227
        }
228
229
        // On Drupal 8 the timestamps should be in UNIX time.
230
        switch ($api_version) {
231
            case 8:
232
                foreach (array('changed', 'created', 'revision_timestamp') as $field) {
233
                    if (!empty($node->$field) && !is_numeric($node->$field)) {
234
                        $node->$field = strtotime($node->$field);
235
                    }
236
                }
237
                break;
238
        }
239
    }
240
241
  /**
242
   * Remove any created nodes.
243
   *
244
   * @AfterScenario
245
   */
246
    public function cleanNodes()
247
    {
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
    {
262
        // Remove any users that were created.
263
        if ($this->userManager->hasUsers()) {
264
            foreach ($this->userManager->getUsers() as $user) {
265
                $this->getDriver()->userDelete($user);
266
            }
267
            $this->getDriver()->processBatch();
268
            $this->userManager->clearUsers();
269
            // If the authentication manager supports logout, no need to check if the user is logged in.
270
            if ($this->getAuthenticationManager() instanceof FastLogoutInterface) {
271
                $this->logout(true);
272
            } elseif ($this->loggedIn()) {
273
                $this->logout();
274
            }
275
        }
276
    }
277
278
  /**
279
   * Remove any created terms.
280
   *
281
   * @AfterScenario
282
   */
283
    public function cleanTerms()
284
    {
285
        // Remove any terms that were created.
286
        foreach ($this->terms as $term) {
287
            $this->getDriver()->termDelete($term);
288
        }
289
        $this->terms = array();
290
    }
291
292
  /**
293
   * Remove any created roles.
294
   *
295
   * @AfterScenario
296
   */
297
    public function cleanRoles()
298
    {
299
        // Remove any roles that were created.
300
        foreach ($this->roles as $rid) {
301
            $this->getDriver()->roleDelete($rid);
302
        }
303
        $this->roles = array();
304
    }
305
306
  /**
307
   * Remove any created languages.
308
   *
309
   * @AfterScenario
310
   */
311
    public function cleanLanguages()
312
    {
313
        // Delete any languages that were created.
314
        foreach ($this->languages as $language) {
315
            $this->getDriver()->languageDelete($language);
316
            unset($this->languages[$language->langcode]);
317
        }
318
    }
319
320
  /**
321
   * Clear static caches.
322
   *
323
   * @AfterScenario @api
324
   */
325
    public function clearStaticCaches()
326
    {
327
        $this->getDriver()->clearStaticCaches();
328
    }
329
330
  /**
331
   * Dispatch scope hooks.
332
   *
333
   * @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...
334
   *   The entity scope to dispatch.
335
   * @param \stdClass $entity
336
   *   The entity.
337
   */
338
    protected function dispatchHooks($scopeType, \stdClass $entity)
339
    {
340
        $fullScopeClass = 'Drupal\\DrupalExtension\\Hook\\Scope\\' . $scopeType;
341
        $scope = new $fullScopeClass($this->getDrupal()->getEnvironment(), $this, $entity);
342
        $callResults = $this->dispatcher->dispatchScopeHooks($scope);
343
344
        // The dispatcher suppresses exceptions, throw them here if there are any.
345
        foreach ($callResults as $result) {
346
            if ($result->hasException()) {
347
                $exception = $result->getException();
348
                throw $exception;
349
            }
350
        }
351
    }
352
353
  /**
354
   * Create a node.
355
   *
356
   * @return object
357
   *   The created node.
358
   */
359 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...
360
    {
361
        $this->dispatchHooks('BeforeNodeCreateScope', $node);
362
        $this->parseEntityFields('node', $node);
363
        $saved = $this->getDriver()->createNode($node);
364
        $this->dispatchHooks('AfterNodeCreateScope', $saved);
365
        $this->nodes[] = $saved;
366
        return $saved;
367
    }
368
369
  /**
370
   * Parses the field values and turns them into the format expected by Drupal.
371
   *
372
   * Multiple values in a single field must be separated by commas. Wrap the
373
   * field value in double quotes in case it should contain a comma.
374
   *
375
   * Compound field properties are identified using a ':' operator, either in
376
   * the column heading or in the cell. If multiple properties are present in a
377
   * single cell, they must be separated using ' - ', and values should not
378
   * contain ':' or ' - '.
379
   *
380
   * Possible formats for the values:
381
   *   A
382
   *   A, B, "a value, containing a comma"
383
   *   A - B
384
   *   x: A - y: B
385
   *   A - B, C - D, "E - F"
386
   *   x: A - y: B,  x: C - y: D,  "x: E - y: F"
387
   *
388
   * See field_handlers.feature for examples of usage.
389
   *
390
   * @param string $entity_type
391
   *   The entity type.
392
   * @param \stdClass $entity
393
   *   An object containing the entity properties and fields as properties.
394
   *
395
   * @throws \Exception
396
   *   Thrown when a field name is invalid.
397
   */
398
    public function parseEntityFields($entity_type, \stdClass $entity)
399
    {
400
        $multicolumn_field = '';
401
        $multicolumn_fields = array();
402
403
        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...
404
            // Reset the multicolumn field if the field name does not contain a column.
405
            if (strpos($field, ':') === false) {
406
                $multicolumn_field = '';
407
            } // Start tracking a new multicolumn field if the field name contains a ':'
408
            // which is preceded by at least 1 character.
409
            elseif (strpos($field, ':', 1) !== false) {
410
                list($multicolumn_field, $multicolumn_column) = explode(':', $field);
411
            } // If a field name starts with a ':' but we are not yet tracking a
412
            // multicolumn field we don't know to which field this belongs.
413
            elseif (empty($multicolumn_field)) {
414
                throw new \Exception('Field name missing for ' . $field);
415
            } // Update the column name if the field name starts with a ':' and we are
416
            // already tracking a multicolumn field.
417
            else {
418
                $multicolumn_column = substr($field, 1);
419
            }
420
421
            $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...
422
            $field_name = $multicolumn_field ?: $field;
423
            if ($this->getDriver()->isField($entity_type, $field_name)) {
424
                // Split up multiple values in multi-value fields.
425
                $values = array();
426
                foreach (str_getcsv($field_value) as $key => $value) {
427
                    $value = trim($value);
428
                    $columns = $value;
429
                    // Split up field columns if the ' - ' separator is present.
430
                    if (strstr($value, ' - ') !== false) {
431
                        $columns = array();
432
                        foreach (explode(' - ', $value) as $column) {
433
                            // Check if it is an inline named column.
434
                            if (!$is_multicolumn && strpos($column, ': ', 1) !== false) {
435
                                list ($key, $column) = explode(': ', $column);
436
                                $columns[$key] = $column;
437
                            } else {
438
                                $columns[] = $column;
439
                            }
440
                        }
441
                    }
442
                    // Use the column name if we are tracking a multicolumn field.
443
                    if ($is_multicolumn) {
444
                        $multicolumn_fields[$multicolumn_field][$key][$multicolumn_column] = $columns;
445
                        unset($entity->$field);
446
                    } else {
447
                        $values[] = $columns;
448
                    }
449
                }
450
                // Replace regular fields inline in the entity after parsing.
451
                if (!$is_multicolumn) {
452
                    $entity->$field_name = $values;
453
                    // Don't specify any value if the step author has left it blank.
454
                    if ($field_value === '') {
455
                        unset($entity->$field_name);
456
                    }
457
                }
458
            }
459
        }
460
461
        // Add the multicolumn fields to the entity.
462
        foreach ($multicolumn_fields as $field_name => $columns) {
463
            // Don't specify any value if the step author has left it blank.
464
            if (count(array_filter($columns, function ($var) {
465
                return ($var !== '');
466
            })) > 0) {
467
                $entity->$field_name = $columns;
468
            }
469
        }
470
    }
471
472
  /**
473
   * Create a user.
474
   *
475
   * @return object
476
   *   The created user.
477
   */
478
    public function userCreate($user)
479
    {
480
        $this->dispatchHooks('BeforeUserCreateScope', $user);
481
        $this->parseEntityFields('user', $user);
482
        $this->getDriver()->userCreate($user);
483
        $this->dispatchHooks('AfterUserCreateScope', $user);
484
        $this->userManager->addUser($user);
485
        return $user;
486
    }
487
488
  /**
489
   * Create a term.
490
   *
491
   * @return object
492
   *   The created term.
493
   */
494 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...
495
    {
496
        $this->dispatchHooks('BeforeTermCreateScope', $term);
497
        $this->parseEntityFields('taxonomy_term', $term);
498
        $saved = $this->getDriver()->createTerm($term);
499
        $this->dispatchHooks('AfterTermCreateScope', $saved);
500
        $this->terms[] = $saved;
501
        return $saved;
502
    }
503
504
  /**
505
   * Creates a language.
506
   *
507
   * @param \stdClass $language
508
   *   An object with the following properties:
509
   *   - langcode: the langcode of the language to create.
510
   *
511
   * @return object|FALSE
512
   *   The created language, or FALSE if the language was already created.
513
   */
514
    public function languageCreate(\stdClass $language)
515
    {
516
        $this->dispatchHooks('BeforeLanguageCreateScope', $language);
517
        $language = $this->getDriver()->languageCreate($language);
518
        if ($language) {
519
            $this->dispatchHooks('AfterLanguageCreateScope', $language);
520
            $this->languages[$language->langcode] = $language;
521
        }
522
        return $language;
523
    }
524
525
  /**
526
   * Log-in the given user.
527
   *
528
   * @param \stdClass $user
529
   *   The user to log in.
530
   */
531
    public function login(\stdClass $user)
532
    {
533
        $this->getAuthenticationManager()->logIn($user);
534
    }
535
536
  /**
537
   * Logs the current user out.
538
   *
539
   * @param bool $fast
540
   *   Utilize direct logout by session if available.
541
   */
542
    public function logout($fast = false)
543
    {
544
        if ($fast && $this->getAuthenticationManager() instanceof FastLogoutInterface) {
545
            $this->getAuthenticationManager()->fastLogout();
546
        } else {
547
            $this->getAuthenticationManager()->logOut();
548
        }
549
    }
550
551
  /**
552
   * Determine if the a user is already logged in.
553
   *
554
   * @return boolean
555
   *   Returns TRUE if a user is logged in for this session.
556
   */
557
    public function loggedIn()
558
    {
559
        return $this->getAuthenticationManager()->loggedIn();
560
    }
561
562
  /**
563
   * User with a given role is already logged in.
564
   *
565
   * @param string $role
566
   *   A single role, or multiple comma-separated roles in a single string.
567
   *
568
   * @return boolean
569
   *   Returns TRUE if the current logged in user has this role (or roles).
570
   */
571
    public function loggedInWithRole($role)
572
    {
573
        return $this->loggedIn() && $this->getUserManager()->currentUserHasRole($role);
574
    }
575
576
  /**
577
   * Returns the Behat context that corresponds with the given class name.
578
   *
579
   * This is inspired by InitializedContextEnvironment::getContext() but also
580
   * returns subclasses of the given class name. This allows us to retrieve for
581
   * example DrupalContext even if it is overridden in a project.
582
   *
583
   * @param string $class
584
   *   A fully namespaced class name.
585
   *
586
   * @return \Behat\Behat\Context\Context|false
587
   *   The requested context, or FALSE if the context is not registered.
588
   *
589
   * @throws \Exception
590
   *   Thrown when the environment is not yet initialized, meaning that contexts
591
   *   cannot yet be retrieved.
592
   */
593
    protected function getContext($class)
594
    {
595
        /** @var InitializedContextEnvironment $environment */
596
        $environment = $this->drupal->getEnvironment();
597
        // Throw an exception if the environment is not yet initialized. To make
598
        // sure state doesn't leak between test scenarios, the environment is
599
        // reinitialized at the start of every scenario. If this code is executed
600
        // before a test scenario starts (e.g. in a `@BeforeScenario` hook) then the
601
        // contexts cannot yet be retrieved.
602
        if (!$environment instanceof InitializedContextEnvironment) {
603
            throw new \Exception('Cannot retrieve contexts when the environment is not yet initialized.');
604
        }
605
        foreach ($environment->getContexts() as $context) {
606
            if ($context instanceof $class) {
607
                return $context;
608
            }
609
        }
610
611
        return false;
612
    }
613
}
614