Merge "Remove single-item HTML list for re-upload link"
[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 protected function getPage() {
19 $mock = $this->getMockBuilder( ChangesListSpecialPage::class )
20 ->setConstructorArgs(
21 [
22 'ChangesListSpecialPage',
23 ''
24 ]
25 )
26 ->setMethods( [ 'getPageTitle' ] )
27 ->getMockForAbstractClass();
28
29 $mock->method( 'getPageTitle' )->willReturn(
30 Title::makeTitle( NS_SPECIAL, 'ChangesListSpecialPage' )
31 );
32
33 $mock = TestingAccessWrapper::newFromObject(
34 $mock
35 );
36
37 return $mock;
38 }
39
40 private function buildQuery(
41 $requestOptions = null,
42 $user = null
43 ) {
44 $context = new RequestContext;
45 $context->setRequest( new FauxRequest( $requestOptions ) );
46 if ( $user ) {
47 $context->setUser( $user );
48 }
49
50 $this->changesListSpecialPage->setContext( $context );
51 $this->changesListSpecialPage->filterGroups = [];
52 $formOptions = $this->changesListSpecialPage->setup( null );
53
54 #  Filter out rc_timestamp conditions which depends on the test runtime
55 # This condition is not needed as of march 2, 2011 -- hashar
56 # @todo FIXME: Find a way to generate the correct rc_timestamp
57
58 $tables = [];
59 $fields = [];
60 $queryConditions = [];
61 $query_options = [];
62 $join_conds = [];
63
64 call_user_func_array(
65 [ $this->changesListSpecialPage, 'buildQuery' ],
66 [
67 &$tables,
68 &$fields,
69 &$queryConditions,
70 &$query_options,
71 &$join_conds,
72 $formOptions
73 ]
74 );
75
76 $queryConditions = array_filter(
77 $queryConditions,
78 'ChangesListSpecialPageTest::filterOutRcTimestampCondition'
79 );
80
81 return $queryConditions;
82 }
83
84 /** helper to test SpecialRecentchanges::buildQuery() */
85 private function assertConditions(
86 $expected,
87 $requestOptions = null,
88 $message = '',
89 $user = null
90 ) {
91 $queryConditions = $this->buildQuery( $requestOptions, $user );
92
93 $this->assertEquals(
94 self::normalizeCondition( $expected ),
95 self::normalizeCondition( $queryConditions ),
96 $message
97 );
98 }
99
100 private static function normalizeCondition( $conds ) {
101 $dbr = wfGetDB( DB_REPLICA );
102 $normalized = array_map(
103 function ( $k, $v ) use ( $dbr ) {
104 if ( is_array( $v ) ) {
105 sort( $v );
106 }
107 // (Ab)use makeList() to format only this entry
108 return $dbr->makeList( [ $k => $v ], Database::LIST_AND );
109 },
110 array_keys( $conds ),
111 $conds
112 );
113 sort( $normalized );
114 return $normalized;
115 }
116
117 /** return false if condition begins with 'rc_timestamp ' */
118 private static function filterOutRcTimestampCondition( $var ) {
119 return ( is_array( $var ) || strpos( $var, 'rc_timestamp ' ) === false );
120 }
121
122 public function testRcNsFilter() {
123 $this->assertConditions(
124 [ # expected
125 "rc_namespace = '0'",
126 ],
127 [
128 'namespace' => NS_MAIN,
129 ],
130 "rc conditions with one namespace"
131 );
132 }
133
134 public function testRcNsFilterInversion() {
135 $this->assertConditions(
136 [ # expected
137 "rc_namespace != '0'",
138 ],
139 [
140 'namespace' => NS_MAIN,
141 'invert' => 1,
142 ],
143 "rc conditions with namespace inverted"
144 );
145 }
146
147 public function testRcNsFilterMultiple() {
148 $this->assertConditions(
149 [ # expected
150 "rc_namespace IN ('1','2','3')",
151 ],
152 [
153 'namespace' => '1;2;3',
154 ],
155 "rc conditions with multiple namespaces"
156 );
157 }
158
159 public function testRcNsFilterMultipleAssociated() {
160 $this->assertConditions(
161 [ # expected
162 "rc_namespace IN ('0','1','4','5','6','7')",
163 ],
164 [
165 'namespace' => '1;4;7',
166 'associated' => 1,
167 ],
168 "rc conditions with multiple namespaces and associated"
169 );
170 }
171
172 public function testRcNsFilterAssociatedSpecial() {
173 $this->assertConditions(
174 [ # expected
175 "rc_namespace IN ('-1','0','1')",
176 ],
177 [
178 'namespace' => '1;-1',
179 'associated' => 1,
180 ],
181 "rc conditions with associated and special namespace"
182 );
183 }
184
185 public function testRcNsFilterMultipleAssociatedInvert() {
186 $this->assertConditions(
187 [ # expected
188 "rc_namespace NOT IN ('2','3','8','9')",
189 ],
190 [
191 'namespace' => '2;3;9',
192 'associated' => 1,
193 'invert' => 1
194 ],
195 "rc conditions with multiple namespaces, associated and inverted"
196 );
197 }
198
199 public function testRcNsFilterMultipleInvert() {
200 $this->assertConditions(
201 [ # expected
202 "rc_namespace NOT IN ('1','2','3')",
203 ],
204 [
205 'namespace' => '1;2;3',
206 'invert' => 1,
207 ],
208 "rc conditions with multiple namespaces inverted"
209 );
210 }
211
212 public function testRcHidemyselfFilter() {
213 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
214 $this->overrideMwServices();
215
216 $user = $this->getTestUser()->getUser();
217 $user->getActorId( wfGetDB( DB_MASTER ) );
218 $this->assertConditions(
219 [ # expected
220 "NOT((rc_actor = '{$user->getActorId()}'))",
221 ],
222 [
223 'hidemyself' => 1,
224 ],
225 "rc conditions: hidemyself=1 (logged in)",
226 $user
227 );
228
229 $user = User::newFromName( '10.11.12.13', false );
230 $id = $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 (anon)",
239 $user
240 );
241 }
242
243 public function testRcHidemyselfFilter_old() {
244 $this->setMwGlobals(
245 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
246 );
247 $this->overrideMwServices();
248
249 $user = $this->getTestUser()->getUser();
250 $user->getActorId( wfGetDB( DB_MASTER ) );
251 $this->assertConditions(
252 [ # expected
253 "NOT((rc_user = '{$user->getId()}'))",
254 ],
255 [
256 'hidemyself' => 1,
257 ],
258 "rc conditions: hidemyself=1 (logged in)",
259 $user
260 );
261
262 $user = User::newFromName( '10.11.12.13', false );
263 $id = $user->getActorId( wfGetDB( DB_MASTER ) );
264 $this->assertConditions(
265 [ # expected
266 "NOT((rc_user_text = '10.11.12.13'))",
267 ],
268 [
269 'hidemyself' => 1,
270 ],
271 "rc conditions: hidemyself=1 (anon)",
272 $user
273 );
274 }
275
276 public function testRcHidebyothersFilter() {
277 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
278 $this->overrideMwServices();
279
280 $user = $this->getTestUser()->getUser();
281 $user->getActorId( wfGetDB( DB_MASTER ) );
282 $this->assertConditions(
283 [ # expected
284 "(rc_actor = '{$user->getActorId()}')",
285 ],
286 [
287 'hidebyothers' => 1,
288 ],
289 "rc conditions: hidebyothers=1 (logged in)",
290 $user
291 );
292
293 $user = User::newFromName( '10.11.12.13', false );
294 $id = $user->getActorId( wfGetDB( DB_MASTER ) );
295 $this->assertConditions(
296 [ # expected
297 "(rc_actor = '{$user->getActorId()}')",
298 ],
299 [
300 'hidebyothers' => 1,
301 ],
302 "rc conditions: hidebyothers=1 (anon)",
303 $user
304 );
305 }
306
307 public function testRcHidebyothersFilter_old() {
308 $this->setMwGlobals(
309 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
310 );
311 $this->overrideMwServices();
312
313 $user = $this->getTestUser()->getUser();
314 $user->getActorId( wfGetDB( DB_MASTER ) );
315 $this->assertConditions(
316 [ # expected
317 "(rc_user_text = '{$user->getName()}')",
318 ],
319 [
320 'hidebyothers' => 1,
321 ],
322 "rc conditions: hidebyothers=1 (logged in)",
323 $user
324 );
325
326 $user = User::newFromName( '10.11.12.13', false );
327 $id = $user->getActorId( wfGetDB( DB_MASTER ) );
328 $this->assertConditions(
329 [ # expected
330 "(rc_user_text = '10.11.12.13')",
331 ],
332 [
333 'hidebyothers' => 1,
334 ],
335 "rc conditions: hidebyothers=1 (anon)",
336 $user
337 );
338 }
339
340 public function testRcHidepageedits() {
341 $this->assertConditions(
342 [ # expected
343 "rc_type != '0'",
344 ],
345 [
346 'hidepageedits' => 1,
347 ],
348 "rc conditions: hidepageedits=1"
349 );
350 }
351
352 public function testRcHidenewpages() {
353 $this->assertConditions(
354 [ # expected
355 "rc_type != '1'",
356 ],
357 [
358 'hidenewpages' => 1,
359 ],
360 "rc conditions: hidenewpages=1"
361 );
362 }
363
364 public function testRcHidelog() {
365 $this->assertConditions(
366 [ # expected
367 "rc_type != '3'",
368 ],
369 [
370 'hidelog' => 1,
371 ],
372 "rc conditions: hidelog=1"
373 );
374 }
375
376 public function testRcHidehumans() {
377 $this->assertConditions(
378 [ # expected
379 'rc_bot' => 1,
380 ],
381 [
382 'hidebots' => 0,
383 'hidehumans' => 1,
384 ],
385 "rc conditions: hidebots=0 hidehumans=1"
386 );
387 }
388
389 public function testRcHidepatrolledDisabledFilter() {
390 $this->setMwGlobals( 'wgUseRCPatrol', false );
391 $user = $this->getTestUser()->getUser();
392 $this->assertConditions(
393 [ # expected
394 ],
395 [
396 'hidepatrolled' => 1,
397 ],
398 "rc conditions: hidepatrolled=1 (user not allowed)",
399 $user
400 );
401 }
402
403 public function testRcHideunpatrolledDisabledFilter() {
404 $this->setMwGlobals( 'wgUseRCPatrol', false );
405 $user = $this->getTestUser()->getUser();
406 $this->assertConditions(
407 [ # expected
408 ],
409 [
410 'hideunpatrolled' => 1,
411 ],
412 "rc conditions: hideunpatrolled=1 (user not allowed)",
413 $user
414 );
415 }
416
417 public function testRcHidepatrolledFilter() {
418 $user = $this->getTestSysop()->getUser();
419 $this->assertConditions(
420 [ # expected
421 'rc_patrolled' => 0,
422 ],
423 [
424 'hidepatrolled' => 1,
425 ],
426 "rc conditions: hidepatrolled=1",
427 $user
428 );
429 }
430
431 public function testRcHideunpatrolledFilter() {
432 $user = $this->getTestSysop()->getUser();
433 $this->assertConditions(
434 [ # expected
435 'rc_patrolled' => [ 1, 2 ],
436 ],
437 [
438 'hideunpatrolled' => 1,
439 ],
440 "rc conditions: hideunpatrolled=1",
441 $user
442 );
443 }
444
445 public function testRcReviewStatusFilter() {
446 $user = $this->getTestSysop()->getUser();
447 $this->assertConditions(
448 [ #expected
449 'rc_patrolled' => 1,
450 ],
451 [
452 'reviewStatus' => 'manual'
453 ],
454 "rc conditions: reviewStatus=manual",
455 $user
456 );
457 $this->assertConditions(
458 [ #expected
459 'rc_patrolled' => [ 0, 2 ],
460 ],
461 [
462 'reviewStatus' => 'unpatrolled;auto'
463 ],
464 "rc conditions: reviewStatus=unpatrolled;auto",
465 $user
466 );
467 }
468
469 public function testRcHideminorFilter() {
470 $this->assertConditions(
471 [ # expected
472 "rc_minor = 0",
473 ],
474 [
475 'hideminor' => 1,
476 ],
477 "rc conditions: hideminor=1"
478 );
479 }
480
481 public function testRcHidemajorFilter() {
482 $this->assertConditions(
483 [ # expected
484 "rc_minor = 1",
485 ],
486 [
487 'hidemajor' => 1,
488 ],
489 "rc conditions: hidemajor=1"
490 );
491 }
492
493 public function testHideCategorization() {
494 $this->assertConditions(
495 [
496 # expected
497 "rc_type != '6'"
498 ],
499 [
500 'hidecategorization' => 1
501 ],
502 "rc conditions: hidecategorization=1"
503 );
504 }
505
506 public function testFilterUserExpLevelAll() {
507 $this->assertConditions(
508 [
509 # expected
510 ],
511 [
512 'userExpLevel' => 'registered;unregistered;newcomer;learner;experienced',
513 ],
514 "rc conditions: userExpLevel=registered;unregistered;newcomer;learner;experienced"
515 );
516 }
517
518 public function testFilterUserExpLevelRegisteredUnregistered() {
519 $this->assertConditions(
520 [
521 # expected
522 ],
523 [
524 'userExpLevel' => 'registered;unregistered',
525 ],
526 "rc conditions: userExpLevel=registered;unregistered"
527 );
528 }
529
530 public function testFilterUserExpLevelRegisteredUnregisteredLearner() {
531 $this->assertConditions(
532 [
533 # expected
534 ],
535 [
536 'userExpLevel' => 'registered;unregistered;learner',
537 ],
538 "rc conditions: userExpLevel=registered;unregistered;learner"
539 );
540 }
541
542 public function testFilterUserExpLevelAllExperienceLevels() {
543 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
544 $this->overrideMwServices();
545
546 $this->assertConditions(
547 [
548 # expected
549 'actor_rc_user.actor_user IS NOT NULL',
550 ],
551 [
552 'userExpLevel' => 'newcomer;learner;experienced',
553 ],
554 "rc conditions: userExpLevel=newcomer;learner;experienced"
555 );
556 }
557
558 public function testFilterUserExpLevelAllExperienceLevels_old() {
559 $this->setMwGlobals(
560 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
561 );
562 $this->overrideMwServices();
563
564 $this->assertConditions(
565 [
566 # expected
567 'rc_user != 0',
568 ],
569 [
570 'userExpLevel' => 'newcomer;learner;experienced',
571 ],
572 "rc conditions: userExpLevel=newcomer;learner;experienced"
573 );
574 }
575
576 public function testFilterUserExpLevelRegistrered() {
577 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
578 $this->overrideMwServices();
579
580 $this->assertConditions(
581 [
582 # expected
583 'actor_rc_user.actor_user IS NOT NULL',
584 ],
585 [
586 'userExpLevel' => 'registered',
587 ],
588 "rc conditions: userExpLevel=registered"
589 );
590 }
591
592 public function testFilterUserExpLevelRegistrered_old() {
593 $this->setMwGlobals(
594 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
595 );
596 $this->overrideMwServices();
597
598 $this->assertConditions(
599 [
600 # expected
601 'rc_user != 0',
602 ],
603 [
604 'userExpLevel' => 'registered',
605 ],
606 "rc conditions: userExpLevel=registered"
607 );
608 }
609
610 public function testFilterUserExpLevelUnregistrered() {
611 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
612 $this->overrideMwServices();
613
614 $this->assertConditions(
615 [
616 # expected
617 'actor_rc_user.actor_user IS NULL',
618 ],
619 [
620 'userExpLevel' => 'unregistered',
621 ],
622 "rc conditions: userExpLevel=unregistered"
623 );
624 }
625
626 public function testFilterUserExpLevelUnregistrered_old() {
627 $this->setMwGlobals(
628 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
629 );
630 $this->overrideMwServices();
631
632 $this->assertConditions(
633 [
634 # expected
635 'rc_user = 0',
636 ],
637 [
638 'userExpLevel' => 'unregistered',
639 ],
640 "rc conditions: userExpLevel=unregistered"
641 );
642 }
643
644 public function testFilterUserExpLevelRegistreredOrLearner() {
645 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
646 $this->overrideMwServices();
647
648 $this->assertConditions(
649 [
650 # expected
651 'actor_rc_user.actor_user IS NOT NULL',
652 ],
653 [
654 'userExpLevel' => 'registered;learner',
655 ],
656 "rc conditions: userExpLevel=registered;learner"
657 );
658 }
659
660 public function testFilterUserExpLevelRegistreredOrLearner_old() {
661 $this->setMwGlobals(
662 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
663 );
664 $this->overrideMwServices();
665
666 $this->assertConditions(
667 [
668 # expected
669 'rc_user != 0',
670 ],
671 [
672 'userExpLevel' => 'registered;learner',
673 ],
674 "rc conditions: userExpLevel=registered;learner"
675 );
676 }
677
678 public function testFilterUserExpLevelUnregistreredOrExperienced() {
679 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
680 $this->overrideMwServices();
681
682 $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
683
684 $this->assertRegExp(
685 '/\(actor_rc_user\.actor_user IS NULL\) OR '
686 . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
687 reset( $conds ),
688 "rc conditions: userExpLevel=unregistered;experienced"
689 );
690 }
691
692 public function testFilterUserExpLevelUnregistreredOrExperienced_old() {
693 $this->setMwGlobals(
694 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
695 );
696 $this->overrideMwServices();
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 }