Merge "Fix option name in maintenance/importDump.php"
[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 ) || false === strpos( $var, 'rc_timestamp ' ) );
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', MIGRATION_WRITE_BOTH );
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()}') OR "
208 . "(rc_actor = '0' AND rc_user = '{$user->getId()}'))",
209 ],
210 [
211 'hidemyself' => 1,
212 ],
213 "rc conditions: hidemyself=1 (logged in)",
214 $user
215 );
216
217 $user = User::newFromName( '10.11.12.13', false );
218 $id = $user->getActorId( wfGetDB( DB_MASTER ) );
219 $this->assertConditions(
220 [ # expected
221 "NOT((rc_actor = '$id') OR (rc_actor = '0' AND rc_user_text = '10.11.12.13'))",
222 ],
223 [
224 'hidemyself' => 1,
225 ],
226 "rc conditions: hidemyself=1 (anon)",
227 $user
228 );
229 }
230
231 public function testRcHidebyothersFilter() {
232 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
233 $this->overrideMwServices();
234
235 $user = $this->getTestUser()->getUser();
236 $user->getActorId( wfGetDB( DB_MASTER ) );
237 $this->assertConditions(
238 [ # expected
239 "(rc_actor = '{$user->getActorId()}') OR "
240 . "(rc_actor = '0' AND rc_user_text = '{$user->getName()}')",
241 ],
242 [
243 'hidebyothers' => 1,
244 ],
245 "rc conditions: hidebyothers=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 "(rc_actor = '$id') OR (rc_actor = '0' AND rc_user_text = '10.11.12.13')",
254 ],
255 [
256 'hidebyothers' => 1,
257 ],
258 "rc conditions: hidebyothers=1 (anon)",
259 $user
260 );
261 }
262
263 public function testRcHidepageedits() {
264 $this->assertConditions(
265 [ # expected
266 "rc_type != '0'",
267 ],
268 [
269 'hidepageedits' => 1,
270 ],
271 "rc conditions: hidepageedits=1"
272 );
273 }
274
275 public function testRcHidenewpages() {
276 $this->assertConditions(
277 [ # expected
278 "rc_type != '1'",
279 ],
280 [
281 'hidenewpages' => 1,
282 ],
283 "rc conditions: hidenewpages=1"
284 );
285 }
286
287 public function testRcHidelog() {
288 $this->assertConditions(
289 [ # expected
290 "rc_type != '3'",
291 ],
292 [
293 'hidelog' => 1,
294 ],
295 "rc conditions: hidelog=1"
296 );
297 }
298
299 public function testRcHidehumans() {
300 $this->assertConditions(
301 [ # expected
302 'rc_bot' => 1,
303 ],
304 [
305 'hidebots' => 0,
306 'hidehumans' => 1,
307 ],
308 "rc conditions: hidebots=0 hidehumans=1"
309 );
310 }
311
312 public function testRcHidepatrolledDisabledFilter() {
313 $this->setMwGlobals( 'wgUseRCPatrol', false );
314 $user = $this->getTestUser()->getUser();
315 $this->assertConditions(
316 [ # expected
317 ],
318 [
319 'hidepatrolled' => 1,
320 ],
321 "rc conditions: hidepatrolled=1 (user not allowed)",
322 $user
323 );
324 }
325
326 public function testRcHideunpatrolledDisabledFilter() {
327 $this->setMwGlobals( 'wgUseRCPatrol', false );
328 $user = $this->getTestUser()->getUser();
329 $this->assertConditions(
330 [ # expected
331 ],
332 [
333 'hideunpatrolled' => 1,
334 ],
335 "rc conditions: hideunpatrolled=1 (user not allowed)",
336 $user
337 );
338 }
339 public function testRcHidepatrolledFilter() {
340 $user = $this->getTestSysop()->getUser();
341 $this->assertConditions(
342 [ # expected
343 'rc_patrolled' => 0,
344 ],
345 [
346 'hidepatrolled' => 1,
347 ],
348 "rc conditions: hidepatrolled=1",
349 $user
350 );
351 }
352
353 public function testRcHideunpatrolledFilter() {
354 $user = $this->getTestSysop()->getUser();
355 $this->assertConditions(
356 [ # expected
357 'rc_patrolled' => [ 1, 2 ],
358 ],
359 [
360 'hideunpatrolled' => 1,
361 ],
362 "rc conditions: hideunpatrolled=1",
363 $user
364 );
365 }
366
367 public function testRcReviewStatusFilter() {
368 $user = $this->getTestSysop()->getUser();
369 $this->assertConditions(
370 [ #expected
371 'rc_patrolled' => 1,
372 ],
373 [
374 'reviewStatus' => 'manual'
375 ],
376 "rc conditions: reviewStatus=manual",
377 $user
378 );
379 $this->assertConditions(
380 [ #expected
381 'rc_patrolled' => [ 0, 2 ],
382 ],
383 [
384 'reviewStatus' => 'unpatrolled;auto'
385 ],
386 "rc conditions: reviewStatus=unpatrolled;auto",
387 $user
388 );
389 }
390
391 public function testRcHideminorFilter() {
392 $this->assertConditions(
393 [ # expected
394 "rc_minor = 0",
395 ],
396 [
397 'hideminor' => 1,
398 ],
399 "rc conditions: hideminor=1"
400 );
401 }
402
403 public function testRcHidemajorFilter() {
404 $this->assertConditions(
405 [ # expected
406 "rc_minor = 1",
407 ],
408 [
409 'hidemajor' => 1,
410 ],
411 "rc conditions: hidemajor=1"
412 );
413 }
414
415 public function testHideCategorization() {
416 $this->assertConditions(
417 [
418 # expected
419 "rc_type != '6'"
420 ],
421 [
422 'hidecategorization' => 1
423 ],
424 "rc conditions: hidecategorization=1"
425 );
426 }
427
428 public function testFilterUserExpLevelAll() {
429 $this->assertConditions(
430 [
431 # expected
432 ],
433 [
434 'userExpLevel' => 'registered;unregistered;newcomer;learner;experienced',
435 ],
436 "rc conditions: userExpLevel=registered;unregistered;newcomer;learner;experienced"
437 );
438 }
439
440 public function testFilterUserExpLevelRegisteredUnregistered() {
441 $this->assertConditions(
442 [
443 # expected
444 ],
445 [
446 'userExpLevel' => 'registered;unregistered',
447 ],
448 "rc conditions: userExpLevel=registered;unregistered"
449 );
450 }
451
452 public function testFilterUserExpLevelRegisteredUnregisteredLearner() {
453 $this->assertConditions(
454 [
455 # expected
456 ],
457 [
458 'userExpLevel' => 'registered;unregistered;learner',
459 ],
460 "rc conditions: userExpLevel=registered;unregistered;learner"
461 );
462 }
463
464 public function testFilterUserExpLevelAllExperienceLevels() {
465 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
466 $this->overrideMwServices();
467
468 $this->assertConditions(
469 [
470 # expected
471 'COALESCE( actor_rc_user.actor_user, rc_user ) != 0',
472 ],
473 [
474 'userExpLevel' => 'newcomer;learner;experienced',
475 ],
476 "rc conditions: userExpLevel=newcomer;learner;experienced"
477 );
478 }
479
480 public function testFilterUserExpLevelRegistrered() {
481 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
482 $this->overrideMwServices();
483
484 $this->assertConditions(
485 [
486 # expected
487 'COALESCE( actor_rc_user.actor_user, rc_user ) != 0',
488 ],
489 [
490 'userExpLevel' => 'registered',
491 ],
492 "rc conditions: userExpLevel=registered"
493 );
494 }
495
496 public function testFilterUserExpLevelUnregistrered() {
497 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
498 $this->overrideMwServices();
499
500 $this->assertConditions(
501 [
502 # expected
503 'COALESCE( actor_rc_user.actor_user, rc_user ) = 0',
504 ],
505 [
506 'userExpLevel' => 'unregistered',
507 ],
508 "rc conditions: userExpLevel=unregistered"
509 );
510 }
511
512 public function testFilterUserExpLevelRegistreredOrLearner() {
513 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
514 $this->overrideMwServices();
515
516 $this->assertConditions(
517 [
518 # expected
519 'COALESCE( actor_rc_user.actor_user, rc_user ) != 0',
520 ],
521 [
522 'userExpLevel' => 'registered;learner',
523 ],
524 "rc conditions: userExpLevel=registered;learner"
525 );
526 }
527
528 public function testFilterUserExpLevelUnregistreredOrExperienced() {
529 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
530 $this->overrideMwServices();
531
532 $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
533
534 $this->assertRegExp(
535 '/\(COALESCE\( actor_rc_user.actor_user, rc_user \) = 0\) OR '
536 . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
537 reset( $conds ),
538 "rc conditions: userExpLevel=unregistered;experienced"
539 );
540 }
541
542 public function testFilterUserExpLevel() {
543 $now = time();
544 $this->setMwGlobals( [
545 'wgLearnerEdits' => 10,
546 'wgLearnerMemberSince' => 4,
547 'wgExperiencedUserEdits' => 500,
548 'wgExperiencedUserMemberSince' => 30,
549 ] );
550
551 $this->createUsers( [
552 'Newcomer1' => [ 'edits' => 2, 'days' => 2 ],
553 'Newcomer2' => [ 'edits' => 12, 'days' => 3 ],
554 'Newcomer3' => [ 'edits' => 8, 'days' => 5 ],
555 'Learner1' => [ 'edits' => 15, 'days' => 10 ],
556 'Learner2' => [ 'edits' => 450, 'days' => 20 ],
557 'Learner3' => [ 'edits' => 460, 'days' => 33 ],
558 'Learner4' => [ 'edits' => 525, 'days' => 28 ],
559 'Experienced1' => [ 'edits' => 538, 'days' => 33 ],
560 ], $now );
561
562 // newcomers only
563 $this->assertArrayEquals(
564 [ 'Newcomer1', 'Newcomer2', 'Newcomer3' ],
565 $this->fetchUsers( [ 'newcomer' ], $now )
566 );
567
568 // newcomers and learner
569 $this->assertArrayEquals(
570 [
571 'Newcomer1', 'Newcomer2', 'Newcomer3',
572 'Learner1', 'Learner2', 'Learner3', 'Learner4',
573 ],
574 $this->fetchUsers( [ 'newcomer', 'learner' ], $now )
575 );
576
577 // newcomers and more learner
578 $this->assertArrayEquals(
579 [
580 'Newcomer1', 'Newcomer2', 'Newcomer3',
581 'Experienced1',
582 ],
583 $this->fetchUsers( [ 'newcomer', 'experienced' ], $now )
584 );
585
586 // learner only
587 $this->assertArrayEquals(
588 [ 'Learner1', 'Learner2', 'Learner3', 'Learner4' ],
589 $this->fetchUsers( [ 'learner' ], $now )
590 );
591
592 // more experienced only
593 $this->assertArrayEquals(
594 [ 'Experienced1' ],
595 $this->fetchUsers( [ 'experienced' ], $now )
596 );
597
598 // learner and more experienced
599 $this->assertArrayEquals(
600 [
601 'Learner1', 'Learner2', 'Learner3', 'Learner4',
602 'Experienced1',
603 ],
604 $this->fetchUsers( [ 'learner', 'experienced' ], $now ),
605 'Learner and more experienced'
606 );
607 }
608
609 private function createUsers( $specs, $now ) {
610 $dbw = wfGetDB( DB_MASTER );
611 foreach ( $specs as $name => $spec ) {
612 User::createNew(
613 $name,
614 [
615 'editcount' => $spec['edits'],
616 'registration' => $dbw->timestamp( $this->daysAgo( $spec['days'], $now ) ),
617 'email' => 'ut',
618 ]
619 );
620 }
621 }
622
623 private function fetchUsers( $filters, $now ) {
624 $tables = [];
625 $conds = [];
626 $fields = [];
627 $query_options = [];
628 $join_conds = [];
629
630 sort( $filters );
631
632 call_user_func_array(
633 [ $this->changesListSpecialPage, 'filterOnUserExperienceLevel' ],
634 [
635 get_class( $this->changesListSpecialPage ),
636 $this->changesListSpecialPage->getContext(),
637 $this->changesListSpecialPage->getDB(),
638 &$tables,
639 &$fields,
640 &$conds,
641 &$query_options,
642 &$join_conds,
643 $filters,
644 $now
645 ]
646 );
647
648 // @todo: This is not at all safe or sane. It just blindly assumes
649 // nothing in $conds depends on any other tables.
650 $result = wfGetDB( DB_MASTER )->select(
651 'user',
652 'user_name',
653 array_filter( $conds ) + [ 'user_email' => 'ut' ]
654 );
655
656 $usernames = [];
657 foreach ( $result as $row ) {
658 $usernames[] = $row->user_name;
659 }
660
661 return $usernames;
662 }
663
664 private function daysAgo( $days, $now ) {
665 $secondsPerDay = 86400;
666 return $now - $days * $secondsPerDay;
667 }
668
669 public function testGetStructuredFilterJsData() {
670 $this->changesListSpecialPage->filterGroups = [];
671
672 $definition = [
673 [
674 'name' => 'gub-group',
675 'title' => 'gub-group-title',
676 'class' => ChangesListBooleanFilterGroup::class,
677 'filters' => [
678 [
679 'name' => 'hidefoo',
680 'label' => 'foo-label',
681 'description' => 'foo-description',
682 'default' => true,
683 'showHide' => 'showhidefoo',
684 'priority' => 2,
685 ],
686 [
687 'name' => 'hidebar',
688 'label' => 'bar-label',
689 'description' => 'bar-description',
690 'default' => false,
691 'priority' => 4,
692 ]
693 ],
694 ],
695
696 [
697 'name' => 'des-group',
698 'title' => 'des-group-title',
699 'class' => ChangesListStringOptionsFilterGroup::class,
700 'isFullCoverage' => true,
701 'filters' => [
702 [
703 'name' => 'grault',
704 'label' => 'grault-label',
705 'description' => 'grault-description',
706 ],
707 [
708 'name' => 'garply',
709 'label' => 'garply-label',
710 'description' => 'garply-description',
711 ],
712 ],
713 'queryCallable' => function () {
714 },
715 'default' => ChangesListStringOptionsFilterGroup::NONE,
716 ],
717
718 [
719 'name' => 'unstructured',
720 'class' => ChangesListBooleanFilterGroup::class,
721 'filters' => [
722 [
723 'name' => 'hidethud',
724 'showHide' => 'showhidethud',
725 'default' => true,
726 ],
727
728 [
729 'name' => 'hidemos',
730 'showHide' => 'showhidemos',
731 'default' => false,
732 ],
733 ],
734 ],
735
736 ];
737
738 $this->changesListSpecialPage->registerFiltersFromDefinitions( $definition );
739
740 $this->assertArrayEquals(
741 [
742 // Filters that only display in the unstructured UI are
743 // are not included, and neither are groups that would
744 // be empty due to the above.
745 'groups' => [
746 [
747 'name' => 'gub-group',
748 'title' => 'gub-group-title',
749 'type' => ChangesListBooleanFilterGroup::TYPE,
750 'priority' => -1,
751 'filters' => [
752 [
753 'name' => 'hidebar',
754 'label' => 'bar-label',
755 'description' => 'bar-description',
756 'default' => false,
757 'priority' => 4,
758 'cssClass' => null,
759 'conflicts' => [],
760 'subset' => [],
761 'defaultHighlightColor' => null
762 ],
763 [
764 'name' => 'hidefoo',
765 'label' => 'foo-label',
766 'description' => 'foo-description',
767 'default' => true,
768 'priority' => 2,
769 'cssClass' => null,
770 'conflicts' => [],
771 'subset' => [],
772 'defaultHighlightColor' => null
773 ],
774 ],
775 'fullCoverage' => true,
776 'conflicts' => [],
777 ],
778
779 [
780 'name' => 'des-group',
781 'title' => 'des-group-title',
782 'type' => ChangesListStringOptionsFilterGroup::TYPE,
783 'priority' => -2,
784 'fullCoverage' => true,
785 'filters' => [
786 [
787 'name' => 'grault',
788 'label' => 'grault-label',
789 'description' => 'grault-description',
790 'cssClass' => null,
791 'priority' => -2,
792 'conflicts' => [],
793 'subset' => [],
794 'defaultHighlightColor' => null
795 ],
796 [
797 'name' => 'garply',
798 'label' => 'garply-label',
799 'description' => 'garply-description',
800 'cssClass' => null,
801 'priority' => -3,
802 'conflicts' => [],
803 'subset' => [],
804 'defaultHighlightColor' => null
805 ],
806 ],
807 'conflicts' => [],
808 'separator' => ';',
809 'default' => ChangesListStringOptionsFilterGroup::NONE,
810 ],
811 ],
812 'messageKeys' => [
813 'gub-group-title',
814 'bar-label',
815 'bar-description',
816 'foo-label',
817 'foo-description',
818 'des-group-title',
819 'grault-label',
820 'grault-description',
821 'garply-label',
822 'garply-description',
823 ],
824 ],
825 $this->changesListSpecialPage->getStructuredFilterJsData(),
826 /** ordered= */ false,
827 /** named= */ true
828 );
829 }
830
831 public function provideParseParameters() {
832 return [
833 [ 'hidebots', [ 'hidebots' => true ] ],
834
835 [ 'bots', [ 'hidebots' => false ] ],
836
837 [ 'hideminor', [ 'hideminor' => true ] ],
838
839 [ 'minor', [ 'hideminor' => false ] ],
840
841 [ 'hidemajor', [ 'hidemajor' => true ] ],
842
843 [ 'hideliu', [ 'hideliu' => true ] ],
844
845 [ 'hidepatrolled', [ 'hidepatrolled' => true ] ],
846
847 [ 'hideunpatrolled', [ 'hideunpatrolled' => true ] ],
848
849 [ 'hideanons', [ 'hideanons' => true ] ],
850
851 [ 'hidemyself', [ 'hidemyself' => true ] ],
852
853 [ 'hidebyothers', [ 'hidebyothers' => true ] ],
854
855 [ 'hidehumans', [ 'hidehumans' => true ] ],
856
857 [ 'hidepageedits', [ 'hidepageedits' => true ] ],
858
859 [ 'pagedits', [ 'hidepageedits' => false ] ],
860
861 [ 'hidenewpages', [ 'hidenewpages' => true ] ],
862
863 [ 'hidecategorization', [ 'hidecategorization' => true ] ],
864
865 [ 'hidelog', [ 'hidelog' => true ] ],
866
867 [
868 'userExpLevel=learner;experienced',
869 [
870 'userExpLevel' => 'learner;experienced'
871 ],
872 ],
873
874 // A few random combos
875 [
876 'bots,hideliu,hidemyself',
877 [
878 'hidebots' => false,
879 'hideliu' => true,
880 'hidemyself' => true,
881 ],
882 ],
883
884 [
885 'minor,hideanons,categorization',
886 [
887 'hideminor' => false,
888 'hideanons' => true,
889 'hidecategorization' => false,
890 ]
891 ],
892
893 [
894 'hidehumans,bots,hidecategorization',
895 [
896 'hidehumans' => true,
897 'hidebots' => false,
898 'hidecategorization' => true,
899 ],
900 ],
901
902 [
903 'hidemyself,userExpLevel=newcomer;learner,hideminor',
904 [
905 'hidemyself' => true,
906 'hideminor' => true,
907 'userExpLevel' => 'newcomer;learner',
908 ],
909 ],
910 ];
911 }
912
913 public function provideGetFilterConflicts() {
914 return [
915 [
916 "parameters" => [],
917 "expectedConflicts" => false,
918 ],
919 [
920 "parameters" => [
921 "hideliu" => true,
922 "userExpLevel" => "newcomer",
923 ],
924 "expectedConflicts" => false,
925 ],
926 [
927 "parameters" => [
928 "hideanons" => true,
929 "userExpLevel" => "learner",
930 ],
931 "expectedConflicts" => false,
932 ],
933 [
934 "parameters" => [
935 "hidemajor" => true,
936 "hidenewpages" => true,
937 "hidepageedits" => true,
938 "hidecategorization" => false,
939 "hidelog" => true,
940 "hideWikidata" => true,
941 ],
942 "expectedConflicts" => true,
943 ],
944 [
945 "parameters" => [
946 "hidemajor" => true,
947 "hidenewpages" => false,
948 "hidepageedits" => true,
949 "hidecategorization" => false,
950 "hidelog" => false,
951 "hideWikidata" => true,
952 ],
953 "expectedConflicts" => true,
954 ],
955 [
956 "parameters" => [
957 "hidemajor" => true,
958 "hidenewpages" => false,
959 "hidepageedits" => false,
960 "hidecategorization" => true,
961 "hidelog" => true,
962 "hideWikidata" => true,
963 ],
964 "expectedConflicts" => false,
965 ],
966 [
967 "parameters" => [
968 "hideminor" => true,
969 "hidenewpages" => true,
970 "hidepageedits" => true,
971 "hidecategorization" => false,
972 "hidelog" => true,
973 "hideWikidata" => true,
974 ],
975 "expectedConflicts" => false,
976 ],
977 ];
978 }
979
980 /**
981 * @dataProvider provideGetFilterConflicts
982 */
983 public function testGetFilterConflicts( $parameters, $expectedConflicts ) {
984 $context = new RequestContext;
985 $context->setRequest( new FauxRequest( $parameters ) );
986 $this->changesListSpecialPage->setContext( $context );
987
988 $this->assertEquals(
989 $expectedConflicts,
990 $this->changesListSpecialPage->areFiltersInConflict()
991 );
992 }
993
994 public function validateOptionsProvider() {
995 return [
996 [
997 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 1 ],
998 true,
999 [ 'userExpLevel' => 'unregistered', 'hidebots' => 1, ],
1000 true,
1001 ],
1002 [
1003 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 0 ],
1004 true,
1005 [ 'hidebots' => 0, 'hidehumans' => 1 ],
1006 true,
1007 ],
1008 [
1009 [ 'hideanons' => 1 ],
1010 true,
1011 [ 'userExpLevel' => 'registered' ],
1012 true,
1013 ],
1014 [
1015 [ 'hideliu' => 1 ],
1016 true,
1017 [ 'userExpLevel' => 'unregistered' ],
1018 true,
1019 ],
1020 [
1021 [ 'hideanons' => 1, 'hidebots' => 1 ],
1022 true,
1023 [ 'userExpLevel' => 'registered', 'hidebots' => 1 ],
1024 true,
1025 ],
1026 [
1027 [ 'hideliu' => 1, 'hidebots' => 0 ],
1028 true,
1029 [ 'userExpLevel' => 'unregistered', 'hidebots' => 0 ],
1030 true,
1031 ],
1032 [
1033 [ 'hidemyself' => 1, 'hidebyothers' => 1 ],
1034 true,
1035 [],
1036 true,
1037 ],
1038 [
1039 [ 'hidebots' => 1, 'hidehumans' => 1 ],
1040 true,
1041 [],
1042 true,
1043 ],
1044 [
1045 [ 'hidepatrolled' => 1, 'hideunpatrolled' => 1 ],
1046 true,
1047 [],
1048 true,
1049 ],
1050 [
1051 [ 'hideminor' => 1, 'hidemajor' => 1 ],
1052 true,
1053 [],
1054 true,
1055 ],
1056 [
1057 // changeType
1058 [ 'hidepageedits' => 1, 'hidenewpages' => 1, 'hidecategorization' => 1, 'hidelog' => 1, ],
1059 true,
1060 [],
1061 true,
1062 ],
1063 ];
1064 }
1065 }