| Drupal | PHP Cross Reference | Content Management Systems |
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, t('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), t('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), t('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', t('Raw text found on the page')); 149 $this->assertNoText(t('A title with @placeholder', array('@placeholder' => 'some other text')), t('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.', t('The administrative theme can be correctly set in a theme callback.')); 170 $this->assertRaw('seven/style.css', t("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.', t('Theme callback inheritance correctly uses the administrative theme.')); 179 $this->assertRaw('seven/style.css', t("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', t('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', t("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.', t('The theme callback system is correctly triggered for an administrator when the site is in maintenance mode.')); 218 $this->assertRaw('seven/style.css', t("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)), t("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)), t("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.', t('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', t("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.', t('The theme callback system uses an optional theme once it has been enabled.')); 267 $this->assertRaw('stark/layout.css', t("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.', t('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', t("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.', t('The theme callback system falls back on the default theme when no theme is requested.')); 285 $this->assertRaw('bartik/css/style.css', t("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.', t('The result of hook_custom_theme() is used as the theme for the current page.')); 301 $this->assertRaw('stark/layout.css', t("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.', t('The result of hook_custom_theme() does not override what was set in a theme callback.')); 317 $this->assertRaw('seven/style.css', t("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, t('Found updated menu link')); 352 $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1')); 353 $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1-1')); 354 $this->assertLink(t('Menu link #2'), 0, t('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, t('Not found deleted menu link')); 361 $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1')); 362 $this->assertNoLink(t('Menu link #1'), 0, t('Not found menu link #1-1')); 363 $this->assertLink(t('Menu link #2'), 0, t('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', t('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', t('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'], t('The parent of a directly attached child is correct.')); 421 $this->assertEqual($unattached_child_link['plid'], $parent_link['mlid'], t('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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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, t('%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', t("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, t('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', t('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', t('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', t('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"', t('Title attribute of a menu link renders.')); 576 $this->assertRaw('testparam=testvalue', t('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', t('Menu title is') . ': ' . $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, t('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, t('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', t("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, t("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', t("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'], t('Parent item #1 exists.')); 959 $this->assertSameLink($this->links[2], $tree[2]['link'], t('Parent item #2 exists.')); 960 $this->assertSameLink($this->links[5], $tree[5]['link'], t('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'], t('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 : t('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', t('Hyphen is changed to a dash on menu_link')); 1029 $this->assertEqual( $output['#theme_wrappers'][0], 'menu_tree__main_menu', t('Hyphen is changed to a dash on menu_tree wrapper')); 1030 // Looking for child items in the data 1031 $this->assertEqual( $output['1']['#below']['2']['#href'], 'a/b', t('Checking the href on a child item')); 1032 $this->assertTrue( in_array('active-trail',$output['1']['#below']['2']['#attributes']['class']) , t('Checking the active trail class')); 1033 // Validate that the hidden and no access items are missing 1034 $this->assertFalse( isset($output['5']), t('Hidden item should be missing')); 1035 $this->assertFalse( isset($output['6']), t('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']), t('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']), t('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, t('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']), t('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, t('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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title