X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=web%2Fcore%2Fmodules%2Fsystem%2Ftests%2Fsrc%2FFunctional%2FSystem%2FThemeTest.php;fp=web%2Fcore%2Fmodules%2Fsystem%2Ftests%2Fsrc%2FFunctional%2FSystem%2FThemeTest.php;h=d1d7880c8fdcf1c50b44bc1f0c63af914b08a3af;hp=0000000000000000000000000000000000000000;hb=0bf8d09d2542548982e81a441b1f16e75873a04f;hpb=74df008bdbb3a11eeea356744f39b802369bda3c diff --git a/web/core/modules/system/tests/src/Functional/System/ThemeTest.php b/web/core/modules/system/tests/src/Functional/System/ThemeTest.php new file mode 100644 index 000000000..d1d7880c8 --- /dev/null +++ b/web/core/modules/system/tests/src/Functional/System/ThemeTest.php @@ -0,0 +1,450 @@ +drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']); + + $this->adminUser = $this->drupalCreateUser(['access administration pages', 'view the administration theme', 'administer themes', 'bypass node access', 'administer blocks']); + $this->drupalLogin($this->adminUser); + $this->node = $this->drupalCreateNode(); + $this->drupalPlaceBlock('local_tasks_block'); + } + + /** + * Test the theme settings form. + */ + public function testThemeSettings() { + // Ensure invalid theme settings form URLs return a proper 404. + $this->drupalGet('admin/appearance/settings/bartik'); + $this->assertResponse(404, 'The theme settings form URL for a uninstalled theme could not be found.'); + $this->drupalGet('admin/appearance/settings/' . $this->randomMachineName()); + $this->assertResponse(404, 'The theme settings form URL for a non-existent theme could not be found.'); + $this->assertTrue(\Drupal::service('theme_installer')->install(['stable'])); + $this->drupalGet('admin/appearance/settings/stable'); + $this->assertResponse(404, 'The theme settings form URL for a hidden theme is unavailable.'); + + // Specify a filesystem path to be used for the logo. + $file = current($this->drupalGetTestFiles('image')); + $file_relative = strtr($file->uri, ['public:/' => PublicStream::basePath()]); + $default_theme_path = 'core/themes/classy'; + + $supported_paths = [ + // Raw stream wrapper URI. + $file->uri => [ + 'form' => file_uri_target($file->uri), + 'src' => file_url_transform_relative(file_create_url($file->uri)), + ], + // Relative path within the public filesystem. + file_uri_target($file->uri) => [ + 'form' => file_uri_target($file->uri), + 'src' => file_url_transform_relative(file_create_url($file->uri)), + ], + // Relative path to a public file. + $file_relative => [ + 'form' => $file_relative, + 'src' => file_url_transform_relative(file_create_url($file->uri)), + ], + // Relative path to an arbitrary file. + 'core/misc/druplicon.png' => [ + 'form' => 'core/misc/druplicon.png', + 'src' => base_path() . 'core/misc/druplicon.png', + ], + // Relative path to a file in a theme. + $default_theme_path . '/logo.svg' => [ + 'form' => $default_theme_path . '/logo.svg', + 'src' => base_path() . $default_theme_path . '/logo.svg', + ], + ]; + foreach ($supported_paths as $input => $expected) { + $edit = [ + 'default_logo' => FALSE, + 'logo_path' => $input, + ]; + $this->drupalPostForm('admin/appearance/settings', $edit, t('Save configuration')); + $this->assertNoText('The custom logo path is invalid.'); + $this->assertFieldByName('logo_path', $expected['form']); + + // Verify logo path examples. + $elements = $this->xpath('//div[contains(@class, :item)]/div[@class=:description]/code', [ + ':item' => 'js-form-item-logo-path', + ':description' => 'description', + ]); + // Expected default values (if all else fails). + $implicit_public_file = 'logo.svg'; + $explicit_file = 'public://logo.svg'; + $local_file = $default_theme_path . '/logo.svg'; + // Adjust for fully qualified stream wrapper URI in public filesystem. + if (file_uri_scheme($input) == 'public') { + $implicit_public_file = file_uri_target($input); + $explicit_file = $input; + $local_file = strtr($input, ['public:/' => PublicStream::basePath()]); + } + // Adjust for fully qualified stream wrapper URI elsewhere. + elseif (file_uri_scheme($input) !== FALSE) { + $explicit_file = $input; + } + // Adjust for relative path within public filesystem. + elseif ($input == file_uri_target($file->uri)) { + $implicit_public_file = $input; + $explicit_file = 'public://' . $input; + $local_file = PublicStream::basePath() . '/' . $input; + } + $this->assertEqual($elements[0]->getText(), $implicit_public_file); + $this->assertEqual($elements[1]->getText(), $explicit_file); + $this->assertEqual($elements[2]->getText(), $local_file); + + // Verify the actual 'src' attribute of the logo being output in a site + // branding block. + $this->drupalPlaceBlock('system_branding_block', ['region' => 'header']); + $this->drupalGet(''); + $elements = $this->xpath('//header//a[@rel=:rel]/img', [ + ':rel' => 'home', + ] + ); + $this->assertEqual($elements[0]->getAttribute('src'), $expected['src']); + } + $unsupported_paths = [ + // Stream wrapper URI to non-existing file. + 'public://whatever.png', + 'private://whatever.png', + 'temporary://whatever.png', + // Bogus stream wrapper URIs. + 'public:/whatever.png', + '://whatever.png', + ':whatever.png', + 'public://', + // Relative path within the public filesystem to non-existing file. + 'whatever.png', + // Relative path to non-existing file in public filesystem. + PublicStream::basePath() . '/whatever.png', + // Semi-absolute path to non-existing file in public filesystem. + '/' . PublicStream::basePath() . '/whatever.png', + // Relative path to arbitrary non-existing file. + 'core/misc/whatever.png', + // Semi-absolute path to arbitrary non-existing file. + '/core/misc/whatever.png', + // Absolute paths to any local file (even if it exists). + \Drupal::service('file_system')->realpath($file->uri), + ]; + $this->drupalGet('admin/appearance/settings'); + foreach ($unsupported_paths as $path) { + $edit = [ + 'default_logo' => FALSE, + 'logo_path' => $path, + ]; + $this->drupalPostForm(NULL, $edit, t('Save configuration')); + $this->assertText('The custom logo path is invalid.'); + } + + // Upload a file to use for the logo. + $edit = [ + 'default_logo' => FALSE, + 'logo_path' => '', + 'files[logo_upload]' => \Drupal::service('file_system')->realpath($file->uri), + ]; + $this->drupalPostForm('admin/appearance/settings', $edit, t('Save configuration')); + + $fields = $this->xpath($this->constructFieldXpath('name', 'logo_path')); + $uploaded_filename = 'public://' . $fields[0]->getValue(); + + $this->drupalPlaceBlock('system_branding_block', ['region' => 'header']); + $this->drupalGet(''); + $elements = $this->xpath('//header//a[@rel=:rel]/img', [ + ':rel' => 'home', + ] + ); + $this->assertEqual($elements[0]->getAttribute('src'), file_url_transform_relative(file_create_url($uploaded_filename))); + + $this->container->get('theme_handler')->install(['bartik']); + + // Ensure only valid themes are listed in the local tasks. + $this->drupalPlaceBlock('local_tasks_block', ['region' => 'header']); + $this->drupalGet('admin/appearance/settings'); + $theme_handler = \Drupal::service('theme_handler'); + $this->assertLink($theme_handler->getName('classy')); + $this->assertLink($theme_handler->getName('bartik')); + $this->assertNoLink($theme_handler->getName('stable')); + + // If a hidden theme is an admin theme it should be viewable. + \Drupal::configFactory()->getEditable('system.theme')->set('admin', 'stable')->save(); + \Drupal::service('router.builder')->rebuildIfNeeded(); + $this->drupalPlaceBlock('local_tasks_block', ['region' => 'header', 'theme' => 'stable']); + $this->drupalGet('admin/appearance/settings'); + $this->assertLink($theme_handler->getName('stable')); + $this->drupalGet('admin/appearance/settings/stable'); + $this->assertResponse(200, 'The theme settings form URL for a hidden theme that is the admin theme is available.'); + + // Ensure default logo and favicons are not triggering custom path + // validation errors if their custom paths are set on the form. + $edit = [ + 'default_logo' => TRUE, + 'logo_path' => 'public://whatever.png', + 'default_favicon' => TRUE, + 'favicon_path' => 'public://whatever.ico', + ]; + $this->drupalPostForm('admin/appearance/settings', $edit, 'Save configuration'); + $this->assertNoText('The custom logo path is invalid.'); + $this->assertNoText('The custom favicon path is invalid.'); + } + + /** + * Test the theme settings logo form. + */ + public function testThemeSettingsLogo() { + // Visit Bartik's theme settings page to replace the logo. + $this->container->get('theme_handler')->install(['bartik']); + $this->drupalGet('admin/appearance/settings/bartik'); + $edit = [ + 'default_logo' => FALSE, + 'logo_path' => 'core/misc/druplicon.png', + ]; + $this->drupalPostForm('admin/appearance/settings/bartik', $edit, t('Save configuration')); + $this->assertFieldByName('default_logo', FALSE); + $this->assertFieldByName('logo_path', 'core/misc/druplicon.png'); + + // Make sure the logo and favicon settings are not available when the file + // module is not enabled. + \Drupal::service('module_installer')->uninstall(['file']); + $this->drupalGet('admin/appearance/settings'); + $this->assertNoText('Logo image settings'); + $this->assertNoText('Shortcut icon settings'); + } + + /** + * Test the administration theme functionality. + */ + public function testAdministrationTheme() { + $this->container->get('theme_handler')->install(['seven']); + + // Install an administration theme and show it on the node admin pages. + $edit = [ + 'admin_theme' => 'seven', + 'use_admin_theme' => TRUE, + ]; + $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); + + $this->drupalGet('admin/config'); + $this->assertRaw('core/themes/seven', 'Administration theme used on an administration page.'); + + $this->drupalGet('node/' . $this->node->id()); + $this->assertRaw('core/themes/classy', 'Site default theme used on node page.'); + + $this->drupalGet('node/add'); + $this->assertRaw('core/themes/seven', 'Administration theme used on the add content page.'); + + $this->drupalGet('node/' . $this->node->id() . '/edit'); + $this->assertRaw('core/themes/seven', 'Administration theme used on the edit content page.'); + + // Disable the admin theme on the node admin pages. + $edit = [ + 'use_admin_theme' => FALSE, + ]; + $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); + + $this->drupalGet('admin/config'); + $this->assertRaw('core/themes/seven', 'Administration theme used on an administration page.'); + + // Ensure that the admin theme is also visible on the 403 page. + $normal_user = $this->drupalCreateUser(['view the administration theme']); + $this->drupalLogin($normal_user); + $this->drupalGet('admin/config'); + $this->assertResponse(403); + $this->assertRaw('core/themes/seven', 'Administration theme used on an administration page.'); + $this->drupalLogin($this->adminUser); + + $this->drupalGet('node/add'); + $this->assertRaw('core/themes/classy', 'Site default theme used on the add content page.'); + + // Reset to the default theme settings. + $edit = [ + 'admin_theme' => '0', + 'use_admin_theme' => FALSE, + ]; + $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); + + $this->drupalGet('admin'); + $this->assertRaw('core/themes/classy', 'Site default theme used on administration page.'); + + $this->drupalGet('node/add'); + $this->assertRaw('core/themes/classy', 'Site default theme used on the add content page.'); + } + + /** + * Test switching the default theme. + */ + public function testSwitchDefaultTheme() { + /** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */ + $theme_handler = \Drupal::service('theme_handler'); + // First, install Stark and set it as the default theme programmatically. + $theme_handler->install(['stark']); + $this->config('system.theme')->set('default', 'stark')->save(); + + // Install Bartik and set it as the default theme. + $theme_handler->install(['bartik']); + $this->drupalGet('admin/appearance'); + $this->clickLink(t('Set as default')); + $this->assertEqual($this->config('system.theme')->get('default'), 'bartik'); + + // Test the default theme on the secondary links (blocks admin page). + $this->drupalGet('admin/structure/block'); + $this->assertText('Bartik(' . t('active tab') . ')', 'Default local task on blocks admin page is the default theme.'); + // Switch back to Stark and test again to test that the menu cache is cleared. + $this->drupalGet('admin/appearance'); + // Stark is the first 'Set as default' link. + $this->clickLink(t('Set as default')); + $this->drupalGet('admin/structure/block'); + $this->assertText('Stark(' . t('active tab') . ')', 'Default local task on blocks admin page has changed.'); + } + + /** + * Test themes can't be installed when the base theme or engine is missing. + * + * Include test for themes that have a missing base theme somewhere further up + * the chain than the immediate base theme. + */ + public function testInvalidTheme() { + // theme_page_test_system_info_alter() un-hides all hidden themes. + $this->container->get('module_installer')->install(['theme_page_test']); + // Clear the system_list() and theme listing cache to pick up the change. + $this->container->get('theme_handler')->reset(); + $this->drupalGet('admin/appearance'); + $this->assertText(t('This theme requires the base theme @base_theme to operate correctly.', ['@base_theme' => 'not_real_test_basetheme'])); + $this->assertText(t('This theme requires the base theme @base_theme to operate correctly.', ['@base_theme' => 'test_invalid_basetheme'])); + $this->assertText(t('This theme requires the theme engine @theme_engine to operate correctly.', ['@theme_engine' => 'not_real_engine'])); + // Check for the error text of a theme with the wrong core version. + $this->assertText("This theme is not compatible with Drupal 8.x. Check that the .info.yml file contains the correct 'core' value."); + // Check for the error text of a theme without a content region. + $this->assertText("This theme is missing a 'content' region."); + } + + /** + * Test uninstalling of themes works. + */ + public function testUninstallingThemes() { + // Install Bartik and set it as the default theme. + \Drupal::service('theme_handler')->install(['bartik']); + // Set up seven as the admin theme. + \Drupal::service('theme_handler')->install(['seven']); + $edit = [ + 'admin_theme' => 'seven', + 'use_admin_theme' => TRUE, + ]; + $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); + $this->drupalGet('admin/appearance'); + $this->clickLink(t('Set as default')); + + // Check that seven cannot be uninstalled as it is the admin theme. + $this->assertNoRaw('Uninstall Seven theme', 'A link to uninstall the Seven theme does not appear on the theme settings page.'); + // Check that bartik cannot be uninstalled as it is the default theme. + $this->assertNoRaw('Uninstall Bartik theme', 'A link to uninstall the Bartik theme does not appear on the theme settings page.'); + // Check that the classy theme cannot be uninstalled as it is a base theme + // of seven and bartik. + $this->assertNoRaw('Uninstall Classy theme', 'A link to uninstall the Classy theme does not appear on the theme settings page.'); + + // Install Stark and set it as the default theme. + \Drupal::service('theme_handler')->install(['stark']); + + $edit = [ + 'admin_theme' => 'stark', + 'use_admin_theme' => TRUE, + ]; + $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); + + // Check that seven can be uninstalled now. + $this->assertRaw('Uninstall Seven theme', 'A link to uninstall the Seven theme does appear on the theme settings page.'); + // Check that the classy theme still cannot be uninstalled as it is a + // base theme of bartik. + $this->assertNoRaw('Uninstall Classy theme', 'A link to uninstall the Classy theme does not appear on the theme settings page.'); + + // Change the default theme to stark, stark is second in the list. + $this->clickLink(t('Set as default'), 1); + + // Check that bartik can be uninstalled now. + $this->assertRaw('Uninstall Bartik theme', 'A link to uninstall the Bartik theme does appear on the theme settings page.'); + + // Check that the classy theme still can't be uninstalled as neither of its + // base themes have been. + $this->assertNoRaw('Uninstall Classy theme', 'A link to uninstall the Classy theme does not appear on the theme settings page.'); + + // Uninstall each of the three themes starting with Bartik. + $this->clickLink(t('Uninstall')); + $this->assertRaw('The Bartik theme has been uninstalled'); + // Seven is the second in the list. + $this->clickLink(t('Uninstall')); + $this->assertRaw('The Seven theme has been uninstalled'); + + // Check that the classy theme still can't be uninstalled as it is hidden. + $this->assertNoRaw('Uninstall Classy theme', 'A link to uninstall the Classy theme does not appear on the theme settings page.'); + } + + /** + * Tests installing a theme and setting it as default. + */ + public function testInstallAndSetAsDefault() { + $this->drupalGet('admin/appearance'); + // Bartik is uninstalled in the test profile and has the third "Install and + // set as default" link. + $this->clickLink(t('Install and set as default'), 2); + // Test the confirmation message. + $this->assertText('Bartik is now the default theme.'); + // Make sure Bartik is now set as the default theme in config. + $this->assertEqual($this->config('system.theme')->get('default'), 'bartik'); + + // This checks for a regression. See https://www.drupal.org/node/2498691. + $this->assertNoText('The bartik theme was not found.'); + + $themes = \Drupal::service('theme_handler')->rebuildThemeData(); + $version = $themes['bartik']->info['version']; + + // Confirm Bartik is indicated as the default theme. + $out = $this->getSession()->getPage()->getContent(); + $this->assertTrue((bool) preg_match('/Bartik ' . preg_quote($version) . '\s{2,}\(default theme\)/', $out)); + } + + /** + * Test the theme settings form when logo and favicon features are disabled. + */ + public function testThemeSettingsNoLogoNoFavicon() { + // Install theme with no logo and no favicon feature. + $this->container->get('theme_handler')->install(['test_theme_settings_features']); + // Visit this theme's settings page. + $this->drupalGet('admin/appearance/settings/test_theme_settings_features'); + $edit = []; + $this->drupalPostForm('admin/appearance/settings/test_theme_settings_features', $edit, t('Save configuration')); + $this->assertText('The configuration options have been saved.'); + } + +}