MenuTest.php 28.9 KB
Newer Older
1 2
<?php

3 4
/**
 * @file
5
 * Definition of Drupal\menu_ui\Tests\MenuTest.
6 7
 */

8
namespace Drupal\menu_ui\Tests;
9

10
use Drupal\Component\Serialization\Json;
11
use Drupal\system\Entity\Menu;
12

13 14 15 16
/**
 * Defines a test class for testing menu and menu link functionality.
 */
class MenuTest extends MenuWebTestBase {
17 18 19 20 21 22

  /**
   * Modules to enable.
   *
   * @var array
   */
23
  public static $modules = array('node', 'block', 'contextual', 'help', 'path', 'test_page_test');
24

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
  /**
   * A user with administration rights.
   *
   * @var \Drupal\user\Entity\User
   */
  protected $admin_user;

  /**
   * An authenticated user.
   *
   * @var \Drupal\user\Entity\User
   */
  protected $authenticated_user;

  /**
   * A test menu.
   *
   * @var \Drupal\system\Entity\Menu
   */
44
  protected $menu;
45 46 47 48 49 50

  /**
   * An array of test menu links.
   *
   * @var array
   */
51 52
  protected $items;

53
  public static function getInfo() {
54
    return array(
55
      'name' => 'Menu link creation/deletion',
56
      'description' => 'Add a custom menu, add menu links to the custom menu and Tools menu, check their data, and delete them using the UI.',
57
      'group' => 'Menu'
58 59 60 61
    );
  }

  function setUp() {
62 63
    parent::setUp();

64 65
    $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));

66
    // Create users.
67 68
    $this->admin_user = $this->drupalCreateUser(array('access administration pages', 'administer blocks', 'administer menu', 'create article content'));
    $this->authenticated_user = $this->drupalCreateUser(array());
69 70 71
  }

  /**
72
   * Tests menu functionality using the admin and user interfaces.
73 74 75
   */
  function testMenu() {
    // Login the user.
76
    $this->drupalLogin($this->admin_user);
77 78
    $this->items = array();

79
    $this->menu = $this->addCustomMenu();
80 81
    $this->doMenuTests();
    $this->addInvalidMenuLink();
82
    $this->addCustomMenuCRUD();
83

84 85 86 87 88 89
    // Verify that the menu links rebuild is idempotent and leaves the same
    // number of links in the table.
    $before_count = db_query('SELECT COUNT(*) FROM {menu_links}')->fetchField();
    menu_link_rebuild_defaults();
    $after_count = db_query('SELECT COUNT(*) FROM {menu_links}')->fetchField();
    $this->assertIdentical($before_count, $after_count, 'menu_link_rebuild_defaults() does not add more links');
90 91
    // Do standard user tests.
    // Login the user.
92 93
    $this->drupalLogin($this->authenticated_user);
    $this->verifyAccess(403);
94
    foreach ($this->items as $item) {
95 96
      // Paths were set as 'node/$nid'.
      $node = node_load(substr($item['link_path'], 5));
97
      $this->verifyMenuLink($item, $node);
98 99
    }

100 101
    // Login the administrator.
    $this->drupalLogin($this->admin_user);
102

103
    // Delete menu links.
104
    foreach ($this->items as $item) {
105
      $this->deleteMenuLink($item);
106 107 108
    }

    // Delete custom menu.
109
    $this->deleteCustomMenu();
110

111 112
    // Modify and reset a standard menu link.
    $item = $this->getStandardMenuLink();
113
    $old_title = $item['link_title'];
114
    $this->modifyMenuLink($item);
115
    $item = entity_load('menu_link', $item['mlid']);
116 117 118
    // Verify that a change to the description is saved.
    $description = $this->randomName(16);
    $item['options']['attributes']['title']  = $description;
119 120 121 122
    $return_value = menu_link_save($item);
    // Save the menu link again to test the return value of the procedural save
    // helper.
    $this->assertIdentical($return_value, $item->save(), 'Return value of menu_link_save() is identical to the return value of $menu_link->save().');
123
    $saved_item = entity_load('menu_link', $item['mlid']);
124
    $this->assertEqual($description, $saved_item['options']['attributes']['title'], 'Saving an existing link updates the description (title attribute)');
125
    $this->resetMenuLink($item, $old_title);
126 127
  }

