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