term_merge.test 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722
  1. <?php
  2. /**
  3. * @file
  4. * Test the Term Merge module.
  5. */
  6. /**
  7. * Base class for all tests of Term Merge module.
  8. */
  9. class TermMergeWebTestCase extends DrupalWebTestCase {
  10. /**
  11. * Fully loaded Drupal user who has access to all required parts of the
  12. * website for testing.
  13. *
  14. * @var object
  15. */
  16. protected $admin;
  17. /**
  18. * Fully loaded Drupal taxonomy vocabulary object on which all tests are run.
  19. *
  20. * @var object
  21. */
  22. protected $vocabulary;
  23. /**
  24. * SetUp method.
  25. */
  26. public function setUp() {
  27. $modules = $this->normalizeSetUpArguments(func_get_args());
  28. $modules[] = 'term_merge';
  29. parent::setUp($modules);
  30. $this->admin = $this->drupalCreateUser(array(
  31. 'administer taxonomy',
  32. 'merge terms',
  33. 'administer content types',
  34. 'bypass node access',
  35. ));
  36. // Creating vocabularies.
  37. $this->drupalLogin($this->admin);
  38. $name = $this->randomName();
  39. $this->drupalPost('admin/structure/taxonomy/add', array(
  40. 'name' => $name,
  41. 'machine_name' => 'vocabulary',
  42. 'description' => $this->randomName(),
  43. ), 'Save');
  44. $this->vocabulary = taxonomy_vocabulary_machine_name_load('vocabulary');
  45. // Flushing static cache.
  46. _field_info_collate_fields(TRUE);
  47. }
  48. /**
  49. * Return last inserted term into the specified vocabulary.
  50. *
  51. * @param object $vocabulary
  52. * Fully loaded taxonomy vocabulary object
  53. *
  54. * @return object
  55. * Fully loaded taxonomy term object of the last inserted term into
  56. * the specified vocabulary
  57. */
  58. protected function getLastTerm($vocabulary) {
  59. drupal_static_reset();
  60. $tree = taxonomy_get_tree($vocabulary->vid);
  61. $max = 0;
  62. $term = NULL;
  63. foreach ($tree as $v) {
  64. if ($v->tid > $max) {
  65. $max = $v->tid;
  66. $term = $v;
  67. }
  68. }
  69. $term = entity_load_unchanged('taxonomy_term', $term->tid);
  70. return $term;
  71. }
  72. /**
  73. * Normalize the input arguments of ::setUp() method.
  74. *
  75. * The arguments of ::setUp() method can either be a single argument (array of
  76. * modules) or a set of input arguments where each single argument is a module
  77. * name.
  78. *
  79. * @param array $args
  80. * Array of input arguments given to a ::setUp() method
  81. *
  82. * @return array
  83. * Array of modules that are given to a ::setUp() method.
  84. */
  85. protected function normalizeSetUpArguments($args) {
  86. return (isset($args[0]) && is_array($args[0])) ? $args[0] : $args;
  87. }
  88. }
  89. /**
  90. * Test the functionality of Term Merge module.
  91. */
  92. class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
  93. /**
  94. * GetInfo method.
  95. */
  96. public static function getInfo() {
  97. return array(
  98. 'name' => 'Term Merge',
  99. 'description' => 'Ensure that the module Term Merge works correctly.',
  100. 'group' => 'Term Merge',
  101. );
  102. }
  103. /**
  104. * Test merging two terms.
  105. */
  106. public function testTermMerge() {
  107. // Checking whether parent's relationship is handled as it should.
  108. // At the same time we make sure 'term_branch_keep' property functions.
  109. $terms = array(
  110. 'trunk' => FALSE,
  111. 'branch' => FALSE,
  112. 'another_parent' => FALSE,
  113. 'branch_child' => FALSE,
  114. );
  115. foreach ($terms as $term_type => $tmp) {
  116. $url = 'admin/structure/taxonomy/vocabulary/add';
  117. $name = $this->randomName();
  118. $edit = array(
  119. 'name' => $name,
  120. );
  121. // Putting "branch" to be parent of "branch_child".
  122. if ($term_type == 'branch_child') {
  123. $edit['parent[]'] = array($terms['branch']->tid, $terms['another_parent']->tid);
  124. }
  125. $this->drupalPost($url, $edit, 'Save');
  126. $terms[$term_type] = $this->getLastTerm($this->vocabulary);
  127. }
  128. // Firstly we try to merge without deleting the branch term and make sure
  129. // branch's children are not reassigned to the trunk term nor the branch
  130. // term itself is deleted.
  131. actions_do('term_merge_action', $terms['branch'], array(
  132. 'term_trunk' => $terms['trunk']->tid,
  133. 'merge_fields' => array(),
  134. 'term_branch_keep' => TRUE,
  135. ));
  136. $this->drupalGet('taxonomy/term/' . $terms['branch']->tid);
  137. $this->assertText($terms['branch']->name);
  138. drupal_static_reset();
  139. $parents = array();
  140. foreach (taxonomy_get_parents_all($terms['branch_child']->tid) as $parent) {
  141. $parents[] = $parent->tid;
  142. }
  143. $valid_parents = array(
  144. $terms['branch_child']->tid,
  145. $terms['branch']->tid,
  146. $terms['another_parent']->tid,
  147. );
  148. $intersection = array_intersect($parents, $valid_parents);
  149. $this->assertTrue(count($intersection) == count($valid_parents), 'The parents of children of term branch are not updated if property "term_branch_keep" is set to FALSE.');
  150. // Now we merge with deletion of branch term, thus the parents of its
  151. // children have to be updated.
  152. actions_do('term_merge_action', $terms['branch'], array(
  153. 'term_trunk' => $terms['trunk']->tid,
  154. 'merge_fields' => array(),
  155. 'term_branch_keep' => FALSE,
  156. ));
  157. $this->drupalGet('taxonomy/term/' . $terms['branch']->tid);
  158. $this->assertResponse(404, 'The branch term has been deleted.');
  159. drupal_static_reset();
  160. $parents = array();
  161. foreach (taxonomy_get_parents_all($terms['branch_child']->tid) as $parent) {
  162. $parents[] = $parent->tid;
  163. }
  164. $valid_parents = array(
  165. $terms['branch_child']->tid,
  166. $terms['trunk']->tid,
  167. $terms['another_parent']->tid,
  168. );
  169. $intersection = array_intersect($parents, $valid_parents);
  170. $this->assertTrue(count($intersection) == count($valid_parents), 'The parents of children of term branch are updated if property "term_branch_keep" is set to TRUE.');
  171. // Now testing 'merge_fields' property. Attaching fields to taxonomy terms.
  172. $bundle = field_extract_bundle('taxonomy_term', $this->vocabulary);
  173. $fields_map = array(
  174. 'term_merge_test_single' => 1,
  175. 'term_merge_test_unlimited' => FIELD_CARDINALITY_UNLIMITED,
  176. 'term_merge_do_not_merge' => 10,
  177. 'term_merge_not_unique' => FIELD_CARDINALITY_UNLIMITED,
  178. );
  179. foreach ($fields_map as $field_name => $cardinality) {
  180. $field = array(
  181. 'field_name' => $field_name,
  182. 'cardinality' => $cardinality,
  183. 'locked' => TRUE,
  184. 'type' => 'text',
  185. );
  186. field_create_field($field);
  187. field_create_instance(array(
  188. 'field_name' => $field_name,
  189. 'entity_type' => 'taxonomy_term',
  190. 'bundle' => $bundle,
  191. 'label' => $field_name,
  192. ));
  193. }
  194. $terms = array(
  195. 'trunk' => FALSE,
  196. 'branch' => FALSE,
  197. );
  198. foreach ($terms as $term_type => $tmp) {
  199. $term = (object) array(
  200. 'vid' => $this->vocabulary->vid,
  201. 'name' => $this->randomName(),
  202. );
  203. foreach ($fields_map as $field_name => $cardinality) {
  204. switch ($field_name) {
  205. case 'term_merge_test_single':
  206. $term->{$field_name}[LANGUAGE_NONE][0]['value'] = $this->randomName();
  207. break;
  208. case 'term_merge_test_unlimited':
  209. case 'term_merge_do_not_merge':
  210. $count = rand(1, 3);
  211. for ($i = 0; $i < $count; $i++) {
  212. $term->{$field_name}[LANGUAGE_NONE][$i]['value'] = $this->randomName();
  213. }
  214. break;
  215. case 'term_merge_not_unique':
  216. $term->{$field_name}[LANGUAGE_NONE][0]['value'] = 'term_merge_not_unique_value';
  217. break;
  218. }
  219. }
  220. taxonomy_term_save($term);
  221. $terms[$term_type] = $this->getLastTerm($this->vocabulary);
  222. }
  223. // Firstly we make sure if 'merge_fields' is disabled, the fields are not
  224. // merged.
  225. actions_do('term_merge_action', $terms['branch'], array(
  226. 'term_trunk' => $terms['trunk']->tid,
  227. 'merge_fields' => array(),
  228. 'term_branch_keep' => TRUE,
  229. ));
  230. $this->drupalGet('taxonomy/term/' . $terms['trunk']->tid);
  231. foreach ($fields_map as $field_name => $cardinality) {
  232. foreach (field_get_items('taxonomy_term', $terms['branch'], $field_name) as $item) {
  233. if ($field_name != 'term_merge_not_unique') {
  234. $this->assertNoText($item['value'], 'Values of field ' . $field_name . ' have not been added to the trunk term with disabled "merge_fields" option.');
  235. }
  236. }
  237. }
  238. // Now we try merging with merging fields. The values of the branch term
  239. // should be added to the trunk term's values only in where we asked them
  240. // to be added. Moreover, only unique values are to be kept in each of the
  241. // merged fields.
  242. actions_do('term_merge_action', $terms['branch'], array(
  243. 'term_trunk' => $terms['trunk']->tid,
  244. 'merge_fields' => array(
  245. 'term_merge_test_single',
  246. 'term_merge_test_unlimited',
  247. 'term_merge_not_unique',
  248. ),
  249. 'term_branch_keep' => TRUE,
  250. ));
  251. $this->drupalGet('taxonomy/term/' . $terms['trunk']->tid);
  252. foreach ($fields_map as $field_name => $cardinality) {
  253. switch ($field_name) {
  254. case 'term_merge_test_single':
  255. case 'term_merge_do_not_merge':
  256. // Make sure if cardinality limit is hit, firstly original trunk term
  257. // values are stored. And make sure values of fields that are not
  258. // instructed to be added to trunk term's values are actually not
  259. // added.
  260. foreach (field_get_items('taxonomy_term', $terms['branch'], $field_name) as $item) {
  261. $this->assertNoText($item['value'], 'Values of field ' . $field_name . ' (cardinality ' . $cardinality . ') have not been added to the trunk term with enabled "merge_fields" option.');
  262. }
  263. break;
  264. case 'term_merge_not_unique':
  265. // Make sure only the unique values in merged field are kept.
  266. foreach (field_get_items('taxonomy_term', $terms['trunk'], $field_name) as $item) {
  267. $this->assertUniqueText($item['value'], 'Only unique field values are kept in the trunk term field after merging terms with enabled "merge_fields" option.');
  268. }
  269. break;
  270. case 'term_merge_test_unlimited':
  271. // Make sure values of fields that are instructed to be added to trunk
  272. // term's values are actually added.
  273. foreach (field_get_items('taxonomy_term', $terms['branch'], $field_name) as $item) {
  274. $this->assertText($item['value'], 'Values of field ' . $field_name . ' (cardinality ' . $cardinality . ') have been added to the trunk term with enabled "merge_fields" option.');
  275. }
  276. break;
  277. }
  278. }
  279. // Make sure that all taxonomy term reference fields are updated to point
  280. // from a branch term to a trunk term in other entities that have taxonomy
  281. // term reference fields.
  282. $terms = array(
  283. 'trunk' => FALSE,
  284. 'branch' => FALSE,
  285. );
  286. foreach ($terms as $term_type => $tmp) {
  287. $url = 'admin/structure/taxonomy/vocabulary/add';
  288. $name = $this->randomName();
  289. $edit = array(
  290. 'name' => $name,
  291. );
  292. $this->drupalPost($url, $edit, 'Save');
  293. $terms[$term_type] = $this->getLastTerm($this->vocabulary);
  294. }
  295. // Firstly we need to create a new content type and assign term reference
  296. // field to this new content type.
  297. $this->drupalPost('admin/structure/types/add', array(
  298. 'name' => $this->randomName(),
  299. 'type' => 'term_merge_node',
  300. ), 'Save content type');
  301. $this->drupalPost('admin/structure/types/manage/term-merge-node/fields', array(
  302. 'fields[_add_new_field][label]' => 'Term Reference',
  303. 'fields[_add_new_field][field_name]' => 'term_reference',
  304. 'fields[_add_new_field][type]' => 'taxonomy_term_reference',
  305. 'fields[_add_new_field][widget_type]' => 'taxonomy_autocomplete',
  306. ), 'Save');
  307. $this->drupalPost(NULL, array(
  308. 'field[settings][allowed_values][0][vocabulary]' => $this->vocabulary->machine_name,
  309. ), 'Save field settings');
  310. $this->drupalPost(NULL, array(
  311. 'field[cardinality]' => FIELD_CARDINALITY_UNLIMITED,
  312. ), 'Save settings');
  313. // Flushing fields API cache.
  314. _field_info_collate_fields(TRUE);
  315. // Creating a new node and settings its term reference field to point to
  316. // the term branch.
  317. $title = $this->randomName();
  318. $this->drupalPost('node/add/term-merge-node', array(
  319. 'title' => $title,
  320. 'field_term_reference[' . LANGUAGE_NONE . ']' => $terms['branch']->name,
  321. ), 'Save');
  322. $node = $this->drupalGetNodeByTitle($title, TRUE);
  323. actions_do('term_merge_action', $terms['branch'], array(
  324. 'term_trunk' => $terms['trunk']->tid,
  325. 'merge_fields' => array(),
  326. 'term_branch_keep' => TRUE,
  327. ));
  328. $this->drupalGet('node/' . $node->nid);
  329. $this->assertText($terms['trunk']->name, 'Taxonomy term reference field gets updated to point from term branch to term trunk after merging terms.');
  330. // Testing 'Keep only unique' setting for merging. We create a node assigned
  331. // to both branch and trunk terms, and merge with, and then without 'Keep
  332. // only unique' setting, asserting each result.
  333. $terms = array(
  334. 'trunk' => FALSE,
  335. 'branch' => FALSE,
  336. );
  337. foreach ($terms as $term_type => $tmp) {
  338. $url = 'admin/structure/taxonomy/vocabulary/add';
  339. $name = $this->randomName();
  340. $edit = array(
  341. 'name' => $name,
  342. );
  343. $this->drupalPost($url, $edit, 'Save');
  344. $terms[$term_type] = $this->getLastTerm($this->vocabulary);
  345. }
  346. $title = $this->randomName();
  347. $this->drupalPost('node/add/term-merge-node', array(
  348. 'title' => $title,
  349. 'field_term_reference[' . LANGUAGE_NONE . ']' => $terms['branch']->name . ', ' . $terms['trunk']->name,
  350. ), 'Save');
  351. actions_do('term_merge_action', $terms['branch'], array(
  352. 'term_trunk' => $terms['trunk']->tid,
  353. 'merge_fields' => array(),
  354. 'term_branch_keep' => TRUE,
  355. 'keep_only_unique' => FALSE,
  356. ));
  357. $node = $this->drupalGetNodeByTitle($title);
  358. $is_first_trunk = $node->field_term_reference[LANGUAGE_NONE][0]['tid'] == $terms['trunk']->tid;
  359. $is_second_trunk = $node->field_term_reference[LANGUAGE_NONE][1]['tid'] == $terms['trunk']->tid;
  360. $this->assertTrue($is_first_trunk && $is_second_trunk, 'The same terms are kept in term reference field values if "Keep only unique" is off.');
  361. // We switch roles of 'trunk' and 'branch' now. We have a node with 2 terms,
  362. // if we merge them into another with "Keep only unique" on we are supposed
  363. // to have only 1 term after merging.
  364. actions_do('term_merge_action', $terms['trunk'], array(
  365. 'term_trunk' => $terms['branch']->tid,
  366. 'merge_fields' => array(),
  367. 'term_branch_keep' => TRUE,
  368. 'keep_only_unique' => TRUE,
  369. ));
  370. $node = $this->drupalGetNodeByTitle($title, TRUE);
  371. $is_single = count($node->field_term_reference[LANGUAGE_NONE]) == 1;
  372. $is_expected_term = $node->field_term_reference[LANGUAGE_NONE][0]['tid'] == $terms['branch']->tid;
  373. $this->assertTrue($is_single && $is_expected_term, 'Only one term is kept in term reference field values if "Keep only unique" is on.');
  374. }
  375. /**
  376. * Test all cases for potentially "buggy" input.
  377. *
  378. * Test the functionality of the action "Term Merge" with various suspicious
  379. * input arguments, and testing the web UI of the module with suspicious
  380. * input.
  381. */
  382. public function testTermMergeResistance() {
  383. drupal_static_reset();
  384. // Trying to merge 2 terms from 2 different vocabularies.
  385. $this->drupalPost('admin/structure/taxonomy/add', array(
  386. 'name' => $this->randomName(),
  387. 'machine_name' => 'vocabulary2',
  388. ), 'Save');
  389. $terms = array(
  390. 'vocabulary' => FALSE,
  391. 'vocabulary2' => FALSE,
  392. );
  393. foreach ($terms as $term_type => $tmp) {
  394. $url = 'admin/structure/taxonomy/' . $term_type . '/add';
  395. $edit = array(
  396. 'name' => $this->randomName(),
  397. );
  398. $this->drupalPost($url, $edit, 'Save');
  399. $terms[$term_type] = $this->getLastTerm(taxonomy_vocabulary_machine_name_load($term_type));
  400. }
  401. actions_do('term_merge_action', $terms['vocabulary'], array(
  402. 'term_trunk' => $terms['vocabulary2']->tid,
  403. 'term_branch_keep' => FALSE,
  404. ));
  405. $this->termMergeResistanceAssert($terms, 'Testing merging 2 terms from 2 different vocabularies.');
  406. // Trying to merge a parent into its child.
  407. $terms = array(
  408. 'parent' => FALSE,
  409. 'child' => FALSE,
  410. );
  411. drupal_static_reset();
  412. foreach ($terms as $term_type => $tmp) {
  413. $url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add';
  414. $edit = array(
  415. 'name' => $this->randomName(),
  416. );
  417. if ($term_type == 'child') {
  418. $edit['parent[]'] = array($terms['parent']->tid);
  419. }
  420. $this->drupalPost($url, $edit, 'Save');
  421. $terms[$term_type] = $this->getLastTerm($this->vocabulary);
  422. }
  423. actions_do('term_merge_action', $terms['parent'], array(
  424. 'term_trunk' => $terms['child']->tid,
  425. 'term_branch_keep' => FALSE,
  426. ));
  427. $this->termMergeResistanceAssert($terms, 'Testing merging a parent into its child.');
  428. // Trying to merge a term into itself.
  429. $terms = array(
  430. 'single' => FALSE,
  431. );
  432. foreach ($terms as $term_type => $tmp) {
  433. $url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add';
  434. $name = $this->randomName();
  435. $edit = array(
  436. 'name' => $name,
  437. );
  438. $this->drupalPost($url, $edit, 'Save');
  439. $terms[$term_type] = $this->getLastTerm($this->vocabulary);
  440. }
  441. actions_do('term_merge_action', $terms['single'], array(
  442. 'term_trunk' => $terms['single']->tid,
  443. 'term_branch_keep' => FALSE,
  444. ));
  445. $this->termMergeResistanceAssert($terms, 'Testing merging a term into itself.');
  446. // Making sure the access rights are respected.
  447. $account = $this->drupalCreateUser(array('merge vocabulary2 terms'));
  448. $this->drupalLogin($account);
  449. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge');
  450. $this->assertResponse(403, 'Per vocabulary term merge permissions are respected in the module - an account cannot merge terms in the vocabulary in which he is not supposed to be able to merge.');
  451. $this->drupalGet('admin/structure/taxonomy/vocabulary2/merge');
  452. $this->assertResponse(200, 'Per vocabulary term merge permissions are respected in the module - an account can merge terms in the vocabulary in which he is supposed to be able to merge.');
  453. }
  454. /**
  455. * Test all cases of usage of Term Merge Batch.
  456. */
  457. public function testTermMergeBatch() {
  458. // Adding fields with unlimited cardinality to our vocabulary.
  459. $this->drupalPost('admin/structure/taxonomy/vocabulary/fields', array(
  460. 'fields[_add_new_field][label]' => 'Test Unlimited Text',
  461. 'fields[_add_new_field][field_name]' => 'test_text',
  462. 'fields[_add_new_field][type]' => 'text',
  463. 'fields[_add_new_field][widget_type]' => 'text_textfield',
  464. ), 'Save');
  465. $this->drupalPost(NULL, array(), 'Save field settings');
  466. $this->drupalPost(NULL, array(
  467. 'field[cardinality]' => FIELD_CARDINALITY_UNLIMITED,
  468. ), 'Save settings');
  469. // Additionally we need to create a new content type and assign term
  470. // reference field to this new content type.
  471. $this->drupalPost('admin/structure/types/add', array(
  472. 'name' => $this->randomName(),
  473. 'type' => 'term_merge_node',
  474. ), 'Save content type');
  475. $this->drupalPost('admin/structure/types/manage/term-merge-node/fields', array(
  476. 'fields[_add_new_field][label]' => 'Term Reference',
  477. 'fields[_add_new_field][field_name]' => 'term_reference',
  478. 'fields[_add_new_field][type]' => 'taxonomy_term_reference',
  479. 'fields[_add_new_field][widget_type]' => 'taxonomy_autocomplete',
  480. ), 'Save');
  481. $this->drupalPost(NULL, array(
  482. 'field[settings][allowed_values][0][vocabulary]' => $this->vocabulary->machine_name,
  483. ), 'Save field settings');
  484. $this->drupalPost(NULL, array(), 'Save settings');
  485. // Flushing fields API cache.
  486. _field_info_collate_fields(TRUE);
  487. // Array of cases for which we test the Term Merge batch.
  488. $cases = array(
  489. 'taxonomy_vocabulary_tab',
  490. 'taxonomy_term_tab',
  491. 'via_term_trunk_widget_select',
  492. 'via_term_trunk_widget_autocomplete',
  493. 'via_term_trunk_widget_autocomplete_without_tid',
  494. 'merge_fields',
  495. 'do_not_merge_fields',
  496. );
  497. foreach ($cases as $case) {
  498. // Creating a necessary set of terms in the vocabulary.
  499. drupal_static_reset();
  500. $terms = array(
  501. 'parent' => FALSE,
  502. 'another_parent' => FALSE,
  503. 'child' => FALSE,
  504. 'term1' => FALSE,
  505. 'term2' => FALSE,
  506. 'term3' => FALSE,
  507. 'term_trunk_parent' => FALSE,
  508. 'term_trunk' => FALSE,
  509. );
  510. foreach ($terms as $term_type => $tmp) {
  511. $url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add';
  512. $edit = array(
  513. 'name' => $term_type . '_' . $this->randomName(),
  514. 'field_test_text[' . LANGUAGE_NONE . '][0][value]' => $term_type,
  515. );
  516. switch ($term_type) {
  517. case 'child':
  518. $edit['parent[]'] = array($terms['parent']->tid, $terms['another_parent']->tid);
  519. break;
  520. case 'term_trunk':
  521. $edit['parent[]'] = array($terms['term_trunk_parent']->tid);
  522. break;
  523. }
  524. $this->drupalPost($url, $edit, 'Save');
  525. $terms[$term_type] = $this->getLastTerm($this->vocabulary);
  526. }
  527. // The initial URL from where the form that kicks off batch is submitted.
  528. $init_url = '';
  529. // What widget to use for choosing term trunk.
  530. $term_trunk_widget = '';
  531. // Value for term trunk in the format, expected by the widget
  532. // $term_trunk_widget. Additionally, if any test case requires any extra
  533. // fields to be submitted, input those fields into this array and they
  534. // won't be taken out from this array, then it will get merged into $edit,
  535. // and this way eventually your values will be successfully submitted.
  536. $term_trunk_edit = array();
  537. // Setting up controlling vars based on case and doing any specific
  538. // assertions for each case.
  539. switch ($case) {
  540. case 'taxonomy_vocabulary_tab':
  541. $init_url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge';
  542. // It doesn't really matter which widget we use, we test widgets
  543. // throughout in other cases.
  544. $term_trunk_widget = array_rand(drupal_map_assoc(array('select', 'autocomplete')));
  545. break;
  546. case 'taxonomy_term_tab':
  547. $init_url = 'taxonomy/term/' . $terms['parent']->tid . '/merge';
  548. // It doesn't really matter which widget we use, we test widgets
  549. // throughout in other cases.
  550. $term_trunk_widget = array_rand(drupal_map_assoc(array('select', 'autocomplete')));
  551. // Assert that the term, for which the tab was clicked, is selected as
  552. // term branch by default.
  553. $this->drupalGet($init_url);
  554. $this->assertOptionSelected('edit-term-branch', $terms['parent']->tid, 'Clicking the "Merge Terms" tab from a term view page sets the viewed term as a term branch by default.');
  555. break;
  556. case 'via_term_trunk_widget_select':
  557. $init_url = 'taxonomy/term/' . $terms['parent']->tid . '/merge';
  558. $term_trunk_widget = 'select';
  559. // Making sure for the term trunk select the selected term branch are
  560. // not available, nor their children.
  561. $this->drupalGet($init_url);
  562. $matches = array();
  563. preg_match('#\<select[^>]+name="term_trunk\[tid\]"[^>]*\>.+?\</select\>#si', $this->content, $matches);
  564. $term_trunk_options = $matches[0];
  565. $str_pos = strpos($term_trunk_options, $terms['child']->name);
  566. $this->assertIdentical(FALSE, $str_pos, 'Child is not available as option for term trunk if its parent is chosen among term branches.');
  567. $str_pos = strpos($term_trunk_options, $terms['parent']->name);
  568. $this->assertIdentical(FALSE, $str_pos, 'Selected branch term is not available as an option for term trunk.');
  569. break;
  570. case 'via_term_trunk_widget_autocomplete':
  571. $init_url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge';
  572. $term_trunk_widget = 'autocomplete';
  573. // Test autocomplete widget menu path to make sure it does reply
  574. // with valid suggestions.
  575. $response = $this->drupalGet('term-merge/autocomplete/term-trunk/' . $this->vocabulary->machine_name . '/' . drupal_strtoupper($terms['term_trunk']->name));
  576. $response = drupal_json_decode($response);
  577. $autocomplete_key = $terms['term_trunk']->name . ' (' . $terms['term_trunk']->tid . ')';
  578. $this->assertTrue(isset($response[$autocomplete_key]), 'Autocomplete menu path replies with valid suggestions for term trunk autocomplete widget.');
  579. // Making sure for the term trunk autocomplete widget doesn't allow to
  580. // submit any of the selected term branches nor their children.
  581. $prohibited_terms = array(
  582. 'parent' => 'Merging into the same term is not allowed in autocomplete widget for term trunk.',
  583. 'child' => 'Merging into any of child of selected branch terms is not allowed in autocomplete widget for term trunk.',
  584. );
  585. foreach ($prohibited_terms as $term => $assert_message) {
  586. $term = $terms[$term];
  587. $this->drupalGet($init_url);
  588. $this->drupalPostAJAX(NULL, array(
  589. 'term_branch[]' => array($terms['parent']->tid),
  590. 'term_trunk[widget]' => $term_trunk_widget,
  591. ), 'term_trunk[widget]');
  592. $this->drupalPost(NULL, array(
  593. 'term_branch[]' => array($terms['parent']->tid),
  594. 'term_trunk[widget]' => $term_trunk_widget,
  595. 'term_trunk[tid]' => $term->name . ' (' . $term->tid . ')',
  596. ), 'Submit');
  597. $this->assertText('Trunk term cannot be one of the selected branch terms or their children', $assert_message);
  598. }
  599. break;
  600. case 'via_term_trunk_widget_autocomplete_without_tid':
  601. $init_url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge';
  602. $term_trunk_widget = 'autocomplete';
  603. // Making sure for the term trunk autocomplete widget doesn't allow to
  604. // submit any of the selected term branches nor their children.
  605. $prohibited_terms = array(
  606. 'parent' => 'Merging into the same term is not allowed in autocomplete widget for term trunk.',
  607. 'child' => 'Merging into any of child of selected branch terms is not allowed in autocomplete widget for term trunk.',
  608. );
  609. foreach ($prohibited_terms as $term => $assert_message) {
  610. $term = $terms[$term];
  611. $this->drupalGet($init_url);
  612. $this->drupalPostAJAX(NULL, array(
  613. 'term_branch[]' => array($terms['parent']->tid),
  614. 'term_trunk[widget]' => $term_trunk_widget,
  615. ), 'term_trunk[widget]');
  616. $this->drupalPost(NULL, array(
  617. 'term_branch[]' => array($terms['parent']->tid),
  618. 'term_trunk[widget]' => $term_trunk_widget,
  619. 'term_trunk[tid]' => $term->name,
  620. ), 'Submit');
  621. $this->assertText('Trunk term cannot be one of the selected branch terms or their children', $assert_message);
  622. }
  623. break;
  624. case 'merge_fields':
  625. $init_url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge';
  626. // It doesn't really matter which widget we use, we test widgets
  627. // throughout in other cases.
  628. $term_trunk_widget = array_rand(drupal_map_assoc(array('select', 'autocomplete')));
  629. // We embed extra info related to field values merging into
  630. // $term_trunk_edit.
  631. $term_trunk_edit['merge_fields[field_test_text]'] = TRUE;
  632. break;
  633. case 'do_not_merge_fields':
  634. $init_url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge';
  635. // It doesn't really matter which widget we use, we test widgets
  636. // throughout in other cases.
  637. $term_trunk_widget = array_rand(drupal_map_assoc(array('select', 'autocomplete')));
  638. break;
  639. }
  640. // Creating a new node and setting its term reference field to point to
  641. // the term branch.
  642. $title = $this->randomName();
  643. $this->drupalPost('node/add/term-merge-node', array(
  644. 'title' => $title,
  645. 'field_term_reference[' . LANGUAGE_NONE . ']' => $terms['term1']->name,
  646. ), 'Save');
  647. $node = $this->drupalGetNodeByTitle($title, TRUE);
  648. // Calling the Term Merge form.
  649. $this->drupalGet($init_url);
  650. // Choosing term branches.
  651. $term_branches = array('term1', 'term2', 'term3');
  652. $term_branches_edit = array();
  653. foreach ($term_branches as $term_type) {
  654. $term_branches_edit[] = $terms[$term_type]->tid;
  655. }
  656. $this->drupalPostAJAX(NULL, array(
  657. 'term_branch[]' => $term_branches_edit,
  658. ), 'term_branch[]');
  659. // Choosing the widget for trunk term.
  660. $this->drupalPostAJAX(NULL, array(
  661. 'term_branch[]' => $term_branches_edit,
  662. 'term_trunk[widget]' => $term_trunk_widget,
  663. ), 'term_trunk[widget]');
  664. // Choosing term trunk.
  665. switch ($term_trunk_widget) {
  666. case 'select':
  667. $term_trunk_edit += array('term_trunk[tid]' => $terms['term_trunk']->tid);
  668. break;
  669. case 'autocomplete':
  670. $term_trunk_edit += array('term_trunk[tid]' => $terms['term_trunk']->name . ' (' . $terms['term_trunk']->tid . ')');
  671. break;
  672. }
  673. // Submitting the form.
  674. $edit = $term_trunk_edit + array(
  675. 'term_branch[]' => $term_branches_edit,
  676. 'term_trunk[widget]' => $term_trunk_widget,
  677. 'term_branch_keep' => FALSE,
  678. 'step' => 2,
  679. );
  680. $this->drupalPost(NULL, $edit, 'Submit');
  681. $this->drupalPost(NULL, array(), 'Confirm');
  682. // Making sure all the branches are deleted.
  683. foreach ($term_branches as $term_type) {
  684. $term = $terms[$term_type];
  685. $this->drupalGet('taxonomy/term/' . $term->tid);
  686. $this->assertResponse(404, 'Branch term ' . $term_type . ' has been deleted after merging.');
  687. }
  688. $text_assertions = array();
  689. $term_trunk = $terms['term_trunk'];
  690. // Adding any extra text assertions on per test-case basis.
  691. switch ($case) {
  692. case 'merge_fields':
  693. // Making sure the term trunk has been merged all the fields from term
  694. // branches into itself.
  695. foreach ($term_branches as $term_type) {
  696. $items = field_get_items('taxonomy_term', $terms[$term_type], 'field_test_text');
  697. foreach ($items as $delta => $item) {
  698. $text_assertions[$term_type . ' text field delta#' . $delta . ' has been merged when instructed to merge field values.'] = $item['value'];
  699. }
  700. }
  701. break;
  702. case 'do_not_merge_fields':
  703. // We need to assert that no values for field have been merged from
  704. // branch terms into the values of trunk term.
  705. $this->drupalGet('taxonomy/term/' . $term_trunk->tid);
  706. foreach ($term_branches as $term_type) {
  707. $items = field_get_items('taxonomy_term', $terms[$term_type], 'field_test_text');
  708. foreach ($items as $delta => $item) {
  709. $this->assertNoText($item['value'], $term_type . ' text field delta#' . $delta . ' has not been merged when instrcuted not to merge field values.');
  710. }
  711. }
  712. break;
  713. }
  714. $this->drupalGet('taxonomy/term/' . $term_trunk->tid);
  715. foreach ($text_assertions as $k => $v) {
  716. $this->assertText($v, 'Term trunk has the property ' . $k);
  717. }
  718. // Making sure the taxonomy term reference in other entities are updated
  719. // to point from term branches to the just created term trunk.
  720. $this->drupalGet('node/' . $node->nid);
  721. $this->assertText($term_trunk->name, 'Taxonomy term reference fields in other entities are updated to point from term branches to the term trunk.');
  722. }
  723. }
  724. /**
  725. * Supportive function for the main test "testTermMergeResistance".
  726. *
  727. * Assert that each term of the array $terms is available.
  728. *
  729. * @param array $terms
  730. * Array of taxonomy terms objects
  731. * @param string $message
  732. * Assertion message to be shown on the test results page
  733. */
  734. protected function termMergeResistanceAssert($terms, $message) {
  735. foreach ($terms as $term) {
  736. $this->drupalGet('taxonomy/term/' . $term->tid);
  737. $this->assertResponse(200, $message);
  738. }
  739. }
  740. }
  741. /**
  742. * Test the Merge Duplicate Terms feature of the Term Merge module.
  743. */
  744. class DuplicatesTermMergeWebTestCase extends TermMergeWebTestCase {
  745. /**
  746. * GetInfo method.
  747. */
  748. public static function getInfo() {
  749. return array(
  750. 'name' => 'Duplicate terms merge',
  751. 'description' => 'Ensure that the feature <i>merge duplicate terms</i> of module Term Merge works correctly.',
  752. 'group' => 'Term Merge',
  753. );
  754. }
  755. /**
  756. * Test access rights.
  757. */
  758. public function testDisabledAndPermissions() {
  759. // Trying a user who doesn't have enough permissions.
  760. $account = $this->drupalCreateUser();
  761. $this->drupalLogin($account);
  762. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates');
  763. $this->assertResponse(403, 'Access to Merge Duplicate Terms is denied for a user who does not have enough permissions.');
  764. // Trying a user who have enough permissions.
  765. $this->drupalLogin($this->admin);
  766. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates');
  767. $this->assertResponse(200, 'Access to Merge Duplicate Terms is granted for a user who has enough permissions.');
  768. }
  769. /**
  770. * Test merging duplicates feature of Term Merge module.
  771. *
  772. * Test the following features:
  773. * - Correctness of merging a group of duplicate terms, namely:
  774. * - Correctness of merge operation when duplicates feature is invoked on
  775. * the entire vocabulary
  776. * - Correctness of merge operation when duplicates feature is invoked on a
  777. * term (merge its children one into another)
  778. * - Correctness of the mechanism that groups terms into sets of duplicate
  779. * entries, namely:
  780. * - Correctness of grouping by term name, i.e. unique terms should not be
  781. * listed in any set of duplicate terms
  782. * - Correctness of the initial set of terms, on which the duplicate tool is
  783. * invoked, i.e. when invoked on a vocabulary, we search for duplicates
  784. * in the whole vocabulary, but when invoked on a term, the tool should
  785. * only search for duplicate among the children of that term
  786. */
  787. public function testDuplicates() {
  788. // Creating duplicate terms firstly.
  789. $groups = array(
  790. 'single' => 1,
  791. 'triple_different_parent' => 3,
  792. 'random' => rand(2, 5),
  793. // We need some term, that will be a parent of some other terms.
  794. 'parent' => 1,
  795. );
  796. $groups = $this->createTerms($groups);
  797. // Let us make two of 'triple_different_parent' terms children of 'parent'
  798. // term.
  799. $groups['triple_different_parent'][1]->parent = $groups['parent'][0]->tid;
  800. taxonomy_term_save($groups['triple_different_parent'][1]);
  801. $groups['triple_different_parent'][2]->parent = $groups['parent'][0]->tid;
  802. taxonomy_term_save($groups['triple_different_parent'][2]);
  803. // Test duplicate suggestion plugin type. Make sure multiple duplicated
  804. // suggestions are properly handed and make sure each of the duplicate
  805. // suggestions does its function.
  806. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates');
  807. $this->assertSuggestedDuplicates(array_merge($groups['triple_different_parent'], $groups['random']), 'Filtering only by term names yields expected results.');
  808. $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates', array(
  809. 'settings[duplicate_suggestion][name]' => FALSE,
  810. 'settings[duplicate_suggestion][description]' => TRUE,
  811. ), 'Re-run duplicate search');
  812. $this->assertSuggestedDuplicates(array_merge($groups['triple_different_parent'], $groups['random']), 'Filtering only by term description yields expected results.');
  813. $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates', array(
  814. 'settings[duplicate_suggestion][name]' => FALSE,
  815. 'settings[duplicate_suggestion][parent]' => TRUE,
  816. ), 'Re-run duplicate search');
  817. $expected_terms = array();
  818. $expected_terms = array_merge($expected_terms, $groups['single'], $groups['random'], $groups['parent']);
  819. $expected_terms[] = $groups['triple_different_parent'][0];
  820. $this->assertSuggestedDuplicates($expected_terms, 'Filtering only by term parent yields expected results.');
  821. $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates', array(
  822. 'settings[duplicate_suggestion][name]' => TRUE,
  823. 'settings[duplicate_suggestion][parent]' => TRUE,
  824. ), 'Re-run duplicate search');
  825. $expected_terms = $groups['triple_different_parent'];
  826. unset($expected_terms[0]);
  827. $this->assertSuggestedDuplicates($expected_terms, 'Filtering by term name and parent yields expected results, i.e. duplicate suggestions can be combined.');
  828. // Assuring the single term is not listed as duplicate.
  829. $this->drupaLGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates');
  830. $this->assertNoText($groups['single'][0]->name, 'Single term is not listed as a duplicate.');
  831. // Making sure the term in 'triple_different_parent' that does not have a
  832. // parent, is not listed when we invoke duplicate tool on a parent term.
  833. $this->drupalGet('taxonomy/term/' . $groups['parent'][0]->tid . '/merge/duplicates');
  834. $this->assertNoFieldByName('group[' . $this->duplicateHashTerm($groups['triple_different_parent'][0]) . '][duplicates][' . $groups['triple_different_parent'][0]->tid . ']', 'Duplicate term is not listed when it is not among children of a term, on which Term Merge module was invoked.');
  835. $edit = array();
  836. // Trying to merge a term into another, invoking Duplicate tool on a parent
  837. // term of both. Important note: we do not test merging options, because
  838. // supposedly those are tested in the main test of this module.
  839. $edit['group[' . $this->duplicateHashTerm($groups['triple_different_parent'][1]) . '][trunk_tid]'] = $groups['triple_different_parent'][1]->tid;
  840. $edit['group[' . $this->duplicateHashTerm($groups['triple_different_parent'][2]) . '][duplicates][' . $groups['triple_different_parent'][2]->tid . ']'] = TRUE;
  841. $groups['triple_different_parent'][2]->merged = TRUE;
  842. $this->drupalPost('taxonomy/term/' . $groups['parent'][0]->tid . '/merge/duplicates', $edit, 'Submit');
  843. // Trying to merge multiple terms. We merge all but the 1st term.
  844. $edit = array();
  845. $edit['group[' . $this->duplicateHashTerm($groups['random'][0]) . '][trunk_tid]'] = $groups['random'][0]->tid;
  846. foreach ($groups['random'] as $k => $term) {
  847. if ($k != 0) {
  848. $edit['group[' . $this->duplicateHashTerm($groups['random'][$k]) . '][duplicates][' . $term->tid . ']'] = TRUE;
  849. $groups['random'][$k]->merged = TRUE;
  850. }
  851. }
  852. $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates', $edit, 'Submit');
  853. // Asserting results of merging.
  854. foreach ($groups as $group) {
  855. foreach ($group as $term) {
  856. $this->drupalGet('taxonomy/term/' . $term->tid);
  857. $code = isset($term->merged) && $term->merged ? 404 : 200;
  858. $message = isset($term->merged) && $term->merged ? 'Term #' . $term->tid . ' has been successfully merged.' : 'Term #' . $term->tid . ' has been successfully untouched during merging.';
  859. $this->assertResponse($code, $message);
  860. }
  861. }
  862. }
  863. /**
  864. * Supportive method.
  865. *
  866. * Create taxonomy terms with similar names.
  867. *
  868. * @param array $groups
  869. * Key should be a name of the group (terms' names in this group may only
  870. * differ in case, but will always use this string as their names), while
  871. * corresponding value to that key should denote how many terms in each
  872. * group should be created
  873. *
  874. * @return array
  875. * Array of fully loaded taxonomy terms objects of the just created terms,
  876. * grouped by their group name
  877. */
  878. protected function createTerms($groups) {
  879. foreach ($groups as $name => $quantity) {
  880. $groups[$name] = array();
  881. $description = $this->randomName();
  882. for ($i = 0; $i < $quantity; $i++) {
  883. $term_name = '';
  884. $term_description = '';
  885. // Randomizing case of the group name.
  886. foreach (str_split($name) as $symbol) {
  887. $symbol = rand(0, 1) ? drupal_strtoupper($symbol) : drupal_strtolower($symbol);
  888. $term_name .= $symbol;
  889. }
  890. // Getting description in different cases.
  891. foreach (str_split($description) as $symbol) {
  892. $symbol = rand(0, 1) ? drupal_strtoupper($symbol) : drupal_strtolower($symbol);
  893. $term_description .= $symbol;
  894. }
  895. $term = (object) array(
  896. 'vid' => $this->vocabulary->vid,
  897. 'name' => $term_name,
  898. 'description' => $description,
  899. );
  900. taxonomy_term_save($term);
  901. $groups[$name][] = $this->getLastTerm($this->vocabulary);
  902. }
  903. }
  904. return $groups;
  905. }
  906. /**
  907. * Supportive method.
  908. *
  909. * Calculate hash a term based on which it will be paired with other terms as
  910. * possible duplicates of each other.
  911. *
  912. * @param object $term
  913. * Term whose duplicate suggestion hash is to be calculated
  914. * @param array $duplicate_suggestions
  915. * Array of duplicate suggestion names that to apply, when determining hash
  916. * of the provided term
  917. *
  918. * @return string
  919. * Hash of the provided term according to enabled duplicate suggestions
  920. */
  921. protected function duplicateHashTerm($term, $duplicate_suggestions = array('name')) {
  922. $hash = '';
  923. foreach ($duplicate_suggestions as $duplicate_suggestion) {
  924. $hash_chunk = '';
  925. switch ($duplicate_suggestion) {
  926. case 'name':
  927. $hash_chunk = drupal_strtoupper($term->name);
  928. // Trying transliteration, if available.
  929. if (module_exists('transliteration')) {
  930. $hash_chunk = transliteration_get($hash_chunk);
  931. // Keeping only ASCII chars.
  932. $hash_chunk = preg_replace('#\W#', '', $hash_chunk);
  933. }
  934. break;
  935. case 'description':
  936. $hash_chunk = drupal_strtoupper($term->description);
  937. break;
  938. case 'parent':
  939. $hash_chunk = $term->parents[0];
  940. break;
  941. }
  942. $hash .= $hash_chunk;
  943. }
  944. return $hash;
  945. }
  946. /**
  947. * Assert expected terms indeed are suggested as duplicates.
  948. *
  949. * @param array $expected_terms
  950. * Array of terms that are expected to be suggested as duplicates
  951. * @param string $message
  952. * Assertion message to display on the test results
  953. */
  954. protected function assertSuggestedDuplicates($expected_terms, $message = '') {
  955. $i = 0;
  956. foreach ($expected_terms as $term) {
  957. $this->assertPattern('#\<input\s+[^>]*type="checkbox"\s+[^>]*name="[^"]+\[duplicates]\[' . $term->tid . '\]"#si', $message . ' (for term #' . $i . ')');
  958. $i++;
  959. }
  960. }
  961. }
  962. /**
  963. * Test the integration between Term Merge module and Path/Redirect modules.
  964. */
  965. class RedirectTermMergeWebTestCase extends TermMergeWebTestCase {
  966. /**
  967. * Fully loaded Drupal user object of the user who has access to configure
  968. * redirects.
  969. *
  970. * @var object
  971. */
  972. protected $superAdmin;
  973. /**
  974. * SetUp method.
  975. */
  976. public function setUp() {
  977. $modules = $this->normalizeSetUpArguments(func_get_args());
  978. $modules[] = 'redirect';
  979. $modules[] = 'path';
  980. parent::setUp($modules);
  981. $this->superAdmin = $this->drupalCreateUser(array(
  982. 'administer taxonomy',
  983. 'merge terms',
  984. 'administer content types',
  985. 'bypass node access',
  986. 'administer redirects',
  987. 'administer url aliases',
  988. ));
  989. }
  990. /**
  991. * GetInfo method.
  992. */
  993. public static function getInfo() {
  994. return array(
  995. 'name' => 'Redirect module integration',
  996. 'description' => 'Ensure that the module Term Merge integrates with ' . l('Redirect', 'http://drupal.org/project/redirect') . '/Path modules correctly.',
  997. 'group' => 'Term Merge',
  998. );
  999. }
  1000. /**
  1001. * Test disabled Redirect module and access rights.
  1002. */
  1003. public function testDisabledAndPermissions() {
  1004. // Checking access rights required to set up redirection during term
  1005. // merging.
  1006. $this->drupalLogin($this->admin);
  1007. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge');
  1008. $this->assertNoPattern('#\<select[^>]+name="redirect"[^>]*\>#i', 'No redirection settings are available for a user that does not possess corresponding permissions.');
  1009. $this->drupalLogin($this->superAdmin);
  1010. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge');
  1011. $this->assertPattern('#\<select[^>]+name="redirect"[^>]*\>#i', 'Redirection settings are available for a user that possesses corresponding permissions.');
  1012. // Making sure redirect settings are not available during merging when
  1013. // merging with disabled Redirect module.
  1014. module_disable(array('redirect'));
  1015. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge');
  1016. $this->assertNoPattern('#\<select[^>]+name="redirect"[^>]*\>#i', 'No redirection settings are available when the redirect module is disabled.');
  1017. }
  1018. /**
  1019. * Test the action 'term_merge_action' in terms of integration with Redirect.
  1020. */
  1021. public function testTermMergeAction() {
  1022. $this->drupalLogin($this->superAdmin);
  1023. $terms = $this->createTerms(array('branch', 'trunk'));
  1024. // Testing default value.
  1025. actions_do('term_merge_action', $terms['branch'], array(
  1026. 'term_trunk' => $terms['trunk']->tid,
  1027. 'term_branch_keep' => TRUE,
  1028. ));
  1029. $this->assertRedirectIntegration($terms, 'By default no redirects should be made.');
  1030. // Testing no redirection.
  1031. actions_do('term_merge_action', $terms['branch'], array(
  1032. 'term_trunk' => $terms['trunk']->tid,
  1033. 'term_branch_keep' => TRUE,
  1034. 'redirect' => TERM_MERGE_NO_REDIRECT,
  1035. ));
  1036. $this->assertRedirectIntegration($terms, 'No redirects are made, if action is not instructed to make ones.');
  1037. // Testing 301 redirection. Besides redirecting 'taxonomy/term/[branch-tid]'
  1038. // to 'taxonomy/term/[trunk-tid]' and their path aliases we want to
  1039. // additionally assert that all existing redirects to branch term will be
  1040. // replaced with redirects to trunk term in Redirect module. Lastly, we also
  1041. // assert that 'taxonomy/term/[branch-tid]/feed' path and all pointing there
  1042. // redirects now point to 'taxonomy/term/[trunk-tid]/feed.
  1043. $redirect_source = $this->randomName();
  1044. $redirect = new stdClass();
  1045. redirect_object_prepare($redirect, array(
  1046. 'source' => $redirect_source,
  1047. 'redirect' => 'taxonomy/term/' . $terms['branch']->tid,
  1048. ));
  1049. redirect_hash($redirect);
  1050. redirect_save($redirect);
  1051. $redirect = new stdClass();
  1052. redirect_object_prepare($redirect, array(
  1053. 'source' => $redirect_source . '/feed',
  1054. 'redirect' => 'taxonomy/term/' . $terms['branch']->tid . '/feed',
  1055. ));
  1056. redirect_hash($redirect);
  1057. redirect_save($redirect);
  1058. actions_do('term_merge_action', $terms['branch'], array(
  1059. 'term_trunk' => $terms['trunk']->tid,
  1060. 'redirect' => 301,
  1061. ));
  1062. $terms['branch']->redirect = $terms['trunk'];
  1063. $this->assertRedirectIntegration($terms, 'Redirects are made, if action is instructed to make ones.');
  1064. $this->drupalGet($redirect_source);
  1065. $this->assertUrl('taxonomy/term/' . $terms['trunk']->tid, array(), 'Redirect pointing to <em>taxonomy/term/[branch-tid]</em> now points to <em>taxonomy/term/[trunk-tid]</em>.');
  1066. $this->drupalGet($redirect_source . '/feed');
  1067. $this->assertUrl('taxonomy/term/' . $terms['trunk']->tid . '/feed', array(), 'Redirect pointing to <em>taxonomy/term/[branch-tid]/feed</em> now points to <em>taxonomy/term/[trunk-tid]/feed</em>.');
  1068. }
  1069. /**
  1070. * Test Term Merge batch in terms of integration with Redirect/Path modules.
  1071. */
  1072. public function testTermMergeBatch() {
  1073. $this->drupalLogin($this->superAdmin);
  1074. // Trying to merge without redirection.
  1075. $terms = $this->createTerms(array('branch', 'trunk'));
  1076. $this->drupalPost('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
  1077. 'term_branch[]' => array($terms['branch']->tid),
  1078. 'term_trunk[widget]' => 'select',
  1079. 'term_trunk[tid]' => $terms['trunk']->tid,
  1080. 'term_branch_keep' => TRUE,
  1081. 'redirect' => TERM_MERGE_NO_REDIRECT,
  1082. ), 'Submit');
  1083. $this->drupalPost(NULL, array(), 'Confirm');
  1084. $this->assertRedirectIntegration($terms, 'No redirection made after running merge batch when not instructed to make redirection.');
  1085. // Trying to merge into a term with redirection.
  1086. $this->drupalPost('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
  1087. 'term_branch[]' => array($terms['branch']->tid),
  1088. 'term_trunk[widget]' => 'select',
  1089. 'term_trunk[tid]' => $terms['trunk']->tid,
  1090. 'redirect' => 0,
  1091. ), 'Submit');
  1092. $terms['branch']->redirect = $terms['trunk'];
  1093. $this->drupalPost(NULL, array(), 'Confirm');
  1094. $this->assertRedirectIntegration($terms, 'Redirection is made after running merge batch merging into an existing term, when instructed to make redirection.');
  1095. }
  1096. /**
  1097. * Supportive method.
  1098. *
  1099. * Assert expected results after doing any test actions.
  1100. *
  1101. * @param array $terms
  1102. * Array of terms as returned by $this->createTerms(). Those terms that have
  1103. * been merged and redirected to another terms, besides all normal keys
  1104. * should have property 'redirect' which should be equal to the fully loaded
  1105. * taxonomy term which they were redirected to
  1106. * @param string $message
  1107. * Assert message to be shown on test results page
  1108. */
  1109. protected function assertRedirectIntegration($terms, $message) {
  1110. foreach ($terms as $term) {
  1111. if (isset($term->redirect)) {
  1112. $sources = array('taxonomy/term/' . $term->tid);
  1113. // Additionally checking path alias.
  1114. if (!in_array(drupal_get_path_alias($sources[0]), $sources)) {
  1115. $sources[] = drupal_get_path_alias($sources[0]);
  1116. }
  1117. foreach ($sources as $source) {
  1118. $this->drupalGet($source);
  1119. $this->assertUrl('taxonomy/term/' . $term->redirect->tid, array(), $message);
  1120. }
  1121. // Additionally assert the 'taxonomy/term/*/feed' path.
  1122. $sources = array('taxonomy/term/' . $term->tid . '/feed');
  1123. if (!in_array(drupal_get_path_alias($sources[0]), $sources)) {
  1124. $sources[] = drupal_get_path_alias($sources[0]);
  1125. }
  1126. foreach ($sources as $source) {
  1127. $this->drupalGet($source);
  1128. $this->assertUrl('taxonomy/term/' . $term->redirect->tid . '/feed', array(), $message);
  1129. }
  1130. }
  1131. }
  1132. }
  1133. /**
  1134. * Supportive method.
  1135. *
  1136. * Create a list of terms, assigning path aliases according to the values
  1137. * of the supplied array.
  1138. *
  1139. * @param array $terms
  1140. * Array of machine readable term keys based on which is generated output
  1141. *
  1142. * @return array
  1143. * Array of taxonomy term objects path alias of which is equal to the value
  1144. * that corresponds to its position in the supplied array
  1145. */
  1146. protected function createTerms($terms) {
  1147. $return = array();
  1148. foreach ($terms as $v) {
  1149. $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add', array(
  1150. 'name' => $this->randomName(),
  1151. 'path[alias]' => $v . $this->randomName(),
  1152. ), 'Save');
  1153. $return[$v] = $this->getLastTerm($this->vocabulary);
  1154. }
  1155. return $return;
  1156. }
  1157. }
  1158. /**
  1159. * Test the integration between Term Merge module and Synonyms module.
  1160. */
  1161. class SynonymsTermMergeWebTestCase extends TermMergeWebTestCase {
  1162. /**
  1163. * Field definition array within which the testing will happen.
  1164. *
  1165. * @var array
  1166. */
  1167. protected $field = array(
  1168. 'field_name' => 'term_merge_synonyms_test',
  1169. 'type' => 'text',
  1170. );
  1171. /**
  1172. * Synonyms behavior implementation that undergoes testing.
  1173. *
  1174. * @var array
  1175. */
  1176. protected $behavior_implementation;
  1177. /**
  1178. * SetUp method.
  1179. */
  1180. public function setUp() {
  1181. $modules = $this->normalizeSetUpArguments(func_get_args());
  1182. $modules[] = 'synonyms';
  1183. $modules[] = 'synonyms_provider_field';
  1184. parent::setUp($modules);
  1185. // Additionally we enable default synonyms field in the vocabulary.
  1186. $this->field = field_create_field($this->field);
  1187. $instance = array(
  1188. 'field_name' => $this->field['field_name'],
  1189. 'label' => 'Testing term merge synonyms integration',
  1190. 'entity_type' => 'taxonomy_term',
  1191. 'bundle' => $this->vocabulary->machine_name,
  1192. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  1193. );
  1194. $instance = field_create_instance($instance);
  1195. $instance = field_info_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
  1196. $this->behavior_implementation = array(
  1197. 'entity_type' => $instance['entity_type'],
  1198. 'bundle' => $instance['bundle'],
  1199. 'provider' => synonyms_provider_field_provider_name($this->field),
  1200. 'behavior' => 'term_merge',
  1201. 'settings' => array(),
  1202. );
  1203. synonyms_behavior_implementation_save($this->behavior_implementation);
  1204. foreach (synonyms_behavior_get($this->behavior_implementation['behavior'], $this->behavior_implementation['entity_type'], $this->behavior_implementation['bundle'], TRUE) as $behavior_implementation) {
  1205. if ($behavior_implementation['provider'] == $this->behavior_implementation['provider']) {
  1206. $this->behavior_implementation = $behavior_implementation;
  1207. break;
  1208. }
  1209. }
  1210. }
  1211. /**
  1212. * GetInfo method.
  1213. */
  1214. public static function getInfo() {
  1215. return array(
  1216. 'name' => 'Synonyms module integration',
  1217. 'description' => 'Ensure that the module Term Merge integrates with ' . l('Synonyms', 'http://drupal.org/project/synonyms') . ' module correctly.',
  1218. 'group' => 'Term Merge',
  1219. );
  1220. }
  1221. /**
  1222. * Test disabled Synonyms module.
  1223. */
  1224. public function testDisabled() {
  1225. // Making sure synonyms settings are not available during merging when
  1226. // Synonyms module is disabled.
  1227. module_disable(array('synonyms'));
  1228. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge');
  1229. $this->assertNoText(t('Add as Synonyms'), 'No synonyms settings are available when the Synonyms module is disabled.');
  1230. }
  1231. /**
  1232. * Test the action 'term_merge_action' in terms of integration with Synonyms.
  1233. */
  1234. public function testTermMergeAction() {
  1235. $this->drupalLogin($this->admin);
  1236. $terms = $this->createTerms(array('branch', 'trunk'));
  1237. // Testing default value.
  1238. actions_do('term_merge_action', $terms['branch'], array(
  1239. 'term_trunk' => $terms['trunk']->tid,
  1240. 'term_branch_keep' => TRUE,
  1241. ));
  1242. $this->assertSynonymsIntegration($terms, 'By default no synonyms should be added.');
  1243. // Testing no synonyms adding.
  1244. actions_do('term_merge_action', $terms['branch'], array(
  1245. 'term_trunk' => $terms['trunk']->tid,
  1246. 'term_branch_keep' => TRUE,
  1247. 'synonyms' => NULL,
  1248. ));
  1249. $this->assertSynonymsIntegration($terms, 'No synonyms are added, if action is not instructed to make ones.');
  1250. // Testing adding as a synonym.
  1251. actions_do('term_merge_action', $terms['branch'], array(
  1252. 'term_trunk' => $terms['trunk']->tid,
  1253. 'synonyms' => $this->behavior_implementation['provider'],
  1254. ));
  1255. $terms['trunk']->synonyms = array($terms['branch']->name);
  1256. $this->assertSynonymsIntegration($terms, 'Synonyms are added, if action is instructed to add ones.');
  1257. }
  1258. /**
  1259. * Test Term Merge batch in terms of integration with Synonyms module.
  1260. */
  1261. public function testTermMergeBatch() {
  1262. // Trying to merge without synonyms adding.
  1263. $terms = $this->createTerms(array('branch', 'trunk'));
  1264. $this->drupalPost('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
  1265. 'term_branch[]' => array($terms['branch']->tid),
  1266. 'term_trunk[widget]' => 'select',
  1267. 'term_trunk[tid]' => $terms['trunk']->tid,
  1268. 'term_branch_keep' => TRUE,
  1269. 'synonyms' => '',
  1270. ), 'Submit');
  1271. $this->drupalPost(NULL, array(), 'Confirm');
  1272. $this->assertSynonymsIntegration($terms, 'No synonyms are added after running merge batch when not instructed to add synonyms.');
  1273. // Trying to merge into a term with synonyms adding.
  1274. $this->drupalPost('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
  1275. 'term_branch[]' => array($terms['branch']->tid),
  1276. 'term_trunk[widget]' => 'select',
  1277. 'term_trunk[tid]' => $terms['trunk']->tid,
  1278. 'term_branch_keep' => TRUE,
  1279. 'synonyms' => $this->behavior_implementation['provider'],
  1280. ), 'Submit');
  1281. $terms['trunk']->synonyms = array($terms['branch']->name);
  1282. $this->drupalPost(NULL, array(), 'Confirm');
  1283. $this->assertSynonymsIntegration($terms, 'Synonyms are added after running merge batch merging into an existing term, when instructed to add synonyms.');
  1284. }
  1285. /**
  1286. * Supportive method.
  1287. *
  1288. * Assert expected results after doing any test actions.
  1289. *
  1290. * @param array $terms
  1291. * Array of terms as returned by $this->createTerms(). Those term trunks
  1292. * that have merged any branch terms with "Synonyms" option on, besides all
  1293. * normal keys should have property 'synonyms' which should be an array of
  1294. * expected synonyms of this term
  1295. * @param string $message
  1296. * Assert message to be shown on test results page
  1297. */
  1298. protected function assertSynonymsIntegration($terms, $message) {
  1299. foreach ($terms as $term) {
  1300. // Getting an array of synonyms according to Synonyms module.
  1301. $context = array();
  1302. $synonyms = synonyms_get_raw(entity_load_unchanged('taxonomy_term', $term->tid), array(), 'synonyms', 'taxonomy_term', $context);
  1303. $expected_synonyms = isset($term->synonyms) ? $term->synonyms : array();
  1304. // Comparing $synonyms to $expected_synonyms.
  1305. if (count($expected_synonyms) != count(array_intersect($expected_synonyms, $synonyms))) {
  1306. $this->fail($message);
  1307. return;
  1308. }
  1309. }
  1310. // If we got here, then all expected synonyms were found.
  1311. $this->pass($message);
  1312. }
  1313. /**
  1314. * Supportive method.
  1315. *
  1316. * Create a list of terms, assigning names according to the values of the
  1317. * supplied array.
  1318. *
  1319. * @param array $terms
  1320. * Array of machine readable term keys based on which is generated output
  1321. *
  1322. * @return array
  1323. * Array of taxonomy term objects name of which is equal to the value that
  1324. * corresponds to its position in the supplied array
  1325. */
  1326. protected function createTerms($terms) {
  1327. $return = array();
  1328. foreach ($terms as $v) {
  1329. $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add', array(
  1330. 'name' => $v,
  1331. ), 'Save');
  1332. $return[$v] = $this->getLastTerm($this->vocabulary);
  1333. }
  1334. return $return;
  1335. }
  1336. }
  1337. /**
  1338. * Test the integration between Term Merge module and Views module.
  1339. */
  1340. class ViewsTermMergeWebTestCase extends TermMergeWebTestCase {
  1341. /**
  1342. * View object on which all tests happen.
  1343. *
  1344. * @var view
  1345. */
  1346. protected $view;
  1347. /**
  1348. * SetUp method.
  1349. */
  1350. public function setUp() {
  1351. $modules = $this->normalizeSetUpArguments(func_get_args());
  1352. $modules[] = 'views';
  1353. parent::setUp($modules);
  1354. // Additionally we create a view.
  1355. $view = views_new_view();
  1356. $view->name = 'term_merge_view_test';
  1357. $view->description = 'Test view to test Term Merge module.';
  1358. $view->tag = '';
  1359. $view->base_table = 'node';
  1360. $view->api_version = '3.0';
  1361. $view->core = 7;
  1362. $display_id = 'default';
  1363. $view->set_display($display_id);
  1364. views_save_view($view);
  1365. $this->view = &$view;
  1366. }
  1367. /**
  1368. * GetInfo method.
  1369. */
  1370. public static function getInfo() {
  1371. return array(
  1372. 'name' => 'Views module integration',
  1373. 'description' => 'Ensure that the module Term Merge integrates with ' . l('Views', 'http://drupal.org/project/views') . ' module correctly.',
  1374. 'group' => 'Term Merge',
  1375. );
  1376. }
  1377. /**
  1378. * Test integration with Views Taxonomy Term reference filter.
  1379. */
  1380. public function testTermReferenceFieldFilter() {
  1381. // We need to create a content type and attach a term reference field to
  1382. // that bundle in order to have some term reference filter available in
  1383. // Views.
  1384. $this->drupalPost('admin/structure/types/add', array(
  1385. 'name' => $this->randomName(),
  1386. 'type' => 'term_merge_node',
  1387. ), 'Save content type');
  1388. $field_name = 'term_reference';
  1389. $this->drupalPost('admin/structure/types/manage/term-merge-node/fields', array(
  1390. 'fields[_add_new_field][label]' => 'Term Reference',
  1391. 'fields[_add_new_field][field_name]' => $field_name,
  1392. 'fields[_add_new_field][type]' => 'taxonomy_term_reference',
  1393. 'fields[_add_new_field][widget_type]' => 'taxonomy_autocomplete',
  1394. ), 'Save');
  1395. $field_name = 'field_' . $field_name;
  1396. $this->drupalPost(NULL, array(
  1397. 'field[settings][allowed_values][0][vocabulary]' => $this->vocabulary->machine_name,
  1398. ), 'Save field settings');
  1399. $this->drupalPost(NULL, array(
  1400. 'field[cardinality]' => FIELD_CARDINALITY_UNLIMITED,
  1401. ), 'Save settings');
  1402. // Flushing fields API cache.
  1403. _field_info_collate_fields(TRUE);
  1404. // Loading field definition array of the term reference field we just
  1405. // created.
  1406. $field = field_info_field($field_name);
  1407. // Creating terms to have stuff to work with.
  1408. $terms = array(
  1409. 'branch' => FALSE,
  1410. 'trunk' => FALSE,
  1411. );
  1412. foreach ($terms as $term_type => $tmp) {
  1413. $url = 'admin/structure/taxonomy/vocabulary/add';
  1414. $name = $this->randomName();
  1415. $edit = array(
  1416. 'name' => $name,
  1417. );
  1418. $this->drupalPost($url, $edit, 'Save');
  1419. $terms[$term_type] = $this->getLastTerm($this->vocabulary);
  1420. }
  1421. // Adding a taxonomy term reference filter to the view.
  1422. $this->view->set_display('default');
  1423. // We use Field API info to look up necessary tables and columns.
  1424. $table = array_keys($field['storage']['details']['sql']['FIELD_LOAD_CURRENT']);
  1425. $table = reset($table);
  1426. $columns = $field['storage']['details']['sql']['FIELD_LOAD_CURRENT'][$table];
  1427. $this->view->display_handler->display->display_options['filters'][$columns['tid']]['id'] = $columns['tid'];
  1428. $this->view->display_handler->display->display_options['filters'][$columns['tid']]['table'] = $table;
  1429. $this->view->display_handler->display->display_options['filters'][$columns['tid']]['field'] = $columns['tid'];
  1430. $this->view->display_handler->display->display_options['filters'][$columns['tid']]['value'] = array(
  1431. $terms['branch']->tid => $terms['branch']->tid,
  1432. );
  1433. $this->view->display_handler->display->display_options['filters'][$columns['tid']]['type'] = 'select';
  1434. $this->view->display_handler->display->display_options['filters'][$columns['tid']]['vocabulary'] = $this->vocabulary->machine_name;
  1435. $this->view->display_handler->display->display_options['filters'][$columns['tid']]['hierarchy'] = 1;
  1436. views_save_view($this->view);
  1437. // After such merge we expect the view's filter to be changed from branch
  1438. // term to trunk term.
  1439. actions_do('term_merge_action', $terms['branch'], array(
  1440. 'term_trunk' => $terms['trunk']->tid,
  1441. 'term_branch_keep' => FALSE,
  1442. ));
  1443. // Loading again the view after merging action.
  1444. $this->view = views_get_view($this->view->name);
  1445. $this->view->set_display('default');
  1446. $filter = $this->view->display_handler->display->display_options['filters'][$columns['tid']]['value'];
  1447. $this->assertTrue(count($filter) == 1 && in_array($terms['trunk']->tid, array_keys($filter)), 'Views term reference filter gets updated to filter on trunk term instead of filtering on branch term if the branch term is instructed to be deleted during merging of terms.');
  1448. }
  1449. }
  1450. /**
  1451. * Test integration with Entity Reference module.
  1452. */
  1453. class EntityReferenceTermMergeWebTestCase extends TermMergeWebTestCase {
  1454. /**
  1455. * Content type used for testing the entity reference field integration.
  1456. *
  1457. * @var string
  1458. */
  1459. protected $content_type = 'term_merge_entity_reference';
  1460. /**
  1461. * Field definition array used for entity reference integration testing.
  1462. *
  1463. * @var array
  1464. */
  1465. protected $field = array(
  1466. 'type' => 'entityreference',
  1467. 'field_name' => 'term_merge_entity_reference',
  1468. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  1469. 'settings' => array(
  1470. 'target_type' => 'taxonomy_term',
  1471. 'handler' => 'base',
  1472. 'handler_settings' => array(),
  1473. ),
  1474. );
  1475. /**
  1476. * Instance definition array used for entity reference integration testing.
  1477. *
  1478. * @var array
  1479. */
  1480. protected $instance = array();
  1481. /**
  1482. * GetInfo method.
  1483. */
  1484. public static function getInfo() {
  1485. return array(
  1486. 'name' => 'Term Merge Entity Reference',
  1487. 'description' => 'Ensure that the module Term Merge integrates with Entity Reference field type correctly.',
  1488. 'group' => 'Term Merge',
  1489. );
  1490. }
  1491. public function setUp() {
  1492. $modules = $this->normalizeSetUpArguments(func_get_args());
  1493. $modules[] = 'entityreference';
  1494. parent::setUp($modules);
  1495. $this->drupalPost('admin/structure/types/add', array(
  1496. 'name' => $this->randomName(),
  1497. 'type' => $this->content_type,
  1498. ), 'Save content type');
  1499. $this->field = field_create_field($this->field);
  1500. $this->instance['field_name'] = $this->field['field_name'];
  1501. $this->instance['entity_type'] = 'node';
  1502. $this->instance['bundle'] = $this->content_type;
  1503. $this->instance['label'] = $this->randomName();
  1504. $this->instance = field_create_instance($this->instance);
  1505. $this->instance = field_info_instance($this->instance['entity_type'], $this->instance['field_name'], $this->instance['bundle']);
  1506. }
  1507. /**
  1508. * Verify that entity reference field values get update upon term merging.
  1509. */
  1510. public function testEntityReferenceField() {
  1511. $terms = array(
  1512. 'trunk' => NULL,
  1513. 'branch' => NULL,
  1514. );
  1515. $nodes = array();
  1516. foreach ($terms as $type => $v) {
  1517. $terms[$type] = (object) array(
  1518. 'vid' => $this->vocabulary->vid,
  1519. 'name' => $this->randomName(),
  1520. );
  1521. taxonomy_term_save($terms[$type]);
  1522. $nodes[$type] = (object) array(
  1523. 'type' => $this->content_type,
  1524. 'title' => $this->randomName(),
  1525. $this->field['field_name'] => array(LANGUAGE_NONE => array(
  1526. array('target_id' => $terms[$type]->tid),
  1527. )),
  1528. );
  1529. node_save($nodes[$type]);
  1530. }
  1531. actions_do('term_merge_action', $terms['branch'], array(
  1532. 'term_trunk' => $terms['trunk']->tid,
  1533. 'term_branch_keep' => FALSE,
  1534. ));
  1535. foreach ($nodes as $type => $node) {
  1536. $node = entity_load_unchanged('node', $node->nid);
  1537. $this->assertEqual($terms['trunk']->tid, $node->{$this->field['field_name']}[LANGUAGE_NONE][0]['target_id'], $type . ' node points to trunk term in the entity reference field after merging the terms.');
  1538. }
  1539. }
  1540. }