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