Merge "Improve rollback tests setup by extracting repeating logic to HistoryPage...
[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 public function testRcHidepatrolledFilter() {
404 $user = $this->getTestSysop()->getUser();
405 $this->assertConditions(
406 [ # expected
407 'rc_patrolled' => 0,
408 ],
409 [
410 'hidepatrolled' => 1,
411 ],
412 "rc conditions: hidepatrolled=1",
413 $user
414 );
415 }
416
417 public function testRcHideunpatrolledFilter() {
418 $user = $this->getTestSysop()->getUser();
419 $this->assertConditions(
420 [ # expected
421 'rc_patrolled' => [ 1, 2 ],
422 ],
423 [
424 'hideunpatrolled' => 1,
425 ],
426 "rc conditions: hideunpatrolled=1",
427 $user
428 );
429 }
430
431 public function testRcReviewStatusFilter() {
432 $user = $this->getTestSysop()->getUser();
433 $this->assertConditions(
434 [ #expected
435 'rc_patrolled' => 1,
436 ],
437 [
438 'reviewStatus' => 'manual'
439 ],
440 "rc conditions: reviewStatus=manual",
441 $user
442 );
443 $this->assertConditions(
444 [ #expected
445 'rc_patrolled' => [ 0, 2 ],
446 ],
447 [
448 'reviewStatus' => 'unpatrolled;auto'
449 ],
450 "rc conditions: reviewStatus=unpatrolled;auto",
451 $user
452 );
453 }
454
455 public function testRcHideminorFilter() {
456 $this->assertConditions(
457 [ # expected
458 "rc_minor = 0",
459 ],
460 [
461 'hideminor' => 1,
462 ],
463 "rc conditions: hideminor=1"
464 );
465 }
466
467 public function testRcHidemajorFilter() {
468 $this->assertConditions(
469 [ # expected
470 "rc_minor = 1",
471 ],
472 [
473 'hidemajor' => 1,
474 ],
475 "rc conditions: hidemajor=1"
476 );
477 }
478
479 public function testHideCategorization() {
480 $this->assertConditions(
481 [
482 # expected
483 "rc_type != '6'"
484 ],
485 [
486 'hidecategorization' => 1
487 ],
488 "rc conditions: hidecategorization=1"
489 );
490 }
491
492 public function testFilterUserExpLevelAll() {
493 $this->assertConditions(
494 [
495 # expected
496 ],
497 [
498 'userExpLevel' => 'registered;unregistered;newcomer;learner;experienced',
499 ],
500 "rc conditions: userExpLevel=registered;unregistered;newcomer;learner;experienced"
501 );
502 }
503
504 public function testFilterUserExpLevelRegisteredUnregistered() {
505 $this->assertConditions(
506 [
507 # expected
508 ],
509 [
510 'userExpLevel' => 'registered;unregistered',
511 ],
512 "rc conditions: userExpLevel=registered;unregistered"
513 );
514 }
515
516 public function testFilterUserExpLevelRegisteredUnregisteredLearner() {
517 $this->assertConditions(
518 [
519 # expected
520 ],
521 [
522 'userExpLevel' => 'registered;unregistered;learner',
523 ],
524 "rc conditions: userExpLevel=registered;unregistered;learner"
525 );
526 }
527
528 public function testFilterUserExpLevelAllExperienceLevels() {
529 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
530 $this->overrideMwServices();
531
532 $this->assertConditions(
533 [
534 # expected
535 'actor_rc_user.actor_user IS NOT NULL',
536 ],
537 [
538 'userExpLevel' => 'newcomer;learner;experienced',
539 ],
540 "rc conditions: userExpLevel=newcomer;learner;experienced"
541 );
542 }
543
544 public function testFilterUserExpLevelAllExperienceLevels_old() {
545 $this->setMwGlobals(
546 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
547 );
548 $this->overrideMwServices();
549
550 $this->assertConditions(
551 [
552 # expected
553 'rc_user != 0',
554 ],
555 [
556 'userExpLevel' => 'newcomer;learner;experienced',
557 ],
558 "rc conditions: userExpLevel=newcomer;learner;experienced"
559 );
560 }
561
562 public function testFilterUserExpLevelRegistrered() {
563 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
564 $this->overrideMwServices();
565
566 $this->assertConditions(
567 [
568 # expected
569 'actor_rc_user.actor_user IS NOT NULL',
570 ],
571 [
572 'userExpLevel' => 'registered',
573 ],
574 "rc conditions: userExpLevel=registered"
575 );
576 }
577
578 public function testFilterUserExpLevelRegistrered_old() {
579 $this->setMwGlobals(
580 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
581 );
582 $this->overrideMwServices();
583
584 $this->assertConditions(
585 [
586 # expected
587 'rc_user != 0',
588 ],
589 [
590 'userExpLevel' => 'registered',
591 ],
592 "rc conditions: userExpLevel=registered"
593 );
594 }
595
596 public function testFilterUserExpLevelUnregistrered() {
597 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
598 $this->overrideMwServices();
599
600 $this->assertConditions(
601 [
602 # expected
603 'actor_rc_user.actor_user IS NULL',
604 ],
605 [
606 'userExpLevel' => 'unregistered',
607 ],
608 "rc conditions: userExpLevel=unregistered"
609 );
610 }
611
612 public function testFilterUserExpLevelUnregistrered_old() {
613 $this->setMwGlobals(
614 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
615 );
616 $this->overrideMwServices();
617
618 $this->assertConditions(
619 [
620 # expected
621 'rc_user = 0',
622 ],
623 [
624 'userExpLevel' => 'unregistered',
625 ],
626 "rc conditions: userExpLevel=unregistered"
627 );
628 }
629
630 public function testFilterUserExpLevelRegistreredOrLearner() {
631 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
632 $this->overrideMwServices();
633
634 $this->assertConditions(
635 [
636 # expected
637 'actor_rc_user.actor_user IS NOT NULL',
638 ],
639 [
640 'userExpLevel' => 'registered;learner',
641 ],
642 "rc conditions: userExpLevel=registered;learner"
643 );
644 }
645
646 public function testFilterUserExpLevelRegistreredOrLearner_old() {
647 $this->setMwGlobals(
648 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
649 );
650 $this->overrideMwServices();
651
652 $this->assertConditions(
653 [
654 # expected
655 'rc_user != 0',
656 ],
657 [
658 'userExpLevel' => 'registered;learner',
659 ],
660 "rc conditions: userExpLevel=registered;learner"
661 );
662 }
663
664 public function testFilterUserExpLevelUnregistreredOrExperienced() {
665 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
666 $this->overrideMwServices();
667
668 $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
669
670 $this->assertRegExp(
671 '/\(actor_rc_user\.actor_user IS NULL\) OR '
672 . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
673 reset( $conds ),
674 "rc conditions: userExpLevel=unregistered;experienced"
675 );
676 }
677
678 public function testFilterUserExpLevelUnregistreredOrExperienced_old() {
679 $this->setMwGlobals(
680 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
681 );
682 $this->overrideMwServices();
683
684 $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
685
686 $this->assertRegExp(
687 '/\(rc_user = 0\) OR '
688 . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
689 reset( $conds ),
690 "rc conditions: userExpLevel=unregistered;experienced"
691 );
692 }
693
694 public function testFilterUserExpLevel() {
695 $now = time();
696 $this->setMwGlobals( [
697 'wgLearnerEdits' => 10,
698 'wgLearnerMemberSince' => 4,
699 'wgExperiencedUserEdits' => 500,
700 'wgExperiencedUserMemberSince' => 30,
701 ] );
702
703 $this->createUsers( [
704 'Newcomer1' => [ 'edits' => 2, 'days' => 2 ],
705 'Newcomer2' => [ 'edits' => 12, 'days' => 3 ],
706 'Newcomer3' => [ 'edits' => 8, 'days' => 5 ],
707 'Learner1' => [ 'edits' => 15, 'days' => 10 ],
708 'Learner2' => [ 'edits' => 450, 'days' => 20 ],
709 'Learner3' => [ 'edits' => 460, 'days' => 33 ],
710 'Learner4' => [ 'edits' => 525, 'days' => 28 ],
711 'Experienced1' => [ 'edits' => 538, 'days' => 33 ],
712 ], $now );
713
714 // newcomers only
715 $this->assertArrayEquals(
716 [ 'Newcomer1', 'Newcomer2', 'Newcomer3' ],
717 $this->fetchUsers( [ 'newcomer' ], $now )
718 );
719
720 // newcomers and learner
721 $this->assertArrayEquals(
722 [
723 'Newcomer1', 'Newcomer2', 'Newcomer3',
724 'Learner1', 'Learner2', 'Learner3', 'Learner4',
725 ],
726 $this->fetchUsers( [ 'newcomer', 'learner' ], $now )
727 );
728
729 // newcomers and more learner
730 $this->assertArrayEquals(
731 [
732 'Newcomer1', 'Newcomer2', 'Newcomer3',
733 'Experienced1',
734 ],
735 $this->fetchUsers( [ 'newcomer', 'experienced' ], $now )
736 );
737
738 // learner only
739 $this->assertArrayEquals(
740 [ 'Learner1', 'Learner2', 'Learner3', 'Learner4' ],
741 $this->fetchUsers( [ 'learner' ], $now )
742 );
743
744 // more experienced only
745 $this->assertArrayEquals(
746 [ 'Experienced1' ],
747 $this->fetchUsers( [ 'experienced' ], $now )
748 );
749
750 // learner and more experienced
751 $this->assertArrayEquals(
752 [
753 'Learner1', 'Learner2', 'Learner3', 'Learner4',
754 'Experienced1',
755 ],
756 $this->fetchUsers( [ 'learner', 'experienced' ], $now )
757 );
758 }
759
760 private function createUsers( $specs, $now ) {
761 $dbw = wfGetDB( DB_MASTER );
762 foreach ( $specs as $name => $spec ) {
763 User::createNew(
764 $name,
765 [
766 'editcount' => $spec['edits'],
767 'registration' => $dbw->timestamp( $this->daysAgo( $spec['days'], $now ) ),
768 'email' => 'ut',
769 ]
770 );
771 }
772 }
773
774 private function fetchUsers( $filters, $now ) {
775 $tables = [];
776 $conds = [];
777 $fields = [];
778 $query_options = [];
779 $join_conds = [];
780
781 sort( $filters );
782
783 call_user_func_array(
784 [ $this->changesListSpecialPage, 'filterOnUserExperienceLevel' ],
785 [
786 get_class( $this->changesListSpecialPage ),
787 $this->changesListSpecialPage->getContext(),
788 $this->changesListSpecialPage->getDB(),
789 &$tables,
790 &$fields,
791 &$conds,
792 &$query_options,
793 &$join_conds,
794 $filters,
795 $now
796 ]
797 );
798
799 // @todo: This is not at all safe or sane. It just blindly assumes
800 // nothing in $conds depends on any other tables.
801 $result = wfGetDB( DB_MASTER )->select(
802 'user',
803 'user_name',
804 array_filter( $conds ) + [ 'user_email' => 'ut' ]
805 );
806
807 $usernames = [];
808 foreach ( $result as $row ) {
809 $usernames[] = $row->user_name;
810 }
811
812 return $usernames;
813 }
814
815 private function daysAgo( $days, $now ) {
816 $secondsPerDay = 86400;
817 return $now - $days * $secondsPerDay;
818 }
819
820 public function testGetStructuredFilterJsData() {
821 $this->changesListSpecialPage->filterGroups = [];
822
823 $definition = [
824 [
825 'name' => 'gub-group',
826 'title' => 'gub-group-title',
827 'class' => ChangesListBooleanFilterGroup::class,
828 'filters' => [
829 [
830 'name' => 'hidefoo',
831 'label' => 'foo-label',
832 'description' => 'foo-description',
833 'default' => true,
834 'showHide' => 'showhidefoo',
835 'priority' => 2,
836 ],
837 [
838 'name' => 'hidebar',
839 'label' => 'bar-label',
840 'description' => 'bar-description',
841 'default' => false,
842 'priority' => 4,
843 ]
844 ],
845 ],
846
847 [
848 'name' => 'des-group',
849 'title' => 'des-group-title',
850 'class' => ChangesListStringOptionsFilterGroup::class,
851 'isFullCoverage' => true,
852 'filters' => [
853 [
854 'name' => 'grault',
855 'label' => 'grault-label',
856 'description' => 'grault-description',
857 ],
858 [
859 'name' => 'garply',
860 'label' => 'garply-label',
861 'description' => 'garply-description',
862 ],
863 ],
864 'queryCallable' => function () {
865 },
866 'default' => ChangesListStringOptionsFilterGroup::NONE,
867 ],
868
869 [
870 'name' => 'unstructured',
871 'class' => ChangesListBooleanFilterGroup::class,
872 'filters' => [
873 [
874 'name' => 'hidethud',
875 'showHide' => 'showhidethud',
876 'default' => true,
877 ],
878
879 [
880 'name' => 'hidemos',
881 'showHide' => 'showhidemos',
882 'default' => false,
883 ],
884 ],
885 ],
886
887 ];
888
889 $this->changesListSpecialPage->registerFiltersFromDefinitions( $definition );
890
891 $this->assertArrayEquals(
892 [
893 // Filters that only display in the unstructured UI are
894 // are not included, and neither are groups that would
895 // be empty due to the above.
896 'groups' => [
897 [
898 'name' => 'gub-group',
899 'title' => 'gub-group-title',
900 'type' => ChangesListBooleanFilterGroup::TYPE,
901 'priority' => -1,
902 'filters' => [
903 [
904 'name' => 'hidebar',
905 'label' => 'bar-label',
906 'description' => 'bar-description',
907 'default' => false,
908 'priority' => 4,
909 'cssClass' => null,
910 'conflicts' => [],
911 'subset' => [],
912 'defaultHighlightColor' => null
913 ],
914 [
915 'name' => 'hidefoo',
916 'label' => 'foo-label',
917 'description' => 'foo-description',
918 'default' => true,
919 'priority' => 2,
920 'cssClass' => null,
921 'conflicts' => [],
922 'subset' => [],
923 'defaultHighlightColor' => null
924 ],
925 ],
926 'fullCoverage' => true,
927 'conflicts' => [],
928 ],
929
930 [
931 'name' => 'des-group',
932 'title' => 'des-group-title',
933 'type' => ChangesListStringOptionsFilterGroup::TYPE,
934 'priority' => -2,
935 'fullCoverage' => true,
936 'filters' => [
937 [
938 'name' => 'grault',
939 'label' => 'grault-label',
940 'description' => 'grault-description',
941 'cssClass' => null,
942 'priority' => -2,
943 'conflicts' => [],
944 'subset' => [],
945 'defaultHighlightColor' => null
946 ],
947 [
948 'name' => 'garply',
949 'label' => 'garply-label',
950 'description' => 'garply-description',
951 'cssClass' => null,
952 'priority' => -3,
953 'conflicts' => [],
954 'subset' => [],
955 'defaultHighlightColor' => null
956 ],
957 ],
958 'conflicts' => [],
959 'separator' => ';',
960 'default' => ChangesListStringOptionsFilterGroup::NONE,
961 ],
962 ],
963 'messageKeys' => [
964 'gub-group-title',
965 'bar-label',
966 'bar-description',
967 'foo-label',
968 'foo-description',
969 'des-group-title',
970 'grault-label',
971 'grault-description',
972 'garply-label',
973 'garply-description',
974 ],
975 ],
976 $this->changesListSpecialPage->getStructuredFilterJsData(),
977 /** ordered= */ false,
978 /** named= */ true
979 );
980 }
981
982 public function provideParseParameters() {
983 return [
984 [ 'hidebots', [ 'hidebots' => true ] ],
985
986 [ 'bots', [ 'hidebots' => false ] ],
987
988 [ 'hideminor', [ 'hideminor' => true ] ],
989
990 [ 'minor', [ 'hideminor' => false ] ],
991
992 [ 'hidemajor', [ 'hidemajor' => true ] ],
993
994 [ 'hideliu', [ 'hideliu' => true ] ],
995
996 [ 'hidepatrolled', [ 'hidepatrolled' => true ] ],
997
998 [ 'hideunpatrolled', [ 'hideunpatrolled' => true ] ],
999
1000 [ 'hideanons', [ 'hideanons' => true ] ],
1001
1002 [ 'hidemyself', [ 'hidemyself' => true ] ],
1003
1004 [ 'hidebyothers', [ 'hidebyothers' => true ] ],
1005
1006 [ 'hidehumans', [ 'hidehumans' => true ] ],
1007
1008 [ 'hidepageedits', [ 'hidepageedits' => true ] ],
1009
1010 [ 'pagedits', [ 'hidepageedits' => false ] ],
1011
1012 [ 'hidenewpages', [ 'hidenewpages' => true ] ],
1013
1014 [ 'hidecategorization', [ 'hidecategorization' => true ] ],
1015
1016 [ 'hidelog', [ 'hidelog' => true ] ],
1017
1018 [
1019 'userExpLevel=learner;experienced',
1020 [
1021 'userExpLevel' => 'learner;experienced'
1022 ],
1023 ],
1024
1025 // A few random combos
1026 [
1027 'bots,hideliu,hidemyself',
1028 [
1029 'hidebots' => false,
1030 'hideliu' => true,
1031 'hidemyself' => true,
1032 ],
1033 ],
1034
1035 [
1036 'minor,hideanons,categorization',
1037 [
1038 'hideminor' => false,
1039 'hideanons' => true,
1040 'hidecategorization' => false,
1041 ]
1042 ],
1043
1044 [
1045 'hidehumans,bots,hidecategorization',
1046 [
1047 'hidehumans' => true,
1048 'hidebots' => false,
1049 'hidecategorization' => true,
1050 ],
1051 ],
1052
1053 [
1054 'hidemyself,userExpLevel=newcomer;learner,hideminor',
1055 [
1056 'hidemyself' => true,
1057 'hideminor' => true,
1058 'userExpLevel' => 'newcomer;learner',
1059 ],
1060 ],
1061 ];
1062 }
1063
1064 public function provideGetFilterConflicts() {
1065 return [
1066 [
1067 "parameters" => [],
1068 "expectedConflicts" => false,
1069 ],
1070 [
1071 "parameters" => [
1072 "hideliu" => true,
1073 "userExpLevel" => "newcomer",
1074 ],
1075 "expectedConflicts" => false,
1076 ],
1077 [
1078 "parameters" => [
1079 "hideanons" => true,
1080 "userExpLevel" => "learner",
1081 ],
1082 "expectedConflicts" => false,
1083 ],
1084 [
1085 "parameters" => [
1086 "hidemajor" => true,
1087 "hidenewpages" => true,
1088 "hidepageedits" => true,
1089 "hidecategorization" => false,
1090 "hidelog" => true,
1091 "hideWikidata" => true,
1092 ],
1093 "expectedConflicts" => true,
1094 ],
1095 [
1096 "parameters" => [
1097 "hidemajor" => true,
1098 "hidenewpages" => false,
1099 "hidepageedits" => true,
1100 "hidecategorization" => false,
1101 "hidelog" => false,
1102 "hideWikidata" => true,
1103 ],
1104 "expectedConflicts" => true,
1105 ],
1106 [
1107 "parameters" => [
1108 "hidemajor" => true,
1109 "hidenewpages" => false,
1110 "hidepageedits" => false,
1111 "hidecategorization" => true,
1112 "hidelog" => true,
1113 "hideWikidata" => true,
1114 ],
1115 "expectedConflicts" => false,
1116 ],
1117 [
1118 "parameters" => [
1119 "hideminor" => true,
1120 "hidenewpages" => true,
1121 "hidepageedits" => true,
1122 "hidecategorization" => false,
1123 "hidelog" => true,
1124 "hideWikidata" => true,
1125 ],
1126 "expectedConflicts" => false,
1127 ],
1128 ];
1129 }
1130
1131 /**
1132 * @dataProvider provideGetFilterConflicts
1133 */
1134 public function testGetFilterConflicts( $parameters, $expectedConflicts ) {
1135 $context = new RequestContext;
1136 $context->setRequest( new FauxRequest( $parameters ) );
1137 $this->changesListSpecialPage->setContext( $context );
1138
1139 $this->assertEquals(
1140 $expectedConflicts,
1141 $this->changesListSpecialPage->areFiltersInConflict()
1142 );
1143 }
1144
1145 public function validateOptionsProvider() {
1146 return [
1147 [
1148 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 1 ],
1149 true,
1150 [ 'userExpLevel' => 'unregistered', 'hidebots' => 1, ],
1151 true,
1152 ],
1153 [
1154 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 0 ],
1155 true,
1156 [ 'hidebots' => 0, 'hidehumans' => 1 ],
1157 true,
1158 ],
1159 [
1160 [ 'hideanons' => 1 ],
1161 true,
1162 [ 'userExpLevel' => 'registered' ],
1163 true,
1164 ],
1165 [
1166 [ 'hideliu' => 1 ],
1167 true,
1168 [ 'userExpLevel' => 'unregistered' ],
1169 true,
1170 ],
1171 [
1172 [ 'hideanons' => 1, 'hidebots' => 1 ],
1173 true,
1174 [ 'userExpLevel' => 'registered', 'hidebots' => 1 ],
1175 true,
1176 ],
1177 [
1178 [ 'hideliu' => 1, 'hidebots' => 0 ],
1179 true,
1180 [ 'userExpLevel' => 'unregistered', 'hidebots' => 0 ],
1181 true,
1182 ],
1183 [
1184 [ 'hidemyself' => 1, 'hidebyothers' => 1 ],
1185 true,
1186 [],
1187 true,
1188 ],
1189 [
1190 [ 'hidebots' => 1, 'hidehumans' => 1 ],
1191 true,
1192 [],
1193 true,
1194 ],
1195 [
1196 [ 'hidepatrolled' => 1, 'hideunpatrolled' => 1 ],
1197 true,
1198 [],
1199 true,
1200 ],
1201 [
1202 [ 'hideminor' => 1, 'hidemajor' => 1 ],
1203 true,
1204 [],
1205 true,
1206 ],
1207 [
1208 // changeType
1209 [ 'hidepageedits' => 1, 'hidenewpages' => 1, 'hidecategorization' => 1, 'hidelog' => 1, ],
1210 true,
1211 [],
1212 true,
1213 ],
1214 ];
1215 }
1216 }