term_merge.test 64 KB

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