Completed
Push — master ( 2eb923...4fd9ab )
by Jonathan
13s
created

src/Drupal/DrupalExtension/Context/MinkContext.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Drupal\DrupalExtension\Context;
4
5
use Behat\Behat\Context\TranslatableContext;
6
use Behat\Mink\Exception\UnsupportedDriverActionException;
7
use Behat\MinkExtension\Context\MinkContext as MinkExtension;
8
9
/**
10
 * Extensions to the Mink Extension.
11
 */
12
class MinkContext extends MinkExtension implements TranslatableContext
13
{
14
15
  /**
16
   * Returns list of definition translation resources paths.
17
   *
18
   * @return array
19
   */
20
    public static function getTranslationResources()
21
    {
22
        return self::getMinkTranslationResources() + glob(__DIR__ . '/../../../../i18n/*.xliff');
23
    }
24
25
  /**
26
   * Return a region from the current page.
27
   *
28
   * @throws \Exception
29
   *   If region cannot be found.
30
   *
31
   * @param string $region
32
   *   The machine name of the region to return.
33
   *
34
   * @return \Behat\Mink\Element\NodeElement
35
   */
36 View Code Duplication
    public function getRegion($region)
0 ignored issues
show
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...
37
    {
38
        $session = $this->getSession();
39
        $regionObj = $session->getPage()->find('region', $region);
40
        if (!$regionObj) {
41
            throw new \Exception(sprintf('No region "%s" found on the page %s.', $region, $session->getCurrentUrl()));
42
        }
43
44
        return $regionObj;
45
    }
46
47
  /**
48
   * Visit a given path, and additionally check for HTTP response code 200.
49
   *
50
   * @Given I am at :path
51
   * @When I visit :path
52
   *
53
   * @throws UnsupportedDriverActionException
54
   */
55
    public function assertAtPath($path)
56
    {
57
        $this->getSession()->visit($this->locatePath($path));
58
59
        // If available, add extra validation that this is a 200 response.
60
        try {
61
            $this->getSession()->getStatusCode();
62
            $this->assertHttpResponse('200');
63
        } catch (UnsupportedDriverActionException $e) {
64
            // Simply continue on, as this driver doesn't support HTTP response codes.
65
        }
66
    }
67
68
  /**
69
   * @When I click :link
70
   */
71
    public function assertClick($link)
72
    {
73
        // Use the Mink Extenstion step definition.
74
        $this->clickLink($link);
75
    }
76
77
  /**
78
   * @Given for :field I enter :value
79
   * @Given I enter :value for :field
80
   */
81
    public function assertEnterField($field, $value)
82
    {
83
        // Use the Mink Extenstion step definition.
84
        $this->fillField($field, $value);
85
    }
86
87
  /**
88
   * For javascript enabled scenarios, always wait for AJAX before clicking.
89
   *
90
   * @BeforeStep
91
   */
92 View Code Duplication
    public function beforeJavascriptStep($event)
93
    {
94
        /** @var \Behat\Behat\Hook\Scope\BeforeStepScope $event */
95
        $tags = $event->getFeature()->getTags();
96
        if (!in_array('javascript', $tags)) {
97
            return;
98
        }
99
        $text = $event->getStep()->getText();
100
        if (preg_match('/(follow|press|click|submit)/i', $text)) {
101
            $this->iWaitForAjaxToFinish();
102
        }
103
    }
104
105
  /**
106
   * For javascript enabled scenarios, always wait for AJAX after clicking.
107
   *
108
   * @AfterStep
109
   */
110 View Code Duplication
    public function afterJavascriptStep($event)
111
    {
112
        /** @var \Behat\Behat\Hook\Scope\BeforeStepScope $event */
113
        $tags = $event->getFeature()->getTags();
114
        if (!in_array('javascript', $tags)) {
115
            return;
116
        }
117
        $text = $event->getStep()->getText();
118
        if (preg_match('/(follow|press|click|submit)/i', $text)) {
119
            $this->iWaitForAjaxToFinish();
120
        }
121
    }
122
123
  /**
124
   * Wait for AJAX to finish.
125
   *
126
   * @see \Drupal\FunctionalJavascriptTests\JSWebAssert::assertWaitOnAjaxRequest()
127
   *
128
   * @Given I wait for AJAX to finish
129
   */
130
    public function iWaitForAjaxToFinish()
131
    {
132
        $condition = <<<JS
133
    (function() {
134
      function isAjaxing(instance) {
135
        return instance && instance.ajaxing === true;
136
      }
137
      var d7_not_ajaxing = true;
138
      if (typeof Drupal !== 'undefined' && typeof Drupal.ajax !== 'undefined' && typeof Drupal.ajax.instances === 'undefined') {
139
        for(var i in Drupal.ajax) { if (isAjaxing(Drupal.ajax[i])) { d7_not_ajaxing = false; } }
140
      }
141
      var d8_not_ajaxing = (typeof Drupal === 'undefined' || typeof Drupal.ajax === 'undefined' || typeof Drupal.ajax.instances === 'undefined' || !Drupal.ajax.instances.some(isAjaxing))
142
      return (
143
        // Assert no AJAX request is running (via jQuery or Drupal) and no
144
        // animation is running.
145
        (typeof jQuery === 'undefined' || (jQuery.active === 0 && jQuery(':animated').length === 0)) &&
146
        d7_not_ajaxing && d8_not_ajaxing
147
      );
148
    }());
149
JS;
150
        $result = $this->getSession()->wait(5000, $condition);
151
        if (!$result) {
152
            throw new \RuntimeException('Unable to complete AJAX request.');
153
        }
154
    }
155
  /**
156
   * Presses button with specified id|name|title|alt|value.
157
   *
158
   * @When I press the :button button
159
   */
160
    public function pressButton($button)
161
    {
162
        // Wait for any open autocomplete boxes to finish closing.  They block
163
        // form-submission if they are still open.
164
        // Use a step 'I press the "Esc" key in the "LABEL" field' to close
165
        // autocomplete suggestion boxes with Mink.  "Click" events on the
166
        // autocomplete suggestion do not work.
167
        try {
168
            $this->getSession()->wait(1000, 'typeof(jQuery)=="undefined" || jQuery("#autocomplete").length === 0');
169
        } catch (UnsupportedDriverActionException $e) {
170
            // The jQuery probably failed because the driver does not support
171
            // javascript.  That is okay, because if the driver does not support
172
            // javascript, it does not support autocomplete boxes either.
173
        }
174
175
        // Use the Mink Extension step definition.
176
        return parent::pressButton($button);
177
    }
178
179
  /**
180
   * @Given I press the :char key in the :field field
181
   *
182
   * @param mixed $char could be either char ('b') or char-code (98)
183
   * @throws \Exception
184
   */
185
    public function pressKey($char, $field)
186
    {
187
        static $keys = array(
188
        'backspace' => 8,
189
        'tab' => 9,
190
        'enter' => 13,
191
        'shift' => 16,
192
        'ctrl' =>  17,
193
        'alt' => 18,
194
        'pause' => 19,
195
        'break' => 19,
196
        'escape' =>  27,
197
        'esc' =>  27,
198
        'end' => 35,
199
        'home' =>  36,
200
        'left' => 37,
201
        'up' => 38,
202
        'right' =>39,
203
        'down' => 40,
204
        'insert' =>  45,
205
        'delete' =>  46,
206
        'pageup' => 33,
207
        'pagedown' => 34,
208
        'capslock' => 20,
209
        );
210
211
        if (is_string($char)) {
212
            if (strlen($char) < 1) {
213
                throw new \Exception('FeatureContext->keyPress($char, $field) was invoked but the $char parameter was empty.');
214
            } elseif (strlen($char) > 1) {
215
                // Support for all variations, e.g. ESC, Esc, page up, pageup.
216
                $char = $keys[strtolower(str_replace(' ', '', $char))];
217
            }
218
        }
219
220
        $element = $this->getSession()->getPage()->findField($field);
221
        if (!$element) {
222
            throw new \Exception("Field '$field' not found");
223
        }
224
225
        $driver = $this->getSession()->getDriver();
226
        // $driver->keyPress($element->getXpath(), $char);
227
        // This alternative to Driver->keyPress() handles cases that depend on
228
        // javascript which binds to key down/up events directly, such as Drupal's
229
        // autocomplete.js.
230
        $driver->keyDown($element->getXpath(), $char);
231
        $driver->keyUp($element->getXpath(), $char);
232
    }
233
234
  /**
235
   * @Then I should see the link :link
236
   */
237 View Code Duplication
    public function assertLinkVisible($link)
238
    {
239
        $element = $this->getSession()->getPage();
240
        $result = $element->findLink($link);
241
242
        try {
243
            if ($result && !$result->isVisible()) {
244
                throw new \Exception(sprintf("No link to '%s' on the page %s", $link, $this->getSession()->getCurrentUrl()));
245
            }
246
        } catch (UnsupportedDriverActionException $e) {
247
            // We catch the UnsupportedDriverActionException exception in case
248
            // this step is not being performed by a driver that supports javascript.
249
            // All other exceptions are valid.
250
        }
251
252
        if (empty($result)) {
253
            throw new \Exception(sprintf("No link to '%s' on the page %s", $link, $this->getSession()->getCurrentUrl()));
254
        }
255
    }
256
257
  /**
258
   * Links are not loaded on the page.
259
   *
260
   * @Then I should not see the link :link
261
   */
262 View Code Duplication
    public function assertNotLinkVisible($link)
263
    {
264
        $element = $this->getSession()->getPage();
265
        $result = $element->findLink($link);
266
267
        try {
268
            if ($result && $result->isVisible()) {
269
                throw new \Exception(sprintf("The link '%s' was present on the page %s and was not supposed to be", $link, $this->getSession()->getCurrentUrl()));
270
            }
271
        } catch (UnsupportedDriverActionException $e) {
272
            // We catch the UnsupportedDriverActionException exception in case
273
            // this step is not being performed by a driver that supports javascript.
274
            // All other exceptions are valid.
275
        }
276
277
        if ($result) {
278
            throw new \Exception(sprintf("The link '%s' was present on the page %s and was not supposed to be", $link, $this->getSession()->getCurrentUrl()));
279
        }
280
    }
281
282
  /**
283
   * Links are loaded but not visually visible (e.g they have display: hidden applied).
284
   *
285
   * @Then I should not visibly see the link :link
286
   */
287 View Code Duplication
    public function assertNotLinkVisuallyVisible($link)
288
    {
289
        $element = $this->getSession()->getPage();
290
        $result = $element->findLink($link);
291
292
        try {
293
            if ($result && $result->isVisible()) {
294
                throw new \Exception(sprintf("The link '%s' was visually visible on the page %s and was not supposed to be", $link, $this->getSession()->getCurrentUrl()));
295
            }
296
        } catch (UnsupportedDriverActionException $e) {
297
            // We catch the UnsupportedDriverActionException exception in case
298
            // this step is not being performed by a driver that supports javascript.
299
            // All other exceptions are valid.
300
        }
301
302
        if (!$result) {
303
            throw new \Exception(sprintf("The link '%s' was not loaded on the page %s at all", $link, $this->getSession()->getCurrentUrl()));
304
        }
305
    }
306
307
  /**
308
   * @Then I (should )see the heading :heading
309
   */
310 View Code Duplication
    public function assertHeading($heading)
311
    {
312
        $element = $this->getSession()->getPage();
313
        foreach (array('h1', 'h2', 'h3', 'h4', 'h5', 'h6') as $tag) {
314
            $results = $element->findAll('css', $tag);
315
            foreach ($results as $result) {
316
                if ($result->getText() == $heading) {
317
                    return;
318
                }
319
            }
320
        }
321
        throw new \Exception(sprintf("The text '%s' was not found in any heading on the page %s", $heading, $this->getSession()->getCurrentUrl()));
322
    }
323
324
  /**
325
   * @Then I (should )not see the heading :heading
326
   */
327 View Code Duplication
    public function assertNotHeading($heading)
328
    {
329
        $element = $this->getSession()->getPage();
330
        foreach (array('h1', 'h2', 'h3', 'h4', 'h5', 'h6') as $tag) {
331
            $results = $element->findAll('css', $tag);
332
            foreach ($results as $result) {
333
                if ($result->getText() == $heading) {
334
                    throw new \Exception(sprintf("The text '%s' was found in a heading on the page %s", $heading, $this->getSession()->getCurrentUrl()));
335
                }
336
            }
337
        }
338
    }
339
340
  /**
341
   * @Then I (should ) see the button :button
342
   * @Then I (should ) see the :button button
343
   */
344 View Code Duplication
    public function assertButton($button)
345
    {
346
        $element = $this->getSession()->getPage();
347
        $buttonObj = $element->findButton($button);
348
        if (empty($buttonObj)) {
349
            throw new \Exception(sprintf("The button '%s' was not found on the page %s", $button, $this->getSession()->getCurrentUrl()));
350
        }
351
    }
352
353
  /**
354
   * @Then I should not see the button :button
355
   * @Then I should not see the :button button
356
   */
357 View Code Duplication
    public function assertNotButton($button)
358
    {
359
        $element = $this->getSession()->getPage();
360
        $buttonObj = $element->findButton($button);
361
        if (!empty($buttonObj)) {
362
            throw new \Exception(sprintf("The button '%s' was found on the page %s", $button, $this->getSession()->getCurrentUrl()));
363
        }
364
    }
365
366
  /**
367
   * @When I follow/click :link in the :region( region)
368
   *
369
   * @throws \Exception
370
   *   If region or link within it cannot be found.
371
   */
372 View Code Duplication
    public function assertRegionLinkFollow($link, $region)
373
    {
374
        $regionObj = $this->getRegion($region);
375
376
        // Find the link within the region
377
        $linkObj = $regionObj->findLink($link);
378
        if (empty($linkObj)) {
379
            throw new \Exception(sprintf('The link "%s" was not found in the region "%s" on the page %s', $link, $region, $this->getSession()->getCurrentUrl()));
380
        }
381
        $linkObj->click();
382
    }
383
384
  /**
385
   * Checks, if a button with id|name|title|alt|value exists or not and pressess the same
386
   *
387
   * @Given I press :button in the :region( region)
388
   *
389
   * @param $button
390
   *   string The id|name|title|alt|value of the button to be pressed
391
   * @param $region
392
   *   string The region in which the button should be pressed
393
   *
394
   * @throws \Exception
395
   *   If region or button within it cannot be found.
396
   */
397 View Code Duplication
    public function assertRegionPressButton($button, $region)
398
    {
399
        $regionObj = $this->getRegion($region);
400
401
        $buttonObj = $regionObj->findButton($button);
402
        if (empty($buttonObj)) {
403
            throw new \Exception(sprintf("The button '%s' was not found in the region '%s' on the page %s", $button, $region, $this->getSession()->getCurrentUrl()));
404
        }
405
        $regionObj->pressButton($button);
406
    }
407
408
  /**
409
   * Fills in a form field with id|name|title|alt|value in the specified region.
410
   *
411
   * @Given I fill in :value for :field in the :region( region)
412
   * @Given I fill in :field with :value in the :region( region)
413
   *
414
   * @throws \Exception
415
   *   If region cannot be found.
416
   */
417
    public function regionFillField($field, $value, $region)
418
    {
419
        $field = $this->fixStepArgument($field);
420
        $value = $this->fixStepArgument($value);
421
        $regionObj = $this->getRegion($region);
422
        $regionObj->fillField($field, $value);
423
    }
424
425
  /**
426
   * Find a heading in a specific region.
427
   *
428
   * @Then I should see the heading :heading in the :region( region)
429
   * @Then I should see the :heading heading in the :region( region)
430
   *
431
   * @throws \Exception
432
   *   If region or header within it cannot be found.
433
   */
434
    public function assertRegionHeading($heading, $region)
435
    {
436
        $regionObj = $this->getRegion($region);
437
438
        foreach (array('h1', 'h2', 'h3', 'h4', 'h5', 'h6') as $tag) {
439
            $elements = $regionObj->findAll('css', $tag);
440
            if (!empty($elements)) {
441
                foreach ($elements as $element) {
442
                    if (trim($element->getText()) === $heading) {
443
                        return;
444
                    }
445
                }
446
            }
447
        }
448
449
        throw new \Exception(sprintf('The heading "%s" was not found in the "%s" region on the page %s', $heading, $region, $this->getSession()->getCurrentUrl()));
450
    }
451
452
  /**
453
   * @Then I should see the link :link in the :region( region)
454
   *
455
   * @throws \Exception
456
   *   If region or link within it cannot be found.
457
   */
458 View Code Duplication
    public function assertLinkRegion($link, $region)
459
    {
460
        $regionObj = $this->getRegion($region);
461
462
        $result = $regionObj->findLink($link);
463
        if (empty($result)) {
464
            throw new \Exception(sprintf('No link to "%s" in the "%s" region on the page %s', $link, $region, $this->getSession()->getCurrentUrl()));
465
        }
466
    }
467
468
  /**
469
   * @Then I should not see the link :link in the :region( region)
470
   *
471
   * @throws \Exception
472
   *   If region or link within it cannot be found.
473
   */
474 View Code Duplication
    public function assertNotLinkRegion($link, $region)
475
    {
476
        $regionObj = $this->getRegion($region);
477
478
        $result = $regionObj->findLink($link);
479
        if (!empty($result)) {
480
            throw new \Exception(sprintf('Link to "%s" in the "%s" region on the page %s', $link, $region, $this->getSession()->getCurrentUrl()));
481
        }
482
    }
483
484
  /**
485
   * @Then I should see( the text) :text in the :region( region)
486
   *
487
   * @throws \Exception
488
   *   If region or text within it cannot be found.
489
   */
490 View Code Duplication
    public function assertRegionText($text, $region)
491
    {
492
        $regionObj = $this->getRegion($region);
493
494
        // Find the text within the region
495
        $regionText = $regionObj->getText();
496
        if (strpos($regionText, $text) === false) {
497
            throw new \Exception(sprintf("The text '%s' was not found in the region '%s' on the page %s", $text, $region, $this->getSession()->getCurrentUrl()));
498
        }
499
    }
500
501
  /**
502
   * @Then I should not see( the text) :text in the :region( region)
503
   *
504
   * @throws \Exception
505
   *   If region or text within it cannot be found.
506
   */
507 View Code Duplication
    public function assertNotRegionText($text, $region)
508
    {
509
        $regionObj = $this->getRegion($region);
510
511
        // Find the text within the region.
512
        $regionText = $regionObj->getText();
513
        if (strpos($regionText, $text) !== false) {
514
            throw new \Exception(sprintf('The text "%s" was found in the region "%s" on the page %s', $text, $region, $this->getSession()->getCurrentUrl()));
515
        }
516
    }
517
518
  /**
519
   * @Then I (should )see the text :text
520
   */
521
    public function assertTextVisible($text)
522
    {
523
        // Use the Mink Extension step definition.
524
        $this->assertPageContainsText($text);
525
    }
526
527
  /**
528
   * @Then I should not see the text :text
529
   */
530
    public function assertNotTextVisible($text)
531
    {
532
        // Use the Mink Extension step definition.
533
        $this->assertPageNotContainsText($text);
534
    }
535
536
  /**
537
   * @Then I should get a :code HTTP response
538
   */
539
    public function assertHttpResponse($code)
540
    {
541
        // Use the Mink Extension step definition.
542
        $this->assertResponseStatus($code);
543
    }
544
545
  /**
546
   * @Then I should not get a :code HTTP response
547
   */
548
    public function assertNotHttpResponse($code)
549
    {
550
        // Use the Mink Extension step definition.
551
        $this->assertResponseStatusIsNot($code);
552
    }
553
554
  /**
555
   * @Given I check the box :checkbox
556
   */
557
    public function assertCheckBox($checkbox)
558
    {
559
        // Use the Mink Extension step definition.
560
        $this->checkOption($checkbox);
561
    }
562
563
  /**
564
   * @Given I uncheck the box :checkbox
565
   */
566
    public function assertUncheckBox($checkbox)
567
    {
568
        // Use the Mink Extension step definition.
569
        $this->uncheckOption($checkbox);
570
    }
571
572
  /**
573
   * @When I select the radio button :label with the id :id
574
   * @When I select the radio button :label
575
   *
576
   * @TODO convert to mink extension.
577
   */
578
    public function assertSelectRadioById($label, $id = '')
579
    {
580
        $element = $this->getSession()->getPage();
581
        $radiobutton = $id ? $element->findById($id) : $element->find('named', array('radio', $this->getSession()->getSelectorsHandler()->xpathLiteral($label)));
582
        if ($radiobutton === null) {
583
            throw new \Exception(sprintf('The radio button with "%s" was not found on the page %s', $id ? $id : $label, $this->getSession()->getCurrentUrl()));
584
        }
585
        $value = $radiobutton->getAttribute('value');
586
        $radio_id = $radiobutton->getAttribute('id');
587
        $labelonpage = $element->find('css', "label[for='$radio_id']")->getText();
588
        if ($label != $labelonpage) {
589
            throw new \Exception(sprintf("Button with id '%s' has label '%s' instead of '%s' on the page %s", $id, $labelonpage, $label, $this->getSession()->getCurrentUrl()));
590
        }
591
        $radiobutton->selectOption($value, false);
592
    }
593
594
  /**
595
   * @} End of defgroup "mink extensions"
596
   */
597
}
598