128
  /**
129
   * Adds a custom menu using CRUD functions.
130 131 132
   */
  function addCustomMenuCRUD() {
    // Add a new custom menu.
133
    $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI);
134
    $label = $this->randomName(16);
135

136 137 138
    $menu = entity_create('menu', array(
      'id' => $menu_name,
      'label' => $label,
139
      'description' => 'Description text',
140 141
    ));
    $menu->save();
142 143

    // Assert the new menu.
144
    $this->drupalGet('admin/structure/menu/manage/' . $menu_name);
145
    $this->assertRaw($label, 'Custom menu was added.');
146 147

    // Edit the menu.
148 149 150
    $new_label = $this->randomName(16);
    $menu->set('label', $new_label);
    $menu->save();
151
    $this->drupalGet('admin/structure/menu/manage/' . $menu_name);
152
    $this->assertRaw($new_label, 'Custom menu was edited.');
153 154 155
  }

  /**
156 157 158 159
   * Creates a custom menu.
   *
   * @return \Drupal\system\Entity\Menu
   *   The custom menu that has been created.
160 161
   */
  function addCustomMenu() {
162
    // Try adding a menu using a menu_name that is too long.
163
    $this->drupalGet('admin/structure/menu/add');
164
    $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI + 1);
165
    $label = $this->randomName(16);
166
    $edit = array(
167
      'id' => $menu_name,
168
      'description' => '',
169
      'label' =>  $label,
170
    );
171
    $this->drupalPostForm('admin/structure/menu/add', $edit, t('Save'));
172

173 174
    // Verify that using a menu_name that is too long results in a validation
    // message.
175 176 177 178 179
    $this->assertRaw(t('!name cannot be longer than %max characters but is currently %length characters long.', array(
      '!name' => t('Menu name'),
      '%max' => MENU_MAX_MENU_NAME_LENGTH_UI,
      '%length' => drupal_strlen($menu_name),
    )));
180 181

    // Change the menu_name so it no longer exceeds the maximum length.
182
    $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI);
183
    $edit['id'] = $menu_name;
184
    $this->drupalPostForm('admin/structure/menu/add', $edit, t('Save'));
185 186

    // Verify that no validation error is given for menu_name length.
187 188 189 190 191
    $this->assertNoRaw(t('!name cannot be longer than %max characters but is currently %length characters long.', array(
      '!name' => t('Menu name'),
      '%max' => MENU_MAX_MENU_NAME_LENGTH_UI,
      '%length' => drupal_strlen($menu_name),
    )));
192
    // Verify that the confirmation message is displayed.
193
    $this->assertRaw(t('Menu %label has been added.', array('%label' => $label)));
194
    $this->drupalGet('admin/structure/menu');
195
    $this->assertText($label, 'Menu created');
196

197
    // Confirm that the custom menu block is available.
198
    $this->drupalGet('admin/structure/block/list/' . \Drupal::config('system.theme')->get('default'));
199
    $this->assertText($label);
200

201
    // Enable the block.
202
    $this->drupalPlaceBlock('system_menu_block:' . $menu_name);
203
    return Menu::load($menu_name);
204 205 206
  }

  /**
207
   * Deletes the locally stored custom menu.
208
   *
209 210
   * This deletes the custom menu that is stored in $this->menu and performs
   * tests on the menu delete user interface.
211
   */
212
  function deleteCustomMenu() {
213 214
    $menu_name = $this->menu->id();
    $label = $this->menu->label();
215 216

    // Delete custom menu.
217
    $this->drupalPostForm("admin/structure/menu/manage/$menu_name/delete", array(), t('Delete'));
218
    $this->assertResponse(200);
219
    $this->assertRaw(t('The custom menu %title has been deleted.', array('%title' => $label)), 'Custom menu was deleted');
220
    $this->assertNull(Menu::load($menu_name), 'Custom menu was deleted');
221
    // Test if all menu links associated to the menu were removed from database.
222
    $result = entity_load_multiple_by_properties('menu_link', array('menu_name' => $menu_name));
223
    $this->assertFalse($result, 'All menu links associated to the custom menu were deleted.');
224 225

    // Make sure there's no delete button on system menus.
226
    $this->drupalGet('admin/structure/menu/manage/main');
227 228 229 230 231
    $this->assertNoRaw('edit-delete', 'The delete button was not found');

    // Try to delete the main menu.
    $this->drupalGet('admin/structure/menu/manage/main/delete');
    $this->assertText(t('You are not authorized to access this page.'));
232 233 234
  }

  /**
235
   * Tests menu functionality.
236
   */
