summaryrefslogtreecommitdiff
path: root/www/wiki/tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php')
-rw-r--r--www/wiki/tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php1098
1 files changed, 1098 insertions, 0 deletions
diff --git a/www/wiki/tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php b/www/wiki/tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php
new file mode 100644
index 00000000..aeaa1aee
--- /dev/null
+++ b/www/wiki/tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php
@@ -0,0 +1,1098 @@
+<?php
+
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * Test class for ChangesListSpecialPage class
+ *
+ * Copyright © 2011-, Antoine Musso, Stephane Bisson, Matthew Flaschen
+ *
+ * @author Antoine Musso
+ * @author Stephane Bisson
+ * @author Matthew Flaschen
+ * @group Database
+ *
+ * @covers ChangesListSpecialPage
+ */
+class ChangesListSpecialPageTest extends AbstractChangesListSpecialPageTestCase {
+ public function setUp() {
+ parent::setUp();
+ $this->setMwGlobals( [
+ 'wgStructuredChangeFiltersShowPreference' => true,
+ ] );
+ }
+
+ protected function getPage() {
+ $mock = $this->getMockBuilder( ChangesListSpecialPage::class )
+ ->setConstructorArgs(
+ [
+ 'ChangesListSpecialPage',
+ ''
+ ]
+ )
+ ->setMethods( [ 'getPageTitle' ] )
+ ->getMockForAbstractClass();
+
+ $mock->method( 'getPageTitle' )->willReturn(
+ Title::makeTitle( NS_SPECIAL, 'ChangesListSpecialPage' )
+ );
+
+ $mock = TestingAccessWrapper::newFromObject(
+ $mock
+ );
+
+ return $mock;
+ }
+
+ private function buildQuery(
+ $requestOptions = null,
+ $user = null
+ ) {
+ $context = new RequestContext;
+ $context->setRequest( new FauxRequest( $requestOptions ) );
+ if ( $user ) {
+ $context->setUser( $user );
+ }
+
+ $this->changesListSpecialPage->setContext( $context );
+ $this->changesListSpecialPage->filterGroups = [];
+ $formOptions = $this->changesListSpecialPage->setup( null );
+
+ #  Filter out rc_timestamp conditions which depends on the test runtime
+ # This condition is not needed as of march 2, 2011 -- hashar
+ # @todo FIXME: Find a way to generate the correct rc_timestamp
+
+ $tables = [];
+ $fields = [];
+ $queryConditions = [];
+ $query_options = [];
+ $join_conds = [];
+
+ call_user_func_array(
+ [ $this->changesListSpecialPage, 'buildQuery' ],
+ [
+ &$tables,
+ &$fields,
+ &$queryConditions,
+ &$query_options,
+ &$join_conds,
+ $formOptions
+ ]
+ );
+
+ $queryConditions = array_filter(
+ $queryConditions,
+ 'ChangesListSpecialPageTest::filterOutRcTimestampCondition'
+ );
+
+ return $queryConditions;
+ }
+
+ /** helper to test SpecialRecentchanges::buildQuery() */
+ private function assertConditions(
+ $expected,
+ $requestOptions = null,
+ $message = '',
+ $user = null
+ ) {
+ $queryConditions = $this->buildQuery( $requestOptions, $user );
+
+ $this->assertEquals(
+ self::normalizeCondition( $expected ),
+ self::normalizeCondition( $queryConditions ),
+ $message
+ );
+ }
+
+ private static function normalizeCondition( $conds ) {
+ $dbr = wfGetDB( DB_REPLICA );
+ $normalized = array_map(
+ function ( $k, $v ) use ( $dbr ) {
+ if ( is_array( $v ) ) {
+ sort( $v );
+ }
+ // (Ab)use makeList() to format only this entry
+ return $dbr->makeList( [ $k => $v ], Database::LIST_AND );
+ },
+ array_keys( $conds ),
+ $conds
+ );
+ sort( $normalized );
+ return $normalized;
+ }
+
+ /** return false if condition begins with 'rc_timestamp ' */
+ private static function filterOutRcTimestampCondition( $var ) {
+ return ( is_array( $var ) || false === strpos( $var, 'rc_timestamp ' ) );
+ }
+
+ public function testRcNsFilter() {
+ $this->assertConditions(
+ [ # expected
+ "rc_namespace = '0'",
+ ],
+ [
+ 'namespace' => NS_MAIN,
+ ],
+ "rc conditions with one namespace"
+ );
+ }
+
+ public function testRcNsFilterInversion() {
+ $this->assertConditions(
+ [ # expected
+ "rc_namespace != '0'",
+ ],
+ [
+ 'namespace' => NS_MAIN,
+ 'invert' => 1,
+ ],
+ "rc conditions with namespace inverted"
+ );
+ }
+
+ public function testRcNsFilterMultiple() {
+ $this->assertConditions(
+ [ # expected
+ "rc_namespace IN ('1','2','3')",
+ ],
+ [
+ 'namespace' => '1;2;3',
+ ],
+ "rc conditions with multiple namespaces"
+ );
+ }
+
+ public function testRcNsFilterMultipleAssociated() {
+ $this->assertConditions(
+ [ # expected
+ "rc_namespace IN ('0','1','4','5','6','7')",
+ ],
+ [
+ 'namespace' => '1;4;7',
+ 'associated' => 1,
+ ],
+ "rc conditions with multiple namespaces and associated"
+ );
+ }
+
+ public function testRcNsFilterMultipleAssociatedInvert() {
+ $this->assertConditions(
+ [ # expected
+ "rc_namespace NOT IN ('2','3','8','9')",
+ ],
+ [
+ 'namespace' => '2;3;9',
+ 'associated' => 1,
+ 'invert' => 1
+ ],
+ "rc conditions with multiple namespaces, associated and inverted"
+ );
+ }
+
+ public function testRcNsFilterMultipleInvert() {
+ $this->assertConditions(
+ [ # expected
+ "rc_namespace NOT IN ('1','2','3')",
+ ],
+ [
+ 'namespace' => '1;2;3',
+ 'invert' => 1,
+ ],
+ "rc conditions with multiple namespaces inverted"
+ );
+ }
+
+ public function testRcHidemyselfFilter() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+ $this->overrideMwServices();
+
+ $user = $this->getTestUser()->getUser();
+ $user->getActorId( wfGetDB( DB_MASTER ) );
+ $this->assertConditions(
+ [ # expected
+ "NOT((rc_actor = '{$user->getActorId()}') OR "
+ . "(rc_actor = '0' AND rc_user = '{$user->getId()}'))",
+ ],
+ [
+ 'hidemyself' => 1,
+ ],
+ "rc conditions: hidemyself=1 (logged in)",
+ $user
+ );
+
+ $user = User::newFromName( '10.11.12.13', false );
+ $id = $user->getActorId( wfGetDB( DB_MASTER ) );
+ $this->assertConditions(
+ [ # expected
+ "NOT((rc_actor = '$id') OR (rc_actor = '0' AND rc_user_text = '10.11.12.13'))",
+ ],
+ [
+ 'hidemyself' => 1,
+ ],
+ "rc conditions: hidemyself=1 (anon)",
+ $user
+ );
+ }
+
+ public function testRcHidebyothersFilter() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+ $this->overrideMwServices();
+
+ $user = $this->getTestUser()->getUser();
+ $user->getActorId( wfGetDB( DB_MASTER ) );
+ $this->assertConditions(
+ [ # expected
+ "(rc_actor = '{$user->getActorId()}') OR "
+ . "(rc_actor = '0' AND rc_user_text = '{$user->getName()}')",
+ ],
+ [
+ 'hidebyothers' => 1,
+ ],
+ "rc conditions: hidebyothers=1 (logged in)",
+ $user
+ );
+
+ $user = User::newFromName( '10.11.12.13', false );
+ $id = $user->getActorId( wfGetDB( DB_MASTER ) );
+ $this->assertConditions(
+ [ # expected
+ "(rc_actor = '$id') OR (rc_actor = '0' AND rc_user_text = '10.11.12.13')",
+ ],
+ [
+ 'hidebyothers' => 1,
+ ],
+ "rc conditions: hidebyothers=1 (anon)",
+ $user
+ );
+ }
+
+ public function testRcHidepageedits() {
+ $this->assertConditions(
+ [ # expected
+ "rc_type != '0'",
+ ],
+ [
+ 'hidepageedits' => 1,
+ ],
+ "rc conditions: hidepageedits=1"
+ );
+ }
+
+ public function testRcHidenewpages() {
+ $this->assertConditions(
+ [ # expected
+ "rc_type != '1'",
+ ],
+ [
+ 'hidenewpages' => 1,
+ ],
+ "rc conditions: hidenewpages=1"
+ );
+ }
+
+ public function testRcHidelog() {
+ $this->assertConditions(
+ [ # expected
+ "rc_type != '3'",
+ ],
+ [
+ 'hidelog' => 1,
+ ],
+ "rc conditions: hidelog=1"
+ );
+ }
+
+ public function testRcHidehumans() {
+ $this->assertConditions(
+ [ # expected
+ 'rc_bot' => 1,
+ ],
+ [
+ 'hidebots' => 0,
+ 'hidehumans' => 1,
+ ],
+ "rc conditions: hidebots=0 hidehumans=1"
+ );
+ }
+
+ public function testRcHidepatrolledDisabledFilter() {
+ $this->setMwGlobals( 'wgUseRCPatrol', false );
+ $user = $this->getTestUser()->getUser();
+ $this->assertConditions(
+ [ # expected
+ ],
+ [
+ 'hidepatrolled' => 1,
+ ],
+ "rc conditions: hidepatrolled=1 (user not allowed)",
+ $user
+ );
+ }
+
+ public function testRcHideunpatrolledDisabledFilter() {
+ $this->setMwGlobals( 'wgUseRCPatrol', false );
+ $user = $this->getTestUser()->getUser();
+ $this->assertConditions(
+ [ # expected
+ ],
+ [
+ 'hideunpatrolled' => 1,
+ ],
+ "rc conditions: hideunpatrolled=1 (user not allowed)",
+ $user
+ );
+ }
+ public function testRcHidepatrolledFilter() {
+ $user = $this->getTestSysop()->getUser();
+ $this->assertConditions(
+ [ # expected
+ 'rc_patrolled' => 0,
+ ],
+ [
+ 'hidepatrolled' => 1,
+ ],
+ "rc conditions: hidepatrolled=1",
+ $user
+ );
+ }
+
+ public function testRcHideunpatrolledFilter() {
+ $user = $this->getTestSysop()->getUser();
+ $this->assertConditions(
+ [ # expected
+ 'rc_patrolled' => [ 1, 2 ],
+ ],
+ [
+ 'hideunpatrolled' => 1,
+ ],
+ "rc conditions: hideunpatrolled=1",
+ $user
+ );
+ }
+
+ public function testRcReviewStatusFilter() {
+ $user = $this->getTestSysop()->getUser();
+ $this->assertConditions(
+ [ #expected
+ 'rc_patrolled' => 1,
+ ],
+ [
+ 'reviewStatus' => 'manual'
+ ],
+ "rc conditions: reviewStatus=manual",
+ $user
+ );
+ $this->assertConditions(
+ [ #expected
+ 'rc_patrolled' => [ 0, 2 ],
+ ],
+ [
+ 'reviewStatus' => 'unpatrolled;auto'
+ ],
+ "rc conditions: reviewStatus=unpatrolled;auto",
+ $user
+ );
+ }
+
+ public function testRcHideminorFilter() {
+ $this->assertConditions(
+ [ # expected
+ "rc_minor = 0",
+ ],
+ [
+ 'hideminor' => 1,
+ ],
+ "rc conditions: hideminor=1"
+ );
+ }
+
+ public function testRcHidemajorFilter() {
+ $this->assertConditions(
+ [ # expected
+ "rc_minor = 1",
+ ],
+ [
+ 'hidemajor' => 1,
+ ],
+ "rc conditions: hidemajor=1"
+ );
+ }
+
+ public function testHideCategorization() {
+ $this->assertConditions(
+ [
+ # expected
+ "rc_type != '6'"
+ ],
+ [
+ 'hidecategorization' => 1
+ ],
+ "rc conditions: hidecategorization=1"
+ );
+ }
+
+ public function testFilterUserExpLevelAll() {
+ $this->assertConditions(
+ [
+ # expected
+ ],
+ [
+ 'userExpLevel' => 'registered;unregistered;newcomer;learner;experienced',
+ ],
+ "rc conditions: userExpLevel=registered;unregistered;newcomer;learner;experienced"
+ );
+ }
+
+ public function testFilterUserExpLevelRegisteredUnregistered() {
+ $this->assertConditions(
+ [
+ # expected
+ ],
+ [
+ 'userExpLevel' => 'registered;unregistered',
+ ],
+ "rc conditions: userExpLevel=registered;unregistered"
+ );
+ }
+
+ public function testFilterUserExpLevelRegisteredUnregisteredLearner() {
+ $this->assertConditions(
+ [
+ # expected
+ ],
+ [
+ 'userExpLevel' => 'registered;unregistered;learner',
+ ],
+ "rc conditions: userExpLevel=registered;unregistered;learner"
+ );
+ }
+
+ public function testFilterUserExpLevelAllExperienceLevels() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+ $this->overrideMwServices();
+
+ $this->assertConditions(
+ [
+ # expected
+ 'COALESCE( actor_rc_user.actor_user, rc_user ) != 0',
+ ],
+ [
+ 'userExpLevel' => 'newcomer;learner;experienced',
+ ],
+ "rc conditions: userExpLevel=newcomer;learner;experienced"
+ );
+ }
+
+ public function testFilterUserExpLevelRegistrered() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+ $this->overrideMwServices();
+
+ $this->assertConditions(
+ [
+ # expected
+ 'COALESCE( actor_rc_user.actor_user, rc_user ) != 0',
+ ],
+ [
+ 'userExpLevel' => 'registered',
+ ],
+ "rc conditions: userExpLevel=registered"
+ );
+ }
+
+ public function testFilterUserExpLevelUnregistrered() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+ $this->overrideMwServices();
+
+ $this->assertConditions(
+ [
+ # expected
+ 'COALESCE( actor_rc_user.actor_user, rc_user ) = 0',
+ ],
+ [
+ 'userExpLevel' => 'unregistered',
+ ],
+ "rc conditions: userExpLevel=unregistered"
+ );
+ }
+
+ public function testFilterUserExpLevelRegistreredOrLearner() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+ $this->overrideMwServices();
+
+ $this->assertConditions(
+ [
+ # expected
+ 'COALESCE( actor_rc_user.actor_user, rc_user ) != 0',
+ ],
+ [
+ 'userExpLevel' => 'registered;learner',
+ ],
+ "rc conditions: userExpLevel=registered;learner"
+ );
+ }
+
+ public function testFilterUserExpLevelUnregistreredOrExperienced() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
+ $this->overrideMwServices();
+
+ $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
+
+ $this->assertRegExp(
+ '/\(COALESCE\( actor_rc_user.actor_user, rc_user \) = 0\) OR '
+ . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
+ reset( $conds ),
+ "rc conditions: userExpLevel=unregistered;experienced"
+ );
+ }
+
+ public function testFilterUserExpLevel() {
+ $now = time();
+ $this->setMwGlobals( [
+ 'wgLearnerEdits' => 10,
+ 'wgLearnerMemberSince' => 4,
+ 'wgExperiencedUserEdits' => 500,
+ 'wgExperiencedUserMemberSince' => 30,
+ ] );
+
+ $this->createUsers( [
+ 'Newcomer1' => [ 'edits' => 2, 'days' => 2 ],
+ 'Newcomer2' => [ 'edits' => 12, 'days' => 3 ],
+ 'Newcomer3' => [ 'edits' => 8, 'days' => 5 ],
+ 'Learner1' => [ 'edits' => 15, 'days' => 10 ],
+ 'Learner2' => [ 'edits' => 450, 'days' => 20 ],
+ 'Learner3' => [ 'edits' => 460, 'days' => 33 ],
+ 'Learner4' => [ 'edits' => 525, 'days' => 28 ],
+ 'Experienced1' => [ 'edits' => 538, 'days' => 33 ],
+ ], $now );
+
+ // newcomers only
+ $this->assertArrayEquals(
+ [ 'Newcomer1', 'Newcomer2', 'Newcomer3' ],
+ $this->fetchUsers( [ 'newcomer' ], $now )
+ );
+
+ // newcomers and learner
+ $this->assertArrayEquals(
+ [
+ 'Newcomer1', 'Newcomer2', 'Newcomer3',
+ 'Learner1', 'Learner2', 'Learner3', 'Learner4',
+ ],
+ $this->fetchUsers( [ 'newcomer', 'learner' ], $now )
+ );
+
+ // newcomers and more learner
+ $this->assertArrayEquals(
+ [
+ 'Newcomer1', 'Newcomer2', 'Newcomer3',
+ 'Experienced1',
+ ],
+ $this->fetchUsers( [ 'newcomer', 'experienced' ], $now )
+ );
+
+ // learner only
+ $this->assertArrayEquals(
+ [ 'Learner1', 'Learner2', 'Learner3', 'Learner4' ],
+ $this->fetchUsers( [ 'learner' ], $now )
+ );
+
+ // more experienced only
+ $this->assertArrayEquals(
+ [ 'Experienced1' ],
+ $this->fetchUsers( [ 'experienced' ], $now )
+ );
+
+ // learner and more experienced
+ $this->assertArrayEquals(
+ [
+ 'Learner1', 'Learner2', 'Learner3', 'Learner4',
+ 'Experienced1',
+ ],
+ $this->fetchUsers( [ 'learner', 'experienced' ], $now ),
+ 'Learner and more experienced'
+ );
+ }
+
+ private function createUsers( $specs, $now ) {
+ $dbw = wfGetDB( DB_MASTER );
+ foreach ( $specs as $name => $spec ) {
+ User::createNew(
+ $name,
+ [
+ 'editcount' => $spec['edits'],
+ 'registration' => $dbw->timestamp( $this->daysAgo( $spec['days'], $now ) ),
+ 'email' => 'ut',
+ ]
+ );
+ }
+ }
+
+ private function fetchUsers( $filters, $now ) {
+ $tables = [];
+ $conds = [];
+ $fields = [];
+ $query_options = [];
+ $join_conds = [];
+
+ sort( $filters );
+
+ call_user_func_array(
+ [ $this->changesListSpecialPage, 'filterOnUserExperienceLevel' ],
+ [
+ get_class( $this->changesListSpecialPage ),
+ $this->changesListSpecialPage->getContext(),
+ $this->changesListSpecialPage->getDB(),
+ &$tables,
+ &$fields,
+ &$conds,
+ &$query_options,
+ &$join_conds,
+ $filters,
+ $now
+ ]
+ );
+
+ // @todo: This is not at all safe or sane. It just blindly assumes
+ // nothing in $conds depends on any other tables.
+ $result = wfGetDB( DB_MASTER )->select(
+ 'user',
+ 'user_name',
+ array_filter( $conds ) + [ 'user_email' => 'ut' ]
+ );
+
+ $usernames = [];
+ foreach ( $result as $row ) {
+ $usernames[] = $row->user_name;
+ }
+
+ return $usernames;
+ }
+
+ private function daysAgo( $days, $now ) {
+ $secondsPerDay = 86400;
+ return $now - $days * $secondsPerDay;
+ }
+
+ public function testGetFilterGroupDefinitionFromLegacyCustomFilters() {
+ $customFilters = [
+ 'hidefoo' => [
+ 'msg' => 'showhidefoo',
+ 'default' => true,
+ ],
+
+ 'hidebar' => [
+ 'msg' => 'showhidebar',
+ 'default' => false,
+ ],
+ ];
+
+ $this->assertEquals(
+ [
+ 'name' => 'unstructured',
+ 'class' => ChangesListBooleanFilterGroup::class,
+ 'priority' => -1,
+ 'filters' => [
+ [
+ 'name' => 'hidefoo',
+ 'showHide' => 'showhidefoo',
+ 'default' => true,
+ ],
+ [
+ 'name' => 'hidebar',
+ 'showHide' => 'showhidebar',
+ 'default' => false,
+ ]
+ ],
+ ],
+ $this->changesListSpecialPage->getFilterGroupDefinitionFromLegacyCustomFilters(
+ $customFilters
+ )
+ );
+ }
+
+ public function testGetStructuredFilterJsData() {
+ $this->changesListSpecialPage->filterGroups = [];
+
+ $definition = [
+ [
+ 'name' => 'gub-group',
+ 'title' => 'gub-group-title',
+ 'class' => ChangesListBooleanFilterGroup::class,
+ 'filters' => [
+ [
+ 'name' => 'hidefoo',
+ 'label' => 'foo-label',
+ 'description' => 'foo-description',
+ 'default' => true,
+ 'showHide' => 'showhidefoo',
+ 'priority' => 2,
+ ],
+ [
+ 'name' => 'hidebar',
+ 'label' => 'bar-label',
+ 'description' => 'bar-description',
+ 'default' => false,
+ 'priority' => 4,
+ ]
+ ],
+ ],
+
+ [
+ 'name' => 'des-group',
+ 'title' => 'des-group-title',
+ 'class' => ChangesListStringOptionsFilterGroup::class,
+ 'isFullCoverage' => true,
+ 'filters' => [
+ [
+ 'name' => 'grault',
+ 'label' => 'grault-label',
+ 'description' => 'grault-description',
+ ],
+ [
+ 'name' => 'garply',
+ 'label' => 'garply-label',
+ 'description' => 'garply-description',
+ ],
+ ],
+ 'queryCallable' => function () {
+ },
+ 'default' => ChangesListStringOptionsFilterGroup::NONE,
+ ],
+
+ [
+ 'name' => 'unstructured',
+ 'class' => ChangesListBooleanFilterGroup::class,
+ 'filters' => [
+ [
+ 'name' => 'hidethud',
+ 'showHide' => 'showhidethud',
+ 'default' => true,
+ ],
+
+ [
+ 'name' => 'hidemos',
+ 'showHide' => 'showhidemos',
+ 'default' => false,
+ ],
+ ],
+ ],
+
+ ];
+
+ $this->changesListSpecialPage->registerFiltersFromDefinitions( $definition );
+
+ $this->assertArrayEquals(
+ [
+ // Filters that only display in the unstructured UI are
+ // are not included, and neither are groups that would
+ // be empty due to the above.
+ 'groups' => [
+ [
+ 'name' => 'gub-group',
+ 'title' => 'gub-group-title',
+ 'type' => ChangesListBooleanFilterGroup::TYPE,
+ 'priority' => -1,
+ 'filters' => [
+ [
+ 'name' => 'hidebar',
+ 'label' => 'bar-label',
+ 'description' => 'bar-description',
+ 'default' => false,
+ 'priority' => 4,
+ 'cssClass' => null,
+ 'conflicts' => [],
+ 'subset' => [],
+ 'defaultHighlightColor' => null
+ ],
+ [
+ 'name' => 'hidefoo',
+ 'label' => 'foo-label',
+ 'description' => 'foo-description',
+ 'default' => true,
+ 'priority' => 2,
+ 'cssClass' => null,
+ 'conflicts' => [],
+ 'subset' => [],
+ 'defaultHighlightColor' => null
+ ],
+ ],
+ 'fullCoverage' => true,
+ 'conflicts' => [],
+ ],
+
+ [
+ 'name' => 'des-group',
+ 'title' => 'des-group-title',
+ 'type' => ChangesListStringOptionsFilterGroup::TYPE,
+ 'priority' => -2,
+ 'fullCoverage' => true,
+ 'filters' => [
+ [
+ 'name' => 'grault',
+ 'label' => 'grault-label',
+ 'description' => 'grault-description',
+ 'cssClass' => null,
+ 'priority' => -2,
+ 'conflicts' => [],
+ 'subset' => [],
+ 'defaultHighlightColor' => null
+ ],
+ [
+ 'name' => 'garply',
+ 'label' => 'garply-label',
+ 'description' => 'garply-description',
+ 'cssClass' => null,
+ 'priority' => -3,
+ 'conflicts' => [],
+ 'subset' => [],
+ 'defaultHighlightColor' => null
+ ],
+ ],
+ 'conflicts' => [],
+ 'separator' => ';',
+ 'default' => ChangesListStringOptionsFilterGroup::NONE,
+ ],
+ ],
+ 'messageKeys' => [
+ 'gub-group-title',
+ 'bar-label',
+ 'bar-description',
+ 'foo-label',
+ 'foo-description',
+ 'des-group-title',
+ 'grault-label',
+ 'grault-description',
+ 'garply-label',
+ 'garply-description',
+ ],
+ ],
+ $this->changesListSpecialPage->getStructuredFilterJsData(),
+ /** ordered= */ false,
+ /** named= */ true
+ );
+ }
+
+ public function provideParseParameters() {
+ return [
+ [ 'hidebots', [ 'hidebots' => true ] ],
+
+ [ 'bots', [ 'hidebots' => false ] ],
+
+ [ 'hideminor', [ 'hideminor' => true ] ],
+
+ [ 'minor', [ 'hideminor' => false ] ],
+
+ [ 'hidemajor', [ 'hidemajor' => true ] ],
+
+ [ 'hideliu', [ 'hideliu' => true ] ],
+
+ [ 'hidepatrolled', [ 'hidepatrolled' => true ] ],
+
+ [ 'hideunpatrolled', [ 'hideunpatrolled' => true ] ],
+
+ [ 'hideanons', [ 'hideanons' => true ] ],
+
+ [ 'hidemyself', [ 'hidemyself' => true ] ],
+
+ [ 'hidebyothers', [ 'hidebyothers' => true ] ],
+
+ [ 'hidehumans', [ 'hidehumans' => true ] ],
+
+ [ 'hidepageedits', [ 'hidepageedits' => true ] ],
+
+ [ 'pagedits', [ 'hidepageedits' => false ] ],
+
+ [ 'hidenewpages', [ 'hidenewpages' => true ] ],
+
+ [ 'hidecategorization', [ 'hidecategorization' => true ] ],
+
+ [ 'hidelog', [ 'hidelog' => true ] ],
+
+ [
+ 'userExpLevel=learner;experienced',
+ [
+ 'userExpLevel' => 'learner;experienced'
+ ],
+ ],
+
+ // A few random combos
+ [
+ 'bots,hideliu,hidemyself',
+ [
+ 'hidebots' => false,
+ 'hideliu' => true,
+ 'hidemyself' => true,
+ ],
+ ],
+
+ [
+ 'minor,hideanons,categorization',
+ [
+ 'hideminor' => false,
+ 'hideanons' => true,
+ 'hidecategorization' => false,
+ ]
+ ],
+
+ [
+ 'hidehumans,bots,hidecategorization',
+ [
+ 'hidehumans' => true,
+ 'hidebots' => false,
+ 'hidecategorization' => true,
+ ],
+ ],
+
+ [
+ 'hidemyself,userExpLevel=newcomer;learner,hideminor',
+ [
+ 'hidemyself' => true,
+ 'hideminor' => true,
+ 'userExpLevel' => 'newcomer;learner',
+ ],
+ ],
+ ];
+ }
+
+ public function provideGetFilterConflicts() {
+ return [
+ [
+ "parameters" => [],
+ "expectedConflicts" => false,
+ ],
+ [
+ "parameters" => [
+ "hideliu" => true,
+ "userExpLevel" => "newcomer",
+ ],
+ "expectedConflicts" => false,
+ ],
+ [
+ "parameters" => [
+ "hideanons" => true,
+ "userExpLevel" => "learner",
+ ],
+ "expectedConflicts" => false,
+ ],
+ [
+ "parameters" => [
+ "hidemajor" => true,
+ "hidenewpages" => true,
+ "hidepageedits" => true,
+ "hidecategorization" => false,
+ "hidelog" => true,
+ "hideWikidata" => true,
+ ],
+ "expectedConflicts" => true,
+ ],
+ [
+ "parameters" => [
+ "hidemajor" => true,
+ "hidenewpages" => false,
+ "hidepageedits" => true,
+ "hidecategorization" => false,
+ "hidelog" => false,
+ "hideWikidata" => true,
+ ],
+ "expectedConflicts" => true,
+ ],
+ [
+ "parameters" => [
+ "hidemajor" => true,
+ "hidenewpages" => false,
+ "hidepageedits" => false,
+ "hidecategorization" => true,
+ "hidelog" => true,
+ "hideWikidata" => true,
+ ],
+ "expectedConflicts" => false,
+ ],
+ [
+ "parameters" => [
+ "hideminor" => true,
+ "hidenewpages" => true,
+ "hidepageedits" => true,
+ "hidecategorization" => false,
+ "hidelog" => true,
+ "hideWikidata" => true,
+ ],
+ "expectedConflicts" => false,
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideGetFilterConflicts
+ */
+ public function testGetFilterConflicts( $parameters, $expectedConflicts ) {
+ $context = new RequestContext;
+ $context->setRequest( new FauxRequest( $parameters ) );
+ $this->changesListSpecialPage->setContext( $context );
+
+ $this->assertEquals(
+ $expectedConflicts,
+ $this->changesListSpecialPage->areFiltersInConflict()
+ );
+ }
+
+ public function validateOptionsProvider() {
+ return [
+ [
+ [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 1 ],
+ true,
+ [ 'userExpLevel' => 'unregistered', 'hidebots' => 1, ],
+ ],
+ [
+ [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 0 ],
+ true,
+ [ 'hidebots' => 0, 'hidehumans' => 1 ],
+ ],
+ [
+ [ 'hideanons' => 1 ],
+ true,
+ [ 'userExpLevel' => 'registered' ]
+ ],
+ [
+ [ 'hideliu' => 1 ],
+ true,
+ [ 'userExpLevel' => 'unregistered' ]
+ ],
+ [
+ [ 'hideanons' => 1, 'hidebots' => 1 ],
+ true,
+ [ 'userExpLevel' => 'registered', 'hidebots' => 1 ]
+ ],
+ [
+ [ 'hideliu' => 1, 'hidebots' => 0 ],
+ true,
+ [ 'userExpLevel' => 'unregistered', 'hidebots' => 0 ]
+ ],
+ [
+ [ 'hidemyself' => 1, 'hidebyothers' => 1 ],
+ true,
+ [],
+ ],
+ [
+ [ 'hidebots' => 1, 'hidehumans' => 1 ],
+ true,
+ [],
+ ],
+ [
+ [ 'hidepatrolled' => 1, 'hideunpatrolled' => 1 ],
+ true,
+ [],
+ ],
+ [
+ [ 'hideminor' => 1, 'hidemajor' => 1 ],
+ true,
+ [],
+ ],
+ [
+ // changeType
+ [ 'hidepageedits' => 1, 'hidenewpages' => 1, 'hidecategorization' => 1, 'hidelog' => 1, ],
+ true,
+ [],
+ ],
+ ];
+ }
+}