3 use Wikimedia\TestingAccessWrapper
;
6 * Test class for ChangesListSpecialPage class
8 * Copyright © 2011-, Antoine Musso, Stephane Bisson, Matthew Flaschen
10 * @author Antoine Musso
11 * @author Stephane Bisson
12 * @author Matthew Flaschen
15 * @covers ChangesListSpecialPage
17 class ChangesListSpecialPageTest
extends AbstractChangesListSpecialPageTestCase
{
18 protected function setUp() {
22 $this->changesListSpecialPage
= $this->getPage();
25 protected function getPage() {
26 return TestingAccessWrapper
::newFromObject(
27 $this->getMockForAbstractClass(
28 'ChangesListSpecialPage',
30 'ChangesListSpecialPage',
37 /** helper to test SpecialRecentchanges::buildMainQueryConds() */
38 private function assertConditions(
40 $requestOptions = null,
44 $context = new RequestContext
;
45 $context->setRequest( new FauxRequest( $requestOptions ) );
47 $context->setUser( $user );
50 $this->changesListSpecialPage
->setContext( $context );
51 $formOptions = $this->changesListSpecialPage
->setup( null );
53 # Filter out rc_timestamp conditions which depends on the test runtime
54 # This condition is not needed as of march 2, 2011 -- hashar
55 # @todo FIXME: Find a way to generate the correct rc_timestamp
59 $queryConditions = [];
64 [ $this->changesListSpecialPage
, 'buildQuery' ],
75 $queryConditions = array_filter(
77 'ChangesListSpecialPageTest::filterOutRcTimestampCondition'
81 self
::normalizeCondition( $expected ),
82 self
::normalizeCondition( $queryConditions ),
87 private static function normalizeCondition( $conds ) {
88 $normalized = array_map(
90 return is_numeric( $k ) ?
$v : "$k = $v";
99 /** return false if condition begin with 'rc_timestamp ' */
100 private static function filterOutRcTimestampCondition( $var ) {
101 return ( false === strpos( $var, 'rc_timestamp ' ) );
104 public function testRcNsFilter() {
105 $this->assertConditions(
107 "rc_namespace = '0'",
110 'namespace' => NS_MAIN
,
112 "rc conditions with one namespace"
116 public function testRcNsFilterInversion() {
117 $this->assertConditions(
119 "rc_namespace != '0'",
122 'namespace' => NS_MAIN
,
125 "rc conditions with namespace inverted"
129 public function testRcNsFilterMultiple() {
130 $this->assertConditions(
132 "rc_namespace IN ('1','2','3')",
135 'namespace' => '1,2,3',
137 "rc conditions with multiple namespaces"
141 public function testRcNsFilterMultipleAssociated() {
142 $this->assertConditions(
144 "rc_namespace IN ('0','1','4','5','6','7')",
147 'namespace' => '1,4,7',
150 "rc conditions with multiple namespaces and associated"
154 public function testRcNsFilterMultipleAssociatedInvert() {
155 $this->assertConditions(
157 "rc_namespace NOT IN ('2','3','8','9')",
160 'namespace' => '2,3,9',
164 "rc conditions with multiple namespaces, associated and inverted"
168 public function testRcNsFilterMultipleInvert() {
169 $this->assertConditions(
171 "rc_namespace NOT IN ('1','2','3')",
174 'namespace' => '1,2,3',
177 "rc conditions with multiple namespaces inverted"
181 public function testRcHidemyselfFilter() {
182 $user = $this->getTestUser()->getUser();
183 $this->assertConditions(
185 "rc_user_text != '{$user->getName()}'",
190 "rc conditions: hidemyself=1 (logged in)",
194 $user = User
::newFromName( '10.11.12.13', false );
195 $this->assertConditions(
197 "rc_user_text != '10.11.12.13'",
202 "rc conditions: hidemyself=1 (anon)",
207 public function testRcHidebyothersFilter() {
208 $user = $this->getTestUser()->getUser();
209 $this->assertConditions(
211 "rc_user_text = '{$user->getName()}'",
216 "rc conditions: hidebyothers=1 (logged in)",
220 $user = User
::newFromName( '10.11.12.13', false );
221 $this->assertConditions(
223 "rc_user_text = '10.11.12.13'",
228 "rc conditions: hidebyothers=1 (anon)",
233 public function testRcHidemyselfHidebyothersFilter() {
234 $user = $this->getTestUser()->getUser();
235 $this->assertConditions(
237 "rc_user_text != '{$user->getName()}'",
238 "rc_user_text = '{$user->getName()}'",
244 "rc conditions: hidemyself=1 hidebyothers=1 (logged in)",
249 public function testRcHidepageedits() {
250 $this->assertConditions(
255 'hidepageedits' => 1,
257 "rc conditions: hidepageedits=1"
261 public function testRcHidenewpages() {
262 $this->assertConditions(
269 "rc conditions: hidenewpages=1"
273 public function testRcHidelog() {
274 $this->assertConditions(
281 "rc conditions: hidelog=1"
285 public function testRcHidehumans() {
286 $this->assertConditions(
294 "rc conditions: hidebots=0 hidehumans=1"
298 public function testRcHidepatrolledDisabledFilter() {
299 $user = $this->getTestUser()->getUser();
300 $this->assertConditions(
304 'hidepatrolled' => 1,
306 "rc conditions: hidepatrolled=1 (user not allowed)",
311 public function testRcHideunpatrolledDisabledFilter() {
312 $user = $this->getTestUser()->getUser();
313 $this->assertConditions(
317 'hideunpatrolled' => 1,
319 "rc conditions: hideunpatrolled=1 (user not allowed)",
323 public function testRcHidepatrolledFilter() {
324 $user = $this->getTestSysop()->getUser();
325 $this->assertConditions(
330 'hidepatrolled' => 1,
332 "rc conditions: hidepatrolled=1",
337 public function testRcHideunpatrolledFilter() {
338 $user = $this->getTestSysop()->getUser();
339 $this->assertConditions(
344 'hideunpatrolled' => 1,
346 "rc conditions: hideunpatrolled=1",
351 public function testRcHideminorFilter() {
352 $this->assertConditions(
359 "rc conditions: hideminor=1"
363 public function testRcHidemajorFilter() {
364 $this->assertConditions(
371 "rc conditions: hidemajor=1"
375 public function testRcHidepatrolledHideunpatrolledFilter() {
376 $user = $this->getTestSysop()->getUser();
377 $this->assertConditions(
383 'hidepatrolled' => 1,
384 'hideunpatrolled' => 1,
386 "rc conditions: hidepatrolled=1 hideunpatrolled=1",
391 public function testHideCategorization() {
392 $this->assertConditions(
398 'hidecategorization' => 1
400 "rc conditions: hidecategorization=1"
404 public function testFilterUserExpLevel() {
405 $this->setMwGlobals( [
406 'wgLearnerEdits' => 10,
407 'wgLearnerMemberSince' => 4,
408 'wgExperiencedUserEdits' => 500,
409 'wgExperiencedUserMemberSince' => 30,
412 $this->createUsers( [
413 'Newcomer1' => [ 'edits' => 2, 'days' => 2 ],
414 'Newcomer2' => [ 'edits' => 12, 'days' => 3 ],
415 'Newcomer3' => [ 'edits' => 8, 'days' => 5 ],
416 'Learner1' => [ 'edits' => 15, 'days' => 10 ],
417 'Learner2' => [ 'edits' => 450, 'days' => 20 ],
418 'Learner3' => [ 'edits' => 460, 'days' => 33 ],
419 'Learner4' => [ 'edits' => 525, 'days' => 28 ],
420 'Experienced1' => [ 'edits' => 538, 'days' => 33 ],
424 $this->assertArrayEquals(
425 [ 'Newcomer1', 'Newcomer2', 'Newcomer3' ],
426 $this->fetchUsers( [ 'newcomer' ] )
429 // newcomers and learner
430 $this->assertArrayEquals(
432 'Newcomer1', 'Newcomer2', 'Newcomer3',
433 'Learner1', 'Learner2', 'Learner3', 'Learner4',
435 $this->fetchUsers( [ 'newcomer', 'learner' ] )
438 // newcomers and more learner
439 $this->assertArrayEquals(
441 'Newcomer1', 'Newcomer2', 'Newcomer3',
444 $this->fetchUsers( [ 'newcomer', 'experienced' ] )
448 $this->assertArrayEquals(
449 [ 'Learner1', 'Learner2', 'Learner3', 'Learner4' ],
450 $this->fetchUsers( [ 'learner' ] )
453 // more experienced only
454 $this->assertArrayEquals(
456 $this->fetchUsers( [ 'experienced' ] )
459 // learner and more experienced
460 $this->assertArrayEquals(
462 'Learner1', 'Learner2', 'Learner3', 'Learner4',
465 $this->fetchUsers( [ 'learner', 'experienced' ] ),
466 'Learner and more experienced'
469 // newcomers, learner, and more experienced
470 // TOOD: Fix test. This needs to test that anons are excluded,
471 // and right now the join fails.
472 /* $this->assertArrayEquals( */
474 /* 'Newcomer1', 'Newcomer2', 'Newcomer3', */
475 /* 'Learner1', 'Learner2', 'Learner3', 'Learner4', */
476 /* 'Experienced1', */
478 /* $this->fetchUsers( [ 'newcomer', 'learner', 'experienced' ] ) */
482 private function createUsers( $specs ) {
483 $dbw = wfGetDB( DB_MASTER
);
484 foreach ( $specs as $name => $spec ) {
488 'editcount' => $spec['edits'],
489 'registration' => $dbw->timestamp( $this->daysAgo( $spec['days'] ) ),
496 private function fetchUsers( $filters ) {
505 call_user_func_array(
506 [ $this->changesListSpecialPage
, 'filterOnUserExperienceLevel' ],
508 get_class( $this->changesListSpecialPage
),
509 $this->changesListSpecialPage
->getContext(),
510 $this->changesListSpecialPage
->getDB(),
520 $result = wfGetDB( DB_MASTER
)->select(
523 array_filter( $conds ) +
[ 'user_email' => 'ut' ]
527 foreach ( $result as $row ) {
528 $usernames[] = $row->user_name
;
534 private function daysAgo( $days ) {
535 $secondsPerDay = 86400;
536 return time() - $days * $secondsPerDay;
539 public function testGetFilterGroupDefinitionFromLegacyCustomFilters() {
542 'msg' => 'showhidefoo',
547 'msg' => 'showhidebar',
554 'name' => 'unstructured',
555 'class' => ChangesListBooleanFilterGroup
::class,
560 'showHide' => 'showhidefoo',
565 'showHide' => 'showhidebar',
570 $this->changesListSpecialPage
->getFilterGroupDefinitionFromLegacyCustomFilters(
576 public function testGetStructuredFilterJsData() {
579 'name' => 'gub-group',
580 'title' => 'gub-group-title',
581 'class' => ChangesListBooleanFilterGroup
::class,
585 'label' => 'foo-label',
586 'description' => 'foo-description',
588 'showHide' => 'showhidefoo',
593 'label' => 'bar-label',
594 'description' => 'bar-description',
602 'name' => 'des-group',
603 'title' => 'des-group-title',
604 'class' => ChangesListStringOptionsFilterGroup
::class,
605 'isFullCoverage' => true,
609 'label' => 'grault-label',
610 'description' => 'grault-description',
614 'label' => 'garply-label',
615 'description' => 'garply-description',
618 'queryCallable' => function () {
620 'default' => ChangesListStringOptionsFilterGroup
::NONE
,
624 'name' => 'unstructured',
625 'class' => ChangesListBooleanFilterGroup
::class,
628 'name' => 'hidethud',
629 'showHide' => 'showhidethud',
635 'showHide' => 'showhidemos',
643 $this->changesListSpecialPage
->registerFiltersFromDefinitions( $definition );
645 $this->assertArrayEquals(
647 // Filters that only display in the unstructured UI are
648 // are not included, and neither are groups that would
649 // be empty due to the above.
652 'name' => 'gub-group',
653 'title' => 'gub-group-title',
654 'type' => ChangesListBooleanFilterGroup
::TYPE
,
659 'label' => 'bar-label',
660 'description' => 'bar-description',
669 'label' => 'foo-label',
670 'description' => 'foo-description',
678 'fullCoverage' => true,
683 'name' => 'des-group',
684 'title' => 'des-group-title',
685 'type' => ChangesListStringOptionsFilterGroup
::TYPE
,
687 'fullCoverage' => true,
691 'label' => 'grault-label',
692 'description' => 'grault-description',
700 'label' => 'garply-label',
701 'description' => 'garply-description',
710 'default' => ChangesListStringOptionsFilterGroup
::NONE
,
721 'grault-description',
723 'garply-description',
726 $this->changesListSpecialPage
->getStructuredFilterJsData(),
727 /** ordered= */ false,
732 public function provideParseParameters() {
734 [ 'hidebots', [ 'hidebots' => true ] ],
736 [ 'bots', [ 'hidebots' => false ] ],
738 [ 'hideminor', [ 'hideminor' => true ] ],
740 [ 'minor', [ 'hideminor' => false ] ],
742 [ 'hidemajor', [ 'hidemajor' => true ] ],
744 [ 'hideliu', [ 'hideliu' => true ] ],
746 [ 'hidepatrolled', [ 'hidepatrolled' => true ] ],
748 [ 'hideunpatrolled', [ 'hideunpatrolled' => true ] ],
750 [ 'hideanons', [ 'hideanons' => true ] ],
752 [ 'hidemyself', [ 'hidemyself' => true ] ],
754 [ 'hidebyothers', [ 'hidebyothers' => true ] ],
756 [ 'hidehumans', [ 'hidehumans' => true ] ],
758 [ 'hidepageedits', [ 'hidepageedits' => true ] ],
760 [ 'pagedits', [ 'hidepageedits' => false ] ],
762 [ 'hidenewpages', [ 'hidenewpages' => true ] ],
764 [ 'hidecategorization', [ 'hidecategorization' => true ] ],
766 [ 'hidelog', [ 'hidelog' => true ] ],
769 'userExpLevel=learner;experienced',
771 'userExpLevel' => 'learner;experienced'
775 // A few random combos
777 'bots,hideliu,hidemyself',
781 'hidemyself' => true,
786 'minor,hideanons,categorization',
788 'hideminor' => false,
790 'hidecategorization' => false,
795 'hidehumans,bots,hidecategorization',
797 'hidehumans' => true,
799 'hidecategorization' => true,
804 'hidemyself,userExpLevel=newcomer;learner,hideminor',
806 'hidemyself' => true,
808 'userExpLevel' => 'newcomer;learner',
814 public function provideGetFilterConflicts() {
818 "expectedConflicts" => false,
823 "userExpLevel" => "newcomer",
825 "expectedConflicts" => true,
830 "userExpLevel" => "learner",
832 "expectedConflicts" => false,
837 "hidenewpages" => true,
838 "hidepageedits" => true,
839 "hidecategorization" => false,
841 "hideWikidata" => true,
843 "expectedConflicts" => true,
848 "hidenewpages" => false,
849 "hidepageedits" => true,
850 "hidecategorization" => false,
852 "hideWikidata" => true,
854 "expectedConflicts" => true,
859 "hidenewpages" => false,
860 "hidepageedits" => false,
861 "hidecategorization" => true,
863 "hideWikidata" => true,
865 "expectedConflicts" => false,
870 "hidenewpages" => true,
871 "hidepageedits" => true,
872 "hidecategorization" => false,
874 "hideWikidata" => true,
876 "expectedConflicts" => false,
882 * @dataProvider provideGetFilterConflicts
884 public function testGetFilterConflicts( $parameters, $expectedConflicts ) {
885 $context = new RequestContext
;
886 $context->setRequest( new FauxRequest( $parameters ) );
887 $this->changesListSpecialPage
->setContext( $context );
891 $this->changesListSpecialPage
->areFiltersInConflict()