diff --git a/core/modules/block/src/Tests/Views/DisplayBlockTest.php b/core/modules/block/src/Tests/Views/DisplayBlockTest.php
index 7f3665a..d3fcf28 100644
--- a/core/modules/block/src/Tests/Views/DisplayBlockTest.php
+++ b/core/modules/block/src/Tests/Views/DisplayBlockTest.php
@@ -288,8 +288,13 @@ public function testBlockContextualLinks() {
$response = $this->drupalPost('contextual/render', 'application/json', $post, array('query' => array('destination' => 'test-page')));
$this->assertResponse(200);
$json = Json::decode($response);
- $this->assertIdentical($json[$id], '
');
- $this->assertIdentical($json[$cached_id], '');
+ $this->setRawContent($json[$id]);
+ $result = $this->xpath('//ul/li/a[contains(@href, :href) and text()="Configure block"] | /li/a[contains(@href, "admin/structure/views/view/test_view_block/edit/block_1") and text()="Edit view"]', [':href' => "admin/structure/block/manage/" . $block->id()]);
+ $this->assertTrue($result, 'The contextual link is present.');
+ $this->setRawContent($json[$cached_id]);
+ $result = $this->xpath('//ul/li/a[contains(@href, :href) and text()="Configure block"] | /li/a[contains(@href, "admin/structure/views/view/test_view_block/edit/block_1") and text()="Edit view"]', [':href' => "admin/structure/block/manage/" . $cached_block->id()]);
+ $this->assertTrue($result, 'The contextual link is present.');
+
}
}
diff --git a/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php b/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php
index 20d55cd..8b7ea7b 100644
--- a/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php
+++ b/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php
@@ -95,9 +95,13 @@ function testDifferentPermissions() {
$response = $this->renderContextualLinks($ids, 'node');
$this->assertResponse(200);
$json = Json::decode($response);
- $this->assertIdentical($json[$ids[0]], '');
+ $this->setRawContent($json[$ids[0]]);
+ $result = $this->xpath('//ul/li/a[contains(@href, :href) and text()="Edit"]', [':href' => "node/1/edit"]);
+ $this->assertTrue($result, 'The contextual link is present.');
$this->assertIdentical($json[$ids[1]], '');
- $this->assertIdentical($json[$ids[2]], '');
+ $this->setRawContent($json[$ids[2]]);
+ $result = $this->xpath('//ul/li/a[contains(@href, :href) and text()="Edit"]', [':href' => "node/3/edit"]);
+ $this->assertTrue($result, 'The contextual link is present.');
$this->assertIdentical($json[$ids[3]], '');
// Authenticated user: can access contextual links, cannot edit articles.
diff --git a/core/modules/menu_ui/src/Tests/MenuTest.php b/core/modules/menu_ui/src/Tests/MenuTest.php
index 722548f..039281f 100644
--- a/core/modules/menu_ui/src/Tests/MenuTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuTest.php
@@ -577,7 +577,9 @@ public function testBlockContextualLinks() {
$response = $this->drupalPost('contextual/render', 'application/json', $post, array('query' => array('destination' => 'test-page')));
$this->assertResponse(200);
$json = Json::decode($response);
- $this->assertIdentical($json[$id], '');
+ $this->setRawContent($json[$id]);
+ $result = $this->xpath('//ul/li/a[contains(@href, :block_id) and text()="Configure block"] | /li/a[contains(@href, :custom_menu) and text()="Edit menu"]', [':block_id' => "admin/structure/block/manage/" . $block->id(), ':custom_menu' => "admin/structure/block/manage/" . $custom_menu->id()]);
+ $this->assertTrue($result, 'The contextual link is present.');
}
/**
diff --git a/core/modules/simpletest/src/AssertContentTrait.php b/core/modules/simpletest/src/AssertContentTrait.php
index 100a1c9..2622853 100644
--- a/core/modules/simpletest/src/AssertContentTrait.php
+++ b/core/modules/simpletest/src/AssertContentTrait.php
@@ -822,6 +822,39 @@ protected function assertThemeOutput($callback, array $variables = array(), $exp
}
/**
+ * Asserts themed output.
+ *
+ * @param string $callback
+ * The name of the theme hook to invoke; e.g. 'links' for links.html.twig.
+ * @param string $variables
+ * An array of variables to pass to the theme function.
+ * @param string $expected
+ * The expected themed output string formatted for XPath.
+ * @param string $message
+ * (optional) A message to display with the assertion. Do not translate
+ * messages: use format_string() to embed variables in the message text, not
+ * t(). If left blank, a default message will be displayed.
+ * @param string $group
+ * (optional) The group this message is in, which is displayed in a column
+ * in test output. Use 'Debug' to indicate this is debugging output. Do not
+ * translate this string. Defaults to 'Other'; most tests do not override
+ * this default.
+ *
+ * @return bool
+ * TRUE on pass, FALSE on fail.
+ */
+ protected function assertThemeOutputByXPath($callback, array $variables = array(), $expected = '', $message = '', $group = 'Other') {
+ $output = \Drupal::theme()->render($callback, $variables);
+ if (!$message) {
+ $message = '%callback rendered correctly.';
+ }
+ $message = format_string($message, array('%callback' => 'theme_' . $callback . '()'));
+ $this->setRawContent($output);
+ $result = $this->xpath($expected);
+ return $this->assertTrue($result, $message, $group);
+ }
+
+ /**
* Asserts that a field exists in the current page by the given XPath.
*
* @param string $xpath
diff --git a/core/modules/system/src/Tests/Theme/FunctionsTest.php b/core/modules/system/src/Tests/Theme/FunctionsTest.php
index a9a3dae..6d18ad6 100644
--- a/core/modules/system/src/Tests/Theme/FunctionsTest.php
+++ b/core/modules/system/src/Tests/Theme/FunctionsTest.php
@@ -217,21 +217,20 @@ function testLinks() {
);
$expected_links = '';
- $expected_links .= '';
- $expected_links .= '' . SafeMarkup::checkPlain('A ') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('Plain "text"') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('potentially unsafe text that be escaped') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('Front page') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('Test route') . ' ';
+ $expected_links .= '/ul[contains(@id, "somelinks")] | ';
+ $expected_links .= '/li/a[contains(@href, "' . Url::fromUri('base:a/link')->toString() . '") and text()="' . SafeMarkup::checkPlain('A ') . '"] | ';
+ $expected_links .= '/li[text()="' . SafeMarkup::checkPlain('Plain "text"') . '"] | ';
+ $expected_links .= '/li/span[text()="' . SafeMarkup::checkPlain('potentially unsafe text that be escaped') . '"] | ';
+ $expected_links .= '/li/a[contains(@href, "' . Url::fromRoute('')->toString() . '") and text()="' . SafeMarkup::checkPlain('Front page') . '"] | ';
+ $expected_links .= '/li/a[contains(@href, "' . \Drupal::urlGenerator()->generate('router_test.1') . '") and text()="' . SafeMarkup::checkPlain('Test route') . '"] | ';
$query = array('key' => 'value');
- $expected_links .= '' . SafeMarkup::checkPlain('Query test route') . ' ';
- $expected_links .= ' ';
+ $expected_links .= '/li/a[contains(@href, "' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '") and text()="' . SafeMarkup::checkPlain('Query test route') . '"]';
// Verify that passing a string as heading works.
$variables['heading'] = 'Links heading';
- $expected_heading = 'Links heading ';
+ $expected_heading = '//h2[text()="Links heading"] | ';
$expected = $expected_heading . $expected_links;
- $this->assertThemeOutput('links', $variables, $expected);
+ $this->assertThemeOutputByXPath('links', $variables, $expected);
// Restore the original request's query.
\Drupal::request()->query->replace($original_query);
@@ -242,49 +241,47 @@ function testLinks() {
'level' => 'h3',
'attributes' => array('class' => array('heading')),
);
- $expected_heading = 'Links heading ';
+ $expected_heading = '//h3[text()="Links heading"] | ';
$expected = $expected_heading . $expected_links;
- $this->assertThemeOutput('links', $variables, $expected);
+ $this->assertThemeOutputByXPath('links', $variables, $expected);
// Verify that passing attributes for the heading works.
$variables['heading'] = array('text' => 'Links heading', 'level' => 'h3', 'attributes' => array('id' => 'heading'));
- $expected_heading = 'Links heading ';
+ $expected_heading = '//h3[contains(@id, "heading") and text()="Links heading"] | ';
$expected = $expected_heading . $expected_links;
- $this->assertThemeOutput('links', $variables, $expected);
+ $this->assertThemeOutputByXPath('links', $variables, $expected);
// Verify that passing attributes for the links work.
$variables['links']['plain text']['attributes'] = array(
'class' => array('a/class'),
);
$expected_links = '';
- $expected_links .= '';
- $expected_links .= '' . SafeMarkup::checkPlain('A ') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('Plain "text"') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('potentially unsafe text that be escaped') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('Front page') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('Test route') . ' ';
+ $expected_links .= '/ul[contains(@id, "somelinks")] | ';
+ $expected_links .= '/li/a[contains(@href, "' . Url::fromUri('base:a/link')->toString() . '") and text()="' . SafeMarkup::checkPlain('A ') . '"] | ';
+ $expected_links .= '/li/span[text()="' . SafeMarkup::checkPlain('Plain "text"') . '"] | ';
+ $expected_links .= '/li/span[text()="' . SafeMarkup::checkPlain('potentially unsafe text that be escaped') . '"] | ';
+ $expected_links .= '/li/a[contains(@href, "' . Url::fromRoute('')->toString() . '") and text()="' . SafeMarkup::checkPlain('Front page') . '"] | ';
+ $expected_links .= '/li/a[contains(@href, "' . \Drupal::urlGenerator()->generate('router_test.1') . '") and text()="' . SafeMarkup::checkPlain('Test route') . '"] | ';
$query = array('key' => 'value');
- $expected_links .= '' . SafeMarkup::checkPlain('Query test route') . ' ';
- $expected_links .= ' ';
+ $expected_links .= '/li/a[contains(@href, "' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '") and text()="' . SafeMarkup::checkPlain('Query test route') . '"]';
$expected = $expected_heading . $expected_links;
- $this->assertThemeOutput('links', $variables, $expected);
+ $this->assertThemeOutputByXPath('links', $variables, $expected);
// Verify the data- attributes for setting the "active" class on links.
\Drupal::currentUser()->setAccount(new UserSession(array('uid' => 1)));
$variables['set_active_class'] = TRUE;
$expected_links = '';
- $expected_links .= '';
- $expected_links .= '' . SafeMarkup::checkPlain('A ') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('Plain "text"') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('potentially unsafe text that be escaped') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('Front page') . ' ';
- $expected_links .= '' . SafeMarkup::checkPlain('Test route') . ' ';
+ $expected_links .= '/ul[contains(@id, "somelinks")] | ';
+ $expected_links .= '/li/a[contains(@href, "' . Url::fromUri('base:a/link')->toString() . '") and text()="' . SafeMarkup::checkPlain('A ') . '"] | ';
+ $expected_links .= '/li/span[text()="' . SafeMarkup::checkPlain('Plain "text"') . '"] | ';
+ $expected_links .= '/li/span[text()="' . SafeMarkup::checkPlain('potentially unsafe text that be escaped') . '"] | ';
+ $expected_links .= '/li[contains(@data-drupal-link-system-path, "<front>")]/a[contains(@href, "' . Url::fromRoute('')->toString() . '") and contains(@data-drupal-link-system-path, "<front>") and text()="' . SafeMarkup::checkPlain('Front page') . '"] | ';
+ $expected_links .= '/li[contains(@data-drupal-link-system-path, "router_test/test1")]/a[contains(@href, "' . \Drupal::urlGenerator()->generate('router_test.1') . '") and contains(@data-drupal-link-system-path, "router_test/test1") and text()="' . SafeMarkup::checkPlain('Test route') . '"] | ';
$query = array('key' => 'value');
$encoded_query = SafeMarkup::checkPlain(Json::encode($query));
- $expected_links .= '' . SafeMarkup::checkPlain('Query test route') . ' ';
- $expected_links .= ' ';
+ $expected_links .= '/li[contains(@data-drupal-link-query, "'.$encoded_query.'") and contains(@data-drupal-link-system-path, "router_test/test1")]/a[contains(@href, "' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '") and contains(@data-drupal-link-query, "'.$encoded_query.'") and contains(@data-drupal-link-system-path, "router_test/test1") and text()="' . SafeMarkup::checkPlain('Query test route') . '"]';
$expected = $expected_heading . $expected_links;
- $this->assertThemeOutput('links', $variables, $expected);
+ $this->assertThemeOutputByXPath('links', $variables, $expected);
}
/**
@@ -353,9 +350,9 @@ function testDrupalPreRenderLinks() {
$this->assertEqual($dom->getElementsByTagName('ul')->length, 1, 'One "ul" tag found in the rendered HTML.');
$list_elements = $dom->getElementsByTagName('li');
$this->assertEqual($list_elements->length, 3, 'Three "li" tags found in the rendered HTML.');
- $this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link original', 'First expected link found.');
- $this->assertEqual($list_elements->item(1)->nodeValue, 'First child link', 'Second expected link found.');
- $this->assertEqual($list_elements->item(2)->nodeValue, 'Second child link', 'Third expected link found.');
+ $this->assertEqual(trim($list_elements->item(0)->nodeValue), 'Parent link original', 'First expected link found.');
+ $this->assertEqual(trim($list_elements->item(1)->nodeValue), 'First child link', 'Second expected link found.');
+ $this->assertEqual(trim($list_elements->item(2)->nodeValue), 'Second child link', 'Third expected link found.');
$this->assertIdentical(strpos($html, 'Parent link copy'), FALSE, '"Parent link copy" link not found.');
$this->assertIdentical(strpos($html, 'Third child link'), FALSE, '"Third child link" link not found.');
@@ -371,16 +368,16 @@ function testDrupalPreRenderLinks() {
$this->assertEqual($dom->getElementsByTagName('ul')->length, 1, 'One "ul" tag found in the rendered child HTML.');
$list_elements = $dom->getElementsByTagName('li');
$this->assertEqual($list_elements->length, 2, 'Two "li" tags found in the rendered child HTML.');
- $this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link copy', 'First expected link found.');
- $this->assertEqual($list_elements->item(1)->nodeValue, 'First child link', 'Second expected link found.');
+ $this->assertEqual(trim($list_elements->item(0)->nodeValue), 'Parent link copy', 'First expected link found.');
+ $this->assertEqual(trim($list_elements->item(1)->nodeValue), 'First child link', 'Second expected link found.');
// Then check the parent HTML.
$dom = new \DOMDocument();
$dom->loadHTML($parent_html);
$this->assertEqual($dom->getElementsByTagName('ul')->length, 1, 'One "ul" tag found in the rendered parent HTML.');
$list_elements = $dom->getElementsByTagName('li');
$this->assertEqual($list_elements->length, 2, 'Two "li" tags found in the rendered parent HTML.');
- $this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link original', 'First expected link found.');
- $this->assertEqual($list_elements->item(1)->nodeValue, 'Second child link', 'Second expected link found.');
+ $this->assertEqual(trim($list_elements->item(0)->nodeValue), 'Parent link original', 'First expected link found.');
+ $this->assertEqual(trim($list_elements->item(1)->nodeValue), 'Second child link', 'Second expected link found.');
$this->assertIdentical(strpos($parent_html, 'First child link'), FALSE, '"First child link" link not found.');
$this->assertIdentical(strpos($parent_html, 'Third child link'), FALSE, '"Third child link" link not found.');
}
diff --git a/core/modules/system/templates/links.html.twig b/core/modules/system/templates/links.html.twig
index 21ab95d..479f0dd 100644
--- a/core/modules/system/templates/links.html.twig
+++ b/core/modules/system/templates/links.html.twig
@@ -36,25 +36,25 @@
* @ingroup themeable
*/
#}
-{% if links -%}
- {%- if heading -%}
- {%- if heading.level -%}
+{% if links %}
+ {% if heading %}
+ {% if heading.level %}
<{{ heading.level }}{{ heading.attributes }}>{{ heading.text }}{{ heading.level }}>
- {%- else -%}
+ {% else %}
{{ heading.text }}
- {%- endif -%}
- {%- endif -%}
+ {% endif %}
+ {% endif %}
- {%- for key, item in links -%}
+ {% for key, item in links %}
- {%- if item.link -%}
+ {% if item.link %}
{{ item.link }}
- {%- elseif item.text_attributes -%}
+ {% elseif item.text_attributes %}
{{ item.text }}
- {%- else -%}
+ {% else %}
{{ item.text }}
- {%- endif -%}
+ {% endif %}
- {%- endfor -%}
+ {% endfor %}
-{%- endif %}
+{% endif %}
diff --git a/core/modules/views_ui/src/Tests/DisplayTest.php b/core/modules/views_ui/src/Tests/DisplayTest.php
index 12c06bd..9807187 100644
--- a/core/modules/views_ui/src/Tests/DisplayTest.php
+++ b/core/modules/views_ui/src/Tests/DisplayTest.php
@@ -191,7 +191,9 @@ public function testPageContextualLinks() {
$response = $this->drupalPost('contextual/render', 'application/json', $post, array('query' => array('destination' => 'test-display')));
$this->assertResponse(200);
$json = Json::decode($response);
- $this->assertIdentical($json[$id], '');
+ $this->setRawContent($json[$id]);
+ $result = $this->xpath('//ul/li/a[contains(@href, :href) and text()="Edit view"]', [':href' => "admin/structure/views/view/test_display/edit/page_1"]);
+ $this->assertTrue($result, 'The contextual link is present.');
}
/**
diff --git a/core/themes/classy/templates/navigation/links.html.twig b/core/themes/classy/templates/navigation/links.html.twig
index 5f62ac3..1d91953 100644
--- a/core/themes/classy/templates/navigation/links.html.twig
+++ b/core/themes/classy/templates/navigation/links.html.twig
@@ -34,25 +34,25 @@
* @see template_preprocess_links()
*/
#}
-{% if links -%}
- {%- if heading -%}
- {%- if heading.level -%}
+{% if links %}
+ {% if heading %}
+ {% if heading.level %}
<{{ heading.level }}{{ heading.attributes }}>{{ heading.text }}{{ heading.level }}>
- {%- else -%}
+ {% else %}
{{ heading.text }}
- {%- endif -%}
- {%- endif -%}
+ {% endif %}
+ {% endif %}
- {%- for key, item in links -%}
+ {% for key, item in links %}
- {%- if item.link -%}
+ {% if item.link %}
{{ item.link }}
- {%- elseif item.text_attributes -%}
+ {% elseif item.text_attributes %}
{{ item.text }}
- {%- else -%}
+ {% else %}
{{ item.text }}
- {%- endif -%}
+ {% endif %}
- {%- endfor -%}
+ {% endfor %}
-{%- endif %}
+{% endif %}