annotate core/modules/toolbar/tests/src/Functional/ToolbarAdminMenuTest.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Tests\toolbar\Functional;
Chris@0 4
Chris@0 5 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
Chris@0 6 use Drupal\Core\Language\LanguageInterface;
Chris@0 7 use Drupal\Core\Url;
Chris@0 8 use Drupal\language\Entity\ConfigurableLanguage;
Chris@0 9 use Drupal\Tests\BrowserTestBase;
Chris@0 10 use Drupal\user\RoleInterface;
Chris@0 11
Chris@0 12 /**
Chris@0 13 * Tests the caching of the admin menu subtree items.
Chris@0 14 *
Chris@0 15 * The cache of the admin menu subtree items will be invalidated if the
Chris@0 16 * following hooks are invoked.
Chris@0 17 *
Chris@0 18 * toolbar_modules_enabled()
Chris@0 19 * toolbar_modules_disabled()
Chris@0 20 * toolbar_menu_link_update()
Chris@0 21 * toolbar_user_update()
Chris@0 22 * toolbar_user_role_update()
Chris@0 23 *
Chris@0 24 * Each hook invocation is simulated and then the previous hash of the admin
Chris@0 25 * menu subtrees is compared to the new hash.
Chris@0 26 *
Chris@0 27 * @group toolbar
Chris@0 28 */
Chris@0 29 class ToolbarAdminMenuTest extends BrowserTestBase {
Chris@0 30
Chris@0 31 /**
Chris@0 32 * A user with permission to access the administrative toolbar.
Chris@0 33 *
Chris@0 34 * @var \Drupal\user\UserInterface
Chris@0 35 */
Chris@0 36 protected $adminUser;
Chris@0 37
Chris@0 38 /**
Chris@0 39 * A second user with permission to access the administrative toolbar.
Chris@0 40 *
Chris@0 41 * @var \Drupal\user\UserInterface
Chris@0 42 */
Chris@0 43 protected $adminUser2;
Chris@0 44
Chris@0 45 /**
Chris@0 46 * The current admin menu subtrees hash for adminUser.
Chris@0 47 *
Chris@0 48 * @var string
Chris@0 49 */
Chris@0 50 protected $hash;
Chris@0 51
Chris@0 52 /**
Chris@0 53 * Modules to enable.
Chris@0 54 *
Chris@0 55 * @var array
Chris@0 56 */
Chris@0 57 public static $modules = ['node', 'block', 'menu_ui', 'user', 'taxonomy', 'toolbar', 'language', 'test_page_test', 'locale'];
Chris@0 58
Chris@0 59 protected function setUp() {
Chris@0 60 parent::setUp();
Chris@0 61
Chris@0 62 $perms = [
Chris@0 63 'access toolbar',
Chris@0 64 'access administration pages',
Chris@0 65 'administer site configuration',
Chris@0 66 'bypass node access',
Chris@0 67 'administer themes',
Chris@0 68 'administer nodes',
Chris@0 69 'access content overview',
Chris@0 70 'administer blocks',
Chris@0 71 'administer menu',
Chris@0 72 'administer modules',
Chris@0 73 'administer permissions',
Chris@0 74 'administer users',
Chris@0 75 'access user profiles',
Chris@0 76 'administer taxonomy',
Chris@0 77 'administer languages',
Chris@0 78 'translate interface',
Chris@0 79 ];
Chris@0 80
Chris@0 81 // Create an administrative user and log it in.
Chris@0 82 $this->adminUser = $this->drupalCreateUser($perms);
Chris@0 83 $this->adminUser2 = $this->drupalCreateUser($perms);
Chris@0 84
Chris@0 85 $this->drupalLogin($this->adminUser);
Chris@0 86
Chris@0 87 $this->drupalGet('test-page');
Chris@0 88 $this->assertResponse(200);
Chris@0 89
Chris@0 90 // Assert that the toolbar is present in the HTML.
Chris@0 91 $this->assertRaw('id="toolbar-administration"');
Chris@0 92
Chris@0 93 // Store the adminUser admin menu subtrees hash for comparison later.
Chris@0 94 $this->hash = $this->getSubtreesHash();
Chris@0 95 }
Chris@0 96
Chris@0 97 /**
Chris@0 98 * Tests the toolbar_modules_installed() and toolbar_modules_uninstalled() hook
Chris@0 99 * implementations.
Chris@0 100 */
Chris@0 101 public function testModuleStatusChangeSubtreesHashCacheClear() {
Chris@0 102 // Uninstall a module.
Chris@0 103 $edit = [];
Chris@0 104 $edit['uninstall[taxonomy]'] = TRUE;
Chris@0 105 $this->drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall'));
Chris@0 106 // Confirm the uninstall form.
Chris@0 107 $this->drupalPostForm(NULL, [], t('Uninstall'));
Chris@0 108 $this->rebuildContainer();
Chris@0 109
Chris@0 110 // Assert that the subtrees hash has been altered because the subtrees
Chris@0 111 // structure changed.
Chris@0 112 $this->assertDifferentHash();
Chris@0 113
Chris@0 114 // Enable a module.
Chris@0 115 $edit = [];
Chris@0 116 $edit['modules[taxonomy][enable]'] = TRUE;
Chris@0 117 $this->drupalPostForm('admin/modules', $edit, t('Install'));
Chris@0 118 $this->rebuildContainer();
Chris@0 119
Chris@0 120 // Assert that the subtrees hash has been altered because the subtrees
Chris@0 121 // structure changed.
Chris@0 122 $this->assertDifferentHash();
Chris@0 123 }
Chris@0 124
Chris@0 125 /**
Chris@0 126 * Tests toolbar cache tags implementation.
Chris@0 127 */
Chris@0 128 public function testMenuLinkUpdateSubtreesHashCacheClear() {
Chris@0 129 // The ID of a (any) admin menu link.
Chris@0 130 $admin_menu_link_id = 'system.admin_config_development';
Chris@0 131
Chris@0 132 // Disable the link.
Chris@0 133 $edit = [];
Chris@0 134 $edit['enabled'] = FALSE;
Chris@0 135 $this->drupalPostForm("admin/structure/menu/link/" . $admin_menu_link_id . "/edit", $edit, t('Save'));
Chris@0 136 $this->assertResponse(200);
Chris@0 137 $this->assertText('The menu link has been saved.');
Chris@0 138
Chris@0 139 // Assert that the subtrees hash has been altered because the subtrees
Chris@0 140 // structure changed.
Chris@0 141 $this->assertDifferentHash();
Chris@0 142 }
Chris@0 143
Chris@0 144 /**
Chris@0 145 * Exercises the toolbar_user_role_update() and toolbar_user_update() hook
Chris@0 146 * implementations.
Chris@0 147 */
Chris@0 148 public function testUserRoleUpdateSubtreesHashCacheClear() {
Chris@0 149 // Find the new role ID.
Chris@0 150 $all_rids = $this->adminUser->getRoles();
Chris@0 151 unset($all_rids[array_search(RoleInterface::AUTHENTICATED_ID, $all_rids)]);
Chris@0 152 $rid = reset($all_rids);
Chris@0 153
Chris@0 154 $edit = [];
Chris@0 155 $edit[$rid . '[administer taxonomy]'] = FALSE;
Chris@0 156 $this->drupalPostForm('admin/people/permissions', $edit, t('Save permissions'));
Chris@0 157
Chris@0 158 // Assert that the subtrees hash has been altered because the subtrees
Chris@0 159 // structure changed.
Chris@0 160 $this->assertDifferentHash();
Chris@0 161
Chris@0 162 // Test that assigning a user an extra role only affects that single user.
Chris@0 163 // Get the hash for a second user.
Chris@0 164 $this->drupalLogin($this->adminUser2);
Chris@0 165 $this->drupalGet('test-page');
Chris@0 166 $this->assertResponse(200);
Chris@0 167
Chris@0 168 // Assert that the toolbar is present in the HTML.
Chris@0 169 $this->assertRaw('id="toolbar-administration"');
Chris@0 170
Chris@0 171 $admin_user_2_hash = $this->getSubtreesHash();
Chris@0 172
Chris@0 173 // Log in the first admin user again.
Chris@0 174 $this->drupalLogin($this->adminUser);
Chris@0 175 $this->drupalGet('test-page');
Chris@0 176 $this->assertResponse(200);
Chris@0 177
Chris@0 178 // Assert that the toolbar is present in the HTML.
Chris@0 179 $this->assertRaw('id="toolbar-administration"');
Chris@0 180
Chris@0 181 $this->hash = $this->getSubtreesHash();
Chris@0 182
Chris@0 183 $rid = $this->drupalCreateRole(['administer content types']);
Chris@0 184
Chris@0 185 // Assign the role to the user.
Chris@0 186 $this->drupalPostForm('user/' . $this->adminUser->id() . '/edit', ["roles[$rid]" => $rid], t('Save'));
Chris@0 187 $this->assertText(t('The changes have been saved.'));
Chris@0 188
Chris@0 189 // Assert that the subtrees hash has been altered because the subtrees
Chris@0 190 // structure changed.
Chris@0 191 $this->assertDifferentHash();
Chris@0 192
Chris@0 193 // Log in the second user again and assert that their subtrees hash did not
Chris@0 194 // change.
Chris@0 195 $this->drupalLogin($this->adminUser2);
Chris@0 196
Chris@0 197 // Request a new page to refresh the drupalSettings object.
Chris@0 198 $this->drupalGet('test-page');
Chris@0 199 $this->assertResponse(200);
Chris@0 200 $new_subtree_hash = $this->getSubtreesHash();
Chris@0 201
Chris@0 202 // Assert that the old admin menu subtree hash and the new admin menu
Chris@0 203 // subtree hash are the same.
Chris@0 204 $this->assertTrue($new_subtree_hash, 'A valid hash value for the admin menu subtrees was created.');
Chris@0 205 $this->assertEqual($admin_user_2_hash, $new_subtree_hash, 'The user-specific subtree menu hash has not been updated.');
Chris@0 206 }
Chris@0 207
Chris@0 208 /**
Chris@0 209 * Tests that changes to a user account by another user clears the changed
Chris@0 210 * account's toolbar cached, not the user's who took the action.
Chris@0 211 */
Chris@0 212 public function testNonCurrentUserAccountUpdates() {
Chris@0 213 $admin_user_id = $this->adminUser->id();
Chris@0 214 $this->hash = $this->getSubtreesHash();
Chris@0 215
Chris@0 216 // adminUser2 will add a role to adminUser.
Chris@0 217 $this->drupalLogin($this->adminUser2);
Chris@0 218 $rid = $this->drupalCreateRole(['administer content types']);
Chris@0 219
Chris@0 220 // Get the subtree hash for adminUser2 to check later that it has not
Chris@0 221 // changed. Request a new page to refresh the drupalSettings object.
Chris@0 222 $this->drupalGet('test-page');
Chris@0 223 $this->assertResponse(200);
Chris@0 224 $admin_user_2_hash = $this->getSubtreesHash();
Chris@0 225
Chris@0 226 // Assign the role to the user.
Chris@0 227 $this->drupalPostForm('user/' . $admin_user_id . '/edit', ["roles[$rid]" => $rid], t('Save'));
Chris@0 228 $this->assertText(t('The changes have been saved.'));
Chris@0 229
Chris@0 230 // Log in adminUser and assert that the subtrees hash has changed.
Chris@0 231 $this->drupalLogin($this->adminUser);
Chris@0 232 $this->assertDifferentHash();
Chris@0 233
Chris@0 234 // Log in adminUser2 to check that its subtrees hash has not changed.
Chris@0 235 $this->drupalLogin($this->adminUser2);
Chris@0 236 $new_subtree_hash = $this->getSubtreesHash();
Chris@0 237
Chris@0 238 // Assert that the old adminUser subtree hash and the new adminUser
Chris@0 239 // subtree hash are the same.
Chris@0 240 $this->assertTrue($new_subtree_hash, 'A valid hash value for the admin menu subtrees was created.');
Chris@0 241 $this->assertEqual($admin_user_2_hash, $new_subtree_hash, 'The user-specific subtree menu hash has not been updated.');
Chris@0 242 }
Chris@0 243
Chris@0 244 /**
Chris@0 245 * Tests that toolbar cache is cleared when string translations are made.
Chris@0 246 */
Chris@0 247 public function testLocaleTranslationSubtreesHashCacheClear() {
Chris@0 248 $admin_user = $this->adminUser;
Chris@0 249 // User to translate and delete string.
Chris@0 250 $translate_user = $this->drupalCreateUser(['translate interface', 'access administration pages']);
Chris@0 251
Chris@0 252 // Create a new language with the langcode 'xx'.
Chris@0 253 $langcode = 'xx';
Chris@0 254 // The English name for the language. This will be translated.
Chris@0 255 $name = $this->randomMachineName(16);
Chris@0 256 // This will be the translation of $name.
Chris@0 257 $translation = $this->randomMachineName(16);
Chris@0 258
Chris@0 259 // Add custom language.
Chris@0 260 $this->drupalLogin($admin_user);
Chris@0 261 $edit = [
Chris@0 262 'predefined_langcode' => 'custom',
Chris@0 263 'langcode' => $langcode,
Chris@0 264 'label' => $name,
Chris@0 265 'direction' => LanguageInterface::DIRECTION_LTR,
Chris@0 266 ];
Chris@0 267 $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
Chris@0 268 t($name, [], ['langcode' => $langcode]);
Chris@0 269 // Reset locale cache.
Chris@0 270 $this->container->get('string_translation')->reset();
Chris@0 271 $this->assertRaw('"edit-languages-' . $langcode . '-weight"', 'Language code found.');
Chris@0 272 $this->assertText(t($name), 'Test language added.');
Chris@0 273
Chris@0 274 // Have the adminUser request a page in the new language.
Chris@0 275 $this->drupalGet($langcode . '/test-page');
Chris@0 276 $this->assertResponse(200);
Chris@0 277
Chris@0 278 // Get a baseline hash for the admin menu subtrees before translating one
Chris@0 279 // of the menu link items.
Chris@0 280 $original_subtree_hash = $this->getSubtreesHash();
Chris@0 281 $this->assertTrue($original_subtree_hash, 'A valid hash value for the admin menu subtrees was created.');
Chris@0 282 $this->drupalLogout();
Chris@0 283
Chris@0 284 // Translate the string 'Search and metadata' in the xx language. This
Chris@0 285 // string appears in a link in the admin menu subtrees. Changing the string
Chris@0 286 // should create a new menu hash if the toolbar subtrees cache is correctly
Chris@0 287 // invalidated.
Chris@0 288 $this->drupalLogin($translate_user);
Chris@0 289 $search = [
Chris@0 290 'string' => 'Search and metadata',
Chris@0 291 'langcode' => $langcode,
Chris@0 292 'translation' => 'untranslated',
Chris@0 293 ];
Chris@0 294 $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter'));
Chris@0 295 $this->assertNoText(t('No strings available'));
Chris@0 296 $this->assertText($name, 'Search found the string as untranslated.');
Chris@0 297
Chris@0 298 // Assume this is the only result.
Chris@0 299 // Translate the string to a random string.
Chris@0 300 $textarea = current($this->xpath('//textarea'));
Chris@0 301 $lid = (string) $textarea->getAttribute('name');
Chris@0 302 $edit = [
Chris@0 303 $lid => $translation,
Chris@0 304 ];
Chris@0 305 $this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations'));
Chris@0 306 $this->assertText(t('The strings have been saved.'), 'The strings have been saved.');
Chris@18 307 $this->assertUrl(Url::fromRoute('locale.translate_page', [], ['absolute' => TRUE])->toString(), [], 'Correct page redirection.');
Chris@0 308 $this->drupalLogout();
Chris@0 309
Chris@0 310 // Log in the adminUser. Check the admin menu subtrees hash now that one
Chris@0 311 // of the link items in the Structure tree (Menus) has had its text
Chris@0 312 // translated.
Chris@0 313 $this->drupalLogin($admin_user);
Chris@0 314 // Have the adminUser request a page in the new language.
Chris@0 315 $this->drupalGet($langcode . '/test-page');
Chris@0 316 $this->assertResponse(200);
Chris@0 317 $new_subtree_hash = $this->getSubtreesHash();
Chris@0 318
Chris@0 319 // Assert that the old admin menu subtrees hash and the new admin menu
Chris@0 320 // subtrees hash are different.
Chris@0 321 $this->assertTrue($new_subtree_hash, 'A valid hash value for the admin menu subtrees was created.');
Chris@0 322 $this->assertNotEqual($original_subtree_hash, $new_subtree_hash, 'The user-specific subtree menu hash has been updated.');
Chris@0 323 }
Chris@0 324
Chris@0 325 /**
Chris@0 326 * Tests that the 'toolbar/subtrees/{hash}' is reachable and correct.
Chris@0 327 */
Chris@0 328 public function testSubtreesJsonRequest() {
Chris@0 329 $admin_user = $this->adminUser;
Chris@0 330 $this->drupalLogin($admin_user);
Chris@0 331 // Request a new page to refresh the drupalSettings object.
Chris@0 332 $subtrees_hash = $this->getSubtreesHash();
Chris@0 333
Chris@0 334 $this->drupalGet('toolbar/subtrees/' . $subtrees_hash, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']], ['X-Requested-With: XMLHttpRequest']);
Chris@0 335 $ajax_result = json_decode($this->getSession()->getPage()->getContent(), TRUE);
Chris@0 336 $this->assertEqual($ajax_result[0]['command'], 'setToolbarSubtrees', 'Subtrees response uses the correct command.');
Chris@0 337 $this->assertEqual(array_keys($ajax_result[0]['subtrees']), ['system-admin_content', 'system-admin_structure', 'system-themes_page', 'system-modules_list', 'system-admin_config', 'entity-user-collection', 'front'], 'Correct subtrees returned.');
Chris@0 338 }
Chris@0 339
Chris@0 340 /**
Chris@0 341 * Test that subtrees hashes vary by the language of the page.
Chris@0 342 */
Chris@0 343 public function testLanguageSwitching() {
Chris@0 344 // Create a new language with the langcode 'xx'.
Chris@0 345 $langcode = 'xx';
Chris@0 346 $language = ConfigurableLanguage::createFromLangcode($langcode);
Chris@0 347 $language->save();
Chris@0 348 // The language path processor is just registered for more than one
Chris@0 349 // configured language, so rebuild the container now that we are
Chris@0 350 // multilingual.
Chris@0 351 $this->rebuildContainer();
Chris@0 352
Chris@0 353 // Get a page with the new language langcode in the URL.
Chris@0 354 $this->drupalGet('test-page', ['language' => $language]);
Chris@0 355 // Assert different hash.
Chris@0 356 $new_subtree_hash = $this->getSubtreesHash();
Chris@0 357
Chris@0 358 // Assert that the old admin menu subtree hash and the new admin menu
Chris@0 359 // subtree hash are different.
Chris@0 360 $this->assertTrue($new_subtree_hash, 'A valid hash value for the admin menu subtrees was created.');
Chris@0 361 $this->assertNotEqual($this->hash, $new_subtree_hash, 'The user-specific subtree menu hash has been updated.');
Chris@0 362 }
Chris@0 363
Chris@0 364 /**
Chris@0 365 * Test that back to site link exists on admin pages, not on content pages.
Chris@0 366 */
Chris@0 367 public function testBackToSiteLink() {
Chris@0 368 // Back to site link should exist in the markup.
Chris@0 369 $this->drupalGet('test-page');
Chris@0 370 $back_link = $this->cssSelect('.home-toolbar-tab');
Chris@0 371 $this->assertTrue($back_link);
Chris@0 372 }
Chris@0 373
Chris@0 374 /**
Chris@0 375 * Tests that external links added to the menu appear in the toolbar.
Chris@0 376 */
Chris@0 377 public function testExternalLink() {
Chris@0 378 $edit = [
Chris@0 379 'title[0][value]' => 'External URL',
Chris@0 380 'link[0][uri]' => 'http://example.org',
Chris@0 381 'menu_parent' => 'admin:system.admin',
Chris@0 382 'description[0][value]' => 'External URL & escaped',
Chris@0 383 ];
Chris@0 384 $this->drupalPostForm('admin/structure/menu/manage/admin/add', $edit, 'Save');
Chris@0 385
Chris@0 386 // Assert that the new menu link is shown on the menu link listing.
Chris@0 387 $this->drupalGet('admin/structure/menu/manage/admin');
Chris@0 388 $this->assertText('External URL');
Chris@0 389
Chris@0 390 // Assert that the new menu link is shown in the toolbar on a regular page.
Chris@0 391 $this->drupalGet(Url::fromRoute('<front>'));
Chris@0 392 $this->assertText('External URL');
Chris@0 393 // Ensure the description is escaped as expected.
Chris@0 394 $this->assertRaw('title="External URL &amp; escaped"');
Chris@0 395 }
Chris@0 396
Chris@0 397 /**
Chris@0 398 * Get the hash value from the admin menu subtrees route path.
Chris@0 399 *
Chris@0 400 * @return string
Chris@0 401 * The hash value from the admin menu subtrees route path.
Chris@0 402 */
Chris@0 403 private function getSubtreesHash() {
Chris@0 404 $settings = $this->getDrupalSettings();
Chris@0 405 // The toolbar module defines a route '/toolbar/subtrees/{hash}' that
Chris@0 406 // returns JSON for the rendered subtrees. This hash is provided to the
Chris@0 407 // client in drupalSettings.
Chris@0 408 return $settings['toolbar']['subtreesHash'];
Chris@0 409 }
Chris@0 410
Chris@0 411 /**
Chris@0 412 * Asserts the subtrees hash on a fresh page GET is different from the hash
Chris@0 413 * from the previous page GET.
Chris@0 414 */
Chris@0 415 private function assertDifferentHash() {
Chris@0 416 // Request a new page to refresh the drupalSettings object.
Chris@0 417 $this->drupalGet('test-page');
Chris@0 418 $this->assertResponse(200);
Chris@0 419 $new_subtree_hash = $this->getSubtreesHash();
Chris@0 420
Chris@0 421 // Assert that the old admin menu subtree hash and the new admin menu
Chris@0 422 // subtree hash are different.
Chris@0 423 $this->assertTrue($new_subtree_hash, 'A valid hash value for the admin menu subtrees was created.');
Chris@0 424 $this->assertNotEqual($this->hash, $new_subtree_hash, 'The user-specific subtree menu hash has been updated.');
Chris@0 425
Chris@0 426 // Save the new subtree hash as the original.
Chris@0 427 $this->hash = $new_subtree_hash;
Chris@0 428 }
Chris@0 429
Chris@0 430 }