237 238
  function doMenuTests() {
    $menu_name = $this->menu->id();
239
    // Add nodes to use as links for menu links.
240 241
    $node1 = $this->drupalCreateNode(array('type' => 'article'));
    $node2 = $this->drupalCreateNode(array('type' => 'article'));
242 243
    $node3 = $this->drupalCreateNode(array('type' => 'article'));
    $node4 = $this->drupalCreateNode(array('type' => 'article'));
244 245 246 247 248 249 250
    // Create a node with an alias.
    $node5 = $this->drupalCreateNode(array(
      'type' => 'article',
      'path' => array(
        'alias' => 'node5',
      ),
    ));
251

252
    // Add menu links.
253 254 255
    $item1 = $this->addMenuLink(0, 'node/' . $node1->id(), $menu_name);
    $item2 = $this->addMenuLink($item1['mlid'], 'node/' . $node2->id(), $menu_name, FALSE);
    $item3 = $this->addMenuLink($item2['mlid'], 'node/' . $node3->id(), $menu_name);
256 257 258 259 260 261
    $this->assertMenuLink($item1['mlid'], array(
      'depth' => 1,
      'has_children' => 1,
      'p1' => $item1['mlid'],
      'p2' => 0,
      // We assert the language code here to make sure that the language
262
      // selection element degrades gracefully without the Language module.
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
      'langcode' => 'en',
    ));
    $this->assertMenuLink($item2['mlid'], array(
      'depth' => 2, 'has_children' => 1,
      'p1' => $item1['mlid'],
      'p2' => $item2['mlid'],
      'p3' => 0,
      // See above.
      'langcode' => 'en',
    ));
    $this->assertMenuLink($item3['mlid'], array(
      'depth' => 3,
      'has_children' => 0,
      'p1' => $item1['mlid'],
      'p2' => $item2['mlid'],
      'p3' => $item3['mlid'],
      'p4' => 0,
      // See above.
      'langcode' => 'en',
    ));
283

284 285 286
    // Verify menu links.
    $this->verifyMenuLink($item1, $node1);
    $this->verifyMenuLink($item2, $node2, $item1, $node1);
287 288 289
    $this->verifyMenuLink($item3, $node3, $item2, $node2);

    // Add more menu links.
290 291
    $item4 = $this->addMenuLink(0, 'node/' . $node4->id(), $menu_name);
    $item5 = $this->addMenuLink($item4['mlid'], 'node/' . $node5->id(), $menu_name);
292 293
    // Create a menu link pointing to an alias.
    $item6 = $this->addMenuLink($item4['mlid'], 'node5', $menu_name, TRUE, '0', 'node/' . $node5->id());
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
    $this->assertMenuLink($item4['mlid'], array(
      'depth' => 1,
      'has_children' => 1,
      'p1' => $item4['mlid'],
      'p2' => 0,
      // See above.
      'langcode' => 'en',
    ));
    $this->assertMenuLink($item5['mlid'], array(
      'depth' => 2,
      'has_children' => 0,
      'p1' => $item4['mlid'],
      'p2' => $item5['mlid'],
      'p3' => 0,
      // See above.
      'langcode' => 'en',
    ));
311 312 313 314 315 316 317 318 319 320
    $this->assertMenuLink($item6['mlid'], array(
      'depth' => 2,
      'has_children' => 0,
      'p1' => $item4['mlid'],
      'p2' => $item6['mlid'],
      'p3' => 0,
      'link_path' => 'node/' . $node5->id(),
      // See above.
      'langcode' => 'en',
    ));
321

322 323 324
    // Modify menu links.
    $this->modifyMenuLink($item1);
    $this->modifyMenuLink($item2);
325

326 327 328
    // Toggle menu links.
    $this->toggleMenuLink($item1);
    $this->toggleMenuLink($item2);
329

330 331
    // Move link and verify that descendants are updated.
    $this->moveMenuLink($item2, $item5['mlid'], $menu_name);
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
    $this->assertMenuLink($item1['mlid'], array(
      'depth' => 1,
      'has_children' => 0,
      'p1' => $item1['mlid'],
      'p2' => 0,
      // See above.
      'langcode' => 'en',
    ));
    $this->assertMenuLink($item4['mlid'], array(
      'depth' => 1,
      'has_children' => 1,
      'p1' => $item4['mlid'],
      'p2' => 0,
      // See above.
      'langcode' => 'en',
    ));
    $this->assertMenuLink($item5['mlid'], array(
      'depth' => 2,
      'has_children' => 1,
      'p1' => $item4['mlid'],
      'p2' => $item5['mlid'],
      'p3' => 0,
      // See above.
      'langcode' => 'en',
    ));
    $this->assertMenuLink($item2['mlid'], array(
      'depth' => 3,
      'has_children' => 1,
      'p1' => $item4['mlid'],
      'p2' => $item5['mlid'],
      'p3' => $item2['mlid'],
      'p4' => 0,
      // See above.
      'langcode' => 'en',
    ));
    $this->assertMenuLink($item3['mlid'], array(
      'depth' => 4,
      'has_children' => 0,
      'p1' => $item4['mlid'],
      'p2' => $item5['mlid'],
      'p3' => $item2['mlid'],
      'p4' => $item3['mlid'],
      'p5' => 0,
      // See above.
      'langcode' => 'en',
    ));
378

379
    // Add 102 menu links with increasing weights, then make sure the last-added
380
    // item's weight doesn't get changed because of the old hardcoded delta=50.
381 382
    $items = array();
    for ($i = -50; $i <= 51; $i++) {
383
      $items[$i] = $this->addMenuLink(0, 'node/' . $node1->id(), $menu_name, TRUE, strval($i));
384 385 386
    }
    $this->assertMenuLink($items[51]['mlid'], array('weight' => '51'));

387 388 389 390
    // Enable a link via the overview form.
    $this->disableMenuLink($item1);
    $edit = array();

391 392 393
    // Note in the UI the 'links[mlid:x][hidden]' form element maps to enabled,
    // or NOT hidden.
    $edit['links[mlid:' . $item1['mlid'] . '][hidden]'] = TRUE;
394
    $this->drupalPostForm('admin/structure/menu/manage/' . $item1['menu_name'], $edit, t('Save'));
395 396

    // Verify in the database.
397
    $this->assertMenuLink($item1['mlid'], array('hidden' => 0));
398

399 400 401 402 403 404 405 406 407 408 409 410 411
    // Add an external link.
    $item7 = $this->addMenuLink(0, 'http://drupal.org', $menu_name);
    $this->assertMenuLink($item7['mlid'], array('link_path' => 'http://drupal.org', 'external' => 1));

    // Add <front> menu item.
    $item8 = $this->addMenuLink(0, '<front>', $menu_name);
    $this->assertMenuLink($item8['mlid'], array('link_path' => '<front>', 'external' => 1));
    $this->drupalGet('');
    $this->assertResponse(200);
    // Make sure we get routed correctly.
    $this->clickLink($item8['link_title']);
    $this->assertResponse(200);

412
    // Save menu links for later tests.
413 414 415 416
    $this->items[] = $item1;
    $this->items[] = $item2;
  }

