3use Wikimedia\TestingAccessWrapper;
19 $mock = $this->getMockBuilder( ChangesListSpecialPage::class )
22 'ChangesListSpecialPage',
26 ->setMethods( [
'getPageTitle' ] )
27 ->getMockForAbstractClass();
29 $mock->method(
'getPageTitle' )->willReturn(
30 Title::makeTitle(
NS_SPECIAL,
'ChangesListSpecialPage' )
33 $mock = TestingAccessWrapper::newFromObject(
41 $requestOptions =
null,
50 $this->changesListSpecialPage->setContext(
$context );
51 $this->changesListSpecialPage->filterGroups = [];
52 $formOptions = $this->changesListSpecialPage->setup(
null );
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
60 $queryConditions = [];
65 [ $this->changesListSpecialPage,
'buildQuery' ],
76 $queryConditions = array_filter(
78 'ChangesListSpecialPageTest::filterOutRcTimestampCondition'
81 return $queryConditions;
87 $requestOptions =
null,
91 $queryConditions = $this->
buildQuery( $requestOptions, $user );
94 self::normalizeCondition( $expected ),
95 self::normalizeCondition( $queryConditions ),
102 $normalized = array_map(
103 function ( $k, $v ) use (
$dbr ) {
104 if ( is_array( $v ) ) {
108 return $dbr->makeList( [ $k => $v ], Database::LIST_AND );
110 array_keys( $conds ),
119 return ( is_array( $var ) ||
false === strpos( $var,
'rc_timestamp ' ) );
125 "rc_namespace = '0'",
130 "rc conditions with one namespace"
137 "rc_namespace != '0'",
143 "rc conditions with namespace inverted"
150 "rc_namespace IN ('1','2','3')",
153 'namespace' =>
'1;2;3',
155 "rc conditions with multiple namespaces"
162 "rc_namespace IN ('0','1','4','5','6','7')",
165 'namespace' =>
'1;4;7',
168 "rc conditions with multiple namespaces and associated"
175 "rc_namespace NOT IN ('2','3','8','9')",
178 'namespace' =>
'2;3;9',
182 "rc conditions with multiple namespaces, associated and inverted"
189 "rc_namespace NOT IN ('1','2','3')",
192 'namespace' =>
'1;2;3',
195 "rc conditions with multiple namespaces inverted"
209 "NOT((rc_user = '{$user->getId()}'))",
214 "rc conditions: hidemyself=1 (logged in)",
222 "NOT((rc_user_text = '10.11.12.13'))",
227 "rc conditions: hidemyself=1 (anon)",
242 "(rc_user_text = '{$user->getName()}')",
247 "rc conditions: hidebyothers=1 (logged in)",
255 "(rc_user_text = '10.11.12.13')",
260 "rc conditions: hidebyothers=1 (anon)",
271 'hidepageedits' => 1,
273 "rc conditions: hidepageedits=1"
285 "rc conditions: hidenewpages=1"
297 "rc conditions: hidelog=1"
310 "rc conditions: hidebots=0 hidehumans=1"
321 'hidepatrolled' => 1,
323 "rc conditions: hidepatrolled=1 (user not allowed)",
335 'hideunpatrolled' => 1,
337 "rc conditions: hideunpatrolled=1 (user not allowed)",
348 'hidepatrolled' => 1,
350 "rc conditions: hidepatrolled=1",
359 'rc_patrolled' => [ 1, 2 ],
362 'hideunpatrolled' => 1,
364 "rc conditions: hideunpatrolled=1",
376 'reviewStatus' =>
'manual'
378 "rc conditions: reviewStatus=manual",
383 'rc_patrolled' => [ 0, 2 ],
386 'reviewStatus' =>
'unpatrolled;auto'
388 "rc conditions: reviewStatus=unpatrolled;auto",
401 "rc conditions: hideminor=1"
413 "rc conditions: hidemajor=1"
424 'hidecategorization' => 1
426 "rc conditions: hidecategorization=1"
436 'userExpLevel' =>
'registered;unregistered;newcomer;learner;experienced',
438 "rc conditions: userExpLevel=registered;unregistered;newcomer;learner;experienced"
448 'userExpLevel' =>
'registered;unregistered',
450 "rc conditions: userExpLevel=registered;unregistered"
460 'userExpLevel' =>
'registered;unregistered;learner',
462 "rc conditions: userExpLevel=registered;unregistered;learner"
478 'userExpLevel' =>
'newcomer;learner;experienced',
480 "rc conditions: userExpLevel=newcomer;learner;experienced"
496 'userExpLevel' =>
'registered',
498 "rc conditions: userExpLevel=registered"
514 'userExpLevel' =>
'unregistered',
516 "rc conditions: userExpLevel=unregistered"
532 'userExpLevel' =>
'registered;learner',
534 "rc conditions: userExpLevel=registered;learner"
544 $conds = $this->
buildQuery( [
'userExpLevel' =>
'unregistered;experienced' ] );
547 '/\(rc_user = 0\) OR '
548 .
'\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
550 "rc conditions: userExpLevel=unregistered;experienced"
557 'wgLearnerEdits' => 10,
558 'wgLearnerMemberSince' => 4,
559 'wgExperiencedUserEdits' => 500,
560 'wgExperiencedUserMemberSince' => 30,
564 'Newcomer1' => [
'edits' => 2,
'days' => 2 ],
565 'Newcomer2' => [
'edits' => 12,
'days' => 3 ],
566 'Newcomer3' => [
'edits' => 8,
'days' => 5 ],
567 'Learner1' => [
'edits' => 15,
'days' => 10 ],
568 'Learner2' => [
'edits' => 450,
'days' => 20 ],
569 'Learner3' => [
'edits' => 460,
'days' => 33 ],
570 'Learner4' => [
'edits' => 525,
'days' => 28 ],
571 'Experienced1' => [
'edits' => 538,
'days' => 33 ],
576 [
'Newcomer1',
'Newcomer2',
'Newcomer3' ],
583 'Newcomer1',
'Newcomer2',
'Newcomer3',
584 'Learner1',
'Learner2',
'Learner3',
'Learner4',
586 $this->
fetchUsers( [
'newcomer',
'learner' ], $now )
592 'Newcomer1',
'Newcomer2',
'Newcomer3',
595 $this->
fetchUsers( [
'newcomer',
'experienced' ], $now )
600 [
'Learner1',
'Learner2',
'Learner3',
'Learner4' ],
613 'Learner1',
'Learner2',
'Learner3',
'Learner4',
616 $this->
fetchUsers( [
'learner',
'experienced' ], $now ),
617 'Learner and more experienced'
623 foreach ( $specs as $name => $spec ) {
627 'editcount' => $spec[
'edits'],
628 'registration' => $dbw->timestamp( $this->daysAgo( $spec[
'days'], $now ) ),
644 call_user_func_array(
645 [ $this->changesListSpecialPage,
'filterOnUserExperienceLevel' ],
647 get_class( $this->changesListSpecialPage ),
648 $this->changesListSpecialPage->getContext(),
649 $this->changesListSpecialPage->getDB(),
665 array_filter( $conds ) + [
'user_email' =>
'ut' ]
669 foreach ( $result as $row ) {
670 $usernames[] = $row->user_name;
677 $secondsPerDay = 86400;
678 return $now - $days * $secondsPerDay;
682 $this->changesListSpecialPage->filterGroups = [];
686 'name' =>
'gub-group',
687 'title' =>
'gub-group-title',
688 'class' => ChangesListBooleanFilterGroup::class,
692 'label' =>
'foo-label',
693 'description' =>
'foo-description',
695 'showHide' =>
'showhidefoo',
700 'label' =>
'bar-label',
701 'description' =>
'bar-description',
709 'name' =>
'des-group',
710 'title' =>
'des-group-title',
711 'class' => ChangesListStringOptionsFilterGroup::class,
712 'isFullCoverage' =>
true,
716 'label' =>
'grault-label',
717 'description' =>
'grault-description',
721 'label' =>
'garply-label',
722 'description' =>
'garply-description',
725 'queryCallable' =>
function () {
731 'name' =>
'unstructured',
732 'class' => ChangesListBooleanFilterGroup::class,
735 'name' =>
'hidethud',
736 'showHide' =>
'showhidethud',
742 'showHide' =>
'showhidemos',
750 $this->changesListSpecialPage->registerFiltersFromDefinitions( $definition );
759 'name' =>
'gub-group',
760 'title' =>
'gub-group-title',
766 'label' =>
'bar-label',
767 'description' =>
'bar-description',
773 'defaultHighlightColor' =>
null
777 'label' =>
'foo-label',
778 'description' =>
'foo-description',
784 'defaultHighlightColor' =>
null
787 'fullCoverage' =>
true,
792 'name' =>
'des-group',
793 'title' =>
'des-group-title',
796 'fullCoverage' =>
true,
800 'label' =>
'grault-label',
801 'description' =>
'grault-description',
806 'defaultHighlightColor' =>
null
810 'label' =>
'garply-label',
811 'description' =>
'garply-description',
816 'defaultHighlightColor' =>
null
832 'grault-description',
834 'garply-description',
837 $this->changesListSpecialPage->getStructuredFilterJsData(),
845 [
'hidebots', [
'hidebots' =>
true ] ],
847 [
'bots', [
'hidebots' =>
false ] ],
849 [
'hideminor', [
'hideminor' =>
true ] ],
851 [
'minor', [
'hideminor' =>
false ] ],
853 [
'hidemajor', [
'hidemajor' =>
true ] ],
855 [
'hideliu', [
'hideliu' =>
true ] ],
857 [
'hidepatrolled', [
'hidepatrolled' =>
true ] ],
859 [
'hideunpatrolled', [
'hideunpatrolled' =>
true ] ],
861 [
'hideanons', [
'hideanons' =>
true ] ],
863 [
'hidemyself', [
'hidemyself' =>
true ] ],
865 [
'hidebyothers', [
'hidebyothers' =>
true ] ],
867 [
'hidehumans', [
'hidehumans' =>
true ] ],
869 [
'hidepageedits', [
'hidepageedits' =>
true ] ],
871 [
'pagedits', [
'hidepageedits' =>
false ] ],
873 [
'hidenewpages', [
'hidenewpages' =>
true ] ],
875 [
'hidecategorization', [
'hidecategorization' =>
true ] ],
877 [
'hidelog', [
'hidelog' =>
true ] ],
880 'userExpLevel=learner;experienced',
882 'userExpLevel' =>
'learner;experienced'
888 'bots,hideliu,hidemyself',
892 'hidemyself' =>
true,
897 'minor,hideanons,categorization',
899 'hideminor' =>
false,
901 'hidecategorization' =>
false,
906 'hidehumans,bots,hidecategorization',
908 'hidehumans' =>
true,
910 'hidecategorization' =>
true,
915 'hidemyself,userExpLevel=newcomer;learner,hideminor',
917 'hidemyself' =>
true,
919 'userExpLevel' =>
'newcomer;learner',
929 "expectedConflicts" =>
false,
934 "userExpLevel" =>
"newcomer",
936 "expectedConflicts" =>
false,
941 "userExpLevel" =>
"learner",
943 "expectedConflicts" =>
false,
948 "hidenewpages" =>
true,
949 "hidepageedits" =>
true,
950 "hidecategorization" =>
false,
952 "hideWikidata" =>
true,
954 "expectedConflicts" =>
true,
959 "hidenewpages" =>
false,
960 "hidepageedits" =>
true,
961 "hidecategorization" =>
false,
963 "hideWikidata" =>
true,
965 "expectedConflicts" =>
true,
970 "hidenewpages" =>
false,
971 "hidepageedits" =>
false,
972 "hidecategorization" =>
true,
974 "hideWikidata" =>
true,
976 "expectedConflicts" =>
false,
981 "hidenewpages" =>
true,
982 "hidepageedits" =>
true,
983 "hidecategorization" =>
false,
985 "hideWikidata" =>
true,
987 "expectedConflicts" =>
false,
998 $this->changesListSpecialPage->setContext(
$context );
1000 $this->assertEquals(
1002 $this->changesListSpecialPage->areFiltersInConflict()
1009 [
'hideanons' => 1,
'hideliu' => 1,
'hidebots' => 1 ],
1011 [
'userExpLevel' =>
'unregistered',
'hidebots' => 1, ],
1015 [
'hideanons' => 1,
'hideliu' => 1,
'hidebots' => 0 ],
1017 [
'hidebots' => 0,
'hidehumans' => 1 ],
1021 [
'hideanons' => 1 ],
1023 [
'userExpLevel' =>
'registered' ],
1029 [
'userExpLevel' =>
'unregistered' ],
1033 [
'hideanons' => 1,
'hidebots' => 1 ],
1035 [
'userExpLevel' =>
'registered',
'hidebots' => 1 ],
1039 [
'hideliu' => 1,
'hidebots' => 0 ],
1041 [
'userExpLevel' =>
'unregistered',
'hidebots' => 0 ],
1045 [
'hidemyself' => 1,
'hidebyothers' => 1 ],
1051 [
'hidebots' => 1,
'hidehumans' => 1 ],
1057 [
'hidepatrolled' => 1,
'hideunpatrolled' => 1 ],
1063 [
'hideminor' => 1,
'hidemajor' => 1 ],
1070 [
'hidepageedits' => 1,
'hidenewpages' => 1,
'hidecategorization' => 1,
'hidelog' => 1, ],
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Abstract base class for shared logic when testing ChangesListSpecialPage and subclasses.
const TYPE
Type marker, used by JavaScript.
Test class for ChangesListSpecialPage class.
testRcHidepatrolledDisabledFilter()
testRcHidepatrolledFilter()
testFilterUserExpLevelRegisteredUnregistered()
testFilterUserExpLevelUnregistrered()
testFilterUserExpLevelAll()
validateOptionsProvider()
testRcHideunpatrolledDisabledFilter()
testRcHidebyothersFilter()
createUsers( $specs, $now)
testGetStructuredFilterJsData()
testFilterUserExpLevelAllExperienceLevels()
testRcNsFilterMultipleAssociated()
fetchUsers( $filters, $now)
testRcNsFilterMultipleInvert()
testRcHideunpatrolledFilter()
static normalizeCondition( $conds)
provideGetFilterConflicts()
testGetFilterConflicts( $parameters, $expectedConflicts)
provideGetFilterConflicts
static filterOutRcTimestampCondition( $var)
return false if condition begins with 'rc_timestamp '
testFilterUserExpLevelRegistrered()
buildQuery( $requestOptions=null, $user=null)
testFilterUserExpLevelRegistreredOrLearner()
testRcNsFilterInversion()
testFilterUserExpLevelRegisteredUnregisteredLearner()
testRcNsFilterMultipleAssociatedInvert()
testFilterUserExpLevelUnregistreredOrExperienced()
testRcReviewStatusFilter()
assertConditions( $expected, $requestOptions=null, $message='', $user=null)
helper to test SpecialRecentchanges::buildQuery()
const TYPE
Type marker, used by JavaScript.
const NONE
Signifies that no options in the group are selected, meaning the group has no effect.
WebRequest clone which takes values from a provided array.
Group all the pieces relevant to the context of a request into one instance.
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
static createNew( $name, $params=[])
Add a user to the database, return the user object.
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
this hook is for auditing only RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist & $tables
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
processing should stop and the error should be shown to the user * false
const SCHEMA_COMPAT_WRITE_BOTH
const SCHEMA_COMPAT_READ_OLD