wiki.techinc.nl/tests/phpunit/includes/changes/EnhancedChangesListTest.php
Fomafix 9bf98ab9db EnhancedChangesList: Use HTML/CSS for collapsing
The collapsing on the enhanced changes list now works without
JavaScript.

Keyboard navigation is still supported but only the space and not the
return toggles the collapsing toggle button.

The <input type="checkbox"> element needs an unique identifier in the
`id` attribute and the same value must be in the `for` attribute of the
<label> element. A simple counter in the class EnhancedChangesList
starts twice from the beginning if the recent changes get included as:

 {{Special:RecentChanges|enhanced=1}}
 {{Special:RecentChanges|enhanced=1}}

Therefore a random value is used as checkbox identifier.

The module 'jquery.makeCollapsible' and its classes `mw-collapsible`,
`mw-collapsed` and `mw-collapsible-toggle-collapsed` are not needed
anymore.

The icons from module 'mediawiki.icon' are directly included because the
module has fixed selectors which do not fit here.

Bug: T172618
Change-Id: Iafd27e5d760b78ae386d833946005f86cee8dd64
2023-07-13 23:12:45 +00:00

294 lines
9.8 KiB
PHP

<?php
use MediaWiki\Title\Title;
/**
* @covers EnhancedChangesList
*
* @group Database
*
* @author Katie Filbert < aude.wiki@gmail.com >
*/
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( '<div class="mw-changeslist" aria-live="polite">', $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 );
}
}