417
  /**
418
   * Adds and removes a menu link with a query string and fragment.
419 420
   */
  function testMenuQueryAndFragment() {
421
    $this->drupalLogin($this->admin_user);
422 423

    // Make a path with query and fragment on.
424
    $path = 'test-page?arg1=value1&arg2=value2';
425 426 427
    $item = $this->addMenuLink(0, $path);

    $this->drupalGet('admin/structure/menu/item/' . $item['mlid'] . '/edit');
428
    $this->assertFieldByName('link_path', $path, 'Path is found with both query and fragment.');
429 430

    // Now change the path to something without query and fragment.
431
    $path = 'test-page';
432
    $this->drupalPostForm('admin/structure/menu/item/' . $item['mlid'] . '/edit', array('link_path' => $path), t('Save'));
433
    $this->drupalGet('admin/structure/menu/item/' . $item['mlid'] . '/edit');
434
    $this->assertFieldByName('link_path', $path, 'Path no longer has query or fragment.');
435 436
  }

437
  /**
438
   * Tests renaming the built-in menu.
439 440
   */
  function testSystemMenuRename() {
441
    $this->drupalLogin($this->admin_user);
442 443 444
    $edit = array(
      'label' => $this->randomName(16),
    );
445
    $this->drupalPostForm('admin/structure/menu/manage/main', $edit, t('Save'));
446 447

    // Make sure menu shows up with new name in block addition.
448
    $default_theme = \Drupal::config('system.theme')->get('default');
449
    $this->drupalget('admin/structure/block/list/' . $default_theme);
450 451 452
    $this->assertText($edit['label']);
  }

