diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageSwitchingTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageSwitchingTest.php index ac834d1..3a108af 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageSwitchingTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageSwitchingTest.php @@ -19,7 +19,7 @@ class LanguageSwitchingTest extends WebTestBase { * * @var array */ - public static $modules = array('language', 'block'); + public static $modules = array('language', 'block', 'language_test'); public static function getInfo() { return array( @@ -89,4 +89,101 @@ function testLanguageBlock() { $this->assertIdentical($anchors, array('active' => array('en'), 'inactive' => array('fr')), 'Only the current language anchor is marked as active on the language switcher block.'); } + /** + * Test active class on links when switching language. + */ + function testLinkLanguageSwitchingActiveClass() { + // Add language. + $edit = array( + 'predefined_langcode' => 'fr', + ); + $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); + + // Enable URL language detection and selection. + $edit = array('language_interface[enabled][language-url]' => '1'); + $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings')); + + // Test l(). + $function_name = 'l()'; + + // Test URLs on an English page. + $current_language = 'English'; + $this->drupalGet('language_test/l-active-class'); + + // No lang link should be active. + $langcode = 'none'; + $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', array(':id' => 'no_lang_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + + // en link should be active. + $langcode = 'en'; + $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', array(':id' => 'en_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + + // fr link should NOT be active. + $langcode = 'fr'; + $links = $this->xpath('//a[@id = :id and not(contains(@class, :class))]', array(':id' => 'fr_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is NOT marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + + // Test URLs on a French page. + $current_language = 'French'; + $this->drupalGet('fr/language_test/l-active-class'); + + // No lang link should be active. + $langcode = 'none'; + $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', array(':id' => 'no_lang_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + + // en link should NOT be active. + $langcode = 'en'; + $links = $this->xpath('//a[@id = :id and not(contains(@class, :class))]', array(':id' => 'en_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is NOT marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + + // fr link should be active. + $langcode = 'fr'; + $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', array(':id' => 'fr_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + + // Test theme('link'). + $function_name = "theme('link')"; + + // Test URLs on an English page. + $current_language = 'English'; + $this->drupalGet('language_test/theme-link-active-class'); + + // No lang link should be active. + $langcode = 'none'; + $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', array(':id' => 'no_lang_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + + // en link should be active. + $langcode = 'en'; + $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', array(':id' => 'en_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + + // fr link should NOT be active. + $langcode = 'fr'; + $links = $this->xpath('//a[@id = :id and not(contains(@class, :class))]', array(':id' => 'fr_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is NOT marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + + // Test URLs on a French page. + $current_language = 'French'; + $this->drupalGet('fr/language_test/theme-link-active-class'); + + // No lang link should be active. + $langcode = 'none'; + $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', array(':id' => 'no_lang_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + + // en link should NOT be active. + $langcode = 'en'; + $links = $this->xpath('//a[@id = :id and not(contains(@class, :class))]', array(':id' => 'en_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is NOT marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + + // fr link should be active. + $langcode = 'fr'; + $links = $this->xpath('//a[@id = :id and contains(@class, :class)]', array(':id' => 'fr_link', ':class' => 'active')); + $this->assertTrue(isset($links[0]), t('A link generated by :function to the current :language page with langcode :langcode is marked active.', array(':function' => $function_name, ':language' => $current_language, ':langcode' => $langcode))); + } + } diff --git a/core/modules/language/tests/language_test/language_test.module b/core/modules/language/tests/language_test/language_test.module index cb97937..43457df 100644 --- a/core/modules/language/tests/language_test/language_test.module +++ b/core/modules/language/tests/language_test/language_test.module @@ -109,6 +109,16 @@ function language_test_menu() { 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); + $items['language_test/l-active-class'] = array( + 'page callback' => 'language_test_l_active_class', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + $items['language_test/theme-link-active-class'] = array( + 'page callback' => 'language_test_theme_link_active_class', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); return $items; } @@ -119,3 +129,89 @@ function language_test_menu() { function language_test_subrequest() { return drupal_container()->get('http_kernel')->handle(Request::create('/user'), HttpKernelInterface::SUB_REQUEST); } + +/** + * Page callback: Displays links to the current page, with different langcodes. + * + * Using #theme causes these links to be rendered with theme_link(). + */ +function language_test_theme_link_active_class() { + $languages = language_list(); + return array( + 'no_language' => array( + '#theme' => 'link', + '#text' => t('Link to the current path with no langcode provided.'), + '#path' => current_path(), + '#options' => array( + 'attributes' => array( + 'id' => array('no_lang_link'), + ), + ), + ), + 'fr' => array( + '#theme' => 'link', + '#text' => t('Link to a French version of the current path.'), + '#path' => current_path(), + '#options' => array( + 'language' => $languages['fr'], + 'attributes' => array( + 'id' => array('fr_link'), + ), + ), + ), + 'en' => array( + '#theme' => 'link', + '#text' => t('Link to an English version of the current path.'), + '#path' => current_path(), + '#options' => array( + 'language' => $languages['en'], + 'attributes' => array( + 'id' => array('en_link'), + ), + ), + ), + ); +} + +/** + * Page callback: Displays links to the current page, with different langcodes. + * + * Using #type causes these links to be rendered with l(). + */ +function language_test_l_active_class() { + $languages = language_list(); + return array( + 'no_language' => array( + '#type' => 'link', + '#title' => t('Link to the current path with no langcode provided.'), + '#href' => current_path(), + '#options' => array( + 'attributes' => array( + 'id' => array('no_lang_link'), + ), + ), + ), + 'fr' => array( + '#type' => 'link', + '#title' => t('Link to a French version of the current path.'), + '#href' => current_path(), + '#options' => array( + 'language' => $languages['fr'], + 'attributes' => array( + 'id' => array('fr_link'), + ), + ), + ), + 'en' => array( + '#type' => 'link', + '#title' => t('Link to an English version of the current path.'), + '#href' => current_path(), + '#options' => array( + 'language' => $languages['en'], + 'attributes' => array( + 'id' => array('en_link'), + ), + ), + ), + ); +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php index 72f8af4..1a3fa14 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php @@ -28,18 +28,29 @@ public static function getInfo() { } /** - * Confirms that invalid URLs are filtered. + * Confirms that invalid URLs are filtered in link generating functions. */ - function testLXSS() { + function testLinkXSS() { + // Test l(). $text = $this->randomName(); $path = ""; $link = l($text, $path); $sanitized_path = check_url(url($path)); - $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered', array('@path' => $path))); + $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by l().', array('@path' => $path))); + + // Test #theme. + $link_array = array( + '#theme' => 'link', + '#text' => $this->randomName(), + '#path' => $path, + ); + $theme_link = render($link_array); + $sanitized_path = check_url(url($path)); + $this->assertTrue(strpos($theme_link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by #theme', array('@path' => $path))); } /** - * Tests for active class in l() function. + * Tests for active class in l() and '#theme' => 'link' rendarable arrays. */ function testLinkActiveClass() { $options_no_query = array(); @@ -66,34 +77,50 @@ function testLinkActiveClass() { $links = $this->xpath('//a[@href = :href and not(contains(@class, :class))]', array(':href' => url($path, $options_no_query), ':class' => 'active')); $this->assertTrue(isset($links[0]), 'A link generated by l() to the current page without a query string when the current page has a query string is not marked active.'); - // Test theme('link'). + // Test #theme. $path = 'common-test/theme-link-active-class'; $this->drupalGet($path, $options_no_query); $links = $this->xpath('//a[@href = :href and contains(@class, :class)]', array(':href' => url($path, $options_no_query), ':class' => 'active')); - $this->assertTrue(isset($links[0]), 'A link generated by theme(\'link\') to the current page is marked active.'); + $this->assertTrue(isset($links[0]), 'A link generated by #theme to the current page is marked active.'); $links = $this->xpath('//a[@href = :href and not(contains(@class, :class))]', array(':href' => url($path, $options_query), ':class' => 'active')); - $this->assertTrue(isset($links[0]), 'A link generated by theme(\'link\') to the current page with a query string when the current page has no query string is not marked active.'); + $this->assertTrue(isset($links[0]), 'A link generated by #theme to the current page with a query string when the current page has no query string is not marked active.'); $this->drupalGet($path, $options_query); $links = $this->xpath('//a[@href = :href and contains(@class, :class)]', array(':href' => url($path, $options_query), ':class' => 'active')); - $this->assertTrue(isset($links[0]), 'A link generated by theme(\'link\') to the current page with a query string that matches the current query string is marked active.'); + $this->assertTrue(isset($links[0]), 'A link generated by #theme to the current page with a query string that matches the current query string is marked active.'); $links = $this->xpath('//a[@href = :href and contains(@class, :class)]', array(':href' => url($path, $options_query_reverse), ':class' => 'active')); - $this->assertTrue(isset($links[0]), 'A link generated by theme(\'link\') to the current page with a query string that has matching parameters to the current query string but in a different order is marked active.'); + $this->assertTrue(isset($links[0]), 'A link generated by #theme to the current page with a query string that has matching parameters to the current query string but in a different order is marked active.'); $links = $this->xpath('//a[@href = :href and not(contains(@class, :class))]', array(':href' => url($path, $options_no_query), ':class' => 'active')); - $this->assertTrue(isset($links[0]), 'A link generated by theme(\'link\') to the current page without a query string when the current page has a query string is not marked active.'); + $this->assertTrue(isset($links[0]), 'A link generated by #theme to the current page without a query string when the current page has a query string is not marked active.'); } /** - * Tests for custom class in l() function. + * Tests for custom class in l() and '#theme' => 'link' renderable arrays. */ - function testLCustomClass() { - $class = $this->randomName(); - $link = l($this->randomName(), current_path(), array('attributes' => array('class' => array($class)))); - $this->assertTrue($this->hasClass($link, $class), format_string('Custom class @class is present on link when requested', array('@class' => $class))); + function testLinkCustomClass() { + // Test l(). + $class_l = $this->randomName(); + $link_l = l($this->randomName(), current_path(), array('attributes' => array('class' => array($class_l)))); + $this->assertTrue($this->hasClass($link_l, $class_l), format_string('Custom class @class is present on link when requested by l()', array('@class' => $class_l))); + + // Test #theme. + $class_theme = $this->randomName(); + $theme_link = array( + '#theme' => 'link', + '#text' => $this->randomName(), + '#path' => current_path(), + '#options' => array( + 'attributes' => array( + 'class' => array($class_theme), + ), + ), + ); + $link_theme = render($theme_link); + $this->assertTrue($this->hasClass($link_theme, $class_theme), format_string('Custom class @class is present on link when requested by #theme', array('@class' => $class_theme))); } /**