Merge "Align search result CSS with Wikimedia UI color palette"
[lhc/web/wiklou.git] / tests / phpunit / includes / PrefixSearchTest.php
1 <?php
2 /**
3 * @group Search
4 * @group Database
5 * @covers PrefixSearch
6 */
7 class PrefixSearchTest extends MediaWikiLangTestCase {
8 const NS_NONCAP = 12346;
9
10 private $originalHandlers;
11
12 public function addDBDataOnce() {
13 if ( !$this->isWikitextNS( NS_MAIN ) ) {
14 // tests are skipped if NS_MAIN is not wikitext
15 return;
16 }
17
18 $this->insertPage( 'Sandbox' );
19 $this->insertPage( 'Bar' );
20 $this->insertPage( 'Example' );
21 $this->insertPage( 'Example Bar' );
22 $this->insertPage( 'Example Foo' );
23 $this->insertPage( 'Example Foo/Bar' );
24 $this->insertPage( 'Example/Baz' );
25 $this->insertPage( 'Redirect test', '#REDIRECT [[Redirect Test]]' );
26 $this->insertPage( 'Redirect Test' );
27 $this->insertPage( 'Redirect Test Worse Result' );
28 $this->insertPage( 'Redirect test2', '#REDIRECT [[Redirect Test2]]' );
29 $this->insertPage( 'Redirect TEST2', '#REDIRECT [[Redirect Test2]]' );
30 $this->insertPage( 'Redirect Test2' );
31 $this->insertPage( 'Redirect Test2 Worse Result' );
32
33 $this->insertPage( 'Talk:Sandbox' );
34 $this->insertPage( 'Talk:Example' );
35
36 $this->insertPage( 'User:Example' );
37
38 $this->insertPage( Title::makeTitle( self::NS_NONCAP, 'Bar' ) );
39 $this->insertPage( Title::makeTitle( self::NS_NONCAP, 'Upper' ) );
40 $this->insertPage( Title::makeTitle( self::NS_NONCAP, 'sandbox' ) );
41 }
42
43 protected function setUp() {
44 parent::setUp();
45
46 if ( !$this->isWikitextNS( NS_MAIN ) ) {
47 $this->markTestSkipped( 'Main namespace does not support wikitext.' );
48 }
49
50 // Avoid special pages from extensions interferring with the tests
51 $this->setMwGlobals( [
52 'wgSpecialPages' => [],
53 'wgHooks' => [],
54 'wgExtraNamespaces' => [ self::NS_NONCAP => 'NonCap' ],
55 'wgCapitalLinkOverrides' => [ self::NS_NONCAP => false ],
56 ] );
57
58 $this->originalHandlers = TestingAccessWrapper::newFromClass( 'Hooks' )->handlers;
59 TestingAccessWrapper::newFromClass( 'Hooks' )->handlers = [];
60
61 // Clear caches so that our new namespace appears
62 MWNamespace::getCanonicalNamespaces( true );
63 Language::factory( 'en' )->resetNamespaces();
64
65 SpecialPageFactory::resetList();
66 }
67
68 public function tearDown() {
69 parent::tearDown();
70
71 TestingAccessWrapper::newFromClass( 'Hooks' )->handlers = $this->originalHandlers;
72
73 SpecialPageFactory::resetList();
74 }
75
76 protected function searchProvision( array $results = null ) {
77 if ( $results === null ) {
78 $this->setMwGlobals( 'wgHooks', [] );
79 } else {
80 $this->setMwGlobals( 'wgHooks', [
81 'PrefixSearchBackend' => [
82 function ( $namespaces, $search, $limit, &$srchres ) use ( $results ) {
83 $srchres = $results;
84 return false;
85 }
86 ],
87 ] );
88 }
89 }
90
91 public static function provideSearch() {
92 return [
93 [ [
94 'Empty string',
95 'query' => '',
96 'results' => [],
97 ] ],
98 [ [
99 'Main namespace with title prefix',
100 'query' => 'Ex',
101 'results' => [
102 'Example',
103 'Example/Baz',
104 'Example Bar',
105 ],
106 // Third result when testing offset
107 'offsetresult' => [
108 'Example Foo',
109 ],
110 ] ],
111 [ [
112 'Talk namespace prefix',
113 'query' => 'Talk:',
114 'results' => [
115 'Talk:Example',
116 'Talk:Sandbox',
117 ],
118 ] ],
119 [ [
120 'User namespace prefix',
121 'query' => 'User:',
122 'results' => [
123 'User:Example',
124 ],
125 ] ],
126 [ [
127 'Special namespace prefix',
128 'query' => 'Special:',
129 'results' => [
130 'Special:ActiveUsers',
131 'Special:AllMessages',
132 'Special:AllMyUploads',
133 ],
134 // Third result when testing offset
135 'offsetresult' => [
136 'Special:AllPages',
137 ],
138 ] ],
139 [ [
140 'Special namespace with prefix',
141 'query' => 'Special:Un',
142 'results' => [
143 'Special:Unblock',
144 'Special:UncategorizedCategories',
145 'Special:UncategorizedFiles',
146 ],
147 // Third result when testing offset
148 'offsetresult' => [
149 'Special:UncategorizedPages',
150 ],
151 ] ],
152 [ [
153 'Special page name',
154 'query' => 'Special:EditWatchlist',
155 'results' => [
156 'Special:EditWatchlist',
157 ],
158 ] ],
159 [ [
160 'Special page subpages',
161 'query' => 'Special:EditWatchlist/',
162 'results' => [
163 'Special:EditWatchlist/clear',
164 'Special:EditWatchlist/raw',
165 ],
166 ] ],
167 [ [
168 'Special page subpages with prefix',
169 'query' => 'Special:EditWatchlist/cl',
170 'results' => [
171 'Special:EditWatchlist/clear',
172 ],
173 ] ],
174 [ [
175 'Namespace with case sensitive first letter',
176 'query' => 'NonCap:upper',
177 'results' => []
178 ] ],
179 [ [
180 'Multinamespace search',
181 'query' => 'B',
182 'results' => [
183 'Bar',
184 'NonCap:Bar',
185 ],
186 'namespaces' => [ NS_MAIN, self::NS_NONCAP ],
187 ] ],
188 [ [
189 'Multinamespace search with lowercase first letter',
190 'query' => 'sand',
191 'results' => [
192 'Sandbox',
193 'NonCap:sandbox',
194 ],
195 'namespaces' => [ NS_MAIN, self::NS_NONCAP ],
196 ] ],
197 ];
198 }
199
200 /**
201 * @dataProvider provideSearch
202 * @covers PrefixSearch::search
203 * @covers PrefixSearch::searchBackend
204 */
205 public function testSearch( array $case ) {
206 $this->searchProvision( null );
207
208 $namespaces = isset( $case['namespaces'] ) ? $case['namespaces'] : [];
209
210 $searcher = new StringPrefixSearch;
211 $results = $searcher->search( $case['query'], 3, $namespaces );
212 $this->assertEquals(
213 $case['results'],
214 $results,
215 $case[0]
216 );
217 }
218
219 /**
220 * @dataProvider provideSearch
221 * @covers PrefixSearch::search
222 * @covers PrefixSearch::searchBackend
223 */
224 public function testSearchWithOffset( array $case ) {
225 $this->searchProvision( null );
226
227 $namespaces = isset( $case['namespaces'] ) ? $case['namespaces'] : [];
228
229 $searcher = new StringPrefixSearch;
230 $results = $searcher->search( $case['query'], 3, $namespaces, 1 );
231
232 // We don't expect the first result when offsetting
233 array_shift( $case['results'] );
234 // And sometimes we expect a different last result
235 $expected = isset( $case['offsetresult'] ) ?
236 array_merge( $case['results'], $case['offsetresult'] ) :
237 $case['results'];
238
239 $this->assertEquals(
240 $expected,
241 $results,
242 $case[0]
243 );
244 }
245
246 public static function provideSearchBackend() {
247 return [
248 [ [
249 'Simple case',
250 'provision' => [
251 'Bar',
252 'Barcelona',
253 'Barbara',
254 ],
255 'query' => 'Bar',
256 'results' => [
257 'Bar',
258 'Barcelona',
259 'Barbara',
260 ],
261 ] ],
262 [ [
263 'Exact match not on top (bug 70958)',
264 'provision' => [
265 'Barcelona',
266 'Bar',
267 'Barbara',
268 ],
269 'query' => 'Bar',
270 'results' => [
271 'Bar',
272 'Barcelona',
273 'Barbara',
274 ],
275 ] ],
276 [ [
277 'Exact match missing (bug 70958)',
278 'provision' => [
279 'Barcelona',
280 'Barbara',
281 'Bart',
282 ],
283 'query' => 'Bar',
284 'results' => [
285 'Bar',
286 'Barcelona',
287 'Barbara',
288 ],
289 ] ],
290 [ [
291 'Exact match missing and not existing',
292 'provision' => [
293 'Exile',
294 'Exist',
295 'External',
296 ],
297 'query' => 'Ex',
298 'results' => [
299 'Exile',
300 'Exist',
301 'External',
302 ],
303 ] ],
304 [ [
305 "Exact match shouldn't override already found match if " .
306 "exact is redirect and found isn't",
307 'provision' => [
308 // Target of the exact match is low in the list
309 'Redirect Test Worse Result',
310 'Redirect Test',
311 ],
312 'query' => 'redirect test',
313 'results' => [
314 // Redirect target is pulled up and exact match isn't added
315 'Redirect Test',
316 'Redirect Test Worse Result',
317 ],
318 ] ],
319 [ [
320 "Exact match shouldn't override already found match if " .
321 "both exact match and found match are redirect",
322 'provision' => [
323 // Another redirect to the same target as the exact match
324 // is low in the list
325 'Redirect Test2 Worse Result',
326 'Redirect test2',
327 ],
328 'query' => 'redirect TEST2',
329 'results' => [
330 // Found redirect is pulled to the top and exact match isn't
331 // added
332 'Redirect test2',
333 'Redirect Test2 Worse Result',
334 ],
335 ] ],
336 [ [
337 "Exact match should override any already found matches that " .
338 "are redirects to it",
339 'provision' => [
340 // Another redirect to the same target as the exact match
341 // is low in the list
342 'Redirect Test Worse Result',
343 'Redirect test',
344 ],
345 'query' => 'Redirect Test',
346 'results' => [
347 // Found redirect is pulled to the top and exact match isn't
348 // added
349 'Redirect Test',
350 'Redirect Test Worse Result',
351 ],
352 ] ],
353 ];
354 }
355
356 /**
357 * @dataProvider provideSearchBackend
358 * @covers PrefixSearch::searchBackend
359 */
360 public function testSearchBackend( array $case ) {
361 $this->searchProvision( $case['provision'] );
362 $searcher = new StringPrefixSearch;
363 $results = $searcher->search( $case['query'], 3 );
364 $this->assertEquals(
365 $case['results'],
366 $results,
367 $case[0]
368 );
369 }
370 }