menu.test 21.4 KB
Newer Older
1 2 3
<?php
// $Id$

4 5 6 7 8
/**
 * @file
 * Tests file for the menu module.
 */

9 10 11 12 13 14
class MenuTestCase extends DrupalWebTestCase {
  protected $big_user;
  protected $std_user;
  protected $menu;
  protected $items;

15
  public static function getInfo() {
16
    return array(
17 18 19
      'name' => 'Menu link creation/deletion',
      'description' => 'Add a custom menu, add menu links to the custom menu and Navigation menu, check their data, and delete them using the menu module UI.',
      'group' => 'Menu'
20 21 22 23 24 25
    );
  }

  function setUp() {
    parent::setUp('menu');
    // Create users.
26
    $this->big_user = $this->drupalCreateUser(array('access administration pages', 'administer blocks', 'administer menu', 'create article content'));
27 28 29 30
    $this->std_user = $this->drupalCreateUser(array());
  }

  /**
31
   * Login users, add menus and menu links, and test menu functionality through the admin and user interfaces.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
   */
  function testMenu() {
    // Login the user.
    $this->drupalLogin($this->big_user);
    $this->items = array();

    // Do standard menu tests.
    $this->doStandardMenuTests();

    // Do custom menu tests.
    $this->doCustomMenuTests();

    // Do standard user tests.
    // Login the user.
    $this->drupalLogin($this->std_user);
    $this->verifyAccess(403);
    foreach ($this->items as $item) {
      $node = node_load(substr($item['link_path'], 5)); // Paths were set as 'node/$nid'.
50
      $this->verifyMenuLink($item, $node);
51 52 53 54 55
    }

    // Login the user.
    $this->drupalLogin($this->big_user);

56
    // Delete menu links.
57
    foreach ($this->items as $item) {
58
      $this->deleteMenuLink($item);
59 60 61 62 63
    }

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

64 65
    // Modify and reset a standard menu link.
    $item = $this->getStandardMenuLink();
66
    $old_title = $item['link_title'];
67
    $this->modifyMenuLink($item);
68
    $item = menu_link_load($item['mlid']);
69 70 71 72 73 74
    // Verify that a change to the description is saved.
    $description = $this->randomName(16);
    $item['options']['attributes']['title']  = $description;
    menu_link_save($item);
    $saved_item = menu_link_load($item['mlid']);
    $this->assertEqual($description, $saved_item['options']['attributes']['title'], t('Saving an existing link updates the description (title attribute)'));
75
    $this->resetMenuLink($item, $old_title);
76 77 78 79 80 81 82 83
  }

  /**
   * Test standard menu functionality using navigation menu.
   *
   */
  function doStandardMenuTests() {
    $this->doMenuTests();
84
    $this->addInvalidMenuLink();
85 86 87 88 89 90 91 92 93
  }

  /**
   * Test custom menu functionality using navigation menu.
   *
   */
  function doCustomMenuTests() {
    $this->menu = $this->addCustomMenu();
    $this->doMenuTests($this->menu['menu_name']);
94
    $this->addInvalidMenuLink($this->menu['menu_name']);
95 96 97 98 99 100 101 102
    $this->addCustomMenuCRUD();
  }

  /**
   * Add custom menu using CRUD functions.
   */
  function addCustomMenuCRUD() {
    // Add a new custom menu.
103
    $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI);
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    $title = $this->randomName(16);

    $menu = array(
      'menu_name' => $menu_name,
      'title' => $title,
      'description' => 'Description text',
    );
    menu_save($menu);

    // Assert the new menu.
    $this->drupalGet('admin/structure/menu/manage/' . $menu_name . '/edit');
    $this->assertText($title, t('Custom menu was added.'));

    // Edit the menu.
    $new_title = $this->randomName(16);
    $menu['title'] = $new_title;
    menu_save($menu);
    $this->drupalGet('admin/structure/menu/manage/' . $menu_name . '/edit');
    $this->assertText($new_title, t('Custom menu was edited.'));
123 124 125 126 127 128 129
  }

  /**
   * Add custom menu.
   */
  function addCustomMenu() {
    // Add custom menu.
130 131

    // Try adding a menu using a menu_name that is too long.
132
    $this->drupalGet('admin/structure/menu/add');
133
    $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI + 1);
134
    $title = $this->randomName(16);
135
    $edit = array(
136 137 138 139
      'menu_name' => $menu_name,
      'description' => '',
      'title' =>  $title,
    );
140
    $this->drupalPost('admin/structure/menu/add', $edit, t('Save'));
