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