Newer
Older
<?php
Dries Buytaert
committed
/**
* @file
* Tests for search.module.
*/
// The search index can contain different types of content. Typically the type is 'node'.
// Here we test with _test_ and _test2_ as the type.
define('SEARCH_TYPE', '_test_');
define('SEARCH_TYPE_2', '_test2_');
Dries Buytaert
committed
define('SEARCH_TYPE_JPN', '_test3_');
class SearchMatchTestCase extends DrupalWebTestCase {
Angie Byron
committed
public static function getInfo() {
return array(
'name' => 'Search engine queries',
'description' => 'Indexes content and queries it.',
'group' => 'Search',
);
}
/**
* Implementation setUp().
*/
function setUp() {
parent::setUp('search');
}
/**
* Test search indexing.
*/
function testMatching() {
$this->_setup();
$this->_testQueries();
}
/**
* Set up a small index of items to test against.
*/
function _setup() {
variable_set('minimum_word_size', 3);
for ($i = 1; $i <= 7; ++$i) {
search_index($i, SEARCH_TYPE, $this->getText($i));
}
for ($i = 1; $i <= 5; ++$i) {
search_index($i + 7, SEARCH_TYPE_2, $this->getText2($i));
}
Dries Buytaert
committed
// No getText builder function for Japanese text; just a simple array.
foreach (array(
13 => '以呂波耳・ほへとち。リヌルヲ。',
14 => 'ドルーパルが大好きよ!',
15 => 'コーヒーとケーキ',
) as $i => $jpn) {
search_index($i, SEARCH_TYPE_JPN, $jpn);
}
search_update_totals();
}
/**
* _test_: Helper method for generating snippets of content.
*
* Generated items to test against:
* 1 ipsum
* 2 dolore sit
* 3 sit am ut
* 4 am ut enim am
* 5 ut enim am minim veniam
* 6 enim am minim veniam es cillum
* 7 am minim veniam es cillum dolore eu
*/
function getText($n) {
$words = explode(' ', "Ipsum dolore sit am. Ut enim am minim veniam. Es cillum dolore eu.");
return implode(' ', array_slice($words, $n - 1, $n));
}
/**
* _test2_: Helper method for generating snippets of content.
*
* Generated items to test against:
* 8 dear
* 9 king philip
* 10 philip came over
* 11 came over from germany
* 12 over from germany swimming
*/
function getText2($n) {
$words = explode(' ', "Dear King Philip came over from Germany swimming.");
return implode(' ', array_slice($words, $n - 1, $n));
}
/**
* Run predefine queries looking for indexed terms.
*/
function _testQueries() {
/*
Note: OR queries that include short words in OR groups are only accepted
if the ORed terms are ANDed with at least one long word in the rest of the query.
e.g. enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut) -> good
e.g. dolore OR ut = (dolore) OR (ut) -> bad
This is a design limitation to avoid full table scans.
*/
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
$queries = array(
// Simple AND queries.
'ipsum' => array(1),
'enim' => array(4, 5, 6),
'xxxxx' => array(),
'enim minim' => array(5, 6),
'enim xxxxx' => array(),
'dolore eu' => array(7),
'dolore xx' => array(),
'ut minim' => array(5),
'xx minim' => array(),
'enim veniam am minim ut' => array(5),
// Simple OR queries.
'dolore OR ipsum' => array(1, 2, 7),
'dolore OR xxxxx' => array(2, 7),
'dolore OR ipsum OR enim' => array(1, 2, 4, 5, 6, 7),
'ipsum OR dolore sit OR cillum' => array(2, 7),
'minim dolore OR ipsum' => array(7),
'dolore OR ipsum veniam' => array(7),
'minim dolore OR ipsum OR enim' => array(5, 6, 7),
'dolore xx OR yy' => array(),
'xxxxx dolore OR ipsum' => array(),
// Negative queries.
'dolore -sit' => array(7),
'dolore -eu' => array(2),
'dolore -xxxxx' => array(2, 7),
'dolore -xx' => array(2, 7),
// Phrase queries.
'"dolore sit"' => array(2),
'"sit dolore"' => array(),
'"am minim veniam es"' => array(6, 7),
'"minim am veniam es"' => array(),
// Mixed queries.
'"am minim veniam es" OR dolore' => array(2, 6, 7),
'"minim am veniam es" OR "dolore sit"' => array(2),
'"minim am veniam es" OR "sit dolore"' => array(),
'"am minim veniam es" -eu' => array(6),
'"am minim veniam" -"cillum dolore"' => array(5, 6),
'"am minim veniam" -"dolore cillum"' => array(5, 6, 7),
'xxxxx "minim am veniam es" OR dolore' => array(),
'xx "minim am veniam es" OR dolore' => array()
);
foreach ($queries as $query => $results) {
Dries Buytaert
committed
$result = db_select('search_index', 'i')
->extend('SearchQuery')
->searchExpression($query, SEARCH_TYPE)
->execute();
$set = $result ? $result->fetchAll() : array();
$this->_testQueryMatching($query, $set, $results);
$this->_testQueryScores($query, $set, $results);
}
// These queries are run against the second index type, SEARCH_TYPE_2.
$queries = array(
// Simple AND queries.
'ipsum' => array(),
'enim' => array(),
'enim minim' => array(),
'dear' => array(8),
'germany' => array(11, 12),
);
foreach ($queries as $query => $results) {
Dries Buytaert
committed
$result = db_select('search_index', 'i')
->extend('SearchQuery')
->searchExpression($query, SEARCH_TYPE_2)
->execute();
$set = $result ? $result->fetchAll() : array();
$this->_testQueryMatching($query, $set, $results);
$this->_testQueryScores($query, $set, $results);
}
Dries Buytaert
committed
Dries Buytaert
committed
// These queries are run against the third index type, SEARCH_TYPE_JPN.
$queries = array(
// Simple AND queries.
'呂波耳' => array(13),
'以呂波耳' => array(13),
'ほへと ヌルヲ' => array(13),
'とちリ' => array(),
'ドルーパル' => array(14),
'パルが大' => array(14),
'コーヒー' => array(15),
'ヒーキ' => array(),
);
foreach ($queries as $query => $results) {
$result = db_select('search_index', 'i')
->extend('SearchQuery')
->searchExpression($query, SEARCH_TYPE_JPN)
->execute();
$set = $result ? $result->fetchAll() : array();
$this->_testQueryMatching($query, $set, $results);
$this->_testQueryScores($query, $set, $results);
Dries Buytaert
committed
}
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
}
/**
* Test the matching abilities of the engine.
*
* Verify if a query produces the correct results.
*/
function _testQueryMatching($query, $set, $results) {
// Get result IDs.
$found = array();
foreach ($set as $item) {
$found[] = $item->sid;
}
// Compare $results and $found.
sort($found);
sort($results);
$this->assertEqual($found, $results, "Query matching '$query'");
}
/**
* Test the scoring abilities of the engine.
*
* Verify if a query produces normalized, monotonous scores.
*/
function _testQueryScores($query, $set, $results) {
// Get result scores.
$scores = array();
foreach ($set as $item) {
$scores[] = $item->calculated_score;
}
// Check order.
$sorted = $scores;
sort($sorted);
$this->assertEqual($scores, array_reverse($sorted), "Query order '$query'");
// Check range.
$this->assertEqual(!count($scores) || (min($scores) > 0.0 && max($scores) <= 1.0001), TRUE, "Query scoring '$query'");
}
}
Dries Buytaert
committed
Dries Buytaert
committed
/**
* Tests the bike shed text on no results page, and text on the search page.
*/
class SearchPageText extends DrupalWebTestCase {
Dries Buytaert
committed
protected $searching_user;
Angie Byron
committed
public static function getInfo() {
Dries Buytaert
committed
return array(
Dries Buytaert
committed
'name' => 'Search page text',
'description' => 'Tests the bike shed text on the no results page, and various other text on search pages.',
'group' => 'Search'
Dries Buytaert
committed
);
}
function setUp() {
parent::setUp('search');
// Create user.
Dries Buytaert
committed
$this->searching_user = $this->drupalCreateUser(array('search content', 'access user profiles'));
Dries Buytaert
committed
}
Dries Buytaert
committed
Dries Buytaert
committed
/**
* Tests the failed search text, and various other text on the search page.
*/
function testSearchText() {
Dries Buytaert
committed
$this->drupalLogin($this->searching_user);
$this->drupalGet('search/node');
$this->assertText(t('Enter your keywords'));
Dries Buytaert
committed
$this->assertText(t('Search'));
$title = t('Search') . ' | Drupal';
$this->assertTitle($title, 'Search page title is correct');
Dries Buytaert
committed
$edit = array();
$edit['keys'] = 'bike shed ' . $this->randomName();
$this->drupalPost('search/node', $edit, t('Search'));
$this->assertText(t('Consider loosening your query with OR. bike OR shed will often show more results than bike shed.'), t('Help text is displayed when search returns no results.'));
Dries Buytaert
committed
$this->assertText(t('Search'));
$this->assertTitle($title, 'Search page title is correct');
$edit['keys'] = $this->searching_user->name;
$this->drupalPost('search/user', $edit, t('Search'));
$this->assertText(t('Search'));
$this->assertTitle($title, 'Search page title is correct');
Angie Byron
committed
// Test that search keywords containing slashes are correctly loaded
// from the path and displayed in the search form.
$arg = $this->randomName() . '/' . $this->randomName();
$this->drupalGet('search/node/' . $arg);
$input = $this->xpath("//input[@id='edit-keys' and @value='{$arg}']");
$this->assertFalse(empty($input), 'Search keys with a / are correctly set as the default value in the search box.');
Dries Buytaert
committed
}
}
class SearchAdvancedSearchForm extends DrupalWebTestCase {
protected $node;
Angie Byron
committed
public static function getInfo() {
return array(
'name' => 'Advanced search form',
'description' => 'Indexes content and tests the advanced search form.',
'group' => 'Search',
);
}
function setUp() {
parent::setUp('search');
// Create and login user.
$test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search', 'administer nodes'));
$this->drupalLogin($test_user);
// Create initial node.
$node = $this->drupalCreateNode();
$this->node = $this->drupalCreateNode();
// First update the index. This does the initial processing.
node_update_index();
// Then, run the shutdown function. Testing is a unique case where indexing
// and searching has to happen in the same request, so running the shutdown
// function manually is needed to finish the indexing process.
search_update_totals();
}
/**
* Test using the search form with GET and POST queries.
Angie Byron
committed
* Test using the advanced search form to limit search to nodes of type "Basic page".
*/
function testNodeType() {
$this->assertTrue($this->node->type == 'page', t('Node type is Basic page.'));
// Assert that the dummy title doesn't equal the real title.
$dummy_title = 'Lorem ipsum';
$this->assertNotEqual($dummy_title, $this->node->title, t("Dummy title doesn't equal node title"));
// Search for the dummy title with a GET query.
Dries Buytaert
committed
$this->drupalGet('search/node/' . $dummy_title);
$this->assertNoText($this->node->title, t('Basic page node is not found with dummy title.'));
// Search for the title of the node with a GET query.
$this->drupalGet('search/node/' . $this->node->title);
$this->assertText($this->node->title, t('Basic page node is found with GET query.'));
// Search for the title of the node with a POST query.
$edit = array('or' => $this->node->title);
$this->drupalPost('search/node', $edit, t('Advanced search'));
$this->assertText($this->node->title, t('Basic page node is found with POST query.'));
// Advanced search type option.
$this->drupalPost('search/node', array_merge($edit, array('type[page]' => 'page')), t('Advanced search'));
$this->assertText($this->node->title, t('Basic page node is found with POST query and type:page.'));
$this->drupalPost('search/node', array_merge($edit, array('type[article]' => 'article')), t('Advanced search'));
$this->assertText('bike shed', t('Article node is not found with POST query and type:article.'));
}
}
Dries Buytaert
committed
class SearchRankingTestCase extends DrupalWebTestCase {
Angie Byron
committed
public static function getInfo() {
Dries Buytaert
committed
return array(
'name' => 'Search engine ranking',
'description' => 'Indexes content and tests ranking factors.',
'group' => 'Search',
Dries Buytaert
committed
);
}
/**
* Implementation setUp().
*/
function setUp() {
parent::setUp('search', 'statistics', 'comment');
Dries Buytaert
committed
}
function testRankings() {
// Login with sufficient privileges.
$this->drupalLogin($this->drupalCreateUser(array('skip comment approval', 'create page content')));
Dries Buytaert
committed
// Build a list of the rankings to test.
$node_ranks = array('sticky', 'promote', 'relevance', 'recent', 'comments', 'views');
// Create nodes for testing.
foreach ($node_ranks as $node_rank) {
$settings = array(
'type' => 'page',
'title' => 'Drupal rocks',
'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))),
);
Dries Buytaert
committed
foreach (array(0, 1) as $num) {
if ($num == 1) {
switch ($node_rank) {
case 'sticky':
case 'promote':
$settings[$node_rank] = 1;
break;
case 'relevance':
$settings['body'][LANGUAGE_NONE][0]['value'] .= " really rocks";
Dries Buytaert
committed
break;
case 'recent':
Dries Buytaert
committed
$settings['created'] = REQUEST_TIME + 3600;
Dries Buytaert
committed
break;
case 'comments':
$settings['comment'] = 2;
break;
}
}
$nodes[$node_rank][$num] = $this->drupalCreateNode($settings);
}
}
// Update the search index.
module_invoke_all('update_index');
Dries Buytaert
committed
search_update_totals();
// Refresh variables after the treatment.
$this->refreshVariables();
Dries Buytaert
committed
// Add a comment to one of the nodes.
$edit = array();
$edit['subject'] = 'my comment title';
$edit['comment_body[' . LANGUAGE_NONE . '][0][value]'] = 'some random comment';
$this->drupalGet('comment/reply/' . $nodes['comments'][1]->nid);
Dries Buytaert
committed
$this->drupalPost(NULL, $edit, t('Preview'));
$this->drupalPost(NULL, $edit, t('Save'));
// Enable counting of statistics.
variable_set('statistics_count_content_views', 1);
// Then View one of the nodes a bunch of times.
Dries Buytaert
committed
$this->drupalGet('node/' . $nodes['views'][1]->nid);
}
// Test each of the possible rankings.
foreach ($node_ranks as $node_rank) {
// Disable all relevancy rankings except the one we are testing.
foreach ($node_ranks as $var) {
variable_set('node_rank_' . $var, $var == $node_rank ? 10 : 0);
Dries Buytaert
committed
}
// Do the search and assert the results.
Dries Buytaert
committed
$set = node_search_execute('rocks');
$this->assertEqual($set[0]['node']->nid, $nodes[$node_rank][1]->nid, 'Search ranking "' . $node_rank . '" order.');
Dries Buytaert
committed
}
}
Dries Buytaert
committed
/**
* Test rankings of HTML tags.
*/
function testHTMLRankings() {
// Login with sufficient privileges.
$this->drupalLogin($this->drupalCreateUser(array('create page content')));
Dries Buytaert
committed
// Test HTML tags with different weights.
$sorted_tags = array('h1', 'h2', 'h3', 'h4', 'a', 'h5', 'h6', 'notag');
$shuffled_tags = $sorted_tags;
// Shuffle tags to ensure HTML tags are ranked properly.
shuffle($shuffled_tags);
$settings = array(
'type' => 'page',
'title' => 'Simple node',
);
foreach ($shuffled_tags as $tag) {
switch ($tag) {
case 'a':
$settings['body'] = array(LANGUAGE_NONE => array(array('value' => l('Drupal Rocks', 'node'), 'format' => 'full_html')));
break;
case 'notag':
$settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'Drupal Rocks')));
break;
default:
$settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks</$tag>", 'format' => 'full_html')));
break;
}
$nodes[$tag] = $this->drupalCreateNode($settings);
}
// Update the search index.
module_invoke_all('update_index');
search_update_totals();
// Refresh variables after the treatment.
$this->refreshVariables();
Dries Buytaert
committed
// Disable all other rankings.
$node_ranks = array('sticky', 'promote', 'recent', 'comments', 'views');
foreach ($node_ranks as $node_rank) {
variable_set('node_rank_' . $node_rank, 0);
}
$set = node_search_execute('rocks');
// Test the ranking of each tag.
foreach ($sorted_tags as $tag_rank => $tag) {
// Assert the results.
if ($tag == 'notag') {
$this->assertEqual($set[$tag_rank]['node']->nid, $nodes[$tag]->nid, 'Search tag ranking for plain text order.');
$this->assertEqual($set[$tag_rank]['node']->nid, $nodes[$tag]->nid, 'Search tag ranking for "<' . $sorted_tags[$tag_rank] . '>" order.');
}
}
// Test tags with the same weight against the sorted tags.
$unsorted_tags = array('u', 'b', 'i', 'strong', 'em');
foreach ($unsorted_tags as $tag) {
$settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks</$tag>", 'format' => 'full_html')));
$node = $this->drupalCreateNode($settings);
// Update the search index.
module_invoke_all('update_index');
search_update_totals();
// Refresh variables after the treatment.
$this->refreshVariables();
$set = node_search_execute('rocks');
// Ranking should always be second to last.
$set = array_slice($set, -2, 1);
// Assert the results.
$this->assertEqual($set[0]['node']->nid, $node->nid, 'Search tag ranking for "<' . $tag . '>" order.');
Dries Buytaert
committed
// Delete node so it doesn't show up in subsequent search results.
node_delete($node->nid);
}
}
Dries Buytaert
committed
/**
* Verifies that if we combine two rankings, search still works.
*
* See issue http://drupal.org/node/771596
*/
function testDoubleRankings() {
// Login with sufficient privileges.
$this->drupalLogin($this->drupalCreateUser(array('skip comment approval', 'create page content')));
Dries Buytaert
committed
// See testRankings() above - build a node that will rank high for sticky.
$settings = array(
Angie Byron
committed
'type' => 'page',
'title' => 'Drupal rocks',
Dries Buytaert
committed
'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))),
'sticky' => 1,
);
$node = $this->drupalCreateNode($settings);
// Update the search index.
module_invoke_all('update_index');
search_update_totals();
// Refresh variables after the treatment.
$this->refreshVariables();
// Set up for ranking sticky and lots of comments; make sure others are
// disabled.
$node_ranks = array('sticky', 'promote', 'relevance', 'recent', 'comments', 'views');
foreach ($node_ranks as $var) {
$value = ($var == 'sticky' || $var == 'comments') ? 10 : 0;
variable_set('node_rank_' . $var, $value);
}
// Do the search and assert the results.
$set = node_search_execute('rocks');
$this->assertEqual($set[0]['node']->nid, $node->nid, 'Search double ranking order.');
}
Dries Buytaert
committed
}
Dries Buytaert
committed
class SearchBlockTestCase extends DrupalWebTestCase {
Angie Byron
committed
public static function getInfo() {
Dries Buytaert
committed
return array(
'name' => 'Block availability',
'description' => 'Check if the search form block is available.',
'group' => 'Search',
Dries Buytaert
committed
);
}
function setUp() {
parent::setUp('search');
// Create and login user
Angie Byron
committed
$admin_user = $this->drupalCreateUser(array('administer blocks', 'search content'));
Dries Buytaert
committed
$this->drupalLogin($admin_user);
}
function testSearchFormBlock() {
// Set block title to confirm that the interface is available.
Angie Byron
committed
$this->drupalPost('admin/structure/block/manage/search/form/configure', array('title' => $this->randomName(8)), t('Save block'));
$this->assertText(t('The block configuration has been saved.'), t('Block configuration set.'));
Dries Buytaert
committed
// Set the block to a region to confirm block is available.
Dries Buytaert
committed
$edit = array();
$edit['blocks[search_form][region]'] = 'footer';
Dries Buytaert
committed
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
$this->assertText(t('The block settings have been updated.'), t('Block successfully move to footer region.'));
Dries Buytaert
committed
}
Angie Byron
committed
/**
* Test that the search block form works correctly.
*/
function testBlock() {
// Enable the block, and place it in the 'content' region so that it isn't
// hidden on 404 pages.
$edit = array('blocks[search_form][region]' => 'content');
Dries Buytaert
committed
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
Angie Byron
committed
// Test a normal search via the block form, from the front page.
$terms = array('search_block_form' => 'test');
$this->drupalPost('node', $terms, t('Search'));
$this->assertText('Your search yielded no results');
// Test a search from the block on a 404 page.
$this->drupalGet('foo');
$this->assertResponse(404);
$this->drupalPost(NULL, $terms, t('Search'));
Angie Byron
committed
$this->assertText('Your search yielded no results');
// Test a search from the block when it doesn't appear on the search page.
$edit = array('pages' => 'search');
Angie Byron
committed
$this->drupalPost('admin/structure/block/manage/search/form/configure', $edit, t('Save block'));
Angie Byron
committed
$this->drupalPost('node', $terms, t('Search'));
$this->assertText('Your search yielded no results');
Angie Byron
committed
// Confirm that the user is redirected to the search page.
$this->assertEqual(
$this->getUrl(),
url('search/node/' . $terms['search_block_form'], array('absolute' => TRUE)),
t('Redirected to correct url.')
);
// Test an empty search via the block form, from the front page.
$terms = array('search_block_form' => '');
$this->drupalPost('node', $terms, t('Search'));
$this->assertText('Please enter some keywords');
// Confirm that the user is redirected to the search page, when form is submitted empty.
$this->assertEqual(
$this->getUrl(),
url('search/node/', array('absolute' => TRUE)),
t('Redirected to correct url.')
);
Angie Byron
committed
}
Dries Buytaert
committed
}
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
/**
* Tests that searching for a phrase gets the correct page count.
*/
class SearchExactTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Search engine phrase queries',
'description' => 'Tests that searching for a phrase gets the correct page count.',
'group' => 'Search',
);
}
function setUp() {
parent::setUp('search');
}
/**
* Tests that the correct number of pager links are found for both keywords and phrases.
*/
function testExactQuery() {
// Login with sufficient privileges.
$this->drupalLogin($this->drupalCreateUser(array('create page content', 'search content')));
$settings = array(
'type' => 'page',
'title' => 'Simple Node',
);
// Create nodes with exact phrase.
for ($i = 0; $i <= 17; $i++) {
$settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'love pizza')));
$this->drupalCreateNode($settings);
}
// Create nodes containing keywords.
for ($i = 0; $i <= 17; $i++) {
$settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'love cheesy pizza')));
$this->drupalCreateNode($settings);
}
// Update the search index.
module_invoke_all('update_index');
search_update_totals();
// Refresh variables after the treatment.
$this->refreshVariables();
// Test that the correct number of pager links are found for keyword search.
$edit = array('keys' => 'love pizza');
$this->drupalPost('search/node', $edit, t('Search'));
$this->assertLinkByHref('page=1', 0, '2nd page link is found for keyword search.');
$this->assertLinkByHref('page=2', 0, '3rd page link is found for keyword search.');
$this->assertLinkByHref('page=3', 0, '4th page link is found for keyword search.');
$this->assertNoLinkByHref('page=4', '5th page link is not found for keyword search.');
// Test that the correct number of pager links are found for exact phrase search.
$edit = array('keys' => '"love pizza"');
$this->drupalPost('search/node', $edit, t('Search'));
$this->assertLinkByHref('page=1', 0, '2nd page link is found for exact phrase search.');
$this->assertNoLinkByHref('page=2', '3rd page link is not found for exact phrase search.');
}
}
/**
* Test integration searching comments.
*/
class SearchCommentTestCase extends DrupalWebTestCase {
protected $admin_user;
public static function getInfo() {
return array(
'name' => 'Comment Search tests',
'description' => 'Verify text formats and filters used elsewhere.',
'group' => 'Search',
);
}
function setUp() {
parent::setUp('comment', 'search');
Dries Buytaert
committed
// Create and log in an administrative user having access to the Full HTML
// text format.
Angie Byron
committed
$full_html_format = filter_format_load('full_html');
Angie Byron
committed
$permissions = array(
Dries Buytaert
committed
'administer filters',
Dries Buytaert
committed
filter_permission_name($full_html_format),
Dries Buytaert
committed
'administer permissions',
'create page content',
'skip comment approval',
Angie Byron
committed
'access comments',
);
$this->admin_user = $this->drupalCreateUser($permissions);
$this->drupalLogin($this->admin_user);
}
/**
* Verify that comments are rendered using proper format in search results.
*/
function testSearchResultsComment() {
Dries Buytaert
committed
$comment_body = 'Test comment body';
Dries Buytaert
committed
variable_set('comment_preview_article', DRUPAL_OPTIONAL);
// Enable check_plain() for 'Filtered HTML' text format.
Angie Byron
committed
$filtered_html_format_id = 'filtered_html';
'filters[filter_html_escape][status]' => TRUE,
$this->drupalPost('admin/config/content/formats/' . $filtered_html_format_id, $edit, t('Save configuration'));
// Allow anonymous users to search content.
$edit = array(
DRUPAL_ANONYMOUS_RID . '[search content]' => 1,
DRUPAL_ANONYMOUS_RID . '[access comments]' => 1,
DRUPAL_ANONYMOUS_RID . '[post comments]' => 1,
);
Angie Byron
committed
$this->drupalPost('admin/people/permissions', $edit, t('Save permissions'));
// Create a node.
$node = $this->drupalCreateNode(array('type' => 'article'));
// Post a comment using 'Full HTML' text format.
$edit_comment = array();
Dries Buytaert
committed
$edit_comment['subject'] = 'Test comment subject';
$edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = '<h1>' . $comment_body . '</h1>';
Angie Byron
committed
$full_html_format_id = 'full_html';
$edit_comment['comment_body[' . LANGUAGE_NONE . '][0][format]'] = $full_html_format_id;
$this->drupalPost('comment/reply/' . $node->nid, $edit_comment, t('Save'));
// Invoke search index update.
$this->drupalLogout();
Angie Byron
committed
$this->cronRun();
// Search for the comment subject.
$edit = array(
Dries Buytaert
committed
'search_block_form' => "'" . $edit_comment['subject'] . "'",
);
$this->drupalPost('', $edit, t('Search'));
$this->assertText($node->title, t('Node found in search results.'));
$this->assertText($edit_comment['subject'], t('Comment subject found in search results.'));
// Search for the comment body.
Dries Buytaert
committed
'search_block_form' => "'" . $comment_body . "'",
);
$this->drupalPost('', $edit, t('Search'));
$this->assertText($node->title, t('Node found in search results.'));
// Verify that comment is rendered using proper format.
$this->assertText($comment_body, t('Comment body text found in search results.'));
$this->assertNoRaw(t('n/a'), t('HTML in comment body is not hidden.'));
$this->assertNoRaw(check_plain($edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]']), t('HTML in comment body is not escaped.'));
Dries Buytaert
committed
// Hide comments.
$this->drupalLogin($this->admin_user);
$node->comment = 0;
node_save($node);
// Invoke search index update.
$this->drupalLogout();
Angie Byron
committed
$this->cronRun();
Dries Buytaert
committed
// Search for $title.
$this->drupalPost('', $edit, t('Search'));
$this->assertNoText($comment_body, t('Comment body text not found in search results.'));
Angie Byron
committed
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
/**
* Verify access rules for comment indexing with different permissions.
*/
function testSearchResultsCommentAccess() {
$comment_body = 'Test comment body';
$this->comment_subject = 'Test comment subject';
$this->admin_role = $this->admin_user->roles;
unset($this->admin_role[DRUPAL_AUTHENTICATED_RID]);
$this->admin_role = key($this->admin_role);
// Create a node.
variable_set('comment_preview_article', DRUPAL_OPTIONAL);
$this->node = $this->drupalCreateNode(array('type' => 'article'));
// Post a comment using 'Full HTML' text format.
$edit_comment = array();
$edit_comment['subject'] = $this->comment_subject;
$edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = '<h1>' . $comment_body . '</h1>';
$this->drupalPost('comment/reply/' . $this->node->nid, $edit_comment, t('Save'));
$this->drupalLogout();
$this->setRolePermissions(DRUPAL_ANONYMOUS_RID);
$this->checkCommentAccess('Anon user has search permission but no access comments permission, comments should not be indexed');
$this->setRolePermissions(DRUPAL_ANONYMOUS_RID, TRUE);
$this->checkCommentAccess('Anon user has search permission and access comments permission, comments should be indexed', TRUE);
$this->drupalLogin($this->admin_user);
$this->drupalGet('admin/people/permissions');
// Disable search access for authenticated user to test admin user.
$this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, FALSE, FALSE);
$this->setRolePermissions($this->admin_role);
$this->checkCommentAccess('Admin user has search permission but no access comments permission, comments should not be indexed');
$this->setRolePermissions($this->admin_role, TRUE);
$this->checkCommentAccess('Admin user has search permission and access comments permission, comments should be indexed', TRUE);
$this->setRolePermissions(DRUPAL_AUTHENTICATED_RID);
$this->checkCommentAccess('Authenticated user has search permission but no access comments permission, comments should not be indexed');
$this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE);
$this->checkCommentAccess('Authenticated user has search permission and access comments permission, comments should be indexed', TRUE);
// Verify that access comments permission is inherited from the
// authenticated role.
$this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE, FALSE);
$this->setRolePermissions($this->admin_role);
$this->checkCommentAccess('Admin user has search permission and no access comments permission, but comments should be indexed because admin user inherits authenticated user\'s permission to access comments', TRUE);
// Verify that search content permission is inherited from the authenticated
// role.
$this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE, TRUE);
$this->setRolePermissions($this->admin_role, TRUE, FALSE);
$this->checkCommentAccess('Admin user has access comments permission and no search permission, but comments should be indexed because admin user inherits authenticated user\'s permission to search', TRUE);
Dries Buytaert
committed
Angie Byron
committed
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
}
/**
* Set permissions for role.
*/
function setRolePermissions($rid, $access_comments = FALSE, $search_content = TRUE) {
$permissions = array(
'access comments' => $access_comments,
'search content' => $search_content,
);
user_role_change_permissions($rid, $permissions);
}
/**
* Update search index and search for comment.
*/
function checkCommentAccess($message, $assume_access = FALSE) {
// Invoke search index update.
search_touch_node($this->node->nid);
$this->cronRun();
// Search for the comment subject.
$edit = array(
'search_block_form' => "'" . $this->comment_subject . "'",
);
$this->drupalPost('', $edit, t('Search'));
$method = $assume_access ? 'assertText' : 'assertNoText';
$verb = $assume_access ? 'found' : 'not found';
$this->{$method}($this->node->title, "Node $verb in search results: " . $message);
$this->{$method}($this->comment_subject, "Comment subject $verb in search results: " . $message);
}
Angie Byron
committed
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
/**
* Verify that 'add new comment' does not appear in search results or index.
*/
function testAddNewComment() {
// Create a node with a short body.
$settings = array(
'type' => 'article',
'title' => 'short title',
'body' => array(LANGUAGE_NONE => array(array('value' => 'short body text'))),
);
$user = $this->drupalCreateUser(array('search content', 'create article content', 'access content'));
$this->drupalLogin($user);
$node = $this->drupalCreateNode($settings);
// Verify that if you view the node on its own page, 'add new comment'
// is there.
$this->drupalGet('node/' . $node->nid);
$this->assertText(t('Add new comment'), t('Add new comment appears on node page'));
// Run cron to index this page.
$this->drupalLogout();
$this->cronRun();
// Search for 'comment'. Should be no results.
$this->drupalLogin($user);
$this->drupalPost('search/node', array('keys' => 'comment'), t('Search'));
$this->assertText(t('Your search yielded no results'), t('No results searching for the word comment'));
// Search for the node title. Should be found, and 'Add new comment' should
// not be part of the search snippet.
$this->drupalPost('search/node', array('keys' => 'short'), t('Search'));
$this->assertText($node->title, t('Search for keyword worked'));
$this->assertNoText(t('Add new comment'), t('Add new comment does not appear on search results page'));
}
Angie Byron
committed
/**
* Tests search_expression_insert() and search_expression_extract().
*
* @see http://drupal.org/node/419388 (issue)
*/
class SearchExpressionInsertExtractTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Search expression insert/extract',
'description' => 'Tests the functions search_expression_insert() and search_expression_extract()',
'group' => 'Search',
);
}
function setUp() {
Dries Buytaert
committed
drupal_load('module', 'search');
parent::setUp();
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
}
/**
* Tests search_expression_insert() and search_expression_extract().
*/
function testInsertExtract() {
$base_expression = "mykeyword";
// Build an array of option, value, what should be in the expression, what
// should be retrieved from expression.
$cases = array(
array('foo', 'bar', 'foo:bar', 'bar'), // Normal case.
array('foo', NULL, '', NULL), // Empty value: shouldn't insert.
array('foo', ' ', 'foo:', ''), // Space as value: should insert but retrieve empty string.
array('foo', '', 'foo:', ''), // Empty string as value: should insert but retrieve empty string.
array('foo', '0', 'foo:0', '0'), // String zero as value: should insert.
array('foo', 0, 'foo:0', '0'), // Numeric zero as value: should insert.
);
foreach ($cases as $index => $case) {
$after_insert = search_expression_insert($base_expression, $case[0], $case[1]);
if (empty($case[2])) {
$this->assertEqual($after_insert, $base_expression, "Empty insert does not change expression in case $index");
}
else {
$this->assertEqual($after_insert, $base_expression . ' ' . $case[2], "Insert added correct expression for case $index");
}
$retrieved = search_expression_extract($after_insert, $case[0]);
if (!isset($case[3])) {
$this->assertFalse(isset($retrieved), "Empty retrieval results in unset value in case $index");
}
else {
$this->assertEqual($retrieved, $case[3], "Value is retrieved for case $index");
}
$after_clear = search_expression_insert($after_insert, $case[0]);
$this->assertEqual(trim($after_clear), $base_expression, "After clearing, base expression is restored for case $index");
$cleared = search_expression_extract($after_clear, $case[0]);
$this->assertFalse(isset($cleared), "After clearing, value could not be retrieved for case $index");
}
}
}
Dries Buytaert
committed
/**
* Tests that comment count display toggles properly on comment status of node
Angie Byron
committed
*
Dries Buytaert
committed
* Issue 537278