3 namespace Drupal\DrupalExtension\Context;
5 use Behat\Behat\Context\TranslatableContext;
6 use Behat\Mink\Exception\UnsupportedDriverActionException;
7 use Behat\MinkExtension\Context\MinkContext as MinkExtension;
10 * Extensions to the Mink Extension.
12 class MinkContext extends MinkExtension implements TranslatableContext {
15 * Returns list of definition translation resources paths.
19 public static function getTranslationResources() {
20 return self::getMinkTranslationResources() + glob(__DIR__ . '/../../../../i18n/*.xliff');
24 * Return a region from the current page.
27 * If region cannot be found.
29 * @param string $region
30 * The machine name of the region to return.
32 * @return \Behat\Mink\Element\NodeElement
34 public function getRegion($region) {
35 $session = $this->getSession();
36 $regionObj = $session->getPage()->find('region', $region);
38 throw new \Exception(sprintf('No region "%s" found on the page %s.', $region, $session->getCurrentUrl()));
45 * Visit a given path, and additionally check for HTTP response code 200.
47 * @Given I am at :path
50 * @throws UnsupportedDriverActionException
52 public function assertAtPath($path) {
53 $this->getSession()->visit($this->locatePath($path));
55 // If available, add extra validation that this is a 200 response.
57 $this->getSession()->getStatusCode();
58 $this->assertHttpResponse('200');
60 catch (UnsupportedDriverActionException $e) {
61 // Simply continue on, as this driver doesn't support HTTP response codes.
68 public function assertClick($link) {
69 // Use the Mink Extenstion step definition.
70 $this->clickLink($link);
74 * @Given for :field I enter :value
75 * @Given I enter :value for :field
77 public function assertEnterField($field, $value) {
78 // Use the Mink Extenstion step definition.
79 $this->fillField($field, $value);
83 * For javascript enabled scenarios, always wait for AJAX before clicking.
87 public function beforeJavascriptStep($event) {
88 /** @var \Behat\Behat\Hook\Scope\BeforeStepScope $event */
89 $tags = $event->getFeature()->getTags();
90 if (!in_array('javascript', $tags)) {
93 $text = $event->getStep()->getText();
94 if (preg_match('/(follow|press|click|submit)/i', $text)) {
95 $this->iWaitForAjaxToFinish();
100 * For javascript enabled scenarios, always wait for AJAX after clicking.
104 public function afterJavascriptStep($event) {
105 /** @var \Behat\Behat\Hook\Scope\BeforeStepScope $event */
106 $tags = $event->getFeature()->getTags();
107 if (!in_array('javascript', $tags)) {
110 $text = $event->getStep()->getText();
111 if (preg_match('/(follow|press|click|submit)/i', $text)) {
112 $this->iWaitForAjaxToFinish();
117 * Wait for AJAX to finish.
119 * @see \Drupal\FunctionalJavascriptTests\JSWebAssert::assertWaitOnAjaxRequest()
121 * @Given I wait for AJAX to finish
123 public function iWaitForAjaxToFinish() {
126 function isAjaxing(instance) {
127 return instance && instance.ajaxing === true;
130 // Assert no AJAX request is running (via jQuery or Drupal) and no
131 // animation is running.
132 (typeof jQuery === 'undefined' || (jQuery.active === 0 && jQuery(':animated').length === 0)) &&
133 (typeof Drupal === 'undefined' || typeof Drupal.ajax === 'undefined' || !Drupal.ajax.instances.some(isAjaxing))
137 $result = $this->getSession()->wait(5000, $condition);
139 throw new \RuntimeException('Unable to complete AJAX request.');
143 * Presses button with specified id|name|title|alt|value.
145 * @When I press the :button button
147 public function pressButton($button) {
148 // Wait for any open autocomplete boxes to finish closing. They block
149 // form-submission if they are still open.
150 // Use a step 'I press the "Esc" key in the "LABEL" field' to close
151 // autocomplete suggestion boxes with Mink. "Click" events on the
152 // autocomplete suggestion do not work.
154 $this->getSession()->wait(1000, 'typeof(jQuery)=="undefined" || jQuery("#autocomplete").length === 0');
156 catch (UnsupportedDriverActionException $e) {
157 // The jQuery probably failed because the driver does not support
158 // javascript. That is okay, because if the driver does not support
159 // javascript, it does not support autocomplete boxes either.
162 // Use the Mink Extension step definition.
163 return parent::pressButton($button);
167 * @Given I press the :char key in the :field field
169 * @param mixed $char could be either char ('b') or char-code (98)
172 public function pressKey($char, $field) {
173 static $keys = array(
197 if (is_string($char)) {
198 if (strlen($char) < 1) {
199 throw new \Exception('FeatureContext->keyPress($char, $field) was invoked but the $char parameter was empty.');
201 elseif (strlen($char) > 1) {
202 // Support for all variations, e.g. ESC, Esc, page up, pageup.
203 $char = $keys[strtolower(str_replace(' ', '', $char))];
207 $element = $this->getSession()->getPage()->findField($field);
209 throw new \Exception("Field '$field' not found");
212 $driver = $this->getSession()->getDriver();
213 // $driver->keyPress($element->getXpath(), $char);
214 // This alternative to Driver->keyPress() handles cases that depend on
215 // javascript which binds to key down/up events directly, such as Drupal's
217 $driver->keyDown($element->getXpath(), $char);
218 $driver->keyUp($element->getXpath(), $char);
222 * @Then I should see the link :link
224 public function assertLinkVisible($link) {
225 $element = $this->getSession()->getPage();
226 $result = $element->findLink($link);
229 if ($result && !$result->isVisible()) {
230 throw new \Exception(sprintf("No link to '%s' on the page %s", $link, $this->getSession()->getCurrentUrl()));
233 catch (UnsupportedDriverActionException $e) {
234 // We catch the UnsupportedDriverActionException exception in case
235 // this step is not being performed by a driver that supports javascript.
236 // All other exceptions are valid.
239 if (empty($result)) {
240 throw new \Exception(sprintf("No link to '%s' on the page %s", $link, $this->getSession()->getCurrentUrl()));
245 * Links are not loaded on the page.
247 * @Then I should not see the link :link
249 public function assertNotLinkVisible($link) {
250 $element = $this->getSession()->getPage();
251 $result = $element->findLink($link);
254 if ($result && $result->isVisible()) {
255 throw new \Exception(sprintf("The link '%s' was present on the page %s and was not supposed to be", $link, $this->getSession()->getCurrentUrl()));
258 catch (UnsupportedDriverActionException $e) {
259 // We catch the UnsupportedDriverActionException exception in case
260 // this step is not being performed by a driver that supports javascript.
261 // All other exceptions are valid.
265 throw new \Exception(sprintf("The link '%s' was present on the page %s and was not supposed to be", $link, $this->getSession()->getCurrentUrl()));
270 * Links are loaded but not visually visible (e.g they have display: hidden applied).
272 * @Then I should not visibly see the link :link
274 public function assertNotLinkVisuallyVisible($link) {
275 $element = $this->getSession()->getPage();
276 $result = $element->findLink($link);
279 if ($result && $result->isVisible()) {
280 throw new \Exception(sprintf("The link '%s' was visually visible on the page %s and was not supposed to be", $link, $this->getSession()->getCurrentUrl()));
283 catch (UnsupportedDriverActionException $e) {
284 // We catch the UnsupportedDriverActionException exception in case
285 // this step is not being performed by a driver that supports javascript.
286 // All other exceptions are valid.
290 throw new \Exception(sprintf("The link '%s' was not loaded on the page %s at all", $link, $this->getSession()->getCurrentUrl()));
296 * @Then I (should )see the heading :heading
298 public function assertHeading($heading) {
299 $element = $this->getSession()->getPage();
300 foreach (array('h1', 'h2', 'h3', 'h4', 'h5', 'h6') as $tag) {
301 $results = $element->findAll('css', $tag);
302 foreach ($results as $result) {
303 if ($result->getText() == $heading) {
308 throw new \Exception(sprintf("The text '%s' was not found in any heading on the page %s", $heading, $this->getSession()->getCurrentUrl()));
312 * @Then I (should )not see the heading :heading
314 public function assertNotHeading($heading) {
315 $element = $this->getSession()->getPage();
316 foreach (array('h1', 'h2', 'h3', 'h4', 'h5', 'h6') as $tag) {
317 $results = $element->findAll('css', $tag);
318 foreach ($results as $result) {
319 if ($result->getText() == $heading) {
320 throw new \Exception(sprintf("The text '%s' was found in a heading on the page %s", $heading, $this->getSession()->getCurrentUrl()));
327 * @Then I (should ) see the button :button
328 * @Then I (should ) see the :button button
330 public function assertButton($button) {
331 $element = $this->getSession()->getPage();
332 $buttonObj = $element->findButton($button);
333 if (empty($buttonObj)) {
334 throw new \Exception(sprintf("The button '%s' was not found on the page %s", $button, $this->getSession()->getCurrentUrl()));
339 * @Then I should not see the button :button
340 * @Then I should not see the :button button
342 public function assertNotButton($button) {
343 $element = $this->getSession()->getPage();
344 $buttonObj = $element->findButton($button);
345 if (!empty($buttonObj)) {
346 throw new \Exception(sprintf("The button '%s' was found on the page %s", $button, $this->getSession()->getCurrentUrl()));
351 * @When I follow/click :link in the :region( region)
354 * If region or link within it cannot be found.
356 public function assertRegionLinkFollow($link, $region) {
357 $regionObj = $this->getRegion($region);
359 // Find the link within the region
360 $linkObj = $regionObj->findLink($link);
361 if (empty($linkObj)) {
362 throw new \Exception(sprintf('The link "%s" was not found in the region "%s" on the page %s', $link, $region, $this->getSession()->getCurrentUrl()));
368 * Checks, if a button with id|name|title|alt|value exists or not and pressess the same
370 * @Given I press :button in the :region( region)
373 * string The id|name|title|alt|value of the button to be pressed
375 * string The region in which the button should be pressed
378 * If region or button within it cannot be found.
380 public function assertRegionPressButton($button, $region) {
381 $regionObj = $this->getRegion($region);
383 $buttonObj = $regionObj->findButton($button);
384 if (empty($buttonObj)) {
385 throw new \Exception(sprintf("The button '%s' was not found in the region '%s' on the page %s", $button, $region, $this->getSession()->getCurrentUrl()));
387 $regionObj->pressButton($button);
391 * Fills in a form field with id|name|title|alt|value in the specified region.
393 * @Given I fill in :value for :field in the :region( region)
394 * @Given I fill in :field with :value in the :region( region)
397 * If region cannot be found.
399 public function regionFillField($field, $value, $region) {
400 $field = $this->fixStepArgument($field);
401 $value = $this->fixStepArgument($value);
402 $regionObj = $this->getRegion($region);
403 $regionObj->fillField($field, $value);
407 * Find a heading in a specific region.
409 * @Then I should see the heading :heading in the :region( region)
410 * @Then I should see the :heading heading in the :region( region)
413 * If region or header within it cannot be found.
415 public function assertRegionHeading($heading, $region) {
416 $regionObj = $this->getRegion($region);
418 foreach (array('h1', 'h2', 'h3', 'h4', 'h5', 'h6') as $tag) {
419 $elements = $regionObj->findAll('css', $tag);
420 if (!empty($elements)) {
421 foreach ($elements as $element) {
422 if (trim($element->getText()) === $heading) {
429 throw new \Exception(sprintf('The heading "%s" was not found in the "%s" region on the page %s', $heading, $region, $this->getSession()->getCurrentUrl()));
433 * @Then I should see the link :link in the :region( region)
436 * If region or link within it cannot be found.
438 public function assertLinkRegion($link, $region) {
439 $regionObj = $this->getRegion($region);
441 $result = $regionObj->findLink($link);
442 if (empty($result)) {
443 throw new \Exception(sprintf('No link to "%s" in the "%s" region on the page %s', $link, $region, $this->getSession()->getCurrentUrl()));
448 * @Then I should not see the link :link in the :region( region)
451 * If region or link within it cannot be found.
453 public function assertNotLinkRegion($link, $region) {
454 $regionObj = $this->getRegion($region);
456 $result = $regionObj->findLink($link);
457 if (!empty($result)) {
458 throw new \Exception(sprintf('Link to "%s" in the "%s" region on the page %s', $link, $region, $this->getSession()->getCurrentUrl()));
463 * @Then I should see( the text) :text in the :region( region)
466 * If region or text within it cannot be found.
468 public function assertRegionText($text, $region) {
469 $regionObj = $this->getRegion($region);
471 // Find the text within the region
472 $regionText = $regionObj->getText();
473 if (strpos($regionText, $text) === FALSE) {
474 throw new \Exception(sprintf("The text '%s' was not found in the region '%s' on the page %s", $text, $region, $this->getSession()->getCurrentUrl()));
479 * @Then I should not see( the text) :text in the :region( region)
482 * If region or text within it cannot be found.
484 public function assertNotRegionText($text, $region) {
485 $regionObj = $this->getRegion($region);
487 // Find the text within the region.
488 $regionText = $regionObj->getText();
489 if (strpos($regionText, $text) !== FALSE) {
490 throw new \Exception(sprintf('The text "%s" was found in the region "%s" on the page %s', $text, $region, $this->getSession()->getCurrentUrl()));
495 * @Then I (should )see the text :text
497 public function assertTextVisible($text) {
498 // Use the Mink Extension step definition.
499 $this->assertPageContainsText($text);
503 * @Then I should not see the text :text
505 public function assertNotTextVisible($text) {
506 // Use the Mink Extension step definition.
507 $this->assertPageNotContainsText($text);
511 * @Then I should get a :code HTTP response
513 public function assertHttpResponse($code) {
514 // Use the Mink Extension step definition.
515 $this->assertResponseStatus($code);
519 * @Then I should not get a :code HTTP response
521 public function assertNotHttpResponse($code) {
522 // Use the Mink Extension step definition.
523 $this->assertResponseStatusIsNot($code);
527 * @Given I check the box :checkbox
529 public function assertCheckBox($checkbox) {
530 // Use the Mink Extension step definition.
531 $this->checkOption($checkbox);
535 * @Given I uncheck the box :checkbox
537 public function assertUncheckBox($checkbox) {
538 // Use the Mink Extension step definition.
539 $this->uncheckOption($checkbox);
543 * @When I select the radio button :label with the id :id
544 * @When I select the radio button :label
546 * @TODO convert to mink extension.
548 public function assertSelectRadioById($label, $id = '') {
549 $element = $this->getSession()->getPage();
550 $radiobutton = $id ? $element->findById($id) : $element->find('named', array('radio', $this->getSession()->getSelectorsHandler()->xpathLiteral($label)));
551 if ($radiobutton === NULL) {
552 throw new \Exception(sprintf('The radio button with "%s" was not found on the page %s', $id ? $id : $label, $this->getSession()->getCurrentUrl()));
554 $value = $radiobutton->getAttribute('value');
555 $labelonpage = $radiobutton->getParent()->getText();
556 if ($label != $labelonpage) {
557 throw new \Exception(sprintf("Button with id '%s' has label '%s' instead of '%s' on the page %s", $id, $labelonpage, $label, $this->getSession()->getCurrentUrl()));
559 $radiobutton->selectOption($value, FALSE);
563 * @} End of defgroup "mink extensions"