term_merge.test in Term Merge 7
Test the Term Merge module.
File
term_merge.testView source
<?php
/**
* @file
* Test the Term Merge module.
*/
/**
* Base class for all tests of Term Merge module.
*/
class TermMergeWebTestCase extends DrupalWebTestCase {
/**
* Fully loaded Drupal user who has access to all required parts of the
* website for testing.
*
* @var object
*/
protected $admin;
/**
* Fully loaded Drupal taxonomy vocabulary object on which all tests are run.
*
* @var object
*/
protected $vocabulary;
/**
* SetUp method.
*/
public function setUp() {
$modules = $this
->normalizeSetUpArguments(func_get_args());
$modules[] = 'term_merge';
parent::setUp($modules);
$this->admin = $this
->drupalCreateUser(array(
'administer fields',
'administer taxonomy',
'administer term merge',
'merge terms',
'administer content types',
'bypass node access',
));
// Creating vocabularies.
$this
->drupalLogin($this->admin);
$name = $this
->randomName();
$this
->drupalPost('admin/structure/taxonomy/add', array(
'name' => $name,
'machine_name' => 'vocabulary',
'description' => $this
->randomName(),
), 'Save');
$this->vocabulary = taxonomy_vocabulary_machine_name_load('vocabulary');
// Flushing static cache.
_field_info_collate_fields(TRUE);
}
/**
* Return last inserted term into the specified vocabulary.
*
* @param object $vocabulary
* Fully loaded taxonomy vocabulary object
*
* @return object
* Fully loaded taxonomy term object of the last inserted term into
* the specified vocabulary
*/
protected function getLastTerm($vocabulary) {
drupal_static_reset();
$tree = taxonomy_get_tree($vocabulary->vid);
$max = 0;
$term = NULL;
foreach ($tree as $v) {
if ($v->tid > $max) {
$max = $v->tid;
$term = $v;
}
}
$term = entity_load_unchanged('taxonomy_term', $term->tid);
return $term;
}
/**
* Normalize the input arguments of ::setUp() method.
*
* The arguments of ::setUp() method can either be a single argument (array of
* modules) or a set of input arguments where each single argument is a module
* name.
*
* @param array $args
* Array of input arguments given to a ::setUp() method
*
* @return array
* Array of modules that are given to a ::setUp() method.
*/
protected function normalizeSetUpArguments($args) {
return isset($args[0]) && is_array($args[0]) ? $args[0] : $args;
}
}
/**
* Test the functionality of Term Merge module.
*/
class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
/**
* GetInfo method.
*/
public static function getInfo() {
return array(
'name' => 'Term Merge',
'description' => 'Ensure that the module Term Merge works correctly.',
'group' => 'Term Merge',
);
}
/**
* Test merging two terms.
*/
public function testTermMerge() {
// Checking whether parent's relationship is handled as it should.
// At the same time we make sure 'term_branch_keep' property functions.
$terms = array(
'trunk' => FALSE,
'branch' => FALSE,
'another_parent' => FALSE,
'branch_child' => FALSE,
);
foreach ($terms as $term_type => $tmp) {
$url = 'admin/structure/taxonomy/vocabulary/add';
$name = $this
->randomName();
$edit = array(
'name' => $name,
);
// Putting "branch" to be parent of "branch_child".
if ($term_type == 'branch_child') {
$edit['parent[]'] = array(
$terms['branch']->tid,
$terms['another_parent']->tid,
);
}
$this
->drupalPost($url, $edit, 'Save');
$terms[$term_type] = $this
->getLastTerm($this->vocabulary);
}
// Firstly we try to merge without deleting the branch term and make sure
// branch's children are not reassigned to the trunk term nor the branch
// term itself is deleted.
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'merge_fields' => array(),
'term_branch_keep' => TRUE,
));
$this
->drupalGet('taxonomy/term/' . $terms['branch']->tid);
$this
->assertText($terms['branch']->name);
drupal_static_reset();
$parents = array();
foreach (taxonomy_get_parents_all($terms['branch_child']->tid) as $parent) {
$parents[] = $parent->tid;
}
$valid_parents = array(
$terms['branch_child']->tid,
$terms['branch']->tid,
$terms['another_parent']->tid,
);
$intersection = array_intersect($parents, $valid_parents);
$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.');
// Now we merge with deletion of branch term, thus the parents of its
// children have to be updated.
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'merge_fields' => array(),
'term_branch_keep' => FALSE,
));
$this
->drupalGet('taxonomy/term/' . $terms['branch']->tid);
$this
->assertResponse(404, 'The branch term has been deleted.');
drupal_static_reset();
$parents = array();
foreach (taxonomy_get_parents_all($terms['branch_child']->tid) as $parent) {
$parents[] = $parent->tid;
}
$valid_parents = array(
$terms['branch_child']->tid,
$terms['trunk']->tid,
$terms['another_parent']->tid,
);
$intersection = array_intersect($parents, $valid_parents);
$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.');
// Now testing 'merge_fields' property. Attaching fields to taxonomy terms.
$bundle = field_extract_bundle('taxonomy_term', $this->vocabulary);
$fields_map = array(
'term_merge_test_single' => 1,
'term_merge_test_unlimited' => FIELD_CARDINALITY_UNLIMITED,
'term_merge_do_not_merge' => 10,
'term_merge_not_unique' => FIELD_CARDINALITY_UNLIMITED,
);
foreach ($fields_map as $field_name => $cardinality) {
$field = array(
'field_name' => $field_name,
'cardinality' => $cardinality,
'locked' => TRUE,
'type' => 'text',
);
field_create_field($field);
field_create_instance(array(
'field_name' => $field_name,
'entity_type' => 'taxonomy_term',
'bundle' => $bundle,
'label' => $field_name,
));
}
$terms = array(
'trunk' => FALSE,
'branch' => FALSE,
);
foreach ($terms as $term_type => $tmp) {
$term = (object) array(
'vid' => $this->vocabulary->vid,
'name' => $this
->randomName(),
);
foreach ($fields_map as $field_name => $cardinality) {
switch ($field_name) {
case 'term_merge_test_single':
$term->{$field_name}[LANGUAGE_NONE][0]['value'] = $this
->randomName();
break;
case 'term_merge_test_unlimited':
case 'term_merge_do_not_merge':
$count = rand(1, 3);
for ($i = 0; $i < $count; $i++) {
$term->{$field_name}[LANGUAGE_NONE][$i]['value'] = $this
->randomName();
}
break;
case 'term_merge_not_unique':
$term->{$field_name}[LANGUAGE_NONE][0]['value'] = 'term_merge_not_unique_value';
break;
}
}
taxonomy_term_save($term);
$terms[$term_type] = $this
->getLastTerm($this->vocabulary);
}
// Firstly we make sure if 'merge_fields' is disabled, the fields are not
// merged.
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'merge_fields' => array(),
'term_branch_keep' => TRUE,
));
$this
->drupalGet('taxonomy/term/' . $terms['trunk']->tid);
foreach ($fields_map as $field_name => $cardinality) {
foreach (field_get_items('taxonomy_term', $terms['branch'], $field_name) as $item) {
if ($field_name != 'term_merge_not_unique') {
$this
->assertNoText($item['value'], 'Values of field ' . $field_name . ' have not been added to the trunk term with disabled "merge_fields" option.');
}
}
}
// Now we try merging with merging fields. The values of the branch term
// should be added to the trunk term's values only in where we asked them
// to be added. Moreover, only unique values are to be kept in each of the
// merged fields.
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'merge_fields' => array(
'term_merge_test_single',
'term_merge_test_unlimited',
'term_merge_not_unique',
),
'term_branch_keep' => TRUE,
));
$this
->drupalGet('taxonomy/term/' . $terms['trunk']->tid);
foreach ($fields_map as $field_name => $cardinality) {
switch ($field_name) {
case 'term_merge_test_single':
case 'term_merge_do_not_merge':
// Make sure if cardinality limit is hit, firstly original trunk term
// values are stored. And make sure values of fields that are not
// instructed to be added to trunk term's values are actually not
// added.
foreach (field_get_items('taxonomy_term', $terms['branch'], $field_name) as $item) {
$this
->assertNoText($item['value'], 'Values of field ' . $field_name . ' (cardinality ' . $cardinality . ') have not been added to the trunk term with enabled "merge_fields" option.');
}
break;
case 'term_merge_not_unique':
// Make sure only the unique values in merged field are kept.
foreach (field_get_items('taxonomy_term', $terms['trunk'], $field_name) as $item) {
$this
->assertUniqueText($item['value'], 'Only unique field values are kept in the trunk term field after merging terms with enabled "merge_fields" option.');
}
break;
case 'term_merge_test_unlimited':
// Make sure values of fields that are instructed to be added to trunk
// term's values are actually added.
foreach (field_get_items('taxonomy_term', $terms['branch'], $field_name) as $item) {
$this
->assertText($item['value'], 'Values of field ' . $field_name . ' (cardinality ' . $cardinality . ') have been added to the trunk term with enabled "merge_fields" option.');
}
break;
}
}
// Make sure that all taxonomy term reference fields are updated to point
// from a branch term to a trunk term in other entities that have taxonomy
// term reference fields.
$terms = array(
'trunk' => FALSE,
'branch' => FALSE,
);
foreach ($terms as $term_type => $tmp) {
$url = 'admin/structure/taxonomy/vocabulary/add';
$name = $this
->randomName();
$edit = array(
'name' => $name,
);
$this
->drupalPost($url, $edit, 'Save');
$terms[$term_type] = $this
->getLastTerm($this->vocabulary);
}
// Firstly we need to create a new content type and assign term reference
// field to this new content type.
$this
->drupalPost('admin/structure/types/add', array(
'name' => $this
->randomName(),
'type' => 'term_merge_node',
), 'Save content type');
$this
->drupalPost('admin/structure/types/manage/term-merge-node/fields', array(
'fields[_add_new_field][label]' => 'Term Reference',
'fields[_add_new_field][field_name]' => 'term_reference',
'fields[_add_new_field][type]' => 'taxonomy_term_reference',
'fields[_add_new_field][widget_type]' => 'taxonomy_autocomplete',
), 'Save');
$this
->drupalPost(NULL, array(
'field[settings][allowed_values][0][vocabulary]' => $this->vocabulary->machine_name,
), 'Save field settings');
$this
->drupalPost(NULL, array(
'field[cardinality]' => FIELD_CARDINALITY_UNLIMITED,
), 'Save settings');
// Flushing fields API cache.
_field_info_collate_fields(TRUE);
// Creating a new node and settings its term reference field to point to
// the term branch.
$title = $this
->randomName();
$this
->drupalPost('node/add/term-merge-node', array(
'title' => $title,
'field_term_reference[' . LANGUAGE_NONE . ']' => $terms['branch']->name,
), 'Save');
$node = $this
->drupalGetNodeByTitle($title, TRUE);
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'merge_fields' => array(),
'term_branch_keep' => TRUE,
));
$this
->drupalGet('node/' . $node->nid);
$this
->assertText($terms['trunk']->name, 'Taxonomy term reference field gets updated to point from term branch to term trunk after merging terms.');
// Testing 'Keep only unique' setting for merging. We create a node assigned
// to both branch and trunk terms, and merge with, and then without 'Keep
// only unique' setting, asserting each result.
$terms = array(
'trunk' => FALSE,
'branch' => FALSE,
);
foreach ($terms as $term_type => $tmp) {
$url = 'admin/structure/taxonomy/vocabulary/add';
$name = $this
->randomName();
$edit = array(
'name' => $name,
);
$this
->drupalPost($url, $edit, 'Save');
$terms[$term_type] = $this
->getLastTerm($this->vocabulary);
}
$title = $this
->randomName();
$this
->drupalPost('node/add/term-merge-node', array(
'title' => $title,
'field_term_reference[' . LANGUAGE_NONE . ']' => $terms['branch']->name . ', ' . $terms['trunk']->name,
), 'Save');
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'merge_fields' => array(),
'term_branch_keep' => TRUE,
'keep_only_unique' => FALSE,
));
$node = $this
->drupalGetNodeByTitle($title);
$is_first_trunk = $node->field_term_reference[LANGUAGE_NONE][0]['tid'] == $terms['trunk']->tid;
$is_second_trunk = $node->field_term_reference[LANGUAGE_NONE][1]['tid'] == $terms['trunk']->tid;
$this
->assertTrue($is_first_trunk && $is_second_trunk, 'The same terms are kept in term reference field values if "Keep only unique" is off.');
// We switch roles of 'trunk' and 'branch' now. We have a node with 2 terms,
// if we merge them into another with "Keep only unique" on we are supposed
// to have only 1 term after merging.
actions_do('term_merge_action', $terms['trunk'], array(
'term_trunk' => $terms['branch']->tid,
'merge_fields' => array(),
'term_branch_keep' => TRUE,
'keep_only_unique' => TRUE,
));
$node = $this
->drupalGetNodeByTitle($title, TRUE);
$is_single = count($node->field_term_reference[LANGUAGE_NONE]) == 1;
$is_expected_term = $node->field_term_reference[LANGUAGE_NONE][0]['tid'] == $terms['branch']->tid;
$this
->assertTrue($is_single && $is_expected_term, 'Only one term is kept in term reference field values if "Keep only unique" is on.');
}
/**
* Test all cases for potentially "buggy" input.
*
* Test the functionality of the action "Term Merge" with various suspicious
* input arguments, and testing the web UI of the module with suspicious
* input.
*/
public function testTermMergeResistance() {
drupal_static_reset();
// Trying to merge 2 terms from 2 different vocabularies.
$this
->drupalPost('admin/structure/taxonomy/add', array(
'name' => $this
->randomName(),
'machine_name' => 'vocabulary2',
), 'Save');
$terms = array(
'vocabulary' => FALSE,
'vocabulary2' => FALSE,
);
foreach ($terms as $term_type => $tmp) {
$url = 'admin/structure/taxonomy/' . $term_type . '/add';
$edit = array(
'name' => $this
->randomName(),
);
$this
->drupalPost($url, $edit, 'Save');
$terms[$term_type] = $this
->getLastTerm(taxonomy_vocabulary_machine_name_load($term_type));
}
actions_do('term_merge_action', $terms['vocabulary'], array(
'term_trunk' => $terms['vocabulary2']->tid,
'term_branch_keep' => FALSE,
));
$this
->termMergeResistanceAssert($terms, 'Testing merging 2 terms from 2 different vocabularies.');
// Trying to merge a parent into its child.
$terms = array(
'parent' => FALSE,
'child' => FALSE,
);
drupal_static_reset();
foreach ($terms as $term_type => $tmp) {
$url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add';
$edit = array(
'name' => $this
->randomName(),
);
if ($term_type == 'child') {
$edit['parent[]'] = array(
$terms['parent']->tid,
);
}
$this
->drupalPost($url, $edit, 'Save');
$terms[$term_type] = $this
->getLastTerm($this->vocabulary);
}
actions_do('term_merge_action', $terms['parent'], array(
'term_trunk' => $terms['child']->tid,
'term_branch_keep' => FALSE,
));
$this
->termMergeResistanceAssert($terms, 'Testing merging a parent into its child.');
// Trying to merge a term into itself.
$terms = array(
'single' => FALSE,
);
foreach ($terms as $term_type => $tmp) {
$url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add';
$name = $this
->randomName();
$edit = array(
'name' => $name,
);
$this
->drupalPost($url, $edit, 'Save');
$terms[$term_type] = $this
->getLastTerm($this->vocabulary);
}
actions_do('term_merge_action', $terms['single'], array(
'term_trunk' => $terms['single']->tid,
'term_branch_keep' => FALSE,
));
$this
->termMergeResistanceAssert($terms, 'Testing merging a term into itself.');
// Making sure the access rights are respected.
$account = $this
->drupalCreateUser(array(
'merge vocabulary2 terms',
));
$this
->drupalLogin($account);
$this
->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge');
$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.');
$this
->drupalGet('admin/structure/taxonomy/vocabulary2/merge');
$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.');
// Test the threshold for "select" widget of the trunk term.
variable_set('term_merge_select_limit', 0);
$this
->drupalGet('admin/structure/taxonomy/vocabulary2/merge');
$this
->assertFieldByXPath('//input[@type="radio"][@name="term_trunk[widget]"][@value="autocomplete"][@checked="checked"]', NULL, 'Threshold for "select" widget of the trunk term is taken into consideration.');
}
/**
* Test all cases of usage of Term Merge Batch.
*/
public function testTermMergeBatch() {
// Adding fields with unlimited cardinality to our vocabulary.
$this
->drupalPost('admin/structure/taxonomy/vocabulary/fields', array(
'fields[_add_new_field][label]' => 'Test Unlimited Text',
'fields[_add_new_field][field_name]' => 'test_text',
'fields[_add_new_field][type]' => 'text',
'fields[_add_new_field][widget_type]' => 'text_textfield',
), 'Save');
$this
->drupalPost(NULL, array(), 'Save field settings');
$this
->drupalPost(NULL, array(
'field[cardinality]' => FIELD_CARDINALITY_UNLIMITED,
), 'Save settings');
// Additionally we need to create a new content type and assign term
// reference field to this new content type.
$this
->drupalPost('admin/structure/types/add', array(
'name' => $this
->randomName(),
'type' => 'term_merge_node',
), 'Save content type');
$this
->drupalPost('admin/structure/types/manage/term-merge-node/fields', array(
'fields[_add_new_field][label]' => 'Term Reference',
'fields[_add_new_field][field_name]' => 'term_reference',
'fields[_add_new_field][type]' => 'taxonomy_term_reference',
'fields[_add_new_field][widget_type]' => 'taxonomy_autocomplete',
), 'Save');
$this
->drupalPost(NULL, array(
'field[settings][allowed_values][0][vocabulary]' => $this->vocabulary->machine_name,
), 'Save field settings');
$this
->drupalPost(NULL, array(), 'Save settings');
// Flushing fields API cache.
_field_info_collate_fields(TRUE);
// Array of cases for which we test the Term Merge batch.
$cases = array(
'taxonomy_vocabulary_tab',
'taxonomy_term_tab',
'via_term_trunk_widget_select',
'via_term_trunk_widget_autocomplete',
'via_term_trunk_widget_autocomplete_without_tid',
'merge_fields',
'do_not_merge_fields',
);
foreach ($cases as $case) {
// Creating a necessary set of terms in the vocabulary.
drupal_static_reset();
$terms = array(
'parent' => FALSE,
'another_parent' => FALSE,
'child' => FALSE,
'term1' => FALSE,
'term2' => FALSE,
'term3' => FALSE,
'term_trunk_parent' => FALSE,
'term_trunk' => FALSE,
);
foreach ($terms as $term_type => $tmp) {
$url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add';
$edit = array(
'name' => $term_type . '_' . $this
->randomName(),
'field_test_text[' . LANGUAGE_NONE . '][0][value]' => $term_type,
);
switch ($term_type) {
case 'child':
$edit['parent[]'] = array(
$terms['parent']->tid,
$terms['another_parent']->tid,
);
break;
case 'term_trunk':
$edit['parent[]'] = array(
$terms['term_trunk_parent']->tid,
);
break;
}
$this
->drupalPost($url, $edit, 'Save');
$terms[$term_type] = $this
->getLastTerm($this->vocabulary);
}
// The initial URL from where the form that kicks off batch is submitted.
$init_url = '';
// What widget to use for choosing term trunk.
$term_trunk_widget = '';
// Value for term trunk in the format, expected by the widget
// $term_trunk_widget. Additionally, if any test case requires any extra
// fields to be submitted, input those fields into this array and they
// won't be taken out from this array, then it will get merged into $edit,
// and this way eventually your values will be successfully submitted.
$term_trunk_edit = array();
// Setting up controlling vars based on case and doing any specific
// assertions for each case.
switch ($case) {
case 'taxonomy_vocabulary_tab':
$init_url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge';
// It doesn't really matter which widget we use, we test widgets
// throughout in other cases.
$term_trunk_widget = array_rand(drupal_map_assoc(array(
'select',
'autocomplete',
)));
break;
case 'taxonomy_term_tab':
$init_url = 'taxonomy/term/' . $terms['parent']->tid . '/merge';
// It doesn't really matter which widget we use, we test widgets
// throughout in other cases.
$term_trunk_widget = array_rand(drupal_map_assoc(array(
'select',
'autocomplete',
)));
// Assert that the term, for which the tab was clicked, is selected as
// term branch by default.
$this
->drupalGet($init_url);
$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.');
break;
case 'via_term_trunk_widget_select':
$init_url = 'taxonomy/term/' . $terms['parent']->tid . '/merge';
$term_trunk_widget = 'select';
// Making sure for the term trunk select the selected term branch are
// not available, nor their children.
$this
->drupalGet($init_url);
$matches = array();
preg_match('#\\<select[^>]+name="term_trunk\\[tid\\]"[^>]*\\>.+?\\</select\\>#si', $this->content, $matches);
$term_trunk_options = $matches[0];
$str_pos = strpos($term_trunk_options, $terms['child']->name);
$this
->assertIdentical(FALSE, $str_pos, 'Child is not available as option for term trunk if its parent is chosen among term branches.');
$str_pos = strpos($term_trunk_options, $terms['parent']->name);
$this
->assertIdentical(FALSE, $str_pos, 'Selected branch term is not available as an option for term trunk.');
break;
case 'via_term_trunk_widget_autocomplete':
$init_url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge';
$term_trunk_widget = 'autocomplete';
// Test autocomplete widget menu path to make sure it does reply
// with valid suggestions.
$response = $this
->drupalGet('term-merge/autocomplete/term-trunk/' . $this->vocabulary->machine_name . '/' . drupal_strtoupper($terms['term_trunk']->name));
$response = drupal_json_decode($response);
$autocomplete_key = $terms['term_trunk']->name . ' (' . $terms['term_trunk']->tid . ')';
$this
->assertTrue(isset($response[$autocomplete_key]), 'Autocomplete menu path replies with valid suggestions for term trunk autocomplete widget.');
// Making sure for the term trunk autocomplete widget doesn't allow to
// submit any of the selected term branches nor their children.
$prohibited_terms = array(
'parent' => 'Merging into the same term is not allowed in autocomplete widget for term trunk.',
'child' => 'Merging into any of child of selected branch terms is not allowed in autocomplete widget for term trunk.',
);
foreach ($prohibited_terms as $term => $assert_message) {
$term = $terms[$term];
$this
->drupalGet($init_url);
$this
->drupalPostAJAX(NULL, array(
'term_branch[]' => array(
$terms['parent']->tid,
),
'term_trunk[widget]' => $term_trunk_widget,
), 'term_trunk[widget]');
$this
->drupalPost(NULL, array(
'term_branch[]' => array(
$terms['parent']->tid,
),
'term_trunk[widget]' => $term_trunk_widget,
'term_trunk[tid]' => $term->name . ' (' . $term->tid . ')',
), 'Submit');
$this
->assertText('Trunk term cannot be one of the selected branch terms or their children', $assert_message);
}
break;
case 'via_term_trunk_widget_autocomplete_without_tid':
$init_url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge';
$term_trunk_widget = 'autocomplete';
// Making sure for the term trunk autocomplete widget doesn't allow to
// submit any of the selected term branches nor their children.
$prohibited_terms = array(
'parent' => 'Merging into the same term is not allowed in autocomplete widget for term trunk.',
'child' => 'Merging into any of child of selected branch terms is not allowed in autocomplete widget for term trunk.',
);
foreach ($prohibited_terms as $term => $assert_message) {
$term = $terms[$term];
$this
->drupalGet($init_url);
$this
->drupalPostAJAX(NULL, array(
'term_branch[]' => array(
$terms['parent']->tid,
),
'term_trunk[widget]' => $term_trunk_widget,
), 'term_trunk[widget]');
$this
->drupalPost(NULL, array(
'term_branch[]' => array(
$terms['parent']->tid,
),
'term_trunk[widget]' => $term_trunk_widget,
'term_trunk[tid]' => $term->name,
), 'Submit');
$this
->assertText('Trunk term cannot be one of the selected branch terms or their children', $assert_message);
}
break;
case 'merge_fields':
$init_url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge';
// It doesn't really matter which widget we use, we test widgets
// throughout in other cases.
$term_trunk_widget = array_rand(drupal_map_assoc(array(
'select',
'autocomplete',
)));
// We embed extra info related to field values merging into
// $term_trunk_edit.
$term_trunk_edit['merge_fields[field_test_text]'] = TRUE;
break;
case 'do_not_merge_fields':
$init_url = 'admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge';
// It doesn't really matter which widget we use, we test widgets
// throughout in other cases.
$term_trunk_widget = array_rand(drupal_map_assoc(array(
'select',
'autocomplete',
)));
break;
}
// Creating a new node and setting its term reference field to point to
// the term branch.
$title = $this
->randomName();
$this
->drupalPost('node/add/term-merge-node', array(
'title' => $title,
'field_term_reference[' . LANGUAGE_NONE . ']' => $terms['term1']->name,
), 'Save');
$node = $this
->drupalGetNodeByTitle($title, TRUE);
// Calling the Term Merge form.
$this
->drupalGet($init_url);
// Choosing term branches.
$term_branches = array(
'term1',
'term2',
'term3',
);
$term_branches_edit = array();
foreach ($term_branches as $term_type) {
$term_branches_edit[] = $terms[$term_type]->tid;
}
$this
->drupalPostAJAX(NULL, array(
'term_branch[]' => $term_branches_edit,
), 'term_branch[]');
// Choosing the widget for trunk term.
$this
->drupalPostAJAX(NULL, array(
'term_branch[]' => $term_branches_edit,
'term_trunk[widget]' => $term_trunk_widget,
), 'term_trunk[widget]');
// Choosing term trunk.
switch ($term_trunk_widget) {
case 'select':
$term_trunk_edit += array(
'term_trunk[tid]' => $terms['term_trunk']->tid,
);
break;
case 'autocomplete':
$term_trunk_edit += array(
'term_trunk[tid]' => $terms['term_trunk']->name . ' (' . $terms['term_trunk']->tid . ')',
);
break;
}
// Submitting the form.
$edit = $term_trunk_edit + array(
'term_branch[]' => $term_branches_edit,
'term_trunk[widget]' => $term_trunk_widget,
'term_branch_keep' => FALSE,
'step' => 2,
);
$this
->drupalPost(NULL, $edit, 'Submit');
$this
->drupalPost(NULL, array(), 'Confirm');
// Making sure all the branches are deleted.
foreach ($term_branches as $term_type) {
$term = $terms[$term_type];
$this
->drupalGet('taxonomy/term/' . $term->tid);
$this
->assertResponse(404, 'Branch term ' . $term_type . ' has been deleted after merging.');
}
$text_assertions = array();
$term_trunk = $terms['term_trunk'];
// Adding any extra text assertions on per test-case basis.
switch ($case) {
case 'merge_fields':
// Making sure the term trunk has been merged all the fields from term
// branches into itself.
foreach ($term_branches as $term_type) {
$items = field_get_items('taxonomy_term', $terms[$term_type], 'field_test_text');
foreach ($items as $delta => $item) {
$text_assertions[$term_type . ' text field delta#' . $delta . ' has been merged when instructed to merge field values.'] = $item['value'];
}
}
break;
case 'do_not_merge_fields':
// We need to assert that no values for field have been merged from
// branch terms into the values of trunk term.
$this
->drupalGet('taxonomy/term/' . $term_trunk->tid);
foreach ($term_branches as $term_type) {
$items = field_get_items('taxonomy_term', $terms[$term_type], 'field_test_text');
foreach ($items as $delta => $item) {
$this
->assertNoText($item['value'], $term_type . ' text field delta#' . $delta . ' has not been merged when instrcuted not to merge field values.');
}
}
break;
}
$this
->drupalGet('taxonomy/term/' . $term_trunk->tid);
foreach ($text_assertions as $k => $v) {
$this
->assertText($v, 'Term trunk has the property ' . $k);
}
// Making sure the taxonomy term reference in other entities are updated
// to point from term branches to the just created term trunk.
$this
->drupalGet('node/' . $node->nid);
$this
->assertText($term_trunk->name, 'Taxonomy term reference fields in other entities are updated to point from term branches to the term trunk.');
}
}
/**
* Supportive function for the main test "testTermMergeResistance".
*
* Assert that each term of the array $terms is available.
*
* @param array $terms
* Array of taxonomy terms objects
* @param string $message
* Assertion message to be shown on the test results page
*/
protected function termMergeResistanceAssert($terms, $message) {
foreach ($terms as $term) {
$this
->drupalGet('taxonomy/term/' . $term->tid);
$this
->assertResponse(200, $message);
}
}
}
/**
* Test the Merge Duplicate Terms feature of the Term Merge module.
*/
class DuplicatesTermMergeWebTestCase extends TermMergeWebTestCase {
/**
* GetInfo method.
*/
public static function getInfo() {
return array(
'name' => 'Duplicate terms merge',
'description' => 'Ensure that the feature <i>merge duplicate terms</i> of module Term Merge works correctly.',
'group' => 'Term Merge',
);
}
/**
* Test access rights.
*/
public function testDisabledAndPermissions() {
// Trying a user who doesn't have enough permissions.
$account = $this
->drupalCreateUser();
$this
->drupalLogin($account);
$this
->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates');
$this
->assertResponse(403, 'Access to Merge Duplicate Terms is denied for a user who does not have enough permissions.');
// Trying a user who have enough permissions.
$this
->drupalLogin($this->admin);
$this
->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates');
$this
->assertResponse(200, 'Access to Merge Duplicate Terms is granted for a user who has enough permissions.');
}
/**
* Test merging duplicates feature of Term Merge module.
*
* Test the following features:
* - Correctness of merging a group of duplicate terms, namely:
* - Correctness of merge operation when duplicates feature is invoked on
* the entire vocabulary
* - Correctness of merge operation when duplicates feature is invoked on a
* term (merge its children one into another)
* - Correctness of the mechanism that groups terms into sets of duplicate
* entries, namely:
* - Correctness of grouping by term name, i.e. unique terms should not be
* listed in any set of duplicate terms
* - Correctness of the initial set of terms, on which the duplicate tool is
* invoked, i.e. when invoked on a vocabulary, we search for duplicates
* in the whole vocabulary, but when invoked on a term, the tool should
* only search for duplicate among the children of that term
*/
public function testDuplicates() {
// Creating duplicate terms firstly.
$groups = array(
'single' => 1,
'triple_different_parent' => 3,
'random' => rand(2, 5),
// We need some term, that will be a parent of some other terms.
'parent' => 1,
);
$groups = $this
->createTerms($groups);
// Let us make two of 'triple_different_parent' terms children of 'parent'
// term.
$groups['triple_different_parent'][1]->parent = $groups['parent'][0]->tid;
taxonomy_term_save($groups['triple_different_parent'][1]);
$groups['triple_different_parent'][2]->parent = $groups['parent'][0]->tid;
taxonomy_term_save($groups['triple_different_parent'][2]);
// Test duplicate suggestion plugin type. Make sure multiple duplicated
// suggestions are properly handed and make sure each of the duplicate
// suggestions does its function.
$this
->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates');
$this
->assertSuggestedDuplicates(array_merge($groups['triple_different_parent'], $groups['random']), 'Filtering only by term names yields expected results.');
$this
->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates', array(
'settings[duplicate_suggestion][name]' => FALSE,
'settings[duplicate_suggestion][description]' => TRUE,
), 'Re-run duplicate search');
$this
->assertSuggestedDuplicates(array_merge($groups['triple_different_parent'], $groups['random']), 'Filtering only by term description yields expected results.');
$this
->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates', array(
'settings[duplicate_suggestion][name]' => FALSE,
'settings[duplicate_suggestion][parent]' => TRUE,
), 'Re-run duplicate search');
$expected_terms = array();
$expected_terms = array_merge($expected_terms, $groups['single'], $groups['random'], $groups['parent']);
$expected_terms[] = $groups['triple_different_parent'][0];
$this
->assertSuggestedDuplicates($expected_terms, 'Filtering only by term parent yields expected results.');
$this
->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates', array(
'settings[duplicate_suggestion][name]' => TRUE,
'settings[duplicate_suggestion][parent]' => TRUE,
), 'Re-run duplicate search');
$expected_terms = $groups['triple_different_parent'];
unset($expected_terms[0]);
$this
->assertSuggestedDuplicates($expected_terms, 'Filtering by term name and parent yields expected results, i.e. duplicate suggestions can be combined.');
// Assuring the single term is not listed as duplicate.
$this
->drupaLGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates');
$this
->assertNoText($groups['single'][0]->name, 'Single term is not listed as a duplicate.');
// Making sure the term in 'triple_different_parent' that does not have a
// parent, is not listed when we invoke duplicate tool on a parent term.
$this
->drupalGet('taxonomy/term/' . $groups['parent'][0]->tid . '/merge/duplicates');
$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.');
$edit = array();
// Trying to merge a term into another, invoking Duplicate tool on a parent
// term of both. Important note: we do not test merging options, because
// supposedly those are tested in the main test of this module.
$edit['group[' . $this
->duplicateHashTerm($groups['triple_different_parent'][1]) . '][trunk_tid]'] = $groups['triple_different_parent'][1]->tid;
$edit['group[' . $this
->duplicateHashTerm($groups['triple_different_parent'][2]) . '][duplicates][' . $groups['triple_different_parent'][2]->tid . ']'] = TRUE;
$groups['triple_different_parent'][2]->merged = TRUE;
$this
->drupalPost('taxonomy/term/' . $groups['parent'][0]->tid . '/merge/duplicates', $edit, 'Submit');
// Trying to merge multiple terms. We merge all but the 1st term.
$edit = array();
$edit['group[' . $this
->duplicateHashTerm($groups['random'][0]) . '][trunk_tid]'] = $groups['random'][0]->tid;
foreach ($groups['random'] as $k => $term) {
if ($k != 0) {
$edit['group[' . $this
->duplicateHashTerm($groups['random'][$k]) . '][duplicates][' . $term->tid . ']'] = TRUE;
$groups['random'][$k]->merged = TRUE;
}
}
$this
->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates', $edit, 'Submit');
// Asserting results of merging.
foreach ($groups as $group) {
foreach ($group as $term) {
$this
->drupalGet('taxonomy/term/' . $term->tid);
$code = isset($term->merged) && $term->merged ? 404 : 200;
$message = isset($term->merged) && $term->merged ? 'Term #' . $term->tid . ' has been successfully merged.' : 'Term #' . $term->tid . ' has been successfully untouched during merging.';
$this
->assertResponse($code, $message);
}
}
}
/**
* Supportive method.
*
* Create taxonomy terms with similar names.
*
* @param array $groups
* Key should be a name of the group (terms' names in this group may only
* differ in case, but will always use this string as their names), while
* corresponding value to that key should denote how many terms in each
* group should be created
*
* @return array
* Array of fully loaded taxonomy terms objects of the just created terms,
* grouped by their group name
*/
protected function createTerms($groups) {
foreach ($groups as $name => $quantity) {
$groups[$name] = array();
$description = $this
->randomName();
for ($i = 0; $i < $quantity; $i++) {
$term_name = '';
$term_description = '';
// Randomizing case of the group name.
foreach (str_split($name) as $symbol) {
$symbol = rand(0, 1) ? drupal_strtoupper($symbol) : drupal_strtolower($symbol);
$term_name .= $symbol;
}
// Getting description in different cases.
foreach (str_split($description) as $symbol) {
$symbol = rand(0, 1) ? drupal_strtoupper($symbol) : drupal_strtolower($symbol);
$term_description .= $symbol;
}
$term = (object) array(
'vid' => $this->vocabulary->vid,
'name' => $term_name,
'description' => $description,
);
taxonomy_term_save($term);
$groups[$name][] = $this
->getLastTerm($this->vocabulary);
}
}
return $groups;
}
/**
* Supportive method.
*
* Calculate hash a term based on which it will be paired with other terms as
* possible duplicates of each other.
*
* @param object $term
* Term whose duplicate suggestion hash is to be calculated
* @param array $duplicate_suggestions
* Array of duplicate suggestion names that to apply, when determining hash
* of the provided term
*
* @return string
* Hash of the provided term according to enabled duplicate suggestions
*/
protected function duplicateHashTerm($term, $duplicate_suggestions = array(
'name',
)) {
$hash = '';
foreach ($duplicate_suggestions as $duplicate_suggestion) {
$hash_chunk = '';
switch ($duplicate_suggestion) {
case 'name':
$hash_chunk = drupal_strtoupper($term->name);
// Trying transliteration, if available.
if (module_exists('transliteration')) {
$hash_chunk = transliteration_get($hash_chunk);
// Keeping only ASCII chars.
$hash_chunk = preg_replace('#\\W#', '', $hash_chunk);
}
break;
case 'description':
$hash_chunk = drupal_strtoupper($term->description);
break;
case 'parent':
$hash_chunk = $term->parents[0];
break;
}
$hash .= $hash_chunk;
}
return $hash;
}
/**
* Assert expected terms indeed are suggested as duplicates.
*
* @param array $expected_terms
* Array of terms that are expected to be suggested as duplicates
* @param string $message
* Assertion message to display on the test results
*/
protected function assertSuggestedDuplicates($expected_terms, $message = '') {
$i = 0;
foreach ($expected_terms as $term) {
$this
->assertPattern('#\\<input\\s+[^>]*type="checkbox"\\s+[^>]*name="[^"]+\\[duplicates]\\[' . $term->tid . '\\]"#si', $message . ' (for term #' . $i . ')');
$i++;
}
}
}
/**
* Test the integration between Term Merge module and Path/Redirect modules.
*/
class RedirectTermMergeWebTestCase extends TermMergeWebTestCase {
/**
* Fully loaded Drupal user object of the user who has access to configure
* redirects.
*
* @var object
*/
protected $superAdmin;
/**
* SetUp method.
*/
public function setUp() {
$modules = $this
->normalizeSetUpArguments(func_get_args());
$modules[] = 'redirect';
$modules[] = 'path';
parent::setUp($modules);
$this->superAdmin = $this
->drupalCreateUser(array(
'administer taxonomy',
'administer term merge',
'merge terms',
'administer content types',
'bypass node access',
'administer redirects',
'administer url aliases',
));
}
/**
* GetInfo method.
*/
public static function getInfo() {
return array(
'name' => 'Redirect module integration',
'description' => 'Ensure that the module Term Merge integrates with ' . l('Redirect', 'http://drupal.org/project/redirect') . '/Path modules correctly.',
'group' => 'Term Merge',
);
}
/**
* Test disabled Redirect module and access rights.
*/
public function testDisabledAndPermissions() {
// Checking access rights required to set up redirection during term
// merging.
$this
->drupalLogin($this->admin);
$this
->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge');
$this
->assertNoPattern('#\\<select[^>]+name="redirect"[^>]*\\>#i', 'No redirection settings are available for a user that does not possess corresponding permissions.');
// Set a default value to make sure term merge form uses it.
variable_set('term_merge_default_redirect', 0);
$this
->drupalLogin($this->superAdmin);
$this
->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge');
$this
->assertPattern('#\\<select[^>]+name="redirect"[^>]*\\>#i', 'Redirection settings are available for a user that possesses corresponding permissions.');
$this
->assertFieldByXPath('//select[@name="redirect"]/option[@value="' . variable_get('term_merge_default_redirect', TERM_MERGE_NO_REDIRECT) . '"][@selected="selected"]', NULL, 'The default redirect action is properly set.');
// Making sure redirect settings are not available during merging when
// merging with disabled Redirect module.
module_disable(array(
'redirect',
));
$this
->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge');
$this
->assertNoPattern('#\\<select[^>]+name="redirect"[^>]*\\>#i', 'No redirection settings are available when the redirect module is disabled.');
}
/**
* Test the action 'term_merge_action' in terms of integration with Redirect.
*/
public function testTermMergeAction() {
$this
->drupalLogin($this->superAdmin);
$terms = $this
->createTerms(array(
'branch',
'trunk',
));
// Testing default value.
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'term_branch_keep' => TRUE,
));
$this
->assertRedirectIntegration($terms, 'By default no redirects should be made.');
// Testing no redirection.
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'term_branch_keep' => TRUE,
'redirect' => TERM_MERGE_NO_REDIRECT,
));
$this
->assertRedirectIntegration($terms, 'No redirects are made, if action is not instructed to make ones.');
// Testing 301 redirection. Besides redirecting 'taxonomy/term/[branch-tid]'
// to 'taxonomy/term/[trunk-tid]' and their path aliases we want to
// additionally assert that all existing redirects to branch term will be
// replaced with redirects to trunk term in Redirect module. Lastly, we also
// assert that 'taxonomy/term/[branch-tid]/feed' path and all pointing there
// redirects now point to 'taxonomy/term/[trunk-tid]/feed.
$redirect_source = $this
->randomName();
$redirect = new stdClass();
redirect_object_prepare($redirect, array(
'source' => $redirect_source,
'redirect' => 'taxonomy/term/' . $terms['branch']->tid,
));
redirect_hash($redirect);
redirect_save($redirect);
$redirect = new stdClass();
redirect_object_prepare($redirect, array(
'source' => $redirect_source . '/feed',
'redirect' => 'taxonomy/term/' . $terms['branch']->tid . '/feed',
));
redirect_hash($redirect);
redirect_save($redirect);
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'redirect' => 301,
));
$terms['branch']->redirect = $terms['trunk'];
$this
->assertRedirectIntegration($terms, 'Redirects are made, if action is instructed to make ones.');
$this
->drupalGet($redirect_source);
$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>.');
$this
->drupalGet($redirect_source . '/feed');
$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>.');
}
/**
* Test Term Merge batch in terms of integration with Redirect/Path modules.
*/
public function testTermMergeBatch() {
$this
->drupalLogin($this->superAdmin);
// Trying to merge without redirection.
$terms = $this
->createTerms(array(
'branch',
'trunk',
));
$this
->drupalPost('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
'term_branch[]' => array(
$terms['branch']->tid,
),
'term_trunk[widget]' => 'select',
'term_trunk[tid]' => $terms['trunk']->tid,
'term_branch_keep' => TRUE,
'redirect' => TERM_MERGE_NO_REDIRECT,
), 'Submit');
$this
->drupalPost(NULL, array(), 'Confirm');
$this
->assertRedirectIntegration($terms, 'No redirection made after running merge batch when not instructed to make redirection.');
// Trying to merge into a term with redirection.
$this
->drupalPost('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
'term_branch[]' => array(
$terms['branch']->tid,
),
'term_trunk[widget]' => 'select',
'term_trunk[tid]' => $terms['trunk']->tid,
'redirect' => 0,
), 'Submit');
$terms['branch']->redirect = $terms['trunk'];
$this
->drupalPost(NULL, array(), 'Confirm');
$this
->assertRedirectIntegration($terms, 'Redirection is made after running merge batch merging into an existing term, when instructed to make redirection.');
}
/**
* Supportive method.
*
* Assert expected results after doing any test actions.
*
* @param array $terms
* Array of terms as returned by $this->createTerms(). Those terms that have
* been merged and redirected to another terms, besides all normal keys
* should have property 'redirect' which should be equal to the fully loaded
* taxonomy term which they were redirected to
* @param string $message
* Assert message to be shown on test results page
*/
protected function assertRedirectIntegration($terms, $message) {
foreach ($terms as $term) {
if (isset($term->redirect)) {
$sources = array(
'taxonomy/term/' . $term->tid,
);
// Additionally checking path alias.
if (!in_array(drupal_get_path_alias($sources[0]), $sources)) {
$sources[] = drupal_get_path_alias($sources[0]);
}
foreach ($sources as $source) {
$this
->drupalGet($source);
$this
->assertUrl('taxonomy/term/' . $term->redirect->tid, array(), $message);
}
// Additionally assert the 'taxonomy/term/*/feed' path.
$sources = array(
'taxonomy/term/' . $term->tid . '/feed',
);
if (!in_array(drupal_get_path_alias($sources[0]), $sources)) {
$sources[] = drupal_get_path_alias($sources[0]);
}
foreach ($sources as $source) {
$this
->drupalGet($source);
$this
->assertUrl('taxonomy/term/' . $term->redirect->tid . '/feed', array(), $message);
}
}
}
}
/**
* Supportive method.
*
* Create a list of terms, assigning path aliases according to the values
* of the supplied array.
*
* @param array $terms
* Array of machine readable term keys based on which is generated output
*
* @return array
* Array of taxonomy term objects path alias of which is equal to the value
* that corresponds to its position in the supplied array
*/
protected function createTerms($terms) {
$return = array();
foreach ($terms as $v) {
$this
->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add', array(
'name' => $this
->randomName(),
'path[alias]' => $v . $this
->randomName(),
), 'Save');
$return[$v] = $this
->getLastTerm($this->vocabulary);
}
return $return;
}
}
/**
* Test the integration between Term Merge module and Synonyms module.
*/
class SynonymsTermMergeWebTestCase extends TermMergeWebTestCase {
/**
* Field definition array within which the testing will happen.
*
* @var array
*/
protected $field = array(
'field_name' => 'term_merge_synonyms_test',
'type' => 'text',
);
/**
* Synonyms behavior implementation that undergoes testing.
*
* @var array
*/
protected $behavior_implementation;
/**
* SetUp method.
*/
public function setUp() {
$modules = $this
->normalizeSetUpArguments(func_get_args());
$modules[] = 'synonyms';
$modules[] = 'synonyms_provider_field';
parent::setUp($modules);
// Additionally we enable default synonyms field in the vocabulary.
$this->field = field_create_field($this->field);
$instance = array(
'field_name' => $this->field['field_name'],
'label' => 'Testing term merge synonyms integration',
'entity_type' => 'taxonomy_term',
'bundle' => $this->vocabulary->machine_name,
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
);
$instance = field_create_instance($instance);
$instance = field_info_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
$this->behavior_implementation = array(
'entity_type' => $instance['entity_type'],
'bundle' => $instance['bundle'],
'provider' => synonyms_provider_field_provider_name($this->field),
'behavior' => 'term_merge',
'settings' => array(),
);
synonyms_behavior_implementation_save($this->behavior_implementation);
foreach (synonyms_behavior_get($this->behavior_implementation['behavior'], $this->behavior_implementation['entity_type'], $this->behavior_implementation['bundle'], TRUE) as $behavior_implementation) {
if ($behavior_implementation['provider'] == $this->behavior_implementation['provider']) {
$this->behavior_implementation = $behavior_implementation;
break;
}
}
}
/**
* GetInfo method.
*/
public static function getInfo() {
return array(
'name' => 'Synonyms module integration',
'description' => 'Ensure that the module Term Merge integrates with ' . l('Synonyms', 'http://drupal.org/project/synonyms') . ' module correctly.',
'group' => 'Term Merge',
);
}
/**
* Test disabled Synonyms module.
*/
public function testDisabled() {
// Making sure synonyms settings are not available during merging when
// Synonyms module is disabled.
module_disable(array(
'synonyms',
));
$this
->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge');
$this
->assertNoText(t('Add as Synonyms'), 'No synonyms settings are available when the Synonyms module is disabled.');
}
/**
* Test the action 'term_merge_action' in terms of integration with Synonyms.
*/
public function testTermMergeAction() {
$this
->drupalLogin($this->admin);
$terms = $this
->createTerms(array(
'branch',
'trunk',
));
// Testing default value.
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'term_branch_keep' => TRUE,
));
$this
->assertSynonymsIntegration($terms, 'By default no synonyms should be added.');
// Testing no synonyms adding.
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'term_branch_keep' => TRUE,
'synonyms' => NULL,
));
$this
->assertSynonymsIntegration($terms, 'No synonyms are added, if action is not instructed to make ones.');
// Testing adding as a synonym.
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'synonyms' => $this->behavior_implementation['provider'],
));
$terms['trunk']->synonyms = array(
$terms['branch']->name,
);
$this
->assertSynonymsIntegration($terms, 'Synonyms are added, if action is instructed to add ones.');
}
/**
* Test Term Merge batch in terms of integration with Synonyms module.
*/
public function testTermMergeBatch() {
// Trying to merge without synonyms adding.
$terms = $this
->createTerms(array(
'branch',
'trunk',
));
$this
->drupalPost('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
'term_branch[]' => array(
$terms['branch']->tid,
),
'term_trunk[widget]' => 'select',
'term_trunk[tid]' => $terms['trunk']->tid,
'term_branch_keep' => TRUE,
'synonyms' => '',
), 'Submit');
$this
->drupalPost(NULL, array(), 'Confirm');
$this
->assertSynonymsIntegration($terms, 'No synonyms are added after running merge batch when not instructed to add synonyms.');
// Trying to merge into a term with synonyms adding.
$this
->drupalPost('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
'term_branch[]' => array(
$terms['branch']->tid,
),
'term_trunk[widget]' => 'select',
'term_trunk[tid]' => $terms['trunk']->tid,
'term_branch_keep' => TRUE,
'synonyms' => $this->behavior_implementation['provider'],
), 'Submit');
$terms['trunk']->synonyms = array(
$terms['branch']->name,
);
$this
->drupalPost(NULL, array(), 'Confirm');
$this
->assertSynonymsIntegration($terms, 'Synonyms are added after running merge batch merging into an existing term, when instructed to add synonyms.');
}
/**
* Supportive method.
*
* Assert expected results after doing any test actions.
*
* @param array $terms
* Array of terms as returned by $this->createTerms(). Those term trunks
* that have merged any branch terms with "Synonyms" option on, besides all
* normal keys should have property 'synonyms' which should be an array of
* expected synonyms of this term
* @param string $message
* Assert message to be shown on test results page
*/
protected function assertSynonymsIntegration($terms, $message) {
foreach ($terms as $term) {
// Getting an array of synonyms according to Synonyms module.
$context = array();
$synonyms = synonyms_get_raw(entity_load_unchanged('taxonomy_term', $term->tid), array(), 'synonyms', 'taxonomy_term', $context);
$expected_synonyms = isset($term->synonyms) ? $term->synonyms : array();
// Comparing $synonyms to $expected_synonyms.
if (count($expected_synonyms) != count(array_intersect($expected_synonyms, $synonyms))) {
$this
->fail($message);
return;
}
}
// If we got here, then all expected synonyms were found.
$this
->pass($message);
}
/**
* Supportive method.
*
* Create a list of terms, assigning names according to the values of the
* supplied array.
*
* @param array $terms
* Array of machine readable term keys based on which is generated output
*
* @return array
* Array of taxonomy term objects name of which is equal to the value that
* corresponds to its position in the supplied array
*/
protected function createTerms($terms) {
$return = array();
foreach ($terms as $v) {
$this
->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add', array(
'name' => $v,
), 'Save');
$return[$v] = $this
->getLastTerm($this->vocabulary);
}
return $return;
}
}
/**
* Test the integration between Term Merge module and Views module.
*/
class ViewsTermMergeWebTestCase extends TermMergeWebTestCase {
/**
* View object on which all tests happen.
*
* @var view
*/
protected $view;
/**
* SetUp method.
*/
public function setUp() {
$modules = $this
->normalizeSetUpArguments(func_get_args());
$modules[] = 'views';
parent::setUp($modules);
// Additionally we create a view.
$view = views_new_view();
$view->name = 'term_merge_view_test';
$view->description = 'Test view to test Term Merge module.';
$view->tag = '';
$view->base_table = 'node';
$view->api_version = '3.0';
$view->core = 7;
$display_id = 'default';
$view
->set_display($display_id);
views_save_view($view);
$this->view =& $view;
}
/**
* GetInfo method.
*/
public static function getInfo() {
return array(
'name' => 'Views module integration',
'description' => 'Ensure that the module Term Merge integrates with ' . l('Views', 'http://drupal.org/project/views') . ' module correctly.',
'group' => 'Term Merge',
);
}
/**
* Test integration with Views Taxonomy Term reference filter.
*/
public function testTermReferenceFieldFilter() {
// We need to create a content type and attach a term reference field to
// that bundle in order to have some term reference filter available in
// Views.
$this
->drupalPost('admin/structure/types/add', array(
'name' => $this
->randomName(),
'type' => 'term_merge_node',
), 'Save content type');
$field_name = 'term_reference';
$this
->drupalPost('admin/structure/types/manage/term-merge-node/fields', array(
'fields[_add_new_field][label]' => 'Term Reference',
'fields[_add_new_field][field_name]' => $field_name,
'fields[_add_new_field][type]' => 'taxonomy_term_reference',
'fields[_add_new_field][widget_type]' => 'taxonomy_autocomplete',
), 'Save');
$field_name = 'field_' . $field_name;
$this
->drupalPost(NULL, array(
'field[settings][allowed_values][0][vocabulary]' => $this->vocabulary->machine_name,
), 'Save field settings');
$this
->drupalPost(NULL, array(
'field[cardinality]' => FIELD_CARDINALITY_UNLIMITED,
), 'Save settings');
// Flushing fields API cache.
_field_info_collate_fields(TRUE);
// Loading field definition array of the term reference field we just
// created.
$field = field_info_field($field_name);
// Creating terms to have stuff to work with.
$terms = array(
'branch' => FALSE,
'trunk' => FALSE,
);
foreach ($terms as $term_type => $tmp) {
$url = 'admin/structure/taxonomy/vocabulary/add';
$name = $this
->randomName();
$edit = array(
'name' => $name,
);
$this
->drupalPost($url, $edit, 'Save');
$terms[$term_type] = $this
->getLastTerm($this->vocabulary);
}
// Adding a taxonomy term reference filter to the view.
$this->view
->set_display('default');
// We use Field API info to look up necessary tables and columns.
$table = array_keys($field['storage']['details']['sql']['FIELD_LOAD_CURRENT']);
$table = reset($table);
$columns = $field['storage']['details']['sql']['FIELD_LOAD_CURRENT'][$table];
$this->view->display_handler->display->display_options['filters'][$columns['tid']]['id'] = $columns['tid'];
$this->view->display_handler->display->display_options['filters'][$columns['tid']]['table'] = $table;
$this->view->display_handler->display->display_options['filters'][$columns['tid']]['field'] = $columns['tid'];
$this->view->display_handler->display->display_options['filters'][$columns['tid']]['value'] = array(
$terms['branch']->tid => $terms['branch']->tid,
);
$this->view->display_handler->display->display_options['filters'][$columns['tid']]['type'] = 'select';
$this->view->display_handler->display->display_options['filters'][$columns['tid']]['vocabulary'] = $this->vocabulary->machine_name;
$this->view->display_handler->display->display_options['filters'][$columns['tid']]['hierarchy'] = 1;
views_save_view($this->view);
// After such merge we expect the view's filter to be changed from branch
// term to trunk term.
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'term_branch_keep' => FALSE,
));
// Loading again the view after merging action.
$this->view = views_get_view($this->view->name);
$this->view
->set_display('default');
$filter = $this->view->display_handler->display->display_options['filters'][$columns['tid']]['value'];
$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.');
}
}
/**
* Test integration with Entity Reference module.
*/
class EntityReferenceTermMergeWebTestCase extends TermMergeWebTestCase {
/**
* Content type used for testing the entity reference field integration.
*
* @var string
*/
protected $content_type = 'term_merge_entity_reference';
/**
* Field definition array used for entity reference integration testing.
*
* @var array
*/
protected $field = array(
'type' => 'entityreference',
'field_name' => 'term_merge_entity_reference',
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
'settings' => array(
'target_type' => 'taxonomy_term',
'handler' => 'base',
'handler_settings' => array(),
),
);
/**
* Instance definition array used for entity reference integration testing.
*
* @var array
*/
protected $instance = array();
/**
* GetInfo method.
*/
public static function getInfo() {
return array(
'name' => 'Term Merge Entity Reference',
'description' => 'Ensure that the module Term Merge integrates with Entity Reference field type correctly.',
'group' => 'Term Merge',
);
}
public function setUp() {
$modules = $this
->normalizeSetUpArguments(func_get_args());
$modules[] = 'entityreference';
parent::setUp($modules);
$this
->drupalPost('admin/structure/types/add', array(
'name' => $this
->randomName(),
'type' => $this->content_type,
), 'Save content type');
$this->field = field_create_field($this->field);
$this->instance['field_name'] = $this->field['field_name'];
$this->instance['entity_type'] = 'node';
$this->instance['bundle'] = $this->content_type;
$this->instance['label'] = $this
->randomName();
$this->instance = field_create_instance($this->instance);
$this->instance = field_info_instance($this->instance['entity_type'], $this->instance['field_name'], $this->instance['bundle']);
}
/**
* Verify that entity reference field values get update upon term merging.
*/
public function testEntityReferenceField() {
$terms = array(
'trunk' => NULL,
'branch' => NULL,
);
$nodes = array();
foreach ($terms as $type => $v) {
$terms[$type] = (object) array(
'vid' => $this->vocabulary->vid,
'name' => $this
->randomName(),
);
taxonomy_term_save($terms[$type]);
$nodes[$type] = (object) array(
'type' => $this->content_type,
'title' => $this
->randomName(),
$this->field['field_name'] => array(
LANGUAGE_NONE => array(
array(
'target_id' => $terms[$type]->tid,
),
),
),
);
node_save($nodes[$type]);
}
actions_do('term_merge_action', $terms['branch'], array(
'term_trunk' => $terms['trunk']->tid,
'term_branch_keep' => FALSE,
));
foreach ($nodes as $type => $node) {
$node = entity_load_unchanged('node', $node->nid);
$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.');
}
}
}
Classes
Name | Description |
---|---|
DuplicatesTermMergeWebTestCase | Test the Merge Duplicate Terms feature of the Term Merge module. |
EntityReferenceTermMergeWebTestCase | Test integration with Entity Reference module. |
RedirectTermMergeWebTestCase | Test the integration between Term Merge module and Path/Redirect modules. |
SynonymsTermMergeWebTestCase | Test the integration between Term Merge module and Synonyms module. |
TermMergeTermMergeWebTestCase | Test the functionality of Term Merge module. |
TermMergeWebTestCase | Base class for all tests of Term Merge module. |
ViewsTermMergeWebTestCase | Test the integration between Term Merge module and Views module. |