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