*/ class EnhancedChangesListTest extends MediaWikiLangTestCase { /** * @var TestRecentChangesHelper */ private $testRecentChangesHelper; protected function setUp(): void { parent::setUp(); $this->testRecentChangesHelper = new TestRecentChangesHelper(); } public function testBeginRecentChangesList_styleModules() { $enhancedChangesList = $this->newEnhancedChangesList(); $enhancedChangesList->beginRecentChangesList(); $styleModules = $enhancedChangesList->getOutput()->getModuleStyles(); $this->assertContains( 'mediawiki.special.changeslist', $styleModules, 'has mediawiki.special.changeslist' ); $this->assertContains( 'mediawiki.special.changeslist.enhanced', $styleModules, 'has mediawiki.special.changeslist.enhanced' ); } public function testBeginRecentChangesList_html() { $enhancedChangesList = $this->newEnhancedChangesList(); $html = $enhancedChangesList->beginRecentChangesList(); $this->assertEquals( '
', $html ); } /** * @todo more tests */ public function testRecentChangesLine() { $enhancedChangesList = $this->newEnhancedChangesList(); $enhancedChangesList->beginRecentChangesList(); $recentChange = $this->getEditChange( '20131103092153' ); $html = $enhancedChangesList->recentChangesLine( $recentChange, false ); $this->assertIsString( $html ); $recentChange2 = $this->getEditChange( '20131103092253' ); $html = $enhancedChangesList->recentChangesLine( $recentChange2, false ); $this->assertSame( '', $html ); } public function testRecentChangesPrefix() { $mockContext = $this->getMockBuilder( RequestContext::class ) ->onlyMethods( [ 'getTitle' ] ) ->getMock(); $mockContext->method( 'getTitle' ) ->willReturn( Title::makeTitle( NS_MAIN, 'Expected Context Title' ) ); // One group of two lines $enhancedChangesList = $this->newEnhancedChangesList(); $enhancedChangesList->setContext( $mockContext ); $enhancedChangesList->setChangeLinePrefixer( function ( $rc, $changesList ) { // Make sure RecentChange and ChangesList objects are the same $this->assertEquals( 'Expected Context Title', $changesList->getContext()->getTitle() ); $this->assertTrue( $rc->getTitle() == 'Cat' || $rc->getTitle() == 'Dog' ); return 'Hello world prefix'; } ); $this->setTemporaryHook( 'EnhancedChangesListModifyLineData', static function ( $enhancedChangesList, &$data, $block, $rc, &$classes, &$attribs ) { $data['recentChangesFlags']['minor'] = 1; } ); $this->setTemporaryHook( 'EnhancedChangesListModifyBlockLineData', static function ( $enhancedChangesList, &$data, $rcObj ) { $data['recentChangesFlags']['bot'] = 1; } ); $enhancedChangesList->beginRecentChangesList(); $recentChange = $this->getEditChange( '20131103092153' ); $enhancedChangesList->recentChangesLine( $recentChange ); $recentChange = $this->getEditChange( '20131103092154' ); $enhancedChangesList->recentChangesLine( $recentChange ); $html = $enhancedChangesList->endRecentChangesList(); $this->assertMatchesRegularExpression( '/Hello world prefix/', $html ); // Test EnhancedChangesListModifyLineData hook was run $this->assertMatchesRegularExpression( '/This is a minor edit/', $html ); // Two separate lines $enhancedChangesList->beginRecentChangesList(); $recentChange = $this->getEditChange( '20131103092153' ); $enhancedChangesList->recentChangesLine( $recentChange ); $recentChange = $this->getEditChange( '20131103092154', 'Dog' ); $enhancedChangesList->recentChangesLine( $recentChange ); $html = $enhancedChangesList->endRecentChangesList(); // Test EnhancedChangesListModifyBlockLineData hook was run $this->assertMatchesRegularExpression( '/This edit was performed by a bot/', $html ); preg_match_all( '/Hello world prefix/', $html, $matches ); $this->assertCount( 2, $matches[0] ); } public function testCategorizationLineFormatting() { $html = $this->createCategorizationLine( $this->getCategorizationChange( '20150629191735', 0, 0 ) ); $this->assertStringNotContainsString( 'diffhist', strip_tags( $html ) ); } public function testCategorizationLineFormattingWithRevision() { $html = $this->createCategorizationLine( $this->getCategorizationChange( '20150629191735', 1025, 1024 ) ); $this->assertStringContainsString( 'diffhist', strip_tags( $html ) ); } /** * @todo more tests for actual formatting, this is more of a smoke test */ public function testEndRecentChangesList() { $enhancedChangesList = $this->newEnhancedChangesList(); $enhancedChangesList->beginRecentChangesList(); $recentChange = $this->getEditChange( '20131103092153' ); $enhancedChangesList->recentChangesLine( $recentChange, false ); $html = $enhancedChangesList->endRecentChangesList(); $this->assertMatchesRegularExpression( '/data-mw-revid="5" data-mw-ts="20131103092153" class="[^"]*mw-enhanced-rc[^"]*"/', $html ); $recentChange2 = $this->getEditChange( '20131103092253' ); $enhancedChangesList->recentChangesLine( $recentChange2, false ); $html = $enhancedChangesList->endRecentChangesList(); preg_match_all( '/td class="mw-enhanced-rc-nested"/', $html, $matches ); $this->assertCount( 2, $matches[0] ); preg_match_all( '/data-target-page="Cat"/', $html, $matches ); $this->assertCount( 2, $matches[0] ); $recentChange3 = $this->getLogChange(); $enhancedChangesList->recentChangesLine( $recentChange3, false ); $html = $enhancedChangesList->endRecentChangesList(); $this->assertStringContainsString( 'data-mw-logaction="foo/bar"', $html ); $this->assertStringContainsString( 'data-mw-logid="25"', $html ); $this->assertStringContainsString( 'data-target-page="Title"', $html ); } /** * @return EnhancedChangesList */ private function newEnhancedChangesList() { $user = User::newFromId( 0 ); $context = $this->testRecentChangesHelper->getTestContext( $user ); return new EnhancedChangesList( $context ); } /** * @param string $timestamp * @param string $pageTitle * @return RecentChange */ private function getEditChange( $timestamp, $pageTitle = 'Cat' ) { $user = $this->getMutableTestUser()->getUser(); $recentChange = $this->testRecentChangesHelper->makeEditRecentChange( $user, $pageTitle, 0, 5, 191, $timestamp, 0, 0 ); return $recentChange; } private function getLogChange() { $user = $this->getMutableTestUser()->getUser(); $recentChange = $this->testRecentChangesHelper->makeLogRecentChange( 'foo', 'bar', $user, 'Title', '20131103092153', 0, 0 ); return $recentChange; } /** * @param string $timestamp * @param int $thisId * @param int $lastId * @return RecentChange */ private function getCategorizationChange( $timestamp, $thisId, $lastId ) { $wikiPage = $this->getServiceContainer()->getWikiPageFactory()->newFromTitle( Title::makeTitle( NS_MAIN, 'Testpage' ) ); $wikiPage->doUserEditContent( new WikitextContent( 'Some random text' ), $this->getTestSysop()->getUser(), 'page created' ); $wikiPage = $this->getServiceContainer()->getWikiPageFactory()->newFromTitle( Title::makeTitle( NS_CATEGORY, 'Foo' ) ); $wikiPage->doUserEditContent( new WikitextContent( 'Some random text' ), $this->getTestSysop()->getUser(), 'category page created' ); $user = $this->getMutableTestUser()->getUser(); $recentChange = $this->testRecentChangesHelper->makeCategorizationRecentChange( $user, 'Category:Foo', $wikiPage->getId(), $thisId, $lastId, $timestamp ); return $recentChange; } private function createCategorizationLine( $recentChange ) { $enhancedChangesList = $this->newEnhancedChangesList(); $cacheEntry = $this->testRecentChangesHelper->getCacheEntry( $recentChange ); $reflection = new \ReflectionClass( get_class( $enhancedChangesList ) ); $method = $reflection->getMethod( 'recentChangesBlockLine' ); $method->setAccessible( true ); return $method->invokeArgs( $enhancedChangesList, [ $cacheEntry ] ); } public function testExpiringWatchlistItem(): void { // Set current time to 2020-05-05. MWTimestamp::setFakeTime( '20200505000000' ); $enhancedChangesList = $this->newEnhancedChangesList(); $enhancedChangesList->getOutput()->enableOOUI(); $enhancedChangesList->setWatchlistDivs( true ); $row = (object)[ 'rc_namespace' => NS_MAIN, 'rc_title' => '', 'rc_timestamp' => '20150921134808', 'rc_deleted' => '', 'rc_comment_text' => 'comment', 'rc_comment_data' => null, 'rc_user' => $this->getTestUser()->getUser()->getId(), 'we_expiry' => '20200101000000', ]; $rc = RecentChange::newFromRow( $row ); // Make sure it doesn't output anything for a past expiry. $html1 = $enhancedChangesList->getWatchlistExpiry( $rc ); $this->assertSame( '', $html1 ); // Check a future expiry for the right tooltip text. $rc->watchlistExpiry = '20200512000000'; $html2 = $enhancedChangesList->getWatchlistExpiry( $rc ); $this->assertStringContainsString( "title='7 days left in your watchlist'", $html2 ); // Check that multiple changes on the same day all get the clock icon. $enhancedChangesList->beginRecentChangesList(); // 1. Expire on 2020-06-01 (27 days): $rc1 = $this->getEditChange( '20200501000001', __METHOD__ . '1' ); $rc1->watchlistExpiry = '20200601000000'; $enhancedChangesList->recentChangesLine( $rc1 ); // 2. Expire on 2020-06-08 (34 days): $rc2 = $this->getEditChange( '20200501000002', __METHOD__ . '2' ); $rc2->watchlistExpiry = '20200608000000'; $enhancedChangesList->recentChangesLine( $rc2 ); // Get and test the HTML. $html3 = $enhancedChangesList->endRecentChangesList(); $this->assertStringContainsString( '27 days left in your watchlist', $html3 ); $this->assertStringContainsString( '34 days left in your watchlist', $html3 ); } }