diff --git a/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php b/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php index a08de40..436d5ee 100644 --- a/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php +++ b/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php @@ -124,7 +124,7 @@ protected function assertResponse($code) { protected function assertFieldByName($name, $value = NULL) { $this->assertSession()->fieldExists($name); if ($value !== NULL) { - $this->assertSession()->fieldValueEquals($name, $value); + $this->assertSession()->fieldValueEquals($name, (string) $value); } } @@ -163,6 +163,21 @@ protected function assertRaw($raw) { } /** + * Passes if the raw text IS not found on the loaded page, fail otherwise. + * + * Raw text refers to the raw HTML that the page generated. + * + * @param string $raw + * Raw (HTML) string to look for. + * + * @deprecated Scheduled for removal in Drupal 9.0.0. + * Use $this->assertSession()->responseNotContains() instead. + */ + protected function assertNoRaw($raw) { + $this->assertSession()->responseNotContains($raw); + } + + /** * Pass if the page title is the given string. * * @param string $expected_title @@ -181,12 +196,98 @@ protected function assertTitle($expected_title) { * Text between the anchor tags. * @param int $index * Link position counting from zero. + * + * @deprecated Scheduled for removal in Drupal 9.0.0. + * Use $this->assertSession()->linkExists() instead. */ protected function assertLink($label, $index = 0) { return $this->assertSession()->linkExists($label, $index); } /** + * Passes if a link with the specified label is found. + * + * An optional link index may be passed. + * + * @param string|\Drupal\Component\Render\MarkupInterface $label + * Text between the anchor tags. + * + * @deprecated Scheduled for removal in Drupal 9.0.0. + * Use $this->assertSession()->linkNotExists() instead. + */ + protected function assertNoLink($label) { + return $this->assertSession()->linkNotExists($label); + } + + /** + * Passes if a link containing a given href (part) is found. + * + * @param string $href + * The full or partial value of the 'href' attribute of the anchor tag. + * @param int $index + * Link position counting from zero. + * + * @deprecated Scheduled for removal in Drupal 9.0.0. + * Use $this->assertSession()->LinkByHref() instead. + */ + protected function assertLinkByHref($href, $index = 0) { + $this->assertSession()->linkByHrefExists($href, $index); + } + + /** + * Passes if a link containing a given href (part) is not found. + * + * @param string $href + * The full or partial value of the 'href' attribute of the anchor tag. + * @param int $index + * Link position counting from zero. + * + * @deprecated Scheduled for removal in Drupal 9.0.0. + * Use $this->assertSession()->LinkByHref() instead. + */ + protected function assertNoLinkByHref($href, $index = 0) { + $this->assertSession()->linkByHrefNotExists($href, $index); + } + + /** + * Asserts that a field does not exist with the given ID and value. + * + * @param string $id + * ID of field to assert. + * @param string $value + * (optional) Value for the field, to assert that the field's value on the + * page doesn't match it. You may pass in NULL to skip checking the value, + * while still checking that the field doesn't exist. However, the default + * value ('') asserts that the field value is not an empty string. + * + * @deprecated Scheduled for removal in Drupal 9.0.0. + * Use $this->assertSession()->fieldExists() or + * $this->assertSession()->fieldValueEquals() instead. + */ + protected function assertNoFieldById($id, $value = '') { + if ($this->getSession()->getPage()->findField($id)) { + $this->assertSession()->fieldValueNotEquals($id, (string) $value); + } + else { + $this->assertSession()->fieldNotExists($id); + } + } + + /** + * Passes if the internal browser's URL matches the given path. + * + * @param \Drupal\Core\Url|string $path + * The expected system path or URL. + * + * @deprecated Scheduled for removal in Drupal 9.0.0. + * Use $this->assertSession()->addressEquals() instead. + */ + protected function assertUrl($path) { + $path = "/$path"; + $this->assertSession()->addressEquals($path); + } + + /** * Asserts that a select option in the current page exists. * * @param string $id @@ -217,16 +318,44 @@ protected function assertNoOption($id, $option) { } /** - * Passes if the internal browser's URL matches the given path. + * Passes if the raw text IS found escaped on the loaded page, fail otherwise. * - * @param string $path - * The expected system path. + * Raw text refers to the raw HTML that the page generated. + * + * @param string $raw + * Raw (HTML) string to look for. * * @deprecated Scheduled for removal in Drupal 9.0.0. - * Use $this->assertSession()->addressEquals() instead. + * Use $this->assertSession()->assertEscaped() instead. */ - protected function assertUrl($path) { - $this->assertSession()->addressEquals($path); + protected function assertEscaped($raw) { + $this->assertSession()->assertEscaped($raw); } + /** + * Passes if the raw text IS NOT found escaped on the loaded page, fail otherwise. + * + * Raw text refers to the raw HTML that the page generated. + * + * @param string $raw + * Raw (HTML) string to look for. + * + * @deprecated Scheduled for removal in Drupal 9.0.0. + * Use $this->assertSession()->assertNoEscaped() instead. + */ + protected function assertNoEscaped($raw) { + $this->assertSession()->assertNoEscaped($raw); + } + + /** + * Returns WebAssert object. + * + * @param string $name + * (optional) Name of the session. Defaults to the active session. + * + * @return \Drupal\Tests\WebAssert + * A new web-assert option for asserting the presence of elements with. + */ + abstract public function assertSession($name = NULL); + } diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php index 07e1fa9..854e7af 100644 --- a/core/tests/Drupal/Tests/BrowserTestBase.php +++ b/core/tests/Drupal/Tests/BrowserTestBase.php @@ -573,40 +573,60 @@ protected function prepareRequest() { } /** - * Retrieves a Drupal path or an absolute path. + * Builds an a absolute URL from a system path or a URL object. * * @param string|\Drupal\Core\Url $path - * Drupal path or URL to load into Mink controlled browser. + * A system path or a URL. * @param array $options - * (optional) Options to be forwarded to the url generator. + * Options to be passed to Url::fromUri(). * * @return string - * The retrieved HTML string, also available as $this->getRawContent() + * An absolute URL stsring. */ - protected function drupalGet($path, array $options = array()) { - $options['absolute'] = TRUE; - + protected function buildUrl($path, array $options = array()) { if ($path instanceof Url) { $url_options = $path->getOptions(); $options = $url_options + $options; $path->setOptions($options); - $url = $path->setAbsolute()->toString(); + return $path->setAbsolute()->toString(); } // The URL generator service is not necessarily available yet; e.g., in // interactive installer tests. elseif ($this->container->has('url_generator')) { - if (UrlHelper::isExternal($path)) { - $url = Url::fromUri($path, $options)->toString(); + $force_internal = isset($options['external']) && $options['external'] == FALSE; + if (!$force_internal && UrlHelper::isExternal($path)) { + return Url::fromUri($path, $options)->toString(); } else { - // This is needed for language prefixing. - $options['path_processing'] = TRUE; - $url = Url::fromUri('base:/' . $path, $options)->toString(); + $uri = $path === '' ? 'base:/' : 'base:/' . $path; + // Path processing is needed for language prefixing. Skip it when a + // path that may look like an external URL is being used as internal. + $options['path_processing'] = !$force_internal; + return Url::fromUri($uri, $options) + ->setAbsolute() + ->toString(); } } else { - $url = $this->getAbsoluteUrl($path); + return $this->getAbsoluteUrl($path); } + } + + /** + * Retrieves a Drupal path or an absolute path. + * + * @param string|\Drupal\Core\Url $path + * Drupal path or URL to load into Mink controlled browser. + * @param array $options + * (optional) Options to be forwarded to the url generator. + * + * @return string + * The retrieved HTML string, also available as $this->getRawContent() + */ + protected function drupalGet($path, array $options = array()) { + $options['absolute'] = TRUE; + $url = $this->buildUrl($path, $options); + $session = $this->getSession(); $this->prepareRequest(); @@ -899,6 +919,11 @@ protected function submitForm(array $edit, $submit, $form_html_id = NULL) { // Edit the form values. foreach ($edit as $name => $value) { + // Provide support for 1, 0 for checkboxes instead of TRUE and FALSE. + if (strpos($name, 'name[') === 0) { + $value = (bool) $value; + } + $field = $assert_session->fieldExists($name, $form); $field->setValue($value); } @@ -1680,64 +1705,11 @@ protected function getTextContent() { * The list of elements matching the xpath expression. */ protected function xpath($xpath, array $arguments = []) { - $xpath = $this->buildXPathQuery($xpath, $arguments); + $xpath = $this->assertSession()->buildXPathQuery($xpath, $arguments); return $this->getSession()->getPage()->findAll('xpath', $xpath); } /** - * Builds an XPath query. - * - * Builds an XPath query by replacing placeholders in the query by the value - * of the arguments. - * - * XPath 1.0 (the version supported by libxml2, the underlying XML library - * used by PHP) doesn't support any form of quotation. This function - * simplifies the building of XPath expression. - * - * @param string $xpath - * An XPath query, possibly with placeholders in the form ':name'. - * @param array $args - * An array of arguments with keys in the form ':name' matching the - * placeholders in the query. The values may be either strings or numeric - * values. - * - * @return string - * An XPath query with arguments replaced. - */ - protected function buildXPathQuery($xpath, array $args = array()) { - // Replace placeholders. - foreach ($args as $placeholder => $value) { - // Cast MarkupInterface objects to string. - if (is_object($value)) { - $value = (string) $value; - } - // XPath 1.0 doesn't support a way to escape single or double quotes in a - // string literal. We split double quotes out of the string, and encode - // them separately. - if (is_string($value)) { - // Explode the text at the quote characters. - $parts = explode('"', $value); - - // Quote the parts. - foreach ($parts as &$part) { - $part = '"' . $part . '"'; - } - - // Return the string. - $value = count($parts) > 1 ? 'concat(' . implode(', \'"\', ', $parts) . ')' : $parts[0]; - } - - // Use preg_replace_callback() instead of preg_replace() to prevent the - // regular expression engine from trying to substitute backreferences. - $replacement = function ($matches) use ($value) { - return $value; - }; - $xpath = preg_replace_callback('/' . preg_quote($placeholder) . '\b/', $replacement, $xpath); - } - return $xpath; - } - - /** * Configuration accessor for tests. Returns non-overridden configuration. * * @param string $name diff --git a/core/tests/Drupal/Tests/WebAssert.php b/core/tests/Drupal/Tests/WebAssert.php index ec31fdf..917a00c 100644 --- a/core/tests/Drupal/Tests/WebAssert.php +++ b/core/tests/Drupal/Tests/WebAssert.php @@ -215,6 +215,150 @@ public function linkExists($label, $index = 0, $message = '') { } /** + * Passes if a link with the specified label is not found. + * + * An optional link index may be passed. + * + * @param string|\Drupal\Component\Render\MarkupInterface $label + * Text between the anchor tags. + * @param int $index + * Link position counting from zero. + * @param string $message + * (optional) A message to display with the assertion. Do not translate + * messages: use strtr() to embed variables in the message text, not + * t(). If left blank, a default message will be displayed. + * + * @throws \Behat\Mink\Exception\ExpectationException + * Thrown when element doesn't exist, or the link label is a different one. + */ + public function linkNotExists($label, $message = '') { + // Cast MarkupInterface objects to string. + $label = (string) $label; + $message = ($message ? $message : strtr('Link with label %label found.', ['%label' => $label])); + $links = $this->session->getPage()->findAll('named', ['link', $label]); + if (!empty($links)) { + throw new ExpectationException($message); + } + $this->assert(TRUE, $message); + } + + /** + * {@inheritdoc} + */ + public function fieldValueEquals($field, $value, TraversableElement $container = NULL) { + parent::fieldValueEquals($field, (string) $value, $container); + } + + /** + * Passes if a link containing a given href (part) is found. + * + * @param string $href + * The full or partial value of the 'href' attribute of the anchor tag. + * @param int $index + * Link position counting from zero. + * @param string $message + * (optional) A message to display with the assertion. Do not translate + * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed + * variables in the message text, not t(). If left blank, a default message + * will be displayed. + * + * @throws \Behat\Mink\Exception\ExpectationException + * Thrown when element doesn't exist, or the link label is a different one. + */ + public function linkByHrefExists($href, $index = 0, $message = '') { + // Cast MarkupInterface objects to string. + $xpath = $this->buildXPathQuery('//a[contains(@href, :href)]', [':href' => $href]); + $message = ($message ? $message : strtr('Link containing href %href found.', ['%href' => $href])); + $links = $this->session->getPage()->findAll('xpath', $xpath); + if (empty($links[$index])) { + throw new ExpectationException($message); + } + $this->assert($links[$index] !== NULL, $message); + } + + /** + * Passes if a link containing a given href (part) is not found. + * + * @param string $href + * The full or partial value of the 'href' attribute of the anchor tag. + * @param int $index + * Link position counting from zero. + * @param string $message + * (optional) A message to display with the assertion. Do not translate + * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed + * variables in the message text, not t(). If left blank, a default message + * will be displayed. + * + * @throws \Behat\Mink\Exception\ExpectationException + * Thrown when element doesn't exist, or the link label is a different one. + */ + public function linkByHrefNotExists($href, $index = 0, $message = '') { + // Cast MarkupInterface objects to string. + $xpath = $this->buildXPathQuery('//a[contains(@href, :href)]', [':href' => $href]); + $message = ($message ? $message : strtr('Link containing href %href found.', ['%href' => $href])); + $links = $this->session->getPage()->findAll('xpath', $xpath); + if (!empty($links[$index])) { + throw new ExpectationException($message); + } + else { + $this->assert(TRUE, $message); + } + } + + /** + * Builds an XPath query. + * + * Builds an XPath query by replacing placeholders in the query by the value + * of the arguments. + * + * XPath 1.0 (the version supported by libxml2, the underlying XML library + * used by PHP) doesn't support any form of quotation. This function + * simplifies the building of XPath expression. + * + * @param string $xpath + * An XPath query, possibly with placeholders in the form ':name'. + * @param array $args + * An array of arguments with keys in the form ':name' matching the + * placeholders in the query. The values may be either strings or numeric + * values. + * + * @return string + * An XPath query with arguments replaced. + */ + public function buildXPathQuery($xpath, array $args = array()) { + // Replace placeholders. + foreach ($args as $placeholder => $value) { + // Cast MarkupInterface objects to string. + if (is_object($value)) { + $value = (string) $value; + } + // XPath 1.0 doesn't support a way to escape single or double quotes in a + // string literal. We split double quotes out of the string, and encode + // them separately. + if (is_string($value)) { + // Explode the text at the quote characters. + $parts = explode('"', $value); + + // Quote the parts. + foreach ($parts as &$part) { + $part = '"' . $part . '"'; + } + + // Return the string. + $value = count($parts) > 1 ? 'concat(' . implode(', \'"\', ', $parts) . ')' : $parts[0]; + } + + // Use preg_replace_callback() instead of preg_replace() to prevent the + // regular expression engine from trying to substitute backreferences. + $replacement = function ($matches) use ($value) { + return $value; + }; + $xpath = preg_replace_callback('/' . preg_quote($placeholder) . '\b/', $replacement, $xpath); + } + return $xpath; + } + + /** * Passes if the raw text IS NOT found escaped on the loaded page. * * Raw text refers to the raw HTML that the page generated. @@ -223,7 +367,19 @@ public function linkExists($label, $index = 0, $message = '') { * Raw (HTML) string to look for. */ public function assertNoEscaped($raw) { - $this->pageTextNotContains(Html::escape($raw)); + $this->responseNotContains(Html::escape($raw)); + } + + /** + * Passes if the raw text IS found escaped on the loaded page. + * + * Raw text refers to the raw HTML that the page generated. + * + * @param string $raw + * Raw (HTML) string to look for. + */ + public function assertEscaped($raw) { + $this->responseContains(Html::escape($raw)); } /**