PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Behat/Mink/WebAssert.php

http://github.com/Behat/Mink
PHP | 748 lines | 326 code | 97 blank | 325 comment | 12 complexity | 8dbbcff3f45c3307235d4e92769d413c MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Mink package.
  4. * (c) Konstantin Kudryashov <ever.zet@gmail.com>
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. namespace Behat\Mink;
  10. use Behat\Mink\Element\Element;
  11. use Behat\Mink\Element\ElementInterface;
  12. use Behat\Mink\Element\NodeElement;
  13. use Behat\Mink\Element\TraversableElement;
  14. use Behat\Mink\Exception\ElementNotFoundException;
  15. use Behat\Mink\Exception\ExpectationException;
  16. use Behat\Mink\Exception\ResponseTextException;
  17. use Behat\Mink\Exception\ElementHtmlException;
  18. use Behat\Mink\Exception\ElementTextException;
  19. /**
  20. * Mink web assertions tool.
  21. *
  22. * @author Konstantin Kudryashov <ever.zet@gmail.com>
  23. */
  24. class WebAssert
  25. {
  26. protected $session;
  27. /**
  28. * Initializes assertion engine.
  29. *
  30. * @param Session $session
  31. */
  32. public function __construct(Session $session)
  33. {
  34. $this->session = $session;
  35. }
  36. /**
  37. * Checks that current session address is equals to provided one.
  38. *
  39. * @param string $page
  40. *
  41. * @throws ExpectationException
  42. */
  43. public function addressEquals($page)
  44. {
  45. $expected = $this->cleanUrl($page);
  46. $actual = $this->getCurrentUrlPath();
  47. $this->assert($actual === $expected, sprintf('Current page is "%s", but "%s" expected.', $actual, $expected));
  48. }
  49. /**
  50. * Checks that current session address is not equals to provided one.
  51. *
  52. * @param string $page
  53. *
  54. * @throws ExpectationException
  55. */
  56. public function addressNotEquals($page)
  57. {
  58. $expected = $this->cleanUrl($page);
  59. $actual = $this->getCurrentUrlPath();
  60. $this->assert($actual !== $expected, sprintf('Current page is "%s", but should not be.', $actual));
  61. }
  62. /**
  63. * Checks that current session address matches regex.
  64. *
  65. * @param string $regex
  66. *
  67. * @throws ExpectationException
  68. */
  69. public function addressMatches($regex)
  70. {
  71. $actual = $this->getCurrentUrlPath();
  72. $message = sprintf('Current page "%s" does not match the regex "%s".', $actual, $regex);
  73. $this->assert((bool) preg_match($regex, $actual), $message);
  74. }
  75. /**
  76. * Checks that specified cookie exists and its value equals to a given one
  77. *
  78. * @param string $name cookie name
  79. * @param string $value cookie value
  80. *
  81. * @throws ExpectationException
  82. */
  83. public function cookieEquals($name, $value)
  84. {
  85. $this->cookieExists($name);
  86. $actualValue = $this->session->getCookie($name);
  87. $message = sprintf('Cookie "%s" value is "%s", but should be "%s".', $name, $actualValue, $value);
  88. $this->assert($actualValue == $value, $message);
  89. }
  90. /**
  91. * Checks that specified cookie exists
  92. *
  93. * @param string $name cookie name
  94. *
  95. * @throws ExpectationException
  96. */
  97. public function cookieExists($name)
  98. {
  99. $message = sprintf('Cookie "%s" is not set, but should be.', $name);
  100. $this->assert($this->session->getCookie($name) !== null, $message);
  101. }
  102. /**
  103. * Checks that current response code equals to provided one.
  104. *
  105. * @param integer $code
  106. *
  107. * @throws ExpectationException
  108. */
  109. public function statusCodeEquals($code)
  110. {
  111. $actual = $this->session->getStatusCode();
  112. $message = sprintf('Current response status code is %d, but %d expected.', $actual, $code);
  113. $this->assert(intval($code) === intval($actual), $message);
  114. }
  115. /**
  116. * Checks that current response code not equals to provided one.
  117. *
  118. * @param integer $code
  119. *
  120. * @throws ExpectationException
  121. */
  122. public function statusCodeNotEquals($code)
  123. {
  124. $actual = $this->session->getStatusCode();
  125. $message = sprintf('Current response status code is %d, but should not be.', $actual);
  126. $this->assert(intval($code) !== intval($actual), $message);
  127. }
  128. /**
  129. * Checks that current page contains text.
  130. *
  131. * @param string $text
  132. *
  133. * @throws ResponseTextException
  134. */
  135. public function pageTextContains($text)
  136. {
  137. $actual = $this->session->getPage()->getText();
  138. $actual = preg_replace('/\s+/u', ' ', $actual);
  139. $regex = '/'.preg_quote($text, '/').'/ui';
  140. $message = sprintf('The text "%s" was not found anywhere in the text of the current page.', $text);
  141. $this->assertResponseText((bool) preg_match($regex, $actual), $message);
  142. }
  143. /**
  144. * Checks that current page does not contains text.
  145. *
  146. * @param string $text
  147. *
  148. * @throws ResponseTextException
  149. */
  150. public function pageTextNotContains($text)
  151. {
  152. $actual = $this->session->getPage()->getText();
  153. $actual = preg_replace('/\s+/u', ' ', $actual);
  154. $regex = '/'.preg_quote($text, '/').'/ui';
  155. $message = sprintf('The text "%s" appears in the text of this page, but it should not.', $text);
  156. $this->assertResponseText(!preg_match($regex, $actual), $message);
  157. }
  158. /**
  159. * Checks that current page text matches regex.
  160. *
  161. * @param string $regex
  162. *
  163. * @throws ResponseTextException
  164. */
  165. public function pageTextMatches($regex)
  166. {
  167. $actual = $this->session->getPage()->getText();
  168. $message = sprintf('The pattern %s was not found anywhere in the text of the current page.', $regex);
  169. $this->assertResponseText((bool) preg_match($regex, $actual), $message);
  170. }
  171. /**
  172. * Checks that current page text does not matches regex.
  173. *
  174. * @param string $regex
  175. *
  176. * @throws ResponseTextException
  177. */
  178. public function pageTextNotMatches($regex)
  179. {
  180. $actual = $this->session->getPage()->getText();
  181. $message = sprintf('The pattern %s was found in the text of the current page, but it should not.', $regex);
  182. $this->assertResponseText(!preg_match($regex, $actual), $message);
  183. }
  184. /**
  185. * Checks that page HTML (response content) contains text.
  186. *
  187. * @param string $text
  188. *
  189. * @throws ExpectationException
  190. */
  191. public function responseContains($text)
  192. {
  193. $actual = $this->session->getPage()->getContent();
  194. $regex = '/'.preg_quote($text, '/').'/ui';
  195. $message = sprintf('The string "%s" was not found anywhere in the HTML response of the current page.', $text);
  196. $this->assert((bool) preg_match($regex, $actual), $message);
  197. }
  198. /**
  199. * Checks that page HTML (response content) does not contains text.
  200. *
  201. * @param string $text
  202. *
  203. * @throws ExpectationException
  204. */
  205. public function responseNotContains($text)
  206. {
  207. $actual = $this->session->getPage()->getContent();
  208. $regex = '/'.preg_quote($text, '/').'/ui';
  209. $message = sprintf('The string "%s" appears in the HTML response of this page, but it should not.', $text);
  210. $this->assert(!preg_match($regex, $actual), $message);
  211. }
  212. /**
  213. * Checks that page HTML (response content) matches regex.
  214. *
  215. * @param string $regex
  216. *
  217. * @throws ExpectationException
  218. */
  219. public function responseMatches($regex)
  220. {
  221. $actual = $this->session->getPage()->getContent();
  222. $message = sprintf('The pattern %s was not found anywhere in the HTML response of the page.', $regex);
  223. $this->assert((bool) preg_match($regex, $actual), $message);
  224. }
  225. /**
  226. * Checks that page HTML (response content) does not matches regex.
  227. *
  228. * @param $regex
  229. *
  230. * @throws ExpectationException
  231. */
  232. public function responseNotMatches($regex)
  233. {
  234. $actual = $this->session->getPage()->getContent();
  235. $message = sprintf('The pattern %s was found in the HTML response of the page, but it should not.', $regex);
  236. $this->assert(!preg_match($regex, $actual), $message);
  237. }
  238. /**
  239. * Checks that there is specified number of specific elements on the page.
  240. *
  241. * @param string $selectorType element selector type (css, xpath)
  242. * @param string|array $selector element selector
  243. * @param integer $count expected count
  244. * @param ElementInterface $container document to check against
  245. *
  246. * @throws ExpectationException
  247. */
  248. public function elementsCount($selectorType, $selector, $count, ElementInterface $container = null)
  249. {
  250. $container = $container ?: $this->session->getPage();
  251. $nodes = $container->findAll($selectorType, $selector);
  252. $message = sprintf(
  253. '%d %s found on the page, but should be %d.',
  254. count($nodes),
  255. $this->getMatchingElementRepresentation($selectorType, $selector, count($nodes) !== 1),
  256. $count
  257. );
  258. $this->assert(intval($count) === count($nodes), $message);
  259. }
  260. /**
  261. * Checks that specific element exists on the current page.
  262. *
  263. * @param string $selectorType element selector type (css, xpath)
  264. * @param string|array $selector element selector
  265. * @param ElementInterface $container document to check against
  266. *
  267. * @return NodeElement
  268. *
  269. * @throws ElementNotFoundException
  270. */
  271. public function elementExists($selectorType, $selector, ElementInterface $container = null)
  272. {
  273. $container = $container ?: $this->session->getPage();
  274. $node = $container->find($selectorType, $selector);
  275. if (null === $node) {
  276. if (is_array($selector)) {
  277. $selector = implode(' ', $selector);
  278. }
  279. throw new ElementNotFoundException($this->session, 'element', $selectorType, $selector);
  280. }
  281. return $node;
  282. }
  283. /**
  284. * Checks that specific element does not exists on the current page.
  285. *
  286. * @param string $selectorType element selector type (css, xpath)
  287. * @param string|array $selector element selector
  288. * @param ElementInterface $container document to check against
  289. *
  290. * @throws ExpectationException
  291. */
  292. public function elementNotExists($selectorType, $selector, ElementInterface $container = null)
  293. {
  294. $container = $container ?: $this->session->getPage();
  295. $node = $container->find($selectorType, $selector);
  296. $message = sprintf(
  297. 'An %s appears on this page, but it should not.',
  298. $this->getMatchingElementRepresentation($selectorType, $selector)
  299. );
  300. $this->assert(null === $node, $message);
  301. }
  302. /**
  303. * Checks that specific element contains text.
  304. *
  305. * @param string $selectorType element selector type (css, xpath)
  306. * @param string|array $selector element selector
  307. * @param string $text expected text
  308. *
  309. * @throws ElementTextException
  310. */
  311. public function elementTextContains($selectorType, $selector, $text)
  312. {
  313. $element = $this->elementExists($selectorType, $selector);
  314. $actual = $element->getText();
  315. $regex = '/'.preg_quote($text, '/').'/ui';
  316. $message = sprintf(
  317. 'The text "%s" was not found in the text of the %s.',
  318. $text,
  319. $this->getMatchingElementRepresentation($selectorType, $selector)
  320. );
  321. $this->assertElementText((bool) preg_match($regex, $actual), $message, $element);
  322. }
  323. /**
  324. * Checks that specific element does not contains text.
  325. *
  326. * @param string $selectorType element selector type (css, xpath)
  327. * @param string|array $selector element selector
  328. * @param string $text expected text
  329. *
  330. * @throws ElementTextException
  331. */
  332. public function elementTextNotContains($selectorType, $selector, $text)
  333. {
  334. $element = $this->elementExists($selectorType, $selector);
  335. $actual = $element->getText();
  336. $regex = '/'.preg_quote($text, '/').'/ui';
  337. $message = sprintf(
  338. 'The text "%s" appears in the text of the %s, but it should not.',
  339. $text,
  340. $this->getMatchingElementRepresentation($selectorType, $selector)
  341. );
  342. $this->assertElementText(!preg_match($regex, $actual), $message, $element);
  343. }
  344. /**
  345. * Checks that specific element contains HTML.
  346. *
  347. * @param string $selectorType element selector type (css, xpath)
  348. * @param string|array $selector element selector
  349. * @param string $html expected text
  350. *
  351. * @throws ElementHtmlException
  352. */
  353. public function elementContains($selectorType, $selector, $html)
  354. {
  355. $element = $this->elementExists($selectorType, $selector);
  356. $actual = $element->getHtml();
  357. $regex = '/'.preg_quote($html, '/').'/ui';
  358. $message = sprintf(
  359. 'The string "%s" was not found in the HTML of the %s.',
  360. $html,
  361. $this->getMatchingElementRepresentation($selectorType, $selector)
  362. );
  363. $this->assertElement((bool) preg_match($regex, $actual), $message, $element);
  364. }
  365. /**
  366. * Checks that specific element does not contains HTML.
  367. *
  368. * @param string $selectorType element selector type (css, xpath)
  369. * @param string|array $selector element selector
  370. * @param string $html expected text
  371. *
  372. * @throws ElementHtmlException
  373. */
  374. public function elementNotContains($selectorType, $selector, $html)
  375. {
  376. $element = $this->elementExists($selectorType, $selector);
  377. $actual = $element->getHtml();
  378. $regex = '/'.preg_quote($html, '/').'/ui';
  379. $message = sprintf(
  380. 'The string "%s" appears in the HTML of the %s, but it should not.',
  381. $html,
  382. $this->getMatchingElementRepresentation($selectorType, $selector)
  383. );
  384. $this->assertElement(!preg_match($regex, $actual), $message, $element);
  385. }
  386. /**
  387. * Checks that an attribute exists in an element.
  388. *
  389. * @param string $selectorType
  390. * @param string|array $selector
  391. * @param string $attribute
  392. *
  393. * @return NodeElement
  394. *
  395. * @throws ElementHtmlException
  396. */
  397. public function elementAttributeExists($selectorType, $selector, $attribute)
  398. {
  399. $element = $this->elementExists($selectorType, $selector);
  400. $message = sprintf(
  401. 'The attribute "%s" was not found in the %s.',
  402. $attribute,
  403. $this->getMatchingElementRepresentation($selectorType, $selector)
  404. );
  405. $this->assertElement($element->hasAttribute($attribute), $message, $element);
  406. return $element;
  407. }
  408. /**
  409. * Checks that an attribute of a specific elements contains text.
  410. *
  411. * @param string $selectorType
  412. * @param string|array $selector
  413. * @param string $attribute
  414. * @param string $text
  415. *
  416. * @throws ElementHtmlException
  417. */
  418. public function elementAttributeContains($selectorType, $selector, $attribute, $text)
  419. {
  420. $element = $this->elementAttributeExists($selectorType, $selector, $attribute);
  421. $actual = $element->getAttribute($attribute);
  422. $regex = '/'.preg_quote($text, '/').'/ui';
  423. $message = sprintf(
  424. 'The text "%s" was not found in the attribute "%s" of the %s.',
  425. $text,
  426. $attribute,
  427. $this->getMatchingElementRepresentation($selectorType, $selector)
  428. );
  429. $this->assertElement((bool) preg_match($regex, $actual), $message, $element);
  430. }
  431. /**
  432. * Checks that an attribute of a specific elements does not contain text.
  433. *
  434. * @param string $selectorType
  435. * @param string|array $selector
  436. * @param string $attribute
  437. * @param string $text
  438. *
  439. * @throws ElementHtmlException
  440. */
  441. public function elementAttributeNotContains($selectorType, $selector, $attribute, $text)
  442. {
  443. $element = $this->elementAttributeExists($selectorType, $selector, $attribute);
  444. $actual = $element->getAttribute($attribute);
  445. $regex = '/'.preg_quote($text, '/').'/ui';
  446. $message = sprintf(
  447. 'The text "%s" was found in the attribute "%s" of the %s.',
  448. $text,
  449. $attribute,
  450. $this->getMatchingElementRepresentation($selectorType, $selector)
  451. );
  452. $this->assertElement(!preg_match($regex, $actual), $message, $element);
  453. }
  454. /**
  455. * Checks that specific field exists on the current page.
  456. *
  457. * @param string $field field id|name|label|value
  458. * @param TraversableElement $container document to check against
  459. *
  460. * @return NodeElement
  461. *
  462. * @throws ElementNotFoundException
  463. */
  464. public function fieldExists($field, TraversableElement $container = null)
  465. {
  466. $container = $container ?: $this->session->getPage();
  467. $node = $container->findField($field);
  468. if (null === $node) {
  469. throw new ElementNotFoundException($this->session, 'form field', 'id|name|label|value', $field);
  470. }
  471. return $node;
  472. }
  473. /**
  474. * Checks that specific field does not exists on the current page.
  475. *
  476. * @param string $field field id|name|label|value
  477. * @param TraversableElement $container document to check against
  478. *
  479. * @throws ExpectationException
  480. */
  481. public function fieldNotExists($field, TraversableElement $container = null)
  482. {
  483. $container = $container ?: $this->session->getPage();
  484. $node = $container->findField($field);
  485. $this->assert(null === $node, sprintf('A field "%s" appears on this page, but it should not.', $field));
  486. }
  487. /**
  488. * Checks that specific field have provided value.
  489. *
  490. * @param string $field field id|name|label|value
  491. * @param string $value field value
  492. * @param TraversableElement $container document to check against
  493. *
  494. * @throws ExpectationException
  495. */
  496. public function fieldValueEquals($field, $value, TraversableElement $container = null)
  497. {
  498. $node = $this->fieldExists($field, $container);
  499. $actual = $node->getValue();
  500. $regex = '/^'.preg_quote($value, '/').'$/ui';
  501. $message = sprintf('The field "%s" value is "%s", but "%s" expected.', $field, $actual, $value);
  502. $this->assert((bool) preg_match($regex, $actual), $message);
  503. }
  504. /**
  505. * Checks that specific field have provided value.
  506. *
  507. * @param string $field field id|name|label|value
  508. * @param string $value field value
  509. * @param TraversableElement $container document to check against
  510. *
  511. * @throws ExpectationException
  512. */
  513. public function fieldValueNotEquals($field, $value, TraversableElement $container = null)
  514. {
  515. $node = $this->fieldExists($field, $container);
  516. $actual = $node->getValue();
  517. $regex = '/^'.preg_quote($value, '/').'$/ui';
  518. $message = sprintf('The field "%s" value is "%s", but it should not be.', $field, $actual);
  519. $this->assert(!preg_match($regex, $actual), $message);
  520. }
  521. /**
  522. * Checks that specific checkbox is checked.
  523. *
  524. * @param string $field field id|name|label|value
  525. * @param TraversableElement $container document to check against
  526. *
  527. * @throws ExpectationException
  528. */
  529. public function checkboxChecked($field, TraversableElement $container = null)
  530. {
  531. $node = $this->fieldExists($field, $container);
  532. $this->assert($node->isChecked(), sprintf('Checkbox "%s" is not checked, but it should be.', $field));
  533. }
  534. /**
  535. * Checks that specific checkbox is unchecked.
  536. *
  537. * @param string $field field id|name|label|value
  538. * @param TraversableElement $container document to check against
  539. *
  540. * @throws ExpectationException
  541. */
  542. public function checkboxNotChecked($field, TraversableElement $container = null)
  543. {
  544. $node = $this->fieldExists($field, $container);
  545. $this->assert(!$node->isChecked(), sprintf('Checkbox "%s" is checked, but it should not be.', $field));
  546. }
  547. /**
  548. * Gets current url of the page.
  549. *
  550. * @return string
  551. */
  552. protected function getCurrentUrlPath()
  553. {
  554. return $this->cleanUrl($this->session->getCurrentUrl());
  555. }
  556. /**
  557. * Trims scriptname from the URL.
  558. *
  559. * @param string $url
  560. *
  561. * @return string
  562. */
  563. protected function cleanUrl($url)
  564. {
  565. $parts = parse_url($url);
  566. $fragment = empty($parts['fragment']) ? '' : '#' . $parts['fragment'];
  567. return preg_replace('/^\/[^\.\/]+\.php/', '', $parts['path']) . $fragment;
  568. }
  569. /**
  570. * Asserts a condition.
  571. *
  572. * @param bool $condition
  573. * @param string $message Failure message
  574. *
  575. * @throws ExpectationException when the condition is not fulfilled
  576. */
  577. private function assert($condition, $message)
  578. {
  579. if ($condition) {
  580. return;
  581. }
  582. throw new ExpectationException($message, $this->session);
  583. }
  584. /**
  585. * Asserts a condition involving the response text.
  586. *
  587. * @param bool $condition
  588. * @param string $message Failure message
  589. *
  590. * @throws ResponseTextException when the condition is not fulfilled
  591. */
  592. private function assertResponseText($condition, $message)
  593. {
  594. if ($condition) {
  595. return;
  596. }
  597. throw new ResponseTextException($message, $this->session);
  598. }
  599. /**
  600. * Asserts a condition on an element.
  601. *
  602. * @param bool $condition
  603. * @param string $message Failure message
  604. * @param Element $element
  605. *
  606. * @throws ElementHtmlException when the condition is not fulfilled
  607. */
  608. private function assertElement($condition, $message, Element $element)
  609. {
  610. if ($condition) {
  611. return;
  612. }
  613. throw new ElementHtmlException($message, $this->session, $element);
  614. }
  615. /**
  616. * Asserts a condition involving the text of an element.
  617. *
  618. * @param bool $condition
  619. * @param string $message Failure message
  620. * @param Element $element
  621. *
  622. * @throws ElementTextException when the condition is not fulfilled
  623. */
  624. private function assertElementText($condition, $message, Element $element)
  625. {
  626. if ($condition) {
  627. return;
  628. }
  629. throw new ElementTextException($message, $this->session, $element);
  630. }
  631. /**
  632. * @param string $selectorType
  633. * @param string|array $selector
  634. * @param boolean $plural
  635. *
  636. * @return string
  637. */
  638. private function getMatchingElementRepresentation($selectorType, $selector, $plural = false)
  639. {
  640. $pluralization = $plural ? 's' : '';
  641. if (in_array($selectorType, array('named', 'named_exact', 'named_partial'))
  642. && is_array($selector) && 2 === count($selector)
  643. ) {
  644. return sprintf('%s%s matching locator "%s"', $selector[0], $pluralization, $selector[1]);
  645. }
  646. if (is_array($selector)) {
  647. $selector = implode(' ', $selector);
  648. }
  649. return sprintf('element%s matching %s "%s"', $pluralization, $selectorType, $selector);
  650. }
  651. }