2022-09-04 09:00:14 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace MediaWiki\Extension\VisualEditor\Tests;
|
|
|
|
|
|
|
|
use Generator;
|
|
|
|
use Language;
|
|
|
|
use MediaWiki\Extension\VisualEditor\DirectParsoidClient;
|
|
|
|
use MediaWiki\Page\PageIdentityValue;
|
2022-08-28 19:25:04 +00:00
|
|
|
use MediaWiki\Parser\Parsoid\ParsoidOutputAccess;
|
2022-12-15 17:25:38 +00:00
|
|
|
use MediaWiki\Rest\Handler\PageRestHelperFactory;
|
|
|
|
use MediaWiki\Rest\HttpException;
|
2022-08-28 19:25:04 +00:00
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
2022-09-04 09:00:14 +00:00
|
|
|
use MediaWikiIntegrationTestCase;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @coversDefaultClass \MediaWiki\Extension\VisualEditor\DirectParsoidClient
|
|
|
|
* @group Database
|
|
|
|
*/
|
|
|
|
class DirectParsoidClientTest extends MediaWikiIntegrationTestCase {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return DirectParsoidClient
|
|
|
|
*/
|
|
|
|
private function createDirectClient(): DirectParsoidClient {
|
|
|
|
$services = $this->getServiceContainer();
|
|
|
|
$directClient = new DirectParsoidClient(
|
2022-11-18 19:46:23 +00:00
|
|
|
$services->getPageRestHelperFactory(),
|
2022-08-28 19:25:04 +00:00
|
|
|
$services->getUserFactory()->newAnonymous()
|
2022-09-04 09:00:14 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
return $directClient;
|
|
|
|
}
|
|
|
|
|
2022-12-15 17:25:38 +00:00
|
|
|
/**
|
|
|
|
* @return DirectParsoidClient
|
|
|
|
*/
|
|
|
|
private function createDirectClientWithHttpExceptionFromFactory(): DirectParsoidClient {
|
|
|
|
$factory = $this->createNoOpMock( PageRestHelperFactory::class, [
|
|
|
|
'newHtmlOutputRendererHelper',
|
|
|
|
'newHtmlInputTransformHelper',
|
|
|
|
] );
|
|
|
|
|
|
|
|
$e = new HttpException( 'testing', 400 );
|
|
|
|
$factory->method( 'newHtmlOutputRendererHelper' )->willThrowException( $e );
|
|
|
|
$factory->method( 'newHtmlInputTransformHelper' )->willThrowException( $e );
|
|
|
|
|
|
|
|
$services = $this->getServiceContainer();
|
|
|
|
$directClient = new DirectParsoidClient(
|
|
|
|
$factory,
|
|
|
|
$services->getUserFactory()->newAnonymous()
|
|
|
|
);
|
|
|
|
|
|
|
|
return $directClient;
|
|
|
|
}
|
|
|
|
|
2022-09-04 09:00:14 +00:00
|
|
|
/** @return Generator */
|
|
|
|
public function provideLanguageCodes() {
|
|
|
|
yield 'German language code' => [ 'de' ];
|
|
|
|
yield 'English language code' => [ 'en' ];
|
|
|
|
yield 'French language code' => [ 'fr' ];
|
|
|
|
yield 'No language code, fallback to en' => [ null ];
|
|
|
|
}
|
|
|
|
|
2022-09-26 20:24:14 +00:00
|
|
|
private function createLanguage( $langCode, $allowNull = false ) {
|
2022-09-04 09:00:14 +00:00
|
|
|
if ( $langCode === null ) {
|
|
|
|
$language = $this->getServiceContainer()->getContentLanguage();
|
|
|
|
$langCode = $language->getCode();
|
2022-09-26 20:24:14 +00:00
|
|
|
if ( $allowNull ) {
|
|
|
|
$language = null;
|
|
|
|
}
|
2022-09-04 09:00:14 +00:00
|
|
|
} else {
|
|
|
|
$language = $this->createNoOpMock(
|
|
|
|
Language::class,
|
|
|
|
[ 'getCode' ]
|
|
|
|
);
|
|
|
|
$language->method( 'getCode' )->willReturn( $langCode );
|
|
|
|
}
|
|
|
|
|
|
|
|
return [ $language, $langCode ];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers ::getPageHtml
|
|
|
|
* @dataProvider provideLanguageCodes
|
|
|
|
*/
|
|
|
|
public function testGetPageHtml( $langCode ) {
|
|
|
|
$directClient = $this->createDirectClient();
|
|
|
|
|
|
|
|
$revision = $this->getExistingTestPage( 'DirectParsoidClient' )
|
|
|
|
->getRevisionRecord();
|
|
|
|
|
2022-09-26 20:24:14 +00:00
|
|
|
[ $language, $langCode ] = $this->createLanguage( $langCode, true );
|
2022-09-04 09:00:14 +00:00
|
|
|
$response = $directClient->getPageHtml( $revision, $language );
|
|
|
|
|
|
|
|
$pageHtml = $response['body'];
|
|
|
|
$headers = $response['headers'];
|
|
|
|
|
|
|
|
$this->assertIsArray( $response );
|
|
|
|
$this->assertArrayHasKey( 'body', $response );
|
|
|
|
$this->assertStringContainsString( 'DirectParsoidClient', $pageHtml );
|
|
|
|
|
|
|
|
$this->assertArrayHasKey( 'headers', $response );
|
|
|
|
$this->assertSame( $langCode, $headers['content-language'] );
|
|
|
|
$this->assertStringContainsString( 'lang="' . $langCode . '"', $pageHtml );
|
|
|
|
|
|
|
|
$this->assertArrayHasKey( 'etag', $headers );
|
2022-08-28 19:25:04 +00:00
|
|
|
$this->assertStringContainsString( (string)$revision->getId(), $headers['etag'] );
|
2022-09-04 09:00:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers ::transformHTML
|
|
|
|
* @dataProvider provideLanguageCodes
|
|
|
|
*/
|
|
|
|
public function testTransformHtml( $langCode ) {
|
|
|
|
$directClient = $this->createDirectClient();
|
|
|
|
|
|
|
|
$pageIdentity = PageIdentityValue::localIdentity(
|
|
|
|
1,
|
|
|
|
NS_MAIN,
|
|
|
|
'DirectParsoidClient'
|
|
|
|
);
|
|
|
|
[ $language, ] = $this->createLanguage( $langCode );
|
|
|
|
|
|
|
|
$html = '<h2>Hello World</h2>';
|
|
|
|
$oldid = $pageIdentity->getId();
|
|
|
|
|
|
|
|
$response = $directClient->transformHTML(
|
|
|
|
$pageIdentity,
|
|
|
|
$language,
|
|
|
|
$html,
|
|
|
|
$oldid,
|
2022-08-28 19:25:04 +00:00
|
|
|
// Supplying "null" will use the $oldid and look at recent rendering in ParserCache.
|
|
|
|
null
|
2022-09-04 09:00:14 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
$this->assertIsArray( $response );
|
|
|
|
$this->assertArrayHasKey( 'headers', $response );
|
2022-08-28 19:25:04 +00:00
|
|
|
$this->assertArrayHasKey( 'Content-Type', $response['headers'] );
|
2022-09-04 09:00:14 +00:00
|
|
|
|
|
|
|
$this->assertArrayHasKey( 'body', $response );
|
|
|
|
// Trim to remove trailing newline
|
|
|
|
$wikitext = trim( $response['body'] );
|
|
|
|
$this->assertStringContainsString( '== Hello World ==', $wikitext );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers ::transformWikitext
|
|
|
|
* @dataProvider provideLanguageCodes
|
|
|
|
*/
|
|
|
|
public function testTransformWikitext( $langCode ) {
|
|
|
|
$directClient = $this->createDirectClient();
|
|
|
|
|
|
|
|
$page = $this->getExistingTestPage( 'DirectParsoidClient' );
|
2022-08-28 19:25:04 +00:00
|
|
|
$pageRecord = $page->toPageRecord();
|
2022-09-04 09:00:14 +00:00
|
|
|
$wikitext = '== Hello World ==';
|
|
|
|
[ $language, $langCode ] = $this->createLanguage( $langCode );
|
|
|
|
|
|
|
|
$response = $directClient->transformWikitext(
|
2022-08-28 19:25:04 +00:00
|
|
|
$pageRecord,
|
2022-09-04 09:00:14 +00:00
|
|
|
$language,
|
|
|
|
$wikitext,
|
|
|
|
false,
|
2022-08-28 19:25:04 +00:00
|
|
|
$pageRecord->getLatest(),
|
2022-09-04 09:00:14 +00:00
|
|
|
false
|
|
|
|
);
|
|
|
|
|
|
|
|
$this->assertIsArray( $response );
|
|
|
|
$this->assertArrayHasKey( 'body', $response );
|
|
|
|
$this->assertArrayHasKey( 'headers', $response );
|
|
|
|
|
|
|
|
$headers = $response['headers'];
|
|
|
|
$this->assertSame( $langCode, $headers['content-language'] );
|
|
|
|
|
|
|
|
$html = $response['body'];
|
|
|
|
$this->assertStringContainsString( $page->getTitle()->getText(), $html );
|
|
|
|
$this->assertStringContainsString( '>Hello World</h2>', $html );
|
|
|
|
}
|
|
|
|
|
2022-08-28 19:25:04 +00:00
|
|
|
/** @covers ::transformHTML */
|
|
|
|
public function testRoundTripSelserWithETag() {
|
|
|
|
$directClient = $this->createDirectClient();
|
|
|
|
|
|
|
|
// Nasty wikitext that would be reformated without selser.
|
|
|
|
$originalWikitext = '*a\n* b\n* <i>c</I>';
|
|
|
|
|
|
|
|
/** @var RevisionRecord $revision */
|
|
|
|
$revision = $this->editPage( 'RoundTripSelserWithETag', $originalWikitext )
|
|
|
|
->getValue()['revision-record'];
|
|
|
|
|
|
|
|
$pageHtmlResponse = $directClient->getPageHtml( $revision );
|
|
|
|
$eTag = $pageHtmlResponse['headers']['etag'];
|
|
|
|
$oldHtml = $pageHtmlResponse['body'];
|
|
|
|
$updatedHtml = str_replace( '</body>', '<p>More Text</p></body>', $oldHtml );
|
|
|
|
|
|
|
|
// Now make a new client object, so we can mock the ParsoidOutputAccess.
|
|
|
|
$parsoidOutputAccess = $this->createNoOpMock( ParsoidOutputAccess::class );
|
|
|
|
$services = $this->getServiceContainer();
|
|
|
|
$directClient = new DirectParsoidClient(
|
2022-11-18 19:46:23 +00:00
|
|
|
$services->getPageRestHelperFactory(),
|
2022-08-28 19:25:04 +00:00
|
|
|
$services->getUserFactory()->newAnonymous()
|
|
|
|
);
|
|
|
|
|
|
|
|
[ $targetLanguage, ] = $this->createLanguage( 'en' );
|
|
|
|
$transformHtmlResponse = $directClient->transformHTML(
|
|
|
|
$revision->getPage(),
|
|
|
|
$targetLanguage,
|
|
|
|
$updatedHtml,
|
|
|
|
$revision->getId(),
|
|
|
|
$eTag
|
|
|
|
);
|
|
|
|
|
|
|
|
$updatedWikitext = $transformHtmlResponse['body'];
|
|
|
|
$this->assertStringContainsString( $originalWikitext, $updatedWikitext );
|
|
|
|
$this->assertStringContainsString( 'More Text', $updatedWikitext );
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @covers ::transformHTML */
|
|
|
|
public function testRoundTripSelserWithoutETag() {
|
|
|
|
$directClient = $this->createDirectClient();
|
|
|
|
|
|
|
|
// Nasty wikitext that would be reformated without selser.
|
|
|
|
$originalWikitext = '*a\n* b\n* <i>c</I>';
|
|
|
|
|
|
|
|
/** @var RevisionRecord $revision */
|
|
|
|
$revision = $this->editPage( 'RoundTripSelserWithoutETag', $originalWikitext )
|
|
|
|
->getValue()['revision-record'];
|
|
|
|
|
|
|
|
$pageHtmlResponse = $directClient->getPageHtml( $revision );
|
|
|
|
$oldHtml = $pageHtmlResponse['body'];
|
|
|
|
$updatedHtml = str_replace( '</body>', '<p>More Text</p></body>', $oldHtml );
|
|
|
|
|
|
|
|
[ $targetLanguage, ] = $this->createLanguage( 'en' );
|
|
|
|
$transformHtmlResponse = $directClient->transformHTML(
|
|
|
|
$revision->getPage(),
|
|
|
|
$targetLanguage,
|
|
|
|
$updatedHtml,
|
|
|
|
$revision->getId(),
|
|
|
|
null
|
|
|
|
);
|
|
|
|
|
|
|
|
// Selser should still work, because the current rendering of the page still matches.
|
|
|
|
$updatedWikitext = $transformHtmlResponse['body'];
|
|
|
|
$this->assertStringContainsString( $originalWikitext, $updatedWikitext );
|
|
|
|
$this->assertStringContainsString( 'More Text', $updatedWikitext );
|
|
|
|
}
|
|
|
|
|
2022-12-15 17:25:38 +00:00
|
|
|
/**
|
|
|
|
* @covers ::getPageHtml
|
|
|
|
*/
|
|
|
|
public function testGetPageHtml_HttpException() {
|
|
|
|
$directClient = $this->createDirectClientWithHttpExceptionFromFactory();
|
|
|
|
|
|
|
|
$revision = $this->getExistingTestPage( 'DirectParsoidClient' )
|
|
|
|
->getRevisionRecord();
|
|
|
|
|
|
|
|
$response = $directClient->getPageHtml( $revision );
|
|
|
|
$this->assertArrayHasKey( 'error', $response );
|
|
|
|
$this->assertSame( 'testing', $response['error']['message'] );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers ::getPageHtml
|
|
|
|
*/
|
|
|
|
public function testTransformHtml_HttpException() {
|
|
|
|
$directClient = $this->createDirectClientWithHttpExceptionFromFactory();
|
|
|
|
|
|
|
|
$page = $this->getExistingTestPage( 'DirectParsoidClient' );
|
|
|
|
|
|
|
|
$response = $directClient->transformHTML(
|
|
|
|
$page, Language::factory( 'en' ), 'some html', null, null
|
|
|
|
);
|
|
|
|
$this->assertArrayHasKey( 'error', $response );
|
|
|
|
$this->assertSame( 'testing', $response['error']['message'] );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @covers ::getPageHtml
|
|
|
|
*/
|
|
|
|
public function testTransformWikitext_HttpException() {
|
|
|
|
$directClient = $this->createDirectClientWithHttpExceptionFromFactory();
|
|
|
|
|
|
|
|
$page = $this->getExistingTestPage( 'DirectParsoidClient' );
|
|
|
|
|
|
|
|
$response = $directClient->transformWikitext(
|
|
|
|
$page, Language::factory( 'en' ), 'some text', false, null, false
|
|
|
|
);
|
|
|
|
$this->assertArrayHasKey( 'error', $response );
|
|
|
|
$this->assertSame( 'testing', $response['error']['message'] );
|
|
|
|
}
|
|
|
|
|
2022-09-04 09:00:14 +00:00
|
|
|
}
|