cms/drupal/modules/simpletest/tests/menu.test
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * Provides SimpleTests for menu.inc.
       
     6  */
       
     7 
       
     8 class MenuWebTestCase extends DrupalWebTestCase {
       
     9   function setUp() {
       
    10     $modules = func_get_args();
       
    11     if (isset($modules[0]) && is_array($modules[0])) {
       
    12       $modules = $modules[0];
       
    13     }
       
    14     parent::setUp($modules);
       
    15   }
       
    16 
       
    17   /**
       
    18    * Assert that a given path shows certain breadcrumb links.
       
    19    *
       
    20    * @param string $goto
       
    21    *   (optional) A system path to pass to DrupalWebTestCase::drupalGet().
       
    22    * @param array $trail
       
    23    *   An associative array whose keys are expected breadcrumb link paths and
       
    24    *   whose values are expected breadcrumb link texts (not sanitized).
       
    25    * @param string $page_title
       
    26    *   (optional) A page title to additionally assert via
       
    27    *   DrupalWebTestCase::assertTitle(). Without site name suffix.
       
    28    * @param array $tree
       
    29    *   (optional) An associative array whose keys are link paths and whose
       
    30    *   values are link titles (not sanitized) of an expected active trail in a
       
    31    *   menu tree output on the page.
       
    32    * @param $last_active
       
    33    *   (optional) Whether the last link in $tree is expected to be active (TRUE)
       
    34    *   or just to be in the active trail (FALSE).
       
    35    */
       
    36   protected function assertBreadcrumb($goto, array $trail, $page_title = NULL, array $tree = array(), $last_active = TRUE) {
       
    37     if (isset($goto)) {
       
    38       $this->drupalGet($goto);
       
    39     }
       
    40     // Compare paths with actual breadcrumb.
       
    41     $parts = $this->getParts();
       
    42     $pass = TRUE;
       
    43     foreach ($trail as $path => $title) {
       
    44       $url = url($path);
       
    45       $part = array_shift($parts);
       
    46       $pass = ($pass && $part['href'] === $url && $part['text'] === check_plain($title));
       
    47     }
       
    48     // No parts must be left, or an expected "Home" will always pass.
       
    49     $pass = ($pass && empty($parts));
       
    50 
       
    51     $this->assertTrue($pass, format_string('Breadcrumb %parts found on @path.', array(
       
    52       '%parts' => implode(' » ', $trail),
       
    53       '@path' => $this->getUrl(),
       
    54     )));
       
    55 
       
    56     // Additionally assert page title, if given.
       
    57     if (isset($page_title)) {
       
    58       $this->assertTitle(strtr('@title | Drupal', array('@title' => $page_title)));
       
    59     }
       
    60 
       
    61     // Additionally assert active trail in a menu tree output, if given.
       
    62     if ($tree) {
       
    63       end($tree);
       
    64       $active_link_path = key($tree);
       
    65       $active_link_title = array_pop($tree);
       
    66       $xpath = '';
       
    67       if ($tree) {
       
    68         $i = 0;
       
    69         foreach ($tree as $link_path => $link_title) {
       
    70           $part_xpath = (!$i ? '//' : '/following-sibling::ul/descendant::');
       
    71           $part_xpath .= 'li[contains(@class, :class)]/a[contains(@href, :href) and contains(text(), :title)]';
       
    72           $part_args = array(
       
    73             ':class' => 'active-trail',
       
    74             ':href' => url($link_path),
       
    75             ':title' => $link_title,
       
    76           );
       
    77           $xpath .= $this->buildXPathQuery($part_xpath, $part_args);
       
    78           $i++;
       
    79         }
       
    80         $elements = $this->xpath($xpath);
       
    81         $this->assertTrue(!empty($elements), 'Active trail to current page was found in menu tree.');
       
    82 
       
    83         // Append prefix for active link asserted below.
       
    84         $xpath .= '/following-sibling::ul/descendant::';
       
    85       }
       
    86       else {
       
    87         $xpath .= '//';
       
    88       }
       
    89       $xpath_last_active = ($last_active ? 'and contains(@class, :class-active)' : '');
       
    90       $xpath .= 'li[contains(@class, :class-trail)]/a[contains(@href, :href) ' . $xpath_last_active . 'and contains(text(), :title)]';
       
    91       $args = array(
       
    92         ':class-trail' => 'active-trail',
       
    93         ':class-active' => 'active',
       
    94         ':href' => url($active_link_path),
       
    95         ':title' => $active_link_title,
       
    96       );
       
    97       $elements = $this->xpath($xpath, $args);
       
    98       $this->assertTrue(!empty($elements), format_string('Active link %title was found in menu tree, including active trail links %tree.', array(
       
    99         '%title' => $active_link_title,
       
   100         '%tree' => implode(' » ', $tree),
       
   101       )));
       
   102     }
       
   103   }
       
   104 
       
   105   /**
       
   106    * Returns the breadcrumb contents of the current page in the internal browser.
       
   107    */
       
   108   protected function getParts() {
       
   109     $parts = array();
       
   110     $elements = $this->xpath('//div[@class="breadcrumb"]/a');
       
   111     if (!empty($elements)) {
       
   112       foreach ($elements as $element) {
       
   113         $parts[] = array(
       
   114           'text' => (string) $element,
       
   115           'href' => (string) $element['href'],
       
   116           'title' => (string) $element['title'],
       
   117         );
       
   118       }
       
   119     }
       
   120     return $parts;
       
   121   }
       
   122 }
       
   123 
       
   124 class MenuRouterTestCase extends DrupalWebTestCase {
       
   125   public static function getInfo() {
       
   126     return array(
       
   127       'name' => 'Menu router',
       
   128       'description' => 'Tests menu router and hook_menu() functionality.',
       
   129       'group' => 'Menu',
       
   130     );
       
   131   }
       
   132 
       
   133   function setUp() {
       
   134     // Enable dummy module that implements hook_menu.
       
   135     parent::setUp('menu_test');
       
   136     // Make the tests below more robust by explicitly setting the default theme
       
   137     // and administrative theme that they expect.
       
   138     theme_enable(array('bartik'));
       
   139     variable_set('theme_default', 'bartik');
       
   140     variable_set('admin_theme', 'seven');
       
   141   }
       
   142 
       
   143   /**
       
   144    * Test title callback set to FALSE.
       
   145    */
       
   146   function testTitleCallbackFalse() {
       
   147     $this->drupalGet('node');
       
   148     $this->assertText('A title with @placeholder', 'Raw text found on the page');
       
   149     $this->assertNoText(t('A title with @placeholder', array('@placeholder' => 'some other text')), 'Text with placeholder substitutions not found.');
       
   150   }
       
   151 
       
   152   /**
       
   153    * Tests page title of MENU_CALLBACKs.
       
   154    */
       
   155   function testTitleMenuCallback() {
       
   156     // Verify that the menu router item title is not visible.
       
   157     $this->drupalGet('');
       
   158     $this->assertNoText(t('Menu Callback Title'));
       
   159     // Verify that the menu router item title is output as page title.
       
   160     $this->drupalGet('menu_callback_title');
       
   161     $this->assertText(t('Menu Callback Title'));
       
   162   }
       
   163 
       
   164   /**
       
   165    * Test the theme callback when it is set to use an administrative theme.
       
   166    */
       
   167   function testThemeCallbackAdministrative() {
       
   168     $this->drupalGet('menu-test/theme-callback/use-admin-theme');
       
   169     $this->assertText('Custom theme: seven. Actual theme: seven.', 'The administrative theme can be correctly set in a theme callback.');
       
   170     $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page.");
       
   171   }
       
   172 
       
   173   /**
       
   174    * Test that the theme callback is properly inherited.
       
   175    */
       
   176   function testThemeCallbackInheritance() {
       
   177     $this->drupalGet('menu-test/theme-callback/use-admin-theme/inheritance');
       
   178     $this->assertText('Custom theme: seven. Actual theme: seven. Theme callback inheritance is being tested.', 'Theme callback inheritance correctly uses the administrative theme.');
       
   179     $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page.");
       
   180   }
       
   181 
       
   182   /**
       
   183    * Test that 'page callback', 'file' and 'file path' keys are properly
       
   184    * inherited from parent menu paths.
       
   185    */
       
   186   function testFileInheritance() {
       
   187     $this->drupalGet('admin/config/development/file-inheritance');
       
   188     $this->assertText('File inheritance test description', 'File inheritance works.');
       
   189   }
       
   190 
       
   191   /**
       
   192    * Test path containing "exotic" characters.
       
   193    */
       
   194   function testExoticPath() {
       
   195     $path = "menu-test/ -._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters.
       
   196       "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
       
   197       "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
       
   198     $this->drupalGet($path);
       
   199     $this->assertRaw('This is menu_test_callback().');
       
   200   }
       
   201 
       
   202   /**
       
   203    * Test the theme callback when the site is in maintenance mode.
       
   204    */
       
   205   function testThemeCallbackMaintenanceMode() {
       
   206     variable_set('maintenance_mode', TRUE);
       
   207 
       
   208     // For a regular user, the fact that the site is in maintenance mode means
       
   209     // we expect the theme callback system to be bypassed entirely.
       
   210     $this->drupalGet('menu-test/theme-callback/use-admin-theme');
       
   211     $this->assertRaw('bartik/css/style.css', "The maintenance theme's CSS appears on the page.");
       
   212 
       
   213     // An administrator, however, should continue to see the requested theme.
       
   214     $admin_user = $this->drupalCreateUser(array('access site in maintenance mode'));
       
   215     $this->drupalLogin($admin_user);
       
   216     $this->drupalGet('menu-test/theme-callback/use-admin-theme');
       
   217     $this->assertText('Custom theme: seven. Actual theme: seven.', 'The theme callback system is correctly triggered for an administrator when the site is in maintenance mode.');
       
   218     $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page.");
       
   219   }
       
   220 
       
   221   /**
       
   222    * Make sure the maintenance mode can be bypassed using hook_menu_site_status_alter().
       
   223    *
       
   224    * @see hook_menu_site_status_alter().
       
   225    */
       
   226   function testMaintenanceModeLoginPaths() {
       
   227     variable_set('maintenance_mode', TRUE);
       
   228 
       
   229     $offline_message = t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')));
       
   230     $this->drupalLogout();
       
   231     $this->drupalGet('node');
       
   232     $this->assertText($offline_message);
       
   233     $this->drupalGet('menu_login_callback');
       
   234     $this->assertText('This is menu_login_callback().', t('Maintenance mode can be bypassed through hook_login_paths().'));
       
   235   }
       
   236 
       
   237   /**
       
   238    * Test that an authenticated user hitting 'user/login' gets redirected to
       
   239    * 'user' and 'user/register' gets redirected to the user edit page.
       
   240    */
       
   241   function testAuthUserUserLogin() {
       
   242     $loggedInUser = $this->drupalCreateUser(array());
       
   243     $this->drupalLogin($loggedInUser);
       
   244 
       
   245     $this->drupalGet('user/login');
       
   246     // Check that we got to 'user'.
       
   247     $this->assertTrue($this->url == url('user', array('absolute' => TRUE)), "Logged-in user redirected to q=user on accessing q=user/login");
       
   248 
       
   249     // user/register should redirect to user/UID/edit.
       
   250     $this->drupalGet('user/register');
       
   251     $this->assertTrue($this->url == url('user/' . $this->loggedInUser->uid . '/edit', array('absolute' => TRUE)), "Logged-in user redirected to q=user/UID/edit on accessing q=user/register");
       
   252   }
       
   253 
       
   254   /**
       
   255    * Test the theme callback when it is set to use an optional theme.
       
   256    */
       
   257   function testThemeCallbackOptionalTheme() {
       
   258     // Request a theme that is not enabled.
       
   259     $this->drupalGet('menu-test/theme-callback/use-stark-theme');
       
   260     $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when a theme that is not enabled is requested.');
       
   261     $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page.");
       
   262 
       
   263     // Now enable the theme and request it again.
       
   264     theme_enable(array('stark'));
       
   265     $this->drupalGet('menu-test/theme-callback/use-stark-theme');
       
   266     $this->assertText('Custom theme: stark. Actual theme: stark.', 'The theme callback system uses an optional theme once it has been enabled.');
       
   267     $this->assertRaw('stark/layout.css', "The optional theme's CSS appears on the page.");
       
   268   }
       
   269 
       
   270   /**
       
   271    * Test the theme callback when it is set to use a theme that does not exist.
       
   272    */
       
   273   function testThemeCallbackFakeTheme() {
       
   274     $this->drupalGet('menu-test/theme-callback/use-fake-theme');
       
   275     $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when a theme that does not exist is requested.');
       
   276     $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page.");
       
   277   }
       
   278 
       
   279   /**
       
   280    * Test the theme callback when no theme is requested.
       
   281    */
       
   282   function testThemeCallbackNoThemeRequested() {
       
   283     $this->drupalGet('menu-test/theme-callback/no-theme-requested');
       
   284     $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when no theme is requested.');
       
   285     $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page.");
       
   286   }
       
   287 
       
   288   /**
       
   289    * Test that hook_custom_theme() can control the theme of a page.
       
   290    */
       
   291   function testHookCustomTheme() {
       
   292     // Trigger hook_custom_theme() to dynamically request the Stark theme for
       
   293     // the requested page.
       
   294     variable_set('menu_test_hook_custom_theme_name', 'stark');
       
   295     theme_enable(array('stark'));
       
   296 
       
   297     // Visit a page that does not implement a theme callback. The above request
       
   298     // should be honored.
       
   299     $this->drupalGet('menu-test/no-theme-callback');
       
   300     $this->assertText('Custom theme: stark. Actual theme: stark.', 'The result of hook_custom_theme() is used as the theme for the current page.');
       
   301     $this->assertRaw('stark/layout.css', "The Stark theme's CSS appears on the page.");
       
   302   }
       
   303 
       
   304   /**
       
   305    * Test that the theme callback wins out over hook_custom_theme().
       
   306    */
       
   307   function testThemeCallbackHookCustomTheme() {
       
   308     // Trigger hook_custom_theme() to dynamically request the Stark theme for
       
   309     // the requested page.
       
   310     variable_set('menu_test_hook_custom_theme_name', 'stark');
       
   311     theme_enable(array('stark'));
       
   312 
       
   313     // The menu "theme callback" should take precedence over a value set in
       
   314     // hook_custom_theme().
       
   315     $this->drupalGet('menu-test/theme-callback/use-admin-theme');
       
   316     $this->assertText('Custom theme: seven. Actual theme: seven.', 'The result of hook_custom_theme() does not override what was set in a theme callback.');
       
   317     $this->assertRaw('seven/style.css', "The Seven theme's CSS appears on the page.");
       
   318   }
       
   319 
       
   320   /**
       
   321    * Tests for menu_link_maintain().
       
   322    */
       
   323   function testMenuLinkMaintain() {
       
   324     $admin_user = $this->drupalCreateUser(array('administer site configuration'));
       
   325     $this->drupalLogin($admin_user);
       
   326 
       
   327     // Create three menu items.
       
   328     menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/1', 'Menu link #1');
       
   329     menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/1', 'Menu link #1-1');
       
   330     menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/2', 'Menu link #2');
       
   331 
       
   332     // Move second link to the main-menu, to test caching later on.
       
   333     db_update('menu_links')
       
   334       ->fields(array('menu_name' => 'main-menu'))
       
   335       ->condition('link_title', 'Menu link #1-1')
       
   336       ->condition('customized', 0)
       
   337       ->condition('module', 'menu_test')
       
   338       ->execute();
       
   339     menu_cache_clear('main-menu');
       
   340 
       
   341     // Load front page.
       
   342     $this->drupalGet('node');
       
   343     $this->assertLink(t('Menu link #1'), 0, 'Found menu link #1');
       
   344     $this->assertLink(t('Menu link #1-1'), 0, 'Found menu link #1-1');
       
   345     $this->assertLink(t('Menu link #2'), 0, 'Found menu link #2');
       
   346 
       
   347     // Rename all links for the given path.
       
   348     menu_link_maintain('menu_test', 'update', 'menu_test_maintain/1', 'Menu link updated');
       
   349     // Load a different page to be sure that we have up to date information.
       
   350     $this->drupalGet('menu_test_maintain/1');
       
   351     $this->assertLink(t('Menu link updated'), 0, 'Found updated menu link');
       
   352     $this->assertNoLink(t('Menu link #1'), 0, 'Not found menu link #1');
       
   353     $this->assertNoLink(t('Menu link #1'), 0, 'Not found menu link #1-1');
       
   354     $this->assertLink(t('Menu link #2'), 0, 'Found menu link #2');
       
   355 
       
   356     // Delete all links for the given path.
       
   357     menu_link_maintain('menu_test', 'delete', 'menu_test_maintain/1', '');
       
   358     // Load a different page to be sure that we have up to date information.
       
   359     $this->drupalGet('menu_test_maintain/2');
       
   360     $this->assertNoLink(t('Menu link updated'), 0, 'Not found deleted menu link');
       
   361     $this->assertNoLink(t('Menu link #1'), 0, 'Not found menu link #1');
       
   362     $this->assertNoLink(t('Menu link #1'), 0, 'Not found menu link #1-1');
       
   363     $this->assertLink(t('Menu link #2'), 0, 'Found menu link #2');
       
   364   }
       
   365 
       
   366   /**
       
   367    * Test menu_get_names().
       
   368    */
       
   369   function testMenuGetNames() {
       
   370     // Create three menu items.
       
   371     for ($i = 0; $i < 3; $i++) {
       
   372       $menu_link = array(
       
   373         'link_title' => 'Menu link #' . $i,
       
   374         'link_path' => 'menu_test/' . $i,
       
   375         'module' => 'menu_test',
       
   376         'menu_name' => 'menu_test_' . $i,
       
   377       );
       
   378       menu_link_save($menu_link);
       
   379     }
       
   380 
       
   381     drupal_static_reset('menu_get_names');
       
   382 
       
   383     // Verify that the menu names are correctly reported by menu_get_names().
       
   384     $menu_names = menu_get_names();
       
   385     $this->pass(implode(' | ', $menu_names));
       
   386     for ($i = 0; $i < 3; $i++) {
       
   387       $this->assertTrue(in_array('menu_test_' . $i, $menu_names), t('Expected menu name %expected is returned.', array('%expected' => 'menu_test_' . $i)));
       
   388     }
       
   389   }
       
   390 
       
   391   /**
       
   392    * Tests for menu_name parameter for hook_menu().
       
   393    */
       
   394   function testMenuName() {
       
   395     $admin_user = $this->drupalCreateUser(array('administer site configuration'));
       
   396     $this->drupalLogin($admin_user);
       
   397 
       
   398     $sql = "SELECT menu_name FROM {menu_links} WHERE router_path = 'menu_name_test'";
       
   399     $name = db_query($sql)->fetchField();
       
   400     $this->assertEqual($name, 'original', 'Menu name is "original".');
       
   401 
       
   402     // Change the menu_name parameter in menu_test.module, then force a menu
       
   403     // rebuild.
       
   404     menu_test_menu_name('changed');
       
   405     menu_rebuild();
       
   406 
       
   407     $sql = "SELECT menu_name FROM {menu_links} WHERE router_path = 'menu_name_test'";
       
   408     $name = db_query($sql)->fetchField();
       
   409     $this->assertEqual($name, 'changed', 'Menu name was successfully changed after rebuild.');
       
   410   }
       
   411 
       
   412   /**
       
   413    * Tests for menu hierarchy.
       
   414    */
       
   415   function testMenuHierarchy() {
       
   416     $parent_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent'))->fetchAssoc();
       
   417     $child_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent/child'))->fetchAssoc();
       
   418     $unattached_child_link = db_query('SELECT * FROM {menu_links} WHERE link_path = :link_path', array(':link_path' => 'menu-test/hierarchy/parent/child2/child'))->fetchAssoc();
       
   419 
       
   420     $this->assertEqual($child_link['plid'], $parent_link['mlid'], 'The parent of a directly attached child is correct.');
       
   421     $this->assertEqual($unattached_child_link['plid'], $parent_link['mlid'], 'The parent of a non-directly attached child is correct.');
       
   422   }
       
   423 
       
   424   /**
       
   425    * Tests menu link depth and parents of local tasks and menu callbacks.
       
   426    */
       
   427   function testMenuHidden() {
       
   428     // Verify links for one dynamic argument.
       
   429     $links = db_select('menu_links', 'ml')
       
   430       ->fields('ml')
       
   431       ->condition('ml.router_path', 'menu-test/hidden/menu%', 'LIKE')
       
   432       ->orderBy('ml.router_path')
       
   433       ->execute()
       
   434       ->fetchAllAssoc('router_path', PDO::FETCH_ASSOC);
       
   435 
       
   436     $parent = $links['menu-test/hidden/menu'];
       
   437     $depth = $parent['depth'] + 1;
       
   438     $plid = $parent['mlid'];
       
   439 
       
   440     $link = $links['menu-test/hidden/menu/list'];
       
   441     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   442     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   443 
       
   444     $link = $links['menu-test/hidden/menu/add'];
       
   445     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   446     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   447 
       
   448     $link = $links['menu-test/hidden/menu/settings'];
       
   449     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   450     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   451 
       
   452     $link = $links['menu-test/hidden/menu/manage/%'];
       
   453     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   454     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   455 
       
   456     $parent = $links['menu-test/hidden/menu/manage/%'];
       
   457     $depth = $parent['depth'] + 1;
       
   458     $plid = $parent['mlid'];
       
   459 
       
   460     $link = $links['menu-test/hidden/menu/manage/%/list'];
       
   461     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   462     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   463 
       
   464     $link = $links['menu-test/hidden/menu/manage/%/add'];
       
   465     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   466     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   467 
       
   468     $link = $links['menu-test/hidden/menu/manage/%/edit'];
       
   469     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   470     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   471 
       
   472     $link = $links['menu-test/hidden/menu/manage/%/delete'];
       
   473     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   474     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   475 
       
   476     // Verify links for two dynamic arguments.
       
   477     $links = db_select('menu_links', 'ml')
       
   478       ->fields('ml')
       
   479       ->condition('ml.router_path', 'menu-test/hidden/block%', 'LIKE')
       
   480       ->orderBy('ml.router_path')
       
   481       ->execute()
       
   482       ->fetchAllAssoc('router_path', PDO::FETCH_ASSOC);
       
   483 
       
   484     $parent = $links['menu-test/hidden/block'];
       
   485     $depth = $parent['depth'] + 1;
       
   486     $plid = $parent['mlid'];
       
   487 
       
   488     $link = $links['menu-test/hidden/block/list'];
       
   489     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   490     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   491 
       
   492     $link = $links['menu-test/hidden/block/add'];
       
   493     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   494     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   495 
       
   496     $link = $links['menu-test/hidden/block/manage/%/%'];
       
   497     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   498     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   499 
       
   500     $parent = $links['menu-test/hidden/block/manage/%/%'];
       
   501     $depth = $parent['depth'] + 1;
       
   502     $plid = $parent['mlid'];
       
   503 
       
   504     $link = $links['menu-test/hidden/block/manage/%/%/configure'];
       
   505     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   506     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   507 
       
   508     $link = $links['menu-test/hidden/block/manage/%/%/delete'];
       
   509     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
       
   510     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
       
   511   }
       
   512 
       
   513   /**
       
   514    * Test menu_get_item() with empty ancestors.
       
   515    */
       
   516   function testMenuGetItemNoAncestors() {
       
   517     variable_set('menu_masks', array());
       
   518     $this->drupalGet('');
       
   519   }
       
   520 
       
   521   /**
       
   522    * Test menu_set_item().
       
   523    */
       
   524   function testMenuSetItem() {
       
   525     $item = menu_get_item('node');
       
   526 
       
   527     $this->assertEqual($item['path'], 'node', "Path from menu_get_item('node') is equal to 'node'", 'menu');
       
   528 
       
   529     // Modify the path for the item then save it.
       
   530     $item['path'] = 'node_test';
       
   531     $item['href'] = 'node_test';
       
   532 
       
   533     menu_set_item('node', $item);
       
   534     $compare_item = menu_get_item('node');
       
   535     $this->assertEqual($compare_item, $item, 'Modified menu item is equal to newly retrieved menu item.', 'menu');
       
   536   }
       
   537 
       
   538   /**
       
   539    * Test menu maintenance hooks.
       
   540    */
       
   541   function testMenuItemHooks() {
       
   542     // Create an item.
       
   543     menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/4', 'Menu link #4');
       
   544     $this->assertEqual(menu_test_static_variable(), 'insert', 'hook_menu_link_insert() fired correctly');
       
   545     // Update the item.
       
   546     menu_link_maintain('menu_test', 'update', 'menu_test_maintain/4', 'Menu link updated');
       
   547     $this->assertEqual(menu_test_static_variable(), 'update', 'hook_menu_link_update() fired correctly');
       
   548     // Delete the item.
       
   549     menu_link_maintain('menu_test', 'delete', 'menu_test_maintain/4', '');
       
   550     $this->assertEqual(menu_test_static_variable(), 'delete', 'hook_menu_link_delete() fired correctly');
       
   551   }
       
   552 
       
   553   /**
       
   554    * Test menu link 'options' storage and rendering.
       
   555    */
       
   556   function testMenuLinkOptions() {
       
   557     // Create a menu link with options.
       
   558     $menu_link = array(
       
   559       'link_title' => 'Menu link options test',
       
   560       'link_path' => 'node',
       
   561       'module' => 'menu_test',
       
   562       'options' => array(
       
   563         'attributes' => array(
       
   564           'title' => 'Test title attribute',
       
   565         ),
       
   566         'query' => array(
       
   567           'testparam' => 'testvalue',
       
   568         ),
       
   569       ),
       
   570     );
       
   571     menu_link_save($menu_link);
       
   572 
       
   573     // Load front page.
       
   574     $this->drupalGet('node');
       
   575     $this->assertRaw('title="Test title attribute"', 'Title attribute of a menu link renders.');
       
   576     $this->assertRaw('testparam=testvalue', 'Query parameter added to menu link.');
       
   577   }
       
   578 
       
   579   /**
       
   580    * Tests the possible ways to set the title for menu items.
       
   581    * Also tests that menu item titles work with string overrides.
       
   582    */
       
   583   function testMenuItemTitlesCases() {
       
   584 
       
   585     // Build array with string overrides.
       
   586     $test_data = array(
       
   587       1 => array('Example title - Case 1' => 'Alternative example title - Case 1'),
       
   588       2 => array('Example @sub1 - Case @op2' => 'Alternative example @sub1 - Case @op2'),
       
   589       3 => array('Example title' => 'Alternative example title'),
       
   590       4 => array('Example title' => 'Alternative example title'),
       
   591     );
       
   592 
       
   593     foreach ($test_data as $case_no => $override) {
       
   594       $this->menuItemTitlesCasesHelper($case_no);
       
   595       variable_set('locale_custom_strings_en', array('' => $override));
       
   596       $this->menuItemTitlesCasesHelper($case_no, TRUE);
       
   597       variable_set('locale_custom_strings_en', array());
       
   598     }
       
   599   }
       
   600 
       
   601   /**
       
   602    * Get a URL and assert the title given a case number. If override is true,
       
   603    * the title is asserted to begin with "Alternative".
       
   604    */
       
   605   private function menuItemTitlesCasesHelper($case_no, $override = FALSE) {
       
   606     $this->drupalGet('menu-title-test/case' . $case_no);
       
   607     $this->assertResponse(200);
       
   608     $asserted_title = $override ? 'Alternative example title - Case ' . $case_no : 'Example title - Case ' . $case_no;
       
   609     $this->assertTitle($asserted_title . ' | Drupal', format_string('Menu title is: %title.', array('%title' => $asserted_title)), 'Menu');
       
   610   }
       
   611 
       
   612   /**
       
   613    * Load the router for a given path.
       
   614    */
       
   615   protected function menuLoadRouter($router_path) {
       
   616     return db_query('SELECT * FROM {menu_router} WHERE path = :path', array(':path' => $router_path))->fetchAssoc();
       
   617   }
       
   618 
       
   619   /**
       
   620    * Tests inheritance of 'load arguments'.
       
   621    */
       
   622   function testMenuLoadArgumentsInheritance() {
       
   623     $expected = array(
       
   624       'menu-test/arguments/%/%' => array(
       
   625         2 => array('menu_test_argument_load' => array(3)),
       
   626         3 => NULL,
       
   627       ),
       
   628       // Arguments are inherited to normal children.
       
   629       'menu-test/arguments/%/%/default' => array(
       
   630         2 => array('menu_test_argument_load' => array(3)),
       
   631         3 => NULL,
       
   632       ),
       
   633       // Arguments are inherited to tab children.
       
   634       'menu-test/arguments/%/%/task' => array(
       
   635         2 => array('menu_test_argument_load' => array(3)),
       
   636         3 => NULL,
       
   637       ),
       
   638       // Arguments are only inherited to the same loader functions.
       
   639       'menu-test/arguments/%/%/common-loader' => array(
       
   640         2 => array('menu_test_argument_load' => array(3)),
       
   641         3 => 'menu_test_other_argument_load',
       
   642       ),
       
   643       // Arguments are not inherited to children not using the same loader
       
   644       // function.
       
   645       'menu-test/arguments/%/%/different-loaders-1' => array(
       
   646         2 => NULL,
       
   647         3 => 'menu_test_argument_load',
       
   648       ),
       
   649       'menu-test/arguments/%/%/different-loaders-2' => array(
       
   650         2 => 'menu_test_other_argument_load',
       
   651         3 => NULL,
       
   652       ),
       
   653       'menu-test/arguments/%/%/different-loaders-3' => array(
       
   654         2 => NULL,
       
   655         3 => NULL,
       
   656       ),
       
   657       // Explicit loader arguments should not be overriden by parent.
       
   658       'menu-test/arguments/%/%/explicit-arguments' => array(
       
   659         2 => array('menu_test_argument_load' => array()),
       
   660         3 => NULL,
       
   661       ),
       
   662     );
       
   663 
       
   664     foreach ($expected as $router_path => $load_functions) {
       
   665       $router_item = $this->menuLoadRouter($router_path);
       
   666       $this->assertIdentical(unserialize($router_item['load_functions']), $load_functions, format_string('Expected load functions for router %router_path' , array('%router_path' => $router_path)));
       
   667     }
       
   668   }
       
   669 }
       
   670 
       
   671 /**
       
   672  * Tests for menu links.
       
   673  */
       
   674 class MenuLinksUnitTestCase extends DrupalWebTestCase {
       
   675   // Use the lightweight testing profile for this test.
       
   676   protected $profile = 'testing';
       
   677 
       
   678   public static function getInfo() {
       
   679     return array(
       
   680       'name' => 'Menu links',
       
   681       'description' => 'Test handling of menu links hierarchies.',
       
   682       'group' => 'Menu',
       
   683     );
       
   684   }
       
   685 
       
   686   /**
       
   687    * Create a simple hierarchy of links.
       
   688    */
       
   689   function createLinkHierarchy($module = 'menu_test') {
       
   690     // First remove all the menu links.
       
   691     db_truncate('menu_links')->execute();
       
   692 
       
   693     // Then create a simple link hierarchy:
       
   694     // - $parent
       
   695     //   - $child-1
       
   696     //      - $child-1-1
       
   697     //      - $child-1-2
       
   698     //   - $child-2
       
   699     $base_options = array(
       
   700       'link_title' => 'Menu link test',
       
   701       'module' => $module,
       
   702       'menu_name' => 'menu_test',
       
   703     );
       
   704 
       
   705     $links['parent'] = $base_options + array(
       
   706       'link_path' => 'menu-test/parent',
       
   707     );
       
   708     menu_link_save($links['parent']);
       
   709 
       
   710     $links['child-1'] = $base_options + array(
       
   711       'link_path' => 'menu-test/parent/child-1',
       
   712       'plid' => $links['parent']['mlid'],
       
   713     );
       
   714     menu_link_save($links['child-1']);
       
   715 
       
   716     $links['child-1-1'] = $base_options + array(
       
   717       'link_path' => 'menu-test/parent/child-1/child-1-1',
       
   718       'plid' => $links['child-1']['mlid'],
       
   719     );
       
   720     menu_link_save($links['child-1-1']);
       
   721 
       
   722     $links['child-1-2'] = $base_options + array(
       
   723       'link_path' => 'menu-test/parent/child-1/child-1-2',
       
   724       'plid' => $links['child-1']['mlid'],
       
   725     );
       
   726     menu_link_save($links['child-1-2']);
       
   727 
       
   728     $links['child-2'] = $base_options + array(
       
   729       'link_path' => 'menu-test/parent/child-2',
       
   730       'plid' => $links['parent']['mlid'],
       
   731     );
       
   732     menu_link_save($links['child-2']);
       
   733 
       
   734     return $links;
       
   735   }
       
   736 
       
   737   /**
       
   738    * Assert that at set of links is properly parented.
       
   739    */
       
   740   function assertMenuLinkParents($links, $expected_hierarchy) {
       
   741     foreach ($expected_hierarchy as $child => $parent) {
       
   742       $mlid = $links[$child]['mlid'];
       
   743       $plid = $parent ? $links[$parent]['mlid'] : 0;
       
   744 
       
   745       $menu_link = menu_link_load($mlid);
       
   746       menu_link_save($menu_link);
       
   747       $this->assertEqual($menu_link['plid'], $plid, format_string('Menu link %mlid has parent of %plid, expected %expected_plid.', array('%mlid' => $mlid, '%plid' => $menu_link['plid'], '%expected_plid' => $plid)));
       
   748     }
       
   749   }
       
   750 
       
   751   /**
       
   752    * Test automatic reparenting of menu links.
       
   753    */
       
   754   function testMenuLinkReparenting($module = 'menu_test') {
       
   755     // Check the initial hierarchy.
       
   756     $links = $this->createLinkHierarchy($module);
       
   757 
       
   758     $expected_hierarchy = array(
       
   759       'parent' => FALSE,
       
   760       'child-1' => 'parent',
       
   761       'child-1-1' => 'child-1',
       
   762       'child-1-2' => 'child-1',
       
   763       'child-2' => 'parent',
       
   764     );
       
   765     $this->assertMenuLinkParents($links, $expected_hierarchy);
       
   766 
       
   767     // Start over, and move child-1 under child-2, and check that all the
       
   768     // childs of child-1 have been moved too.
       
   769     $links = $this->createLinkHierarchy($module);
       
   770     $links['child-1']['plid'] = $links['child-2']['mlid'];
       
   771     menu_link_save($links['child-1']);
       
   772 
       
   773     $expected_hierarchy = array(
       
   774       'parent' => FALSE,
       
   775       'child-1' => 'child-2',
       
   776       'child-1-1' => 'child-1',
       
   777       'child-1-2' => 'child-1',
       
   778       'child-2' => 'parent',
       
   779     );
       
   780     $this->assertMenuLinkParents($links, $expected_hierarchy);
       
   781 
       
   782     // Start over, and delete child-1, and check that the children of child-1
       
   783     // have been reassigned to the parent. menu_link_delete() will cowardly
       
   784     // refuse to delete a menu link defined by the system module, so skip the
       
   785     // test in that case.
       
   786     if ($module != 'system') {
       
   787       $links = $this->createLinkHierarchy($module);
       
   788       menu_link_delete($links['child-1']['mlid']);
       
   789 
       
   790       $expected_hierarchy = array(
       
   791         'parent' => FALSE,
       
   792         'child-1-1' => 'parent',
       
   793         'child-1-2' => 'parent',
       
   794         'child-2' => 'parent',
       
   795       );
       
   796       $this->assertMenuLinkParents($links, $expected_hierarchy);
       
   797     }
       
   798 
       
   799     // Start over, forcefully delete child-1 from the database, simulating a
       
   800     // database crash. Check that the children of child-1 have been reassigned
       
   801     // to the parent, going up on the old path hierarchy stored in each of the
       
   802     // links.
       
   803     $links = $this->createLinkHierarchy($module);
       
   804     // Don't do that at home.
       
   805     db_delete('menu_links')
       
   806       ->condition('mlid', $links['child-1']['mlid'])
       
   807       ->execute();
       
   808 
       
   809     $expected_hierarchy = array(
       
   810       'parent' => FALSE,
       
   811       'child-1-1' => 'parent',
       
   812       'child-1-2' => 'parent',
       
   813       'child-2' => 'parent',
       
   814     );
       
   815     $this->assertMenuLinkParents($links, $expected_hierarchy);
       
   816 
       
   817     // Start over, forcefully delete the parent from the database, simulating a
       
   818     // database crash. Check that the children of parent are now top-level.
       
   819     $links = $this->createLinkHierarchy($module);
       
   820     // Don't do that at home.
       
   821     db_delete('menu_links')
       
   822       ->condition('mlid', $links['parent']['mlid'])
       
   823       ->execute();
       
   824 
       
   825     $expected_hierarchy = array(
       
   826       'child-1-1' => 'child-1',
       
   827       'child-1-2' => 'child-1',
       
   828       'child-2' => FALSE,
       
   829     );
       
   830     $this->assertMenuLinkParents($links, $expected_hierarchy);
       
   831   }
       
   832 
       
   833   /**
       
   834    * Test automatic reparenting of menu links derived from menu routers.
       
   835    */
       
   836   function testMenuLinkRouterReparenting() {
       
   837     // Run all the standard parenting tests on menu links derived from
       
   838     // menu routers.
       
   839     $this->testMenuLinkReparenting('system');
       
   840 
       
   841     // Additionnaly, test reparenting based on path.
       
   842     $links = $this->createLinkHierarchy('system');
       
   843 
       
   844     // Move child-1-2 has a child of child-2, making the link hierarchy
       
   845     // inconsistent with the path hierarchy.
       
   846     $links['child-1-2']['plid'] = $links['child-2']['mlid'];
       
   847     menu_link_save($links['child-1-2']);
       
   848 
       
   849     // Check the new hierarchy.
       
   850     $expected_hierarchy = array(
       
   851       'parent' => FALSE,
       
   852       'child-1' => 'parent',
       
   853       'child-1-1' => 'child-1',
       
   854       'child-2' => 'parent',
       
   855       'child-1-2' => 'child-2',
       
   856     );
       
   857     $this->assertMenuLinkParents($links, $expected_hierarchy);
       
   858 
       
   859     // Now delete 'parent' directly from the database, simulating a database
       
   860     // crash. 'child-1' and 'child-2' should get moved to the
       
   861     // top-level.
       
   862     // Don't do that at home.
       
   863     db_delete('menu_links')
       
   864       ->condition('mlid', $links['parent']['mlid'])
       
   865       ->execute();
       
   866     $expected_hierarchy = array(
       
   867       'child-1' => FALSE,
       
   868       'child-1-1' => 'child-1',
       
   869       'child-2' => FALSE,
       
   870       'child-1-2' => 'child-2',
       
   871     );
       
   872     $this->assertMenuLinkParents($links, $expected_hierarchy);
       
   873 
       
   874     // Now delete 'child-2' directly from the database, simulating a database
       
   875     // crash. 'child-1-2' will get reparented under 'child-1' based on its
       
   876     // path.
       
   877     // Don't do that at home.
       
   878     db_delete('menu_links')
       
   879       ->condition('mlid', $links['child-2']['mlid'])
       
   880       ->execute();
       
   881     $expected_hierarchy = array(
       
   882       'child-1' => FALSE,
       
   883       'child-1-1' => 'child-1',
       
   884       'child-1-2' => 'child-1',
       
   885     );
       
   886     $this->assertMenuLinkParents($links, $expected_hierarchy);
       
   887   }
       
   888 }
       
   889 
       
   890 /**
       
   891  * Tests rebuilding the menu by setting 'menu_rebuild_needed.'
       
   892  */
       
   893 class MenuRebuildTestCase extends DrupalWebTestCase {
       
   894   public static function getInfo() {
       
   895     return array(
       
   896       'name' => 'Menu rebuild test',
       
   897       'description' => 'Test rebuilding of menu.',
       
   898       'group' => 'Menu',
       
   899     );
       
   900   }
       
   901 
       
   902   /**
       
   903    * Test if the 'menu_rebuild_needed' variable triggers a menu_rebuild() call.
       
   904    */
       
   905   function testMenuRebuildByVariable() {
       
   906     // Check if 'admin' path exists.
       
   907     $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField();
       
   908     $this->assertEqual($admin_exists, 'admin', "The path 'admin/' exists prior to deleting.");
       
   909 
       
   910     // Delete the path item 'admin', and test that the path doesn't exist in the database.
       
   911     $delete = db_delete('menu_router')
       
   912       ->condition('path', 'admin')
       
   913       ->execute();
       
   914     $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField();
       
   915     $this->assertFalse($admin_exists, "The path 'admin/' has been deleted and doesn't exist in the database.");
       
   916 
       
   917     // Now we enable the rebuild variable and trigger menu_execute_active_handler()
       
   918     // to rebuild the menu item. Now 'admin' should exist.
       
   919     variable_set('menu_rebuild_needed', TRUE);
       
   920     // menu_execute_active_handler() should trigger the rebuild.
       
   921     $this->drupalGet('<front>');
       
   922     $admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField();
       
   923     $this->assertEqual($admin_exists, 'admin', "The menu has been rebuilt, the path 'admin' now exists again.");
       
   924   }
       
   925 
       
   926 }
       
   927 
       
   928 /**
       
   929  * Menu tree data related tests.
       
   930  */
       
   931 class MenuTreeDataTestCase extends DrupalUnitTestCase {
       
   932   /**
       
   933    * Dummy link structure acceptable for menu_tree_data().
       
   934    */
       
   935   var $links = array(
       
   936     1 => array('mlid' => 1, 'depth' => 1),
       
   937     2 => array('mlid' => 2, 'depth' => 1),
       
   938     3 => array('mlid' => 3, 'depth' => 2),
       
   939     4 => array('mlid' => 4, 'depth' => 3),
       
   940     5 => array('mlid' => 5, 'depth' => 1),
       
   941   );
       
   942 
       
   943   public static function getInfo() {
       
   944     return array(
       
   945       'name' => 'Menu tree generation',
       
   946       'description' => 'Tests recursive menu tree generation functions.',
       
   947       'group' => 'Menu',
       
   948     );
       
   949   }
       
   950 
       
   951   /**
       
   952    * Validate the generation of a proper menu tree hierarchy.
       
   953    */
       
   954   function testMenuTreeData() {
       
   955     $tree = menu_tree_data($this->links);
       
   956 
       
   957     // Validate that parent items #1, #2, and #5 exist on the root level.
       
   958     $this->assertSameLink($this->links[1], $tree[1]['link'], 'Parent item #1 exists.');
       
   959     $this->assertSameLink($this->links[2], $tree[2]['link'], 'Parent item #2 exists.');
       
   960     $this->assertSameLink($this->links[5], $tree[5]['link'], 'Parent item #5 exists.');
       
   961 
       
   962     // Validate that child item #4 exists at the correct location in the hierarchy.
       
   963     $this->assertSameLink($this->links[4], $tree[2]['below'][3]['below'][4]['link'], 'Child item #4 exists in the hierarchy.');
       
   964   }
       
   965 
       
   966   /**
       
   967    * Check that two menu links are the same by comparing the mlid.
       
   968    *
       
   969    * @param $link1
       
   970    *   A menu link item.
       
   971    * @param $link2
       
   972    *   A menu link item.
       
   973    * @param $message
       
   974    *   The message to display along with the assertion.
       
   975    * @return
       
   976    *   TRUE if the assertion succeeded, FALSE otherwise.
       
   977    */
       
   978   protected function assertSameLink($link1, $link2, $message = '') {
       
   979     return $this->assert($link1['mlid'] == $link2['mlid'], $message ? $message : 'First link is identical to second link');
       
   980   }
       
   981 }
       
   982 
       
   983 /**
       
   984  * Menu tree output related tests.
       
   985  */
       
   986 class MenuTreeOutputTestCase extends DrupalWebTestCase {
       
   987   /**
       
   988    * Dummy link structure acceptable for menu_tree_output().
       
   989    */
       
   990   var $tree_data = array(
       
   991     '1'=> array(
       
   992       'link' => array( 'menu_name' => 'main-menu', 'mlid' => 1, 'hidden'=>0, 'has_children' => 1, 'title' => 'Item 1', 'in_active_trail' => 1, 'access'=>1, 'href' => 'a', 'localized_options' => array('attributes' => array('title' =>'')) ),
       
   993       'below' => array(
       
   994         '2' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 2, 'hidden'=>0, 'has_children' => 1, 'title' => 'Item 2', 'in_active_trail' => 1, 'access'=>1, 'href' => 'a/b', 'localized_options' => array('attributes' => array('title' =>'')) ),
       
   995           'below' => array(
       
   996             '3' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 3, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 3', 'in_active_trail' => 0, 'access'=>1, 'href' => 'a/b/c', 'localized_options' => array('attributes' => array('title' =>'')) ),
       
   997               'below' => array() ),
       
   998             '4' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 4, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 4', 'in_active_trail' => 0, 'access'=>1, 'href' => 'a/b/d', 'localized_options' => array('attributes' => array('title' =>'')) ),
       
   999               'below' => array() )
       
  1000             )
       
  1001           )
       
  1002         )
       
  1003       ),
       
  1004     '5' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 5, 'hidden'=>1, 'has_children' => 0, 'title' => 'Item 5', 'in_active_trail' => 0, 'access'=>1, 'href' => 'e', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) ),
       
  1005     '6' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 6, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 6', 'in_active_trail' => 0, 'access'=>0, 'href' => 'f', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) ),
       
  1006     '7' => array('link' => array( 'menu_name' => 'main-menu', 'mlid' => 7, 'hidden'=>0, 'has_children' => 0, 'title' => 'Item 7', 'in_active_trail' => 0, 'access'=>1, 'href' => 'g', 'localized_options' => array('attributes' => array('title' =>'')) ), 'below' => array( ) )
       
  1007   );
       
  1008 
       
  1009   public static function getInfo() {
       
  1010     return array(
       
  1011       'name' => 'Menu tree output',
       
  1012       'description' => 'Tests menu tree output functions.',
       
  1013       'group' => 'Menu',
       
  1014     );
       
  1015   }
       
  1016 
       
  1017   function setUp() {
       
  1018     parent::setUp();
       
  1019   }
       
  1020 
       
  1021   /**
       
  1022    * Validate the generation of a proper menu tree output.
       
  1023    */
       
  1024   function testMenuTreeData() {
       
  1025     $output = menu_tree_output($this->tree_data);
       
  1026 
       
  1027     // Validate that the - in main-menu is changed into an underscore
       
  1028     $this->assertEqual($output['1']['#theme'], 'menu_link__main_menu', 'Hyphen is changed to an underscore on menu_link');
       
  1029     $this->assertEqual($output['#theme_wrappers'][0], 'menu_tree__main_menu', 'Hyphen is changed to an underscore on menu_tree wrapper');
       
  1030     // Looking for child items in the data
       
  1031     $this->assertEqual( $output['1']['#below']['2']['#href'], 'a/b', 'Checking the href on a child item');
       
  1032     $this->assertTrue( in_array('active-trail',$output['1']['#below']['2']['#attributes']['class']) , 'Checking the active trail class');
       
  1033     // Validate that the hidden and no access items are missing
       
  1034     $this->assertFalse( isset($output['5']), 'Hidden item should be missing');
       
  1035     $this->assertFalse( isset($output['6']), 'False access should be missing');
       
  1036     // Item 7 is after a couple hidden items. Just to make sure that 5 and 6 are skipped and 7 still included
       
  1037     $this->assertTrue( isset($output['7']), 'Item after hidden items is present');
       
  1038   }
       
  1039 }
       
  1040 
       
  1041 /**
       
  1042  * Menu breadcrumbs related tests.
       
  1043  */
       
  1044 class MenuBreadcrumbTestCase extends MenuWebTestCase {
       
  1045   public static function getInfo() {
       
  1046     return array(
       
  1047       'name' => 'Breadcrumbs',
       
  1048       'description' => 'Tests breadcrumbs functionality.',
       
  1049       'group' => 'Menu',
       
  1050     );
       
  1051   }
       
  1052 
       
  1053   function setUp() {
       
  1054     $modules = func_get_args();
       
  1055     if (isset($modules[0]) && is_array($modules[0])) {
       
  1056       $modules = $modules[0];
       
  1057     }
       
  1058     $modules[] = 'menu_test';
       
  1059     parent::setUp($modules);
       
  1060     $perms = array_keys(module_invoke_all('permission'));
       
  1061     $this->admin_user = $this->drupalCreateUser($perms);
       
  1062     $this->drupalLogin($this->admin_user);
       
  1063 
       
  1064     // This test puts menu links in the Navigation menu and then tests for
       
  1065     // their presence on the page, so we need to ensure that the Navigation
       
  1066     // block will be displayed in all active themes.
       
  1067     db_update('block')
       
  1068       ->fields(array(
       
  1069         // Use a region that is valid for all themes.
       
  1070         'region' => 'content',
       
  1071         'status' => 1,
       
  1072       ))
       
  1073       ->condition('module', 'system')
       
  1074       ->condition('delta', 'navigation')
       
  1075       ->execute();
       
  1076   }
       
  1077 
       
  1078   /**
       
  1079    * Tests breadcrumbs on node and administrative paths.
       
  1080    */
       
  1081   function testBreadCrumbs() {
       
  1082     // Prepare common base breadcrumb elements.
       
  1083     $home = array('<front>' => 'Home');
       
  1084     $admin = $home + array('admin' => t('Administration'));
       
  1085     $config = $admin + array('admin/config' => t('Configuration'));
       
  1086     $type = 'article';
       
  1087     $langcode = LANGUAGE_NONE;
       
  1088 
       
  1089     // Verify breadcrumbs for default local tasks.
       
  1090     $expected = array(
       
  1091       'menu-test' => t('Menu test root'),
       
  1092     );
       
  1093     $title = t('Breadcrumbs test: Local tasks');
       
  1094     $trail = $home + $expected;
       
  1095     $tree = $expected + array(
       
  1096       'menu-test/breadcrumb/tasks' => $title,
       
  1097     );
       
  1098     $this->assertBreadcrumb('menu-test/breadcrumb/tasks', $trail, $title, $tree);
       
  1099     $this->assertBreadcrumb('menu-test/breadcrumb/tasks/first', $trail, $title, $tree);
       
  1100     $this->assertBreadcrumb('menu-test/breadcrumb/tasks/first/first', $trail, $title, $tree);
       
  1101     $trail += array(
       
  1102       'menu-test/breadcrumb/tasks' => t('Breadcrumbs test: Local tasks'),
       
  1103     );
       
  1104     $this->assertBreadcrumb('menu-test/breadcrumb/tasks/first/second', $trail, $title, $tree);
       
  1105     $this->assertBreadcrumb('menu-test/breadcrumb/tasks/second', $trail, $title, $tree);
       
  1106     $this->assertBreadcrumb('menu-test/breadcrumb/tasks/second/first', $trail, $title, $tree);
       
  1107     $trail += array(
       
  1108       'menu-test/breadcrumb/tasks/second' => t('Second'),
       
  1109     );
       
  1110     $this->assertBreadcrumb('menu-test/breadcrumb/tasks/second/second', $trail, $title, $tree);
       
  1111 
       
  1112     // Verify Taxonomy administration breadcrumbs.
       
  1113     $trail = $admin + array(
       
  1114       'admin/structure' => t('Structure'),
       
  1115     );
       
  1116     $this->assertBreadcrumb('admin/structure/taxonomy', $trail);
       
  1117 
       
  1118     $trail += array(
       
  1119       'admin/structure/taxonomy' => t('Taxonomy'),
       
  1120     );
       
  1121     $this->assertBreadcrumb('admin/structure/taxonomy/tags', $trail);
       
  1122     $trail += array(
       
  1123       'admin/structure/taxonomy/tags' => t('Tags'),
       
  1124     );
       
  1125     $this->assertBreadcrumb('admin/structure/taxonomy/tags/edit', $trail);
       
  1126     $this->assertBreadcrumb('admin/structure/taxonomy/tags/fields', $trail);
       
  1127     $this->assertBreadcrumb('admin/structure/taxonomy/tags/add', $trail);
       
  1128 
       
  1129     // Verify Menu administration breadcrumbs.
       
  1130     $trail = $admin + array(
       
  1131       'admin/structure' => t('Structure'),
       
  1132     );
       
  1133     $this->assertBreadcrumb('admin/structure/menu', $trail);
       
  1134 
       
  1135     $trail += array(
       
  1136       'admin/structure/menu' => t('Menus'),
       
  1137     );
       
  1138     $this->assertBreadcrumb('admin/structure/menu/manage/navigation', $trail);
       
  1139     $trail += array(
       
  1140       'admin/structure/menu/manage/navigation' => t('Navigation'),
       
  1141     );
       
  1142     $this->assertBreadcrumb("admin/structure/menu/item/6/edit", $trail);
       
  1143     $this->assertBreadcrumb('admin/structure/menu/manage/navigation/edit', $trail);
       
  1144     $this->assertBreadcrumb('admin/structure/menu/manage/navigation/add', $trail);
       
  1145 
       
  1146     // Verify Node administration breadcrumbs.
       
  1147     $trail = $admin + array(
       
  1148       'admin/structure' => t('Structure'),
       
  1149       'admin/structure/types' => t('Content types'),
       
  1150     );
       
  1151     $this->assertBreadcrumb('admin/structure/types/add', $trail);
       
  1152     $this->assertBreadcrumb("admin/structure/types/manage/$type", $trail);
       
  1153     $trail += array(
       
  1154       "admin/structure/types/manage/$type" => t('Article'),
       
  1155     );
       
  1156     $this->assertBreadcrumb("admin/structure/types/manage/$type/fields", $trail);
       
  1157     $this->assertBreadcrumb("admin/structure/types/manage/$type/display", $trail);
       
  1158     $trail_teaser = $trail + array(
       
  1159       "admin/structure/types/manage/$type/display" => t('Manage display'),
       
  1160     );
       
  1161     $this->assertBreadcrumb("admin/structure/types/manage/$type/display/teaser", $trail_teaser);
       
  1162     $this->assertBreadcrumb("admin/structure/types/manage/$type/comment/fields", $trail);
       
  1163     $this->assertBreadcrumb("admin/structure/types/manage/$type/comment/display", $trail);
       
  1164     $this->assertBreadcrumb("admin/structure/types/manage/$type/delete", $trail);
       
  1165     $trail += array(
       
  1166       "admin/structure/types/manage/$type/fields" => t('Manage fields'),
       
  1167     );
       
  1168     $this->assertBreadcrumb("admin/structure/types/manage/$type/fields/body", $trail);
       
  1169     $trail += array(
       
  1170       "admin/structure/types/manage/$type/fields/body" => t('Body'),
       
  1171     );
       
  1172     $this->assertBreadcrumb("admin/structure/types/manage/$type/fields/body/widget-type", $trail);
       
  1173 
       
  1174     // Verify Filter text format administration breadcrumbs.
       
  1175     $format = db_query_range("SELECT format, name FROM {filter_format}", 1, 1)->fetch();
       
  1176     $format_id = $format->format;
       
  1177     $trail = $config + array(
       
  1178       'admin/config/content' => t('Content authoring'),
       
  1179     );
       
  1180     $this->assertBreadcrumb('admin/config/content/formats', $trail);
       
  1181 
       
  1182     $trail += array(
       
  1183       'admin/config/content/formats' => t('Text formats'),
       
  1184     );
       
  1185     $this->assertBreadcrumb('admin/config/content/formats/add', $trail);
       
  1186     $this->assertBreadcrumb("admin/config/content/formats/$format_id", $trail);
       
  1187     $trail += array(
       
  1188       "admin/config/content/formats/$format_id" => $format->name,
       
  1189     );
       
  1190     $this->assertBreadcrumb("admin/config/content/formats/$format_id/disable", $trail);
       
  1191 
       
  1192     // Verify node breadcrumbs (without menu link).
       
  1193     $node1 = $this->drupalCreateNode();
       
  1194     $nid1 = $node1->nid;
       
  1195     $trail = $home;
       
  1196     $this->assertBreadcrumb("node/$nid1", $trail);
       
  1197     // Also verify that the node does not appear elsewhere (e.g., menu trees).
       
  1198     $this->assertNoLink($node1->title);
       
  1199     // The node itself should not be contained in the breadcrumb on the default
       
  1200     // local task, since there is no difference between both pages.
       
  1201     $this->assertBreadcrumb("node/$nid1/view", $trail);
       
  1202     // Also verify that the node does not appear elsewhere (e.g., menu trees).
       
  1203     $this->assertNoLink($node1->title);
       
  1204 
       
  1205     $trail += array(
       
  1206       "node/$nid1" => $node1->title,
       
  1207     );
       
  1208     $this->assertBreadcrumb("node/$nid1/edit", $trail);
       
  1209 
       
  1210     // Verify that breadcrumb on node listing page contains "Home" only.
       
  1211     $trail = array();
       
  1212     $this->assertBreadcrumb('node', $trail);
       
  1213 
       
  1214     // Verify node breadcrumbs (in menu).
       
  1215     // Do this separately for Main menu and Navigation menu, since only the
       
  1216     // latter is a preferred menu by default.
       
  1217     // @todo Also test all themes? Manually testing led to the suspicion that
       
  1218     //   breadcrumbs may differ, possibly due to template.php overrides.
       
  1219     $menus = array('main-menu', 'navigation');
       
  1220     // Alter node type menu settings.
       
  1221     variable_set("menu_options_$type", $menus);
       
  1222     variable_set("menu_parent_$type", 'navigation:0');
       
  1223 
       
  1224     foreach ($menus as $menu) {
       
  1225       // Create a parent node in the current menu.
       
  1226       $title = $this->randomName();
       
  1227       $node2 = $this->drupalCreateNode(array(
       
  1228         'type' => $type,
       
  1229         'title' => $title,
       
  1230         'menu' => array(
       
  1231           'enabled' => 1,
       
  1232           'link_title' => 'Parent ' . $title,
       
  1233           'description' => '',
       
  1234           'menu_name' => $menu,
       
  1235           'plid' => 0,
       
  1236         ),
       
  1237       ));
       
  1238       $nid2 = $node2->nid;
       
  1239 
       
  1240       $trail = $home;
       
  1241       $tree = array(
       
  1242         "node/$nid2" => $node2->menu['link_title'],
       
  1243       );
       
  1244       $this->assertBreadcrumb("node/$nid2", $trail, $node2->title, $tree);
       
  1245       // The node itself should not be contained in the breadcrumb on the
       
  1246       // default local task, since there is no difference between both pages.
       
  1247       $this->assertBreadcrumb("node/$nid2/view", $trail, $node2->title, $tree);
       
  1248       $trail += array(
       
  1249         "node/$nid2" => $node2->menu['link_title'],
       
  1250       );
       
  1251       $this->assertBreadcrumb("node/$nid2/edit", $trail);
       
  1252 
       
  1253       // Create a child node in the current menu.
       
  1254       $title = $this->randomName();
       
  1255       $node3 = $this->drupalCreateNode(array(
       
  1256         'type' => $type,
       
  1257         'title' => $title,
       
  1258         'menu' => array(
       
  1259           'enabled' => 1,
       
  1260           'link_title' => 'Child ' . $title,
       
  1261           'description' => '',
       
  1262           'menu_name' => $menu,
       
  1263           'plid' => $node2->menu['mlid'],
       
  1264         ),
       
  1265       ));
       
  1266       $nid3 = $node3->nid;
       
  1267 
       
  1268       $this->assertBreadcrumb("node/$nid3", $trail, $node3->title, $tree, FALSE);
       
  1269       // The node itself should not be contained in the breadcrumb on the
       
  1270       // default local task, since there is no difference between both pages.
       
  1271       $this->assertBreadcrumb("node/$nid3/view", $trail, $node3->title, $tree, FALSE);
       
  1272       $trail += array(
       
  1273         "node/$nid3" => $node3->menu['link_title'],
       
  1274       );
       
  1275       $tree += array(
       
  1276         "node/$nid3" => $node3->menu['link_title'],
       
  1277       );
       
  1278       $this->assertBreadcrumb("node/$nid3/edit", $trail);
       
  1279 
       
  1280       // Verify that node listing page still contains "Home" only.
       
  1281       $trail = array();
       
  1282       $this->assertBreadcrumb('node', $trail);
       
  1283 
       
  1284       if ($menu == 'navigation') {
       
  1285         $parent = $node2;
       
  1286         $child = $node3;
       
  1287       }
       
  1288     }
       
  1289 
       
  1290     // Create a Navigation menu link for 'node', move the last parent node menu
       
  1291     // link below it, and verify a full breadcrumb for the last child node.
       
  1292     $menu = 'navigation';
       
  1293     $edit = array(
       
  1294       'link_title' => 'Root',
       
  1295       'link_path' => 'node',
       
  1296     );
       
  1297     $this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save'));
       
  1298     $link = db_query('SELECT * FROM {menu_links} WHERE link_title = :title', array(':title' => 'Root'))->fetchAssoc();
       
  1299 
       
  1300     $edit = array(
       
  1301       'menu[parent]' => $link['menu_name'] . ':' . $link['mlid'],
       
  1302     );
       
  1303     $this->drupalPost("node/{$parent->nid}/edit", $edit, t('Save'));
       
  1304     $expected = array(
       
  1305       "node" => $link['link_title'],
       
  1306     );
       
  1307     $trail = $home + $expected;
       
  1308     $tree = $expected + array(
       
  1309       "node/{$parent->nid}" => $parent->menu['link_title'],
       
  1310     );
       
  1311     $this->assertBreadcrumb(NULL, $trail, $parent->title, $tree);
       
  1312     $trail += array(
       
  1313       "node/{$parent->nid}" => $parent->menu['link_title'],
       
  1314     );
       
  1315     $tree += array(
       
  1316       "node/{$child->nid}" => $child->menu['link_title'],
       
  1317     );
       
  1318     $this->assertBreadcrumb("node/{$child->nid}", $trail, $child->title, $tree);
       
  1319 
       
  1320     // Add a taxonomy term/tag to last node, and add a link for that term to the
       
  1321     // Navigation menu.
       
  1322     $tags = array(
       
  1323       'Drupal' => array(),
       
  1324       'Breadcrumbs' => array(),
       
  1325     );
       
  1326     $edit = array(
       
  1327       "field_tags[$langcode]" => implode(',', array_keys($tags)),
       
  1328     );
       
  1329     $this->drupalPost("node/{$parent->nid}/edit", $edit, t('Save'));
       
  1330 
       
  1331     // Put both terms into a hierarchy Drupal » Breadcrumbs. Required for both
       
  1332     // the menu links and the terms itself, since taxonomy_term_page() resets
       
  1333     // the breadcrumb based on taxonomy term hierarchy.
       
  1334     $parent_tid = 0;
       
  1335     foreach ($tags as $name => $null) {
       
  1336       $terms = taxonomy_term_load_multiple(NULL, array('name' => $name));
       
  1337       $term = reset($terms);
       
  1338       $tags[$name]['term'] = $term;
       
  1339       if ($parent_tid) {
       
  1340         $edit = array(
       
  1341           'parent[]' => array($parent_tid),
       
  1342         );
       
  1343         $this->drupalPost("taxonomy/term/{$term->tid}/edit", $edit, t('Save'));
       
  1344       }
       
  1345       $parent_tid = $term->tid;
       
  1346     }
       
  1347     $parent_mlid = 0;
       
  1348     foreach ($tags as $name => $data) {
       
  1349       $term = $data['term'];
       
  1350       $edit = array(
       
  1351         'link_title' => "$name link",
       
  1352         'link_path' => "taxonomy/term/{$term->tid}",
       
  1353         'parent' => "$menu:{$parent_mlid}",
       
  1354       );
       
  1355       $this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save'));
       
  1356       $tags[$name]['link'] = db_query('SELECT * FROM {menu_links} WHERE link_title = :title AND link_path = :href', array(
       
  1357         ':title' => $edit['link_title'],
       
  1358         ':href' => $edit['link_path'],
       
  1359       ))->fetchAssoc();
       
  1360       $tags[$name]['link']['link_path'] = $edit['link_path'];
       
  1361       $parent_mlid = $tags[$name]['link']['mlid'];
       
  1362     }
       
  1363 
       
  1364     // Verify expected breadcrumbs for menu links.
       
  1365     $trail = $home;
       
  1366     $tree = array();
       
  1367     foreach ($tags as $name => $data) {
       
  1368       $term = $data['term'];
       
  1369       $link = $data['link'];
       
  1370 
       
  1371       $tree += array(
       
  1372         $link['link_path'] => $link['link_title'],
       
  1373       );
       
  1374       $this->assertBreadcrumb($link['link_path'], $trail, $term->name, $tree);
       
  1375       $this->assertRaw(check_plain($parent->title), 'Tagged node found.');
       
  1376 
       
  1377       // Additionally make sure that this link appears only once; i.e., the
       
  1378       // untranslated menu links automatically generated from menu router items
       
  1379       // ('taxonomy/term/%') should never be translated and appear in any menu
       
  1380       // other than the breadcrumb trail.
       
  1381       $elements = $this->xpath('//div[@id=:menu]/descendant::a[@href=:href]', array(
       
  1382         ':menu' => 'block-system-navigation',
       
  1383         ':href' => url($link['link_path']),
       
  1384       ));
       
  1385       $this->assertTrue(count($elements) == 1, "Link to {$link['link_path']} appears only once.");
       
  1386 
       
  1387       // Next iteration should expect this tag as parent link.
       
  1388       // Note: Term name, not link name, due to taxonomy_term_page().
       
  1389       $trail += array(
       
  1390         $link['link_path'] => $term->name,
       
  1391       );
       
  1392     }
       
  1393 
       
  1394     // Verify breadcrumbs on user and user/%.
       
  1395     // We need to log back in and out below, and cannot simply grant the
       
  1396     // 'administer users' permission, since user_page() makes your head explode.
       
  1397     user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array(
       
  1398       'access user profiles',
       
  1399     ));
       
  1400     $this->drupalLogout();
       
  1401 
       
  1402     // Verify breadcrumb on front page.
       
  1403     $this->assertBreadcrumb('<front>', array());
       
  1404 
       
  1405     // Verify breadcrumb on user pages (without menu link) for anonymous user.
       
  1406     $trail = $home;
       
  1407     $this->assertBreadcrumb('user', $trail, t('User account'));
       
  1408     $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $this->admin_user->name);
       
  1409 
       
  1410     // Verify breadcrumb on user pages (without menu link) for registered users.
       
  1411     $this->drupalLogin($this->admin_user);
       
  1412     $trail = $home;
       
  1413     $this->assertBreadcrumb('user', $trail, $this->admin_user->name);
       
  1414     $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $this->admin_user->name);
       
  1415     $trail += array(
       
  1416       'user/' . $this->admin_user->uid => $this->admin_user->name,
       
  1417     );
       
  1418     $this->assertBreadcrumb('user/' . $this->admin_user->uid . '/edit', $trail, $this->admin_user->name);
       
  1419 
       
  1420     // Create a second user to verify breadcrumb on user pages again.
       
  1421     $this->web_user = $this->drupalCreateUser(array(
       
  1422       'administer users',
       
  1423       'access user profiles',
       
  1424     ));
       
  1425     $this->drupalLogin($this->web_user);
       
  1426 
       
  1427     // Verify correct breadcrumb and page title on another user's account pages
       
  1428     // (without menu link).
       
  1429     $trail = $home;
       
  1430     $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $this->admin_user->name);
       
  1431     $trail += array(
       
  1432       'user/' . $this->admin_user->uid => $this->admin_user->name,
       
  1433     );
       
  1434     $this->assertBreadcrumb('user/' . $this->admin_user->uid . '/edit', $trail, $this->admin_user->name);
       
  1435 
       
  1436     // Verify correct breadcrumb and page title when viewing own user account
       
  1437     // pages (without menu link).
       
  1438     $trail = $home;
       
  1439     $this->assertBreadcrumb('user/' . $this->web_user->uid, $trail, $this->web_user->name);
       
  1440     $trail += array(
       
  1441       'user/' . $this->web_user->uid => $this->web_user->name,
       
  1442     );
       
  1443     $this->assertBreadcrumb('user/' . $this->web_user->uid . '/edit', $trail, $this->web_user->name);
       
  1444 
       
  1445     // Add a Navigation menu links for 'user' and $this->admin_user.
       
  1446     // Although it may be faster to manage these links via low-level API
       
  1447     // functions, there's a lot that can go wrong in doing so.
       
  1448     $this->drupalLogin($this->admin_user);
       
  1449     $edit = array(
       
  1450       'link_title' => 'User',
       
  1451       'link_path' => 'user',
       
  1452     );
       
  1453     $this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save'));
       
  1454     $link_user = db_query('SELECT * FROM {menu_links} WHERE link_title = :title AND link_path = :href', array(
       
  1455       ':title' => $edit['link_title'],
       
  1456       ':href' => $edit['link_path'],
       
  1457     ))->fetchAssoc();
       
  1458 
       
  1459     $edit = array(
       
  1460       'link_title' => $this->admin_user->name . ' link',
       
  1461       'link_path' => 'user/' . $this->admin_user->uid,
       
  1462     );
       
  1463     $this->drupalPost("admin/structure/menu/manage/$menu/add", $edit, t('Save'));
       
  1464     $link_admin_user = db_query('SELECT * FROM {menu_links} WHERE link_title = :title AND link_path = :href', array(
       
  1465       ':title' => $edit['link_title'],
       
  1466       ':href' => $edit['link_path'],
       
  1467     ))->fetchAssoc();
       
  1468 
       
  1469     // Verify expected breadcrumbs for the two separate links.
       
  1470     $this->drupalLogout();
       
  1471     $trail = $home;
       
  1472     $tree = array(
       
  1473       $link_user['link_path'] => $link_user['link_title'],
       
  1474     );
       
  1475     $this->assertBreadcrumb('user', $trail, $link_user['link_title'], $tree);
       
  1476     $tree = array(
       
  1477       $link_admin_user['link_path'] => $link_admin_user['link_title'],
       
  1478     );
       
  1479     $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $link_admin_user['link_title'], $tree);
       
  1480 
       
  1481     $this->drupalLogin($this->admin_user);
       
  1482     $trail += array(
       
  1483       $link_admin_user['link_path'] => $link_admin_user['link_title'],
       
  1484     );
       
  1485     $this->assertBreadcrumb('user/' . $this->admin_user->uid . '/edit', $trail, $link_admin_user['link_title'], $tree, FALSE);
       
  1486 
       
  1487     // Move 'user/%' below 'user' and verify again.
       
  1488     $edit = array(
       
  1489       'parent' => "$menu:{$link_user['mlid']}",
       
  1490     );
       
  1491     $this->drupalPost("admin/structure/menu/item/{$link_admin_user['mlid']}/edit", $edit, t('Save'));
       
  1492 
       
  1493     $this->drupalLogout();
       
  1494     $trail = $home;
       
  1495     $tree = array(
       
  1496       $link_user['link_path'] => $link_user['link_title'],
       
  1497     );
       
  1498     $this->assertBreadcrumb('user', $trail, $link_user['link_title'], $tree);
       
  1499     $trail += array(
       
  1500       $link_user['link_path'] => $link_user['link_title'],
       
  1501     );
       
  1502     $tree += array(
       
  1503       $link_admin_user['link_path'] => $link_admin_user['link_title'],
       
  1504     );
       
  1505     $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $link_admin_user['link_title'], $tree);
       
  1506 
       
  1507     $this->drupalLogin($this->admin_user);
       
  1508     $trail += array(
       
  1509       $link_admin_user['link_path'] => $link_admin_user['link_title'],
       
  1510     );
       
  1511     $this->assertBreadcrumb('user/' . $this->admin_user->uid . '/edit', $trail, $link_admin_user['link_title'], $tree, FALSE);
       
  1512 
       
  1513     // Create an only slightly privileged user being able to access site reports
       
  1514     // but not administration pages.
       
  1515     $this->web_user = $this->drupalCreateUser(array(
       
  1516       'access site reports',
       
  1517     ));
       
  1518     $this->drupalLogin($this->web_user);
       
  1519 
       
  1520     // Verify that we can access recent log entries, there is a corresponding
       
  1521     // page title, and that the breadcrumb is empty (because the user is not
       
  1522     // able to access "Administer", so the trail cannot recurse into it).
       
  1523     $trail = array();
       
  1524     $this->assertBreadcrumb('admin', $trail, t('Access denied'));
       
  1525     $this->assertResponse(403);
       
  1526 
       
  1527     $trail = $home;
       
  1528     $this->assertBreadcrumb('admin/reports', $trail, t('Reports'));
       
  1529     $this->assertNoResponse(403);
       
  1530 
       
  1531     $this->assertBreadcrumb('admin/reports/dblog', $trail, t('Recent log messages'));
       
  1532     $this->assertNoResponse(403);
       
  1533   }
       
  1534 }
       
  1535 
       
  1536 /**
       
  1537  * Tests active menu trails.
       
  1538  */
       
  1539 class MenuTrailTestCase extends MenuWebTestCase {
       
  1540   public static function getInfo() {
       
  1541     return array(
       
  1542       'name' => 'Active trail',
       
  1543       'description' => 'Tests active menu trails and alteration functionality.',
       
  1544       'group' => 'Menu',
       
  1545     );
       
  1546   }
       
  1547 
       
  1548   function setUp() {
       
  1549     $modules = func_get_args();
       
  1550     if (isset($modules[0]) && is_array($modules[0])) {
       
  1551       $modules = $modules[0];
       
  1552     }
       
  1553     $modules[] = 'menu_test';
       
  1554     parent::setUp($modules);
       
  1555     $this->admin_user = $this->drupalCreateUser(array('administer site configuration', 'access administration pages'));
       
  1556     $this->drupalLogin($this->admin_user);
       
  1557 
       
  1558     // This test puts menu links in the Navigation menu and then tests for
       
  1559     // their presence on the page, so we need to ensure that the Navigation
       
  1560     // block will be displayed in all active themes.
       
  1561     db_update('block')
       
  1562       ->fields(array(
       
  1563         // Use a region that is valid for all themes.
       
  1564         'region' => 'content',
       
  1565         'status' => 1,
       
  1566       ))
       
  1567       ->condition('module', 'system')
       
  1568       ->condition('delta', 'navigation')
       
  1569       ->execute();
       
  1570 
       
  1571     // This test puts menu links in the Management menu and then tests for
       
  1572     // their presence on the page, so we need to ensure that the Management
       
  1573     // block will be displayed in all active themes.
       
  1574     db_update('block')
       
  1575       ->fields(array(
       
  1576         // Use a region that is valid for all themes.
       
  1577         'region' => 'content',
       
  1578         'status' => 1,
       
  1579       ))
       
  1580       ->condition('module', 'system')
       
  1581       ->condition('delta', 'management')
       
  1582       ->execute();
       
  1583   }
       
  1584 
       
  1585   /**
       
  1586    * Tests active trails are properly affected by menu_tree_set_path().
       
  1587    */
       
  1588   function testMenuTreeSetPath() {
       
  1589     $home = array('<front>' => 'Home');
       
  1590     $config_tree = array(
       
  1591       'admin' => t('Administration'),
       
  1592       'admin/config' => t('Configuration'),
       
  1593     );
       
  1594     $config = $home + $config_tree;
       
  1595 
       
  1596     // The menu_test_menu_tree_set_path system variable controls whether or not
       
  1597     // the menu_test_menu_trail_callback() callback (used by all paths in these
       
  1598     // tests) issues an overriding call to menu_trail_set_path().
       
  1599     $test_menu_path = array(
       
  1600       'menu_name' => 'management',
       
  1601       'path' => 'admin/config/system/site-information',
       
  1602     );
       
  1603 
       
  1604     $breadcrumb = $home + array(
       
  1605       'menu-test' => t('Menu test root'),
       
  1606     );
       
  1607     $tree = array(
       
  1608       'menu-test' => t('Menu test root'),
       
  1609       'menu-test/menu-trail' => t('Menu trail - Case 1'),
       
  1610     );
       
  1611 
       
  1612     // Test the tree generation for the Navigation menu.
       
  1613     variable_del('menu_test_menu_tree_set_path');
       
  1614     $this->assertBreadcrumb('menu-test/menu-trail', $breadcrumb, t('Menu trail - Case 1'), $tree);
       
  1615 
       
  1616     // Override the active trail for the Management tree; it should not affect
       
  1617     // the Navigation tree.
       
  1618     variable_set('menu_test_menu_tree_set_path', $test_menu_path);
       
  1619     $this->assertBreadcrumb('menu-test/menu-trail', $breadcrumb, t('Menu trail - Case 1'), $tree);
       
  1620 
       
  1621     $breadcrumb = $config + array(
       
  1622       'admin/config/development' => t('Development'),
       
  1623     );
       
  1624     $tree = $config_tree + array(
       
  1625       'admin/config/development' => t('Development'),
       
  1626       'admin/config/development/menu-trail' => t('Menu trail - Case 2'),
       
  1627     );
       
  1628 
       
  1629     $override_breadcrumb = $config + array(
       
  1630       'admin/config/system' => t('System'),
       
  1631       'admin/config/system/site-information' => t('Site information'),
       
  1632     );
       
  1633     $override_tree = $config_tree + array(
       
  1634       'admin/config/system' => t('System'),
       
  1635       'admin/config/system/site-information' => t('Site information'),
       
  1636     );
       
  1637 
       
  1638     // Test the tree generation for the Management menu.
       
  1639     variable_del('menu_test_menu_tree_set_path');
       
  1640     $this->assertBreadcrumb('admin/config/development/menu-trail', $breadcrumb, t('Menu trail - Case 2'), $tree);
       
  1641 
       
  1642     // Override the active trail for the Management tree; it should affect the
       
  1643     // breadcrumbs and Management tree.
       
  1644     variable_set('menu_test_menu_tree_set_path', $test_menu_path);
       
  1645     $this->assertBreadcrumb('admin/config/development/menu-trail', $override_breadcrumb, t('Menu trail - Case 2'), $override_tree);
       
  1646   }
       
  1647 
       
  1648   /**
       
  1649    * Tests that the active trail works correctly on custom 403 and 404 pages.
       
  1650    */
       
  1651   function testCustom403And404Pages() {
       
  1652     // Set the custom 403 and 404 pages we will use.
       
  1653     variable_set('site_403', 'menu-test/custom-403-page');
       
  1654     variable_set('site_404', 'menu-test/custom-404-page');
       
  1655 
       
  1656     // Define the paths we'll visit to trigger 403 and 404 responses during
       
  1657     // this test, and the expected active trail for each case.
       
  1658     $paths = array(
       
  1659       403 => 'admin/config',
       
  1660       404 => $this->randomName(),
       
  1661     );
       
  1662     // For the 403 page, the initial trail during the Drupal bootstrap should
       
  1663     // include the page that the user is trying to visit, while the final trail
       
  1664     // should reflect the custom 403 page that the user was redirected to.
       
  1665     $expected_trail[403]['initial'] = array(
       
  1666       '<front>' => 'Home',
       
  1667       'admin/config' => 'Configuration',
       
  1668     );
       
  1669     $expected_trail[403]['final'] = array(
       
  1670       '<front>' => 'Home',
       
  1671       'menu-test' => 'Menu test root',
       
  1672       'menu-test/custom-403-page' => 'Custom 403 page',
       
  1673     );
       
  1674     // For the 404 page, the initial trail during the Drupal bootstrap should
       
  1675     // only contain the link back to "Home" (since the page the user is trying
       
  1676     // to visit doesn't have any menu items associated with it), while the
       
  1677     // final trail should reflect the custom 404 page that the user was
       
  1678     // redirected to.
       
  1679     $expected_trail[404]['initial'] = array(
       
  1680       '<front>' => 'Home',
       
  1681     );
       
  1682     $expected_trail[404]['final'] = array(
       
  1683       '<front>' => 'Home',
       
  1684       'menu-test' => 'Menu test root',
       
  1685       'menu-test/custom-404-page' => 'Custom 404 page',
       
  1686     );
       
  1687 
       
  1688     // Visit each path as an anonymous user so that we will actually get a 403
       
  1689     // on admin/config.
       
  1690     $this->drupalLogout();
       
  1691     foreach (array(403, 404) as $status_code) {
       
  1692       // Before visiting the page, trigger the code in the menu_test module
       
  1693       // that will record the active trail (so we can check it in this test).
       
  1694       variable_set('menu_test_record_active_trail', TRUE);
       
  1695       $this->drupalGet($paths[$status_code]);
       
  1696       $this->assertResponse($status_code);
       
  1697 
       
  1698       // Check that the initial trail (during the Drupal bootstrap) matches
       
  1699       // what we expect.
       
  1700       $initial_trail = variable_get('menu_test_active_trail_initial', array());
       
  1701       $this->assertEqual(count($initial_trail), count($expected_trail[$status_code]['initial']), format_string('The initial active trail for a @status_code page contains the expected number of items (expected: @expected, found: @found).', array(
       
  1702         '@status_code' => $status_code,
       
  1703         '@expected' => count($expected_trail[$status_code]['initial']),
       
  1704         '@found' => count($initial_trail),
       
  1705       )));
       
  1706       foreach (array_keys($expected_trail[$status_code]['initial']) as $index => $path) {
       
  1707         $this->assertEqual($initial_trail[$index]['href'], $path, format_string('Element number @number of the initial active trail for a @status_code page contains the correct path (expected: @expected, found: @found)', array(
       
  1708           '@number' => $index + 1,
       
  1709           '@status_code' => $status_code,
       
  1710           '@expected' => $path,
       
  1711           '@found' => $initial_trail[$index]['href'],
       
  1712         )));
       
  1713       }
       
  1714 
       
  1715       // Check that the final trail (after the user has been redirected to the
       
  1716       // custom 403/404 page) matches what we expect.
       
  1717       $final_trail = variable_get('menu_test_active_trail_final', array());
       
  1718       $this->assertEqual(count($final_trail), count($expected_trail[$status_code]['final']), format_string('The final active trail for a @status_code page contains the expected number of items (expected: @expected, found: @found).', array(
       
  1719         '@status_code' => $status_code,
       
  1720         '@expected' => count($expected_trail[$status_code]['final']),
       
  1721         '@found' => count($final_trail),
       
  1722       )));
       
  1723       foreach (array_keys($expected_trail[$status_code]['final']) as $index => $path) {
       
  1724         $this->assertEqual($final_trail[$index]['href'], $path, format_string('Element number @number of the final active trail for a @status_code page contains the correct path (expected: @expected, found: @found)', array(
       
  1725           '@number' => $index + 1,
       
  1726           '@status_code' => $status_code,
       
  1727           '@expected' => $path,
       
  1728           '@found' => $final_trail[$index]['href'],
       
  1729         )));
       
  1730       }
       
  1731 
       
  1732       // Check that the breadcrumb displayed on the final custom 403/404 page
       
  1733       // matches what we expect. (The last item of the active trail represents
       
  1734       // the current page, which is not supposed to appear in the breadcrumb,
       
  1735       // so we need to remove it from the array before checking.)
       
  1736       array_pop($expected_trail[$status_code]['final']);
       
  1737       $this->assertBreadcrumb(NULL, $expected_trail[$status_code]['final']);
       
  1738     }
       
  1739   }
       
  1740 }