3 use MediaWiki\Context\RequestContext
;
4 use MediaWiki\MainConfigNames
;
5 use MediaWiki\MainConfigSchema
;
6 use MediaWiki\Message\Message
;
7 use MediaWiki\Page\ParserOutputAccess
;
8 use MediaWiki\Parser\ParserOptions
;
9 use MediaWiki\Parser\ParserOutput
;
10 use MediaWiki\Status\Status
;
11 use MediaWiki\Title\Title
;
12 use MediaWiki\User\User
;
17 class ArticleTest
extends \MediaWikiIntegrationTestCase
{
21 * @param User|null $user
25 private function newArticle( Title
$title, ?User
$user = null ): Article
{
27 $user = $this->getTestUser()->getUser();
30 $context = new RequestContext();
31 $article = new Article( $title );
32 $context->setUser( $user );
33 $context->setTitle( $title );
34 $article->setContext( $context );
40 * @covers \Article::__sleep
42 public function testSerialization_fails() {
43 $article = new Article( Title
::newMainPage() );
45 $this->expectException( LogicException
::class );
46 serialize( $article );
50 * Tests that missing article page shows parser contents
51 * of the well-known system message for NS_MEDIAWIKI pages
52 * @covers \Article::showMissingArticle
54 public function testMissingArticleMessage() {
55 // Use a well-known system message
56 $title = Title
::makeTitle( NS_MEDIAWIKI
, 'Uploadedimage' );
57 $article = $this->newArticle( $title );
59 $article->showMissingArticle();
60 $output = $article->getContext()->getOutput();
61 $this->assertStringContainsString(
62 Message
::newFromKey( 'uploadedimage' )->parse(),
68 * Test if patrol footer is possible to show
69 * @covers \Article::showPatrolFooter
70 * @dataProvider provideShowPatrolFooter
72 public function testShowPatrolFooter( $group, $title, $editPageText, $isEditedBySameUser, $expectedResult ) {
73 $testPage = $this->getNonexistingTestPage( $title );
74 $user1 = $this->getTestUser( $group )->getUser();
75 $user2 = $this->getTestUser()->getUser();
76 if ( $editPageText !== null ) {
77 $editedUser = $isEditedBySameUser ?
$user1 : $user2;
78 $editIsGood = $this->editPage( $testPage, $editPageText, '', NS_MAIN
, $editedUser )->isGood();
79 $this->assertTrue( $editIsGood, 'edited a page' );
82 $article = $this->newArticle( $title, $user1 );
83 $this->assertSame( $expectedResult, $article->showPatrolFooter() );
86 public static function provideShowPatrolFooter() {
87 yield
'UserAllowedRevExist' => [
89 Title
::makeTitle( NS_MAIN
, 'Page1' ),
95 yield
'UserNotAllowedRevExist' => [
97 Title
::makeTitle( NS_MAIN
, 'Page2' ),
103 yield
'UserAllowedNoRev' => [
105 Title
::makeTitle( NS_MAIN
, 'Page3' ),
111 yield
'UserAllowedRevExistBySameUser' => [
113 Title
::makeTitle( NS_MAIN
, 'Page4' ),
121 * Show patrol footer even if the page was moved (T162871).
123 * @covers \Article::showPatrolFooter
125 public function testShowPatrolFooterMovedPage() {
126 $oldTitle = Title
::makeTitle( NS_USER
, 'NewDraft' );
127 $newTitle = Title
::makeTitle( NS_MAIN
, 'NewDraft' );
128 $editor = $this->getTestUser()->getUser();
130 $editIsGood = $this->editPage( $oldTitle, 'Content', '', NS_USER
, $editor )->isGood();
131 $this->assertTrue( $editIsGood, 'edited a page' );
133 $status = $this->getServiceContainer()
134 ->getMovePageFactory()
135 ->newMovePage( $oldTitle, $newTitle )
136 ->move( $this->getTestUser()->getUser() );
137 $this->assertTrue( $status->isOK() );
139 $sysop = $this->getTestUser( 'sysop' )->getUser();
140 $article = $this->newArticle( $newTitle, $sysop );
142 $this->assertTrue( $article->showPatrolFooter() );
146 * Ensure that content that is present in the parser cache will be used.
148 * @covers \Article::generateContentOutput
150 public function testUsesCachedOutput() {
151 $title = $this->getExistingTestPage()->getTitle();
153 $parserOutputAccess = $this->createNoOpMock( ParserOutputAccess
::class, [ 'getCachedParserOutput' ] );
154 $parserOutputAccess->method( 'getCachedParserOutput' )
155 ->willReturn( new ParserOutput( 'Kittens' ) );
157 $this->setService( 'ParserOutputAccess', $parserOutputAccess );
159 $article = $this->newArticle( $title );
161 $this->assertStringContainsString( 'Kittens', $article->getContext()->getOutput()->getHTML() );
165 * Ensure that content that is present in the parser cache will be used.
167 * @covers \Article::generateContentOutput
169 public function testOutputIsCached() {
170 $this->overrideConfigValue(
171 MainConfigNames
::ParsoidCacheConfig
,
172 [ 'WarmParsoidParserCache' => true ]
173 + MainConfigSchema
::getDefaultValue( MainConfigNames
::ParsoidCacheConfig
)
175 $title = $this->getExistingTestPage()->getTitle();
176 // Run any jobs enqueued by the creation of the test page
177 $this->runJobs( [ 'minJobs' => 0 ] );
179 $beforePreWarm = true;
180 $parserOutputAccess = $this->createNoOpMock(
181 ParserOutputAccess
::class,
182 [ 'getCachedParserOutput', 'getParserOutput', ]
184 $parserOutputAccess->method( 'getCachedParserOutput' )
185 ->willReturn( null );
187 ->expects( $this->exactly( 2 ) ) // This is the key assertion in this test case.
188 ->method( 'getParserOutput' )
191 $this->callback( function ( ParserOptions
$parserOptions ) use ( &$beforePreWarm ) {
192 $expectedReason = $beforePreWarm ?
'page-view' : 'view';
193 $this->assertSame( $expectedReason, $parserOptions->getRenderReason() );
197 $this->callback( function ( $options ) use ( &$beforePreWarm ) {
198 if ( $beforePreWarm ) {
199 $this->assertTrue( (bool)( $options & ParserOutputAccess
::OPT_NO_CHECK_CACHE
),
200 "The cache is not checked again" );
201 $this->assertTrue( (bool)( $options & ParserOutputAccess
::OPT_LINKS_UPDATE
),
202 "WikiPage::triggerOpportunisticLinksUpdate is attempted" );
207 ->willReturnCallback( static function ( $page, $parserOptions, $revision, $options ) use ( &$beforePreWarm ) {
208 $content = $beforePreWarm ?
'Old Kittens' : 'New Kittens';
209 return Status
::newGood( new ParserOutput( $content ) );
212 $this->setService( 'ParserOutputAccess', $parserOutputAccess );
214 $article = $this->newArticle( $title );
217 $beforePreWarm = false;
218 $this->runJobs( [ 'minJobs' => 1, 'maxJobs' => 1 ], [ 'type' => 'parsoidCachePrewarm' ] );
220 // This is just a sanity check, not the key assertion.
221 $this->assertStringContainsString( 'Old Kittens', $article->getContext()->getOutput()->getHTML() );
225 * Ensure that protection indicators are shown when the page is protected.
226 * @covers \Article::showProtectionIndicator
228 public function testShowProtectionIndicator() {
229 $this->overrideConfigValue(
230 MainConfigNames
::EnableProtectionIndicators
,
233 $title = $this->getExistingTestPage()->getTitle();
234 $article = $this->newArticle( $title );
236 $wikiPage = new WikiPage( $title );
238 $wikiPage->doUpdateRestrictions( [
239 'edit' => 'autoconfirmed',
241 [ 'edit' => 'infinity' ],
244 $this->getTestSysop()->getUser()
247 $article->showProtectionIndicator();
248 $output = $article->getContext()->getOutput();
249 $this->assertArrayHasKey( 'protection-autoconfirmed', $output->getIndicators(), 'Protection indicators are shown when a page is protected' );
251 $templateTitle = Title
::newFromText( 'CascadeProtectionTest', NS_TEMPLATE
);
252 $this->editPage( $templateTitle, 'Some text here', 'Test', NS_TEMPLATE
, $this->getTestSysop()->getUser() );
253 $articleTitle = $this->getExistingTestPage()->getTitle();
254 $this->editPage( $articleTitle, '{{CascadeProtectionTest}}', 'Test', NS_MAIN
, $this->getTestSysop()->getUser() );
255 $wikiPage = new WikiPage( $articleTitle );
257 $wikiPage->doUpdateRestrictions( [
260 [ 'edit' => 'infinity' ],
263 $this->getTestSysop()->getUser()
266 $template = $this->newArticle( $templateTitle );
268 $template->showProtectionIndicator();
269 $output = $template->getContext()->getOutput();
270 $this->assertArrayHasKey(
271 'protection-sysop-cascade',
272 $output->getIndicators(),
273 'Protection indicators are shown when a page protected using cascade protection'