admin_menu.test 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. <?php
  2. /**
  3. * @file
  4. * Tests for the Administration menu module.
  5. */
  6. /**
  7. * Base class for all administration menu web test cases.
  8. */
  9. class AdminMenuWebTestCase extends DrupalWebTestCase {
  10. protected $profile = 'testing';
  11. protected $basePermissions = array(
  12. 'system' => 'access administration pages',
  13. 'admin_menu' => 'access administration menu',
  14. );
  15. /**
  16. *
  17. */
  18. public function setUp() {
  19. // Enable admin menu module and any other modules.
  20. $modules = func_get_args();
  21. $modules = isset($modules[0]) ? $modules[0] : $modules;
  22. $modules[] = 'admin_menu';
  23. parent::setUp($modules);
  24. // Disable client-side caching.
  25. variable_set('admin_menu_cache_client', FALSE);
  26. // Disable Clean URLs to ensure drupal.org testbot compatibility.
  27. variable_set('clean_url', 0);
  28. }
  29. /**
  30. * Check that an element exists in HTML markup.
  31. *
  32. * @param $xpath
  33. * An XPath expression.
  34. * @param array $arguments
  35. * (optional) An associative array of XPath replacement tokens to pass to
  36. * DrupalWebTestCase::buildXPathQuery().
  37. * @param $message
  38. * The message to display along with the assertion.
  39. * @param $group
  40. * The type of assertion - examples are "Browser", "PHP".
  41. *
  42. * @return
  43. * TRUE if the assertion succeeded, FALSE otherwise.
  44. */
  45. protected function assertElementByXPath($xpath, array $arguments = array(), $message, $group = 'Other') {
  46. $elements = $this->xpath($xpath, $arguments);
  47. return $this->assertTrue(!empty($elements[0]), $message, $group);
  48. }
  49. /**
  50. * Check that an element does not exist in HTML markup.
  51. *
  52. * @param $xpath
  53. * An XPath expression.
  54. * @param array $arguments
  55. * (optional) An associative array of XPath replacement tokens to pass to
  56. * DrupalWebTestCase::buildXPathQuery().
  57. * @param $message
  58. * The message to display along with the assertion.
  59. * @param $group
  60. * The type of assertion - examples are "Browser", "PHP".
  61. *
  62. * @return
  63. * TRUE if the assertion succeeded, FALSE otherwise.
  64. */
  65. protected function assertNoElementByXPath($xpath, array $arguments = array(), $message, $group = 'Other') {
  66. $elements = $this->xpath($xpath, $arguments);
  67. return $this->assertTrue(empty($elements), $message, $group);
  68. }
  69. /**
  70. * Asserts that links appear in the menu in a specified trail.
  71. *
  72. * @param array $trail
  73. * A list of menu link titles to assert in the menu.
  74. */
  75. protected function assertLinkTrailByTitle(array $trail) {
  76. $xpath = array();
  77. $args = array();
  78. $message = '';
  79. foreach ($trail as $i => $title) {
  80. $xpath[] = '/li/a[text()=:title' . $i . ']';
  81. $args[':title' . $i] = $title;
  82. $message .= ($i ? ' » ' : '') . check_plain($title);
  83. }
  84. $xpath = '//div[@id="admin-menu"]/div/ul' . implode('/parent::li/ul', $xpath);
  85. $this->assertElementByXPath($xpath, $args, $message . ' link found.');
  86. }
  87. /**
  88. * Asserts that no link appears in the menu for a specified trail.
  89. *
  90. * @param array $trail
  91. * A list of menu link titles to assert in the menu.
  92. */
  93. protected function assertNoLinkTrailByTitle(array $trail) {
  94. $xpath = array();
  95. $args = array();
  96. $message = '';
  97. foreach ($trail as $i => $title) {
  98. $xpath[] = '/li/a[text()=:title' . $i . ']';
  99. $args[':title' . $i] = $title;
  100. $message .= ($i ? ' » ' : '') . check_plain($title);
  101. }
  102. $xpath = '//div[@id="admin-menu"]/div/ul' . implode('/parent::li/ul', $xpath);
  103. $this->assertNoElementByXPath($xpath, $args, $message . ' link not found.');
  104. }
  105. }
  106. /**
  107. * Tests menu links depending on user permissions.
  108. */
  109. class AdminMenuPermissionsTestCase extends AdminMenuWebTestCase {
  110. /**
  111. *
  112. */
  113. public static function getInfo() {
  114. return array(
  115. 'name' => 'Menu link access permissions',
  116. 'description' => 'Tests appearance of menu links depending on user permissions.',
  117. 'group' => 'Administration menu',
  118. );
  119. }
  120. /**
  121. *
  122. */
  123. public function setUp() {
  124. parent::setUp(array('node'));
  125. }
  126. /**
  127. * Test that the links are added to the page (no JS testing).
  128. */
  129. public function testPermissions() {
  130. module_enable(array('contact'));
  131. $this->resetAll();
  132. // Anonymous users should not see the menu.
  133. $this->drupalGet('');
  134. $this->assertNoElementByXPath('//div[@id="admin-menu"]', array(), t('Administration menu not found.'));
  135. // Create a user who
  136. // - can access content overview
  137. // - cannot access drupal.org links
  138. // - cannot administer Contact module.
  139. $permissions = $this->basePermissions + array(
  140. 'access content overview',
  141. );
  142. $admin_user = $this->drupalCreateUser($permissions);
  143. $this->drupalLogin($admin_user);
  144. // Check that the user can see the admin links, but not the drupal links.
  145. $this->assertElementByXPath('//div[@id="admin-menu"]', array(), 'Administration menu found.');
  146. $this->assertElementByXPath('//div[@id="admin-menu"]//a[contains(@href, :path)]', array(':path' => 'admin/content'), 'Content link found.');
  147. $this->assertNoElementByXPath('//div[@id="admin-menu"]//a[@href=:path]', array(':path' => 'http://drupal.org'), 'Icon » Drupal.org link not found.');
  148. $this->assertNoElementByXPath('//div[@id="admin-menu"]//a[contains(@href, :path)]', array(':path' => 'admin/structure/contact'), 'Structure » Contact link not found.');
  149. // Create a user "reversed" to the above; i.e., who
  150. // - cannot access content overview
  151. // - can access drupal.org links
  152. // - can administer Contact module.
  153. $permissions = $this->basePermissions + array(
  154. 'display drupal links',
  155. 'administer contact forms',
  156. );
  157. $admin_user2 = $this->drupalCreateUser($permissions);
  158. $this->drupalLogin($admin_user2);
  159. $this->assertElementByXPath('//div[@id="admin-menu"]', array(), 'Administration menu found.');
  160. $this->assertNoElementByXPath('//div[@id="admin-menu"]//a[contains(@href, :path)]', array(':path' => 'admin/content'), 'Content link not found.');
  161. $this->assertElementByXPath('//div[@id="admin-menu"]//a[@href=:path]', array(':path' => 'http://drupal.org'), 'Icon » Drupal.org link found.');
  162. $this->assertElementByXPath('//div[@id="admin-menu"]//a[contains(@href, :path)]', array(':path' => 'admin/structure/contact'), 'Structure » Contact link found.');
  163. }
  164. /**
  165. * Tests handling of links pointing to category/overview pages.
  166. */
  167. public function testCategories() {
  168. // Create a user with minimum permissions.
  169. $admin_user = $this->drupalCreateUser($this->basePermissions);
  170. $this->drupalLogin($admin_user);
  171. // Verify that no category links appear.
  172. $this->assertNoLinkTrailByTitle(array(t('Structure')));
  173. $this->assertNoLinkTrailByTitle(array(t('Configuration')));
  174. // Create a user with access to one configuration category.
  175. $permissions = $this->basePermissions + array(
  176. 'administer users',
  177. );
  178. $admin_user = $this->drupalCreateUser($permissions);
  179. $this->drupalLogin($admin_user);
  180. // Verify that only expected category links appear.
  181. $this->assertNoLinkTrailByTitle(array(t('Structure')));
  182. $this->assertLinkTrailByTitle(array(t('People')));
  183. $this->assertLinkTrailByTitle(array(t('Configuration')));
  184. $this->assertLinkTrailByTitle(array(t('Configuration'), t('People')));
  185. // Random picks are sufficient.
  186. $this->assertNoLinkTrailByTitle(array(t('Configuration'), t('Media')));
  187. $this->assertNoLinkTrailByTitle(array(t('Configuration'), t('System')));
  188. }
  189. /**
  190. * Tests that user role and permission changes are properly taken up.
  191. */
  192. public function testPermissionChanges() {
  193. // Create a user who is able to change permissions.
  194. $permissions = $this->basePermissions + array(
  195. 'administer permissions',
  196. );
  197. $admin_user = $this->drupalCreateUser($permissions);
  198. $this->drupalLogin($admin_user);
  199. // Extract the user role ID that was created for above permissions.
  200. $rid = key(array_diff_key($admin_user->roles, array(DRUPAL_AUTHENTICATED_RID => 0)));
  201. // Verify that Configuration does not appear.
  202. $this->assertNoLinkTrailByTitle(array(t('Configuration')));
  203. // Grant the 'administer site configuration' permission to ourselves.
  204. $edit = array(
  205. $rid . '[administer site configuration]' => TRUE,
  206. );
  207. $this->drupalPost('admin/people/permissions', $edit, t('Save permissions'));
  208. // Verify that Configuration appears.
  209. $this->assertLinkTrailByTitle(array(t('Configuration')));
  210. // Verify that Structure » Content types does not appear.
  211. $this->assertNoLinkTrailByTitle(array(t('Structure'), t('Content types')));
  212. // Create a new role.
  213. $edit = array(
  214. 'name' => 'test',
  215. );
  216. $this->drupalPost('admin/people/permissions/roles', $edit, t('Add role'));
  217. // It should be safe to assume that the new role gets the next ID.
  218. $test_rid = $rid + 1;
  219. // Grant the 'administer content types' permission for the role.
  220. $edit = array(
  221. $test_rid . '[administer content types]' => TRUE,
  222. );
  223. $this->drupalPost('admin/people/permissions/' . $test_rid, $edit, t('Save permissions'));
  224. // Verify that Structure » Content types does not appear.
  225. $this->assertNoLinkTrailByTitle(array(t('Structure'), t('Content types')));
  226. // Assign the role to ourselves.
  227. $edit = array(
  228. 'roles[' . $test_rid . ']' => TRUE,
  229. );
  230. $this->drupalPost('user/' . $admin_user->uid . '/edit', $edit, t('Save'));
  231. // Verify that Structure » Content types appears.
  232. $this->assertLinkTrailByTitle(array(t('Structure'), t('Content types')));
  233. // Delete the role.
  234. $this->drupalPost('admin/people/permissions/roles/edit/' . $test_rid, array(), t('Delete role'));
  235. $this->drupalPost(NULL, array(), t('Delete'));
  236. // Verify that Structure » Content types does not appear.
  237. $this->assertNoLinkTrailByTitle(array(t('Structure'), t('Content types')));
  238. }
  239. }
  240. /**
  241. * Tests appearance, localization, and escaping of dynamic links.
  242. */
  243. class AdminMenuDynamicLinksTestCase extends AdminMenuWebTestCase {
  244. /**
  245. *
  246. */
  247. public static function getInfo() {
  248. return array(
  249. 'name' => 'Dynamic links',
  250. 'description' => 'Tests appearance, localization, and escaping of dynamic links.',
  251. 'group' => 'Administration menu',
  252. );
  253. }
  254. /**
  255. *
  256. */
  257. public function setUp() {
  258. parent::setUp(array('node'));
  259. }
  260. /**
  261. * Tests node type links.
  262. */
  263. public function testNode() {
  264. $type = $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
  265. // Create a content-type with special characters.
  266. $type = $this->drupalCreateContentType(array('type' => 'special', 'name' => 'Cool & Special'));
  267. $permissions = $this->basePermissions + array(
  268. 'administer content types',
  269. 'create article content',
  270. 'create special content',
  271. );
  272. $this->admin_user = $this->drupalCreateUser($permissions);
  273. $this->drupalLogin($this->admin_user);
  274. // Verify that dynamic links are displayed.
  275. $this->drupalGet('');
  276. $this->assertElementByXPath('//div[@id="admin-menu"]', array(), t('Administration menu found.'));
  277. $this->assertElementByXPath('//div[@id="admin-menu"]//a[contains(@href, :path)]', array(':path' => 'admin/structure/types'), "Structure » Content types link found.");
  278. // Verify link title output escaping.
  279. $this->assertNoRaw('Cool & Special');
  280. $this->assertRaw(check_plain('Cool & Special'));
  281. // Verify Manage content type links.
  282. $links = array(
  283. 'admin/structure/types/manage/article' => 'Article',
  284. 'admin/structure/types/manage/special' => 'Cool & Special',
  285. );
  286. foreach ($links as $path => $title) {
  287. $this->assertElementByXPath('//div[@id="admin-menu"]//a[contains(@href, :path) and text()=:title]', array(
  288. ':path' => $path,
  289. ':title' => $title,
  290. ), "Structure » Content types » $title link found.");
  291. }
  292. // Verify Add content links.
  293. $links = array(
  294. 'node/add/article' => 'Article',
  295. 'node/add/special' => 'Cool & Special',
  296. );
  297. foreach ($links as $path => $title) {
  298. $this->assertElementByXPath('//div[@id="admin-menu"]//a[contains(@href, :path) and text()=:title]', array(
  299. ':path' => $path,
  300. ':title' => $title,
  301. ), "Add content » $title link found.");
  302. }
  303. }
  304. /**
  305. * Tests Add content links.
  306. */
  307. public function testNodeAdd() {
  308. $type = $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
  309. // Verify that "Add content" does not appear for unprivileged users.
  310. $permissions = $this->basePermissions + array(
  311. 'access content',
  312. );
  313. $this->web_user = $this->drupalCreateUser($permissions);
  314. $this->drupalLogin($this->web_user);
  315. $this->assertNoText(t('Add content'));
  316. // Verify "Add content" appears below "Content" for administrative users.
  317. $permissions = $this->basePermissions + array(
  318. 'access content overview',
  319. 'access content',
  320. 'create article content',
  321. );
  322. $this->admin_user = $this->drupalCreateUser($permissions);
  323. $this->drupalLogin($this->admin_user);
  324. $this->assertLinkTrailByTitle(array(
  325. t('Content'),
  326. t('Add content'),
  327. ));
  328. // Verify "Add content" appears on the top-level for regular users.
  329. $permissions = $this->basePermissions + array(
  330. 'access content',
  331. 'create article content',
  332. );
  333. $this->web_user = $this->drupalCreateUser($permissions);
  334. $this->drupalLogin($this->web_user);
  335. $this->assertLinkTrailByTitle(array(
  336. t('Add content'),
  337. ));
  338. }
  339. }
  340. /**
  341. * Tests appearance of different types of links.
  342. */
  343. class AdminMenuLinkTypesTestCase extends AdminMenuWebTestCase {
  344. /**
  345. *
  346. */
  347. public static function getInfo() {
  348. return array(
  349. 'name' => 'Link types',
  350. 'description' => 'Tests appearance of different types of links.',
  351. 'group' => 'Administration menu',
  352. );
  353. }
  354. /**
  355. *
  356. */
  357. public function setUp() {
  358. parent::setUp(array('help'));
  359. $permissions = module_invoke_all('permission');
  360. $permissions = array_keys($permissions);
  361. $this->admin_user = $this->drupalCreateUser($permissions);
  362. $this->drupalLogin($this->admin_user);
  363. }
  364. /**
  365. * Tests appearance of different router item link types.
  366. */
  367. public function testLinkTypes() {
  368. // Verify that MENU_NORMAL_ITEMs appear.
  369. $this->assertLinkTrailByTitle(array(
  370. t('Configuration'),
  371. t('System'),
  372. t('Site information'),
  373. ));
  374. // Verify that MENU_LOCAL_TASKs appear.
  375. $this->assertLinkTrailByTitle(array(t('People'), t('Permissions')));
  376. $this->assertLinkTrailByTitle(array(t('Appearance'), t('Settings')));
  377. $this->assertLinkTrailByTitle(array(t('Modules'), t('Uninstall')));
  378. // Verify that MENU_LOCAL_ACTIONs appear.
  379. $this->assertLinkTrailByTitle(array(
  380. t('People'),
  381. t('Add user'),
  382. ));
  383. // Verify that MENU_DEFAULT_LOCAL_TASKs do NOT appear.
  384. $this->assertNoLinkTrailByTitle(array(t('Modules'), t('List')));
  385. $this->assertNoLinkTrailByTitle(array(t('People'), t('List')));
  386. $this->assertNoLinkTrailByTitle(array(t('People'), t('Permissions'), t('Permissions')));
  387. $this->assertNoLinkTrailByTitle(array(t('Appearance'), t('List')));
  388. // Verify that MENU_VISIBLE_IN_BREADCRUMB items (exact type) do NOT appear.
  389. $this->assertNoLinkTrailByTitle(array(t('Modules'), t('Uninstall'), t('Uninstall')));
  390. $this->assertNoLinkTrailByTitle(array(t('Help'), 'admin_menu'));
  391. // Verify that special "Index" link appears below icon.
  392. $this->assertElementByXPath('//div[@id="admin-menu"]//a[contains(@href, :path) and text()=:title]', array(
  393. ':path' => 'admin/index',
  394. ':title' => t('Index'),
  395. ), "Icon » Index link found.");
  396. }
  397. }
  398. /**
  399. * Tests customized menu links.
  400. */
  401. class AdminMenuCustomizedTestCase extends AdminMenuWebTestCase {
  402. /**
  403. *
  404. */
  405. public static function getInfo() {
  406. return array(
  407. 'name' => 'Customized links',
  408. 'description' => 'Tests customized menu links.',
  409. 'group' => 'Administration menu',
  410. );
  411. }
  412. /**
  413. *
  414. */
  415. public function setUp() {
  416. parent::setUp(array('menu'));
  417. $this->admin_user = $this->drupalCreateUser($this->basePermissions + array(
  418. 'administer menu',
  419. ));
  420. $this->drupalLogin($this->admin_user);
  421. }
  422. /**
  423. * Test disabled custom links.
  424. */
  425. public function testCustomDisabled() {
  426. $type = $this->drupalCreateContentType();
  427. $node = $this->drupalCreateNode(array('type' => $type->type));
  428. $text = $this->randomName();
  429. $xpath = $this->buildXPathQuery('//div[@id=:id]//a[contains(text(), :text)]', array(
  430. ':id' => 'admin-menu',
  431. ':text' => $text,
  432. ));
  433. // Verify that the link does not appear in the menu.
  434. $this->drupalGet('node');
  435. $elements = $this->xpath($xpath);
  436. $this->assertFalse($elements, 'Custom link not found.');
  437. // Add a custom link to the node to the menu.
  438. $edit = array(
  439. 'link_path' => 'node/' . $node->nid,
  440. 'link_title' => $text,
  441. 'parent' => 'management:' . $this->queryMlidByPath('admin'),
  442. );
  443. $this->drupalPost('admin/structure/menu/manage/management/add', $edit, t('Save'));
  444. // Verify that the link appears in the menu.
  445. $this->drupalGet('node');
  446. $elements = $this->xpath($xpath);
  447. $this->assertTrue($elements, 'Custom link found.');
  448. // Disable the link.
  449. $edit = array(
  450. 'enabled' => FALSE,
  451. );
  452. $this->drupalPost('admin/structure/menu/item/' . $this->queryMlidByPath('node/' . $node->nid) . '/edit', $edit, t('Save'));
  453. // Verify that the disabled link does not appear in the menu.
  454. $this->drupalGet('node');
  455. $elements = $this->xpath($xpath);
  456. $this->assertFalse($elements, 'Disabled custom link not found.');
  457. }
  458. /**
  459. * Tests external links.
  460. */
  461. public function testCustomExternal() {
  462. // Add a custom link to the node to the menu.
  463. $edit = array(
  464. 'link_path' => 'http://example.com',
  465. 'link_title' => 'Example',
  466. 'parent' => 'management:' . $this->queryMlidByPath('admin'),
  467. );
  468. $this->drupalPost('admin/structure/menu/manage/management/add', $edit, t('Save'));
  469. // Verify that the link appears in the menu.
  470. $this->drupalGet('');
  471. $elements = $this->xpath('//div[@id=:id]//a[@href=:href and contains(text(), :text)]', array(
  472. ':id' => 'admin-menu',
  473. ':href' => $edit['link_path'],
  474. ':text' => $edit['link_title'],
  475. ));
  476. $this->assertTrue($elements, 'External link found.');
  477. }
  478. /**
  479. * Returns the menu link ID for a given link path in the management menu.
  480. */
  481. protected function queryMlidByPath($path) {
  482. return db_query('SELECT mlid FROM {menu_links} WHERE menu_name = :menu AND link_path = :path', array(
  483. ':menu' => 'management',
  484. ':path' => $path,
  485. ))->fetchField();
  486. }
  487. }