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