token.test 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095
  1. <?php
  2. /**
  3. * @file
  4. * Test integration for the token module.
  5. */
  6. /**
  7. * Helper test class with some added functions for testing.
  8. */
  9. class TokenTestHelper extends DrupalWebTestCase {
  10. protected $profile = 'testing';
  11. public function setUp($modules = array()) {
  12. $modules[] = 'path';
  13. $modules[] = 'token';
  14. $modules[] = 'token_test';
  15. parent::setUp($modules);
  16. variable_set('clean_url', 1);
  17. }
  18. function assertToken($type, array $data, $token, $expected, array $options = array()) {
  19. return $this->assertTokens($type, $data, array($token => $expected), $options);
  20. }
  21. function assertTokens($type, array $data, array $tokens, array $options = array()) {
  22. $input = $this->mapTokenNames($type, array_keys($tokens));
  23. $replacements = token_generate($type, $input, $data, $options);
  24. foreach ($tokens as $name => $expected) {
  25. $token = $input[$name];
  26. if (!isset($expected)) {
  27. $this->assertTrue(!isset($values[$token]), t("Token value for @token was not generated.", array('@type' => $type, '@token' => $token)));
  28. }
  29. elseif (!isset($replacements[$token])) {
  30. $this->fail(t("Token value for @token was not generated.", array('@type' => $type, '@token' => $token)));
  31. }
  32. elseif (!empty($options['regex'])) {
  33. $this->assertTrue(preg_match('/^' . $expected . '$/', $replacements[$token]), t("Token value for @token was '@actual', matching regular expression pattern '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $replacements[$token], '@expected' => $expected)));
  34. }
  35. else {
  36. $this->assertIdentical($replacements[$token], $expected, t("Token value for @token was '@actual', expected value '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $replacements[$token], '@expected' => $expected)));
  37. }
  38. }
  39. return $replacements;
  40. }
  41. function mapTokenNames($type, array $tokens = array()) {
  42. $return = array();
  43. foreach ($tokens as $token) {
  44. $return[$token] = "[$type:$token]";
  45. }
  46. return $return;
  47. }
  48. function assertNoTokens($type, array $data, array $tokens, array $options = array()) {
  49. $input = $this->mapTokenNames($type, $tokens);
  50. $replacements = token_generate($type, $input, $data, $options);
  51. foreach ($tokens as $name) {
  52. $token = $input[$name];
  53. $this->assertTrue(!isset($replacements[$token]), t("Token value for @token was not generated.", array('@type' => $type, '@token' => $token)));
  54. }
  55. return $values;
  56. }
  57. function saveAlias($source, $alias, $language = LANGUAGE_NONE) {
  58. $alias = array(
  59. 'source' => $source,
  60. 'alias' => $alias,
  61. 'language' => $language,
  62. );
  63. path_save($alias);
  64. return $alias;
  65. }
  66. function saveEntityAlias($entity_type, $entity, $alias, $language = LANGUAGE_NONE) {
  67. $uri = entity_uri($entity_type, $entity);
  68. return $this->saveAlias($uri['path'], $alias, $language);
  69. }
  70. /**
  71. * Make a page request and test for token generation.
  72. */
  73. function assertPageTokens($url, array $tokens, array $data = array(), array $options = array()) {
  74. if (empty($tokens)) {
  75. return TRUE;
  76. }
  77. $token_page_tokens = array(
  78. 'tokens' => $tokens,
  79. 'data' => $data,
  80. 'options' => $options,
  81. );
  82. variable_set('token_page_tokens', $token_page_tokens);
  83. $options += array('url_options' => array());
  84. $this->drupalGet($url, $options['url_options']);
  85. $this->refreshVariables();
  86. $result = variable_get('token_page_tokens', array());
  87. if (!isset($result['values']) || !is_array($result['values'])) {
  88. return $this->fail('Failed to generate tokens.');
  89. }
  90. foreach ($tokens as $token => $expected) {
  91. if (!isset($expected)) {
  92. $this->assertTrue(!isset($result['values'][$token]) || $result['values'][$token] === $token, t("Token value for @token was not generated.", array('@token' => $token)));
  93. }
  94. elseif (!isset($result['values'][$token])) {
  95. $this->fail(t('Failed to generate token @token.', array('@token' => $token)));
  96. }
  97. else {
  98. $this->assertIdentical($result['values'][$token], (string) $expected, t("Token value for @token was '@actual', expected value '@expected'.", array('@token' => $token, '@actual' => $result['values'][$token], '@expected' => $expected)));
  99. }
  100. }
  101. }
  102. }
  103. class TokenUnitTestCase extends TokenTestHelper {
  104. public static function getInfo() {
  105. return array(
  106. 'name' => 'Token unit tests',
  107. 'description' => 'Test basic, low-level token functions.',
  108. 'group' => 'Token',
  109. );
  110. }
  111. /**
  112. * Test token_get_invalid_tokens() and token_get_invalid_tokens_by_context().
  113. */
  114. public function testGetInvalidTokens() {
  115. $tests = array();
  116. $tests[] = array(
  117. 'valid tokens' => array(
  118. '[node:title]',
  119. '[node:created:short]',
  120. '[node:created:custom:invalid]',
  121. '[node:created:custom:mm-YYYY]',
  122. '[site:name]',
  123. '[site:slogan]',
  124. '[current-date:short]',
  125. '[current-user:uid]',
  126. '[current-user:ip-address]',
  127. ),
  128. 'invalid tokens' => array(
  129. '[node:title:invalid]',
  130. '[node:created:invalid]',
  131. '[node:created:short:invalid]',
  132. '[invalid:title]',
  133. '[site:invalid]',
  134. '[user:ip-address]',
  135. '[user:uid]',
  136. '[comment:cid]',
  137. // Deprecated tokens
  138. '[node:tnid]',
  139. '[node:type]',
  140. '[node:type-name]',
  141. '[date:short]',
  142. ),
  143. 'types' => array('node'),
  144. );
  145. $tests[] = array(
  146. 'valid tokens' => array(
  147. '[node:title]',
  148. '[node:created:short]',
  149. '[node:created:custom:invalid]',
  150. '[node:created:custom:mm-YYYY]',
  151. '[site:name]',
  152. '[site:slogan]',
  153. '[user:uid]',
  154. '[current-date:short]',
  155. '[current-user:uid]',
  156. ),
  157. 'invalid tokens' => array(
  158. '[node:title:invalid]',
  159. '[node:created:invalid]',
  160. '[node:created:short:invalid]',
  161. '[invalid:title]',
  162. '[site:invalid]',
  163. '[user:ip-address]',
  164. '[comment:cid]',
  165. // Deprecated tokens
  166. '[node:tnid]',
  167. '[node:type]',
  168. '[node:type-name]',
  169. ),
  170. 'types' => array('all'),
  171. );
  172. foreach ($tests as $test) {
  173. $tokens = array_merge($test['valid tokens'], $test['invalid tokens']);
  174. shuffle($tokens);
  175. $invalid_tokens = token_get_invalid_tokens_by_context(implode(' ', $tokens), $test['types']);
  176. sort($invalid_tokens);
  177. sort($test['invalid tokens']);
  178. $this->assertEqual($invalid_tokens, $test['invalid tokens'], 'Invalid tokens detected properly: ' . implode(', ', $invalid_tokens));
  179. }
  180. }
  181. }
  182. class TokenURLTestCase extends TokenTestHelper {
  183. public static function getInfo() {
  184. return array(
  185. 'name' => 'URL token tests',
  186. 'description' => 'Test the URL tokens.',
  187. 'group' => 'Token',
  188. );
  189. }
  190. public function setUp($modules = array()) {
  191. parent::setUp($modules);
  192. $this->saveAlias('node/1', 'first-node');
  193. }
  194. function testURLTokens() {
  195. $tokens = array(
  196. 'absolute' => 'http://example.com/first-node',
  197. 'relative' => base_path() . 'first-node',
  198. 'path' => 'first-node',
  199. 'brief' => 'example.com/first-node',
  200. 'args:value:0' => 'first-node',
  201. 'args:value:1' => NULL,
  202. 'args:value:N' => NULL,
  203. 'unaliased' => 'http://example.com/node/1',
  204. 'unaliased:relative' => base_path() . 'node/1',
  205. 'unaliased:path' => 'node/1',
  206. 'unaliased:brief' => 'example.com/node/1',
  207. 'unaliased:args:value:0' => 'node',
  208. 'unaliased:args:value:1' => '1',
  209. 'unaliased:args:value:2' => NULL,
  210. // Deprecated tokens.
  211. 'alias' => 'first-node',
  212. );
  213. $this->assertTokens('url', array('path' => 'node/1', 'options' => array('base_url' => 'http://example.com')), $tokens);
  214. }
  215. }
  216. class TokenCommentTestCase extends TokenTestHelper {
  217. public static function getInfo() {
  218. return array(
  219. 'name' => 'Comment token tests',
  220. 'description' => 'Test the comment tokens.',
  221. 'group' => 'Token',
  222. );
  223. }
  224. public function setUp($modules = array()) {
  225. $modules[] = 'comment';
  226. parent::setUp($modules);
  227. }
  228. function testCommentTokens() {
  229. $node = $this->drupalCreateNode(array('comment' => COMMENT_NODE_OPEN));
  230. $parent_comment = new stdClass;
  231. $parent_comment->nid = $node->nid;
  232. $parent_comment->pid = 0;
  233. $parent_comment->cid = NULL;
  234. $parent_comment->uid = 0;
  235. $parent_comment->name = 'anonymous user';
  236. $parent_comment->mail = 'anonymous@example.com';
  237. $parent_comment->subject = $this->randomName();
  238. $parent_comment->timestamp = mt_rand($node->created, REQUEST_TIME);
  239. $parent_comment->language = LANGUAGE_NONE;
  240. $parent_comment->body[LANGUAGE_NONE][0] = $this->randomName();
  241. comment_save($parent_comment);
  242. $tokens = array(
  243. 'url' => url('comment/' . $parent_comment->cid, array('fragment' => 'comment-' . $parent_comment->cid, 'absolute' => TRUE)),
  244. 'url:absolute' => url('comment/' . $parent_comment->cid, array('fragment' => 'comment-' . $parent_comment->cid, 'absolute' => TRUE)),
  245. 'url:relative' => url('comment/' . $parent_comment->cid, array('fragment' => 'comment-' . $parent_comment->cid, 'absolute' => FALSE)),
  246. 'url:path' => 'comment/' . $parent_comment->cid,
  247. 'parent:url:absolute' => NULL,
  248. );
  249. $this->assertTokens('comment', array('comment' => $parent_comment), $tokens);
  250. $comment = new stdClass();
  251. $comment->nid = $node->nid;
  252. $comment->pid = $parent_comment->cid;
  253. $comment->cid = NULL;
  254. $comment->uid = 1;
  255. $comment->subject = $this->randomName();
  256. $comment->timestamp = mt_rand($parent_comment->created, REQUEST_TIME);
  257. $comment->language = LANGUAGE_NONE;
  258. $comment->body[LANGUAGE_NONE][0] = $this->randomName();
  259. comment_save($comment);
  260. $tokens = array(
  261. 'url' => url('comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid, 'absolute' => TRUE)),
  262. 'url:absolute' => url('comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid, 'absolute' => TRUE)),
  263. 'url:relative' => url('comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid, 'absolute' => FALSE)),
  264. 'url:path' => 'comment/' . $comment->cid,
  265. 'parent:url:absolute' => url('comment/' . $parent_comment->cid, array('fragment' => 'comment-' . $parent_comment->cid, 'absolute' => TRUE)),
  266. );
  267. $this->assertTokens('comment', array('comment' => $comment), $tokens);
  268. }
  269. }
  270. class TokenNodeTestCase extends TokenTestHelper {
  271. protected $profile = 'standard';
  272. public static function getInfo() {
  273. return array(
  274. 'name' => 'Node and content type token tests',
  275. 'description' => 'Test the node and content type tokens.',
  276. 'group' => 'Token',
  277. );
  278. }
  279. function testNodeTokens() {
  280. $source_node = $this->drupalCreateNode(array('log' => $this->randomName(), 'path' => array('alias' => 'content/source-node')));
  281. $tokens = array(
  282. 'source' => NULL,
  283. 'source:nid' => NULL,
  284. 'log' => $source_node->log,
  285. 'url:path' => 'content/source-node',
  286. 'url:absolute' => url("node/{$source_node->nid}", array('absolute' => TRUE)),
  287. 'url:relative' => url("node/{$source_node->nid}", array('absolute' => FALSE)),
  288. 'url:unaliased:path' => "node/{$source_node->nid}",
  289. 'content-type' => 'Basic page',
  290. 'content-type:name' => 'Basic page',
  291. 'content-type:machine-name' => 'page',
  292. 'content-type:description' => "Use <em>basic pages</em> for your static content, such as an 'About us' page.",
  293. 'content-type:node-count' => 1,
  294. 'content-type:edit-url' => url('admin/structure/types/manage/page', array('absolute' => TRUE)),
  295. // Deprecated tokens.
  296. 'tnid' => 0,
  297. 'type' => 'page',
  298. 'type-name' => 'Basic page',
  299. 'url:alias' => 'content/source-node',
  300. );
  301. $this->assertTokens('node', array('node' => $source_node), $tokens);
  302. $translated_node = $this->drupalCreateNode(array('tnid' => $source_node->nid, 'type' => 'article'));
  303. $tokens = array(
  304. 'source' => $source_node->title,
  305. 'source:nid' => $source_node->nid,
  306. 'log' => '',
  307. 'url:path' => "node/{$translated_node->nid}",
  308. 'url:absolute' => url("node/{$translated_node->nid}", array('absolute' => TRUE)),
  309. 'url:relative' => url("node/{$translated_node->nid}", array('absolute' => FALSE)),
  310. 'url:unaliased:path' => "node/{$translated_node->nid}",
  311. 'content-type' => 'Article',
  312. 'content-type:name' => 'Article',
  313. 'content-type:machine-name' => 'article',
  314. 'content-type:description' => "Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.",
  315. 'content-type:node-count' => 1,
  316. 'content-type:edit-url' => url('admin/structure/types/manage/article', array('absolute' => TRUE)),
  317. // Deprecated tokens.
  318. 'type' => 'article',
  319. 'type-name' => 'Article',
  320. 'tnid' => $source_node->nid,
  321. 'url:alias' => "node/{$translated_node->nid}",
  322. );
  323. $this->assertTokens('node', array('node' => $translated_node), $tokens);
  324. }
  325. }
  326. class TokenMenuTestCase extends TokenTestHelper {
  327. public static function getInfo() {
  328. return array(
  329. 'name' => 'Menu link and menu token tests',
  330. 'description' => 'Test the menu tokens.',
  331. 'group' => 'Token',
  332. );
  333. }
  334. public function setUp($modules = array()) {
  335. $modules[] = 'menu';
  336. parent::setUp($modules);
  337. }
  338. function testMenuTokens() {
  339. // Add a root link.
  340. $root_link = array(
  341. 'link_path' => 'root',
  342. 'link_title' => 'Root link',
  343. 'menu_name' => 'main-menu',
  344. );
  345. menu_link_save($root_link);
  346. // Add another link with the root link as the parent
  347. $parent_link = array(
  348. 'link_path' => 'root/parent',
  349. 'link_title' => 'Parent link',
  350. 'menu_name' => 'main-menu',
  351. 'plid' => $root_link['mlid'],
  352. );
  353. menu_link_save($parent_link);
  354. // Test menu link tokens.
  355. $tokens = array(
  356. 'mlid' => $parent_link['mlid'],
  357. 'title' => 'Parent link',
  358. 'menu' => 'Main menu',
  359. 'menu:name' => 'Main menu',
  360. 'menu:machine-name' => 'main-menu',
  361. 'menu:description' => 'The <em>Main</em> menu is used on many sites to show the major sections of the site, often in a top navigation bar.',
  362. 'menu:menu-link-count' => 2,
  363. 'menu:edit-url' => url("admin/structure/menu/manage/main-menu", array('absolute' => TRUE)),
  364. 'url' => url('root/parent', array('absolute' => TRUE)),
  365. 'url:absolute' => url('root/parent', array('absolute' => TRUE)),
  366. 'url:relative' => url('root/parent', array('absolute' => FALSE)),
  367. 'url:path' => 'root/parent',
  368. 'url:alias' => 'root/parent',
  369. 'edit-url' => url("admin/structure/menu/item/{$parent_link['mlid']}/edit", array('absolute' => TRUE)),
  370. 'parent' => 'Root link',
  371. 'parent:mlid' => $root_link['mlid'],
  372. 'parent:title' => 'Root link',
  373. 'parent:menu' => 'Main menu',
  374. 'parent:parent' => NULL,
  375. 'parents' => 'Root link',
  376. 'parents:count' => 1,
  377. 'parents:keys' => $root_link['mlid'],
  378. 'root' => 'Root link',
  379. 'root:mlid' => $root_link['mlid'],
  380. 'root:parent' => NULL,
  381. 'root:root' => NULL,
  382. );
  383. $this->assertTokens('menu-link', array('menu-link' => $parent_link), $tokens);
  384. // Add a node menu link
  385. $node_link = array(
  386. 'enabled' => TRUE,
  387. 'link_title' => 'Node link',
  388. 'plid' => $parent_link['mlid'],
  389. 'customized' => 0,
  390. 'description' => '',
  391. );
  392. $node = $this->drupalCreateNode(array('menu' => $node_link));
  393. // Test [node:menu] tokens.
  394. $tokens = array(
  395. 'menu-link' => 'Node link',
  396. 'menu-link:mlid' => $node->menu['mlid'],
  397. 'menu-link:title' => 'Node link',
  398. 'menu-link:menu' => 'Main menu',
  399. 'menu-link:url' => url('node/' . $node->nid, array('absolute' => TRUE)),
  400. 'menu-link:url:path' => 'node/' . $node->nid,
  401. 'menu-link:edit-url' => url("admin/structure/menu/item/{$node->menu['mlid']}/edit", array('absolute' => TRUE)),
  402. 'menu-link:parent' => 'Parent link',
  403. 'menu-link:parent:mlid' => $node->menu['plid'],
  404. 'menu-link:parent:mlid' => $parent_link['mlid'],
  405. 'menu-link:parents' => 'Root link, Parent link',
  406. 'menu-link:parents:count' => 2,
  407. 'menu-link:parents:keys' => $root_link['mlid'] . ', ' . $parent_link['mlid'],
  408. 'menu-link:root' => 'Root link',
  409. 'menu-link:root:mlid' => $root_link['mlid'],
  410. );
  411. $this->assertTokens('node', array('node' => $node), $tokens);
  412. // Reload the node which will not have $node->menu defined and re-test.
  413. $loaded_node = node_load($node->nid);
  414. $this->assertTokens('node', array('node' => $loaded_node), $tokens);
  415. // Regression test for http://drupal.org/node/1317926 to ensure the
  416. // original node object is not changed when calling menu_node_prepare().
  417. $this->assertTrue(!isset($loaded_node->menu), t('The $node->menu property was not modified during token replacement.'), 'Regression');
  418. }
  419. }
  420. class TokenTaxonomyTestCase extends TokenTestHelper {
  421. protected $vocab;
  422. public static function getInfo() {
  423. return array(
  424. 'name' => 'Taxonomy and vocabulary token tests',
  425. 'description' => 'Test the taxonomy tokens.',
  426. 'group' => 'Token',
  427. );
  428. }
  429. public function setUp($modules = array()) {
  430. $modules[] = 'taxonomy';
  431. parent::setUp($modules);
  432. // Create the default tags vocabulary.
  433. $vocabulary = (object) array(
  434. 'name' => 'Tags',
  435. 'machine_name' => 'tags',
  436. );
  437. taxonomy_vocabulary_save($vocabulary);
  438. $this->vocab = $vocabulary;
  439. }
  440. /**
  441. * Test the additional taxonomy term tokens.
  442. */
  443. function testTaxonomyTokens() {
  444. $root_term = $this->addTerm($this->vocab, array('name' => 'Root term', 'path' => array('alias' => 'root-term')));
  445. $tokens = array(
  446. 'url' => url("taxonomy/term/{$root_term->tid}", array('absolute' => TRUE)),
  447. 'url:absolute' => url("taxonomy/term/{$root_term->tid}", array('absolute' => TRUE)),
  448. 'url:relative' => url("taxonomy/term/{$root_term->tid}", array('absolute' => FALSE)),
  449. 'url:path' => 'root-term',
  450. 'url:unaliased:path' => "taxonomy/term/{$root_term->tid}",
  451. 'edit-url' => url("taxonomy/term/{$root_term->tid}/edit", array('absolute' => TRUE)),
  452. 'parents' => NULL,
  453. 'parents:count' => NULL,
  454. 'parents:keys' => NULL,
  455. 'root' => NULL,
  456. // Deprecated tokens
  457. 'url:alias' => 'root-term',
  458. );
  459. $this->assertTokens('term', array('term' => $root_term), $tokens);
  460. $parent_term = $this->addTerm($this->vocab, array('name' => 'Parent term', 'parent' => $root_term->tid));
  461. $tokens = array(
  462. 'url' => url("taxonomy/term/{$parent_term->tid}", array('absolute' => TRUE)),
  463. 'url:absolute' => url("taxonomy/term/{$parent_term->tid}", array('absolute' => TRUE)),
  464. 'url:relative' => url("taxonomy/term/{$parent_term->tid}", array('absolute' => FALSE)),
  465. 'url:path' => "taxonomy/term/{$parent_term->tid}",
  466. 'url:unaliased:path' => "taxonomy/term/{$parent_term->tid}",
  467. 'edit-url' => url("taxonomy/term/{$parent_term->tid}/edit", array('absolute' => TRUE)),
  468. 'parents' => 'Root term',
  469. 'parents:count' => 1,
  470. 'parents:keys' => $root_term->tid,
  471. 'root' => check_plain($root_term->name),
  472. 'root:tid' => $root_term->tid,
  473. // Deprecated tokens
  474. 'url:alias' => "taxonomy/term/{$parent_term->tid}",
  475. );
  476. $this->assertTokens('term', array('term' => $parent_term), $tokens);
  477. $term = $this->addTerm($this->vocab, array('name' => 'Test term', 'parent' => $parent_term->tid));
  478. $tokens = array(
  479. 'parents' => 'Root term, Parent term',
  480. 'parents:count' => 2,
  481. 'parents:keys' => implode(', ', array($root_term->tid, $parent_term->tid)),
  482. );
  483. $this->assertTokens('term', array('term' => $term), $tokens);
  484. }
  485. /**
  486. * Test the additional vocabulary tokens.
  487. */
  488. function testVocabularyTokens() {
  489. $vocabulary = $this->vocab;
  490. $tokens = array(
  491. 'machine-name' => 'tags',
  492. 'edit-url' => url("admin/structure/taxonomy/{$vocabulary->machine_name}/edit", array('absolute' => TRUE)),
  493. );
  494. $this->assertTokens('vocabulary', array('vocabulary' => $vocabulary), $tokens);
  495. }
  496. function addVocabulary(array $vocabulary = array()) {
  497. $vocabulary += array(
  498. 'name' => drupal_strtolower($this->randomName(5)),
  499. 'nodes' => array('article' => 'article'),
  500. );
  501. $vocabulary = (object) $vocabulary;
  502. taxonomy_vocabulary_save($vocabulary);
  503. return $vocabulary;
  504. }
  505. function addTerm(stdClass $vocabulary, array $term = array()) {
  506. $term += array(
  507. 'name' => drupal_strtolower($this->randomName(5)),
  508. 'vid' => $vocabulary->vid,
  509. );
  510. $term = (object) $term;
  511. taxonomy_term_save($term);
  512. return $term;
  513. }
  514. }
  515. class TokenUserTestCase extends TokenTestHelper {
  516. protected $account = NULL;
  517. public static function getInfo() {
  518. return array(
  519. 'name' => 'User token tests',
  520. 'description' => 'Test the user tokens.',
  521. 'group' => 'Token',
  522. );
  523. }
  524. public function setUp($modules = array()) {
  525. parent::setUp($modules);
  526. // Enable user pictures.
  527. variable_set('user_pictures', 1);
  528. variable_set('user_picture_file_size', '');
  529. // Set up the pictures directory.
  530. $picture_path = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures');
  531. if (!file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY)) {
  532. $this->fail('Could not create directory ' . $picture_path . '.');
  533. }
  534. $this->account = $this->drupalCreateUser(array('administer users'));
  535. $this->drupalLogin($this->account);
  536. }
  537. function testUserTokens() {
  538. // Add a user picture to the account.
  539. $image = current($this->drupalGetTestFiles('image'));
  540. $edit = array('files[picture_upload]' => drupal_realpath($image->uri));
  541. $this->drupalPost('user/' . $this->account->uid . '/edit', $edit, t('Save'));
  542. // Load actual user data from database.
  543. $this->account = user_load($this->account->uid, TRUE);
  544. $this->assertTrue(!empty($this->account->picture->fid), 'User picture uploaded.');
  545. $user_tokens = array(
  546. 'picture' => theme('user_picture', array('account' => $this->account)),
  547. 'picture:fid' => $this->account->picture->fid,
  548. 'picture:size-raw' => 125,
  549. 'ip-address' => NULL,
  550. 'roles' => implode(', ', $this->account->roles),
  551. 'roles:keys' => implode(', ', array_keys($this->account->roles)),
  552. );
  553. $this->assertTokens('user', array('user' => $this->account), $user_tokens);
  554. $edit = array('user_pictures' => FALSE);
  555. $this->drupalPost('admin/config/people/accounts', $edit, 'Save configuration');
  556. $this->assertText('The configuration options have been saved.');
  557. // Remove the simpletest-created user role.
  558. user_role_delete(end($this->account->roles));
  559. $this->account = user_load($this->account->uid, TRUE);
  560. $user_tokens = array(
  561. 'picture' => NULL,
  562. 'picture:fid' => NULL,
  563. 'ip-address' => NULL,
  564. 'roles' => 'authenticated user',
  565. 'roles:keys' => (string) DRUPAL_AUTHENTICATED_RID,
  566. );
  567. $this->assertTokens('user', array('user' => $this->account), $user_tokens);
  568. // The ip address token should work for the current user token type.
  569. $tokens = array(
  570. 'ip-address' => ip_address(),
  571. );
  572. $this->assertTokens('current-user', array(), $tokens);
  573. $anonymous = drupal_anonymous_user();
  574. // Mess with the role array to ensure we still get consistent output.
  575. $anonymous->roles[DRUPAL_ANONYMOUS_RID] = DRUPAL_ANONYMOUS_RID;
  576. $tokens = array(
  577. 'roles' => 'anonymous user',
  578. 'roles:keys' => (string) DRUPAL_ANONYMOUS_RID,
  579. );
  580. $this->assertTokens('user', array('user' => $anonymous), $tokens);
  581. }
  582. }
  583. class TokenEntityTestCase extends TokenTestHelper {
  584. public static function getInfo() {
  585. return array(
  586. 'name' => 'Entity token tests',
  587. 'description' => 'Test the entity tokens.',
  588. 'group' => 'Token',
  589. );
  590. }
  591. public function setUp($modules = array()) {
  592. $modules[] = 'taxonomy';
  593. parent::setUp($modules);
  594. // Create the default tags vocabulary.
  595. $vocabulary = (object) array(
  596. 'name' => 'Tags',
  597. 'machine_name' => 'tags',
  598. );
  599. taxonomy_vocabulary_save($vocabulary);
  600. $this->vocab = $vocabulary;
  601. }
  602. function testEntityMapping() {
  603. $this->assertIdentical(token_get_entity_mapping('token', 'node'), 'node');
  604. $this->assertIdentical(token_get_entity_mapping('token', 'term'), 'taxonomy_term');
  605. $this->assertIdentical(token_get_entity_mapping('token', 'vocabulary'), 'taxonomy_vocabulary');
  606. $this->assertIdentical(token_get_entity_mapping('token', 'invalid'), FALSE);
  607. $this->assertIdentical(token_get_entity_mapping('token', 'invalid', TRUE), 'invalid');
  608. $this->assertIdentical(token_get_entity_mapping('entity', 'node'), 'node');
  609. $this->assertIdentical(token_get_entity_mapping('entity', 'taxonomy_term'), 'term');
  610. $this->assertIdentical(token_get_entity_mapping('entity', 'taxonomy_vocabulary'), 'vocabulary');
  611. $this->assertIdentical(token_get_entity_mapping('entity', 'invalid'), FALSE);
  612. $this->assertIdentical(token_get_entity_mapping('entity', 'invalid', TRUE), 'invalid');
  613. // Test that when we send the mis-matched entity type into token_replace()
  614. // that we still get the tokens replaced.
  615. $vocabulary = taxonomy_vocabulary_machine_name_load('tags');
  616. $term = $this->addTerm($vocabulary);
  617. $this->assertIdentical(token_replace('[vocabulary:name]', array('taxonomy_vocabulary' => $vocabulary)), $vocabulary->name);
  618. $this->assertIdentical(token_replace('[term:name][term:vocabulary:name]', array('taxonomy_term' => $term)), $term->name . $vocabulary->name);
  619. }
  620. function addTerm(stdClass $vocabulary, array $term = array()) {
  621. $term += array(
  622. 'name' => drupal_strtolower($this->randomName(5)),
  623. 'vid' => $vocabulary->vid,
  624. );
  625. $term = (object) $term;
  626. taxonomy_term_save($term);
  627. return $term;
  628. }
  629. /**
  630. * Test the [entity:original:*] tokens.
  631. */
  632. function testEntityOriginal() {
  633. $node = $this->drupalCreateNode(array('title' => 'Original title'));
  634. $tokens = array(
  635. 'nid' => $node->nid,
  636. 'title' => 'Original title',
  637. 'original' => NULL,
  638. 'original:nid' => NULL,
  639. );
  640. $this->assertTokens('node', array('node' => $node), $tokens);
  641. // Emulate the original entity property that would be available from
  642. // node_save() and change the title for the node.
  643. $node->original = entity_load_unchanged('node', $node->nid);
  644. $node->title = 'New title';
  645. $tokens = array(
  646. 'nid' => $node->nid,
  647. 'title' => 'New title',
  648. 'original' => 'Original title',
  649. 'original:nid' => $node->nid,
  650. );
  651. $this->assertTokens('node', array('node' => $node), $tokens);
  652. }
  653. }
  654. /**
  655. * Test the profile tokens.
  656. */
  657. class TokenProfileTestCase extends TokenTestHelper {
  658. private $account;
  659. public static function getInfo() {
  660. return array(
  661. 'name' => 'Profile token tests',
  662. 'description' => 'Test the profile tokens.',
  663. 'group' => 'Token',
  664. );
  665. }
  666. public function setUp($modules = array()) {
  667. $modules[] = 'profile';
  668. parent::setUp($modules);
  669. $this->account = $this->drupalCreateUser(array('administer users'));
  670. $this->drupalLogin($this->account);
  671. }
  672. /**
  673. * Test the profile tokens.
  674. */
  675. function testProfileTokens() {
  676. $field_types = _profile_field_types();
  677. foreach (array_keys($field_types) as $field_type) {
  678. $field = array();
  679. switch ($field_type) {
  680. case 'checkbox':
  681. $field['title'] = 'This is a checkbox';
  682. break;
  683. case 'selection':
  684. $field['options'] = implode("\n", array('Red', 'Blue', 'Green'));
  685. break;
  686. }
  687. $this->addProfileField($field_type, $field);
  688. }
  689. // Submit the profile fields for the user.
  690. $edit = array(
  691. 'profile_textfield' => 'This is a text field',
  692. 'profile_textarea' => "First paragraph.\n\nSecond paragraph.",
  693. 'profile_checkbox' => TRUE,
  694. 'profile_selection' => 'Red',
  695. 'profile_list' => ' Drupal , Joomla ',
  696. 'profile_url' => 'http://www.example.com/',
  697. 'profile_date[month]' => 5,
  698. 'profile_date[day]' => 20,
  699. 'profile_date[year]' => 1984,
  700. );
  701. $this->drupalPost("user/{$this->account->uid}/edit/SimpleTest", $edit, 'Save');
  702. $account = user_load($this->account->uid, TRUE);
  703. // Test the profile token values.
  704. $tokens = array(
  705. 'profile-textfield' => 'This is a text field',
  706. 'profile-textarea' => "<p>First paragraph.</p>\n<p>Second paragraph.</p>\n",
  707. 'profile-checkbox' => 'This is a checkbox',
  708. 'profile-selection' => 'Red',
  709. 'profile-list' => 'Drupal, Joomla',
  710. 'profile-url' => 'http://www.example.com/',
  711. 'profile-date' => format_date(453859200, 'medium', '', NULL),
  712. 'profile-date:raw' => '453859200',
  713. 'profile-date:custom:Y' => '1984',
  714. );
  715. $this->assertTokens('user', array('user' => $account), $tokens);
  716. // 'Un-select' the checkbox and select profile fields.
  717. $edit = array(
  718. 'profile_checkbox' => FALSE,
  719. 'profile_selection' => '0',
  720. );
  721. $this->drupalPost("user/{$this->account->uid}/edit/SimpleTest", $edit, 'Save');
  722. $account = user_load($this->account->uid, TRUE);
  723. // The checkbox and select profile tokens should no longer return a value.
  724. $tokens = array(
  725. 'profile-checkbox' => NULL,
  726. 'profile-selection' => NULL,
  727. );
  728. $this->assertTokens('user', array('user' => $account), $tokens);
  729. }
  730. /**
  731. * Add a profile field.
  732. *
  733. * @param $type
  734. * The profile field type.
  735. * @param $field
  736. * (optional) An array of the profile field properties.
  737. *
  738. * @return
  739. * The saved profile field record object.
  740. *
  741. * @see drupal_form_submit()
  742. */
  743. function addProfileField($type, array $field = array()) {
  744. $field += array(
  745. 'type' => $type,
  746. 'category' => 'SimpleTest',
  747. 'title' => $this->randomName(8),
  748. 'name' => 'profile_' . $type,
  749. 'explanation' => $this->randomName(50),
  750. 'autocomplete' => 0,
  751. 'required' => 0,
  752. 'register' => 0,
  753. );
  754. drupal_write_record('profile_field', $field);
  755. // Verify the profile field was created successfully.
  756. $saved_field = db_query("SELECT * FROM {profile_field} WHERE type = :type AND name = :name", array(':type' => $type, ':name' => $field['name']))->fetchObject();
  757. if (empty($saved_field)) {
  758. $this->fail(t('Failed to create profile field @name.', array('@name' => $saved_field->name)));
  759. }
  760. return $saved_field;
  761. }
  762. }
  763. /**
  764. * Test the current page tokens.
  765. */
  766. class TokenCurrentPageTestCase extends TokenTestHelper {
  767. public static function getInfo() {
  768. return array(
  769. 'name' => 'Current page token tests',
  770. 'description' => 'Test the [current-page:*] tokens.',
  771. 'group' => 'Token',
  772. );
  773. }
  774. function testCurrentPageTokens() {
  775. $tokens = array(
  776. '[current-page:title]' => t('Welcome to @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))),
  777. '[current-page:url]' => url('node', array('absolute' => TRUE)),
  778. '[current-page:url:absolute]' => url('node', array('absolute' => TRUE)),
  779. '[current-page:url:relative]' => url('node', array('absolute' => FALSE)),
  780. '[current-page:url:path]' => 'node',
  781. '[current-page:url:args:value:0]' => 'node',
  782. '[current-page:url:args:value:1]' => NULL,
  783. '[current-page:url:unaliased]' => url('node', array('absolute' => TRUE, 'alias' => TRUE)),
  784. '[current-page:page-number]' => 1,
  785. '[current-page:query:foo]' => NULL,
  786. '[current-page:query:bar]' => NULL,
  787. '[current-page:query:q]' => 'node',
  788. // Deprecated tokens
  789. '[current-page:arg:0]' => 'node',
  790. '[current-page:arg:1]' => NULL,
  791. );
  792. $this->assertPageTokens('', $tokens);
  793. $node = $this->drupalCreateNode(array('title' => 'Node title', 'path' => array('alias' => 'node-alias')));
  794. $tokens = array(
  795. '[current-page:title]' => 'Node title',
  796. '[current-page:url]' => url("node/{$node->nid}", array('absolute' => TRUE)),
  797. '[current-page:url:absolute]' => url("node/{$node->nid}", array('absolute' => TRUE)),
  798. '[current-page:url:relative]' => url("node/{$node->nid}", array('absolute' => FALSE)),
  799. '[current-page:url:alias]' => 'node-alias',
  800. '[current-page:url:args:value:0]' => 'node-alias',
  801. '[current-page:url:args:value:1]' => NULL,
  802. '[current-page:url:unaliased]' => url("node/{$node->nid}", array('absolute' => TRUE, 'alias' => TRUE)),
  803. '[current-page:url:unaliased:args:value:0]' => 'node',
  804. '[current-page:url:unaliased:args:value:1]' => $node->nid,
  805. '[current-page:url:unaliased:args:value:2]' => NULL,
  806. '[current-page:page-number]' => 1,
  807. '[current-page:query:foo]' => 'bar',
  808. '[current-page:query:bar]' => NULL,
  809. '[current-page:query:q]' => 'node/1',
  810. // Deprecated tokens
  811. '[current-page:arg:0]' => 'node',
  812. '[current-page:arg:1]' => 1,
  813. '[current-page:arg:2]' => NULL,
  814. );
  815. $this->assertPageTokens("node/{$node->nid}", $tokens, array(), array('url_options' => array('query' => array('foo' => 'bar'))));
  816. }
  817. }
  818. class TokenArrayTestCase extends TokenTestHelper {
  819. public static function getInfo() {
  820. return array(
  821. 'name' => 'Array token tests',
  822. 'description' => 'Test the array tokens.',
  823. 'group' => 'Token',
  824. );
  825. }
  826. function testArrayTokens() {
  827. // Test a simple array.
  828. $array = array(0 => 'a', 1 => 'b', 2 => 'c', 4 => 'd');
  829. $tokens = array(
  830. 'first' => 'a',
  831. 'last' => 'd',
  832. 'value:0' => 'a',
  833. 'value:2' => 'c',
  834. 'count' => 4,
  835. 'keys' => '0, 1, 2, 4',
  836. 'keys:value:3' => '4',
  837. 'keys:join' => '0124',
  838. 'reversed' => 'd, c, b, a',
  839. 'reversed:keys' => '4, 2, 1, 0',
  840. 'join:/' => 'a/b/c/d',
  841. 'join' => 'abcd',
  842. 'join:, ' => 'a, b, c, d',
  843. 'join: ' => 'a b c d',
  844. );
  845. $this->assertTokens('array', array('array' => $array), $tokens);
  846. // Test a mixed simple and render array.
  847. // 2 => c, 0 => a, 4 => d, 1 => b
  848. $array = array(
  849. '#property' => 'value',
  850. 0 => 'a',
  851. 1 => array('#markup' => 'b', '#weight' => 0.01),
  852. 2 => array('#markup' => 'c', '#weight' => -10),
  853. 4 => array('#markup' => 'd', '#weight' => 0),
  854. );
  855. $tokens = array(
  856. 'first' => 'c',
  857. 'last' => 'b',
  858. 'value:0' => 'a',
  859. 'value:2' => 'c',
  860. 'count' => 4,
  861. 'keys' => '2, 0, 4, 1',
  862. 'keys:value:3' => '1',
  863. 'keys:join' => '2041',
  864. 'reversed' => 'b, d, a, c',
  865. 'reversed:keys' => '1, 4, 0, 2',
  866. 'join:/' => 'c/a/d/b',
  867. 'join' => 'cadb',
  868. 'join:, ' => 'c, a, d, b',
  869. 'join: ' => 'c a d b',
  870. );
  871. $this->assertTokens('array', array('array' => $array), $tokens);
  872. }
  873. }
  874. class TokenRandomTestCase extends TokenTestHelper {
  875. public static function getInfo() {
  876. return array(
  877. 'name' => 'Random token tests',
  878. 'description' => 'Test the random tokens.',
  879. 'group' => 'Token',
  880. );
  881. }
  882. function testRandomTokens() {
  883. $tokens = array(
  884. 'number' => '[0-9]{1,}',
  885. 'hash:md5' => '[0-9a-f]{32}',
  886. 'hash:sha1' => '[0-9a-f]{40}',
  887. 'hash:sha256' => '[0-9a-f]{64}',
  888. 'hash:invalid-algo' => NULL,
  889. );
  890. $first_set = $this->assertTokens('random', array(), $tokens, array('regex' => TRUE));
  891. $second_set = $this->assertTokens('random', array(), $tokens, array('regex' => TRUE));
  892. foreach ($first_set as $token => $value) {
  893. $this->assertNotIdentical($first_set[$token], $second_set[$token]);
  894. }
  895. }
  896. }
  897. /**
  898. * @todo Remove when http://drupal.org/node/1173706 is fixed.
  899. */
  900. class TokenDateTestCase extends TokenTestHelper {
  901. public static function getInfo() {
  902. return array(
  903. 'name' => 'Date token tests',
  904. 'description' => 'Test the date tokens.',
  905. 'group' => 'Token',
  906. );
  907. }
  908. function testDateTokens() {
  909. $tokens = array(
  910. 'token_test' => '1984',
  911. 'invalid_format' => NULL,
  912. );
  913. $this->assertTokens('date', array('date' => 453859200), $tokens);
  914. }
  915. }
  916. class TokenFileTestCase extends TokenTestHelper {
  917. public static function getInfo() {
  918. return array(
  919. 'name' => 'File token tests',
  920. 'description' => 'Test the file tokens.',
  921. 'group' => 'Token',
  922. );
  923. }
  924. function testFileTokens() {
  925. // Create a test file object.
  926. $file = new stdClass();
  927. $file->fid = 1;
  928. $file->filename = 'test.png';
  929. $file->filesize = 100;
  930. $file->uri = 'public://images/test.png';
  931. $file->filemime = 'image/png';
  932. $tokens = array(
  933. 'basename' => 'test.png',
  934. 'extension' => 'png',
  935. 'size-raw' => 100,
  936. );
  937. $this->assertTokens('file', array('file' => $file), $tokens);
  938. // Test a file with no extension and a fake name.
  939. $file->filename = 'Test PNG image';
  940. $file->uri = 'public://images/test';
  941. $tokens = array(
  942. 'basename' => 'test',
  943. 'extension' => '',
  944. 'size-raw' => 100,
  945. );
  946. $this->assertTokens('file', array('file' => $file), $tokens);
  947. }
  948. }
  949. class TokenBlockTestCase extends TokenTestHelper {
  950. public static function getInfo() {
  951. return array(
  952. 'name' => 'Block token tests',
  953. 'description' => 'Test the block title token replacement.',
  954. 'group' => 'Token',
  955. );
  956. }
  957. public function setUp($modules = array()) {
  958. $modules[] = 'block';
  959. parent::setUp($modules);
  960. $this->admin_user = $this->drupalCreateUser(array('access content', 'administer blocks'));
  961. $this->drupalLogin($this->admin_user);
  962. }
  963. public function testBlockTitleTokens() {
  964. $edit['title'] = '[user:name]';
  965. $edit['info'] = 'Test token title block';
  966. $edit['body[value]'] = 'This is the test token title block.';
  967. $this->drupalPost('admin/structure/block/add', $edit, 'Save block');
  968. // Ensure token validation is working on the block.
  969. $this->assertText('The Block title is using the following invalid tokens: [user:name].');
  970. // Create the block for real now with a valid title.
  971. $edit['title'] = '[current-page:title] block title';
  972. $edit['regions[bartik]'] = 'sidebar_first';
  973. $this->drupalPost(NULL, $edit, 'Save block');
  974. $this->drupalGet('node');
  975. $this->assertText('Welcome to ' . variable_get('site_name', 'Drupal') . ' block title');
  976. // Ensure that tokens are not double-escaped when output as a block title.
  977. $node = $this->drupalCreateNode(array('title' => "Site's first node"));
  978. $this->drupalGet('node/' . $node->nid);
  979. // The apostraphe should only be escaped once via check_plain().
  980. $this->assertRaw("Site&#039;s first node block title");
  981. }
  982. }