ThemeTest.php 15 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Contains \Drupal\system\Tests\System\ThemeTest.
6 7 8 9
 */

namespace Drupal\system\Tests\System;

10
use Drupal\Core\StreamWrapper\PublicStream;
11 12 13
use Drupal\simpletest\WebTestBase;

/**
14 15 16 17
 * Tests the theme interface functionality by enabling and switching themes, and
 * using an administration theme.
 *
 * @group system
18 19
 */
class ThemeTest extends WebTestBase {
20

21 22 23 24 25 26 27
  /**
   * A user with administrative permissions.
   *
   * @var \Drupal\user\UserInterface
   */
  protected $adminUser;

28 29 30 31 32
  /**
   * Modules to enable.
   *
   * @var array
   */
33
  public static $modules = ['node', 'block', 'file'];
34

35
  protected function setUp() {
36
    parent::setUp();
37 38 39

    $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));

40 41
    $this->adminUser = $this->drupalCreateUser(array('access administration pages', 'view the administration theme', 'administer themes', 'bypass node access', 'administer blocks'));
    $this->drupalLogin($this->adminUser);
42
    $this->node = $this->drupalCreateNode();
43
    $this->drupalPlaceBlock('local_tasks_block');
44 45 46 47 48 49
  }

  /**
   * Test the theme settings form.
   */
  function testThemeSettings() {
50 51
    // Ensure invalid theme settings form URLs return a proper 404.
    $this->drupalGet('admin/appearance/settings/bartik');
52
    $this->assertResponse(404, 'The theme settings form URL for a uninstalled theme could not be found.');
53
    $this->drupalGet('admin/appearance/settings/' . $this->randomMachineName());
54 55
    $this->assertResponse(404, 'The theme settings form URL for a non-existent theme could not be found.');

56 57
    // Specify a filesystem path to be used for the logo.
    $file = current($this->drupalGetTestFiles('image'));
58
    $file_relative = strtr($file->uri, array('public:/' => PublicStream::basePath()));
59
    $default_theme_path = 'core/themes/classy';
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

    $supported_paths = array(
      // Raw stream wrapper URI.
      $file->uri => array(
        'form' => file_uri_target($file->uri),
        'src' => file_create_url($file->uri),
      ),
      // Relative path within the public filesystem.
      file_uri_target($file->uri) => array(
        'form' => file_uri_target($file->uri),
        'src' => file_create_url($file->uri),
      ),
      // Relative path to a public file.
      $file_relative => array(
        'form' => $file_relative,
        'src' => file_create_url($file->uri),
      ),
      // Relative path to an arbitrary file.
      'core/misc/druplicon.png' => array(
        'form' => 'core/misc/druplicon.png',
        'src' => $GLOBALS['base_url'] . '/' . 'core/misc/druplicon.png',
      ),
      // Relative path to a file in a theme.
83 84 85
      $default_theme_path . '/logo.svg' => array(
        'form' => $default_theme_path . '/logo.svg',
        'src' => $GLOBALS['base_url'] . '/' . $default_theme_path . '/logo.svg',
86 87 88 89 90 91 92
      ),
    );
    foreach ($supported_paths as $input => $expected) {
      $edit = array(
        'default_logo' => FALSE,
        'logo_path' => $input,
      );
93
      $this->drupalPostForm('admin/appearance/settings', $edit, t('Save configuration'));
94 95 96 97 98 99 100 101 102
      $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', array(
        ':item' => 'form-item-logo-path',
        ':description' => 'description',
      ));
      // Expected default values (if all else fails).
103 104 105
      $implicit_public_file = 'logo.svg';
      $explicit_file = 'public://logo.svg';
      $local_file = $default_theme_path . '/logo.svg';
106 107 108 109
      // 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;
110
        $local_file = strtr($input, array('public:/' => PublicStream::basePath()));
111 112 113 114 115 116 117 118 119
      }
      // 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;
120
        $local_file = PublicStream::basePath() . '/' . $input;
121 122 123 124 125
      }
      $this->assertEqual((string) $elements[0], $implicit_public_file);
      $this->assertEqual((string) $elements[1], $explicit_file);
      $this->assertEqual((string) $elements[2], $local_file);

126 127 128
      // Verify the actual 'src' attribute of the logo being output in a site
      // branding block.
      $this->drupalPlaceBlock('system_branding_block', ['region' => 'header']);
129
      $this->drupalGet('');
130
      $elements = $this->xpath('//header//a[@rel=:rel]/img', array(
131 132 133
          ':rel' => 'home',
        )
      );
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
      $this->assertEqual((string) $elements[0]['src'], $expected['src']);
    }
    $unsupported_paths = array(
      // 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.
149
      PublicStream::basePath() . '/whatever.png',
150
      // Semi-absolute path to non-existing file in public filesystem.
151
      '/' . PublicStream::basePath() . '/whatever.png',
152 153 154 155 156 157 158 159 160 161 162 163 164
      // 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_realpath($file->uri),
    );
    $this->drupalGet('admin/appearance/settings');
    foreach ($unsupported_paths as $path) {
      $edit = array(
        'default_logo' => FALSE,
        'logo_path' => $path,
      );
165
      $this->drupalPostForm(NULL, $edit, t('Save configuration'));
166 167 168 169 170 171 172 173 174
      $this->assertText('The custom logo path is invalid.');
    }

    // Upload a file to use for the logo.
    $edit = array(
      'default_logo' => FALSE,
      'logo_path' => '',
      'files[logo_upload]' => drupal_realpath($file->uri),
    );
175
    $this->drupalPostForm('admin/appearance/settings', $edit, t('Save configuration'));