453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
  /**
   * Tests that menu items pointing to unpublished nodes are editable.
   */
  function testUnpublishedNodeMenuItem() {
    $this->drupalLogin($this->drupalCreateUser(array('access administration pages', 'administer blocks', 'administer menu', 'create article content', 'bypass node access')));
    // Create an unpublished node.
    $node = $this->drupalCreateNode(array(
      'type' => 'article',
      'status' => NODE_NOT_PUBLISHED,
    ));

    $item = $this->addMenuLink(0, 'node/' . $node->id());
    $this->modifyMenuLink($item);

    // Test that a user with 'administer menu' but without 'bypass node access'
    // cannot see the menu item.
    $this->drupalLogout();
    $this->drupalLogin($this->admin_user);
    $this->drupalGet('admin/structure/menu/manage/' . $item['menu_name']);
    $this->assertNoText($item['link_title'], "Menu link pointing to unpublished node is only visible to users with 'bypass node access' permission");
  }

475 476 477 478
  /**
   * Tests the contextual links on a menu block.
   */
  public function testBlockContextualLinks() {
479
    $this->drupalLogin($this->drupalCreateUser(array('administer menu', 'access contextual links', 'administer blocks')));
480
    $this->addMenuLink();
481
    $block = $this->drupalPlaceBlock('system_menu_block:tools', array('label' => 'Tools', 'provider' => 'system'));
482
    $this->drupalGet('test-page');
483

484
    $id = 'block:block=' . $block->id() . ':|menu:menu=tools:';
485 486 487 488 489
    // @see \Drupal\contextual\Tests\ContextualDynamicContextTest:assertContextualLinkPlaceHolder()
    $this->assertRaw('<div data-contextual-id="'. $id . '"></div>', format_string('Contextual link placeholder with id @id exists.', array('@id' => $id)));

    // Get server-rendered contextual links.
    // @see \Drupal\contextual\Tests\ContextualDynamicContextTest:renderContextualLinks()
490 491
    $post = array('ids[0]' => $id);
    $response =  $this->drupalPost('contextual/render', 'application/json', $post, array('query' => array('destination' => 'test-page')));
492
    $this->assertResponse(200);
493
    $json = Json::decode($response);
494
    $this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="block-configure"><a href="' . base_path() . 'admin/structure/block/manage/' . $block->id() . '">Configure block</a></li><li class="menu-ui-edit"><a href="' . base_path() . 'admin/structure/menu/manage/tools">Edit menu</a></li></ul>');
495 496
  }

497 498 499 500
  /**
   * Tests menu link bundles.
   */
  public function testMenuBundles() {
501
    $this->drupalLogin($this->admin_user);
502
    $menu = $this->addCustomMenu();
503 504
    // Clear the entity info cache to ensure the static caches are rebuilt.
    entity_info_cache_clear();
505
    $bundles = entity_get_bundles('menu_link');
506
    $this->assertTrue(isset($bundles[$menu->id()]));
507 508 509 510 511 512 513
    $menus = menu_list_system_menus();
    $menus[$menu->id()] = $menu->label();
    ksort($menus);
    $this->assertIdentical(array_keys($bundles), array_keys($menus));

    // Test if moving a menu link between menus changes the bundle.
    $node = $this->drupalCreateNode(array('type' => 'article'));
514
    $item = $this->addMenuLink(0, 'node/' . $node->id(), 'tools');
515 516 517 518 519 520
    $this->moveMenuLink($item, 0, $menu->id());
    $this->assertEqual($item->bundle(), 'tools', 'Menu link bundle matches the menu');

    $moved_item = entity_load('menu_link', $item->id(), TRUE);
    $this->assertNotEqual($moved_item->bundle(), $item->bundle(), 'Menu link bundle was changed');
    $this->assertEqual($moved_item->bundle(), $menu->id(), 'Menu link bundle matches the menu');
521 522 523 524

    $unsaved_item = entity_create('menu_link', array('menu_name' => $menu->id(), 'link_title' => $this->randomName(16), 'link_path' => '<front>'));
    $this->assertEqual($unsaved_item->bundle(), $menu->id(), 'Unsaved menu link bundle matches the menu');
    $this->assertEqual($unsaved_item->menu_name, $menu->id(), 'Unsaved menu link menu name matches the menu');
525 526
  }

