|
1 <?php |
|
2 |
|
3 /** |
|
4 * @file |
|
5 * Tests for search.module. |
|
6 */ |
|
7 |
|
8 // The search index can contain different types of content. Typically the type is 'node'. |
|
9 // Here we test with _test_ and _test2_ as the type. |
|
10 define('SEARCH_TYPE', '_test_'); |
|
11 define('SEARCH_TYPE_2', '_test2_'); |
|
12 define('SEARCH_TYPE_JPN', '_test3_'); |
|
13 |
|
14 /** |
|
15 * Indexes content and queries it. |
|
16 */ |
|
17 class SearchMatchTestCase extends DrupalWebTestCase { |
|
18 public static function getInfo() { |
|
19 return array( |
|
20 'name' => 'Search engine queries', |
|
21 'description' => 'Indexes content and queries it.', |
|
22 'group' => 'Search', |
|
23 ); |
|
24 } |
|
25 |
|
26 /** |
|
27 * Implementation setUp(). |
|
28 */ |
|
29 function setUp() { |
|
30 parent::setUp('search'); |
|
31 } |
|
32 |
|
33 /** |
|
34 * Test search indexing. |
|
35 */ |
|
36 function testMatching() { |
|
37 $this->_setup(); |
|
38 $this->_testQueries(); |
|
39 } |
|
40 |
|
41 /** |
|
42 * Set up a small index of items to test against. |
|
43 */ |
|
44 function _setup() { |
|
45 variable_set('minimum_word_size', 3); |
|
46 |
|
47 for ($i = 1; $i <= 7; ++$i) { |
|
48 search_index($i, SEARCH_TYPE, $this->getText($i)); |
|
49 } |
|
50 for ($i = 1; $i <= 5; ++$i) { |
|
51 search_index($i + 7, SEARCH_TYPE_2, $this->getText2($i)); |
|
52 } |
|
53 // No getText builder function for Japanese text; just a simple array. |
|
54 foreach (array( |
|
55 13 => '以呂波耳・ほへとち。リヌルヲ。', |
|
56 14 => 'ドルーパルが大好きよ!', |
|
57 15 => 'コーヒーとケーキ', |
|
58 ) as $i => $jpn) { |
|
59 search_index($i, SEARCH_TYPE_JPN, $jpn); |
|
60 } |
|
61 search_update_totals(); |
|
62 } |
|
63 |
|
64 /** |
|
65 * _test_: Helper method for generating snippets of content. |
|
66 * |
|
67 * Generated items to test against: |
|
68 * 1 ipsum |
|
69 * 2 dolore sit |
|
70 * 3 sit am ut |
|
71 * 4 am ut enim am |
|
72 * 5 ut enim am minim veniam |
|
73 * 6 enim am minim veniam es cillum |
|
74 * 7 am minim veniam es cillum dolore eu |
|
75 */ |
|
76 function getText($n) { |
|
77 $words = explode(' ', "Ipsum dolore sit am. Ut enim am minim veniam. Es cillum dolore eu."); |
|
78 return implode(' ', array_slice($words, $n - 1, $n)); |
|
79 } |
|
80 |
|
81 /** |
|
82 * _test2_: Helper method for generating snippets of content. |
|
83 * |
|
84 * Generated items to test against: |
|
85 * 8 dear |
|
86 * 9 king philip |
|
87 * 10 philip came over |
|
88 * 11 came over from germany |
|
89 * 12 over from germany swimming |
|
90 */ |
|
91 function getText2($n) { |
|
92 $words = explode(' ', "Dear King Philip came over from Germany swimming."); |
|
93 return implode(' ', array_slice($words, $n - 1, $n)); |
|
94 } |
|
95 |
|
96 /** |
|
97 * Run predefine queries looking for indexed terms. |
|
98 */ |
|
99 function _testQueries() { |
|
100 /* |
|
101 Note: OR queries that include short words in OR groups are only accepted |
|
102 if the ORed terms are ANDed with at least one long word in the rest of the query. |
|
103 |
|
104 e.g. enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut) -> good |
|
105 e.g. dolore OR ut = (dolore) OR (ut) -> bad |
|
106 |
|
107 This is a design limitation to avoid full table scans. |
|
108 */ |
|
109 $queries = array( |
|
110 // Simple AND queries. |
|
111 'ipsum' => array(1), |
|
112 'enim' => array(4, 5, 6), |
|
113 'xxxxx' => array(), |
|
114 'enim minim' => array(5, 6), |
|
115 'enim xxxxx' => array(), |
|
116 'dolore eu' => array(7), |
|
117 'dolore xx' => array(), |
|
118 'ut minim' => array(5), |
|
119 'xx minim' => array(), |
|
120 'enim veniam am minim ut' => array(5), |
|
121 // Simple OR queries. |
|
122 'dolore OR ipsum' => array(1, 2, 7), |
|
123 'dolore OR xxxxx' => array(2, 7), |
|
124 'dolore OR ipsum OR enim' => array(1, 2, 4, 5, 6, 7), |
|
125 'ipsum OR dolore sit OR cillum' => array(2, 7), |
|
126 'minim dolore OR ipsum' => array(7), |
|
127 'dolore OR ipsum veniam' => array(7), |
|
128 'minim dolore OR ipsum OR enim' => array(5, 6, 7), |
|
129 'dolore xx OR yy' => array(), |
|
130 'xxxxx dolore OR ipsum' => array(), |
|
131 // Negative queries. |
|
132 'dolore -sit' => array(7), |
|
133 'dolore -eu' => array(2), |
|
134 'dolore -xxxxx' => array(2, 7), |
|
135 'dolore -xx' => array(2, 7), |
|
136 // Phrase queries. |
|
137 '"dolore sit"' => array(2), |
|
138 '"sit dolore"' => array(), |
|
139 '"am minim veniam es"' => array(6, 7), |
|
140 '"minim am veniam es"' => array(), |
|
141 // Mixed queries. |
|
142 '"am minim veniam es" OR dolore' => array(2, 6, 7), |
|
143 '"minim am veniam es" OR "dolore sit"' => array(2), |
|
144 '"minim am veniam es" OR "sit dolore"' => array(), |
|
145 '"am minim veniam es" -eu' => array(6), |
|
146 '"am minim veniam" -"cillum dolore"' => array(5, 6), |
|
147 '"am minim veniam" -"dolore cillum"' => array(5, 6, 7), |
|
148 'xxxxx "minim am veniam es" OR dolore' => array(), |
|
149 'xx "minim am veniam es" OR dolore' => array() |
|
150 ); |
|
151 foreach ($queries as $query => $results) { |
|
152 $result = db_select('search_index', 'i') |
|
153 ->extend('SearchQuery') |
|
154 ->searchExpression($query, SEARCH_TYPE) |
|
155 ->execute(); |
|
156 |
|
157 $set = $result ? $result->fetchAll() : array(); |
|
158 $this->_testQueryMatching($query, $set, $results); |
|
159 $this->_testQueryScores($query, $set, $results); |
|
160 } |
|
161 |
|
162 // These queries are run against the second index type, SEARCH_TYPE_2. |
|
163 $queries = array( |
|
164 // Simple AND queries. |
|
165 'ipsum' => array(), |
|
166 'enim' => array(), |
|
167 'enim minim' => array(), |
|
168 'dear' => array(8), |
|
169 'germany' => array(11, 12), |
|
170 ); |
|
171 foreach ($queries as $query => $results) { |
|
172 $result = db_select('search_index', 'i') |
|
173 ->extend('SearchQuery') |
|
174 ->searchExpression($query, SEARCH_TYPE_2) |
|
175 ->execute(); |
|
176 |
|
177 $set = $result ? $result->fetchAll() : array(); |
|
178 $this->_testQueryMatching($query, $set, $results); |
|
179 $this->_testQueryScores($query, $set, $results); |
|
180 } |
|
181 |
|
182 // These queries are run against the third index type, SEARCH_TYPE_JPN. |
|
183 $queries = array( |
|
184 // Simple AND queries. |
|
185 '呂波耳' => array(13), |
|
186 '以呂波耳' => array(13), |
|
187 'ほへと ヌルヲ' => array(13), |
|
188 'とちリ' => array(), |
|
189 'ドルーパル' => array(14), |
|
190 'パルが大' => array(14), |
|
191 'コーヒー' => array(15), |
|
192 'ヒーキ' => array(), |
|
193 ); |
|
194 foreach ($queries as $query => $results) { |
|
195 $result = db_select('search_index', 'i') |
|
196 ->extend('SearchQuery') |
|
197 ->searchExpression($query, SEARCH_TYPE_JPN) |
|
198 ->execute(); |
|
199 |
|
200 $set = $result ? $result->fetchAll() : array(); |
|
201 $this->_testQueryMatching($query, $set, $results); |
|
202 $this->_testQueryScores($query, $set, $results); |
|
203 } |
|
204 } |
|
205 |
|
206 /** |
|
207 * Test the matching abilities of the engine. |
|
208 * |
|
209 * Verify if a query produces the correct results. |
|
210 */ |
|
211 function _testQueryMatching($query, $set, $results) { |
|
212 // Get result IDs. |
|
213 $found = array(); |
|
214 foreach ($set as $item) { |
|
215 $found[] = $item->sid; |
|
216 } |
|
217 |
|
218 // Compare $results and $found. |
|
219 sort($found); |
|
220 sort($results); |
|
221 $this->assertEqual($found, $results, "Query matching '$query'"); |
|
222 } |
|
223 |
|
224 /** |
|
225 * Test the scoring abilities of the engine. |
|
226 * |
|
227 * Verify if a query produces normalized, monotonous scores. |
|
228 */ |
|
229 function _testQueryScores($query, $set, $results) { |
|
230 // Get result scores. |
|
231 $scores = array(); |
|
232 foreach ($set as $item) { |
|
233 $scores[] = $item->calculated_score; |
|
234 } |
|
235 |
|
236 // Check order. |
|
237 $sorted = $scores; |
|
238 sort($sorted); |
|
239 $this->assertEqual($scores, array_reverse($sorted), "Query order '$query'"); |
|
240 |
|
241 // Check range. |
|
242 $this->assertEqual(!count($scores) || (min($scores) > 0.0 && max($scores) <= 1.0001), TRUE, "Query scoring '$query'"); |
|
243 } |
|
244 } |
|
245 |
|
246 /** |
|
247 * Tests the bike shed text on no results page, and text on the search page. |
|
248 */ |
|
249 class SearchPageText extends DrupalWebTestCase { |
|
250 protected $searching_user; |
|
251 |
|
252 public static function getInfo() { |
|
253 return array( |
|
254 'name' => 'Search page text', |
|
255 'description' => 'Tests the bike shed text on the no results page, and various other text on search pages.', |
|
256 'group' => 'Search' |
|
257 ); |
|
258 } |
|
259 |
|
260 function setUp() { |
|
261 parent::setUp('search'); |
|
262 |
|
263 // Create user. |
|
264 $this->searching_user = $this->drupalCreateUser(array('search content', 'access user profiles')); |
|
265 } |
|
266 |
|
267 /** |
|
268 * Tests the failed search text, and various other text on the search page. |
|
269 */ |
|
270 function testSearchText() { |
|
271 $this->drupalLogin($this->searching_user); |
|
272 $this->drupalGet('search/node'); |
|
273 $this->assertText(t('Enter your keywords')); |
|
274 $this->assertText(t('Search')); |
|
275 $title = t('Search') . ' | Drupal'; |
|
276 $this->assertTitle($title, 'Search page title is correct'); |
|
277 |
|
278 $edit = array(); |
|
279 $edit['keys'] = 'bike shed ' . $this->randomName(); |
|
280 $this->drupalPost('search/node', $edit, t('Search')); |
|
281 $this->assertText(t('Consider loosening your query with OR. bike OR shed will often show more results than bike shed.'), 'Help text is displayed when search returns no results.'); |
|
282 $this->assertText(t('Search')); |
|
283 $this->assertTitle($title, 'Search page title is correct'); |
|
284 |
|
285 $edit['keys'] = $this->searching_user->name; |
|
286 $this->drupalPost('search/user', $edit, t('Search')); |
|
287 $this->assertText(t('Search')); |
|
288 $this->assertTitle($title, 'Search page title is correct'); |
|
289 |
|
290 // Test that search keywords containing slashes are correctly loaded |
|
291 // from the path and displayed in the search form. |
|
292 $arg = $this->randomName() . '/' . $this->randomName(); |
|
293 $this->drupalGet('search/node/' . $arg); |
|
294 $input = $this->xpath("//input[@id='edit-keys' and @value='{$arg}']"); |
|
295 $this->assertFalse(empty($input), 'Search keys with a / are correctly set as the default value in the search box.'); |
|
296 |
|
297 // Test a search input exceeding the limit of AND/OR combinations to test |
|
298 // the Denial-of-Service protection. |
|
299 $limit = variable_get('search_and_or_limit', 7); |
|
300 $keys = array(); |
|
301 for ($i = 0; $i < $limit + 1; $i++) { |
|
302 $keys[] = $this->randomName(3); |
|
303 if ($i % 2 == 0) { |
|
304 $keys[] = 'OR'; |
|
305 } |
|
306 } |
|
307 $edit['keys'] = implode(' ', $keys); |
|
308 $this->drupalPost('search/node', $edit, t('Search')); |
|
309 $this->assertRaw(t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => $limit))); |
|
310 } |
|
311 } |
|
312 |
|
313 /** |
|
314 * Indexes content and tests the advanced search form. |
|
315 */ |
|
316 class SearchAdvancedSearchForm extends DrupalWebTestCase { |
|
317 protected $node; |
|
318 |
|
319 public static function getInfo() { |
|
320 return array( |
|
321 'name' => 'Advanced search form', |
|
322 'description' => 'Indexes content and tests the advanced search form.', |
|
323 'group' => 'Search', |
|
324 ); |
|
325 } |
|
326 |
|
327 function setUp() { |
|
328 parent::setUp('search'); |
|
329 // Create and login user. |
|
330 $test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search', 'administer nodes')); |
|
331 $this->drupalLogin($test_user); |
|
332 |
|
333 // Create initial node. |
|
334 $node = $this->drupalCreateNode(); |
|
335 $this->node = $this->drupalCreateNode(); |
|
336 |
|
337 // First update the index. This does the initial processing. |
|
338 node_update_index(); |
|
339 |
|
340 // Then, run the shutdown function. Testing is a unique case where indexing |
|
341 // and searching has to happen in the same request, so running the shutdown |
|
342 // function manually is needed to finish the indexing process. |
|
343 search_update_totals(); |
|
344 } |
|
345 |
|
346 /** |
|
347 * Test using the search form with GET and POST queries. |
|
348 * Test using the advanced search form to limit search to nodes of type "Basic page". |
|
349 */ |
|
350 function testNodeType() { |
|
351 $this->assertTrue($this->node->type == 'page', 'Node type is Basic page.'); |
|
352 |
|
353 // Assert that the dummy title doesn't equal the real title. |
|
354 $dummy_title = 'Lorem ipsum'; |
|
355 $this->assertNotEqual($dummy_title, $this->node->title, "Dummy title doesn't equal node title"); |
|
356 |
|
357 // Search for the dummy title with a GET query. |
|
358 $this->drupalGet('search/node/' . $dummy_title); |
|
359 $this->assertNoText($this->node->title, 'Basic page node is not found with dummy title.'); |
|
360 |
|
361 // Search for the title of the node with a GET query. |
|
362 $this->drupalGet('search/node/' . $this->node->title); |
|
363 $this->assertText($this->node->title, 'Basic page node is found with GET query.'); |
|
364 |
|
365 // Search for the title of the node with a POST query. |
|
366 $edit = array('or' => $this->node->title); |
|
367 $this->drupalPost('search/node', $edit, t('Advanced search')); |
|
368 $this->assertText($this->node->title, 'Basic page node is found with POST query.'); |
|
369 |
|
370 // Advanced search type option. |
|
371 $this->drupalPost('search/node', array_merge($edit, array('type[page]' => 'page')), t('Advanced search')); |
|
372 $this->assertText($this->node->title, 'Basic page node is found with POST query and type:page.'); |
|
373 |
|
374 $this->drupalPost('search/node', array_merge($edit, array('type[article]' => 'article')), t('Advanced search')); |
|
375 $this->assertText('bike shed', 'Article node is not found with POST query and type:article.'); |
|
376 } |
|
377 } |
|
378 |
|
379 /** |
|
380 * Indexes content and tests ranking factors. |
|
381 */ |
|
382 class SearchRankingTestCase extends DrupalWebTestCase { |
|
383 public static function getInfo() { |
|
384 return array( |
|
385 'name' => 'Search engine ranking', |
|
386 'description' => 'Indexes content and tests ranking factors.', |
|
387 'group' => 'Search', |
|
388 ); |
|
389 } |
|
390 |
|
391 /** |
|
392 * Implementation setUp(). |
|
393 */ |
|
394 function setUp() { |
|
395 parent::setUp('search', 'statistics', 'comment'); |
|
396 } |
|
397 |
|
398 function testRankings() { |
|
399 // Login with sufficient privileges. |
|
400 $this->drupalLogin($this->drupalCreateUser(array('skip comment approval', 'create page content'))); |
|
401 |
|
402 // Build a list of the rankings to test. |
|
403 $node_ranks = array('sticky', 'promote', 'relevance', 'recent', 'comments', 'views'); |
|
404 |
|
405 // Create nodes for testing. |
|
406 foreach ($node_ranks as $node_rank) { |
|
407 $settings = array( |
|
408 'type' => 'page', |
|
409 'title' => 'Drupal rocks', |
|
410 'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))), |
|
411 ); |
|
412 foreach (array(0, 1) as $num) { |
|
413 if ($num == 1) { |
|
414 switch ($node_rank) { |
|
415 case 'sticky': |
|
416 case 'promote': |
|
417 $settings[$node_rank] = 1; |
|
418 break; |
|
419 case 'relevance': |
|
420 $settings['body'][LANGUAGE_NONE][0]['value'] .= " really rocks"; |
|
421 break; |
|
422 case 'recent': |
|
423 $settings['created'] = REQUEST_TIME + 3600; |
|
424 break; |
|
425 case 'comments': |
|
426 $settings['comment'] = 2; |
|
427 break; |
|
428 } |
|
429 } |
|
430 $nodes[$node_rank][$num] = $this->drupalCreateNode($settings); |
|
431 } |
|
432 } |
|
433 |
|
434 // Update the search index. |
|
435 module_invoke_all('update_index'); |
|
436 search_update_totals(); |
|
437 |
|
438 // Refresh variables after the treatment. |
|
439 $this->refreshVariables(); |
|
440 |
|
441 // Add a comment to one of the nodes. |
|
442 $edit = array(); |
|
443 $edit['subject'] = 'my comment title'; |
|
444 $edit['comment_body[' . LANGUAGE_NONE . '][0][value]'] = 'some random comment'; |
|
445 $this->drupalGet('comment/reply/' . $nodes['comments'][1]->nid); |
|
446 $this->drupalPost(NULL, $edit, t('Preview')); |
|
447 $this->drupalPost(NULL, $edit, t('Save')); |
|
448 |
|
449 // Enable counting of statistics. |
|
450 variable_set('statistics_count_content_views', 1); |
|
451 |
|
452 // Then View one of the nodes a bunch of times. |
|
453 for ($i = 0; $i < 5; $i ++) { |
|
454 $this->drupalGet('node/' . $nodes['views'][1]->nid); |
|
455 } |
|
456 |
|
457 // Test each of the possible rankings. |
|
458 foreach ($node_ranks as $node_rank) { |
|
459 // Disable all relevancy rankings except the one we are testing. |
|
460 foreach ($node_ranks as $var) { |
|
461 variable_set('node_rank_' . $var, $var == $node_rank ? 10 : 0); |
|
462 } |
|
463 |
|
464 // Do the search and assert the results. |
|
465 $set = node_search_execute('rocks'); |
|
466 $this->assertEqual($set[0]['node']->nid, $nodes[$node_rank][1]->nid, 'Search ranking "' . $node_rank . '" order.'); |
|
467 } |
|
468 } |
|
469 |
|
470 /** |
|
471 * Test rankings of HTML tags. |
|
472 */ |
|
473 function testHTMLRankings() { |
|
474 // Login with sufficient privileges. |
|
475 $this->drupalLogin($this->drupalCreateUser(array('create page content'))); |
|
476 |
|
477 // Test HTML tags with different weights. |
|
478 $sorted_tags = array('h1', 'h2', 'h3', 'h4', 'a', 'h5', 'h6', 'notag'); |
|
479 $shuffled_tags = $sorted_tags; |
|
480 |
|
481 // Shuffle tags to ensure HTML tags are ranked properly. |
|
482 shuffle($shuffled_tags); |
|
483 $settings = array( |
|
484 'type' => 'page', |
|
485 'title' => 'Simple node', |
|
486 ); |
|
487 foreach ($shuffled_tags as $tag) { |
|
488 switch ($tag) { |
|
489 case 'a': |
|
490 $settings['body'] = array(LANGUAGE_NONE => array(array('value' => l('Drupal Rocks', 'node'), 'format' => 'full_html'))); |
|
491 break; |
|
492 case 'notag': |
|
493 $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'Drupal Rocks'))); |
|
494 break; |
|
495 default: |
|
496 $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks</$tag>", 'format' => 'full_html'))); |
|
497 break; |
|
498 } |
|
499 $nodes[$tag] = $this->drupalCreateNode($settings); |
|
500 } |
|
501 |
|
502 // Update the search index. |
|
503 module_invoke_all('update_index'); |
|
504 search_update_totals(); |
|
505 |
|
506 // Refresh variables after the treatment. |
|
507 $this->refreshVariables(); |
|
508 |
|
509 // Disable all other rankings. |
|
510 $node_ranks = array('sticky', 'promote', 'recent', 'comments', 'views'); |
|
511 foreach ($node_ranks as $node_rank) { |
|
512 variable_set('node_rank_' . $node_rank, 0); |
|
513 } |
|
514 $set = node_search_execute('rocks'); |
|
515 |
|
516 // Test the ranking of each tag. |
|
517 foreach ($sorted_tags as $tag_rank => $tag) { |
|
518 // Assert the results. |
|
519 if ($tag == 'notag') { |
|
520 $this->assertEqual($set[$tag_rank]['node']->nid, $nodes[$tag]->nid, 'Search tag ranking for plain text order.'); |
|
521 } else { |
|
522 $this->assertEqual($set[$tag_rank]['node']->nid, $nodes[$tag]->nid, 'Search tag ranking for "<' . $sorted_tags[$tag_rank] . '>" order.'); |
|
523 } |
|
524 } |
|
525 |
|
526 // Test tags with the same weight against the sorted tags. |
|
527 $unsorted_tags = array('u', 'b', 'i', 'strong', 'em'); |
|
528 foreach ($unsorted_tags as $tag) { |
|
529 $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks</$tag>", 'format' => 'full_html'))); |
|
530 $node = $this->drupalCreateNode($settings); |
|
531 |
|
532 // Update the search index. |
|
533 module_invoke_all('update_index'); |
|
534 search_update_totals(); |
|
535 |
|
536 // Refresh variables after the treatment. |
|
537 $this->refreshVariables(); |
|
538 |
|
539 $set = node_search_execute('rocks'); |
|
540 |
|
541 // Ranking should always be second to last. |
|
542 $set = array_slice($set, -2, 1); |
|
543 |
|
544 // Assert the results. |
|
545 $this->assertEqual($set[0]['node']->nid, $node->nid, 'Search tag ranking for "<' . $tag . '>" order.'); |
|
546 |
|
547 // Delete node so it doesn't show up in subsequent search results. |
|
548 node_delete($node->nid); |
|
549 } |
|
550 } |
|
551 |
|
552 /** |
|
553 * Verifies that if we combine two rankings, search still works. |
|
554 * |
|
555 * See issue http://drupal.org/node/771596 |
|
556 */ |
|
557 function testDoubleRankings() { |
|
558 // Login with sufficient privileges. |
|
559 $this->drupalLogin($this->drupalCreateUser(array('skip comment approval', 'create page content'))); |
|
560 |
|
561 // See testRankings() above - build a node that will rank high for sticky. |
|
562 $settings = array( |
|
563 'type' => 'page', |
|
564 'title' => 'Drupal rocks', |
|
565 'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))), |
|
566 'sticky' => 1, |
|
567 ); |
|
568 |
|
569 $node = $this->drupalCreateNode($settings); |
|
570 |
|
571 // Update the search index. |
|
572 module_invoke_all('update_index'); |
|
573 search_update_totals(); |
|
574 |
|
575 // Refresh variables after the treatment. |
|
576 $this->refreshVariables(); |
|
577 |
|
578 // Set up for ranking sticky and lots of comments; make sure others are |
|
579 // disabled. |
|
580 $node_ranks = array('sticky', 'promote', 'relevance', 'recent', 'comments', 'views'); |
|
581 foreach ($node_ranks as $var) { |
|
582 $value = ($var == 'sticky' || $var == 'comments') ? 10 : 0; |
|
583 variable_set('node_rank_' . $var, $value); |
|
584 } |
|
585 |
|
586 // Do the search and assert the results. |
|
587 $set = node_search_execute('rocks'); |
|
588 $this->assertEqual($set[0]['node']->nid, $node->nid, 'Search double ranking order.'); |
|
589 } |
|
590 } |
|
591 |
|
592 /** |
|
593 * Tests the rendering of the search block. |
|
594 */ |
|
595 class SearchBlockTestCase extends DrupalWebTestCase { |
|
596 public static function getInfo() { |
|
597 return array( |
|
598 'name' => 'Block availability', |
|
599 'description' => 'Check if the search form block is available.', |
|
600 'group' => 'Search', |
|
601 ); |
|
602 } |
|
603 |
|
604 function setUp() { |
|
605 parent::setUp('search'); |
|
606 |
|
607 // Create and login user |
|
608 $admin_user = $this->drupalCreateUser(array('administer blocks', 'search content')); |
|
609 $this->drupalLogin($admin_user); |
|
610 } |
|
611 |
|
612 function testSearchFormBlock() { |
|
613 // Set block title to confirm that the interface is available. |
|
614 $this->drupalPost('admin/structure/block/manage/search/form/configure', array('title' => $this->randomName(8)), t('Save block')); |
|
615 $this->assertText(t('The block configuration has been saved.'), 'Block configuration set.'); |
|
616 |
|
617 // Set the block to a region to confirm block is available. |
|
618 $edit = array(); |
|
619 $edit['blocks[search_form][region]'] = 'footer'; |
|
620 $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); |
|
621 $this->assertText(t('The block settings have been updated.'), 'Block successfully move to footer region.'); |
|
622 } |
|
623 |
|
624 /** |
|
625 * Test that the search block form works correctly. |
|
626 */ |
|
627 function testBlock() { |
|
628 // Enable the block, and place it in the 'content' region so that it isn't |
|
629 // hidden on 404 pages. |
|
630 $edit = array('blocks[search_form][region]' => 'content'); |
|
631 $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); |
|
632 |
|
633 // Test a normal search via the block form, from the front page. |
|
634 $terms = array('search_block_form' => 'test'); |
|
635 $this->drupalPost('node', $terms, t('Search')); |
|
636 $this->assertText('Your search yielded no results'); |
|
637 |
|
638 // Test a search from the block on a 404 page. |
|
639 $this->drupalGet('foo'); |
|
640 $this->assertResponse(404); |
|
641 $this->drupalPost(NULL, $terms, t('Search')); |
|
642 $this->assertResponse(200); |
|
643 $this->assertText('Your search yielded no results'); |
|
644 |
|
645 // Test a search from the block when it doesn't appear on the search page. |
|
646 $edit = array('pages' => 'search'); |
|
647 $this->drupalPost('admin/structure/block/manage/search/form/configure', $edit, t('Save block')); |
|
648 $this->drupalPost('node', $terms, t('Search')); |
|
649 $this->assertText('Your search yielded no results'); |
|
650 |
|
651 // Confirm that the user is redirected to the search page. |
|
652 $this->assertEqual( |
|
653 $this->getUrl(), |
|
654 url('search/node/' . $terms['search_block_form'], array('absolute' => TRUE)), |
|
655 'Redirected to correct url.' |
|
656 ); |
|
657 |
|
658 // Test an empty search via the block form, from the front page. |
|
659 $terms = array('search_block_form' => ''); |
|
660 $this->drupalPost('node', $terms, t('Search')); |
|
661 $this->assertText('Please enter some keywords'); |
|
662 |
|
663 // Confirm that the user is redirected to the search page, when form is submitted empty. |
|
664 $this->assertEqual( |
|
665 $this->getUrl(), |
|
666 url('search/node/', array('absolute' => TRUE)), |
|
667 'Redirected to correct url.' |
|
668 ); |
|
669 |
|
670 // Test that after entering a too-short keyword in the form, you can then |
|
671 // search again with a longer keyword. First test using the block form. |
|
672 $terms = array('search_block_form' => 'a'); |
|
673 $this->drupalPost('node', $terms, t('Search')); |
|
674 $this->assertText('You must include at least one positive keyword with 3 characters or more'); |
|
675 $terms = array('search_block_form' => 'foo'); |
|
676 $this->drupalPost(NULL, $terms, t('Search')); |
|
677 $this->assertNoText('You must include at least one positive keyword with 3 characters or more'); |
|
678 $this->assertText('Your search yielded no results'); |
|
679 |
|
680 // Same test again, using the search page form for the second search this time. |
|
681 $terms = array('search_block_form' => 'a'); |
|
682 $this->drupalPost('node', $terms, t('Search')); |
|
683 $terms = array('keys' => 'foo'); |
|
684 $this->drupalPost(NULL, $terms, t('Search')); |
|
685 $this->assertNoText('You must include at least one positive keyword with 3 characters or more'); |
|
686 $this->assertText('Your search yielded no results'); |
|
687 } |
|
688 } |
|
689 |
|
690 /** |
|
691 * Tests that searching for a phrase gets the correct page count. |
|
692 */ |
|
693 class SearchExactTestCase extends DrupalWebTestCase { |
|
694 public static function getInfo() { |
|
695 return array( |
|
696 'name' => 'Search engine phrase queries', |
|
697 'description' => 'Tests that searching for a phrase gets the correct page count.', |
|
698 'group' => 'Search', |
|
699 ); |
|
700 } |
|
701 |
|
702 function setUp() { |
|
703 parent::setUp('search'); |
|
704 } |
|
705 |
|
706 /** |
|
707 * Tests that the correct number of pager links are found for both keywords and phrases. |
|
708 */ |
|
709 function testExactQuery() { |
|
710 // Login with sufficient privileges. |
|
711 $this->drupalLogin($this->drupalCreateUser(array('create page content', 'search content'))); |
|
712 |
|
713 $settings = array( |
|
714 'type' => 'page', |
|
715 'title' => 'Simple Node', |
|
716 ); |
|
717 // Create nodes with exact phrase. |
|
718 for ($i = 0; $i <= 17; $i++) { |
|
719 $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'love pizza'))); |
|
720 $this->drupalCreateNode($settings); |
|
721 } |
|
722 // Create nodes containing keywords. |
|
723 for ($i = 0; $i <= 17; $i++) { |
|
724 $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'love cheesy pizza'))); |
|
725 $this->drupalCreateNode($settings); |
|
726 } |
|
727 |
|
728 // Update the search index. |
|
729 module_invoke_all('update_index'); |
|
730 search_update_totals(); |
|
731 |
|
732 // Refresh variables after the treatment. |
|
733 $this->refreshVariables(); |
|
734 |
|
735 // Test that the correct number of pager links are found for keyword search. |
|
736 $edit = array('keys' => 'love pizza'); |
|
737 $this->drupalPost('search/node', $edit, t('Search')); |
|
738 $this->assertLinkByHref('page=1', 0, '2nd page link is found for keyword search.'); |
|
739 $this->assertLinkByHref('page=2', 0, '3rd page link is found for keyword search.'); |
|
740 $this->assertLinkByHref('page=3', 0, '4th page link is found for keyword search.'); |
|
741 $this->assertNoLinkByHref('page=4', '5th page link is not found for keyword search.'); |
|
742 |
|
743 // Test that the correct number of pager links are found for exact phrase search. |
|
744 $edit = array('keys' => '"love pizza"'); |
|
745 $this->drupalPost('search/node', $edit, t('Search')); |
|
746 $this->assertLinkByHref('page=1', 0, '2nd page link is found for exact phrase search.'); |
|
747 $this->assertNoLinkByHref('page=2', '3rd page link is not found for exact phrase search.'); |
|
748 } |
|
749 } |
|
750 |
|
751 /** |
|
752 * Test integration searching comments. |
|
753 */ |
|
754 class SearchCommentTestCase extends DrupalWebTestCase { |
|
755 protected $admin_user; |
|
756 |
|
757 public static function getInfo() { |
|
758 return array( |
|
759 'name' => 'Comment Search tests', |
|
760 'description' => 'Test integration searching comments.', |
|
761 'group' => 'Search', |
|
762 ); |
|
763 } |
|
764 |
|
765 function setUp() { |
|
766 parent::setUp('comment', 'search'); |
|
767 |
|
768 // Create and log in an administrative user having access to the Full HTML |
|
769 // text format. |
|
770 $full_html_format = filter_format_load('full_html'); |
|
771 $permissions = array( |
|
772 'administer filters', |
|
773 filter_permission_name($full_html_format), |
|
774 'administer permissions', |
|
775 'create page content', |
|
776 'skip comment approval', |
|
777 'access comments', |
|
778 ); |
|
779 $this->admin_user = $this->drupalCreateUser($permissions); |
|
780 $this->drupalLogin($this->admin_user); |
|
781 } |
|
782 |
|
783 /** |
|
784 * Verify that comments are rendered using proper format in search results. |
|
785 */ |
|
786 function testSearchResultsComment() { |
|
787 $comment_body = 'Test comment body'; |
|
788 |
|
789 variable_set('comment_preview_article', DRUPAL_OPTIONAL); |
|
790 // Enable check_plain() for 'Filtered HTML' text format. |
|
791 $filtered_html_format_id = 'filtered_html'; |
|
792 $edit = array( |
|
793 'filters[filter_html_escape][status]' => TRUE, |
|
794 ); |
|
795 $this->drupalPost('admin/config/content/formats/' . $filtered_html_format_id, $edit, t('Save configuration')); |
|
796 // Allow anonymous users to search content. |
|
797 $edit = array( |
|
798 DRUPAL_ANONYMOUS_RID . '[search content]' => 1, |
|
799 DRUPAL_ANONYMOUS_RID . '[access comments]' => 1, |
|
800 DRUPAL_ANONYMOUS_RID . '[post comments]' => 1, |
|
801 ); |
|
802 $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); |
|
803 |
|
804 // Create a node. |
|
805 $node = $this->drupalCreateNode(array('type' => 'article')); |
|
806 // Post a comment using 'Full HTML' text format. |
|
807 $edit_comment = array(); |
|
808 $edit_comment['subject'] = 'Test comment subject'; |
|
809 $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = '<h1>' . $comment_body . '</h1>'; |
|
810 $full_html_format_id = 'full_html'; |
|
811 $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][format]'] = $full_html_format_id; |
|
812 $this->drupalPost('comment/reply/' . $node->nid, $edit_comment, t('Save')); |
|
813 |
|
814 // Invoke search index update. |
|
815 $this->drupalLogout(); |
|
816 $this->cronRun(); |
|
817 |
|
818 // Search for the comment subject. |
|
819 $edit = array( |
|
820 'search_block_form' => "'" . $edit_comment['subject'] . "'", |
|
821 ); |
|
822 $this->drupalPost('', $edit, t('Search')); |
|
823 $this->assertText($node->title, 'Node found in search results.'); |
|
824 $this->assertText($edit_comment['subject'], 'Comment subject found in search results.'); |
|
825 |
|
826 // Search for the comment body. |
|
827 $edit = array( |
|
828 'search_block_form' => "'" . $comment_body . "'", |
|
829 ); |
|
830 $this->drupalPost('', $edit, t('Search')); |
|
831 $this->assertText($node->title, 'Node found in search results.'); |
|
832 |
|
833 // Verify that comment is rendered using proper format. |
|
834 $this->assertText($comment_body, 'Comment body text found in search results.'); |
|
835 $this->assertNoRaw(t('n/a'), 'HTML in comment body is not hidden.'); |
|
836 $this->assertNoRaw(check_plain($edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]']), 'HTML in comment body is not escaped.'); |
|
837 |
|
838 // Hide comments. |
|
839 $this->drupalLogin($this->admin_user); |
|
840 $node->comment = 0; |
|
841 node_save($node); |
|
842 |
|
843 // Invoke search index update. |
|
844 $this->drupalLogout(); |
|
845 $this->cronRun(); |
|
846 |
|
847 // Search for $title. |
|
848 $this->drupalPost('', $edit, t('Search')); |
|
849 $this->assertNoText($comment_body, 'Comment body text not found in search results.'); |
|
850 } |
|
851 |
|
852 /** |
|
853 * Verify access rules for comment indexing with different permissions. |
|
854 */ |
|
855 function testSearchResultsCommentAccess() { |
|
856 $comment_body = 'Test comment body'; |
|
857 $this->comment_subject = 'Test comment subject'; |
|
858 $this->admin_role = $this->admin_user->roles; |
|
859 unset($this->admin_role[DRUPAL_AUTHENTICATED_RID]); |
|
860 $this->admin_role = key($this->admin_role); |
|
861 |
|
862 // Create a node. |
|
863 variable_set('comment_preview_article', DRUPAL_OPTIONAL); |
|
864 $this->node = $this->drupalCreateNode(array('type' => 'article')); |
|
865 |
|
866 // Post a comment using 'Full HTML' text format. |
|
867 $edit_comment = array(); |
|
868 $edit_comment['subject'] = $this->comment_subject; |
|
869 $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = '<h1>' . $comment_body . '</h1>'; |
|
870 $this->drupalPost('comment/reply/' . $this->node->nid, $edit_comment, t('Save')); |
|
871 |
|
872 $this->drupalLogout(); |
|
873 $this->setRolePermissions(DRUPAL_ANONYMOUS_RID); |
|
874 $this->checkCommentAccess('Anon user has search permission but no access comments permission, comments should not be indexed'); |
|
875 |
|
876 $this->setRolePermissions(DRUPAL_ANONYMOUS_RID, TRUE); |
|
877 $this->checkCommentAccess('Anon user has search permission and access comments permission, comments should be indexed', TRUE); |
|
878 |
|
879 $this->drupalLogin($this->admin_user); |
|
880 $this->drupalGet('admin/people/permissions'); |
|
881 |
|
882 // Disable search access for authenticated user to test admin user. |
|
883 $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, FALSE, FALSE); |
|
884 |
|
885 $this->setRolePermissions($this->admin_role); |
|
886 $this->checkCommentAccess('Admin user has search permission but no access comments permission, comments should not be indexed'); |
|
887 |
|
888 $this->setRolePermissions($this->admin_role, TRUE); |
|
889 $this->checkCommentAccess('Admin user has search permission and access comments permission, comments should be indexed', TRUE); |
|
890 |
|
891 $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID); |
|
892 $this->checkCommentAccess('Authenticated user has search permission but no access comments permission, comments should not be indexed'); |
|
893 |
|
894 $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE); |
|
895 $this->checkCommentAccess('Authenticated user has search permission and access comments permission, comments should be indexed', TRUE); |
|
896 |
|
897 // Verify that access comments permission is inherited from the |
|
898 // authenticated role. |
|
899 $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE, FALSE); |
|
900 $this->setRolePermissions($this->admin_role); |
|
901 $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); |
|
902 |
|
903 // Verify that search content permission is inherited from the authenticated |
|
904 // role. |
|
905 $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE, TRUE); |
|
906 $this->setRolePermissions($this->admin_role, TRUE, FALSE); |
|
907 $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); |
|
908 |
|
909 } |
|
910 |
|
911 /** |
|
912 * Set permissions for role. |
|
913 */ |
|
914 function setRolePermissions($rid, $access_comments = FALSE, $search_content = TRUE) { |
|
915 $permissions = array( |
|
916 'access comments' => $access_comments, |
|
917 'search content' => $search_content, |
|
918 ); |
|
919 user_role_change_permissions($rid, $permissions); |
|
920 } |
|
921 |
|
922 /** |
|
923 * Update search index and search for comment. |
|
924 */ |
|
925 function checkCommentAccess($message, $assume_access = FALSE) { |
|
926 // Invoke search index update. |
|
927 search_touch_node($this->node->nid); |
|
928 $this->cronRun(); |
|
929 |
|
930 // Search for the comment subject. |
|
931 $edit = array( |
|
932 'search_block_form' => "'" . $this->comment_subject . "'", |
|
933 ); |
|
934 $this->drupalPost('', $edit, t('Search')); |
|
935 $method = $assume_access ? 'assertText' : 'assertNoText'; |
|
936 $verb = $assume_access ? 'found' : 'not found'; |
|
937 $this->{$method}($this->node->title, "Node $verb in search results: " . $message); |
|
938 $this->{$method}($this->comment_subject, "Comment subject $verb in search results: " . $message); |
|
939 } |
|
940 |
|
941 /** |
|
942 * Verify that 'add new comment' does not appear in search results or index. |
|
943 */ |
|
944 function testAddNewComment() { |
|
945 // Create a node with a short body. |
|
946 $settings = array( |
|
947 'type' => 'article', |
|
948 'title' => 'short title', |
|
949 'body' => array(LANGUAGE_NONE => array(array('value' => 'short body text'))), |
|
950 ); |
|
951 |
|
952 $user = $this->drupalCreateUser(array('search content', 'create article content', 'access content')); |
|
953 $this->drupalLogin($user); |
|
954 |
|
955 $node = $this->drupalCreateNode($settings); |
|
956 // Verify that if you view the node on its own page, 'add new comment' |
|
957 // is there. |
|
958 $this->drupalGet('node/' . $node->nid); |
|
959 $this->assertText(t('Add new comment'), 'Add new comment appears on node page'); |
|
960 |
|
961 // Run cron to index this page. |
|
962 $this->drupalLogout(); |
|
963 $this->cronRun(); |
|
964 |
|
965 // Search for 'comment'. Should be no results. |
|
966 $this->drupalLogin($user); |
|
967 $this->drupalPost('search/node', array('keys' => 'comment'), t('Search')); |
|
968 $this->assertText(t('Your search yielded no results'), 'No results searching for the word comment'); |
|
969 |
|
970 // Search for the node title. Should be found, and 'Add new comment' should |
|
971 // not be part of the search snippet. |
|
972 $this->drupalPost('search/node', array('keys' => 'short'), t('Search')); |
|
973 $this->assertText($node->title, 'Search for keyword worked'); |
|
974 $this->assertNoText(t('Add new comment'), 'Add new comment does not appear on search results page'); |
|
975 } |
|
976 |
|
977 } |
|
978 |
|
979 /** |
|
980 * Tests search_expression_insert() and search_expression_extract(). |
|
981 * |
|
982 * @see http://drupal.org/node/419388 (issue) |
|
983 */ |
|
984 class SearchExpressionInsertExtractTestCase extends DrupalUnitTestCase { |
|
985 public static function getInfo() { |
|
986 return array( |
|
987 'name' => 'Search expression insert/extract', |
|
988 'description' => 'Tests the functions search_expression_insert() and search_expression_extract()', |
|
989 'group' => 'Search', |
|
990 ); |
|
991 } |
|
992 |
|
993 function setUp() { |
|
994 drupal_load('module', 'search'); |
|
995 parent::setUp(); |
|
996 } |
|
997 |
|
998 /** |
|
999 * Tests search_expression_insert() and search_expression_extract(). |
|
1000 */ |
|
1001 function testInsertExtract() { |
|
1002 $base_expression = "mykeyword"; |
|
1003 // Build an array of option, value, what should be in the expression, what |
|
1004 // should be retrieved from expression. |
|
1005 $cases = array( |
|
1006 array('foo', 'bar', 'foo:bar', 'bar'), // Normal case. |
|
1007 array('foo', NULL, '', NULL), // Empty value: shouldn't insert. |
|
1008 array('foo', ' ', 'foo:', ''), // Space as value: should insert but retrieve empty string. |
|
1009 array('foo', '', 'foo:', ''), // Empty string as value: should insert but retrieve empty string. |
|
1010 array('foo', '0', 'foo:0', '0'), // String zero as value: should insert. |
|
1011 array('foo', 0, 'foo:0', '0'), // Numeric zero as value: should insert. |
|
1012 ); |
|
1013 |
|
1014 foreach ($cases as $index => $case) { |
|
1015 $after_insert = search_expression_insert($base_expression, $case[0], $case[1]); |
|
1016 if (empty($case[2])) { |
|
1017 $this->assertEqual($after_insert, $base_expression, "Empty insert does not change expression in case $index"); |
|
1018 } |
|
1019 else { |
|
1020 $this->assertEqual($after_insert, $base_expression . ' ' . $case[2], "Insert added correct expression for case $index"); |
|
1021 } |
|
1022 |
|
1023 $retrieved = search_expression_extract($after_insert, $case[0]); |
|
1024 if (!isset($case[3])) { |
|
1025 $this->assertFalse(isset($retrieved), "Empty retrieval results in unset value in case $index"); |
|
1026 } |
|
1027 else { |
|
1028 $this->assertEqual($retrieved, $case[3], "Value is retrieved for case $index"); |
|
1029 } |
|
1030 |
|
1031 $after_clear = search_expression_insert($after_insert, $case[0]); |
|
1032 $this->assertEqual(trim($after_clear), $base_expression, "After clearing, base expression is restored for case $index"); |
|
1033 |
|
1034 $cleared = search_expression_extract($after_clear, $case[0]); |
|
1035 $this->assertFalse(isset($cleared), "After clearing, value could not be retrieved for case $index"); |
|
1036 } |
|
1037 } |
|
1038 } |
|
1039 |
|
1040 /** |
|
1041 * Tests that comment count display toggles properly on comment status of node |
|
1042 * |
|
1043 * Issue 537278 |
|
1044 * |
|
1045 * - Nodes with comment status set to Open should always how comment counts |
|
1046 * - Nodes with comment status set to Closed should show comment counts |
|
1047 * only when there are comments |
|
1048 * - Nodes with comment status set to Hidden should never show comment counts |
|
1049 */ |
|
1050 class SearchCommentCountToggleTestCase extends DrupalWebTestCase { |
|
1051 protected $searching_user; |
|
1052 protected $searchable_nodes; |
|
1053 |
|
1054 public static function getInfo() { |
|
1055 return array( |
|
1056 'name' => 'Comment count toggle', |
|
1057 'description' => 'Verify that comment count display toggles properly on comment status of node.', |
|
1058 'group' => 'Search', |
|
1059 ); |
|
1060 } |
|
1061 |
|
1062 function setUp() { |
|
1063 parent::setUp('search'); |
|
1064 |
|
1065 // Create searching user. |
|
1066 $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'skip comment approval')); |
|
1067 |
|
1068 // Create initial nodes. |
|
1069 $node_params = array('type' => 'article', 'body' => array(LANGUAGE_NONE => array(array('value' => 'SearchCommentToggleTestCase')))); |
|
1070 |
|
1071 $this->searchable_nodes['1 comment'] = $this->drupalCreateNode($node_params); |
|
1072 $this->searchable_nodes['0 comments'] = $this->drupalCreateNode($node_params); |
|
1073 |
|
1074 // Login with sufficient privileges. |
|
1075 $this->drupalLogin($this->searching_user); |
|
1076 |
|
1077 // Create a comment array |
|
1078 $edit_comment = array(); |
|
1079 $edit_comment['subject'] = $this->randomName(); |
|
1080 $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = $this->randomName(); |
|
1081 $filtered_html_format_id = 'filtered_html'; |
|
1082 $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][format]'] = $filtered_html_format_id; |
|
1083 |
|
1084 // Post comment to the test node with comment |
|
1085 $this->drupalPost('comment/reply/' . $this->searchable_nodes['1 comment']->nid, $edit_comment, t('Save')); |
|
1086 |
|
1087 // First update the index. This does the initial processing. |
|
1088 node_update_index(); |
|
1089 |
|
1090 // Then, run the shutdown function. Testing is a unique case where indexing |
|
1091 // and searching has to happen in the same request, so running the shutdown |
|
1092 // function manually is needed to finish the indexing process. |
|
1093 search_update_totals(); |
|
1094 } |
|
1095 |
|
1096 /** |
|
1097 * Verify that comment count display toggles properly on comment status of node |
|
1098 */ |
|
1099 function testSearchCommentCountToggle() { |
|
1100 // Search for the nodes by string in the node body. |
|
1101 $edit = array( |
|
1102 'search_block_form' => "'SearchCommentToggleTestCase'", |
|
1103 ); |
|
1104 |
|
1105 // Test comment count display for nodes with comment status set to Open |
|
1106 $this->drupalPost('', $edit, t('Search')); |
|
1107 $this->assertText(t('0 comments'), 'Empty comment count displays for nodes with comment status set to Open'); |
|
1108 $this->assertText(t('1 comment'), 'Non-empty comment count displays for nodes with comment status set to Open'); |
|
1109 |
|
1110 // Test comment count display for nodes with comment status set to Closed |
|
1111 $this->searchable_nodes['0 comments']->comment = COMMENT_NODE_CLOSED; |
|
1112 node_save($this->searchable_nodes['0 comments']); |
|
1113 $this->searchable_nodes['1 comment']->comment = COMMENT_NODE_CLOSED; |
|
1114 node_save($this->searchable_nodes['1 comment']); |
|
1115 |
|
1116 $this->drupalPost('', $edit, t('Search')); |
|
1117 $this->assertNoText(t('0 comments'), 'Empty comment count does not display for nodes with comment status set to Closed'); |
|
1118 $this->assertText(t('1 comment'), 'Non-empty comment count displays for nodes with comment status set to Closed'); |
|
1119 |
|
1120 // Test comment count display for nodes with comment status set to Hidden |
|
1121 $this->searchable_nodes['0 comments']->comment = COMMENT_NODE_HIDDEN; |
|
1122 node_save($this->searchable_nodes['0 comments']); |
|
1123 $this->searchable_nodes['1 comment']->comment = COMMENT_NODE_HIDDEN; |
|
1124 node_save($this->searchable_nodes['1 comment']); |
|
1125 |
|
1126 $this->drupalPost('', $edit, t('Search')); |
|
1127 $this->assertNoText(t('0 comments'), 'Empty comment count does not display for nodes with comment status set to Hidden'); |
|
1128 $this->assertNoText(t('1 comment'), 'Non-empty comment count does not display for nodes with comment status set to Hidden'); |
|
1129 } |
|
1130 } |
|
1131 |
|
1132 /** |
|
1133 * Test search_simplify() on every Unicode character, and some other cases. |
|
1134 */ |
|
1135 class SearchSimplifyTestCase extends DrupalWebTestCase { |
|
1136 public static function getInfo() { |
|
1137 return array( |
|
1138 'name' => 'Search simplify', |
|
1139 'description' => 'Check that the search_simply() function works as intended.', |
|
1140 'group' => 'Search', |
|
1141 ); |
|
1142 } |
|
1143 |
|
1144 /** |
|
1145 * Tests that all Unicode characters simplify correctly. |
|
1146 */ |
|
1147 function testSearchSimplifyUnicode() { |
|
1148 // This test uses a file that was constructed so that the even lines are |
|
1149 // boundary characters, and the odd lines are valid word characters. (It |
|
1150 // was generated as a sequence of all the Unicode characters, and then the |
|
1151 // boundary chararacters (punctuation, spaces, etc.) were split off into |
|
1152 // their own lines). So the even-numbered lines should simplify to nothing, |
|
1153 // and the odd-numbered lines we need to split into shorter chunks and |
|
1154 // verify that simplification doesn't lose any characters. |
|
1155 $input = file_get_contents(DRUPAL_ROOT . '/modules/search/tests/UnicodeTest.txt'); |
|
1156 $basestrings = explode(chr(10), $input); |
|
1157 $strings = array(); |
|
1158 foreach ($basestrings as $key => $string) { |
|
1159 if ($key %2) { |
|
1160 // Even line - should simplify down to a space. |
|
1161 $simplified = search_simplify($string); |
|
1162 $this->assertIdentical($simplified, ' ', "Line $key is excluded from the index"); |
|
1163 } |
|
1164 else { |
|
1165 // Odd line, should be word characters. |
|
1166 // Split this into 30-character chunks, so we don't run into limits |
|
1167 // of truncation in search_simplify(). |
|
1168 $start = 0; |
|
1169 while ($start < drupal_strlen($string)) { |
|
1170 $newstr = drupal_substr($string, $start, 30); |
|
1171 // Special case: leading zeros are removed from numeric strings, |
|
1172 // and there's one string in this file that is numbers starting with |
|
1173 // zero, so prepend a 1 on that string. |
|
1174 if (preg_match('/^[0-9]+$/', $newstr)) { |
|
1175 $newstr = '1' . $newstr; |
|
1176 } |
|
1177 $strings[] = $newstr; |
|
1178 $start += 30; |
|
1179 } |
|
1180 } |
|
1181 } |
|
1182 foreach ($strings as $key => $string) { |
|
1183 $simplified = search_simplify($string); |
|
1184 $this->assertTrue(drupal_strlen($simplified) >= drupal_strlen($string), "Nothing is removed from string $key."); |
|
1185 } |
|
1186 |
|
1187 // Test the low-numbered ASCII control characters separately. They are not |
|
1188 // in the text file because they are problematic for diff, especially \0. |
|
1189 $string = ''; |
|
1190 for ($i = 0; $i < 32; $i++) { |
|
1191 $string .= chr($i); |
|
1192 } |
|
1193 $this->assertIdentical(' ', search_simplify($string), 'Search simplify works for ASCII control characters.'); |
|
1194 } |
|
1195 |
|
1196 /** |
|
1197 * Tests that search_simplify() does the right thing with punctuation. |
|
1198 */ |
|
1199 function testSearchSimplifyPunctuation() { |
|
1200 $cases = array( |
|
1201 array('20.03/94-28,876', '20039428876', 'Punctuation removed from numbers'), |
|
1202 array('great...drupal--module', 'great drupal module', 'Multiple dot and dashes are word boundaries'), |
|
1203 array('very_great-drupal.module', 'verygreatdrupalmodule', 'Single dot, dash, underscore are removed'), |
|
1204 array('regular,punctuation;word', 'regular punctuation word', 'Punctuation is a word boundary'), |
|
1205 ); |
|
1206 |
|
1207 foreach ($cases as $case) { |
|
1208 $out = trim(search_simplify($case[0])); |
|
1209 $this->assertEqual($out, $case[1], $case[2]); |
|
1210 } |
|
1211 } |
|
1212 } |
|
1213 |
|
1214 |
|
1215 /** |
|
1216 * Tests keywords and conditions. |
|
1217 */ |
|
1218 class SearchKeywordsConditions extends DrupalWebTestCase { |
|
1219 |
|
1220 public static function getInfo() { |
|
1221 return array( |
|
1222 'name' => 'Keywords and conditions', |
|
1223 'description' => 'Verify the search pulls in keywords and extra conditions.', |
|
1224 'group' => 'Search', |
|
1225 ); |
|
1226 } |
|
1227 |
|
1228 function setUp() { |
|
1229 parent::setUp('search', 'search_extra_type'); |
|
1230 // Create searching user. |
|
1231 $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'skip comment approval')); |
|
1232 // Login with sufficient privileges. |
|
1233 $this->drupalLogin($this->searching_user); |
|
1234 // Test with all search modules enabled. |
|
1235 variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type')); |
|
1236 menu_rebuild(); |
|
1237 } |
|
1238 |
|
1239 /** |
|
1240 * Verify the kewords are captured and conditions respected. |
|
1241 */ |
|
1242 function testSearchKeyswordsConditions() { |
|
1243 // No keys, not conditions - no results. |
|
1244 $this->drupalGet('search/dummy_path'); |
|
1245 $this->assertNoText('Dummy search snippet to display'); |
|
1246 // With keys - get results. |
|
1247 $keys = 'bike shed ' . $this->randomName(); |
|
1248 $this->drupalGet("search/dummy_path/{$keys}"); |
|
1249 $this->assertText("Dummy search snippet to display. Keywords: {$keys}"); |
|
1250 $keys = 'blue drop ' . $this->randomName(); |
|
1251 $this->drupalGet("search/dummy_path", array('query' => array('keys' => $keys))); |
|
1252 $this->assertText("Dummy search snippet to display. Keywords: {$keys}"); |
|
1253 // Add some conditions and keys. |
|
1254 $keys = 'moving drop ' . $this->randomName(); |
|
1255 $this->drupalGet("search/dummy_path/bike", array('query' => array('search_conditions' => $keys))); |
|
1256 $this->assertText("Dummy search snippet to display."); |
|
1257 $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE)); |
|
1258 // Add some conditions and no keys. |
|
1259 $keys = 'drop kick ' . $this->randomName(); |
|
1260 $this->drupalGet("search/dummy_path", array('query' => array('search_conditions' => $keys))); |
|
1261 $this->assertText("Dummy search snippet to display."); |
|
1262 $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE)); |
|
1263 } |
|
1264 } |
|
1265 |
|
1266 /** |
|
1267 * Tests that numbers can be searched. |
|
1268 */ |
|
1269 class SearchNumbersTestCase extends DrupalWebTestCase { |
|
1270 protected $test_user; |
|
1271 protected $numbers; |
|
1272 protected $nodes; |
|
1273 |
|
1274 public static function getInfo() { |
|
1275 return array( |
|
1276 'name' => 'Search numbers', |
|
1277 'description' => 'Check that numbers can be searched', |
|
1278 'group' => 'Search', |
|
1279 ); |
|
1280 } |
|
1281 |
|
1282 function setUp() { |
|
1283 parent::setUp('search'); |
|
1284 |
|
1285 $this->test_user = $this->drupalCreateUser(array('search content', 'access content', 'administer nodes', 'access site reports')); |
|
1286 $this->drupalLogin($this->test_user); |
|
1287 |
|
1288 // Create content with various numbers in it. |
|
1289 // Note: 50 characters is the current limit of the search index's word |
|
1290 // field. |
|
1291 $this->numbers = array( |
|
1292 'ISBN' => '978-0446365383', |
|
1293 'UPC' => '036000 291452', |
|
1294 'EAN bar code' => '5901234123457', |
|
1295 'negative' => '-123456.7890', |
|
1296 'quoted negative' => '"-123456.7890"', |
|
1297 'leading zero' => '0777777777', |
|
1298 'tiny' => '111', |
|
1299 'small' => '22222222222222', |
|
1300 'medium' => '333333333333333333333333333', |
|
1301 'large' => '444444444444444444444444444444444444444', |
|
1302 'gigantic' => '5555555555555555555555555555555555555555555555555', |
|
1303 'over fifty characters' => '666666666666666666666666666666666666666666666666666666666666', |
|
1304 'date', '01/02/2009', |
|
1305 'commas', '987,654,321', |
|
1306 ); |
|
1307 |
|
1308 foreach ($this->numbers as $doc => $num) { |
|
1309 $info = array( |
|
1310 'body' => array(LANGUAGE_NONE => array(array('value' => $num))), |
|
1311 'type' => 'page', |
|
1312 'language' => LANGUAGE_NONE, |
|
1313 'title' => $doc . ' number', |
|
1314 ); |
|
1315 $this->nodes[$doc] = $this->drupalCreateNode($info); |
|
1316 } |
|
1317 |
|
1318 // Run cron to ensure the content is indexed. |
|
1319 $this->cronRun(); |
|
1320 $this->drupalGet('admin/reports/dblog'); |
|
1321 $this->assertText(t('Cron run completed'), 'Log shows cron run completed'); |
|
1322 } |
|
1323 |
|
1324 /** |
|
1325 * Tests that all the numbers can be searched. |
|
1326 */ |
|
1327 function testNumberSearching() { |
|
1328 $types = array_keys($this->numbers); |
|
1329 |
|
1330 foreach ($types as $type) { |
|
1331 $number = $this->numbers[$type]; |
|
1332 // If the number is negative, remove the - sign, because - indicates |
|
1333 // "not keyword" when searching. |
|
1334 $number = ltrim($number, '-'); |
|
1335 $node = $this->nodes[$type]; |
|
1336 |
|
1337 // Verify that the node title does not appear on the search page |
|
1338 // with a dummy search. |
|
1339 $this->drupalPost('search/node', |
|
1340 array('keys' => 'foo'), |
|
1341 t('Search')); |
|
1342 $this->assertNoText($node->title, $type . ': node title not shown in dummy search'); |
|
1343 |
|
1344 // Verify that the node title does appear as a link on the search page |
|
1345 // when searching for the number. |
|
1346 $this->drupalPost('search/node', |
|
1347 array('keys' => $number), |
|
1348 t('Search')); |
|
1349 $this->assertText($node->title, format_string('%type: node title shown (search found the node) in search for number %number.', array('%type' => $type, '%number' => $number))); |
|
1350 } |
|
1351 } |
|
1352 } |
|
1353 |
|
1354 /** |
|
1355 * Tests that numbers can be searched, with more complex matching. |
|
1356 */ |
|
1357 class SearchNumberMatchingTestCase extends DrupalWebTestCase { |
|
1358 protected $test_user; |
|
1359 protected $numbers; |
|
1360 protected $nodes; |
|
1361 |
|
1362 public static function getInfo() { |
|
1363 return array( |
|
1364 'name' => 'Search number matching', |
|
1365 'description' => 'Check that numbers can be searched with more complex matching', |
|
1366 'group' => 'Search', |
|
1367 ); |
|
1368 } |
|
1369 |
|
1370 function setUp() { |
|
1371 parent::setUp('search'); |
|
1372 |
|
1373 $this->test_user = $this->drupalCreateUser(array('search content', 'access content', 'administer nodes', 'access site reports')); |
|
1374 $this->drupalLogin($this->test_user); |
|
1375 |
|
1376 // Define a group of numbers that should all match each other -- |
|
1377 // numbers with internal punctuation should match each other, as well |
|
1378 // as numbers with and without leading zeros and leading/trailing |
|
1379 // . and -. |
|
1380 $this->numbers = array( |
|
1381 '123456789', |
|
1382 '12/34/56789', |
|
1383 '12.3456789', |
|
1384 '12-34-56789', |
|
1385 '123,456,789', |
|
1386 '-123456789', |
|
1387 '0123456789', |
|
1388 ); |
|
1389 |
|
1390 foreach ($this->numbers as $num) { |
|
1391 $info = array( |
|
1392 'body' => array(LANGUAGE_NONE => array(array('value' => $num))), |
|
1393 'type' => 'page', |
|
1394 'language' => LANGUAGE_NONE, |
|
1395 ); |
|
1396 $this->nodes[] = $this->drupalCreateNode($info); |
|
1397 } |
|
1398 |
|
1399 // Run cron to ensure the content is indexed. |
|
1400 $this->cronRun(); |
|
1401 $this->drupalGet('admin/reports/dblog'); |
|
1402 $this->assertText(t('Cron run completed'), 'Log shows cron run completed'); |
|
1403 } |
|
1404 |
|
1405 /** |
|
1406 * Tests that all the numbers can be searched. |
|
1407 */ |
|
1408 function testNumberSearching() { |
|
1409 for ($i = 0; $i < count($this->numbers); $i++) { |
|
1410 $node = $this->nodes[$i]; |
|
1411 |
|
1412 // Verify that the node title does not appear on the search page |
|
1413 // with a dummy search. |
|
1414 $this->drupalPost('search/node', |
|
1415 array('keys' => 'foo'), |
|
1416 t('Search')); |
|
1417 $this->assertNoText($node->title, format_string('%number: node title not shown in dummy search', array('%number' => $i))); |
|
1418 |
|
1419 // Now verify that we can find node i by searching for any of the |
|
1420 // numbers. |
|
1421 for ($j = 0; $j < count($this->numbers); $j++) { |
|
1422 $number = $this->numbers[$j]; |
|
1423 // If the number is negative, remove the - sign, because - indicates |
|
1424 // "not keyword" when searching. |
|
1425 $number = ltrim($number, '-'); |
|
1426 |
|
1427 $this->drupalPost('search/node', |
|
1428 array('keys' => $number), |
|
1429 t('Search')); |
|
1430 $this->assertText($node->title, format_string('%i: node title shown (search found the node) in search for number %number', array('%i' => $i, '%number' => $number))); |
|
1431 } |
|
1432 } |
|
1433 |
|
1434 } |
|
1435 } |
|
1436 |
|
1437 /** |
|
1438 * Test config page. |
|
1439 */ |
|
1440 class SearchConfigSettingsForm extends DrupalWebTestCase { |
|
1441 public $search_user; |
|
1442 public $search_node; |
|
1443 |
|
1444 public static function getInfo() { |
|
1445 return array( |
|
1446 'name' => 'Config settings form', |
|
1447 'description' => 'Verify the search config settings form.', |
|
1448 'group' => 'Search', |
|
1449 ); |
|
1450 } |
|
1451 |
|
1452 function setUp() { |
|
1453 parent::setUp('search', 'search_extra_type'); |
|
1454 |
|
1455 // Login as a user that can create and search content. |
|
1456 $this->search_user = $this->drupalCreateUser(array('search content', 'administer search', 'administer nodes', 'bypass node access', 'access user profiles', 'administer users', 'administer blocks', 'access site reports')); |
|
1457 $this->drupalLogin($this->search_user); |
|
1458 |
|
1459 // Add a single piece of content and index it. |
|
1460 $node = $this->drupalCreateNode(); |
|
1461 $this->search_node = $node; |
|
1462 // Link the node to itself to test that it's only indexed once. The content |
|
1463 // also needs the word "pizza" so we can use it as the search keyword. |
|
1464 $langcode = LANGUAGE_NONE; |
|
1465 $body_key = "body[$langcode][0][value]"; |
|
1466 $edit[$body_key] = l($node->title, 'node/' . $node->nid) . ' pizza sandwich'; |
|
1467 $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); |
|
1468 |
|
1469 node_update_index(); |
|
1470 search_update_totals(); |
|
1471 |
|
1472 // Enable the search block. |
|
1473 $edit = array(); |
|
1474 $edit['blocks[search_form][region]'] = 'content'; |
|
1475 $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); |
|
1476 } |
|
1477 |
|
1478 /** |
|
1479 * Verify the search settings form. |
|
1480 */ |
|
1481 function testSearchSettingsPage() { |
|
1482 |
|
1483 // Test that the settings form displays the correct count of items left to index. |
|
1484 $this->drupalGet('admin/config/search/settings'); |
|
1485 $this->assertText(t('There are @count items left to index.', array('@count' => 0))); |
|
1486 |
|
1487 // Test the re-index button. |
|
1488 $this->drupalPost('admin/config/search/settings', array(), t('Re-index site')); |
|
1489 $this->assertText(t('Are you sure you want to re-index the site')); |
|
1490 $this->drupalPost('admin/config/search/settings/reindex', array(), t('Re-index site')); |
|
1491 $this->assertText(t('The index will be rebuilt')); |
|
1492 $this->drupalGet('admin/config/search/settings'); |
|
1493 $this->assertText(t('There is 1 item left to index.')); |
|
1494 |
|
1495 // Test that the form saves with the default values. |
|
1496 $this->drupalPost('admin/config/search/settings', array(), t('Save configuration')); |
|
1497 $this->assertText(t('The configuration options have been saved.'), 'Form saves with the default values.'); |
|
1498 |
|
1499 // Test that the form does not save with an invalid word length. |
|
1500 $edit = array( |
|
1501 'minimum_word_size' => $this->randomName(3), |
|
1502 ); |
|
1503 $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration')); |
|
1504 $this->assertNoText(t('The configuration options have been saved.'), 'Form does not save with an invalid word length.'); |
|
1505 |
|
1506 // Test logging setting. It should be on by default. |
|
1507 $text = $this->randomName(5); |
|
1508 $this->drupalPost('search/node', array('keys' => $text), t('Search')); |
|
1509 $this->drupalGet('admin/reports/dblog'); |
|
1510 $this->assertLink('Searched Content for ' . $text . '.', 0, 'Search was logged'); |
|
1511 |
|
1512 // Turn off logging. |
|
1513 variable_set('search_logging', FALSE); |
|
1514 $text = $this->randomName(5); |
|
1515 $this->drupalPost('search/node', array('keys' => $text), t('Search')); |
|
1516 $this->drupalGet('admin/reports/dblog'); |
|
1517 $this->assertNoLink('Searched Content for ' . $text . '.', 'Search was not logged'); |
|
1518 } |
|
1519 |
|
1520 /** |
|
1521 * Verify that you can disable individual search modules. |
|
1522 */ |
|
1523 function testSearchModuleDisabling() { |
|
1524 // Array of search modules to test: 'path' is the search path, 'title' is |
|
1525 // the tab title, 'keys' are the keywords to search for, and 'text' is |
|
1526 // the text to assert is on the results page. |
|
1527 $module_info = array( |
|
1528 'node' => array( |
|
1529 'path' => 'node', |
|
1530 'title' => 'Content', |
|
1531 'keys' => 'pizza', |
|
1532 'text' => $this->search_node->title, |
|
1533 ), |
|
1534 'user' => array( |
|
1535 'path' => 'user', |
|
1536 'title' => 'User', |
|
1537 'keys' => $this->search_user->name, |
|
1538 'text' => $this->search_user->mail, |
|
1539 ), |
|
1540 'search_extra_type' => array( |
|
1541 'path' => 'dummy_path', |
|
1542 'title' => 'Dummy search type', |
|
1543 'keys' => 'foo', |
|
1544 'text' => 'Dummy search snippet to display', |
|
1545 ), |
|
1546 ); |
|
1547 $modules = array_keys($module_info); |
|
1548 |
|
1549 // Test each module if it's enabled as the only search module. |
|
1550 foreach ($modules as $module) { |
|
1551 // Enable the one module and disable other ones. |
|
1552 $info = $module_info[$module]; |
|
1553 $edit = array(); |
|
1554 foreach ($modules as $other) { |
|
1555 $edit['search_active_modules[' . $other . ']'] = (($other == $module) ? $module : FALSE); |
|
1556 } |
|
1557 $edit['search_default_module'] = $module; |
|
1558 $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration')); |
|
1559 |
|
1560 // Run a search from the correct search URL. |
|
1561 $this->drupalGet('search/' . $info['path'] . '/' . $info['keys']); |
|
1562 $this->assertNoText('no results', $info['title'] . ' search found results'); |
|
1563 $this->assertText($info['text'], 'Correct search text found'); |
|
1564 |
|
1565 // Verify that other module search tab titles are not visible. |
|
1566 foreach ($modules as $other) { |
|
1567 if ($other != $module) { |
|
1568 $title = $module_info[$other]['title']; |
|
1569 $this->assertNoText($title, $title . ' search tab is not shown'); |
|
1570 } |
|
1571 } |
|
1572 |
|
1573 // Run a search from the search block on the node page. Verify you get |
|
1574 // to this module's search results page. |
|
1575 $terms = array('search_block_form' => $info['keys']); |
|
1576 $this->drupalPost('node', $terms, t('Search')); |
|
1577 $this->assertEqual( |
|
1578 $this->getURL(), |
|
1579 url('search/' . $info['path'] . '/' . $info['keys'], array('absolute' => TRUE)), |
|
1580 'Block redirected to right search page'); |
|
1581 |
|
1582 // Try an invalid search path. Should redirect to our active module. |
|
1583 $this->drupalGet('search/not_a_module_path'); |
|
1584 $this->assertEqual( |
|
1585 $this->getURL(), |
|
1586 url('search/' . $info['path'], array('absolute' => TRUE)), |
|
1587 'Invalid search path redirected to default search page'); |
|
1588 } |
|
1589 |
|
1590 // Test with all search modules enabled. When you go to the search |
|
1591 // page or run search, all modules should be shown. |
|
1592 $edit = array(); |
|
1593 foreach ($modules as $module) { |
|
1594 $edit['search_active_modules[' . $module . ']'] = $module; |
|
1595 } |
|
1596 $edit['search_default_module'] = 'node'; |
|
1597 |
|
1598 $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration')); |
|
1599 |
|
1600 foreach (array('search/node/pizza', 'search/node') as $path) { |
|
1601 $this->drupalGet($path); |
|
1602 foreach ($modules as $module) { |
|
1603 $title = $module_info[$module]['title']; |
|
1604 $this->assertText($title, format_string('%title search tab is shown', array('%title' => $title))); |
|
1605 } |
|
1606 } |
|
1607 } |
|
1608 } |
|
1609 |
|
1610 /** |
|
1611 * Tests the search_excerpt() function. |
|
1612 */ |
|
1613 class SearchExcerptTestCase extends DrupalWebTestCase { |
|
1614 public static function getInfo() { |
|
1615 return array( |
|
1616 'name' => 'Search excerpt extraction', |
|
1617 'description' => 'Tests that the search_excerpt() function works.', |
|
1618 'group' => 'Search', |
|
1619 ); |
|
1620 } |
|
1621 |
|
1622 function setUp() { |
|
1623 parent::setUp('search'); |
|
1624 } |
|
1625 |
|
1626 /** |
|
1627 * Tests search_excerpt() with several simulated search keywords. |
|
1628 * |
|
1629 * Passes keywords and a sample marked up string, "The quick |
|
1630 * brown fox jumps over the lazy dog", and compares it to the |
|
1631 * correctly marked up string. The correctly marked up string |
|
1632 * contains either highlighted keywords or the original marked |
|
1633 * up string if no keywords matched the string. |
|
1634 */ |
|
1635 function testSearchExcerpt() { |
|
1636 // Make some text with entities and tags. |
|
1637 $text = 'The <strong>quick</strong> <a href="#">brown</a> fox & jumps <h2>over</h2> the lazy dog'; |
|
1638 // Note: The search_excerpt() function adds some extra spaces -- not |
|
1639 // important for HTML formatting. Remove these for comparison. |
|
1640 $expected = 'The quick brown fox & jumps over the lazy dog'; |
|
1641 $result = preg_replace('| +|', ' ', search_excerpt('nothing', $text)); |
|
1642 $this->assertEqual(preg_replace('| +|', ' ', $result), $expected, 'Entire string is returned when keyword is not found in short string'); |
|
1643 |
|
1644 $result = preg_replace('| +|', ' ', search_excerpt('fox', $text)); |
|
1645 $this->assertEqual($result, 'The quick brown <strong>fox</strong> & jumps over the lazy dog ...', 'Found keyword is highlighted'); |
|
1646 |
|
1647 $longtext = str_repeat($text . ' ', 10); |
|
1648 $result = preg_replace('| +|', ' ', search_excerpt('nothing', $longtext)); |
|
1649 $this->assertTrue(strpos($result, $expected) === 0, 'When keyword is not found in long string, return value starts as expected'); |
|
1650 |
|
1651 $entities = str_repeat('készítése ', 20); |
|
1652 $result = preg_replace('| +|', ' ', search_excerpt('nothing', $entities)); |
|
1653 $this->assertFalse(strpos($result, '&'), 'Entities are not present in excerpt'); |
|
1654 $this->assertTrue(strpos($result, 'í') > 0, 'Entities are converted in excerpt'); |
|
1655 |
|
1656 // The node body that will produce this rendered $text is: |
|
1657 // 123456789 HTMLTest +123456789+‘ +‘ +‘ +‘ +12345678 +‘ +‘ +‘ ‘ |
|
1658 $text = "<div class=\"field field-name-body field-type-text-with-summary field-label-hidden\"><div class=\"field-items\"><div class=\"field-item even\" property=\"content:encoded\"><p>123456789 HTMLTest +123456789+‘ +‘ +‘ +‘ +12345678 +‘ +‘ +‘ ‘</p>\n</div></div></div> "; |
|
1659 $result = search_excerpt('HTMLTest', $text); |
|
1660 $this->assertFalse(empty($result), 'Rendered Multi-byte HTML encodings are not corrupted in search excerpts'); |
|
1661 } |
|
1662 |
|
1663 /** |
|
1664 * Tests search_excerpt() with search keywords matching simplified words. |
|
1665 * |
|
1666 * Excerpting should handle keywords that are matched only after going through |
|
1667 * search_simplify(). This test passes keywords that match simplified words |
|
1668 * and compares them with strings that contain the original unsimplified word. |
|
1669 */ |
|
1670 function testSearchExcerptSimplified() { |
|
1671 $lorem1 = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae arcu at leo cursus laoreet. Curabitur dui tortor, adipiscing malesuada tempor in, bibendum ac diam. Cras non tellus a libero pellentesque condimentum. What is a Drupalism? Suspendisse ac lacus libero. Ut non est vel nisl faucibus interdum nec sed leo. Pellentesque sem risus, vulputate eu semper eget, auctor in libero.'; |
|
1672 $lorem2 = 'Ut fermentum est vitae metus convallis scelerisque. Phasellus pellentesque rhoncus tellus, eu dignissim purus posuere id. Quisque eu fringilla ligula. Morbi ullamcorper, lorem et mattis egestas, tortor neque pretium velit, eget eleifend odio turpis eu purus. Donec vitae metus quis leo pretium tincidunt a pulvinar sem. Morbi adipiscing laoreet mauris vel placerat. Nullam elementum, nisl sit amet scelerisque malesuada, dolor nunc hendrerit quam, eu ultrices erat est in orci.'; |
|
1673 |
|
1674 // Make some text with some keywords that will get simplified. |
|
1675 $text = $lorem1 . ' Number: 123456.7890 Hyphenated: one-two abc,def ' . $lorem2; |
|
1676 // Note: The search_excerpt() function adds some extra spaces -- not |
|
1677 // important for HTML formatting. Remove these for comparison. |
|
1678 $result = preg_replace('| +|', ' ', search_excerpt('123456.7890', $text)); |
|
1679 $this->assertTrue(strpos($result, 'Number: <strong>123456.7890</strong>') !== FALSE, 'Numeric keyword is highlighted with exact match'); |
|
1680 |
|
1681 $result = preg_replace('| +|', ' ', search_excerpt('1234567890', $text)); |
|
1682 $this->assertTrue(strpos($result, 'Number: <strong>123456.7890</strong>') !== FALSE, 'Numeric keyword is highlighted with simplified match'); |
|
1683 |
|
1684 $result = preg_replace('| +|', ' ', search_excerpt('Number 1234567890', $text)); |
|
1685 $this->assertTrue(strpos($result, '<strong>Number</strong>: <strong>123456.7890</strong>') !== FALSE, 'Punctuated and numeric keyword is highlighted with simplified match'); |
|
1686 |
|
1687 $result = preg_replace('| +|', ' ', search_excerpt('"Number 1234567890"', $text)); |
|
1688 $this->assertTrue(strpos($result, '<strong>Number: 123456.7890</strong>') !== FALSE, 'Phrase with punctuated and numeric keyword is highlighted with simplified match'); |
|
1689 |
|
1690 $result = preg_replace('| +|', ' ', search_excerpt('"Hyphenated onetwo"', $text)); |
|
1691 $this->assertTrue(strpos($result, '<strong>Hyphenated: one-two</strong>') !== FALSE, 'Phrase with punctuated and hyphenated keyword is highlighted with simplified match'); |
|
1692 |
|
1693 $result = preg_replace('| +|', ' ', search_excerpt('"abc def"', $text)); |
|
1694 $this->assertTrue(strpos($result, '<strong>abc,def</strong>') !== FALSE, 'Phrase with keyword simplified into two separate words is highlighted with simplified match'); |
|
1695 |
|
1696 // Test phrases with characters which are being truncated. |
|
1697 $result = preg_replace('| +|', ' ', search_excerpt('"ipsum _"', $text)); |
|
1698 $this->assertTrue(strpos($result, '<strong>ipsum </strong>') !== FALSE, 'Only valid part of the phrase is highlighted and invalid part containing "_" is ignored.'); |
|
1699 |
|
1700 $result = preg_replace('| +|', ' ', search_excerpt('"ipsum 0000"', $text)); |
|
1701 $this->assertTrue(strpos($result, '<strong>ipsum </strong>') !== FALSE, 'Only valid part of the phrase is highlighted and invalid part "0000" is ignored.'); |
|
1702 |
|
1703 // Test combination of the valid keyword and keyword containing only |
|
1704 // characters which are being truncated during simplification. |
|
1705 $result = preg_replace('| +|', ' ', search_excerpt('ipsum _', $text)); |
|
1706 $this->assertTrue(strpos($result, '<strong>ipsum</strong>') !== FALSE, 'Only valid keyword is highlighted and invalid keyword "_" is ignored.'); |
|
1707 |
|
1708 $result = preg_replace('| +|', ' ', search_excerpt('ipsum 0000', $text)); |
|
1709 $this->assertTrue(strpos($result, '<strong>ipsum</strong>') !== FALSE, 'Only valid keyword is highlighted and invalid keyword "0000" is ignored.'); |
|
1710 } |
|
1711 } |
|
1712 |
|
1713 /** |
|
1714 * Test the CJK tokenizer. |
|
1715 */ |
|
1716 class SearchTokenizerTestCase extends DrupalWebTestCase { |
|
1717 public static function getInfo() { |
|
1718 return array( |
|
1719 'name' => 'CJK tokenizer', |
|
1720 'description' => 'Check that CJK tokenizer works as intended.', |
|
1721 'group' => 'Search', |
|
1722 ); |
|
1723 } |
|
1724 |
|
1725 function setUp() { |
|
1726 parent::setUp('search'); |
|
1727 } |
|
1728 |
|
1729 /** |
|
1730 * Verifies that strings of CJK characters are tokenized. |
|
1731 * |
|
1732 * The search_simplify() function does special things with numbers, symbols, |
|
1733 * and punctuation. So we only test that CJK characters that are not in these |
|
1734 * character classes are tokenized properly. See PREG_CLASS_CKJ for more |
|
1735 * information. |
|
1736 */ |
|
1737 function testTokenizer() { |
|
1738 // Set the minimum word size to 1 (to split all CJK characters) and make |
|
1739 // sure CJK tokenizing is turned on. |
|
1740 variable_set('minimum_word_size', 1); |
|
1741 variable_set('overlap_cjk', TRUE); |
|
1742 $this->refreshVariables(); |
|
1743 |
|
1744 // Create a string of CJK characters from various character ranges in |
|
1745 // the Unicode tables. |
|
1746 |
|
1747 // Beginnings of the character ranges. |
|
1748 $starts = array( |
|
1749 'CJK unified' => 0x4e00, |
|
1750 'CJK Ext A' => 0x3400, |
|
1751 'CJK Compat' => 0xf900, |
|
1752 'Hangul Jamo' => 0x1100, |
|
1753 'Hangul Ext A' => 0xa960, |
|
1754 'Hangul Ext B' => 0xd7b0, |
|
1755 'Hangul Compat' => 0x3131, |
|
1756 'Half non-punct 1' => 0xff21, |
|
1757 'Half non-punct 2' => 0xff41, |
|
1758 'Half non-punct 3' => 0xff66, |
|
1759 'Hangul Syllables' => 0xac00, |
|
1760 'Hiragana' => 0x3040, |
|
1761 'Katakana' => 0x30a1, |
|
1762 'Katakana Ext' => 0x31f0, |
|
1763 'CJK Reserve 1' => 0x20000, |
|
1764 'CJK Reserve 2' => 0x30000, |
|
1765 'Bomofo' => 0x3100, |
|
1766 'Bomofo Ext' => 0x31a0, |
|
1767 'Lisu' => 0xa4d0, |
|
1768 'Yi' => 0xa000, |
|
1769 ); |
|
1770 |
|
1771 // Ends of the character ranges. |
|
1772 $ends = array( |
|
1773 'CJK unified' => 0x9fcf, |
|
1774 'CJK Ext A' => 0x4dbf, |
|
1775 'CJK Compat' => 0xfaff, |
|
1776 'Hangul Jamo' => 0x11ff, |
|
1777 'Hangul Ext A' => 0xa97f, |
|
1778 'Hangul Ext B' => 0xd7ff, |
|
1779 'Hangul Compat' => 0x318e, |
|
1780 'Half non-punct 1' => 0xff3a, |
|
1781 'Half non-punct 2' => 0xff5a, |
|
1782 'Half non-punct 3' => 0xffdc, |
|
1783 'Hangul Syllables' => 0xd7af, |
|
1784 'Hiragana' => 0x309f, |
|
1785 'Katakana' => 0x30ff, |
|
1786 'Katakana Ext' => 0x31ff, |
|
1787 'CJK Reserve 1' => 0x2fffd, |
|
1788 'CJK Reserve 2' => 0x3fffd, |
|
1789 'Bomofo' => 0x312f, |
|
1790 'Bomofo Ext' => 0x31b7, |
|
1791 'Lisu' => 0xa4fd, |
|
1792 'Yi' => 0xa48f, |
|
1793 ); |
|
1794 |
|
1795 // Generate characters consisting of starts, midpoints, and ends. |
|
1796 $chars = array(); |
|
1797 $charcodes = array(); |
|
1798 foreach ($starts as $key => $value) { |
|
1799 $charcodes[] = $starts[$key]; |
|
1800 $chars[] = $this->code2utf($starts[$key]); |
|
1801 $mid = round(0.5 * ($starts[$key] + $ends[$key])); |
|
1802 $charcodes[] = $mid; |
|
1803 $chars[] = $this->code2utf($mid); |
|
1804 $charcodes[] = $ends[$key]; |
|
1805 $chars[] = $this->code2utf($ends[$key]); |
|
1806 } |
|
1807 |
|
1808 // Merge into a string and tokenize. |
|
1809 $string = implode('', $chars); |
|
1810 $out = trim(search_simplify($string)); |
|
1811 $expected = drupal_strtolower(implode(' ', $chars)); |
|
1812 |
|
1813 // Verify that the output matches what we expect. |
|
1814 $this->assertEqual($out, $expected, 'CJK tokenizer worked on all supplied CJK characters'); |
|
1815 } |
|
1816 |
|
1817 /** |
|
1818 * Verifies that strings of non-CJK characters are not tokenized. |
|
1819 * |
|
1820 * This is just a sanity check - it verifies that strings of letters are |
|
1821 * not tokenized. |
|
1822 */ |
|
1823 function testNoTokenizer() { |
|
1824 // Set the minimum word size to 1 (to split all CJK characters) and make |
|
1825 // sure CJK tokenizing is turned on. |
|
1826 variable_set('minimum_word_size', 1); |
|
1827 variable_set('overlap_cjk', TRUE); |
|
1828 $this->refreshVariables(); |
|
1829 |
|
1830 $letters = 'abcdefghijklmnopqrstuvwxyz'; |
|
1831 $out = trim(search_simplify($letters)); |
|
1832 |
|
1833 $this->assertEqual($letters, $out, 'Letters are not CJK tokenized'); |
|
1834 } |
|
1835 |
|
1836 /** |
|
1837 * Like PHP chr() function, but for unicode characters. |
|
1838 * |
|
1839 * chr() only works for ASCII characters up to character 255. This function |
|
1840 * converts a number to the corresponding unicode character. Adapted from |
|
1841 * functions supplied in comments on several functions on php.net. |
|
1842 */ |
|
1843 function code2utf($num) { |
|
1844 if ($num < 128) { |
|
1845 return chr($num); |
|
1846 } |
|
1847 |
|
1848 if ($num < 2048) { |
|
1849 return chr(($num >> 6) + 192) . chr(($num & 63) + 128); |
|
1850 } |
|
1851 |
|
1852 if ($num < 65536) { |
|
1853 return chr(($num >> 12) + 224) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); |
|
1854 } |
|
1855 |
|
1856 if ($num < 2097152) { |
|
1857 return chr(($num >> 18) + 240) . chr((($num >> 12) & 63) + 128) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); |
|
1858 } |
|
1859 |
|
1860 return ''; |
|
1861 } |
|
1862 } |
|
1863 |
|
1864 /** |
|
1865 * Tests that we can embed a form in search results and submit it. |
|
1866 */ |
|
1867 class SearchEmbedForm extends DrupalWebTestCase { |
|
1868 /** |
|
1869 * Node used for testing. |
|
1870 */ |
|
1871 public $node; |
|
1872 |
|
1873 /** |
|
1874 * Count of how many times the form has been submitted. |
|
1875 */ |
|
1876 public $submit_count = 0; |
|
1877 |
|
1878 public static function getInfo() { |
|
1879 return array( |
|
1880 'name' => 'Embedded forms', |
|
1881 'description' => 'Verifies that a form embedded in search results works', |
|
1882 'group' => 'Search', |
|
1883 ); |
|
1884 } |
|
1885 |
|
1886 function setUp() { |
|
1887 parent::setUp('search', 'search_embedded_form'); |
|
1888 |
|
1889 // Create a user and a node, and update the search index. |
|
1890 $test_user = $this->drupalCreateUser(array('access content', 'search content', 'administer nodes')); |
|
1891 $this->drupalLogin($test_user); |
|
1892 |
|
1893 $this->node = $this->drupalCreateNode(); |
|
1894 |
|
1895 node_update_index(); |
|
1896 search_update_totals(); |
|
1897 |
|
1898 // Set up a dummy initial count of times the form has been submitted. |
|
1899 $this->submit_count = 12; |
|
1900 variable_set('search_embedded_form_submitted', $this->submit_count); |
|
1901 $this->refreshVariables(); |
|
1902 } |
|
1903 |
|
1904 /** |
|
1905 * Tests that the embedded form appears and can be submitted. |
|
1906 */ |
|
1907 function testEmbeddedForm() { |
|
1908 // First verify we can submit the form from the module's page. |
|
1909 $this->drupalPost('search_embedded_form', |
|
1910 array('name' => 'John'), |
|
1911 t('Send away')); |
|
1912 $this->assertText(t('Test form was submitted'), 'Form message appears'); |
|
1913 $count = variable_get('search_embedded_form_submitted', 0); |
|
1914 $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct'); |
|
1915 $this->submit_count = $count; |
|
1916 |
|
1917 // Now verify that we can see and submit the form from the search results. |
|
1918 $this->drupalGet('search/node/' . $this->node->title); |
|
1919 $this->assertText(t('Your name'), 'Form is visible'); |
|
1920 $this->drupalPost('search/node/' . $this->node->title, |
|
1921 array('name' => 'John'), |
|
1922 t('Send away')); |
|
1923 $this->assertText(t('Test form was submitted'), 'Form message appears'); |
|
1924 $count = variable_get('search_embedded_form_submitted', 0); |
|
1925 $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct'); |
|
1926 $this->submit_count = $count; |
|
1927 |
|
1928 // Now verify that if we submit the search form, it doesn't count as |
|
1929 // our form being submitted. |
|
1930 $this->drupalPost('search', |
|
1931 array('keys' => 'foo'), |
|
1932 t('Search')); |
|
1933 $this->assertNoText(t('Test form was submitted'), 'Form message does not appear'); |
|
1934 $count = variable_get('search_embedded_form_submitted', 0); |
|
1935 $this->assertEqual($this->submit_count, $count, 'Form submission count is correct'); |
|
1936 $this->submit_count = $count; |
|
1937 } |
|
1938 } |
|
1939 |
|
1940 /** |
|
1941 * Tests that hook_search_page runs. |
|
1942 */ |
|
1943 class SearchPageOverride extends DrupalWebTestCase { |
|
1944 public $search_user; |
|
1945 |
|
1946 public static function getInfo() { |
|
1947 return array( |
|
1948 'name' => 'Search page override', |
|
1949 'description' => 'Verify that hook_search_page can override search page display.', |
|
1950 'group' => 'Search', |
|
1951 ); |
|
1952 } |
|
1953 |
|
1954 function setUp() { |
|
1955 parent::setUp('search', 'search_extra_type'); |
|
1956 |
|
1957 // Login as a user that can create and search content. |
|
1958 $this->search_user = $this->drupalCreateUser(array('search content', 'administer search')); |
|
1959 $this->drupalLogin($this->search_user); |
|
1960 |
|
1961 // Enable the extra type module for searching. |
|
1962 variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type')); |
|
1963 menu_rebuild(); |
|
1964 } |
|
1965 |
|
1966 function testSearchPageHook() { |
|
1967 $keys = 'bike shed ' . $this->randomName(); |
|
1968 $this->drupalGet("search/dummy_path/{$keys}"); |
|
1969 $this->assertText('Dummy search snippet', 'Dummy search snippet is shown'); |
|
1970 $this->assertText('Test page text is here', 'Page override is working'); |
|
1971 } |
|
1972 } |
|
1973 |
|
1974 /** |
|
1975 * Test node search with multiple languages. |
|
1976 */ |
|
1977 class SearchLanguageTestCase extends DrupalWebTestCase { |
|
1978 public static function getInfo() { |
|
1979 return array( |
|
1980 'name' => 'Search language selection', |
|
1981 'description' => 'Tests advanced search with different languages enabled.', |
|
1982 'group' => 'Search', |
|
1983 ); |
|
1984 } |
|
1985 |
|
1986 /** |
|
1987 * Implementation setUp(). |
|
1988 */ |
|
1989 function setUp() { |
|
1990 parent::setUp('search', 'locale'); |
|
1991 |
|
1992 // Create and login user. |
|
1993 $test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search', 'administer nodes', 'administer languages', 'access administration pages')); |
|
1994 $this->drupalLogin($test_user); |
|
1995 } |
|
1996 |
|
1997 function testLanguages() { |
|
1998 // Check that there are initially no languages displayed. |
|
1999 $this->drupalGet('search/node'); |
|
2000 $this->assertNoText(t('Languages'), 'No languages to choose from.'); |
|
2001 |
|
2002 // Add predefined language. |
|
2003 $edit = array('langcode' => 'fr'); |
|
2004 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); |
|
2005 $this->assertText('fr', 'Language added successfully.'); |
|
2006 |
|
2007 // Now we should have languages displayed. |
|
2008 $this->drupalGet('search/node'); |
|
2009 $this->assertText(t('Languages'), 'Languages displayed to choose from.'); |
|
2010 $this->assertText(t('English'), 'English is a possible choice.'); |
|
2011 $this->assertText(t('French'), 'French is a possible choice.'); |
|
2012 |
|
2013 // Ensure selecting no language does not make the query different. |
|
2014 $this->drupalPost('search/node', array(), t('Advanced search')); |
|
2015 $this->assertEqual($this->getUrl(), url('search/node/', array('absolute' => TRUE)), 'Correct page redirection, no language filtering.'); |
|
2016 |
|
2017 // Pick French and ensure it is selected. |
|
2018 $edit = array('language[fr]' => TRUE); |
|
2019 $this->drupalPost('search/node', $edit, t('Advanced search')); |
|
2020 $this->assertFieldByXPath('//input[@name="keys"]', 'language:fr', 'Language filter added to query.'); |
|
2021 |
|
2022 // Change the default language and disable English. |
|
2023 $path = 'admin/config/regional/language'; |
|
2024 $this->drupalGet($path); |
|
2025 $this->assertFieldChecked('edit-site-default-en', 'English is the default language.'); |
|
2026 $edit = array('site_default' => 'fr'); |
|
2027 $this->drupalPost(NULL, $edit, t('Save configuration')); |
|
2028 $this->assertNoFieldChecked('edit-site-default-en', 'Default language updated.'); |
|
2029 $edit = array('enabled[en]' => FALSE); |
|
2030 $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration')); |
|
2031 $this->assertNoFieldChecked('edit-enabled-en', 'Language disabled.'); |
|
2032 |
|
2033 // Check that there are again no languages displayed. |
|
2034 $this->drupalGet('search/node'); |
|
2035 $this->assertNoText(t('Languages'), 'No languages to choose from.'); |
|
2036 } |
|
2037 } |
|
2038 |
|
2039 /** |
|
2040 * Tests node search with node access control. |
|
2041 */ |
|
2042 class SearchNodeAccessTest extends DrupalWebTestCase { |
|
2043 public $test_user; |
|
2044 |
|
2045 public static function getInfo() { |
|
2046 return array( |
|
2047 'name' => 'Search and node access', |
|
2048 'description' => 'Tests search functionality with node access control.', |
|
2049 'group' => 'Search', |
|
2050 ); |
|
2051 } |
|
2052 |
|
2053 function setUp() { |
|
2054 parent::setUp('search', 'node_access_test'); |
|
2055 node_access_rebuild(); |
|
2056 |
|
2057 // Create a test user and log in. |
|
2058 $this->test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search')); |
|
2059 $this->drupalLogin($this->test_user); |
|
2060 } |
|
2061 |
|
2062 /** |
|
2063 * Tests that search works with punctuation and HTML entities. |
|
2064 */ |
|
2065 function testPhraseSearchPunctuation() { |
|
2066 $node = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => "The bunny's ears were fuzzy."))))); |
|
2067 $node2 = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => 'Dignissim Aliquam & Quieligo meus natu quae quia te. Damnum© erat— neo pneum. Facilisi feugiat ibidem ratis.'))))); |
|
2068 |
|
2069 // Update the search index. |
|
2070 module_invoke_all('update_index'); |
|
2071 search_update_totals(); |
|
2072 |
|
2073 // Refresh variables after the treatment. |
|
2074 $this->refreshVariables(); |
|
2075 |
|
2076 // Submit a phrase wrapped in double quotes to include the punctuation. |
|
2077 $edit = array('keys' => '"bunny\'s"'); |
|
2078 $this->drupalPost('search/node', $edit, t('Search')); |
|
2079 $this->assertText($node->title); |
|
2080 |
|
2081 // Search for "&" and verify entities are not broken up in the output. |
|
2082 $edit = array('keys' => '&'); |
|
2083 $this->drupalPost('search/node', $edit, t('Search')); |
|
2084 $this->assertNoRaw('<strong>&</strong>amp;'); |
|
2085 $this->assertText('You must include at least one positive keyword'); |
|
2086 |
|
2087 $edit = array('keys' => '&'); |
|
2088 $this->drupalPost('search/node', $edit, t('Search')); |
|
2089 $this->assertNoRaw('<strong>&</strong>amp;'); |
|
2090 $this->assertText('You must include at least one positive keyword'); |
|
2091 } |
|
2092 } |
|
2093 |
|
2094 /** |
|
2095 * Tests node search with query tags. |
|
2096 */ |
|
2097 class SearchNodeTagTest extends DrupalWebTestCase { |
|
2098 public $test_user; |
|
2099 |
|
2100 public static function getInfo() { |
|
2101 return array( |
|
2102 'name' => 'Node search query tags', |
|
2103 'description' => 'Tests Node search tags functionality.', |
|
2104 'group' => 'Search', |
|
2105 ); |
|
2106 } |
|
2107 |
|
2108 function setUp() { |
|
2109 parent::setUp('search', 'search_node_tags'); |
|
2110 node_access_rebuild(); |
|
2111 |
|
2112 // Create a test user and log in. |
|
2113 $this->test_user = $this->drupalCreateUser(array('search content')); |
|
2114 $this->drupalLogin($this->test_user); |
|
2115 } |
|
2116 |
|
2117 /** |
|
2118 * Tests that the correct tags are available and hooks invoked. |
|
2119 */ |
|
2120 function testNodeSearchQueryTags() { |
|
2121 $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => 'testing testing testing.'))))); |
|
2122 |
|
2123 // Update the search index. |
|
2124 module_invoke_all('update_index'); |
|
2125 search_update_totals(); |
|
2126 |
|
2127 $edit = array('keys' => 'testing'); |
|
2128 $this->drupalPost('search/node', $edit, t('Search')); |
|
2129 |
|
2130 $this->assertTrue(variable_get('search_node_tags_test_query_tag', FALSE), 'hook_query_alter() was invoked and the query contained the "search_node" tag.'); |
|
2131 $this->assertTrue(variable_get('search_node_tags_test_query_tag_hook', FALSE), 'hook_query_search_node_alter() was invoked.'); |
|
2132 } |
|
2133 } |
|
2134 |
|
2135 /** |
|
2136 * Tests searching with locale values set. |
|
2137 */ |
|
2138 class SearchSetLocaleTest extends DrupalWebTestCase { |
|
2139 |
|
2140 public static function getInfo() { |
|
2141 return array( |
|
2142 'name' => 'Search with numeric locale set', |
|
2143 'description' => 'Check that search works with numeric locale settings', |
|
2144 'group' => 'Search', |
|
2145 ); |
|
2146 } |
|
2147 |
|
2148 function setUp() { |
|
2149 parent::setUp('search'); |
|
2150 |
|
2151 // Create a simple node so something will be put in the index. |
|
2152 $info = array( |
|
2153 'body' => array(LANGUAGE_NONE => array(array('value' => 'Tapir'))), |
|
2154 ); |
|
2155 $this->drupalCreateNode($info); |
|
2156 |
|
2157 // Run cron to index. |
|
2158 $this->cronRun(); |
|
2159 } |
|
2160 |
|
2161 /** |
|
2162 * Verify that search works with a numeric locale set. |
|
2163 */ |
|
2164 public function testSearchWithNumericLocale() { |
|
2165 // French decimal point is comma. |
|
2166 setlocale(LC_NUMERIC, 'fr_FR'); |
|
2167 |
|
2168 // An exception will be thrown if a float in the wrong format occurs in the |
|
2169 // query to the database, so an assertion is not necessary here. |
|
2170 db_select('search_index', 'i') |
|
2171 ->extend('searchquery') |
|
2172 ->searchexpression('tapir', 'node') |
|
2173 ->execute(); |
|
2174 } |
|
2175 } |