176 177 178 179

    $fields = $this->xpath($this->constructFieldXpath('name', 'logo_path'));
    $uploaded_filename = 'public://' . $fields[0]['value'];

180
    $this->drupalPlaceBlock('system_branding_block', ['region' => 'header']);
181
    $this->drupalGet('');
182
    $elements = $this->xpath('//header//a[@rel=:rel]/img', array(
183 184 185
        ':rel' => 'home',
      )
    );
186 187 188 189 190 191 192
    $this->assertEqual($elements[0]['src'], file_create_url($uploaded_filename));
  }

  /**
   * Test the administration theme functionality.
   */
  function testAdministrationTheme() {
193
    $this->container->get('theme_handler')->install(array('seven'));
194

195
    // Install an administration theme and show it on the node admin pages.
196 197
    $edit = array(
      'admin_theme' => 'seven',
198
      'use_admin_theme' => TRUE,
199
    );
200
    $this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
201 202

    $this->drupalGet('admin/config');
203
    $this->assertRaw('core/themes/seven', 'Administration theme used on an administration page.');
204

205
    $this->drupalGet('node/' . $this->node->id());
206
    $this->assertRaw('core/themes/classy', 'Site default theme used on node page.');
207 208

    $this->drupalGet('node/add');
209
    $this->assertRaw('core/themes/seven', 'Administration theme used on the add content page.');
210

211
    $this->drupalGet('node/' . $this->node->id() . '/edit');
212
    $this->assertRaw('core/themes/seven', 'Administration theme used on the edit content page.');
213 214 215

    // Disable the admin theme on the node admin pages.
    $edit = array(
216
      'use_admin_theme' => FALSE,
217
    );
218
    $this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
219 220

    $this->drupalGet('admin/config');
221
    $this->assertRaw('core/themes/seven', 'Administration theme used on an administration page.');
222

223 224 225 226 227 228
    // 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.');
229
    $this->drupalLogin($this->adminUser);
230

231
    $this->drupalGet('node/add');
232
    $this->assertRaw('core/themes/classy', 'Site default theme used on the add content page.');
233 234 235 236

    // Reset to the default theme settings.
    $edit = array(
      'admin_theme' => '0',
237
      'use_admin_theme' => FALSE,
238
    );
239
    $this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
240 241

    $this->drupalGet('admin');
242
    $this->assertRaw('core/themes/classy', 'Site default theme used on administration page.');
243 244

    $this->drupalGet('node/add');
245
    $this->assertRaw('core/themes/classy', 'Site default theme used on the add content page.');
246 247 248 249 250 251
  }

  /**
   * Test switching the default theme.
   */
  function testSwitchDefaultTheme() {
252 253
    // Install Bartik and set it as the default theme.
    \Drupal::service('theme_handler')->install(array('bartik'));
254
    $this->drupalGet('admin/appearance');
255
    $this->clickLink(t('Set as default'));
256
    $this->assertEqual($this->config('system.theme')->get('default'), 'bartik');
257 258 259

    // Test the default theme on the secondary links (blocks admin page).
    $this->drupalGet('admin/structure/block');
260
    $this->assertText('Bartik(' . t('active tab') . ')', 'Default local task on blocks admin page is the default theme.');
261 262
    // Switch back to Stark and test again to test that the menu cache is cleared.
    $this->drupalGet('admin/appearance');
263 264
    // Classy is the first 'Set as default' link.
    $this->clickLink(t('Set as default'), 0);
265
    $this->drupalGet('admin/structure/block');
266
    $this->assertText('Classy(' . t('active tab') . ')', 'Default local task on blocks admin page has changed.');
267
  }
268 269

  /**
270
   * Test themes can't be installed when the base theme or engine is missing.
271 272
   */
  function testInvalidTheme() {
273
    // theme_page_test_system_info_alter() un-hides all hidden themes.
274
    $this->container->get('module_installer')->install(array('theme_page_test'));
275 276
    // Clear the system_list() and theme listing cache to pick up the change.
    $this->container->get('theme_handler')->reset();
277
    $this->drupalGet('admin/appearance');
278 279
    $this->assertText(t('This theme requires the base theme @base_theme to operate correctly.', array('@base_theme' => 'not_real_test_basetheme')));
    $this->assertText(t('This theme requires the theme engine @theme_engine to operate correctly.', array('@theme_engine' => 'not_real_engine')));
280 281 282 283
    // 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.");
284
  }
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344

  /**
   * Test uninstalling of themes works.
   */
  function testUninstallingThemes() {
    // Install Bartik and set it as the default theme.
    \Drupal::service('theme_handler')->install(array('bartik'));
    // Set up seven as the admin theme.
    \Drupal::service('theme_handler')->install(array('seven'));
    $edit = array(
      '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(array('stark'));

    $edit = array(
      '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 third in the list.
    $this->clickLink(t('Set as default'), 2);

    // 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 it's
    // 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 <em class="placeholder">Bartik</em> theme has been uninstalled');
    // Seven is the second in the list.
    $this->clickLink(t('Uninstall'));
    $this->assertRaw('The <em class="placeholder">Seven</em> theme has been uninstalled');
    // Now uninstall classy.
    $this->clickLink(t('Uninstall'));
    $this->assertRaw('The <em class="placeholder">Classy</em> theme has been uninstalled');
  }
345 346 347 348 349 350

  /**
   * Tests installing a theme and setting it as default.
   */
  function testInstallAndSetAsDefault() {
    $this->drupalGet('admin/appearance');
351
    // Bartik is uninstalled in the test profile and has the third "Install and
352
    // set as default" link.
353
    $this->clickLink(t('Install and set as default'), 2);
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
    // 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.
    $this->assertTextPattern('/Bartik ' . preg_quote($version) . '\s{2,}\(default theme\)/');
  }

369
}