4 * This file is part of the Mink package.
5 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
13 use Behat\Mink\Element\Element;
14 use Behat\Mink\Element\ElementInterface;
15 use Behat\Mink\Element\NodeElement;
16 use Behat\Mink\Element\TraversableElement;
17 use Behat\Mink\Exception\ElementNotFoundException;
18 use Behat\Mink\Exception\ExpectationException;
19 use Behat\Mink\Exception\ResponseTextException;
20 use Behat\Mink\Exception\ElementHtmlException;
21 use Behat\Mink\Exception\ElementTextException;
24 * Mink web assertions tool.
26 * @author Konstantin Kudryashov <ever.zet@gmail.com>
33 * Initializes assertion engine.
35 * @param Session $session
37 public function __construct(Session $session)
39 $this->session = $session;
43 * Checks that current session address is equals to provided one.
47 * @throws ExpectationException
49 public function addressEquals($page)
51 $expected = $this->cleanUrl($page);
52 $actual = $this->getCurrentUrlPath();
54 $this->assert($actual === $expected, sprintf('Current page is "%s", but "%s" expected.', $actual, $expected));
58 * Checks that current session address is not equals to provided one.
62 * @throws ExpectationException
64 public function addressNotEquals($page)
66 $expected = $this->cleanUrl($page);
67 $actual = $this->getCurrentUrlPath();
69 $this->assert($actual !== $expected, sprintf('Current page is "%s", but should not be.', $actual));
73 * Checks that current session address matches regex.
75 * @param string $regex
77 * @throws ExpectationException
79 public function addressMatches($regex)
81 $actual = $this->getCurrentUrlPath();
82 $message = sprintf('Current page "%s" does not match the regex "%s".', $actual, $regex);
84 $this->assert((bool) preg_match($regex, $actual), $message);
88 * Checks that specified cookie exists and its value equals to a given one.
90 * @param string $name cookie name
91 * @param string $value cookie value
93 * @throws ExpectationException
95 public function cookieEquals($name, $value)
97 $this->cookieExists($name);
99 $actualValue = $this->session->getCookie($name);
100 $message = sprintf('Cookie "%s" value is "%s", but should be "%s".', $name, $actualValue, $value);
102 $this->assert($actualValue == $value, $message);
106 * Checks that specified cookie exists.
108 * @param string $name cookie name
110 * @throws ExpectationException
112 public function cookieExists($name)
114 $message = sprintf('Cookie "%s" is not set, but should be.', $name);
115 $this->assert($this->session->getCookie($name) !== null, $message);
119 * Checks that current response code equals to provided one.
123 * @throws ExpectationException
125 public function statusCodeEquals($code)
127 $actual = $this->session->getStatusCode();
128 $message = sprintf('Current response status code is %d, but %d expected.', $actual, $code);
130 $this->assert(intval($code) === intval($actual), $message);
134 * Checks that current response code not equals to provided one.
138 * @throws ExpectationException
140 public function statusCodeNotEquals($code)
142 $actual = $this->session->getStatusCode();
143 $message = sprintf('Current response status code is %d, but should not be.', $actual);
145 $this->assert(intval($code) !== intval($actual), $message);
149 * Checks that current response header equals value.
151 * @param string $name
152 * @param string $value
154 * @throws ExpectationException
156 public function responseHeaderEquals($name, $value)
158 $actual = $this->session->getResponseHeader($name);
159 $message = sprintf('Current response header "%s" is "%s", but "%s" expected.', $name, $actual, $value);
161 $this->assert($value === $actual, $message);
165 * Checks that current response header does not equal value.
167 * @param string $name
168 * @param string $value
170 * @throws ExpectationException
172 public function responseHeaderNotEquals($name, $value)
174 $actual = $this->session->getResponseHeader($name);
175 $message = sprintf('Current response header "%s" is "%s", but should not be.', $name, $actual, $value);
177 $this->assert($value !== $actual, $message);
181 * Checks that current response header contains value.
183 * @param string $name
184 * @param string $value
186 * @throws ExpectationException
188 public function responseHeaderContains($name, $value)
190 $actual = $this->session->getResponseHeader($name);
191 $message = sprintf('The text "%s" was not found anywhere in the "%s" response header.', $value, $name);
193 $this->assert(false !== stripos($actual, $value), $message);
197 * Checks that current response header does not contain value.
199 * @param string $name
200 * @param string $value
202 * @throws ExpectationException
204 public function responseHeaderNotContains($name, $value)
206 $actual = $this->session->getResponseHeader($name);
207 $message = sprintf('The text "%s" was found in the "%s" response header, but it should not.', $value, $name);
209 $this->assert(false === stripos($actual, $value), $message);
213 * Checks that current response header matches regex.
215 * @param string $name
216 * @param string $regex
218 * @throws ExpectationException
220 public function responseHeaderMatches($name, $regex)
222 $actual = $this->session->getResponseHeader($name);
223 $message = sprintf('The pattern "%s" was not found anywhere in the "%s" response header.', $regex, $name);
225 $this->assert((bool) preg_match($regex, $actual), $message);
229 * Checks that current response header does not match regex.
231 * @param string $name
232 * @param string $regex
234 * @throws ExpectationException
236 public function responseHeaderNotMatches($name, $regex)
238 $actual = $this->session->getResponseHeader($name);
240 'The pattern "%s" was found in the text of the "%s" response header, but it should not.',
245 $this->assert(!preg_match($regex, $actual), $message);
249 * Checks that current page contains text.
251 * @param string $text
253 * @throws ResponseTextException
255 public function pageTextContains($text)
257 $actual = $this->session->getPage()->getText();
258 $actual = preg_replace('/\s+/u', ' ', $actual);
259 $regex = '/'.preg_quote($text, '/').'/ui';
260 $message = sprintf('The text "%s" was not found anywhere in the text of the current page.', $text);
262 $this->assertResponseText((bool) preg_match($regex, $actual), $message);
266 * Checks that current page does not contains text.
268 * @param string $text
270 * @throws ResponseTextException
272 public function pageTextNotContains($text)
274 $actual = $this->session->getPage()->getText();
275 $actual = preg_replace('/\s+/u', ' ', $actual);
276 $regex = '/'.preg_quote($text, '/').'/ui';
277 $message = sprintf('The text "%s" appears in the text of this page, but it should not.', $text);
279 $this->assertResponseText(!preg_match($regex, $actual), $message);
283 * Checks that current page text matches regex.
285 * @param string $regex
287 * @throws ResponseTextException
289 public function pageTextMatches($regex)
291 $actual = $this->session->getPage()->getText();
292 $message = sprintf('The pattern %s was not found anywhere in the text of the current page.', $regex);
294 $this->assertResponseText((bool) preg_match($regex, $actual), $message);
298 * Checks that current page text does not matches regex.
300 * @param string $regex
302 * @throws ResponseTextException
304 public function pageTextNotMatches($regex)
306 $actual = $this->session->getPage()->getText();
307 $message = sprintf('The pattern %s was found in the text of the current page, but it should not.', $regex);
309 $this->assertResponseText(!preg_match($regex, $actual), $message);
313 * Checks that page HTML (response content) contains text.
315 * @param string $text
317 * @throws ExpectationException
319 public function responseContains($text)
321 $actual = $this->session->getPage()->getContent();
322 $regex = '/'.preg_quote($text, '/').'/ui';
323 $message = sprintf('The string "%s" was not found anywhere in the HTML response of the current page.', $text);
325 $this->assert((bool) preg_match($regex, $actual), $message);
329 * Checks that page HTML (response content) does not contains text.
331 * @param string $text
333 * @throws ExpectationException
335 public function responseNotContains($text)
337 $actual = $this->session->getPage()->getContent();
338 $regex = '/'.preg_quote($text, '/').'/ui';
339 $message = sprintf('The string "%s" appears in the HTML response of this page, but it should not.', $text);
341 $this->assert(!preg_match($regex, $actual), $message);
345 * Checks that page HTML (response content) matches regex.
347 * @param string $regex
349 * @throws ExpectationException
351 public function responseMatches($regex)
353 $actual = $this->session->getPage()->getContent();
354 $message = sprintf('The pattern %s was not found anywhere in the HTML response of the page.', $regex);
356 $this->assert((bool) preg_match($regex, $actual), $message);
360 * Checks that page HTML (response content) does not matches regex.
364 * @throws ExpectationException
366 public function responseNotMatches($regex)
368 $actual = $this->session->getPage()->getContent();
369 $message = sprintf('The pattern %s was found in the HTML response of the page, but it should not.', $regex);
371 $this->assert(!preg_match($regex, $actual), $message);
375 * Checks that there is specified number of specific elements on the page.
377 * @param string $selectorType element selector type (css, xpath)
378 * @param string|array $selector element selector
379 * @param int $count expected count
380 * @param ElementInterface $container document to check against
382 * @throws ExpectationException
384 public function elementsCount($selectorType, $selector, $count, ElementInterface $container = null)
386 $container = $container ?: $this->session->getPage();
387 $nodes = $container->findAll($selectorType, $selector);
390 '%d %s found on the page, but should be %d.',
392 $this->getMatchingElementRepresentation($selectorType, $selector, count($nodes) !== 1),
396 $this->assert(intval($count) === count($nodes), $message);
400 * Checks that specific element exists on the current page.
402 * @param string $selectorType element selector type (css, xpath)
403 * @param string|array $selector element selector
404 * @param ElementInterface $container document to check against
406 * @return NodeElement
408 * @throws ElementNotFoundException
410 public function elementExists($selectorType, $selector, ElementInterface $container = null)
412 $container = $container ?: $this->session->getPage();
413 $node = $container->find($selectorType, $selector);
415 if (null === $node) {
416 if (is_array($selector)) {
417 $selector = implode(' ', $selector);
420 throw new ElementNotFoundException($this->session->getDriver(), 'element', $selectorType, $selector);
427 * Checks that specific element does not exists on the current page.
429 * @param string $selectorType element selector type (css, xpath)
430 * @param string|array $selector element selector
431 * @param ElementInterface $container document to check against
433 * @throws ExpectationException
435 public function elementNotExists($selectorType, $selector, ElementInterface $container = null)
437 $container = $container ?: $this->session->getPage();
438 $node = $container->find($selectorType, $selector);
441 'An %s appears on this page, but it should not.',
442 $this->getMatchingElementRepresentation($selectorType, $selector)
445 $this->assert(null === $node, $message);
449 * Checks that specific element contains text.
451 * @param string $selectorType element selector type (css, xpath)
452 * @param string|array $selector element selector
453 * @param string $text expected text
455 * @throws ElementTextException
457 public function elementTextContains($selectorType, $selector, $text)
459 $element = $this->elementExists($selectorType, $selector);
460 $actual = $element->getText();
461 $regex = '/'.preg_quote($text, '/').'/ui';
464 'The text "%s" was not found in the text of the %s.',
466 $this->getMatchingElementRepresentation($selectorType, $selector)
469 $this->assertElementText((bool) preg_match($regex, $actual), $message, $element);
473 * Checks that specific element does not contains text.
475 * @param string $selectorType element selector type (css, xpath)
476 * @param string|array $selector element selector
477 * @param string $text expected text
479 * @throws ElementTextException
481 public function elementTextNotContains($selectorType, $selector, $text)
483 $element = $this->elementExists($selectorType, $selector);
484 $actual = $element->getText();
485 $regex = '/'.preg_quote($text, '/').'/ui';
488 'The text "%s" appears in the text of the %s, but it should not.',
490 $this->getMatchingElementRepresentation($selectorType, $selector)
493 $this->assertElementText(!preg_match($regex, $actual), $message, $element);
497 * Checks that specific element contains HTML.
499 * @param string $selectorType element selector type (css, xpath)
500 * @param string|array $selector element selector
501 * @param string $html expected text
503 * @throws ElementHtmlException
505 public function elementContains($selectorType, $selector, $html)
507 $element = $this->elementExists($selectorType, $selector);
508 $actual = $element->getHtml();
509 $regex = '/'.preg_quote($html, '/').'/ui';
512 'The string "%s" was not found in the HTML of the %s.',
514 $this->getMatchingElementRepresentation($selectorType, $selector)
517 $this->assertElement((bool) preg_match($regex, $actual), $message, $element);
521 * Checks that specific element does not contains HTML.
523 * @param string $selectorType element selector type (css, xpath)
524 * @param string|array $selector element selector
525 * @param string $html expected text
527 * @throws ElementHtmlException
529 public function elementNotContains($selectorType, $selector, $html)
531 $element = $this->elementExists($selectorType, $selector);
532 $actual = $element->getHtml();
533 $regex = '/'.preg_quote($html, '/').'/ui';
536 'The string "%s" appears in the HTML of the %s, but it should not.',
538 $this->getMatchingElementRepresentation($selectorType, $selector)
541 $this->assertElement(!preg_match($regex, $actual), $message, $element);
545 * Checks that an attribute exists in an element.
547 * @param string $selectorType
548 * @param string|array $selector
549 * @param string $attribute
551 * @return NodeElement
553 * @throws ElementHtmlException
555 public function elementAttributeExists($selectorType, $selector, $attribute)
557 $element = $this->elementExists($selectorType, $selector);
560 'The attribute "%s" was not found in the %s.',
562 $this->getMatchingElementRepresentation($selectorType, $selector)
565 $this->assertElement($element->hasAttribute($attribute), $message, $element);
571 * Checks that an attribute of a specific elements contains text.
573 * @param string $selectorType
574 * @param string|array $selector
575 * @param string $attribute
576 * @param string $text
578 * @throws ElementHtmlException
580 public function elementAttributeContains($selectorType, $selector, $attribute, $text)
582 $element = $this->elementAttributeExists($selectorType, $selector, $attribute);
583 $actual = $element->getAttribute($attribute);
584 $regex = '/'.preg_quote($text, '/').'/ui';
587 'The text "%s" was not found in the attribute "%s" of the %s.',
590 $this->getMatchingElementRepresentation($selectorType, $selector)
593 $this->assertElement((bool) preg_match($regex, $actual), $message, $element);
597 * Checks that an attribute of a specific elements does not contain text.
599 * @param string $selectorType
600 * @param string|array $selector
601 * @param string $attribute
602 * @param string $text
604 * @throws ElementHtmlException
606 public function elementAttributeNotContains($selectorType, $selector, $attribute, $text)
608 $element = $this->elementAttributeExists($selectorType, $selector, $attribute);
609 $actual = $element->getAttribute($attribute);
610 $regex = '/'.preg_quote($text, '/').'/ui';
613 'The text "%s" was found in the attribute "%s" of the %s.',
616 $this->getMatchingElementRepresentation($selectorType, $selector)
619 $this->assertElement(!preg_match($regex, $actual), $message, $element);
623 * Checks that specific field exists on the current page.
625 * @param string $field field id|name|label|value
626 * @param TraversableElement $container document to check against
628 * @return NodeElement
630 * @throws ElementNotFoundException
632 public function fieldExists($field, TraversableElement $container = null)
634 $container = $container ?: $this->session->getPage();
635 $node = $container->findField($field);
637 if (null === $node) {
638 throw new ElementNotFoundException($this->session->getDriver(), 'form field', 'id|name|label|value', $field);
645 * Checks that specific field does not exists on the current page.
647 * @param string $field field id|name|label|value
648 * @param TraversableElement $container document to check against
650 * @throws ExpectationException
652 public function fieldNotExists($field, TraversableElement $container = null)
654 $container = $container ?: $this->session->getPage();
655 $node = $container->findField($field);
657 $this->assert(null === $node, sprintf('A field "%s" appears on this page, but it should not.', $field));
661 * Checks that specific field have provided value.
663 * @param string $field field id|name|label|value
664 * @param string $value field value
665 * @param TraversableElement $container document to check against
667 * @throws ExpectationException
669 public function fieldValueEquals($field, $value, TraversableElement $container = null)
671 $node = $this->fieldExists($field, $container);
672 $actual = $node->getValue();
673 $regex = '/^'.preg_quote($value, '/').'$/ui';
675 $message = sprintf('The field "%s" value is "%s", but "%s" expected.', $field, $actual, $value);
677 $this->assert((bool) preg_match($regex, $actual), $message);
681 * Checks that specific field have provided value.
683 * @param string $field field id|name|label|value
684 * @param string $value field value
685 * @param TraversableElement $container document to check against
687 * @throws ExpectationException
689 public function fieldValueNotEquals($field, $value, TraversableElement $container = null)
691 $node = $this->fieldExists($field, $container);
692 $actual = $node->getValue();
693 $regex = '/^'.preg_quote($value, '/').'$/ui';
695 $message = sprintf('The field "%s" value is "%s", but it should not be.', $field, $actual);
697 $this->assert(!preg_match($regex, $actual), $message);
701 * Checks that specific checkbox is checked.
703 * @param string $field field id|name|label|value
704 * @param TraversableElement $container document to check against
706 * @throws ExpectationException
708 public function checkboxChecked($field, TraversableElement $container = null)
710 $node = $this->fieldExists($field, $container);
712 $this->assert($node->isChecked(), sprintf('Checkbox "%s" is not checked, but it should be.', $field));
716 * Checks that specific checkbox is unchecked.
718 * @param string $field field id|name|label|value
719 * @param TraversableElement $container document to check against
721 * @throws ExpectationException
723 public function checkboxNotChecked($field, TraversableElement $container = null)
725 $node = $this->fieldExists($field, $container);
727 $this->assert(!$node->isChecked(), sprintf('Checkbox "%s" is checked, but it should not be.', $field));
731 * Gets current url of the page.
735 protected function getCurrentUrlPath()
737 return $this->cleanUrl($this->session->getCurrentUrl());
741 * Trims scriptname from the URL.
747 protected function cleanUrl($url)
749 $parts = parse_url($url);
750 $fragment = empty($parts['fragment']) ? '' : '#'.$parts['fragment'];
751 $path = empty($parts['path']) ? '/' : $parts['path'];
753 return preg_replace('/^\/[^\.\/]+\.php\//', '/', $path).$fragment;
757 * Asserts a condition.
759 * @param bool $condition
760 * @param string $message Failure message
762 * @throws ExpectationException when the condition is not fulfilled
764 private function assert($condition, $message)
770 throw new ExpectationException($message, $this->session->getDriver());
774 * Asserts a condition involving the response text.
776 * @param bool $condition
777 * @param string $message Failure message
779 * @throws ResponseTextException when the condition is not fulfilled
781 private function assertResponseText($condition, $message)
787 throw new ResponseTextException($message, $this->session->getDriver());
791 * Asserts a condition on an element.
793 * @param bool $condition
794 * @param string $message Failure message
795 * @param Element $element
797 * @throws ElementHtmlException when the condition is not fulfilled
799 private function assertElement($condition, $message, Element $element)
805 throw new ElementHtmlException($message, $this->session->getDriver(), $element);
809 * Asserts a condition involving the text of an element.
811 * @param bool $condition
812 * @param string $message Failure message
813 * @param Element $element
815 * @throws ElementTextException when the condition is not fulfilled
817 private function assertElementText($condition, $message, Element $element)
823 throw new ElementTextException($message, $this->session->getDriver(), $element);
827 * @param string $selectorType
828 * @param string|array $selector
829 * @param bool $plural
833 private function getMatchingElementRepresentation($selectorType, $selector, $plural = false)
835 $pluralization = $plural ? 's' : '';
837 if (in_array($selectorType, array('named', 'named_exact', 'named_partial'))
838 && is_array($selector) && 2 === count($selector)
840 return sprintf('%s%s matching locator "%s"', $selector[0], $pluralization, $selector[1]);
843 if (is_array($selector)) {
844 $selector = implode(' ', $selector);
847 return sprintf('element%s matching %s "%s"', $pluralization, $selectorType, $selector);