141 142 143 144 145

    // Verify that using a menu_name that is too long results in a validation message.
    $this->assertText(format_plural(MENU_MAX_MENU_NAME_LENGTH_UI, "The menu name can't be longer than 1 character.", "The menu name can't be longer than @count characters."), t('Validation failed when menu name is too long.'));

    // Change the menu_name so it no longer exceeds the maximum length.
146
    $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI);
147
    $edit['menu_name'] = $menu_name;
148
    $this->drupalPost('admin/structure/menu/add', $edit, t('Save'));
149 150 151

    // Verify that no validation error is given for menu_name length.
    $this->assertNoText(format_plural(MENU_MAX_MENU_NAME_LENGTH_UI, "The menu name can't be longer than 1 character.", "The menu name can't be longer than @count characters."), t('Validation failed when menu name is too long.'));
152 153
    // Unlike most other modules, there is no confirmation message displayed.

154
    $this->drupalGet('admin/structure/menu');
155 156 157
    $this->assertText($title, 'Menu created');

    // Enable the custom menu block.
158
    $menu_name = 'menu-' . $menu_name; // Drupal prepends the name with 'menu-'.
159
    $edit = array();
160
    $edit['menu_' . $menu_name . '[region]'] = 'sidebar_first';
161
    $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
    $this->assertResponse(200);
    $this->assertText(t('The block settings have been updated.'), t('Custom menu block was enabled'));

    return menu_load($menu_name);
  }

  /**
   * Delete custom menu.
   *
   * @param string $menu_name Custom menu name.
   */
  function deleteCustomMenu($menu) {
    $menu_name = $this->menu['menu_name'];
    $title = $this->menu['title'];

    // Delete custom menu.
178
    $this->drupalPost("admin/structure/menu/manage/$menu_name/delete", array(), t('Delete'));
179 180 181
    $this->assertResponse(200);
    $this->assertRaw(t('The custom menu %title has been deleted.', array('%title' => $title)), t('Custom menu was deleted'));
    $this->assertFalse(menu_load($menu_name), 'Custom menu was deleted');
182 183
    // Test if all menu links associated to the menu were removed from database.
    $result = db_query("SELECT menu_name FROM {menu_links} WHERE menu_name = :menu_name", array(':menu_name' => $menu_name))->fetchField();
184
    $this->assertFalse($result, t('All menu links associated to the custom menu were deleted.'));
185 186 187 188 189 190 191
  }

  /**
   * Test menu functionality using navigation menu.
   *
   */
  function doMenuTests($menu_name = 'navigation') {
192
    // Add nodes to use as links for menu links.
193 194
    $node1 = $this->drupalCreateNode(array('type' => 'article'));
    $node2 = $this->drupalCreateNode(array('type' => 'article'));
195

196 197 198
    // Add menu links.
    $item1 = $this->addMenuLink(0, 'node/' . $node1->nid, $menu_name);
    $item2 = $this->addMenuLink($item1['mlid'], 'node/' . $node2->nid, $menu_name);
199

200 201 202
    // Verify menu links.
    $this->verifyMenuLink($item1, $node1);
    $this->verifyMenuLink($item2, $node2, $item1, $node1);
203

204 205 206
    // Modify menu links.
    $this->modifyMenuLink($item1);
    $this->modifyMenuLink($item2);
207

208 209 210
    // Toggle menu links.
    $this->toggleMenuLink($item1);
    $this->toggleMenuLink($item2);
211

212 213 214 215 216 217 218
    // Enable a link via the overview form.
    $this->disableMenuLink($item1);
    $edit = array();

    // Note in the UI the 'mlid:x[hidden]' form element maps to enabled, or
    // NOT hidden.
    $edit['mlid:' . $item1['mlid'] . '[hidden]'] = TRUE;
219
    $this->drupalPost('admin/structure/menu/manage/' . $item1['menu_name'], $edit, t('Save configuration'));
220 221

    // Verify in the database.
222
    $hidden = db_query("SELECT hidden FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $item1['mlid']))->fetchField();
223
    $this->assertEqual($hidden, 0, t('Link is not hidden in the database table when enabled via the overview form'));
224

225
    // Save menu links for later tests.
226 227 228 229
    $this->items[] = $item1;
    $this->items[] = $item2;
  }

230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
  /**
   * Add and remove a menu link with a query string and fragment.
   */
  function testMenuQueryAndFragment() {
    $this->drupalLogin($this->big_user);

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

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

    // Now change the path to something without query and fragment.
    $path = 'node';
    $this->drupalPost('admin/structure/menu/item/' . $item['mlid'] . '/edit', array('link_path' => $path), t('Save'));
    $this->drupalGet('admin/structure/menu/item/' . $item['mlid'] . '/edit');
    $this->assertFieldByName('link_path', $path, t('Path no longer has query or fragment.'));
  }

250
  /**
251
   * Add a menu link using the menu module UI.
252 253 254 255
   *
   * @param integer $plid Parent menu link id.
   * @param string $link Link path.
   * @param string $menu_name Menu name.
256
   * @return array Menu link created.
257
   */
258 259
  function addMenuLink($plid = 0, $link = '<front>', $menu_name = 'navigation') {
    // View add menu link page.
260
    $this->drupalGet("admin/structure/menu/manage/$menu_name/add");
261 262
    $this->assertResponse(200);

263
    $title = '!link_' . $this->randomName(16);
264
    $edit = array(
265 266 267 268 269 270 271
      'link_path' => $link,
      'link_title' => $title,
      'description' => '',
      'enabled' => TRUE, // Use this to disable the menu and test.
      'expanded' => TRUE, // Setting this to true should test whether it works when we do the std_user tests.
      'parent' =>  $menu_name . ':' . $plid,
      'weight' => '0',
272 273
    );

274
    // Add menu link.
275
    $this->drupalPost(NULL, $edit, t('Save'));
276 277
    $this->assertResponse(200);
    // Unlike most other modules, there is no confirmation message displayed.
278 279 280
    $this->assertText($title, 'Menu link was added');

    // Retrieve menu link.
281
    $item = db_query("SELECT * FROM {menu_links} WHERE link_title = :title", array(':title' => $title))->fetchAssoc();
282

283
    // Check the structure in the DB of the two menu links.
284 285
    // In general, if $n = $item['depth'] then $item['p'. $n] == $item['mlid'] and $item['p' . ($n - 1)] == $item['plid'] (unless depth == 0).
    // All $item['p' . $n] for $n > depth must be 0.
286 287
    // We know link1 is at the top level, so $item1['deptj'] == 1 and $item1['plid'] == 0.
    // We know that the parent of link2 is link1, so $item2['plid'] == $item1['mlid'].
288
    // Both menu links were created in the navigation menu.
289 290
    $this->assertEqual($item['menu_name'], $menu_name);
    $this->assertEqual($item['plid'], $plid);
291 292 293 294 295 296 297
    $options = unserialize($item['options']);
    if (!empty($options['query'])) {
      $item['link_path'] .= '?' . drupal_http_build_query($options['query']);
    }
    if (!empty($options['fragment'])) {
      $item['link_path'] .= '#' . $options['fragment'];
    }
298 299
    $this->assertEqual($item['link_path'], $link);
    $this->assertEqual($item['link_title'], $title);
300
    if ($plid == 0) {
301
      $this->assertTrue($item['depth'] == 1 && !$item['has_children'] && $item['p1'] == $item['mlid'] && $item['p2'] == 0, 'Menu link has correct data');
302 303
    }
    else {
304
      $this->assertTrue($item['depth'] == 2 && !$item['has_children'] && $item['p1'] == $plid && $item['p2'] == $item['mlid'], 'Menu link has correct data');
305 306 307 308 309 310
    }

    return $item;
  }

  /**
311
   * Attempt to add menu link with invalid path or no access permission.
312 313 314
   *
   * @param string $menu_name Menu name.
   */
315
  function addInvalidMenuLink($menu_name = 'navigation') {
316
    foreach (array('-&-', 'admin/people/permissions') as $link_path) {
317
      $edit = array(
318 319
        'link_path' => $link_path,
        'link_title' => 'title',
320
      );
321
      $this->drupalPost("admin/structure/menu/manage/$menu_name/add", $edit, t('Save'));
322
      $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');
323 324 325 326
    }
  }

  /**
327
   * Verify a menu link using the menu module UI.
328
   *
329 330 331 332
   * @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.
333
   */
334
  function verifyMenuLink($item, $item_node, $parent = NULL, $parent_node = NULL) {
335 336 337 338
    // View home page.
    $this->drupalGet('');
    $this->assertResponse(200);

339
    // Verify parent menu link.
340
    if (isset($parent)) {
341
      // Verify menu link.
342
      $title = $parent['link_title'];
343
      $this->assertText($title, 'Parent menu link was displayed');
344

345
      // Verify menu link link.
346
      $this->clickLink($title);
347
      $title = $parent_node->title;
348
      $this->assertTitle(t("@title | Drupal", array('@title' => $title)), t('Parent menu link link target was correct'));
349 350
    }

351
    // Verify menu link.
352
    $title = $item['link_title'];
353
    $this->assertText($title, 'Menu link was displayed');
354

355
    // Verify menu link link.
356
    $this->clickLink($title);
357
    $title = $item_node->title;
358
    $this->assertTitle(t("@title | Drupal", array('@title' => $title)), t('Menu link link target was correct'));
359 360 361
  }

  /**
362
   * Modify a menu link using the menu module UI.
363
   *
364
   * @param array &$item Menu link passed by reference.
365
   */
366
  function modifyMenuLink(&$item) {
367 368 369 370 371
    $item['link_title'] = $this->randomName(16);

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

372
    // Edit menu link.
373
    $edit = array();
374
    $edit['link_title'] = $title;
375
    $this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
376 377 378
    $this->assertResponse(200);
    // Unlike most other modules, there is no confirmation message displayed.

379
    // Verify menu link.
380
    $this->drupalGet('admin/structure/menu/manage/' . $item['menu_name']);
381
    $this->assertText($title, 'Menu link was edited');
382 383 384
  }

  /**
385
   * Reset a standard menu link using the menu module UI.
386
   *
387 388
   * @param array $item Menu link.
   * @param string $old_title Original title for menu link.
389
   */
390
  function resetMenuLink($item, $old_title) {
391 392 393
    $mlid = $item['mlid'];
    $title = $item['link_title'];

394
    // Reset menu link.
395
    $this->drupalPost("admin/structure/menu/item/$mlid/reset", array(), t('Reset'));
396
    $this->assertResponse(200);
397
    $this->assertRaw(t('The menu link was reset to its default settings.'), t('Menu link was reset'));
398

399
    // Verify menu link.
400
    $this->drupalGet('');
401 402
    $this->assertNoText($title, 'Menu link was reset');
    $this->assertText($old_title, 'Menu link was reset');
403 404 405
  }

  /**
406
   * Delete a menu link using the menu module UI.
407
   *
408
   * @param array $item Menu link.
409
   */
410
  function deleteMenuLink($item) {
411 412 413
    $mlid = $item['mlid'];
    $title = $item['link_title'];

414
    // Delete menu link.
415
    $this->drupalPost("admin/structure/menu/item/$mlid/delete", array(), t('Confirm'));
416
    $this->assertResponse(200);
417
    $this->assertRaw(t('The menu link %title has been deleted.', array('%title' => $title)), t('Menu link was deleted'));
418 419 420

    // Verify deletion.
    $this->drupalGet('');
421
    $this->assertNoText($title, 'Menu link was deleted');
422 423 424
  }

  /**
425
   * Alternately disable and enable a menu link.
426
   *
427 428
   * @param $item
   *   Menu link.
429
   */
430
  function toggleMenuLink($item) {
431
    $this->disableMenuLink($item);
432

433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
    // 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');
  }

  /**
   * Disable a menu link.
   *
   * @param $item
   *   Menu link.
   */
  function disableMenuLink($item) {
    $mlid = $item['mlid'];
451
    $edit['enabled'] = FALSE;
452
    $this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
453

454 455
    // Unlike most other modules, there is no confirmation message displayed.
    // Verify in the database.
456
    $hidden = db_query("SELECT hidden FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchField();
457 458
    $this->assertEqual($hidden, 1, t('Link is hidden in the database table'));
  }
459

460 461 462 463 464 465 466 467
  /**
   * Enable a menu link.
   *
   * @param $item
   *   Menu link.
   */
  function enableMenuLink($item) {
    $mlid = $item['mlid'];
468
    $edit['enabled'] = TRUE;
469
    $this->drupalPost("admin/structure/menu/item/$mlid/edit", $edit, t('Save'));
470

471
    // Verify in the database.
472
    $hidden = db_query("SELECT hidden FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchField();
473
    $this->assertEqual($hidden, 0, t('Link is not hidden in the database table'));
474 475 476
  }

  /**
477
   * Get standard menu link.
478
   */
479 480
  private function getStandardMenuLink() {
    // Retrieve menu link id of the Log out menu link, which will always be on the front page.
481
    $mlid = db_query("SELECT mlid FROM {menu_links} WHERE module = 'system' AND router_path = 'user/logout'")->fetchField();
482
    $this->assertTrue($mlid > 0, 'Standard menu link id was found');
483
    // Load menu link.
484 485
    // Use api function so that link is translated for rendering.
    $item = menu_link_load($mlid);
486
    $this->assertTrue((bool) $item, 'Standard menu link was loaded');
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
    return $item;
  }

  /**
   * Verify the logged in user has the desired access to the various menu nodes.
   *
   * @param integer $response HTTP response code.
   */
  private function verifyAccess($response = 200) {
    // View menu help node.
    $this->drupalGet('admin/help/menu');
    $this->assertResponse($response);
    if ($response == 200) {
      $this->assertText(t('Menu'), t('Menu help was displayed'));
    }

    // View menu build overview node.
504
    $this->drupalGet('admin/structure/menu');
505 506 507 508 509 510
    $this->assertResponse($response);
    if ($response == 200) {
      $this->assertText(t('Menus'), t('Menu build overview node was displayed'));
    }

    // View navigation menu customization node.
511
    $this->drupalGet('admin/structure/menu/manage/navigation');
512 513 514 515 516 517
        $this->assertResponse($response);
    if ($response == 200) {
      $this->assertText(t('Navigation'), t('Navigation menu node was displayed'));
    }

    // View menu edit node.
518
    $item = $this->getStandardMenuLink();
519
    $this->drupalGet('admin/structure/menu/item/' . $item['mlid'] . '/edit');
520 521 522 523 524 525
    $this->assertResponse($response);
    if ($response == 200) {
      $this->assertText(t('Edit menu item'), t('Menu edit node was displayed'));
    }

    // View menu settings node.
526
    $this->drupalGet('admin/structure/menu/settings');
527 528 529 530 531 532
    $this->assertResponse($response);
    if ($response == 200) {
      $this->assertText(t('Menus'), t('Menu settings node was displayed'));
    }

    // View add menu node.
533
    $this->drupalGet('admin/structure/menu/add');
534 535 536 537 538 539
    $this->assertResponse($response);
    if ($response == 200) {
      $this->assertText(t('Menus'), t('Add menu node was displayed'));
    }
  }
}
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584

/**
 * Test menu settings for nodes.
 */
class MenuNodeTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Menu settings for nodes',
      'description' => 'Add, edit, and delete a node with menu link.',
      'group' => 'Menu',
    );
  }

  function setUp() {
    parent::setUp('menu');

    $this->admin_user = $this->drupalCreateUser(array(
      'access administration pages',
      'administer content types',
      'administer menu',
      'create page content',
      'edit any page content',
      'delete any page content',
    ));
    $this->drupalLogin($this->admin_user);
  }

  /**
   * Test creating, editing, deleting menu links via node form widget.
   */
  function testMenuNodeFormWidget() {
    // Enable Navigation menu as available menu.
    $edit = array(
      'menu_options[navigation]' => 1,
    );
    $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
    // Change default parent item to Navigation menu, so we can assert more
    // easily.
    $edit = array(
      'menu_parent' => 'navigation:0',
    );
    $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));

    // Create a node.
    $node_title = $this->randomName();
