3 namespace MediaWiki\Tests\SpecialPage
;
5 use ChangesListBooleanFilterGroup
;
6 use ChangesListStringOptionsFilterGroup
;
7 use MediaWiki\Context\RequestContext
;
8 use MediaWiki\MainConfigNames
;
9 use MediaWiki\Request\FauxRequest
;
10 use MediaWiki\SpecialPage\ChangesListSpecialPage
;
11 use MediaWiki\Tests\User\TempUser\TempUserTestTrait
;
12 use MediaWiki\Title\Title
;
13 use MediaWiki\User\User
;
14 use Wikimedia\Rdbms\Database
;
15 use Wikimedia\Rdbms\IExpression
;
16 use Wikimedia\TestingAccessWrapper
;
17 use Wikimedia\Timestamp\ConvertibleTimestamp
;
20 * Test class for ChangesListSpecialPage class
22 * Copyright © 2011-, Antoine Musso, Stephane Bisson, Matthew Flaschen
24 * @author Antoine Musso
25 * @author Stephane Bisson
26 * @author Matthew Flaschen
29 * @covers \MediaWiki\SpecialPage\ChangesListSpecialPage
31 class ChangesListSpecialPageTest
extends AbstractChangesListSpecialPageTestCase
{
33 use TempUserTestTrait
{
34 enableAutoCreateTempUser
as _enableAutoCreateTempUser
;
35 disableAutoCreateTempUser
as _disableAutoCreateTempUser
;
38 protected function setUp(): void
{
39 $this->overrideConfigValue(
40 MainConfigNames
::GroupPermissions
,
41 [ '*' => [ 'edit' => true ] ]
48 * @return ChangesListSpecialPage
50 protected function getPageAccessWrapper() {
51 $mock = $this->getMockBuilder( ChangesListSpecialPage
::class )
54 'ChangesListSpecialPage',
56 $this->getServiceContainer()->getUserIdentityUtils(),
57 $this->getServiceContainer()->getTempUserConfig()
60 ->onlyMethods( [ 'getPageTitle' ] )
61 ->getMockForAbstractClass();
63 $mock->method( 'getPageTitle' )->willReturn(
64 Title
::makeTitle( NS_SPECIAL
, 'ChangesListSpecialPage' )
67 $mock = TestingAccessWrapper
::newFromObject(
74 private function buildQuery(
75 array $requestOptions,
78 $context = new RequestContext
;
79 $context->setRequest( new FauxRequest( $requestOptions ) );
81 $context->setUser( $user );
84 $this->changesListSpecialPage
->setContext( $context );
85 $formOptions = $this->changesListSpecialPage
->setup( null );
87 # Filter out rc_timestamp conditions which depends on the test runtime
88 # This condition is not needed as of march 2, 2011 -- hashar
89 # @todo FIXME: Find a way to generate the correct rc_timestamp
93 $queryConditions = [];
98 [ $this->changesListSpecialPage
, 'buildQuery' ],
109 $queryConditions = array_filter(
111 [ __CLASS__
, 'filterOutRcTimestampCondition' ]
114 return $queryConditions;
118 * helper to test SpecialRecentchanges::buildQuery()
119 * @param array $expected
120 * @param array $requestOptions
121 * @param string $message
122 * @param User|null $user
124 private function assertConditions(
126 array $requestOptions,
130 $queryConditions = $this->buildQuery( $requestOptions, $user );
133 $this->normalizeCondition( $expected ),
134 $this->normalizeCondition( $queryConditions ),
139 private function normalizeCondition( array $conds ): array {
140 $dbr = $this->getDb();
141 $normalized = array_map(
142 static function ( $k, $v ) use ( $dbr ) {
143 if ( is_array( $v ) ) {
146 // (Ab)use makeList() to format only this entry
147 return $dbr->makeList( [ $k => $v ], Database
::LIST_AND
);
149 array_keys( $conds ),
157 * @param array|string|IExpression $var
158 * @return bool false if condition begins with 'rc_timestamp '
160 private static function filterOutRcTimestampCondition( $var ): bool {
161 if ( $var instanceof IExpression
) {
162 $var = $var->toGeneralizedSql();
164 return is_array( $var ) ||
!str_contains( (string)$var, 'rc_timestamp ' );
167 public function testRcNsFilter() {
168 $this->assertConditions(
173 'namespace' => NS_MAIN
,
175 "rc conditions with one namespace"
179 public function testRcNsFilterInversion() {
180 $this->assertConditions(
185 'namespace' => NS_MAIN
,
188 "rc conditions with namespace inverted"
192 public function testRcNsFilterMultiple() {
193 $this->assertConditions(
195 'rc_namespace IN (1,2,3)',
198 'namespace' => '1;2;3',
200 "rc conditions with multiple namespaces"
204 public function testRcNsFilterMultipleAssociated() {
205 $this->assertConditions(
207 'rc_namespace IN (0,1,4,5,6,7)',
210 'namespace' => '1;4;7',
213 "rc conditions with multiple namespaces and associated"
217 public function testRcNsFilterAssociatedSpecial() {
218 $this->assertConditions(
220 'rc_namespace IN (-1,0,1)',
223 'namespace' => '1;-1',
226 "rc conditions with associated and special namespace"
230 public function testRcNsFilterMultipleAssociatedInvert() {
231 $this->assertConditions(
233 'rc_namespace NOT IN (2,3,8,9)',
236 'namespace' => '2;3;9',
240 "rc conditions with multiple namespaces, associated and inverted"
244 public function testRcNsFilterMultipleInvert() {
245 $this->assertConditions(
247 'rc_namespace NOT IN (1,2,3)',
250 'namespace' => '1;2;3',
253 "rc conditions with multiple namespaces inverted"
257 public function testRcNsFilterAllContents() {
258 $namespaces = $this->getServiceContainer()->getNamespaceInfo()->getSubjectNamespaces();
259 $this->assertConditions(
261 'rc_namespace IN (' . $this->getDb()->makeList( $namespaces ) . ')',
264 'namespace' => 'all-contents',
266 "rc conditions with all-contents"
270 public function testRcNsFilterInvalid() {
271 $this->assertConditions(
275 'namespace' => 'invalid',
277 "rc conditions with invalid namespace"
281 public function testRcNsFilterPartialInvalid() {
282 $namespaces = array_merge(
284 $this->getServiceContainer()->getNamespaceInfo()->getSubjectNamespaces()
287 $this->assertConditions(
289 'rc_namespace IN (' . $this->getDb()->makeList( $namespaces ) . ')',
292 'namespace' => 'all-contents;1;invalid',
294 "rc conditions with invalid namespace"
298 public function testRcHidemyselfFilter() {
299 $user = $this->getTestUser()->getUser();
300 $this->assertConditions(
302 $this->getDb()->expr( 'actor_name', '!=', $user->getName() ),
307 "rc conditions: hidemyself=1 (logged in)",
311 $user = User
::newFromName( '10.11.12.13', false );
312 $this->assertConditions(
314 "actor_name != '10.11.12.13'",
319 "rc conditions: hidemyself=1 (anon)",
324 public function testRcHidebyothersFilter() {
325 $user = $this->getTestUser()->getUser();
326 $this->assertConditions(
328 'actor_user' => $user->getId(),
333 "rc conditions: hidebyothers=1 (logged in)",
337 $user = User
::newFromName( '10.11.12.13', false );
338 $this->assertConditions(
340 'actor_name' => '10.11.12.13',
345 "rc conditions: hidebyothers=1 (anon)",
350 public function testRcHidepageedits() {
351 $this->assertConditions(
356 'hidepageedits' => 1,
358 "rc conditions: hidepageedits=1"
362 public function testRcHidenewpages() {
363 $this->assertConditions(
370 "rc conditions: hidenewpages=1"
374 public function testRcHidelog() {
375 $this->assertConditions(
382 "rc conditions: hidelog=1"
386 public function testRcHidehumans() {
387 $this->assertConditions(
395 "rc conditions: hidebots=0 hidehumans=1"
399 public function testRcHidepatrolledDisabledFilter() {
400 $this->overrideConfigValue( MainConfigNames
::UseRCPatrol
, false );
401 $this->changesListSpecialPage
->filterGroups
= [];
402 $user = $this->getTestUser()->getUser();
403 $this->assertConditions(
407 'hidepatrolled' => 1,
409 "rc conditions: hidepatrolled=1 (user not allowed)",
414 public function testRcHideunpatrolledDisabledFilter() {
415 $this->overrideConfigValue( MainConfigNames
::UseRCPatrol
, false );
416 $this->changesListSpecialPage
->filterGroups
= [];
417 $user = $this->getTestUser()->getUser();
418 $this->assertConditions(
422 'hideunpatrolled' => 1,
424 "rc conditions: hideunpatrolled=1 (user not allowed)",
429 public function testRcHidepatrolledFilter() {
430 $user = $this->getTestSysop()->getUser();
431 $this->assertConditions(
436 'hidepatrolled' => 1,
438 "rc conditions: hidepatrolled=1",
443 public function testRcHideunpatrolledFilter() {
444 $user = $this->getTestSysop()->getUser();
445 $this->assertConditions(
447 'rc_patrolled' => [ 1, 2 ],
450 'hideunpatrolled' => 1,
452 "rc conditions: hideunpatrolled=1",
457 public function testRcReviewStatusFilter() {
458 $user = $this->getTestSysop()->getUser();
459 $this->assertConditions(
464 'reviewStatus' => 'manual'
466 "rc conditions: reviewStatus=manual",
469 $this->assertConditions(
471 'rc_patrolled' => [ 0, 2 ],
474 'reviewStatus' => 'unpatrolled;auto'
476 "rc conditions: reviewStatus=unpatrolled;auto",
481 public function testRcHideminorFilter() {
482 $this->assertConditions(
489 "rc conditions: hideminor=1"
493 public function testRcHidemajorFilter() {
494 $this->assertConditions(
501 "rc conditions: hidemajor=1"
505 public function testHideCategorization() {
506 $this->assertConditions(
512 'hidecategorization' => 1
514 "rc conditions: hidecategorization=1"
518 /** @see TempUserTestTrait::enableAutoCreateTempUser */
519 protected function enableAutoCreateTempUser( array $configOverrides = [] ): void
{
520 $this->_enableAutoCreateTempUser( $configOverrides );
521 $this->changesListSpecialPage
->setTempUserConfig( $this->getServiceContainer()->getTempUserConfig() );
524 /** @see TempUserTestTrait::disableAutoCreateTempUser */
525 protected function disableAutoCreateTempUser( array $configOverrides = [] ): void
{
526 $this->_disableAutoCreateTempUser( $configOverrides );
527 $this->changesListSpecialPage
->setTempUserConfig( $this->getServiceContainer()->getTempUserConfig() );
530 public function testRegistrationHideliu() {
531 $this->enableAutoCreateTempUser();
532 $tempUserMatchPattern = $this->getServiceContainer()->getTempUserConfig()
533 ->getMatchCondition( $this->getDb(), 'actor_name', IExpression
::LIKE
)
534 ->toSql( $this->getDb() );
535 $this->assertConditions(
537 "((actor_user IS NULL OR $tempUserMatchPattern))",
542 "rc conditions: hideliu=1"
546 public function testRegistrationHideanons() {
547 $this->enableAutoCreateTempUser();
548 $tempUserMatchPattern = $this->getServiceContainer()->getTempUserConfig()
549 ->getMatchCondition( $this->getDb(), 'actor_name', IExpression
::NOT_LIKE
)
550 ->toSql( $this->getDb() );
551 $this->assertConditions(
553 "((actor_user IS NOT NULL AND $tempUserMatchPattern))",
558 "rc conditions: hideanons=1"
562 public function testFilterUserExpLevelAll() {
563 $this->assertConditions(
568 'userExpLevel' => 'registered;unregistered;newcomer;learner;experienced',
570 "rc conditions: userExpLevel=registered;unregistered;newcomer;learner;experienced"
574 public function testFilterUserExpLevelRegisteredUnregistered() {
575 $this->assertConditions(
580 'userExpLevel' => 'registered;unregistered',
582 "rc conditions: userExpLevel=registered;unregistered"
586 public function testFilterUserExpLevelRegisteredUnregisteredLearner() {
587 $this->assertConditions(
592 'userExpLevel' => 'registered;unregistered;learner',
594 "rc conditions: userExpLevel=registered;unregistered;learner"
598 public function testFilterUserExpLevelAllExperienceLevels() {
599 $this->disableAutoCreateTempUser();
600 $this->assertConditions(
603 '(actor_user IS NOT NULL)',
606 'userExpLevel' => 'newcomer;learner;experienced',
608 "rc conditions: userExpLevel=newcomer;learner;experienced"
612 public function testFilterUserExpLevelRegistered() {
613 $this->disableAutoCreateTempUser();
614 $this->assertConditions(
617 '(actor_user IS NOT NULL)',
620 'userExpLevel' => 'registered',
622 "rc conditions: userExpLevel=registered"
626 public function testFilterUserExpLevelRegisteredTempAccountsEnabled() {
627 $this->enableAutoCreateTempUser();
628 $tempUserMatchPattern = $this->getServiceContainer()->getTempUserConfig()
629 ->getMatchCondition( $this->getDb(), 'actor_name', IExpression
::NOT_LIKE
)
630 ->toSql( $this->getDb() );
631 $this->assertConditions(
634 "((actor_user IS NOT NULL AND $tempUserMatchPattern))",
637 'userExpLevel' => 'registered',
639 "rc conditions: userExpLevel=registered"
643 public function testFilterUserExpLevelUnregistered() {
644 $this->disableAutoCreateTempUser();
645 $this->assertConditions(
648 '(actor_user IS NULL)'
651 'userExpLevel' => 'unregistered',
653 "rc conditions: userExpLevel=unregistered"
657 public function testFilterUserExpLevelUnregisteredTempAccountsEnabled() {
658 $this->enableAutoCreateTempUser();
659 $tempUserMatchPattern = $this->getServiceContainer()->getTempUserConfig()
660 ->getMatchCondition( $this->getDb(), 'actor_name', IExpression
::LIKE
)
661 ->toSql( $this->getDb() );
662 $this->assertConditions(
665 "((actor_user IS NULL OR $tempUserMatchPattern))",
668 'userExpLevel' => 'unregistered',
670 "rc conditions: userExpLevel=unregistered"
674 public function testFilterUserExpLevelRegisteredOrLearner() {
675 $this->disableAutoCreateTempUser();
676 $this->assertConditions(
679 '(actor_user IS NOT NULL)',
682 'userExpLevel' => 'registered;learner',
684 "rc conditions: userExpLevel=registered;learner"
688 public function testFilterUserExpLevelLearner() {
689 $this->disableAutoCreateTempUser();
690 ConvertibleTimestamp
::setFakeTime( '20201231000000' );
691 $this->assertConditions(
694 "((actor_user IS NOT NULL AND "
695 . "(user_editcount >= 10 AND (user_registration IS NULL OR user_registration <= '{$this->getDb()->timestamp( '20201227000000' )}')) AND "
696 . "(user_editcount < 500 OR user_registration > '{$this->getDb()->timestamp( '20201201000000' )}')"
700 'userExpLevel' => 'learner'
702 "rc conditions: userExpLevel=learner"
706 public function testFilterUserExpLevelLearnerWhenTemporaryAccountsEnabled() {
707 $this->enableAutoCreateTempUser();
708 ConvertibleTimestamp
::setFakeTime( '20201231000000' );
710 $notLikeTempUserMatchExpression = $this->getServiceContainer()->getTempUserConfig()
711 ->getMatchCondition( $this->getDb(), 'actor_name', IExpression
::NOT_LIKE
)
712 ->toSql( $this->getDb() );
714 $this->assertConditions(
717 "(((actor_user IS NOT NULL AND $notLikeTempUserMatchExpression) AND "
718 . "(user_editcount >= 10 AND (user_registration IS NULL OR user_registration <= '{$this->getDb()->timestamp( '20201227000000' )}')) AND "
719 . "(user_editcount < 500 OR user_registration > '{$this->getDb()->timestamp( '20201201000000' )}')"
723 'userExpLevel' => 'learner'
725 "rc conditions: userExpLevel=learner"
729 public function testFilterUserExpLevelUnregisteredOrExperienced() {
730 $this->disableAutoCreateTempUser();
731 ConvertibleTimestamp
::setFakeTime( '20201231000000' );
732 $this->assertConditions(
735 "(actor_user IS NULL OR "
736 . "(actor_user IS NOT NULL AND "
737 . "(user_editcount >= 500 AND (user_registration IS NULL OR user_registration <= '{$this->getDb()->timestamp( '20201201000000' )}'))"
741 'userExpLevel' => 'unregistered;experienced'
743 "rc conditions: userExpLevel=unregistered;experienced"
747 public function testFilterUserExpLevelUnregisteredOrExperiencedWhenTemporaryAccountsEnabled() {
748 $this->enableAutoCreateTempUser();
749 ConvertibleTimestamp
::setFakeTime( '20201231000000' );
751 $notLikeTempUserMatchExpression = $this->getServiceContainer()->getTempUserConfig()
752 ->getMatchCondition( $this->getDb(), 'actor_name', IExpression
::NOT_LIKE
)
753 ->toSql( $this->getDb() );
754 $likeTempUserMatchExpression = $this->getServiceContainer()->getTempUserConfig()
755 ->getMatchCondition( $this->getDb(), 'actor_name', IExpression
::LIKE
)
756 ->toSql( $this->getDb() );
758 $this->assertConditions(
761 "((actor_user IS NULL OR $likeTempUserMatchExpression) OR "
762 . "((actor_user IS NOT NULL AND $notLikeTempUserMatchExpression) AND "
763 . "(user_editcount >= 500 AND (user_registration IS NULL OR user_registration <= '{$this->getDb()->timestamp( '20201201000000' )}'))"
767 'userExpLevel' => 'unregistered;experienced'
769 "rc conditions: userExpLevel=unregistered;experienced"
773 public function testFilterUserExpLevelRegistrationRequiredToEditRemovesRegistrationFilters() {
774 $this->overrideConfigValue(
775 MainConfigNames
::GroupPermissions
,
776 [ '*' => [ 'edit' => false ] ]
779 $this->assertCount( 3, $this->changesListSpecialPage
->filterGroupDefinitions
[1]['filters'] );
781 $actualFilterGroupDefinitions = [];
782 foreach ( $this->changesListSpecialPage
->filterGroupDefinitions
[1]['filters'] as $key => $value ) {
783 if ( $value['name'] ) {
784 array_push( $actualFilterGroupDefinitions, $value['name'] );
787 $this->assertSame( [ "newcomer", "learner", "experienced" ], $actualFilterGroupDefinitions );
790 public function testFilterUserExpLevelRegistrationNotRequiredToEditDoesNotRemoveRegistrationFilters() {
791 $this->assertCount( 5, $this->changesListSpecialPage
->filterGroupDefinitions
[1]['filters'] );
793 $actualFilterGroupDefinitions = [];
794 foreach ( $this->changesListSpecialPage
->filterGroupDefinitions
[1]['filters'] as $key => $value ) {
795 if ( $value['name'] ) {
796 array_push( $actualFilterGroupDefinitions, $value['name'] );
799 $this->assertSame( [ "unregistered", "registered", "newcomer", "learner", "experienced" ], $actualFilterGroupDefinitions );
802 public function testFilterUserExpLevel() {
804 $this->overrideConfigValues( [
805 MainConfigNames
::LearnerEdits
=> 10,
806 MainConfigNames
::LearnerMemberSince
=> 4,
807 MainConfigNames
::ExperiencedUserEdits
=> 500,
808 MainConfigNames
::ExperiencedUserMemberSince
=> 30,
811 $this->createUsers( [
812 'Newcomer1' => [ 'edits' => 2, 'days' => 2 ],
813 'Newcomer2' => [ 'edits' => 12, 'days' => 3 ],
814 'Newcomer3' => [ 'edits' => 8, 'days' => 5 ],
815 'Learner1' => [ 'edits' => 15, 'days' => 10 ],
816 'Learner2' => [ 'edits' => 450, 'days' => 20 ],
817 'Learner3' => [ 'edits' => 460, 'days' => 33 ],
818 'Learner4' => [ 'edits' => 525, 'days' => 28 ],
819 'Experienced1' => [ 'edits' => 538, 'days' => 33 ],
823 $this->assertArrayEquals(
824 [ 'Newcomer1', 'Newcomer2', 'Newcomer3' ],
825 $this->fetchUsers( [ 'newcomer' ], $now )
828 // newcomers and learner
829 $this->assertArrayEquals(
831 'Newcomer1', 'Newcomer2', 'Newcomer3',
832 'Learner1', 'Learner2', 'Learner3', 'Learner4',
834 $this->fetchUsers( [ 'newcomer', 'learner' ], $now )
837 // newcomers and more learner
838 $this->assertArrayEquals(
840 'Newcomer1', 'Newcomer2', 'Newcomer3',
843 $this->fetchUsers( [ 'newcomer', 'experienced' ], $now )
847 $this->assertArrayEquals(
848 [ 'Learner1', 'Learner2', 'Learner3', 'Learner4' ],
849 $this->fetchUsers( [ 'learner' ], $now )
852 // more experienced only
853 $this->assertArrayEquals(
855 $this->fetchUsers( [ 'experienced' ], $now )
858 // learner and more experienced
859 $this->assertArrayEquals(
861 'Learner1', 'Learner2', 'Learner3', 'Learner4',
864 $this->fetchUsers( [ 'learner', 'experienced' ], $now )
868 private function createUsers( array $specs, int $now ) {
869 $dbw = $this->getDb();
870 foreach ( $specs as $name => $spec ) {
874 'editcount' => $spec['edits'],
875 'registration' => $dbw->timestamp( $this->daysAgo( $spec['days'], $now ) ),
882 private function fetchUsers( array $filters, int $now ): array {
891 call_user_func_array(
892 [ $this->changesListSpecialPage
, 'filterOnUserExperienceLevel' ],
894 get_class( $this->changesListSpecialPage
),
895 $this->changesListSpecialPage
->getContext(),
896 $this->changesListSpecialPage
->getDB(),
907 // @todo: This is not at all safe or sensible. It just blindly assumes
908 // nothing in $conds depends on any other tables.
909 $result = $this->getDb()->newSelectQueryBuilder()
910 ->select( 'user_name' )
912 ->leftJoin( 'actor', null, 'actor_user=user_id' )
914 ->andWhere( [ 'user_email' => 'ut' ] )
918 foreach ( $result as $row ) {
919 $usernames[] = $row->user_name
;
925 private function daysAgo( int $days, int $now ): int {
926 $secondsPerDay = 86400;
927 return $now - $days * $secondsPerDay;
930 public function testGetStructuredFilterJsData() {
931 $this->changesListSpecialPage
->filterGroups
= [];
935 'name' => 'gub-group',
936 'title' => 'gub-group-title',
937 'class' => ChangesListBooleanFilterGroup
::class,
941 'label' => 'foo-label',
942 'description' => 'foo-description',
944 'showHide' => 'showhidefoo',
949 'label' => 'bar-label',
950 'description' => 'bar-description',
958 'name' => 'des-group',
959 'title' => 'des-group-title',
960 'class' => ChangesListStringOptionsFilterGroup
::class,
961 'isFullCoverage' => true,
965 'label' => 'grault-label',
966 'description' => 'grault-description',
970 'label' => 'garply-label',
971 'description' => 'garply-description',
974 'queryCallable' => static function () {
976 'default' => ChangesListStringOptionsFilterGroup
::NONE
,
980 'name' => 'unstructured',
981 'class' => ChangesListBooleanFilterGroup
::class,
984 'name' => 'hidethud',
985 'showHide' => 'showhidethud',
991 'showHide' => 'showhidemos',
999 $this->changesListSpecialPage
->registerFiltersFromDefinitions( $definition );
1001 $this->assertArrayEquals(
1003 // Filters that only display in the unstructured UI are
1004 // are not included, and neither are groups that would
1005 // be empty due to the above.
1008 'name' => 'gub-group',
1009 'title' => 'gub-group-title',
1010 'type' => ChangesListBooleanFilterGroup
::TYPE
,
1014 'name' => 'hidebar',
1015 'label' => 'bar-label',
1016 'description' => 'bar-description',
1022 'defaultHighlightColor' => null
1025 'name' => 'hidefoo',
1026 'label' => 'foo-label',
1027 'description' => 'foo-description',
1033 'defaultHighlightColor' => null
1036 'fullCoverage' => true,
1041 'name' => 'des-group',
1042 'title' => 'des-group-title',
1043 'type' => ChangesListStringOptionsFilterGroup
::TYPE
,
1045 'fullCoverage' => true,
1049 'label' => 'grault-label',
1050 'description' => 'grault-description',
1055 'defaultHighlightColor' => null
1059 'label' => 'garply-label',
1060 'description' => 'garply-description',
1065 'defaultHighlightColor' => null
1070 'default' => ChangesListStringOptionsFilterGroup
::NONE
,
1081 'grault-description',
1083 'garply-description',
1086 $this->changesListSpecialPage
->getStructuredFilterJsData(),
1087 /** ordered= */ false,
1092 public function provideParseParameters() {
1094 [ 'hidebots', [ 'hidebots' => true ] ],
1096 [ 'bots', [ 'hidebots' => false ] ],
1098 [ 'hideminor', [ 'hideminor' => true ] ],
1100 [ 'minor', [ 'hideminor' => false ] ],
1102 [ 'hidemajor', [ 'hidemajor' => true ] ],
1104 [ 'hideliu', [ 'hideliu' => true ] ],
1106 [ 'hidepatrolled', [ 'hidepatrolled' => true ] ],
1108 [ 'hideunpatrolled', [ 'hideunpatrolled' => true ] ],
1110 [ 'hideanons', [ 'hideanons' => true ] ],
1112 [ 'hidemyself', [ 'hidemyself' => true ] ],
1114 [ 'hidebyothers', [ 'hidebyothers' => true ] ],
1116 [ 'hidehumans', [ 'hidehumans' => true ] ],
1118 [ 'hidepageedits', [ 'hidepageedits' => true ] ],
1120 [ 'pagedits', [ 'hidepageedits' => false ] ],
1122 [ 'hidenewpages', [ 'hidenewpages' => true ] ],
1124 [ 'hidecategorization', [ 'hidecategorization' => true ] ],
1126 [ 'hidelog', [ 'hidelog' => true ] ],
1129 'userExpLevel=learner;experienced',
1131 'userExpLevel' => 'learner;experienced'
1135 // A few random combos
1137 'bots,hideliu,hidemyself',
1139 'hidebots' => false,
1141 'hidemyself' => true,
1146 'minor,hideanons,categorization',
1148 'hideminor' => false,
1149 'hideanons' => true,
1150 'hidecategorization' => false,
1155 'hidehumans,bots,hidecategorization',
1157 'hidehumans' => true,
1158 'hidebots' => false,
1159 'hidecategorization' => true,
1164 'hidemyself,userExpLevel=newcomer;learner,hideminor',
1166 'hidemyself' => true,
1167 'hideminor' => true,
1168 'userExpLevel' => 'newcomer;learner',
1174 public static function provideGetFilterConflicts() {
1178 "expectedConflicts" => false,
1183 "userExpLevel" => "newcomer",
1185 "expectedConflicts" => false,
1189 "hideanons" => true,
1190 "userExpLevel" => "learner",
1192 "expectedConflicts" => false,
1196 "hidemajor" => true,
1197 "hidenewpages" => true,
1198 "hidepageedits" => true,
1199 "hidecategorization" => false,
1201 "hideWikidata" => true,
1203 "expectedConflicts" => true,
1207 "hidemajor" => true,
1208 "hidenewpages" => false,
1209 "hidepageedits" => true,
1210 "hidecategorization" => false,
1212 "hideWikidata" => true,
1214 "expectedConflicts" => true,
1218 "hidemajor" => true,
1219 "hidenewpages" => false,
1220 "hidepageedits" => false,
1221 "hidecategorization" => true,
1223 "hideWikidata" => true,
1225 "expectedConflicts" => false,
1229 "hideminor" => true,
1230 "hidenewpages" => true,
1231 "hidepageedits" => true,
1232 "hidecategorization" => false,
1234 "hideWikidata" => true,
1236 "expectedConflicts" => false,
1242 * @dataProvider provideGetFilterConflicts
1244 public function testGetFilterConflicts( $parameters, $expectedConflicts ) {
1245 $context = new RequestContext
;
1246 $context->setRequest( new FauxRequest( $parameters ) );
1247 $this->changesListSpecialPage
->setContext( $context );
1249 $this->assertEquals(
1251 $this->changesListSpecialPage
->areFiltersInConflict()
1255 public function validateOptionsProvider() {
1258 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 1 ],
1260 [ 'userExpLevel' => 'unregistered', 'hidebots' => 1, ],
1264 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 0 ],
1266 [ 'hidebots' => 0, 'hidehumans' => 1 ],
1270 [ 'hideanons' => 1 ],
1272 [ 'userExpLevel' => 'registered' ],
1278 [ 'userExpLevel' => 'unregistered' ],
1282 [ 'hideanons' => 1, 'hidebots' => 1 ],
1284 [ 'userExpLevel' => 'registered', 'hidebots' => 1 ],
1288 [ 'hideliu' => 1, 'hidebots' => 0 ],
1290 [ 'userExpLevel' => 'unregistered', 'hidebots' => 0 ],
1294 [ 'hidemyself' => 1, 'hidebyothers' => 1 ],
1300 [ 'hidebots' => 1, 'hidehumans' => 1 ],
1306 [ 'hidepatrolled' => 1, 'hideunpatrolled' => 1 ],
1312 [ 'hideminor' => 1, 'hidemajor' => 1 ],
1319 [ 'hidepageedits' => 1, 'hidenewpages' => 1, 'hidecategorization' => 1, 'hidelog' => 1, 'hidenewuserlog' => 1 ],