summaryrefslogtreecommitdiffstats
path: root/core/modules/system/src/Tests/System/ThemeTest.php
blob: def8de6e921743be5f40cdbcd06da6f96f190d89 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
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
<?php

/**
 * @file
 * Definition of Drupal\system\Tests\System\ThemeTest.
 */

namespace Drupal\system\Tests\System;

use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\simpletest\WebTestBase;

/**
 * Tests the theme interface functionality by enabling and switching themes, and
 * using an administration theme.
 *
 * @group system
 */
class ThemeTest extends WebTestBase {

  /**
   * Modules to enable.
   *
   * @var array
   */
  public static $modules = array('node', 'block', 'file');

  protected function setUp() {
    parent::setUp();

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

    $this->admin_user = $this->drupalCreateUser(array('access administration pages', 'view the administration theme', 'administer themes', 'bypass node access', 'administer blocks'));
    $this->drupalLogin($this->admin_user);
    $this->node = $this->drupalCreateNode();
  }

  /**
   * Test the theme settings form.
   */
  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.');

    // Specify a filesystem path to be used for the logo.
    $file = current($this->drupalGetTestFiles('image'));
    $file_relative = strtr($file->uri, array('public:/' => PublicStream::basePath()));
    $default_theme_path = 'core/themes/classy';

    $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.
      $default_theme_path . '/logo.png' => array(
        'form' => $default_theme_path . '/logo.png',
        'src' => $GLOBALS['base_url'] . '/' . $default_theme_path . '/logo.png',
      ),
    );
    foreach ($supported_paths as $input => $expected) {
      $edit = array(
        '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', array(
        ':item' => 'form-item-logo-path',
        ':description' => 'description',
      ));
      // Expected default values (if all else fails).
      $implicit_public_file = 'logo.png';
      $explicit_file = 'public://logo.png';
      $local_file = $default_theme_path . '/logo.png';
      // 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, array('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((string) $elements[0], $implicit_public_file);
      $this->assertEqual((string) $elements[1], $explicit_file);
      $this->assertEqual((string) $elements[2], $local_file);

      // Verify the actual 'src' attribute of the logo being output.
      $this->drupalGet('');
      $elements = $this->xpath('//header/a[@rel=:rel]/img', array(
          ':rel' => 'home',
        )
      );
      $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.
      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_realpath($file->uri),
    );
    $this->drupalGet('admin/appearance/settings');
    foreach ($unsupported_paths as $path) {
      $edit = array(
        '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 = array(
      'default_logo' => FALSE,
      'logo_path' => '',
      'files[logo_upload]' => drupal_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]['value'];

    $this->drupalGet('');
    $elements = $this->xpath('//header/a[@rel=:rel]/img', array(
        ':rel' => 'home',
      )
    );
    $this->assertEqual($elements[0]['src'], file_create_url($uploaded_filename));
  }

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

    // Install an administration theme and show it on the node admin pages.
    $edit = array(
      '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 = array(
      '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->admin_user);

    $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 = array(
      '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.
   */
  function testSwitchDefaultTheme() {
    // Install Bartik and set it as the default theme.
    \Drupal::service('theme_handler')->install(array('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');
    // Classy is the first 'Set as default' link.
    $this->clickLink(t('Set as default'), 0);
    $this->drupalGet('admin/structure/block');
    $this->assertText('Classy(' . 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.
   */
  function testInvalidTheme() {
    // theme_page_test_system_info_alter() un-hides all hidden themes.
    $this->container->get('module_installer')->install(array('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.', 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')));
  }

  /**
   * 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');
  }
}