585
    $language = LANGUAGE_NONE;
586
    $edit = array(
587
      "title" => $node_title,
588
      "body[$language][0][value]" => $this->randomString(),
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
    );
    $this->drupalPost('node/add/page', $edit, t('Save'));
    $node = $this->drupalGetNodeByTitle($node_title);
    // Assert that there is no link for the node.
    $this->drupalGet('');
    $this->assertNoLink($node_title);

    // Edit the node, enable the menu link setting, but skip the link title.
    $edit = array(
      'menu[enabled]' => 1,
    );
    $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
    // Assert that there is no link for the node.
    $this->drupalGet('');
    $this->assertNoLink($node_title);

    // Edit the node and create a menu link.
    $edit = array(
      'menu[enabled]' => 1,
      'menu[link_title]' => $node_title,
609
      'menu[weight]' => 17,
610 611 612 613 614 615
    );
    $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
    // Assert that the link exists.
    $this->drupalGet('');
    $this->assertLink($node_title);

616 617 618
    $this->drupalGet('node/' . $node->nid . '/edit');
    $this->assertOptionSelected('edit-menu-weight', 17, t('Menu weight correct in edit form'));

619 620 621 622 623 624 625 626 627 628
    // Edit the node and remove the menu link.
    $edit = array(
      'menu[enabled]' => FALSE,
    );
    $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
    // Assert that there is no link for the node.
    $this->drupalGet('');
    $this->assertNoLink($node_title);
  }
}