Merge "Test ApiUserrights"
[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 testGetFilterGroupDefinitionFromLegacyCustomFilters() {
677 $customFilters = [
678 'hidefoo' => [
679 'msg' => 'showhidefoo',
680 'default' => true,
681 ],
682
683 'hidebar' => [
684 'msg' => 'showhidebar',
685 'default' => false,
686 ],
687 ];
688
689 $this->assertEquals(
690 [
691 'name' => 'unstructured',
692 'class' => ChangesListBooleanFilterGroup::class,
693 'priority' => -1,
694 'filters' => [
695 [
696 'name' => 'hidefoo',
697 'showHide' => 'showhidefoo',
698 'default' => true,
699 ],
700 [
701 'name' => 'hidebar',
702 'showHide' => 'showhidebar',
703 'default' => false,
704 ]
705 ],
706 ],
707 $this->changesListSpecialPage->getFilterGroupDefinitionFromLegacyCustomFilters(
708 $customFilters
709 )
710 );
711 }
712
713 public function testGetStructuredFilterJsData() {
714 $this->changesListSpecialPage->filterGroups = [];
715
716 $definition = [
717 [
718 'name' => 'gub-group',
719 'title' => 'gub-group-title',
720 'class' => ChangesListBooleanFilterGroup::class,
721 'filters' => [
722 [
723 'name' => 'hidefoo',
724 'label' => 'foo-label',
725 'description' => 'foo-description',
726 'default' => true,
727 'showHide' => 'showhidefoo',
728 'priority' => 2,
729 ],
730 [
731 'name' => 'hidebar',
732 'label' => 'bar-label',
733 'description' => 'bar-description',
734 'default' => false,
735 'priority' => 4,
736 ]
737 ],
738 ],
739
740 [
741 'name' => 'des-group',
742 'title' => 'des-group-title',
743 'class' => ChangesListStringOptionsFilterGroup::class,
744 'isFullCoverage' => true,
745 'filters' => [
746 [
747 'name' => 'grault',
748 'label' => 'grault-label',
749 'description' => 'grault-description',
750 ],
751 [
752 'name' => 'garply',
753 'label' => 'garply-label',
754 'description' => 'garply-description',
755 ],
756 ],
757 'queryCallable' => function () {
758 },
759 'default' => ChangesListStringOptionsFilterGroup::NONE,
760 ],
761
762 [
763 'name' => 'unstructured',
764 'class' => ChangesListBooleanFilterGroup::class,
765 'filters' => [
766 [
767 'name' => 'hidethud',
768 'showHide' => 'showhidethud',
769 'default' => true,
770 ],
771
772 [
773 'name' => 'hidemos',
774 'showHide' => 'showhidemos',
775 'default' => false,
776 ],
777 ],
778 ],
779
780 ];
781
782 $this->changesListSpecialPage->registerFiltersFromDefinitions( $definition );
783
784 $this->assertArrayEquals(
785 [
786 // Filters that only display in the unstructured UI are
787 // are not included, and neither are groups that would
788 // be empty due to the above.
789 'groups' => [
790 [
791 'name' => 'gub-group',
792 'title' => 'gub-group-title',
793 'type' => ChangesListBooleanFilterGroup::TYPE,
794 'priority' => -1,
795 'filters' => [
796 [
797 'name' => 'hidebar',
798 'label' => 'bar-label',
799 'description' => 'bar-description',
800 'default' => false,
801 'priority' => 4,
802 'cssClass' => null,
803 'conflicts' => [],
804 'subset' => [],
805 'defaultHighlightColor' => null
806 ],
807 [
808 'name' => 'hidefoo',
809 'label' => 'foo-label',
810 'description' => 'foo-description',
811 'default' => true,
812 'priority' => 2,
813 'cssClass' => null,
814 'conflicts' => [],
815 'subset' => [],
816 'defaultHighlightColor' => null
817 ],
818 ],
819 'fullCoverage' => true,
820 'conflicts' => [],
821 ],
822
823 [
824 'name' => 'des-group',
825 'title' => 'des-group-title',
826 'type' => ChangesListStringOptionsFilterGroup::TYPE,
827 'priority' => -2,
828 'fullCoverage' => true,
829 'filters' => [
830 [
831 'name' => 'grault',
832 'label' => 'grault-label',
833 'description' => 'grault-description',
834 'cssClass' => null,
835 'priority' => -2,
836 'conflicts' => [],
837 'subset' => [],
838 'defaultHighlightColor' => null
839 ],
840 [
841 'name' => 'garply',
842 'label' => 'garply-label',
843 'description' => 'garply-description',
844 'cssClass' => null,
845 'priority' => -3,
846 'conflicts' => [],
847 'subset' => [],
848 'defaultHighlightColor' => null
849 ],
850 ],
851 'conflicts' => [],
852 'separator' => ';',
853 'default' => ChangesListStringOptionsFilterGroup::NONE,
854 ],
855 ],
856 'messageKeys' => [
857 'gub-group-title',
858 'bar-label',
859 'bar-description',
860 'foo-label',
861 'foo-description',
862 'des-group-title',
863 'grault-label',
864 'grault-description',
865 'garply-label',
866 'garply-description',
867 ],
868 ],
869 $this->changesListSpecialPage->getStructuredFilterJsData(),
870 /** ordered= */ false,
871 /** named= */ true
872 );
873 }
874
875 public function provideParseParameters() {
876 return [
877 [ 'hidebots', [ 'hidebots' => true ] ],
878
879 [ 'bots', [ 'hidebots' => false ] ],
880
881 [ 'hideminor', [ 'hideminor' => true ] ],
882
883 [ 'minor', [ 'hideminor' => false ] ],
884
885 [ 'hidemajor', [ 'hidemajor' => true ] ],
886
887 [ 'hideliu', [ 'hideliu' => true ] ],
888
889 [ 'hidepatrolled', [ 'hidepatrolled' => true ] ],
890
891 [ 'hideunpatrolled', [ 'hideunpatrolled' => true ] ],
892
893 [ 'hideanons', [ 'hideanons' => true ] ],
894
895 [ 'hidemyself', [ 'hidemyself' => true ] ],
896
897 [ 'hidebyothers', [ 'hidebyothers' => true ] ],
898
899 [ 'hidehumans', [ 'hidehumans' => true ] ],
900
901 [ 'hidepageedits', [ 'hidepageedits' => true ] ],
902
903 [ 'pagedits', [ 'hidepageedits' => false ] ],
904
905 [ 'hidenewpages', [ 'hidenewpages' => true ] ],
906
907 [ 'hidecategorization', [ 'hidecategorization' => true ] ],
908
909 [ 'hidelog', [ 'hidelog' => true ] ],
910
911 [
912 'userExpLevel=learner;experienced',
913 [
914 'userExpLevel' => 'learner;experienced'
915 ],
916 ],
917
918 // A few random combos
919 [
920 'bots,hideliu,hidemyself',
921 [
922 'hidebots' => false,
923 'hideliu' => true,
924 'hidemyself' => true,
925 ],
926 ],
927
928 [
929 'minor,hideanons,categorization',
930 [
931 'hideminor' => false,
932 'hideanons' => true,
933 'hidecategorization' => false,
934 ]
935 ],
936
937 [
938 'hidehumans,bots,hidecategorization',
939 [
940 'hidehumans' => true,
941 'hidebots' => false,
942 'hidecategorization' => true,
943 ],
944 ],
945
946 [
947 'hidemyself,userExpLevel=newcomer;learner,hideminor',
948 [
949 'hidemyself' => true,
950 'hideminor' => true,
951 'userExpLevel' => 'newcomer;learner',
952 ],
953 ],
954 ];
955 }
956
957 public function provideGetFilterConflicts() {
958 return [
959 [
960 "parameters" => [],
961 "expectedConflicts" => false,
962 ],
963 [
964 "parameters" => [
965 "hideliu" => true,
966 "userExpLevel" => "newcomer",
967 ],
968 "expectedConflicts" => false,
969 ],
970 [
971 "parameters" => [
972 "hideanons" => true,
973 "userExpLevel" => "learner",
974 ],
975 "expectedConflicts" => false,
976 ],
977 [
978 "parameters" => [
979 "hidemajor" => true,
980 "hidenewpages" => true,
981 "hidepageedits" => true,
982 "hidecategorization" => false,
983 "hidelog" => true,
984 "hideWikidata" => true,
985 ],
986 "expectedConflicts" => true,
987 ],
988 [
989 "parameters" => [
990 "hidemajor" => true,
991 "hidenewpages" => false,
992 "hidepageedits" => true,
993 "hidecategorization" => false,
994 "hidelog" => false,
995 "hideWikidata" => true,
996 ],
997 "expectedConflicts" => true,
998 ],
999 [
1000 "parameters" => [
1001 "hidemajor" => true,
1002 "hidenewpages" => false,
1003 "hidepageedits" => false,
1004 "hidecategorization" => true,
1005 "hidelog" => true,
1006 "hideWikidata" => true,
1007 ],
1008 "expectedConflicts" => false,
1009 ],
1010 [
1011 "parameters" => [
1012 "hideminor" => true,
1013 "hidenewpages" => true,
1014 "hidepageedits" => true,
1015 "hidecategorization" => false,
1016 "hidelog" => true,
1017 "hideWikidata" => true,
1018 ],
1019 "expectedConflicts" => false,
1020 ],
1021 ];
1022 }
1023
1024 /**
1025 * @dataProvider provideGetFilterConflicts
1026 */
1027 public function testGetFilterConflicts( $parameters, $expectedConflicts ) {
1028 $context = new RequestContext;
1029 $context->setRequest( new FauxRequest( $parameters ) );
1030 $this->changesListSpecialPage->setContext( $context );
1031
1032 $this->assertEquals(
1033 $expectedConflicts,
1034 $this->changesListSpecialPage->areFiltersInConflict()
1035 );
1036 }
1037
1038 public function validateOptionsProvider() {
1039 return [
1040 [
1041 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 1 ],
1042 true,
1043 [ 'userExpLevel' => 'unregistered', 'hidebots' => 1, ],
1044 ],
1045 [
1046 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 0 ],
1047 true,
1048 [ 'hidebots' => 0, 'hidehumans' => 1 ],
1049 ],
1050 [
1051 [ 'hideanons' => 1 ],
1052 true,
1053 [ 'userExpLevel' => 'registered' ]
1054 ],
1055 [
1056 [ 'hideliu' => 1 ],
1057 true,
1058 [ 'userExpLevel' => 'unregistered' ]
1059 ],
1060 [
1061 [ 'hideanons' => 1, 'hidebots' => 1 ],
1062 true,
1063 [ 'userExpLevel' => 'registered', 'hidebots' => 1 ]
1064 ],
1065 [
1066 [ 'hideliu' => 1, 'hidebots' => 0 ],
1067 true,
1068 [ 'userExpLevel' => 'unregistered', 'hidebots' => 0 ]
1069 ],
1070 [
1071 [ 'hidemyself' => 1, 'hidebyothers' => 1 ],
1072 true,
1073 [],
1074 ],
1075 [
1076 [ 'hidebots' => 1, 'hidehumans' => 1 ],
1077 true,
1078 [],
1079 ],
1080 [
1081 [ 'hidepatrolled' => 1, 'hideunpatrolled' => 1 ],
1082 true,
1083 [],
1084 ],
1085 [
1086 [ 'hideminor' => 1, 'hidemajor' => 1 ],
1087 true,
1088 [],
1089 ],
1090 [
1091 // changeType
1092 [ 'hidepageedits' => 1, 'hidenewpages' => 1, 'hidecategorization' => 1, 'hidelog' => 1, ],
1093 true,
1094 [],
1095 ],
1096 ];
1097 }
1098 }