More updates to stop using dev or alpha or beta versions.
[yaffs-website] / web / core / modules / search / src / Tests / SearchConfigSettingsFormTest.php
1 <?php
2
3 namespace Drupal\search\Tests;
4
5 use Drupal\Core\Url;
6 use Drupal\search\Entity\SearchPage;
7
8 /**
9  * Verify the search config settings form.
10  *
11  * @group search
12  */
13 class SearchConfigSettingsFormTest extends SearchTestBase {
14
15   /**
16    * Modules to enable.
17    *
18    * @var array
19    */
20   public static $modules = ['block', 'search_extra_type', 'test_page_test'];
21
22   /**
23    * User who can search and administer search.
24    *
25    * @var \Drupal\user\UserInterface
26    */
27   protected $searchUser;
28
29   /**
30    * Node indexed for searching.
31    *
32    * @var \Drupal\node\NodeInterface
33    */
34   protected $searchNode;
35
36   protected function setUp() {
37     parent::setUp();
38
39     // Log in as a user that can create and search content.
40     $this->searchUser = $this->drupalCreateUser(['search content', 'administer search', 'administer nodes', 'bypass node access', 'access user profiles', 'administer users', 'administer blocks', 'access site reports']);
41     $this->drupalLogin($this->searchUser);
42
43     // Add a single piece of content and index it.
44     $node = $this->drupalCreateNode();
45     $this->searchNode = $node;
46     // Link the node to itself to test that it's only indexed once. The content
47     // also needs the word "pizza" so we can use it as the search keyword.
48     $body_key = 'body[0][value]';
49     $edit[$body_key] = \Drupal::l($node->label(), $node->urlInfo()) . ' pizza sandwich';
50     $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
51
52     $this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
53     search_update_totals();
54
55     // Enable the search block.
56     $this->drupalPlaceBlock('search_form_block');
57     $this->drupalPlaceBlock('local_tasks_block');
58     $this->drupalPlaceBlock('page_title_block');
59   }
60
61   /**
62    * Verifies the search settings form.
63    */
64   public function testSearchSettingsPage() {
65
66     // Test that the settings form displays the correct count of items left to index.
67     $this->drupalGet('admin/config/search/pages');
68     $this->assertText(t('There are @count items left to index.', ['@count' => 0]));
69
70     // Test the re-index button.
71     $this->drupalPostForm('admin/config/search/pages', [], t('Re-index site'));
72     $this->assertText(t('Are you sure you want to re-index the site'));
73     $this->drupalPostForm('admin/config/search/pages/reindex', [], t('Re-index site'));
74     $this->assertText(t('All search indexes will be rebuilt'));
75     $this->drupalGet('admin/config/search/pages');
76     $this->assertText(t('There is 1 item left to index.'));
77
78     // Test that the form saves with the default values.
79     $this->drupalPostForm('admin/config/search/pages', [], t('Save configuration'));
80     $this->assertText(t('The configuration options have been saved.'), 'Form saves with the default values.');
81
82     // Test that the form does not save with an invalid word length.
83     $edit = [
84       'minimum_word_size' => $this->randomMachineName(3),
85     ];
86     $this->drupalPostForm('admin/config/search/pages', $edit, t('Save configuration'));
87     $this->assertNoText(t('The configuration options have been saved.'), 'Form does not save with an invalid word length.');
88
89     // Test logging setting. It should be off by default.
90     $text = $this->randomMachineName(5);
91     $this->drupalPostForm('search/node', ['keys' => $text], t('Search'));
92     $this->drupalGet('admin/reports/dblog');
93     $this->assertNoLink('Searched Content for ' . $text . '.', 'Search was not logged');
94
95     // Turn on logging.
96     $edit = ['logging' => TRUE];
97     $this->drupalPostForm('admin/config/search/pages', $edit, t('Save configuration'));
98     $text = $this->randomMachineName(5);
99     $this->drupalPostForm('search/node', ['keys' => $text], t('Search'));
100     $this->drupalGet('admin/reports/dblog');
101     $this->assertLink('Searched Content for ' . $text . '.', 0, 'Search was logged');
102
103   }
104
105   /**
106    * Verifies plugin-supplied settings form.
107    */
108   public function testSearchModuleSettingsPage() {
109     $this->drupalGet('admin/config/search/pages');
110     $this->clickLink(t('Edit'), 1);
111
112     // Ensure that the default setting was picked up from the default config
113     $this->assertTrue($this->xpath('//select[@id="edit-extra-type-settings-boost"]//option[@value="bi" and @selected="selected"]'), 'Module specific settings are picked up from the default config');
114
115     // Change extra type setting and also modify a common search setting.
116     $edit = [
117       'extra_type_settings[boost]' => 'ii',
118     ];
119     $this->drupalPostForm(NULL, $edit, t('Save search page'));
120
121     // Ensure that the modifications took effect.
122     $this->assertRaw(t('The %label search page has been updated.', ['%label' => 'Dummy search type']));
123     $this->drupalGet('admin/config/search/pages/manage/dummy_search_type');
124     $this->assertTrue($this->xpath('//select[@id="edit-extra-type-settings-boost"]//option[@value="ii" and @selected="selected"]'), 'Module specific settings can be changed');
125   }
126
127   /**
128    * Verifies that you can disable individual search plugins.
129    */
130   public function testSearchModuleDisabling() {
131     // Array of search plugins to test: 'keys' are the keywords to search for,
132     // and 'text' is the text to assert is on the results page.
133     $plugin_info = [
134       'node_search' => [
135         'keys' => 'pizza',
136         'text' => $this->searchNode->label(),
137       ],
138       'user_search' => [
139         'keys' => $this->searchUser->getUsername(),
140         'text' => $this->searchUser->getEmail(),
141       ],
142       'dummy_search_type' => [
143         'keys' => 'foo',
144         'text' => 'Dummy search snippet to display',
145       ],
146     ];
147     $plugins = array_keys($plugin_info);
148     /** @var $entities \Drupal\search\SearchPageInterface[] */
149     $entities = SearchPage::loadMultiple();
150     // Disable all of the search pages.
151     foreach ($entities as $entity) {
152       $entity->disable()->save();
153     }
154
155     // Test each plugin if it's enabled as the only search plugin.
156     foreach ($entities as $entity_id => $entity) {
157       $this->setDefaultThroughUi($entity_id);
158
159       // Run a search from the correct search URL.
160       $info = $plugin_info[$entity_id];
161       $this->drupalGet('search/' . $entity->getPath(), ['query' => ['keys' => $info['keys']]]);
162       $this->assertResponse(200);
163       $this->assertNoText('no results', $entity->label() . ' search found results');
164       $this->assertText($info['text'], 'Correct search text found');
165
166       // Verify that other plugin search tab labels are not visible.
167       foreach ($plugins as $other) {
168         if ($other != $entity_id) {
169           $label = $entities[$other]->label();
170           $this->assertNoText($label, $label . ' search tab is not shown');
171         }
172       }
173
174       // Run a search from the search block on the node page. Verify you get
175       // to this plugin's search results page.
176       $terms = ['keys' => $info['keys']];
177       $this->submitGetForm('node', $terms, t('Search'));
178       $current = $this->getURL();
179       $expected = \Drupal::url('search.view_' . $entity->id(), [], ['query' => ['keys' => $info['keys']], 'absolute' => TRUE]);
180       $this->assertEqual($current, $expected, 'Block redirected to right search page');
181
182       // Try an invalid search path, which should 404.
183       $this->drupalGet('search/not_a_plugin_path');
184       $this->assertResponse(404);
185
186       $entity->disable()->save();
187     }
188
189     // Set the node search as default.
190     $this->setDefaultThroughUi('node_search');
191
192     // Test with all search plugins enabled. When you go to the search
193     // page or run search, all plugins should be shown.
194     foreach ($entities as $entity) {
195       $entity->enable()->save();
196     }
197
198     \Drupal::service('router.builder')->rebuild();
199
200     $paths = [
201       ['path' => 'search/node', 'options' => ['query' => ['keys' => 'pizza']]],
202       ['path' => 'search/node', 'options' => []],
203     ];
204
205     foreach ($paths as $item) {
206       $this->drupalGet($item['path'], $item['options']);
207       foreach ($plugins as $entity_id) {
208         $label = $entities[$entity_id]->label();
209         $this->assertText($label, format_string('%label search tab is shown', ['%label' => $label]));
210       }
211     }
212   }
213
214   /**
215    * Tests the ordering of search pages on a clean install.
216    */
217   public function testDefaultSearchPageOrdering() {
218     $this->drupalGet('search');
219     $elements = $this->xpath('//*[contains(@class, :class)]//a', [':class' => 'tabs primary']);
220     $this->assertIdentical((string) $elements[0]['href'], \Drupal::url('search.view_node_search'));
221     $this->assertIdentical((string) $elements[1]['href'], \Drupal::url('search.view_dummy_search_type'));
222     $this->assertIdentical((string) $elements[2]['href'], \Drupal::url('search.view_user_search'));
223   }
224
225   /**
226    * Tests multiple search pages of the same type.
227    */
228   public function testMultipleSearchPages() {
229     $this->assertDefaultSearch('node_search', 'The default page is set to the installer default.');
230     $search_storage = \Drupal::entityManager()->getStorage('search_page');
231     $entities = $search_storage->loadMultiple();
232     $search_storage->delete($entities);
233     $this->assertDefaultSearch(FALSE);
234
235     // Ensure that no search pages are configured.
236     $this->drupalGet('admin/config/search/pages');
237     $this->assertText(t('No search pages have been configured.'));
238
239     // Add a search page.
240     $edit = [];
241     $edit['search_type'] = 'search_extra_type_search';
242     $this->drupalPostForm(NULL, $edit, t('Add search page'));
243     $this->assertTitle('Add new search page | Drupal');
244
245     $first = [];
246     $first['label'] = $this->randomString();
247     $first_id = $first['id'] = strtolower($this->randomMachineName(8));
248     $first['path'] = strtolower($this->randomMachineName(8));
249     $this->drupalPostForm(NULL, $first, t('Save'));
250     $this->assertDefaultSearch($first_id, 'The default page matches the only search page.');
251     $this->assertRaw(t('The %label search page has been added.', ['%label' => $first['label']]));
252
253     // Attempt to add a search page with an existing path.
254     $edit = [];
255     $edit['search_type'] = 'search_extra_type_search';
256     $this->drupalPostForm(NULL, $edit, t('Add search page'));
257     $edit = [];
258     $edit['label'] = $this->randomString();
259     $edit['id'] = strtolower($this->randomMachineName(8));
260     $edit['path'] = $first['path'];
261     $this->drupalPostForm(NULL, $edit, t('Save'));
262     $this->assertText(t('The search page path must be unique.'));
263
264     // Add a second search page.
265     $second = [];
266     $second['label'] = $this->randomString();
267     $second_id = $second['id'] = strtolower($this->randomMachineName(8));
268     $second['path'] = strtolower($this->randomMachineName(8));
269     $this->drupalPostForm(NULL, $second, t('Save'));
270     $this->assertDefaultSearch($first_id, 'The default page matches the only search page.');
271
272     // Ensure both search pages have their tabs displayed.
273     $this->drupalGet('search');
274     $elements = $this->xpath('//*[contains(@class, :class)]//a', [':class' => 'tabs primary']);
275     $this->assertIdentical((string) $elements[0]['href'], Url::fromRoute('search.view_' . $first_id)->toString());
276     $this->assertIdentical((string) $elements[1]['href'], Url::fromRoute('search.view_' . $second_id)->toString());
277
278     // Switch the weight of the search pages and check the order of the tabs.
279     $edit = [
280       'entities[' . $first_id . '][weight]' => 10,
281       'entities[' . $second_id . '][weight]' => -10,
282     ];
283     $this->drupalPostForm('admin/config/search/pages', $edit, t('Save configuration'));
284     $this->drupalGet('search');
285     $elements = $this->xpath('//*[contains(@class, :class)]//a', [':class' => 'tabs primary']);
286     $this->assertIdentical((string) $elements[0]['href'], Url::fromRoute('search.view_' . $second_id)->toString());
287     $this->assertIdentical((string) $elements[1]['href'], Url::fromRoute('search.view_' . $first_id)->toString());
288
289     // Check the initial state of the search pages.
290     $this->drupalGet('admin/config/search/pages');
291     $this->verifySearchPageOperations($first_id, TRUE, FALSE, FALSE, FALSE);
292     $this->verifySearchPageOperations($second_id, TRUE, TRUE, TRUE, FALSE);
293
294     // Change the default search page.
295     $this->clickLink(t('Set as default'));
296     $this->assertRaw(t('The default search page is now %label. Be sure to check the ordering of your search pages.', ['%label' => $second['label']]));
297     $this->verifySearchPageOperations($first_id, TRUE, TRUE, TRUE, FALSE);
298     $this->verifySearchPageOperations($second_id, TRUE, FALSE, FALSE, FALSE);
299
300     // Disable the first search page.
301     $this->clickLink(t('Disable'));
302     $this->assertResponse(200);
303     $this->assertNoLink(t('Disable'));
304     $this->verifySearchPageOperations($first_id, TRUE, TRUE, FALSE, TRUE);
305     $this->verifySearchPageOperations($second_id, TRUE, FALSE, FALSE, FALSE);
306
307     // Enable the first search page.
308     $this->clickLink(t('Enable'));
309     $this->assertResponse(200);
310     $this->verifySearchPageOperations($first_id, TRUE, TRUE, TRUE, FALSE);
311     $this->verifySearchPageOperations($second_id, TRUE, FALSE, FALSE, FALSE);
312
313     // Test deleting.
314     $this->clickLink(t('Delete'));
315     $this->assertRaw(t('Are you sure you want to delete the search page %label?', ['%label' => $first['label']]));
316     $this->drupalPostForm(NULL, [], t('Delete'));
317     $this->assertRaw(t('The search page %label has been deleted.', ['%label' => $first['label']]));
318     $this->verifySearchPageOperations($first_id, FALSE, FALSE, FALSE, FALSE);
319   }
320
321   /**
322    * Tests that the enable/disable/default routes are protected from CSRF.
323    */
324   public function testRouteProtection() {
325     // Ensure that the enable and disable routes are protected.
326     $this->drupalGet('admin/config/search/pages/manage/node_search/enable');
327     $this->assertResponse(403);
328     $this->drupalGet('admin/config/search/pages/manage/node_search/disable');
329     $this->assertResponse(403);
330     $this->drupalGet('admin/config/search/pages/manage/node_search/set-default');
331     $this->assertResponse(403);
332   }
333
334   /**
335    * Checks that the search page operations match expectations.
336    *
337    * @param string $id
338    *   The search page ID to check.
339    * @param bool $edit
340    *   Whether the edit link is expected.
341    * @param bool $delete
342    *   Whether the delete link is expected.
343    * @param bool $disable
344    *   Whether the disable link is expected.
345    * @param bool $enable
346    *   Whether the enable link is expected.
347    */
348   protected function verifySearchPageOperations($id, $edit, $delete, $disable, $enable) {
349     if ($edit) {
350       $this->assertLinkByHref("admin/config/search/pages/manage/$id");
351     }
352     else {
353       $this->assertNoLinkByHref("admin/config/search/pages/manage/$id");
354     }
355     if ($delete) {
356       $this->assertLinkByHref("admin/config/search/pages/manage/$id/delete");
357     }
358     else {
359       $this->assertNoLinkByHref("admin/config/search/pages/manage/$id/delete");
360     }
361     if ($disable) {
362       $this->assertLinkByHref("admin/config/search/pages/manage/$id/disable");
363     }
364     else {
365       $this->assertNoLinkByHref("admin/config/search/pages/manage/$id/disable");
366     }
367     if ($enable) {
368       $this->assertLinkByHref("admin/config/search/pages/manage/$id/enable");
369     }
370     else {
371       $this->assertNoLinkByHref("admin/config/search/pages/manage/$id/enable");
372     }
373   }
374
375   /**
376    * Checks that the default search page matches expectations.
377    *
378    * @param string $expected
379    *   The expected search page.
380    * @param string $message
381    *   (optional) A message to display with the assertion.
382    * @param string $group
383    *   (optional) The group this message is in.
384    */
385   protected function assertDefaultSearch($expected, $message = '', $group = 'Other') {
386     /** @var $search_page_repository \Drupal\search\SearchPageRepositoryInterface */
387     $search_page_repository = \Drupal::service('search.search_page_repository');
388     $this->assertIdentical($search_page_repository->getDefaultSearchPage(), $expected, $message, $group);
389   }
390
391   /**
392    * Sets a search page as the default in the UI.
393    *
394    * @param string $entity_id
395    *   The search page entity ID to enable.
396    */
397   protected function setDefaultThroughUi($entity_id) {
398     $this->drupalGet('admin/config/search/pages');
399     preg_match('|href="([^"]+' . $entity_id . '/set-default[^"]+)"|', $this->getRawContent(), $matches);
400
401     $this->drupalGet($this->getAbsoluteUrl($matches[1]));
402   }
403
404 }