527
  /**
528
   * Adds a menu link using the UI.
529
   *
530 531 532 533 534 535 536 537 538 539 540 541
   * @param integer $plid
   *   Optional parent menu link id.
   * @param string $link
   *   Link path. Defaults to the front page.
   * @param string $menu_name
   *   Menu name. Defaults to 'tools'.
   * @param bool $expanded
   *   Whether or not this menu link is expanded. Setting this to TRUE should
   *   test whether it works when we do the authenticated_user tests. Defaults
   *   to FALSE.
   * @param string $weight
   *  Menu weight. Defaults to 0.
542 543
   * @param string $actual_link
   *   Actual link path in case $link is an alias.
544
   *
545
   * @return \Drupal\menu_link\Entity\MenuLink
546
   *   A menu link entity.
547
   */
548
  function addMenuLink($plid = 0, $link = '<front>', $menu_name = 'tools', $expanded = TRUE, $weight = '0', $actual_link = FALSE) {
549
    // View add menu link page.
550
    $this->drupalGet("admin/structure/menu/manage/$menu_name/add");
551 552
    $this->assertResponse(200);

553
    $title = '!link_' . $this->randomName(16);
554
    $edit = array(
555 556 557
      'link_path' => $link,
      'link_title' => $title,
      'description' => '',
558 559
      'enabled' => TRUE,
      'expanded' => $expanded,
560
      'parent' =>  $menu_name . ':' . $plid,
561
      'weight' => $weight,
562 563
    );

564 565 566
    if (!$actual_link) {
      $actual_link = $link;
    }
567
    // Add menu link.
568
    $this->drupalPostForm(NULL, $edit, t('Save'));
569
    $this->assertResponse(200);
570
    $this->assertText('The menu link has been saved.');
571

572 573
    $menu_links = entity_load_multiple_by_properties('menu_link', array('link_title' => $title));
    $menu_link = reset($menu_links);
574
    $this->assertTrue($menu_link, 'Menu link was found in database.');
575
    $this->assertMenuLink($menu_link->id(), array('menu_name' => $menu_name, 'link_path' => $actual_link, 'has_children' => 0, 'plid' => $plid));
576

577
    return $menu_link;
578 579 580
  }

  /**
581
   * Attempts to add menu link with invalid path or no access permission.
582
   */
583
  function addInvalidMenuLink() {
584
    foreach (array('-&-', 'admin/people/permissions', '#') as $link_path) {
585
      $edit = array(
586 587
        'link_path' => $link_path,
        'link_title' => 'title',
588
      );
589
      $this->drupalPostForm("admin/structure/menu/manage/{$this->menu->id()}/add", $edit, t('Save'));
590
      $this->assertRaw(t("The path '@path' is either invalid or you do not have access to it.", array('@path' => $link_path)), 'Menu link was not created');
591 592 593 594
    }
  }

  /**
595
   * Verifies a menu link using the UI.
596
   *
597 598 599 600 601 602 603 604
   * @param array $item
   *   Menu link.
   * @param object $item_node
   *   Menu link content node.
   * @param array $parent
   *   Parent menu link.
   * @param object $parent_node
   *   Parent menu link content node.
605
   */
