Merge "Selenium: replace UserLoginPage with BlankPage where possible"
[lhc/web/wiklou.git] / tests / phpunit / includes / specialpage / ChangesListSpecialPageTest.php
1 <?php
2
3 use Wikimedia\TestingAccessWrapper;
4
5 /**
6 * Test class for ChangesListSpecialPage class
7 *
8 * Copyright © 2011-, Antoine Musso, Stephane Bisson, Matthew Flaschen
9 *
10 * @author Antoine Musso
11 * @author Stephane Bisson
12 * @author Matthew Flaschen
13 * @group Database
14 *
15 * @covers ChangesListSpecialPage
16 */
17 class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase {
18 protected function getPage() {
19 $mock = $this->getMockBuilder( ChangesListSpecialPage::class )
20 ->setConstructorArgs(
21 [
22 'ChangesListSpecialPage',
23 ''
24 ]
25 )
26 ->setMethods( [ 'getPageTitle' ] )
27 ->getMockForAbstractClass();
28
29 $mock->method( 'getPageTitle' )->willReturn(
30 Title::makeTitle( NS_SPECIAL, 'ChangesListSpecialPage' )
31 );
32
33 $mock = TestingAccessWrapper::newFromObject(
34 $mock
35 );
36
37 return $mock;
38 }
39
40 private function buildQuery(
41 $requestOptions = null,
42 $user = null
43 ) {
44 $context = new RequestContext;
45 $context->setRequest( new FauxRequest( $requestOptions ) );
46 if ( $user ) {
47 $context->setUser( $user );
48 }
49
50 $this->changesListSpecialPage->setContext( $context );
51 $this->changesListSpecialPage->filterGroups = [];
52 $formOptions = $this->changesListSpecialPage->setup( null );
53
54 #  Filter out rc_timestamp conditions which depends on the test runtime
55 # This condition is not needed as of march 2, 2011 -- hashar
56 # @todo FIXME: Find a way to generate the correct rc_timestamp
57
58 $tables = [];
59 $fields = [];
60 $queryConditions = [];
61 $query_options = [];
62 $join_conds = [];
63
64 call_user_func_array(
65 [ $this->changesListSpecialPage, 'buildQuery' ],
66 [
67 &$tables,
68 &$fields,
69 &$queryConditions,
70 &$query_options,
71 &$join_conds,
72 $formOptions
73 ]
74 );
75
76 $queryConditions = array_filter(
77 $queryConditions,
78 'ChangesListSpecialPageTest::filterOutRcTimestampCondition'
79 );
80
81 return $queryConditions;
82 }
83
84 /** helper to test SpecialRecentchanges::buildQuery() */
85 private function assertConditions(
86 $expected,
87 $requestOptions = null,
88 $message = '',
89 $user = null
90 ) {
91 $queryConditions = $this->buildQuery( $requestOptions, $user );
92
93 $this->assertEquals(
94 self::normalizeCondition( $expected ),
95 self::normalizeCondition( $queryConditions ),
96 $message
97 );
98 }
99
100 private static function normalizeCondition( $conds ) {
101 $dbr = wfGetDB( DB_REPLICA );
102 $normalized = array_map(
103 function ( $k, $v ) use ( $dbr ) {
104 if ( is_array( $v ) ) {
105 sort( $v );
106 }
107 // (Ab)use makeList() to format only this entry
108 return $dbr->makeList( [ $k => $v ], Database::LIST_AND );
109 },
110 array_keys( $conds ),
111 $conds
112 );
113 sort( $normalized );
114 return $normalized;
115 }
116
117 /** return false if condition begins with 'rc_timestamp ' */
118 private static function filterOutRcTimestampCondition( $var ) {
119 return ( is_array( $var ) || strpos( $var, 'rc_timestamp ' ) === false );
120 }
121
122 public function testRcNsFilter() {
123 $this->assertConditions(
124 [ # expected
125 "rc_namespace = '0'",
126 ],
127 [
128 'namespace' => NS_MAIN,
129 ],
130 "rc conditions with one namespace"
131 );
132 }
133
134 public function testRcNsFilterInversion() {
135 $this->assertConditions(
136 [ # expected
137 "rc_namespace != '0'",
138 ],
139 [
140 'namespace' => NS_MAIN,
141 'invert' => 1,
142 ],
143 "rc conditions with namespace inverted"
144 );
145 }
146
147 public function testRcNsFilterMultiple() {
148 $this->assertConditions(
149 [ # expected
150 "rc_namespace IN ('1','2','3')",
151 ],
152 [
153 'namespace' => '1;2;3',
154 ],
155 "rc conditions with multiple namespaces"
156 );
157 }
158
159 public function testRcNsFilterMultipleAssociated() {
160 $this->assertConditions(
161 [ # expected
162 "rc_namespace IN ('0','1','4','5','6','7')",
163 ],
164 [
165 'namespace' => '1;4;7',
166 'associated' => 1,
167 ],
168 "rc conditions with multiple namespaces and associated"
169 );
170 }
171
172 public function testRcNsFilterMultipleAssociatedInvert() {
173 $this->assertConditions(
174 [ # expected
175 "rc_namespace NOT IN ('2','3','8','9')",
176 ],
177 [
178 'namespace' => '2;3;9',
179 'associated' => 1,
180 'invert' => 1
181 ],
182 "rc conditions with multiple namespaces, associated and inverted"
183 );
184 }
185
186 public function testRcNsFilterMultipleInvert() {
187 $this->assertConditions(
188 [ # expected
189 "rc_namespace NOT IN ('1','2','3')",
190 ],
191 [
192 'namespace' => '1;2;3',
193 'invert' => 1,
194 ],
195 "rc conditions with multiple namespaces inverted"
196 );
197 }
198
199 public function testRcHidemyselfFilter() {
200 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
201 $this->overrideMwServices();
202
203 $user = $this->getTestUser()->getUser();
204 $user->getActorId( wfGetDB( DB_MASTER ) );
205 $this->assertConditions(
206 [ # expected
207 "NOT((rc_actor = '{$user->getActorId()}'))",
208 ],
209 [
210 'hidemyself' => 1,
211 ],
212 "rc conditions: hidemyself=1 (logged in)",
213 $user
214 );
215
216 $user = User::newFromName( '10.11.12.13', false );
217 $id = $user->getActorId( wfGetDB( DB_MASTER ) );
218 $this->assertConditions(
219 [ # expected
220 "NOT((rc_actor = '{$user->getActorId()}'))",
221 ],
222 [
223 'hidemyself' => 1,
224 ],
225 "rc conditions: hidemyself=1 (anon)",
226 $user
227 );
228 }
229
230 public function testRcHidemyselfFilter_old() {
231 $this->setMwGlobals(
232 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
233 );
234 $this->overrideMwServices();
235
236 $user = $this->getTestUser()->getUser();
237 $user->getActorId( wfGetDB( DB_MASTER ) );
238 $this->assertConditions(
239 [ # expected
240 "NOT((rc_user = '{$user->getId()}'))",
241 ],
242 [
243 'hidemyself' => 1,
244 ],
245 "rc conditions: hidemyself=1 (logged in)",
246 $user
247 );
248
249 $user = User::newFromName( '10.11.12.13', false );
250 $id = $user->getActorId( wfGetDB( DB_MASTER ) );
251 $this->assertConditions(
252 [ # expected
253 "NOT((rc_user_text = '10.11.12.13'))",
254 ],
255 [
256 'hidemyself' => 1,
257 ],
258 "rc conditions: hidemyself=1 (anon)",
259 $user
260 );
261 }
262
263 public function testRcHidebyothersFilter() {
264 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
265 $this->overrideMwServices();
266
267 $user = $this->getTestUser()->getUser();
268 $user->getActorId( wfGetDB( DB_MASTER ) );
269 $this->assertConditions(
270 [ # expected
271 "(rc_actor = '{$user->getActorId()}')",
272 ],
273 [
274 'hidebyothers' => 1,
275 ],
276 "rc conditions: hidebyothers=1 (logged in)",
277 $user
278 );
279
280 $user = User::newFromName( '10.11.12.13', false );
281 $id = $user->getActorId( wfGetDB( DB_MASTER ) );
282 $this->assertConditions(
283 [ # expected
284 "(rc_actor = '{$user->getActorId()}')",
285 ],
286 [
287 'hidebyothers' => 1,
288 ],
289 "rc conditions: hidebyothers=1 (anon)",
290 $user
291 );
292 }
293
294 public function testRcHidebyothersFilter_old() {
295 $this->setMwGlobals(
296 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
297 );
298 $this->overrideMwServices();
299
300 $user = $this->getTestUser()->getUser();
301 $user->getActorId( wfGetDB( DB_MASTER ) );
302 $this->assertConditions(
303 [ # expected
304 "(rc_user_text = '{$user->getName()}')",
305 ],
306 [
307 'hidebyothers' => 1,
308 ],
309 "rc conditions: hidebyothers=1 (logged in)",
310 $user
311 );
312
313 $user = User::newFromName( '10.11.12.13', false );
314 $id = $user->getActorId( wfGetDB( DB_MASTER ) );
315 $this->assertConditions(
316 [ # expected
317 "(rc_user_text = '10.11.12.13')",
318 ],
319 [
320 'hidebyothers' => 1,
321 ],
322 "rc conditions: hidebyothers=1 (anon)",
323 $user
324 );
325 }
326
327 public function testRcHidepageedits() {
328 $this->assertConditions(
329 [ # expected
330 "rc_type != '0'",
331 ],
332 [
333 'hidepageedits' => 1,
334 ],
335 "rc conditions: hidepageedits=1"
336 );
337 }
338
339 public function testRcHidenewpages() {
340 $this->assertConditions(
341 [ # expected
342 "rc_type != '1'",
343 ],
344 [
345 'hidenewpages' => 1,
346 ],
347 "rc conditions: hidenewpages=1"
348 );
349 }
350
351 public function testRcHidelog() {
352 $this->assertConditions(
353 [ # expected
354 "rc_type != '3'",
355 ],
356 [
357 'hidelog' => 1,
358 ],
359 "rc conditions: hidelog=1"
360 );
361 }
362
363 public function testRcHidehumans() {
364 $this->assertConditions(
365 [ # expected
366 'rc_bot' => 1,
367 ],
368 [
369 'hidebots' => 0,
370 'hidehumans' => 1,
371 ],
372 "rc conditions: hidebots=0 hidehumans=1"
373 );
374 }
375
376 public function testRcHidepatrolledDisabledFilter() {
377 $this->setMwGlobals( 'wgUseRCPatrol', false );
378 $user = $this->getTestUser()->getUser();
379 $this->assertConditions(
380 [ # expected
381 ],
382 [
383 'hidepatrolled' => 1,
384 ],
385 "rc conditions: hidepatrolled=1 (user not allowed)",
386 $user
387 );
388 }
389
390 public function testRcHideunpatrolledDisabledFilter() {
391 $this->setMwGlobals( 'wgUseRCPatrol', false );
392 $user = $this->getTestUser()->getUser();
393 $this->assertConditions(
394 [ # expected
395 ],
396 [
397 'hideunpatrolled' => 1,
398 ],
399 "rc conditions: hideunpatrolled=1 (user not allowed)",
400 $user
401 );
402 }
403
404 public function testRcHidepatrolledFilter() {
405 $user = $this->getTestSysop()->getUser();
406 $this->assertConditions(
407 [ # expected
408 'rc_patrolled' => 0,
409 ],
410 [
411 'hidepatrolled' => 1,
412 ],
413 "rc conditions: hidepatrolled=1",
414 $user
415 );
416 }
417
418 public function testRcHideunpatrolledFilter() {
419 $user = $this->getTestSysop()->getUser();
420 $this->assertConditions(
421 [ # expected
422 'rc_patrolled' => [ 1, 2 ],
423 ],
424 [
425 'hideunpatrolled' => 1,
426 ],
427 "rc conditions: hideunpatrolled=1",
428 $user
429 );
430 }
431
432 public function testRcReviewStatusFilter() {
433 $user = $this->getTestSysop()->getUser();
434 $this->assertConditions(
435 [ #expected
436 'rc_patrolled' => 1,
437 ],
438 [
439 'reviewStatus' => 'manual'
440 ],
441 "rc conditions: reviewStatus=manual",
442 $user
443 );
444 $this->assertConditions(
445 [ #expected
446 'rc_patrolled' => [ 0, 2 ],
447 ],
448 [
449 'reviewStatus' => 'unpatrolled;auto'
450 ],
451 "rc conditions: reviewStatus=unpatrolled;auto",
452 $user
453 );
454 }
455
456 public function testRcHideminorFilter() {
457 $this->assertConditions(
458 [ # expected
459 "rc_minor = 0",
460 ],
461 [
462 'hideminor' => 1,
463 ],
464 "rc conditions: hideminor=1"
465 );
466 }
467
468 public function testRcHidemajorFilter() {
469 $this->assertConditions(
470 [ # expected
471 "rc_minor = 1",
472 ],
473 [
474 'hidemajor' => 1,
475 ],
476 "rc conditions: hidemajor=1"
477 );
478 }
479
480 public function testHideCategorization() {
481 $this->assertConditions(
482 [
483 # expected
484 "rc_type != '6'"
485 ],
486 [
487 'hidecategorization' => 1
488 ],
489 "rc conditions: hidecategorization=1"
490 );
491 }
492
493 public function testFilterUserExpLevelAll() {
494 $this->assertConditions(
495 [
496 # expected
497 ],
498 [
499 'userExpLevel' => 'registered;unregistered;newcomer;learner;experienced',
500 ],
501 "rc conditions: userExpLevel=registered;unregistered;newcomer;learner;experienced"
502 );
503 }
504
505 public function testFilterUserExpLevelRegisteredUnregistered() {
506 $this->assertConditions(
507 [
508 # expected
509 ],
510 [
511 'userExpLevel' => 'registered;unregistered',
512 ],
513 "rc conditions: userExpLevel=registered;unregistered"
514 );
515 }
516
517 public function testFilterUserExpLevelRegisteredUnregisteredLearner() {
518 $this->assertConditions(
519 [
520 # expected
521 ],
522 [
523 'userExpLevel' => 'registered;unregistered;learner',
524 ],
525 "rc conditions: userExpLevel=registered;unregistered;learner"
526 );
527 }
528
529 public function testFilterUserExpLevelAllExperienceLevels() {
530 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
531 $this->overrideMwServices();
532
533 $this->assertConditions(
534 [
535 # expected
536 'actor_rc_user.actor_user IS NOT NULL',
537 ],
538 [
539 'userExpLevel' => 'newcomer;learner;experienced',
540 ],
541 "rc conditions: userExpLevel=newcomer;learner;experienced"
542 );
543 }
544
545 public function testFilterUserExpLevelAllExperienceLevels_old() {
546 $this->setMwGlobals(
547 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
548 );
549 $this->overrideMwServices();
550
551 $this->assertConditions(
552 [
553 # expected
554 'rc_user != 0',
555 ],
556 [
557 'userExpLevel' => 'newcomer;learner;experienced',
558 ],
559 "rc conditions: userExpLevel=newcomer;learner;experienced"
560 );
561 }
562
563 public function testFilterUserExpLevelRegistrered() {
564 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
565 $this->overrideMwServices();
566
567 $this->assertConditions(
568 [
569 # expected
570 'actor_rc_user.actor_user IS NOT NULL',
571 ],
572 [
573 'userExpLevel' => 'registered',
574 ],
575 "rc conditions: userExpLevel=registered"
576 );
577 }
578
579 public function testFilterUserExpLevelRegistrered_old() {
580 $this->setMwGlobals(
581 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
582 );
583 $this->overrideMwServices();
584
585 $this->assertConditions(
586 [
587 # expected
588 'rc_user != 0',
589 ],
590 [
591 'userExpLevel' => 'registered',
592 ],
593 "rc conditions: userExpLevel=registered"
594 );
595 }
596
597 public function testFilterUserExpLevelUnregistrered() {
598 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
599 $this->overrideMwServices();
600
601 $this->assertConditions(
602 [
603 # expected
604 'actor_rc_user.actor_user IS NULL',
605 ],
606 [
607 'userExpLevel' => 'unregistered',
608 ],
609 "rc conditions: userExpLevel=unregistered"
610 );
611 }
612
613 public function testFilterUserExpLevelUnregistrered_old() {
614 $this->setMwGlobals(
615 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
616 );
617 $this->overrideMwServices();
618
619 $this->assertConditions(
620 [
621 # expected
622 'rc_user = 0',
623 ],
624 [
625 'userExpLevel' => 'unregistered',
626 ],
627 "rc conditions: userExpLevel=unregistered"
628 );
629 }
630
631 public function testFilterUserExpLevelRegistreredOrLearner() {
632 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
633 $this->overrideMwServices();
634
635 $this->assertConditions(
636 [
637 # expected
638 'actor_rc_user.actor_user IS NOT NULL',
639 ],
640 [
641 'userExpLevel' => 'registered;learner',
642 ],
643 "rc conditions: userExpLevel=registered;learner"
644 );
645 }
646
647 public function testFilterUserExpLevelRegistreredOrLearner_old() {
648 $this->setMwGlobals(
649 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
650 );
651 $this->overrideMwServices();
652
653 $this->assertConditions(
654 [
655 # expected
656 'rc_user != 0',
657 ],
658 [
659 'userExpLevel' => 'registered;learner',
660 ],
661 "rc conditions: userExpLevel=registered;learner"
662 );
663 }
664
665 public function testFilterUserExpLevelUnregistreredOrExperienced() {
666 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
667 $this->overrideMwServices();
668
669 $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
670
671 $this->assertRegExp(
672 '/\(actor_rc_user\.actor_user IS NULL\) OR '
673 . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
674 reset( $conds ),
675 "rc conditions: userExpLevel=unregistered;experienced"
676 );
677 }
678
679 public function testFilterUserExpLevelUnregistreredOrExperienced_old() {
680 $this->setMwGlobals(
681 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
682 );
683 $this->overrideMwServices();
684
685 $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
686
687 $this->assertRegExp(
688 '/\(rc_user = 0\) OR '
689 . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
690 reset( $conds ),
691 "rc conditions: userExpLevel=unregistered;experienced"
692 );
693 }
694
695 public function testFilterUserExpLevel() {
696 $now = time();
697 $this->setMwGlobals( [
698 'wgLearnerEdits' => 10,
699 'wgLearnerMemberSince' => 4,
700 'wgExperiencedUserEdits' => 500,
701 'wgExperiencedUserMemberSince' => 30,
702 ] );
703
704 $this->createUsers( [
705 'Newcomer1' => [ 'edits' => 2, 'days' => 2 ],
706 'Newcomer2' => [ 'edits' => 12, 'days' => 3 ],
707 'Newcomer3' => [ 'edits' => 8, 'days' => 5 ],
708 'Learner1' => [ 'edits' => 15, 'days' => 10 ],
709 'Learner2' => [ 'edits' => 450, 'days' => 20 ],
710 'Learner3' => [ 'edits' => 460, 'days' => 33 ],
711 'Learner4' => [ 'edits' => 525, 'days' => 28 ],
712 'Experienced1' => [ 'edits' => 538, 'days' => 33 ],
713 ], $now );
714
715 // newcomers only
716 $this->assertArrayEquals(
717 [ 'Newcomer1', 'Newcomer2', 'Newcomer3' ],
718 $this->fetchUsers( [ 'newcomer' ], $now )
719 );
720
721 // newcomers and learner
722 $this->assertArrayEquals(
723 [
724 'Newcomer1', 'Newcomer2', 'Newcomer3',
725 'Learner1', 'Learner2', 'Learner3', 'Learner4',
726 ],
727 $this->fetchUsers( [ 'newcomer', 'learner' ], $now )
728 );
729
730 // newcomers and more learner
731 $this->assertArrayEquals(
732 [
733 'Newcomer1', 'Newcomer2', 'Newcomer3',
734 'Experienced1',
735 ],
736 $this->fetchUsers( [ 'newcomer', 'experienced' ], $now )
737 );
738
739 // learner only
740 $this->assertArrayEquals(
741 [ 'Learner1', 'Learner2', 'Learner3', 'Learner4' ],
742 $this->fetchUsers( [ 'learner' ], $now )
743 );
744
745 // more experienced only
746 $this->assertArrayEquals(
747 [ 'Experienced1' ],
748 $this->fetchUsers( [ 'experienced' ], $now )
749 );
750
751 // learner and more experienced
752 $this->assertArrayEquals(
753 [
754 'Learner1', 'Learner2', 'Learner3', 'Learner4',
755 'Experienced1',
756 ],
757 $this->fetchUsers( [ 'learner', 'experienced' ], $now )
758 );
759 }
760
761 private function createUsers( $specs, $now ) {
762 $dbw = wfGetDB( DB_MASTER );
763 foreach ( $specs as $name => $spec ) {
764 User::createNew(
765 $name,
766 [
767 'editcount' => $spec['edits'],
768 'registration' => $dbw->timestamp( $this->daysAgo( $spec['days'], $now ) ),
769 'email' => 'ut',
770 ]
771 );
772 }
773 }
774
775 private function fetchUsers( $filters, $now ) {
776 $tables = [];
777 $conds = [];
778 $fields = [];
779 $query_options = [];
780 $join_conds = [];
781
782 sort( $filters );
783
784 call_user_func_array(
785 [ $this->changesListSpecialPage, 'filterOnUserExperienceLevel' ],
786 [
787 get_class( $this->changesListSpecialPage ),
788 $this->changesListSpecialPage->getContext(),
789 $this->changesListSpecialPage->getDB(),
790 &$tables,
791 &$fields,
792 &$conds,
793 &$query_options,
794 &$join_conds,
795 $filters,
796 $now
797 ]
798 );
799
800 // @todo: This is not at all safe or sane. It just blindly assumes
801 // nothing in $conds depends on any other tables.
802 $result = wfGetDB( DB_MASTER )->select(
803 'user',
804 'user_name',
805 array_filter( $conds ) + [ 'user_email' => 'ut' ]
806 );
807
808 $usernames = [];
809 foreach ( $result as $row ) {
810 $usernames[] = $row->user_name;
811 }
812
813 return $usernames;
814 }
815
816 private function daysAgo( $days, $now ) {
817 $secondsPerDay = 86400;
818 return $now - $days * $secondsPerDay;
819 }
820
821 public function testGetStructuredFilterJsData() {
822 $this->changesListSpecialPage->filterGroups = [];
823
824 $definition = [
825 [
826 'name' => 'gub-group',
827 'title' => 'gub-group-title',
828 'class' => ChangesListBooleanFilterGroup::class,
829 'filters' => [
830 [
831 'name' => 'hidefoo',
832 'label' => 'foo-label',
833 'description' => 'foo-description',
834 'default' => true,
835 'showHide' => 'showhidefoo',
836 'priority' => 2,
837 ],
838 [
839 'name' => 'hidebar',
840 'label' => 'bar-label',
841 'description' => 'bar-description',
842 'default' => false,
843 'priority' => 4,
844 ]
845 ],
846 ],
847
848 [
849 'name' => 'des-group',
850 'title' => 'des-group-title',
851 'class' => ChangesListStringOptionsFilterGroup::class,
852 'isFullCoverage' => true,
853 'filters' => [
854 [
855 'name' => 'grault',
856 'label' => 'grault-label',
857 'description' => 'grault-description',
858 ],
859 [
860 'name' => 'garply',
861 'label' => 'garply-label',
862 'description' => 'garply-description',
863 ],
864 ],
865 'queryCallable' => function () {
866 },
867 'default' => ChangesListStringOptionsFilterGroup::NONE,
868 ],
869
870 [
871 'name' => 'unstructured',
872 'class' => ChangesListBooleanFilterGroup::class,
873 'filters' => [
874 [
875 'name' => 'hidethud',
876 'showHide' => 'showhidethud',
877 'default' => true,
878 ],
879
880 [
881 'name' => 'hidemos',
882 'showHide' => 'showhidemos',
883 'default' => false,
884 ],
885 ],
886 ],
887
888 ];
889
890 $this->changesListSpecialPage->registerFiltersFromDefinitions( $definition );
891
892 $this->assertArrayEquals(
893 [
894 // Filters that only display in the unstructured UI are
895 // are not included, and neither are groups that would
896 // be empty due to the above.
897 'groups' => [
898 [
899 'name' => 'gub-group',
900 'title' => 'gub-group-title',
901 'type' => ChangesListBooleanFilterGroup::TYPE,
902 'priority' => -1,
903 'filters' => [
904 [
905 'name' => 'hidebar',
906 'label' => 'bar-label',
907 'description' => 'bar-description',
908 'default' => false,
909 'priority' => 4,
910 'cssClass' => null,
911 'conflicts' => [],
912 'subset' => [],
913 'defaultHighlightColor' => null
914 ],
915 [
916 'name' => 'hidefoo',
917 'label' => 'foo-label',
918 'description' => 'foo-description',
919 'default' => true,
920 'priority' => 2,
921 'cssClass' => null,
922 'conflicts' => [],
923 'subset' => [],
924 'defaultHighlightColor' => null
925 ],
926 ],
927 'fullCoverage' => true,
928 'conflicts' => [],
929 ],
930
931 [
932 'name' => 'des-group',
933 'title' => 'des-group-title',
934 'type' => ChangesListStringOptionsFilterGroup::TYPE,
935 'priority' => -2,
936 'fullCoverage' => true,
937 'filters' => [
938 [
939 'name' => 'grault',
940 'label' => 'grault-label',
941 'description' => 'grault-description',
942 'cssClass' => null,
943 'priority' => -2,
944 'conflicts' => [],
945 'subset' => [],
946 'defaultHighlightColor' => null
947 ],
948 [
949 'name' => 'garply',
950 'label' => 'garply-label',
951 'description' => 'garply-description',
952 'cssClass' => null,
953 'priority' => -3,
954 'conflicts' => [],
955 'subset' => [],
956 'defaultHighlightColor' => null
957 ],
958 ],
959 'conflicts' => [],
960 'separator' => ';',
961 'default' => ChangesListStringOptionsFilterGroup::NONE,
962 ],
963 ],
964 'messageKeys' => [
965 'gub-group-title',
966 'bar-label',
967 'bar-description',
968 'foo-label',
969 'foo-description',
970 'des-group-title',
971 'grault-label',
972 'grault-description',
973 'garply-label',
974 'garply-description',
975 ],
976 ],
977 $this->changesListSpecialPage->getStructuredFilterJsData(),
978 /** ordered= */ false,
979 /** named= */ true
980 );
981 }
982
983 public function provideParseParameters() {
984 return [
985 [ 'hidebots', [ 'hidebots' => true ] ],
986
987 [ 'bots', [ 'hidebots' => false ] ],
988
989 [ 'hideminor', [ 'hideminor' => true ] ],
990
991 [ 'minor', [ 'hideminor' => false ] ],
992
993 [ 'hidemajor', [ 'hidemajor' => true ] ],
994
995 [ 'hideliu', [ 'hideliu' => true ] ],
996
997 [ 'hidepatrolled', [ 'hidepatrolled' => true ] ],
998
999 [ 'hideunpatrolled', [ 'hideunpatrolled' => true ] ],
1000
1001 [ 'hideanons', [ 'hideanons' => true ] ],
1002
1003 [ 'hidemyself', [ 'hidemyself' => true ] ],
1004
1005 [ 'hidebyothers', [ 'hidebyothers' => true ] ],
1006
1007 [ 'hidehumans', [ 'hidehumans' => true ] ],
1008
1009 [ 'hidepageedits', [ 'hidepageedits' => true ] ],
1010
1011 [ 'pagedits', [ 'hidepageedits' => false ] ],
1012
1013 [ 'hidenewpages', [ 'hidenewpages' => true ] ],
1014
1015 [ 'hidecategorization', [ 'hidecategorization' => true ] ],
1016
1017 [ 'hidelog', [ 'hidelog' => true ] ],
1018
1019 [
1020 'userExpLevel=learner;experienced',
1021 [
1022 'userExpLevel' => 'learner;experienced'
1023 ],
1024 ],
1025
1026 // A few random combos
1027 [
1028 'bots,hideliu,hidemyself',
1029 [
1030 'hidebots' => false,
1031 'hideliu' => true,
1032 'hidemyself' => true,
1033 ],
1034 ],
1035
1036 [
1037 'minor,hideanons,categorization',
1038 [
1039 'hideminor' => false,
1040 'hideanons' => true,
1041 'hidecategorization' => false,
1042 ]
1043 ],
1044
1045 [
1046 'hidehumans,bots,hidecategorization',
1047 [
1048 'hidehumans' => true,
1049 'hidebots' => false,
1050 'hidecategorization' => true,
1051 ],
1052 ],
1053
1054 [
1055 'hidemyself,userExpLevel=newcomer;learner,hideminor',
1056 [
1057 'hidemyself' => true,
1058 'hideminor' => true,
1059 'userExpLevel' => 'newcomer;learner',
1060 ],
1061 ],
1062 ];
1063 }
1064
1065 public function provideGetFilterConflicts() {
1066 return [
1067 [
1068 "parameters" => [],
1069 "expectedConflicts" => false,
1070 ],
1071 [
1072 "parameters" => [
1073 "hideliu" => true,
1074 "userExpLevel" => "newcomer",
1075 ],
1076 "expectedConflicts" => false,
1077 ],
1078 [
1079 "parameters" => [
1080 "hideanons" => true,
1081 "userExpLevel" => "learner",
1082 ],
1083 "expectedConflicts" => false,
1084 ],
1085 [
1086 "parameters" => [
1087 "hidemajor" => true,
1088 "hidenewpages" => true,
1089 "hidepageedits" => true,
1090 "hidecategorization" => false,
1091 "hidelog" => true,
1092 "hideWikidata" => true,
1093 ],
1094 "expectedConflicts" => true,
1095 ],
1096 [
1097 "parameters" => [
1098 "hidemajor" => true,
1099 "hidenewpages" => false,
1100 "hidepageedits" => true,
1101 "hidecategorization" => false,
1102 "hidelog" => false,
1103 "hideWikidata" => true,
1104 ],
1105 "expectedConflicts" => true,
1106 ],
1107 [
1108 "parameters" => [
1109 "hidemajor" => true,
1110 "hidenewpages" => false,
1111 "hidepageedits" => false,
1112 "hidecategorization" => true,
1113 "hidelog" => true,
1114 "hideWikidata" => true,
1115 ],
1116 "expectedConflicts" => false,
1117 ],
1118 [
1119 "parameters" => [
1120 "hideminor" => true,
1121 "hidenewpages" => true,
1122 "hidepageedits" => true,
1123 "hidecategorization" => false,
1124 "hidelog" => true,
1125 "hideWikidata" => true,
1126 ],
1127 "expectedConflicts" => false,
1128 ],
1129 ];
1130 }
1131
1132 /**
1133 * @dataProvider provideGetFilterConflicts
1134 */
1135 public function testGetFilterConflicts( $parameters, $expectedConflicts ) {
1136 $context = new RequestContext;
1137 $context->setRequest( new FauxRequest( $parameters ) );
1138 $this->changesListSpecialPage->setContext( $context );
1139
1140 $this->assertEquals(
1141 $expectedConflicts,
1142 $this->changesListSpecialPage->areFiltersInConflict()
1143 );
1144 }
1145
1146 public function validateOptionsProvider() {
1147 return [
1148 [
1149 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 1 ],
1150 true,
1151 [ 'userExpLevel' => 'unregistered', 'hidebots' => 1, ],
1152 true,
1153 ],
1154 [
1155 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 0 ],
1156 true,
1157 [ 'hidebots' => 0, 'hidehumans' => 1 ],
1158 true,
1159 ],
1160 [
1161 [ 'hideanons' => 1 ],
1162 true,
1163 [ 'userExpLevel' => 'registered' ],
1164 true,
1165 ],
1166 [
1167 [ 'hideliu' => 1 ],
1168 true,
1169 [ 'userExpLevel' => 'unregistered' ],
1170 true,
1171 ],
1172 [
1173 [ 'hideanons' => 1, 'hidebots' => 1 ],
1174 true,
1175 [ 'userExpLevel' => 'registered', 'hidebots' => 1 ],
1176 true,
1177 ],
1178 [
1179 [ 'hideliu' => 1, 'hidebots' => 0 ],
1180 true,
1181 [ 'userExpLevel' => 'unregistered', 'hidebots' => 0 ],
1182 true,
1183 ],
1184 [
1185 [ 'hidemyself' => 1, 'hidebyothers' => 1 ],
1186 true,
1187 [],
1188 true,
1189 ],
1190 [
1191 [ 'hidebots' => 1, 'hidehumans' => 1 ],
1192 true,
1193 [],
1194 true,
1195 ],
1196 [
1197 [ 'hidepatrolled' => 1, 'hideunpatrolled' => 1 ],
1198 true,
1199 [],
1200 true,
1201 ],
1202 [
1203 [ 'hideminor' => 1, 'hidemajor' => 1 ],
1204 true,
1205 [],
1206 true,
1207 ],
1208 [
1209 // changeType
1210 [ 'hidepageedits' => 1, 'hidenewpages' => 1, 'hidecategorization' => 1, 'hidelog' => 1, ],
1211 true,
1212 [],
1213 true,
1214 ],
1215 ];
1216 }
1217 }