wiki.techinc.nl/tests/phpunit/includes/linker/LinkRendererTest.php
Bartosz Dziewoński 6ba47296d9 Fix Phan suppressions related to Title::castFrom*() and friends
There is no way to express that Title::castFromPageIdentity(),
Title::castFromPageReference() and Title::castFromLinkTarget()
can only return null when the parameter is null. We need to add
Phan suppressions or explicit types almost everywhere that these
methods are used with parameters that are known to not be null.

Instead, introduce new methods Title::newFromPageIdentity() and
Title::newFromPageReference() (Title::newFromLinkTarget() already
exists), without the null-coalescing behavior, and use them when
the parameter is not null. This lets static analysis tools, and
humans, easily understand where nulls can't appear.

Do the same with the corresponding TitleFactory methods.

Change the obvious uses of castFrom*() to newFrom*() (if there is
a Phan suppression, a type check, or a method call on the result).

Change-Id: Ida4da75953cf3bca372a40dc88022443109ca0cb
2023-04-22 16:45:09 +02:00

253 lines
7.6 KiB
PHP

<?php
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Linker\LinkRenderer;
use MediaWiki\Linker\LinkRendererFactory;
use MediaWiki\MainConfigNames;
use MediaWiki\Page\PageReference;
use MediaWiki\Page\PageReferenceValue;
use MediaWiki\Title\Title;
/**
* @covers MediaWiki\Linker\LinkRenderer
*/
class LinkRendererTest extends MediaWikiLangTestCase {
use LinkCacheTestTrait;
/**
* @var LinkRendererFactory
*/
private $factory;
protected function setUp(): void {
parent::setUp();
$this->overrideConfigValues( [
MainConfigNames::ArticlePath => '/wiki/$1',
MainConfigNames::Server => '//example.org',
MainConfigNames::CanonicalServer => 'http://example.org',
MainConfigNames::ScriptPath => '/w',
MainConfigNames::Script => '/w/index.php',
] );
$this->factory = $this->getServiceContainer()->getLinkRendererFactory();
}
public static function provideMergeAttribs() {
yield [ new TitleValue( NS_SPECIAL, 'BlankPage' ) ];
yield [ new PageReferenceValue( NS_SPECIAL, 'BlankPage', PageReference::LOCAL ) ];
}
/**
* @dataProvider provideMergeAttribs
* @covers \MediaWiki\Linker\LinkRenderer::makeBrokenLink
*/
public function testMergeAttribs( $target ) {
$linkRenderer = $this->factory->create();
$link = $linkRenderer->makeBrokenLink( $target, null, [
// Appended to class
'class' => 'foobar',
// Suppresses href attribute
'href' => false,
// Extra attribute
'bar' => 'baz'
] );
$this->assertEquals(
'<a href="/wiki/Special:BlankPage" class="new foobar" '
. 'title="Special:BlankPage (page does not exist)" bar="baz">'
. 'Special:BlankPage</a>',
$link
);
}
public static function provideMakeKnownLink() {
yield [ new TitleValue( NS_MAIN, 'Foobar' ) ];
yield [ new PageReferenceValue( NS_MAIN, 'Foobar', PageReference::LOCAL ) ];
}
/**
* @dataProvider provideMakeKnownLink
* @covers \MediaWiki\Linker\LinkRenderer::makeKnownLink
*/
public function testMakeKnownLink( $target ) {
$linkRenderer = $this->factory->create();
// Query added
$this->assertEquals(
'<a href="/w/index.php?title=Foobar&amp;foo=bar" title="Foobar">Foobar</a>',
$linkRenderer->makeKnownLink( $target, null, [], [ 'foo' => 'bar' ] )
);
$linkRenderer->setForceArticlePath( true );
$this->assertEquals(
'<a href="/wiki/Foobar?foo=bar" title="Foobar">Foobar</a>',
$linkRenderer->makeKnownLink( $target, null, [], [ 'foo' => 'bar' ] )
);
// expand = HTTPS
$linkRenderer->setForceArticlePath( false );
$linkRenderer->setExpandURLs( PROTO_HTTPS );
$this->assertEquals(
'<a href="https://example.org/wiki/Foobar" title="Foobar">Foobar</a>',
$linkRenderer->makeKnownLink( $target )
);
}
public static function provideMakeBrokenLink() {
yield [
new TitleValue( NS_MAIN, 'Foobar' ),
new TitleValue( NS_SPECIAL, 'Foobar' )
];
yield [
new PageReferenceValue( NS_MAIN, 'Foobar', PageReference::LOCAL ),
new PageReferenceValue( NS_SPECIAL, 'Foobar', PageReference::LOCAL )
];
}
/**
* @dataProvider provideMakeBrokenLink
* @covers \MediaWiki\Linker\LinkRenderer::makeBrokenLink
*/
public function testMakeBrokenLink( $target, $special ) {
$linkRenderer = $this->factory->create();
// action=edit&redlink=1 added
$this->assertEquals(
'<a href="/w/index.php?title=Foobar&amp;action=edit&amp;redlink=1" '
. 'class="new" title="Foobar (page does not exist)">Foobar</a>',
$linkRenderer->makeBrokenLink( $target )
);
// action=edit&redlink=1 not added due to action query parameter
$this->assertEquals(
'<a href="/w/index.php?title=Foobar&amp;action=foobar" class="new" '
. 'title="Foobar (page does not exist)">Foobar</a>',
$linkRenderer->makeBrokenLink( $target, null, [], [ 'action' => 'foobar' ] )
);
// action=edit&redlink=1 not added due to NS_SPECIAL
$this->assertEquals(
'<a href="/wiki/Special:Foobar" class="new" title="Special:Foobar '
. '(page does not exist)">Special:Foobar</a>',
$linkRenderer->makeBrokenLink( $special )
);
// fragment stripped
if ( $target instanceof LinkTarget ) {
$this->assertEquals(
'<a href="/w/index.php?title=Foobar&amp;action=foobar" class="new" '
. 'title="Foobar (page does not exist)">Foobar</a>',
$linkRenderer->makeBrokenLink( $target->createFragmentTarget( 'foobar' ) )
);
}
}
public static function provideMakeLink() {
yield [
new TitleValue( NS_SPECIAL, 'Foobar' ),
new TitleValue( NS_SPECIAL, 'BlankPage' )
];
yield [
new PageReferenceValue( NS_SPECIAL, 'Foobar', PageReference::LOCAL ),
new PageReferenceValue( NS_SPECIAL, 'BlankPage', PageReference::LOCAL )
];
}
/**
* @dataProvider provideMakeLink
* @covers \MediaWiki\Linker\LinkRenderer::makeLink
*/
public function testMakeLink( $foobar, $blankpage ) {
$linkRenderer = $this->factory->create();
$this->assertEquals(
'<a href="/wiki/Special:Foobar" class="new" title="Special:Foobar '
. '(page does not exist)">foo</a>',
$linkRenderer->makeLink( $foobar, 'foo' )
);
$this->assertEquals(
'<a href="/wiki/Special:BlankPage" title="Special:BlankPage">blank</a>',
$linkRenderer->makeLink( $blankpage, 'blank' )
);
$this->assertEquals(
'<a href="/wiki/Special:Foobar" class="new" title="Special:Foobar '
. '(page does not exist)">&lt;script&gt;evil()&lt;/script&gt;</a>',
$linkRenderer->makeLink( $foobar, '<script>evil()</script>' )
);
$this->assertEquals(
'<a href="/wiki/Special:Foobar" class="new" title="Special:Foobar '
. '(page does not exist)"><script>evil()</script></a>',
$linkRenderer->makeLink( $foobar, new HtmlArmor( '<script>evil()</script>' ) )
);
$this->assertEquals(
'<a href="#fragment">fragment</a>',
$linkRenderer->makeLink( Title::newFromText( '#fragment' ) )
);
}
public static function provideGetLinkClasses() {
yield [
new TitleValue( NS_MAIN, 'FooBar' ),
new TitleValue( NS_MAIN, 'Redirect' ),
new TitleValue( NS_USER, 'Someuser' )
];
yield [
new PageReferenceValue( NS_MAIN, 'FooBar', PageReference::LOCAL ),
new PageReferenceValue( NS_MAIN, 'Redirect', PageReference::LOCAL ),
new PageReferenceValue( NS_USER, 'Someuser', PageReference::LOCAL )
];
}
/**
* @dataProvider provideGetLinkClasses
* @covers \MediaWiki\Linker\LinkRenderer::getLinkClasses
*/
public function testGetLinkClasses( $foobarTitle, $redirectTitle, $userTitle ) {
$services = $this->getServiceContainer();
$titleFormatter = $services->getTitleFormatter();
$specialPageFactory = $services->getSpecialPageFactory();
$hookContainer = $services->getHookContainer();
$linkCache = $services->getLinkCache();
if ( $foobarTitle instanceof PageReference ) {
$cacheTitle = Title::newFromPageReference( $foobarTitle );
} else {
$cacheTitle = $foobarTitle;
}
$this->addGoodLinkObject( 1, $cacheTitle, 10, 0 );
if ( $redirectTitle instanceof PageReference ) {
$cacheTitle = Title::newFromPageReference( $redirectTitle );
} else {
$cacheTitle = $redirectTitle;
}
$this->addGoodLinkObject( 2, $cacheTitle, 10, 1 );
if ( $userTitle instanceof PageReference ) {
$cacheTitle = Title::newFromPageReference( $userTitle );
} else {
$cacheTitle = $userTitle;
}
$this->addGoodLinkObject( 3, $cacheTitle, 10, 0 );
$linkRenderer = new LinkRenderer(
$titleFormatter,
$linkCache,
$specialPageFactory,
$hookContainer,
new ServiceOptions( LinkRenderer::CONSTRUCTOR_OPTIONS, [ 'renderForComment' => false ] )
);
$this->assertSame(
'',
$linkRenderer->getLinkClasses( $foobarTitle )
);
$this->assertEquals(
'mw-redirect',
$linkRenderer->getLinkClasses( $redirectTitle )
);
}
protected function tearDown(): void {
Title::clearCaches();
parent::tearDown();
}
}