PageRenderTime 56ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/core/modules/simpletest/src/AssertContentTrait.php

https://gitlab.com/reasonat/test8
PHP | 1257 lines | 348 code | 60 blank | 849 comment | 52 complexity | ccccdef3d9989fa737be53ebe3e40484 MD5 | raw file
  1. <?php
  2. namespace Drupal\simpletest;
  3. use Drupal\Component\Serialization\Json;
  4. use Drupal\Component\Utility\Html;
  5. use Drupal\Component\Utility\SafeMarkup;
  6. use Drupal\Component\Utility\Xss;
  7. use Drupal\Core\Render\RenderContext;
  8. use Symfony\Component\CssSelector\CssSelectorConverter;
  9. /**
  10. * Provides test methods to assert content.
  11. */
  12. trait AssertContentTrait {
  13. /**
  14. * The current raw content.
  15. *
  16. * @var string
  17. */
  18. protected $content;
  19. /**
  20. * The plain-text content of raw $content (text nodes).
  21. *
  22. * @var string
  23. */
  24. protected $plainTextContent;
  25. /**
  26. * The drupalSettings value from the current raw $content.
  27. *
  28. * drupalSettings refers to the drupalSettings JavaScript variable.
  29. *
  30. * @var array
  31. */
  32. protected $drupalSettings;
  33. /**
  34. * The XML structure parsed from the current raw $content.
  35. *
  36. * @var \SimpleXMLElement
  37. */
  38. protected $elements;
  39. /**
  40. * Gets the current raw content.
  41. */
  42. protected function getRawContent() {
  43. return $this->content;
  44. }
  45. /**
  46. * Sets the raw content (e.g. HTML).
  47. *
  48. * @param string $content
  49. * The raw content to set.
  50. */
  51. protected function setRawContent($content) {
  52. $this->content = $content;
  53. $this->plainTextContent = NULL;
  54. $this->elements = NULL;
  55. $this->drupalSettings = array();
  56. if (preg_match('@<script type="application/json" data-drupal-selector="drupal-settings-json">([^<]*)</script>@', $content, $matches)) {
  57. $this->drupalSettings = Json::decode($matches[1]);
  58. }
  59. }
  60. /**
  61. * Retrieves the plain-text content from the current raw content.
  62. */
  63. protected function getTextContent() {
  64. if (!isset($this->plainTextContent)) {
  65. $raw_content = $this->getRawContent();
  66. // Strip everything between the HEAD tags.
  67. $raw_content = preg_replace('@<head>(.+?)</head>@si', '', $raw_content);
  68. $this->plainTextContent = Xss::filter($raw_content, array());
  69. }
  70. return $this->plainTextContent;
  71. }
  72. /**
  73. * Removes all white-space between HTML tags from the raw content.
  74. *
  75. * White-space is only removed if there are no non-white-space characters
  76. * between HTML tags.
  77. *
  78. * Use this (once) after performing an operation that sets new raw content,
  79. * and when you want to use e.g. assertText() but ignore potential white-space
  80. * caused by HTML output templates.
  81. */
  82. protected function removeWhiteSpace() {
  83. $this->content = preg_replace('@>\s+<@', '><', $this->content);
  84. $this->plainTextContent = NULL;
  85. $this->elements = NULL;
  86. }
  87. /**
  88. * Gets the value of drupalSettings for the currently-loaded page.
  89. *
  90. * drupalSettings refers to the drupalSettings JavaScript variable.
  91. */
  92. protected function getDrupalSettings() {
  93. return $this->drupalSettings;
  94. }
  95. /**
  96. * Sets the value of drupalSettings for the currently-loaded page.
  97. *
  98. * drupalSettings refers to the drupalSettings JavaScript variable.
  99. */
  100. protected function setDrupalSettings($settings) {
  101. $this->drupalSettings = $settings;
  102. }
  103. /**
  104. * Parse content returned from curlExec using DOM and SimpleXML.
  105. *
  106. * @return \SimpleXMLElement|FALSE
  107. * A SimpleXMLElement or FALSE on failure.
  108. */
  109. protected function parse() {
  110. if (!isset($this->elements)) {
  111. // DOM can load HTML soup. But, HTML soup can throw warnings, suppress
  112. // them.
  113. $html_dom = new \DOMDocument();
  114. @$html_dom->loadHTML('<?xml encoding="UTF-8">' . $this->getRawContent());
  115. if ($html_dom) {
  116. $this->pass(SafeMarkup::format('Valid HTML found on "@path"', array('@path' => $this->getUrl())), 'Browser');
  117. // It's much easier to work with simplexml than DOM, luckily enough
  118. // we can just simply import our DOM tree.
  119. $this->elements = simplexml_import_dom($html_dom);
  120. }
  121. }
  122. if ($this->elements === FALSE) {
  123. $this->fail('Parsed page successfully.', 'Browser');
  124. }
  125. return $this->elements;
  126. }
  127. /**
  128. * Get the current URL from the cURL handler.
  129. *
  130. * @return string
  131. * The current URL.
  132. */
  133. protected function getUrl() {
  134. return isset($this->url) ? $this->url : 'no-url';
  135. }
  136. /**
  137. * Builds an XPath query.
  138. *
  139. * Builds an XPath query by replacing placeholders in the query by the value
  140. * of the arguments.
  141. *
  142. * XPath 1.0 (the version supported by libxml2, the underlying XML library
  143. * used by PHP) doesn't support any form of quotation. This function
  144. * simplifies the building of XPath expression.
  145. *
  146. * @param string $xpath
  147. * An XPath query, possibly with placeholders in the form ':name'.
  148. * @param array $args
  149. * An array of arguments with keys in the form ':name' matching the
  150. * placeholders in the query. The values may be either strings or numeric
  151. * values.
  152. *
  153. * @return string
  154. * An XPath query with arguments replaced.
  155. */
  156. protected function buildXPathQuery($xpath, array $args = array()) {
  157. // Replace placeholders.
  158. foreach ($args as $placeholder => $value) {
  159. // Cast MarkupInterface objects to string.
  160. if (is_object($value)) {
  161. $value = (string) $value;
  162. }
  163. // XPath 1.0 doesn't support a way to escape single or double quotes in a
  164. // string literal. We split double quotes out of the string, and encode
  165. // them separately.
  166. if (is_string($value)) {
  167. // Explode the text at the quote characters.
  168. $parts = explode('"', $value);
  169. // Quote the parts.
  170. foreach ($parts as &$part) {
  171. $part = '"' . $part . '"';
  172. }
  173. // Return the string.
  174. $value = count($parts) > 1 ? 'concat(' . implode(', \'"\', ', $parts) . ')' : $parts[0];
  175. }
  176. // Use preg_replace_callback() instead of preg_replace() to prevent the
  177. // regular expression engine from trying to substitute backreferences.
  178. $replacement = function ($matches) use ($value) {
  179. return $value;
  180. };
  181. $xpath = preg_replace_callback('/' . preg_quote($placeholder) . '\b/', $replacement, $xpath);
  182. }
  183. return $xpath;
  184. }
  185. /**
  186. * Performs an xpath search on the contents of the internal browser.
  187. *
  188. * The search is relative to the root element (HTML tag normally) of the page.
  189. *
  190. * @param string $xpath
  191. * The xpath string to use in the search.
  192. * @param array $arguments
  193. * An array of arguments with keys in the form ':name' matching the
  194. * placeholders in the query. The values may be either strings or numeric
  195. * values.
  196. *
  197. * @return \SimpleXMLElement[]|bool
  198. * The return value of the xpath search or FALSE on failure. For details on
  199. * the xpath string format and return values see the SimpleXML
  200. * documentation.
  201. *
  202. * @see http://php.net/manual/function.simplexml-element-xpath.php
  203. */
  204. protected function xpath($xpath, array $arguments = []) {
  205. if ($this->parse()) {
  206. $xpath = $this->buildXPathQuery($xpath, $arguments);
  207. $result = $this->elements->xpath($xpath);
  208. // Some combinations of PHP / libxml versions return an empty array
  209. // instead of the documented FALSE. Forcefully convert any falsish values
  210. // to an empty array to allow foreach(...) constructions.
  211. return $result ?: [];
  212. }
  213. return FALSE;
  214. }
  215. /**
  216. * Searches elements using a CSS selector in the raw content.
  217. *
  218. * The search is relative to the root element (HTML tag normally) of the page.
  219. *
  220. * @param string $selector
  221. * CSS selector to use in the search.
  222. *
  223. * @return \SimpleXMLElement[]
  224. * The return value of the XPath search performed after converting the CSS
  225. * selector to an XPath selector.
  226. */
  227. protected function cssSelect($selector) {
  228. return $this->xpath((new CssSelectorConverter())->toXPath($selector));
  229. }
  230. /**
  231. * Get all option elements, including nested options, in a select.
  232. *
  233. * @param \SimpleXMLElement $element
  234. * The element for which to get the options.
  235. *
  236. * @return \SimpleXmlElement[]
  237. * Option elements in select.
  238. */
  239. protected function getAllOptions(\SimpleXMLElement $element) {
  240. $options = array();
  241. // Add all options items.
  242. foreach ($element->option as $option) {
  243. $options[] = $option;
  244. }
  245. // Search option group children.
  246. if (isset($element->optgroup)) {
  247. foreach ($element->optgroup as $group) {
  248. $options = array_merge($options, $this->getAllOptions($group));
  249. }
  250. }
  251. return $options;
  252. }
  253. /**
  254. * Passes if a link with the specified label is found.
  255. *
  256. * An optional link index may be passed.
  257. *
  258. * @param string|\Drupal\Component\Render\MarkupInterface $label
  259. * Text between the anchor tags.
  260. * @param int $index
  261. * Link position counting from zero.
  262. * @param string $message
  263. * (optional) A message to display with the assertion. Do not translate
  264. * messages: use strtr() to embed variables in the message text, not
  265. * t(). If left blank, a default message will be displayed.
  266. * @param string $group
  267. * (optional) The group this message is in, which is displayed in a column
  268. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  269. * translate this string. Defaults to 'Other'; most tests do not override
  270. * this default.
  271. *
  272. * @return bool
  273. * TRUE if the assertion succeeded, FALSE otherwise.
  274. */
  275. protected function assertLink($label, $index = 0, $message = '', $group = 'Other') {
  276. // Cast MarkupInterface objects to string.
  277. $label = (string) $label;
  278. $links = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
  279. $message = ($message ? $message : strtr('Link with label %label found.', array('%label' => $label)));
  280. return $this->assert(isset($links[$index]), $message, $group);
  281. }
  282. /**
  283. * Passes if a link with the specified label is not found.
  284. *
  285. * @param string|\Drupal\Component\Render\MarkupInterface $label
  286. * Text between the anchor tags.
  287. * @param string $message
  288. * (optional) A message to display with the assertion. Do not translate
  289. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  290. * variables in the message text, not t(). If left blank, a default message
  291. * will be displayed.
  292. * @param string $group
  293. * (optional) The group this message is in, which is displayed in a column
  294. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  295. * translate this string. Defaults to 'Other'; most tests do not override
  296. * this default.
  297. *
  298. * @return bool
  299. * TRUE if the assertion succeeded, FALSE otherwise.
  300. */
  301. protected function assertNoLink($label, $message = '', $group = 'Other') {
  302. // Cast MarkupInterface objects to string.
  303. $label = (string) $label;
  304. $links = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
  305. $message = ($message ? $message : SafeMarkup::format('Link with label %label not found.', array('%label' => $label)));
  306. return $this->assert(empty($links), $message, $group);
  307. }
  308. /**
  309. * Passes if a link containing a given href (part) is found.
  310. *
  311. * @param string $href
  312. * The full or partial value of the 'href' attribute of the anchor tag.
  313. * @param string $index
  314. * Link position counting from zero.
  315. * @param string $message
  316. * (optional) A message to display with the assertion. Do not translate
  317. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  318. * variables in the message text, not t(). If left blank, a default message
  319. * will be displayed.
  320. * @param string $group
  321. * (optional) The group this message is in, which is displayed in a column
  322. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  323. * translate this string. Defaults to 'Other'; most tests do not override
  324. * this default.
  325. *
  326. * @return bool
  327. * TRUE if the assertion succeeded, FALSE otherwise.
  328. */
  329. protected function assertLinkByHref($href, $index = 0, $message = '', $group = 'Other') {
  330. $links = $this->xpath('//a[contains(@href, :href)]', array(':href' => $href));
  331. $message = ($message ? $message : SafeMarkup::format('Link containing href %href found.', array('%href' => $href)));
  332. return $this->assert(isset($links[$index]), $message, $group);
  333. }
  334. /**
  335. * Passes if a link containing a given href (part) is not found.
  336. *
  337. * @param string $href
  338. * The full or partial value of the 'href' attribute of the anchor tag.
  339. * @param string $message
  340. * (optional) A message to display with the assertion. Do not translate
  341. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  342. * variables in the message text, not t(). If left blank, a default message
  343. * will be displayed.
  344. * @param string $group
  345. * (optional) The group this message is in, which is displayed in a column
  346. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  347. * translate this string. Defaults to 'Other'; most tests do not override
  348. * this default.
  349. *
  350. * @return bool
  351. * TRUE if the assertion succeeded, FALSE otherwise.
  352. */
  353. protected function assertNoLinkByHref($href, $message = '', $group = 'Other') {
  354. $links = $this->xpath('//a[contains(@href, :href)]', array(':href' => $href));
  355. $message = ($message ? $message : SafeMarkup::format('No link containing href %href found.', array('%href' => $href)));
  356. return $this->assert(empty($links), $message, $group);
  357. }
  358. /**
  359. * Passes if a link containing a given href is not found in the main region.
  360. *
  361. * @param string $href
  362. * The full or partial value of the 'href' attribute of the anchor tag.
  363. * @param string $message
  364. * (optional) A message to display with the assertion. Do not translate
  365. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  366. * variables in the message text, not t(). If left blank, a default message
  367. * will be displayed.
  368. * @param string $group
  369. * (optional) The group this message is in, which is displayed in a column
  370. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  371. * translate this string. Defaults to 'Other'; most tests do not override
  372. * this default.
  373. *
  374. * @return bool
  375. * TRUE if the assertion succeeded, FALSE otherwise.
  376. */
  377. protected function assertNoLinkByHrefInMainRegion($href, $message = '', $group = 'Other') {
  378. $links = $this->xpath('//main//a[contains(@href, :href)]', array(':href' => $href));
  379. $message = ($message ? $message : SafeMarkup::format('No link containing href %href found.', array('%href' => $href)));
  380. return $this->assert(empty($links), $message, $group);
  381. }
  382. /**
  383. * Passes if the raw text IS found on the loaded page, fail otherwise.
  384. *
  385. * Raw text refers to the raw HTML that the page generated.
  386. *
  387. * @param string $raw
  388. * Raw (HTML) string to look for.
  389. * @param string $message
  390. * (optional) A message to display with the assertion. Do not translate
  391. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  392. * variables in the message text, not t(). If left blank, a default message
  393. * will be displayed.
  394. * @param string $group
  395. * (optional) The group this message is in, which is displayed in a column
  396. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  397. * translate this string. Defaults to 'Other'; most tests do not override
  398. * this default.
  399. *
  400. * @return bool
  401. * TRUE on pass, FALSE on fail.
  402. */
  403. protected function assertRaw($raw, $message = '', $group = 'Other') {
  404. if (!$message) {
  405. $message = 'Raw "' . Html::escape($raw) . '" found';
  406. }
  407. return $this->assert(strpos($this->getRawContent(), (string) $raw) !== FALSE, $message, $group);
  408. }
  409. /**
  410. * Passes if the raw text is NOT found on the loaded page, fail otherwise.
  411. *
  412. * Raw text refers to the raw HTML that the page generated.
  413. *
  414. * @param string $raw
  415. * Raw (HTML) string to look for.
  416. * @param string $message
  417. * (optional) A message to display with the assertion. Do not translate
  418. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  419. * variables in the message text, not t(). If left blank, a default message
  420. * will be displayed.
  421. * @param string $group
  422. * (optional) The group this message is in, which is displayed in a column
  423. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  424. * translate this string. Defaults to 'Other'; most tests do not override
  425. * this default.
  426. *
  427. * @return bool
  428. * TRUE on pass, FALSE on fail.
  429. */
  430. protected function assertNoRaw($raw, $message = '', $group = 'Other') {
  431. if (!$message) {
  432. $message = 'Raw "' . Html::escape($raw) . '" not found';
  433. }
  434. return $this->assert(strpos($this->getRawContent(), (string) $raw) === FALSE, $message, $group);
  435. }
  436. /**
  437. * Passes if the raw text IS found escaped on the loaded page, fail otherwise.
  438. *
  439. * Raw text refers to the raw HTML that the page generated.
  440. *
  441. * @param string $raw
  442. * Raw (HTML) string to look for.
  443. * @param string $message
  444. * (optional) A message to display with the assertion. Do not translate
  445. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  446. * variables in the message text, not t(). If left blank, a default message
  447. * will be displayed.
  448. * @param string $group
  449. * (optional) The group this message is in, which is displayed in a column
  450. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  451. * translate this string. Defaults to 'Other'; most tests do not override
  452. * this default.
  453. *
  454. * @return bool
  455. * TRUE on pass, FALSE on fail.
  456. */
  457. protected function assertEscaped($raw, $message = '', $group = 'Other') {
  458. if (!$message) {
  459. $message = 'Escaped "' . Html::escape($raw) . '" found';
  460. }
  461. return $this->assert(strpos($this->getRawContent(), Html::escape($raw)) !== FALSE, $message, $group);
  462. }
  463. /**
  464. * Passes if the raw text IS NOT found escaped on the loaded page, fail
  465. * otherwise.
  466. *
  467. * Raw text refers to the raw HTML that the page generated.
  468. *
  469. * @param string $raw
  470. * Raw (HTML) string to look for.
  471. * @param string $message
  472. * (optional) A message to display with the assertion. Do not translate
  473. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  474. * variables in the message text, not t(). If left blank, a default message
  475. * will be displayed.
  476. * @param string $group
  477. * (optional) The group this message is in, which is displayed in a column
  478. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  479. * translate this string. Defaults to 'Other'; most tests do not override
  480. * this default.
  481. *
  482. * @return bool
  483. * TRUE on pass, FALSE on fail.
  484. */
  485. protected function assertNoEscaped($raw, $message = '', $group = 'Other') {
  486. if (!$message) {
  487. $message = 'Escaped "' . Html::escape($raw) . '" not found';
  488. }
  489. return $this->assert(strpos($this->getRawContent(), Html::escape($raw)) === FALSE, $message, $group);
  490. }
  491. /**
  492. * Passes if the page (with HTML stripped) contains the text.
  493. *
  494. * Note that stripping HTML tags also removes their attributes, such as
  495. * the values of text fields.
  496. *
  497. * @param string $text
  498. * Plain text to look for.
  499. * @param string $message
  500. * (optional) A message to display with the assertion. Do not translate
  501. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  502. * variables in the message text, not t(). If left blank, a default message
  503. * will be displayed.
  504. * @param string $group
  505. * (optional) The group this message is in, which is displayed in a column
  506. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  507. * translate this string. Defaults to 'Other'; most tests do not override
  508. * this default.
  509. *
  510. * @return bool
  511. * TRUE on pass, FALSE on fail.
  512. *
  513. * @see \Drupal\simpletest\AssertContentTrait::assertRaw()
  514. */
  515. protected function assertText($text, $message = '', $group = 'Other') {
  516. return $this->assertTextHelper($text, $message, $group, FALSE);
  517. }
  518. /**
  519. * Passes if the page (with HTML stripped) does not contains the text.
  520. *
  521. * Note that stripping HTML tags also removes their attributes, such as
  522. * the values of text fields.
  523. *
  524. * @param string $text
  525. * Plain text to look for.
  526. * @param string $message
  527. * (optional) A message to display with the assertion. Do not translate
  528. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  529. * variables in the message text, not t(). If left blank, a default message
  530. * will be displayed.
  531. * @param string $group
  532. * (optional) The group this message is in, which is displayed in a column
  533. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  534. * translate this string. Defaults to 'Other'; most tests do not override
  535. * this default.
  536. *
  537. * @return bool
  538. * TRUE on pass, FALSE on fail.
  539. *
  540. * @see \Drupal\simpletest\AssertContentTrait::assertNoRaw()
  541. */
  542. protected function assertNoText($text, $message = '', $group = 'Other') {
  543. return $this->assertTextHelper($text, $message, $group, TRUE);
  544. }
  545. /**
  546. * Helper for assertText and assertNoText.
  547. *
  548. * It is not recommended to call this function directly.
  549. *
  550. * @param string $text
  551. * Plain text to look for.
  552. * @param string $message
  553. * (optional) A message to display with the assertion. Do not translate
  554. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  555. * variables in the message text, not t(). If left blank, a default message
  556. * will be displayed.
  557. * @param string $group
  558. * (optional) The group this message is in, which is displayed in a column
  559. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  560. * translate this string. Defaults to 'Other'; most tests do not override
  561. * this default. Defaults to 'Other'.
  562. * @param bool $not_exists
  563. * (optional) TRUE if this text should not exist, FALSE if it should.
  564. * Defaults to TRUE.
  565. *
  566. * @return bool
  567. * TRUE on pass, FALSE on fail.
  568. */
  569. protected function assertTextHelper($text, $message = '', $group = 'Other', $not_exists = TRUE) {
  570. if (!$message) {
  571. $message = !$not_exists ? SafeMarkup::format('"@text" found', array('@text' => $text)) : SafeMarkup::format('"@text" not found', array('@text' => $text));
  572. }
  573. return $this->assert($not_exists == (strpos($this->getTextContent(), (string) $text) === FALSE), $message, $group);
  574. }
  575. /**
  576. * Passes if the text is found ONLY ONCE on the text version of the page.
  577. *
  578. * The text version is the equivalent of what a user would see when viewing
  579. * through a web browser. In other words the HTML has been filtered out of
  580. * the contents.
  581. *
  582. * @param string|\Drupal\Component\Render\MarkupInterface $text
  583. * Plain text to look for.
  584. * @param string $message
  585. * (optional) A message to display with the assertion. Do not translate
  586. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  587. * variables in the message text, not t(). If left blank, a default message
  588. * will be displayed.
  589. * @param string $group
  590. * (optional) The group this message is in, which is displayed in a column
  591. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  592. * translate this string. Defaults to 'Other'; most tests do not override
  593. * this default.
  594. *
  595. * @return bool
  596. * TRUE on pass, FALSE on fail.
  597. */
  598. protected function assertUniqueText($text, $message = '', $group = 'Other') {
  599. return $this->assertUniqueTextHelper($text, $message, $group, TRUE);
  600. }
  601. /**
  602. * Passes if the text is found MORE THAN ONCE on the text version of the page.
  603. *
  604. * The text version is the equivalent of what a user would see when viewing
  605. * through a web browser. In other words the HTML has been filtered out of
  606. * the contents.
  607. *
  608. * @param string|\Drupal\Component\Render\MarkupInterface $text
  609. * Plain text to look for.
  610. * @param string $message
  611. * (optional) A message to display with the assertion. Do not translate
  612. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  613. * variables in the message text, not t(). If left blank, a default message
  614. * will be displayed.
  615. * @param string $group
  616. * (optional) The group this message is in, which is displayed in a column
  617. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  618. * translate this string. Defaults to 'Other'; most tests do not override
  619. * this default.
  620. *
  621. * @return bool
  622. * TRUE on pass, FALSE on fail.
  623. */
  624. protected function assertNoUniqueText($text, $message = '', $group = 'Other') {
  625. return $this->assertUniqueTextHelper($text, $message, $group, FALSE);
  626. }
  627. /**
  628. * Helper for assertUniqueText and assertNoUniqueText.
  629. *
  630. * It is not recommended to call this function directly.
  631. *
  632. * @param string|\Drupal\Component\Render\MarkupInterface $text
  633. * Plain text to look for.
  634. * @param string $message
  635. * (optional) A message to display with the assertion. Do not translate
  636. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  637. * variables in the message text, not t(). If left blank, a default message
  638. * will be displayed.
  639. * @param string $group
  640. * (optional) The group this message is in, which is displayed in a column
  641. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  642. * translate this string. Defaults to 'Other'; most tests do not override
  643. * this default. Defaults to 'Other'.
  644. * @param bool $be_unique
  645. * (optional) TRUE if this text should be found only once, FALSE if it
  646. * should be found more than once. Defaults to FALSE.
  647. *
  648. * @return bool
  649. * TRUE on pass, FALSE on fail.
  650. */
  651. protected function assertUniqueTextHelper($text, $message = '', $group = 'Other', $be_unique = FALSE) {
  652. // Cast MarkupInterface objects to string.
  653. $text = (string) $text;
  654. if (!$message) {
  655. $message = '"' . $text . '"' . ($be_unique ? ' found only once' : ' found more than once');
  656. }
  657. $first_occurrence = strpos($this->getTextContent(), $text);
  658. if ($first_occurrence === FALSE) {
  659. return $this->assert(FALSE, $message, $group);
  660. }
  661. $offset = $first_occurrence + strlen($text);
  662. $second_occurrence = strpos($this->getTextContent(), $text, $offset);
  663. return $this->assert($be_unique == ($second_occurrence === FALSE), $message, $group);
  664. }
  665. /**
  666. * Triggers a pass if the Perl regex pattern is found in the raw content.
  667. *
  668. * @param string $pattern
  669. * Perl regex to look for including the regex delimiters.
  670. * @param string $message
  671. * (optional) A message to display with the assertion. Do not translate
  672. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  673. * variables in the message text, not t(). If left blank, a default message
  674. * will be displayed.
  675. * @param string $group
  676. * (optional) The group this message is in, which is displayed in a column
  677. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  678. * translate this string. Defaults to 'Other'; most tests do not override
  679. * this default.
  680. *
  681. * @return bool
  682. * TRUE on pass, FALSE on fail.
  683. */
  684. protected function assertPattern($pattern, $message = '', $group = 'Other') {
  685. if (!$message) {
  686. $message = SafeMarkup::format('Pattern "@pattern" found', array('@pattern' => $pattern));
  687. }
  688. return $this->assert((bool) preg_match($pattern, $this->getRawContent()), $message, $group);
  689. }
  690. /**
  691. * Triggers a pass if the perl regex pattern is not found in raw content.
  692. *
  693. * @param string $pattern
  694. * Perl regex to look for including the regex delimiters.
  695. * @param string $message
  696. * (optional) A message to display with the assertion. Do not translate
  697. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  698. * variables in the message text, not t(). If left blank, a default message
  699. * will be displayed.
  700. * @param string $group
  701. * (optional) The group this message is in, which is displayed in a column
  702. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  703. * translate this string. Defaults to 'Other'; most tests do not override
  704. * this default.
  705. *
  706. * @return bool
  707. * TRUE on pass, FALSE on fail.
  708. */
  709. protected function assertNoPattern($pattern, $message = '', $group = 'Other') {
  710. if (!$message) {
  711. $message = SafeMarkup::format('Pattern "@pattern" not found', array('@pattern' => $pattern));
  712. }
  713. return $this->assert(!preg_match($pattern, $this->getRawContent()), $message, $group);
  714. }
  715. /**
  716. * Asserts that a Perl regex pattern is found in the plain-text content.
  717. *
  718. * @param string $pattern
  719. * Perl regex to look for including the regex delimiters.
  720. * @param string $message
  721. * (optional) A message to display with the assertion.
  722. * @param string $group
  723. * (optional) The group this message is in, which is displayed in a column
  724. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  725. * translate this string. Defaults to 'Other'; most tests do not override
  726. * this default.
  727. *
  728. * @return bool
  729. * TRUE on pass, FALSE on failure.
  730. */
  731. protected function assertTextPattern($pattern, $message = NULL, $group = 'Other') {
  732. if (!isset($message)) {
  733. $message = SafeMarkup::format('Pattern "@pattern" found', array('@pattern' => $pattern));
  734. }
  735. return $this->assert((bool) preg_match($pattern, $this->getTextContent()), $message, $group);
  736. }
  737. /**
  738. * Pass if the page title is the given string.
  739. *
  740. * @param string $title
  741. * The string the title should be.
  742. * @param string $message
  743. * (optional) A message to display with the assertion. Do not translate
  744. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  745. * variables in the message text, not t(). If left blank, a default message
  746. * will be displayed.
  747. * @param string $group
  748. * (optional) The group this message is in, which is displayed in a column
  749. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  750. * translate this string. Defaults to 'Other'; most tests do not override
  751. * this default.
  752. *
  753. * @return bool
  754. * TRUE on pass, FALSE on fail.
  755. */
  756. protected function assertTitle($title, $message = '', $group = 'Other') {
  757. // Don't use xpath as it messes with HTML escaping.
  758. preg_match('@<title>(.*)</title>@', $this->getRawContent(), $matches);
  759. if (isset($matches[1])) {
  760. $actual = $matches[1];
  761. $actual = $this->castSafeStrings($actual);
  762. $title = $this->castSafeStrings($title);
  763. if (!$message) {
  764. $message = SafeMarkup::format('Page title @actual is equal to @expected.', array(
  765. '@actual' => var_export($actual, TRUE),
  766. '@expected' => var_export($title, TRUE),
  767. ));
  768. }
  769. return $this->assertEqual($actual, $title, $message, $group);
  770. }
  771. return $this->fail('No title element found on the page.');
  772. }
  773. /**
  774. * Pass if the page title is not the given string.
  775. *
  776. * @param string $title
  777. * The string the title should not be.
  778. * @param string $message
  779. * (optional) A message to display with the assertion. Do not translate
  780. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  781. * variables in the message text, not t(). If left blank, a default message
  782. * will be displayed.
  783. * @param string $group
  784. * (optional) The group this message is in, which is displayed in a column
  785. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  786. * translate this string. Defaults to 'Other'; most tests do not override
  787. * this default.
  788. *
  789. * @return bool
  790. * TRUE on pass, FALSE on fail.
  791. */
  792. protected function assertNoTitle($title, $message = '', $group = 'Other') {
  793. $actual = (string) current($this->xpath('//title'));
  794. if (!$message) {
  795. $message = SafeMarkup::format('Page title @actual is not equal to @unexpected.', array(
  796. '@actual' => var_export($actual, TRUE),
  797. '@unexpected' => var_export($title, TRUE),
  798. ));
  799. }
  800. return $this->assertNotEqual($actual, $title, $message, $group);
  801. }
  802. /**
  803. * Asserts themed output.
  804. *
  805. * @param string $callback
  806. * The name of the theme hook to invoke; e.g. 'links' for links.html.twig.
  807. * @param string $variables
  808. * An array of variables to pass to the theme function.
  809. * @param string $expected
  810. * The expected themed output string.
  811. * @param string $message
  812. * (optional) A message to display with the assertion. Do not translate
  813. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  814. * variables in the message text, not t(). If left blank, a default message
  815. * will be displayed.
  816. * @param string $group
  817. * (optional) The group this message is in, which is displayed in a column
  818. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  819. * translate this string. Defaults to 'Other'; most tests do not override
  820. * this default.
  821. *
  822. * @return bool
  823. * TRUE on pass, FALSE on fail.
  824. */
  825. protected function assertThemeOutput($callback, array $variables = array(), $expected = '', $message = '', $group = 'Other') {
  826. /** @var \Drupal\Core\Render\RendererInterface $renderer */
  827. $renderer = \Drupal::service('renderer');
  828. // The string cast is necessary because theme functions return
  829. // MarkupInterface objects. This means we can assert that $expected
  830. // matches the theme output without having to worry about 0 == ''.
  831. $output = (string) $renderer->executeInRenderContext(new RenderContext(), function() use ($callback, $variables) {
  832. return \Drupal::theme()->render($callback, $variables);
  833. });
  834. $this->verbose(
  835. '<hr />' . 'Result:' . '<pre>' . Html::escape(var_export($output, TRUE)) . '</pre>'
  836. . '<hr />' . 'Expected:' . '<pre>' . Html::escape(var_export($expected, TRUE)) . '</pre>'
  837. . '<hr />' . $output
  838. );
  839. if (!$message) {
  840. $message = '%callback rendered correctly.';
  841. }
  842. $message = format_string($message, array('%callback' => 'theme_' . $callback . '()'));
  843. return $this->assertIdentical($output, $expected, $message, $group);
  844. }
  845. /**
  846. * Asserts that a field exists in the current page with a given Xpath result.
  847. *
  848. * @param \SimpleXmlElement[] $fields
  849. * Xml elements.
  850. * @param string $value
  851. * (optional) Value of the field to assert. You may pass in NULL (default) to skip
  852. * checking the actual value, while still checking that the field exists.
  853. * @param string $message
  854. * (optional) A message to display with the assertion. Do not translate
  855. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  856. * variables in the message text, not t(). If left blank, a default message
  857. * will be displayed.
  858. * @param string $group
  859. * (optional) The group this message is in, which is displayed in a column
  860. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  861. * translate this string. Defaults to 'Other'; most tests do not override
  862. * this default.
  863. *
  864. * @return bool
  865. * TRUE on pass, FALSE on fail.
  866. */
  867. protected function assertFieldsByValue($fields, $value = NULL, $message = '', $group = 'Other') {
  868. // If value specified then check array for match.
  869. $found = TRUE;
  870. if (isset($value)) {
  871. $found = FALSE;
  872. if ($fields) {
  873. foreach ($fields as $field) {
  874. if (isset($field['value']) && $field['value'] == $value) {
  875. // Input element with correct value.
  876. $found = TRUE;
  877. }
  878. elseif (isset($field->option) || isset($field->optgroup)) {
  879. // Select element found.
  880. $selected = $this->getSelectedItem($field);
  881. if ($selected === FALSE) {
  882. // No item selected so use first item.
  883. $items = $this->getAllOptions($field);
  884. if (!empty($items) && $items[0]['value'] == $value) {
  885. $found = TRUE;
  886. }
  887. }
  888. elseif ($selected == $value) {
  889. $found = TRUE;
  890. }
  891. }
  892. elseif ((string) $field == $value) {
  893. // Text area with correct text.
  894. $found = TRUE;
  895. }
  896. }
  897. }
  898. }
  899. return $this->assertTrue($fields && $found, $message, $group);
  900. }
  901. /**
  902. * Asserts that a field exists in the current page by the given XPath.
  903. *
  904. * @param string $xpath
  905. * XPath used to find the field.
  906. * @param string $value
  907. * (optional) Value of the field to assert. You may pass in NULL (default)
  908. * to skip checking the actual value, while still checking that the field
  909. * exists.
  910. * @param string $message
  911. * (optional) A message to display with the assertion. Do not translate
  912. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  913. * variables in the message text, not t(). If left blank, a default message
  914. * will be displayed.
  915. * @param string $group
  916. * (optional) The group this message is in, which is displayed in a column
  917. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  918. * translate this string. Defaults to 'Other'; most tests do not override
  919. * this default.
  920. *
  921. * @return bool
  922. * TRUE on pass, FALSE on fail.
  923. */
  924. protected function assertFieldByXPath($xpath, $value = NULL, $message = '', $group = 'Other') {
  925. $fields = $this->xpath($xpath);
  926. return $this->assertFieldsByValue($fields, $value, $message, $group);
  927. }
  928. /**
  929. * Get the selected value from a select field.
  930. *
  931. * @param \SimpleXmlElement $element
  932. * SimpleXMLElement select element.
  933. *
  934. * @return bool
  935. * The selected value or FALSE.
  936. */
  937. protected function getSelectedItem(\SimpleXMLElement $element) {
  938. foreach ($element->children() as $item) {
  939. if (isset($item['selected'])) {
  940. return $item['value'];
  941. }
  942. elseif ($item->getName() == 'optgroup') {
  943. if ($value = $this->getSelectedItem($item)) {
  944. return $value;
  945. }
  946. }
  947. }
  948. return FALSE;
  949. }
  950. /**
  951. * Asserts that a field does not exist or its value does not match, by XPath.
  952. *
  953. * @param string $xpath
  954. * XPath used to find the field.
  955. * @param string $value
  956. * (optional) Value of the field, to assert that the field's value on the
  957. * page does not match it.
  958. * @param string $message
  959. * (optional) A message to display with the assertion. Do not translate
  960. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  961. * variables in the message text, not t(). If left blank, a default message
  962. * will be displayed.
  963. * @param string $group
  964. * (optional) The group this message is in, which is displayed in a column
  965. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  966. * translate this string. Defaults to 'Other'; most tests do not override
  967. * this default.
  968. *
  969. * @return bool
  970. * TRUE on pass, FALSE on fail.
  971. */
  972. protected function assertNoFieldByXPath($xpath, $value = NULL, $message = '', $group = 'Other') {
  973. $fields = $this->xpath($xpath);
  974. // If value specified then check array for match.
  975. $found = TRUE;
  976. if (isset($value)) {
  977. $found = FALSE;
  978. if ($fields) {
  979. foreach ($fields as $field) {
  980. if ($field['value'] == $value) {
  981. $found = TRUE;
  982. }
  983. }
  984. }
  985. }
  986. return $this->assertFalse($fields && $found, $message, $group);
  987. }
  988. /**
  989. * Asserts that a field exists with the given name and value.
  990. *
  991. * @param string $name
  992. * Name of field to assert.
  993. * @param string $value
  994. * (optional) Value of the field to assert. You may pass in NULL (default)
  995. * to skip checking the actual value, while still checking that the field
  996. * exists.
  997. * @param string $message
  998. * (optional) A message to display with the assertion. Do not translate
  999. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  1000. * variables in the message text, not t(). If left blank, a default message
  1001. * will be displayed.
  1002. * @param string $group
  1003. * (optional) The group this message is in, which is displayed in a column
  1004. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  1005. * translate this string. Defaults to 'Browser'; most tests do not override
  1006. * this default.
  1007. *
  1008. * @return bool
  1009. * TRUE on pass, FALSE on fail.
  1010. */
  1011. protected function assertFieldByName($name, $value = NULL, $message = NULL, $group = 'Browser') {
  1012. if (!isset($message)) {
  1013. if (!isset($value)) {
  1014. $message = SafeMarkup::format('Found field with name @name', array(
  1015. '@name' => var_export($name, TRUE),
  1016. ));
  1017. }
  1018. else {
  1019. $message = SafeMarkup::format('Found field with name @name and value @value', array(
  1020. '@name' => var_export($name, TRUE),
  1021. '@value' => var_export($value, TRUE),
  1022. ));
  1023. }
  1024. }
  1025. return $this->assertFieldByXPath($this->constructFieldXpath('name', $name), $value, $message, $group);
  1026. }
  1027. /**
  1028. * Asserts that a field does not exist with the given name and value.
  1029. *
  1030. * @param string $name
  1031. * Name of field to assert.
  1032. * @param string $value
  1033. * (optional) Value for the field, to assert that the field's value on the
  1034. * page doesn't match it. You may pass in NULL to skip checking the
  1035. * value, while still checking that the field doesn't exist. However, the
  1036. * default value ('') asserts that the field value is not an empty string.
  1037. * @param string $message
  1038. * (optional) A message to display with the assertion. Do not translate
  1039. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  1040. * variables in the message text, not t(). If left blank, a default message
  1041. * will be displayed.
  1042. * @param string $group
  1043. * (optional) The group this message is in, which is displayed in a column
  1044. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  1045. * translate this string. Defaults to 'Browser'; most tests do not override
  1046. * this default.
  1047. *
  1048. * @return bool
  1049. * TRUE on pass, FALSE on fail.
  1050. */
  1051. protected function assertNoFieldByName($name, $value = '', $message = '', $group = 'Browser') {
  1052. return $this->assertNoFieldByXPath($this->constructFieldXpath('name', $name), $value, $message ? $message : SafeMarkup::format('Did not find field by name @name', array('@name' => $name)), $group);
  1053. }
  1054. /**
  1055. * Asserts that a field exists with the given ID and value.
  1056. *
  1057. * @param string $id
  1058. * ID of field to assert.
  1059. * @param string|\Drupal\Component\Render\MarkupInterface $value
  1060. * (optional) Value for the field to assert. You may pass in NULL to skip
  1061. * checking the value, while still checking that the field exists.
  1062. * However, the default value ('') asserts that the field value is an empty
  1063. * string.
  1064. * @param string|\Drupal\Component\Render\MarkupInterface $message
  1065. * (optional) A message to display with the assertion. Do not translate
  1066. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  1067. * variables in the message text, not t(). If left blank, a default message
  1068. * will be displayed.
  1069. * @param string $group
  1070. * (optional) The group this message is in, which is displayed in a column
  1071. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  1072. * translate this string. Defaults to 'Browser'; most tests do not override
  1073. * this default.
  1074. *
  1075. * @return bool
  1076. * TRUE on pass, FALSE on fail.
  1077. */
  1078. protected function assertFieldById($id, $value = '', $message = '', $group = 'Browser') {
  1079. // Cast MarkupInterface objects to string.
  1080. if (isset($value)) {
  1081. $value = (string) $value;
  1082. }
  1083. $message = (string) $message;
  1084. return $this->assertFieldByXPath($this->constructFieldXpath('id', $id), $value, $message ? $message : SafeMarkup::format('Found field by id @id', array('@id' => $id)), $group);
  1085. }
  1086. /**
  1087. * Asserts that a field does not exist with the given ID and value.
  1088. *
  1089. * @param string $id
  1090. * ID of field to assert.
  1091. * @param string $value
  1092. * (optional) Value for the field, to assert that the field's value on the
  1093. * page doesn't match it. You may pass in NULL to skip checking the value,
  1094. * while still checking that the field doesn't exist. However, the default
  1095. * value ('') asserts that the field value is not an empty string.
  1096. * @param string $message
  1097. * (optional) A message to display with the assertion. Do not translate
  1098. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  1099. * variables in the message text, not t(). If left blank, a default message
  1100. * will be displayed.
  1101. * @param string $group
  1102. * (optional) The group this message is in, which is displayed in a column
  1103. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  1104. * translate this string. Defaults to 'Browser'; most tests do not override
  1105. * this default.
  1106. *
  1107. * @return bool
  1108. * TRUE on pass, FALSE on fail.
  1109. */
  1110. protected function assertNoFieldById($id, $value = '', $message = '', $group = 'Browser') {
  1111. return $this->assertNoFieldByXPath($this->constructFieldXpath('id', $id), $value, $message ? $message : SafeMarkup::format('Did not find field by id @id', array('@id' => $id)), $group);
  1112. }
  1113. /**
  1114. * Asserts that a checkbox field in the current page is checked.
  1115. *
  1116. * @param string $id
  1117. * ID of field to assert.
  1118. * @param string $message
  1119. * (optional) A message to display with the assertion. Do not translate
  1120. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  1121. * variables in the message text, not t(). If left blank, a default message
  1122. * will be displayed.
  1123. * @param string $group
  1124. * (optional) The group this message is in, which is displayed in a column
  1125. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  1126. * translate this string. Defaults to 'Browser'; most tests do not override
  1127. * this default.
  1128. *
  1129. * @return bool
  1130. * TRUE on pass, FALSE on fail.
  1131. */
  1132. protected function assertFieldChecked($id, $message = '', $group = 'Browser') {
  1133. $elements = $this->xpath('//input[@id=:id]', array(':id' => $id));
  1134. return $this->assertTrue(isset($elements[0]) && !empty($elements[0]['checked']), $message ? $message : SafeMarkup::format('Checkbox field @id is checked.', array('@id' => $id)), $group);
  1135. }
  1136. /**
  1137. * Asserts that a checkbox field in the current page is not checked.
  1138. *
  1139. * @param string $id
  1140. * ID of field to assert.
  1141. * @param string $message
  1142. * (optional) A message to display with the assertion. Do not translate
  1143. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  1144. * variables in the message text, not t(). If left blank, a default message
  1145. * will be displayed.
  1146. * @param string $group
  1147. * (optional) The group this message is in, which is displayed in a column
  1148. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  1149. * translate this string. Defaults to 'Browser'; most tests do not override
  1150. * this default.
  1151. *
  1152. * @return bool
  1153. * TRUE on pass, FALSE on fail.
  1154. */
  1155. protected function assertNoFieldChecked($id, $message = '', $group = 'Browser') {
  1156. $elements = $this->xpath('//input[@id=:id]', array(':id' => $id));
  1157. return $this->assertTrue(isset($elements[0]) && empty($elements[0]['checked']), $message ? $message : SafeMarkup::format('Checkbox field @id is not checked.', array('@id' => $id)), $group);
  1158. }
  1159. /**
  1160. * Asserts that a select option in the current page exists.
  1161. *
  1162. * @param string $id
  1163. * ID of select field to assert.
  1164. * @param string $option
  1165. * Option to assert.
  1166. * @param string $message
  1167. * (optional) A message to display with the assertion. Do not translate
  1168. * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
  1169. * variables in the message text, not t(). If left blank, a default message
  1170. * will be displayed.
  1171. * @param string $group
  1172. * (optional) The group this message is in, which is displayed in a column
  1173. * in test output. Use 'Debug' to indicate this is debugging output. Do not
  1174. * translate this string. Defaults to 'Browser'; most tests do not override
  1175. * this default.
  1176. *
  1177. * @return bool
  1178. * TRUE on pass, FALSE on fail.
  1179. */
  1180. protected function assertOption($id, $option, $message = '', $group = 'Browser') {
  1181. $options = $this->xpath('//select[@id=:id]//option[@value=:option]', array(':id' => $id, ':option' => $option));
  1182. return $this->assertTrue(isset($options[0]), $message ? $message : SafeMarkup::format('Option @option for field @id exists.', array('@option' => $option, '@id' => $id)), $group);
  1183. }
  1184. /**
  1185. * Asserts that a select option with the visible text exists.
  1186. *
  1187. * @param string $id
  1188. * The ID of the select field to assert.
  1189. * @param string $text
  1190. * The text for the option tag to assert.
  1191. * @param string $message
  1192. * (optional) A message to display with the assertion.
  1193. *
  1194. * @return bool
  1195. * TRUE on pass, FALSE on fail.
  1196. */
  1197. protected function assertOptionByText($id, $text,