AssertContentTrait.php 61 KB

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