606
  function verifyMenuLink($item, $item_node, $parent = NULL, $parent_node = NULL) {
607 608 609 610
    // View home page.
    $this->drupalGet('');
    $this->assertResponse(200);

611
    // Verify parent menu link.
612
    if (isset($parent)) {
613
      // Verify menu link.
614
      $title = $parent['link_title'];
615
      $this->assertLink($title, 0, 'Parent menu link was displayed');
616

617
      // Verify menu link link.
618
      $this->clickLink($title);
619
      $title = $parent_node->label();
620
      $this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Parent menu link link target was correct');
621 622
    }

623
    // Verify menu link.
624
    $title = $item['link_title'];
625
    $this->assertLink($title, 0, 'Menu link was displayed');
626

627
    // Verify menu link link.
628
    $this->clickLink($title);
629
    $title = $item_node->label();
630
    $this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Menu link link target was correct');
631 632
  }

633
  /**
634
   * Changes the parent of a menu link using the UI.
635 636 637 638 639 640 641
   *
   * @param array $item
   *   The menu link item to move.
   * @param int $plid
   *   The id of the new parent.
   * @param string $menu_name
   *   The menu the menu link will be moved to.
642 643 644 645 646 647 648
   */
  function moveMenuLink($item, $plid, $menu_name) {
    $mlid = $item['mlid'];

    $edit = array(
      'parent' => $menu_name . ':' . $plid,
    );
649
    $this->drupalPostForm("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
650 651 652
    $this->assertResponse(200);
  }

653
  /**
654
   * Modifies a menu link using the UI.
655
   *
656 657
   * @param array $item
   *   Menu link passed by reference.
658
   */
659
  function modifyMenuLink(&$item) {
660 661 662 663 664
    $item['link_title'] = $this->randomName(16);

    $mlid = $item['mlid'];
    $title = $item['link_title'];

665
    // Edit menu link.
666
    $edit = array();
667
    $edit['link_title'] = $title;
668
    $this->drupalPostForm("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
669
    $this->assertResponse(200);
670
    $this->assertText('The menu link has been saved.');
671
    // Verify menu link.
672
    $this->drupalGet('admin/structure/menu/manage/' . $item['menu_name']);
673
    $this->assertText($title, 'Menu link was edited');
674 675 676
  }

  /**
677
   * Resets a standard menu link using the UI.
678
   *
679 680 681 682
   * @param array $item
   *   Menu link.
   * @param string $old_title
   *   Original title for menu link.
683
   */
684
  function resetMenuLink($item, $old_title) {
685 686 687
    $mlid = $item['mlid'];
    $title = $item['link_title'];

688
    // Reset menu link.
689
    $this->drupalPostForm("admin/structure/menu/item/$mlid/reset", array(), t('Reset'));
690
    $this->assertResponse(200);
691
    $this->assertRaw(t('The menu link was reset to its default settings.'), 'Menu link was reset');
692

693
    // Verify menu link.
694
    $this->drupalGet('');
695 696
    $this->assertNoText($title, 'Menu link was reset');
    $this->assertText($old_title, 'Menu link was reset');
697 698 699
  }

  /**
700
   * Deletes a menu link using the UI.
701
   *
702 703
   * @param array $item
   *   Menu link.
704
   */
705
  function deleteMenuLink($item) {
706 707 708
    $mlid = $item['mlid'];
    $title = $item['link_title'];

709
    // Delete menu link.
710
    $this->drupalPostForm("admin/structure/menu/item/$mlid/delete", array(), t('Confirm'));
711
    $this->assertResponse(200);
712
    $this->assertRaw(t('The menu link %title has been deleted.', array('%title' => $title)), 'Menu link was deleted');
713 714 715

    // Verify deletion.
    $this->drupalGet('');
716
    $this->assertNoText($title, 'Menu link was deleted');
717 718 719
  }

  /**
720
   * Alternately disables and enables a menu link.
721
   *
722 723
   * @param $item
   *   Menu link.
724
   */
725
  function toggleMenuLink($item) {
726
    $this->disableMenuLink($item);
727

728 729 730 731 732 733 734 735 736 737 738
    // Verify menu link is absent.
    $this->drupalGet('');
    $this->assertNoText($item['link_title'], 'Menu link was not displayed');
    $this->enableMenuLink($item);

    // Verify menu link is displayed.
    $this->drupalGet('');
    $this->assertText($item['link_title'], 'Menu link was displayed');
  }

  /**
739
   * Disables a menu link.
740 741 742 743 744 745
   *
   * @param $item
   *   Menu link.
   */
  function disableMenuLink($item) {
    $mlid = $item['mlid'];
746
    $edit['enabled'] = FALSE;
747
    $this->drupalPostForm("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
748

749 750
    // Unlike most other modules, there is no confirmation message displayed.
    // Verify in the database.
751
    $this->assertMenuLink($mlid, array('hidden' => 1));
752
  }
753

754
  /**
755
   * Enables a menu link.
756 757 758 759 760 761
   *
   * @param $item
   *   Menu link.
   */
  function enableMenuLink($item) {
    $mlid = $item['mlid'];
762
    $edit['enabled'] = TRUE;
763
    $this->drupalPostForm("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
764

765
    // Verify in the database.
766 767 768
    $this->assertMenuLink($mlid, array('hidden' => 0));
  }

769
  /**
770 771
   * Tests if administrative users other than user 1 can access the menu parents
   * AJAX callback.
772 773 774 775 776 777 778 779 780 781
   */
  public function testMenuParentsJsAccess() {
    $admin = $this->drupalCreateUser(array('administer menu'));
    $this->drupalLogin($admin);
    // Just check access to the callback overall, the POST data is irrelevant.
    $this->drupalGetAJAX('admin/structure/menu/parents');
    $this->assertResponse(200);

    // Do standard user tests.
    // Login the user.
782
    $this->drupalLogin($this->authenticated_user);
783 784 785 786
    $this->drupalGetAJAX('admin/structure/menu/parents');
    $this->assertResponse(403);
  }

787
  /**
788 789 790 791
   * Returns standard menu link.
   *
   * @return \Drupal\menu_link\Entity\MenuLink
   *   A menu link entity.
792
   */
793
  private function getStandardMenuLink() {
794 795 796
    $mlid = 0;
    // Retrieve menu link id of the Log out menu link, which will always be on
    // the front page.
797
    $query = \Drupal::entityQuery('menu_link')
798 799
      ->condition('module', 'user')
      ->condition('machine_name', 'user.logout');
800 801 802 803 804
    $result = $query->execute();
    if (!empty($result)) {
      $mlid = reset($result);
    }

805
    $this->assertTrue($mlid > 0, 'Standard menu link id was found');
806
    // Load menu link.
807
    // Use api function so that link is translated for rendering.
808
    $item = entity_load('menu_link', $mlid);
809
    $this->assertTrue((bool) $item, 'Standard menu link was loaded');
810 811 812 813
    return $item;
  }

  /**
814
   * Verifies the logged in user has the desired access to various menu pages.
815
   *
816 817
   * @param integer $response
   *   The expected HTTP response code. Defaults to 200.
818
   */
819 820
  private function verifyAccess($response = 200) {
    // View menu help page.
821 822 823
    $this->drupalGet('admin/help/menu');
    $this->assertResponse($response);
    if ($response == 200) {
824
      $this->assertText(t('Menu'), 'Menu help was displayed');
825 826
    }

827
    // View menu build overview page.
828
    $this->drupalGet('admin/structure/menu');
829 830
    $this->assertResponse($response);
    if ($response == 200) {
831
      $this->assertText(t('Menus'), 'Menu build overview page was displayed');
832 833
    }

834 835
    // View tools menu customization page.
    $this->drupalGet('admin/structure/menu/manage/' . $this->menu->id());
836
        $this->assertResponse($response);
837
    if ($response == 200) {
838
      $this->assertText(t('Tools'), 'Tools menu page was displayed');
839 840
    }

841
    // View menu edit page.
842
    $item = $this->getStandardMenuLink();
843
    $this->drupalGet('admin/structure/menu/item/' . $item['mlid'] . '/edit');
844 845
    $this->assertResponse($response);
    if ($response == 200) {
846
      $this->assertText(t('Edit menu item'), 'Menu edit page was displayed');
847 848
    }

849
    // View menu settings page.
850
    $this->drupalGet('admin/structure/menu/settings');
851 852
    $this->assertResponse($response);
    if ($response == 200) {
853
      $this->assertText(t('Menus'), 'Menu settings page was displayed');
854 855
    }

856
    // View add menu page.
857
    $this->drupalGet('admin/structure/menu/add');
858 859
    $this->assertResponse($response);
    if ($response == 200) {
860
      $this->assertText(t('Menus'), 'Add menu page was displayed');
861 862
    }
  }
863

864
}