Merge "Drop cache interwiki"
[mediawiki.git] / tests / phpunit / includes / specials / SpecialContributionsTest.php
blob6bac52e91a2a2e226982ff1cc6f9d8bb702ebe17
1 <?php
3 use MediaWiki\MainConfigNames;
4 use MediaWiki\Permissions\Authority;
5 use MediaWiki\Permissions\UltimateAuthority;
6 use MediaWiki\Request\FauxRequest;
7 use MediaWiki\Specials\SpecialContributions;
8 use MediaWiki\Tests\User\TempUser\TempUserTestTrait;
9 use MediaWiki\User\User;
10 use MediaWiki\User\UserFactory;
11 use Wikimedia\Parsoid\Utils\DOMCompat;
12 use Wikimedia\Parsoid\Utils\DOMUtils;
14 /**
15 * @author Ammarpad
16 * @group Database
17 * @covers \MediaWiki\Specials\SpecialContributions
18 * @covers \MediaWiki\SpecialPage\ContributionsSpecialPage
20 class SpecialContributionsTest extends SpecialPageTestBase {
21 use TempUserTestTrait;
23 private const PAGE_NAME = __CLASS__ . 'BlaBlaTest';
24 /** @var Authority */
25 private static $admin;
26 /** @var User */
27 private static $user;
28 private static int $useModWikiIPRevId;
30 public function addDBDataOnce() {
31 $this->overrideConfigValue(
32 MainConfigNames::RangeContributionsCIDRLimit,
34 'IPv4' => 16,
35 'IPv6' => 32,
38 $this->setTemporaryHook(
39 'SpecialContributionsBeforeMainOutput',
40 static function () {
43 self::$admin = new UltimateAuthority( $this->getTestSysop()->getUser() );
44 $this->assertTrue(
45 $this->editPage(
46 self::PAGE_NAME, 'Test Content', 'test', NS_MAIN, self::$admin
47 )->isOK(),
48 'Edit failed for admin'
51 self::$user = $this->getTestUser()->getUser();
52 $this->assertTrue(
53 $this->editPage(
54 'Test', 'Test Content', 'test', NS_MAIN, self::$user
55 )->isOK(),
56 'Edit failed for user'
59 $this->disableAutoCreateTempUser();
60 $useModWikiIP = $this->getServiceContainer()->getUserFactory()
61 ->newFromName( '1.2.3.xxx', UserFactory::RIGOR_NONE );
62 $useModWikiIPEditStatus = $this->editPage( 'Test1234', 'Test Content', 'test', NS_MAIN, $useModWikiIP );
63 $this->assertStatusGood( $useModWikiIPEditStatus, 'Edit failed for IP in usemod format' );
64 static::$useModWikiIPRevId = $useModWikiIPEditStatus->getNewRevision()->getId();
66 $blockStatus = $this->getServiceContainer()->getBlockUserFactory()
67 ->newBlockUser(
68 self::$user->getName(),
69 self::$admin,
70 'infinity',
71 '',
72 [ 'isHideUser' => true ],
74 ->placeBlock();
75 $this->assertStatusGood( $blockStatus, 'Block was not placed' );
78 public function testExecuteEmptyTarget() {
79 [ $html ] = $this->executeSpecialPage();
80 // This 'topOnly' filter should always be added to Special:Contributions
81 $this->assertStringContainsString( 'topOnly', $html );
82 $this->assertStringNotContainsString( 'mw-pager-body', $html );
85 public function testExecuteHiddenTarget() {
86 [ $html ] = $this->executeSpecialPage(
87 self::$user->getName()
89 $this->assertStringNotContainsString( 'mw-pager-body', $html );
92 public function testExecuteHiddenTargetWithPermissions() {
93 [ $html ] = $this->executeSpecialPage(
94 self::$user->getName(),
95 null,
96 'qqx',
97 // This is necessary because permission checks aren't actually
98 // done on the UlitmateAuthority that is self::$admin. Instead,
99 // they are done on a UserAuthority. See the TODO comment in
100 // User::getThisAsAuthority for more details.
101 $this->getTestUser( [
102 'sysop',
103 'bureaucrat',
104 'suppress'
105 ] )->getUser()
107 $this->assertStringContainsString( 'mw-pager-body', $html );
110 public function testExecuteInvalidNamespace() {
111 [ $html ] = $this->executeSpecialPage(
112 '::1',
113 new FauxRequest( [
114 'namespace' => -1,
117 $this->assertStringNotContainsString( 'mw-pager-body', $html );
120 /** @dataProvider provideExecuteNoResultsForIPTarget */
121 public function testExecuteNoResultsForIPTarget( $temporaryAccountsEnabled, $expectedPageTitleMessageKey ) {
122 if ( $temporaryAccountsEnabled ) {
123 $this->enableAutoCreateTempUser();
124 } else {
125 $this->disableAutoCreateTempUser();
127 [ $html ] = $this->executeSpecialPage( '4.3.2.1', null, null, null, true );
128 $specialPageDocument = DOMUtils::parseHTML( $html );
129 $contentHtml = DOMCompat::querySelector( $specialPageDocument, '.mw-content-container' )->nodeValue;
130 $this->assertStringNotContainsString( 'mw-pager-body', $contentHtml );
131 $this->assertStringContainsString( "($expectedPageTitleMessageKey: 4.3.2.1", $contentHtml );
134 public static function provideExecuteNoResultsForIPTarget() {
135 return [
136 'Temporary accounts not enabled' => [ false, 'contributions-title' ],
137 'Temporary accounts enabled' => [
138 true, 'contributions-title-for-ip-when-temporary-accounts-enabled',
143 public function testExecuteForUseModWikiIP() {
144 // Regression test for T370413
145 [ $html ] = $this->executeSpecialPage( '1.2.3.xxx' );
146 $contributionsList = DOMCompat::querySelectorAll( DOMUtils::parseHTML( $html ), '.mw-contributions-list' );
147 $this->assertCount(
148 1, $contributionsList, 'Should have the contributions list as 1.2.3.xxx has made one edit'
150 $matchingLines = DOMCompat::querySelectorAll(
151 $contributionsList[0], '[data-mw-revid="' . static::$useModWikiIPRevId . '"]'
153 $this->assertCount( 1, $matchingLines, "The edit made by the usemod IP is missing" );
157 * @dataProvider provideTestExecuteRange
159 public function testExecuteRange( $username, $shouldShowLinks ) {
160 [ $html ] = $this->executeSpecialPage( $username, null, 'qqx', self::$admin, true );
162 if ( $shouldShowLinks ) {
163 $this->assertStringContainsString( 'blocklink', $html );
164 } else {
165 $this->assertStringNotContainsString( 'blocklink', $html );
166 $this->assertStringContainsString( 'sp-contributions-outofrange', $html );
171 * @dataProvider provideTestExecuteNonRange
173 public function testExecuteNonRange( $username, $shouldShowLinks ) {
174 [ $html ] = $this->executeSpecialPage( $username, null, 'qqx', self::$admin, true );
176 if ( $shouldShowLinks ) {
177 $this->assertStringContainsString( 'blocklink', $html );
178 } else {
179 $this->assertStringNotContainsString( 'blocklink', $html );
183 public static function provideTestExecuteRange() {
184 yield 'Queryable IPv4 range should have blocklink for admin'
185 => [ '24.237.208.166/30', true ];
186 yield 'Queryable IPv6 range should have blocklink for admin'
187 => [ '2001:DB8:0:0:0:0:0:01/43', true ];
188 yield 'Unqueryable IPv4 range should not have blocklink for admin'
189 => [ '212.35.31.121/14', false ];
190 yield 'Unqueryable IPv6 range should not have blocklink for admin'
191 => [ '2000::/24', false ];
194 public static function provideTestExecuteNonRange() {
195 yield 'Valid IPv4 should have blocklink for admin' => [ '124.24.52.13', true ];
196 yield 'Valid IPv6 should have blocklink for admin' => [ '2001:db8::', true ];
197 yield 'Local user should have blocklink for admin' => [ 'UTSysop', true ];
198 yield 'Invalid IP should not have blocklink for admin' => [ '24.237.222208.166', false ];
199 yield 'External user should not have blocklink for admin' => [ 'imported>UTSysop', false ];
200 yield 'Nonexistent user should not have blocklink for admin' => [ __CLASS__, false ];
203 public static function provideYearMonthParams() {
204 yield 'Current year/month' => [
205 'year' => date( 'Y' ),
206 'month' => date( 'm' ),
207 'expect' => true,
209 yield 'Old year/moth' => [
210 'year' => '2007',
211 'month' => '01',
212 'expect' => false,
214 yield 'Garbage' => [
215 'year' => '123garbage123',
216 'month' => date( 'm' ),
217 'expect' => true,
222 * @dataProvider provideYearMonthParams
224 public function testYearMonthParams( string $year, string $month, bool $expect ) {
225 [ $html ] = $this->executeSpecialPage(
226 self::$admin->getUser()->getName(),
227 new FauxRequest( [
228 'year' => $year,
229 'month' => $month,
230 ] ) );
231 if ( $expect ) {
232 $this->assertStringContainsString( self::PAGE_NAME, $html );
233 } else {
234 $this->assertStringNotContainsString( self::PAGE_NAME, $html );
238 public function testBotParam() {
239 [ $html ] = $this->executeSpecialPage(
240 '::1',
241 new FauxRequest( [
242 'bot' => 1,
243 ] ),
244 null,
245 self::$admin
247 $this->assertStringContainsString( 'bot', $html );
250 public function testFeedFormat() {
251 $specialPage = $this->newSpecialPage();
252 [ $html ] = ( new SpecialPageExecutor() )->executeSpecialPage(
253 $specialPage,
254 '::1',
255 new FauxRequest( [
256 'feed' => 'atom',
257 'namespace' => 2,
258 'topOnly' => true,
259 'newOnly' => true,
260 'hideMinor' => true,
261 'deletedOnly' => true,
262 'tagfilter' => 'mw-reverted',
263 'year' => '2000',
264 'month' => '01',
267 $url = $specialPage->getOutput()->getRedirect();
268 $this->assertStringContainsString( 'namespace', $url );
269 $this->assertStringContainsString( 'toponly', $url );
270 $this->assertStringContainsString( 'newonly', $url );
271 $this->assertStringContainsString( 'hideminor', $url );
272 $this->assertStringContainsString( 'deletedonly', $url );
273 $this->assertStringContainsString( 'tagfilter', $url );
274 $this->assertStringContainsString( 'year', $url );
275 $this->assertStringContainsString( 'month', $url );
279 * @dataProvider providePrefixSearchSubpages
281 public function testPrefixSearchSubpages( $search, $expected ) {
282 $specialPage = $this->newSpecialPage();
283 $this->assertCount(
284 $expected,
285 $specialPage->prefixSearchSubpages( $search, 10, 0 )
289 public static function providePrefixSearchSubpages() {
290 return [
291 'Invalid prefix' => [ '/', 0 ],
292 'Valid prefix' => [ 'U', 1 ],
296 protected function newSpecialPage(): SpecialContributions {
297 $services = $this->getServiceContainer();
299 return new SpecialContributions(
300 $services->getLinkBatchFactory(),
301 $services->getPermissionManager(),
302 $services->getConnectionProvider(),
303 $services->getRevisionStore(),
304 $services->getNamespaceInfo(),
305 $services->getUserNameUtils(),
306 $services->getUserNamePrefixSearch(),
307 $services->getUserOptionsLookup(),
308 $services->getCommentFormatter(),
309 $services->getUserFactory(),
310 $services->getUserIdentityLookup(),
311 $services->getDatabaseBlockStore(),
312 $services->getTempUserConfig()