Projet

Général

Profil

Paste
Télécharger (71,6 ko) Statistiques
| Branche: | Révision:

root / drupal7 / modules / simpletest / tests / menu.test @ 76597ebf

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
}