Completed
Push — master ( 951284...b5c57e )
by
unknown
06:36 queued 11s
created

Hooks/ParserOutputUpdateHookHandlerTest.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace Wikibase\Client\Tests\Integration\Hooks;
6
7
use HashSiteStore;
8
use Language;
9
use MediaWiki\HookContainer\HookContainer;
10
use MediaWikiIntegrationTestCase;
11
use MediaWikiSite;
12
use ParserOutput;
13
use Psr\Log\NullLogger;
14
use Site;
15
use SiteLookup;
16
use Title;
17
use Wikibase\Client\Hooks\LangLinkHandlerFactory;
18
use Wikibase\Client\Hooks\LanguageLinkBadgeDisplay;
19
use Wikibase\Client\Hooks\OtherProjectsSidebarGeneratorFactory;
20
use Wikibase\Client\Hooks\ParserOutputUpdateHookHandler;
21
use Wikibase\Client\Hooks\SidebarLinkBadgeDisplay;
22
use Wikibase\Client\NamespaceChecker;
23
use Wikibase\Client\ParserOutput\ClientParserOutputDataUpdater;
24
use Wikibase\Client\Usage\EntityUsage;
25
use Wikibase\Client\Usage\EntityUsageFactory;
26
use Wikibase\DataModel\Entity\BasicEntityIdParser;
27
use Wikibase\DataModel\Entity\Item;
28
use Wikibase\DataModel\Entity\ItemId;
29
use Wikibase\DataModel\Entity\PropertyId;
30
use Wikibase\DataModel\Services\Lookup\InMemoryEntityLookup;
31
use Wikibase\DataModel\Services\Lookup\LabelDescriptionLookup;
32
use Wikibase\DataModel\SiteLink;
33
use Wikibase\DataModel\SiteLinkList;
34
use Wikibase\DataModel\Snak\PropertyValueSnak;
35
use Wikibase\DataModel\Statement\Statement;
36
use Wikibase\DataModel\Statement\StatementList;
37
use Wikibase\DataModel\Term\Term;
38
use Wikibase\Lib\DataValue\UnmappedEntityIdValue;
39
use Wikibase\Lib\SettingsArray;
40
use Wikibase\Lib\Store\SiteLinkLookup;
41
use Wikibase\Lib\Tests\MockRepository;
42
43
/**
44
 * @covers \Wikibase\Client\Hooks\ParserOutputUpdateHookHandler
45
 *
46
 * @group WikibaseClient
47
 * @group Wikibase
48
 * @group WikibaseHooks
49
 *
50
 * @license GPL-2.0-or-later
51
 * @author Daniel Kinzler
52
 */
53
class ParserOutputUpdateHookHandlerTest extends MediaWikiIntegrationTestCase {
54
55
	/**
56
	 * @param string $globalId
57
	 * @param string $group
58
	 * @param string $languageCode
59
	 *
60
	 * @return Site
61
	 */
62
	private function newSite( $globalId, $group, $languageCode ) {
63
		$site = new MediaWikiSite();
64
		$site->setGlobalId( $globalId );
65
		$site->setGroup( $group );
66
		$site->setLanguageCode( $languageCode );
67
		$site->addNavigationId( $languageCode );
68
		$site->setPagePath( 'wiki/' );
69
		$site->setFilePath( 'w/' );
70
		$site->setLinkPath( 'http://' . $globalId . '.test.com/wiki/$1' );
71
72
		return $site;
73
	}
74
75
	/**
76
	 * @return SiteLookup
77
	 */
78
	private function getSiteLookup() {
79
		$siteLookup = new HashSiteStore( [
80
			$this->newSite( 'wikidatawiki', 'wikidata', 'en' ),
81
			$this->newSite( 'commonswiki', 'commons', 'en' ),
82
			$this->newSite( 'enwiki', 'wikipedia', 'en' ),
83
			$this->newSite( 'dewiki', 'wikipedia', 'de' ),
84
		] );
85
86
		return $siteLookup;
87
	}
88
89
	private function getBadgeItem() {
90
		$item = new Item( new ItemId( 'Q17' ) );
91
		$item->setLabel( 'de', 'exzellent' );
92
		$item->setLabel( 'en', 'featured' );
93
94
		return $item;
95
	}
96
97
	private function getTestSiteLinkData() {
98
		$badgeId = $this->getBadgeItem()->getId();
99
100
		return [
101
			'Q1' => [
102
				new SiteLink( 'dewiki', 'Sauerstoff', [ $badgeId ] ),
0 ignored issues
show
array($badgeId) is of type array<integer,object<Wik...Entity\\ItemId>|null"}>, but the function expects a object<Wikibase\DataMode...el\Entity\ItemId>>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
103
				new SiteLink( 'enwiki', 'Oxygen' ),
104
				new SiteLink( 'commonswiki', 'Oxygen' ),
105
			],
106
		];
107
	}
108
109
	private function getTestSiteLinkDataInNotEnabledNamespace() {
110
		return [
111
			'Q7' => [
112
				new SiteLink( 'dewiki', 'User:Foo' ),
113
				new SiteLink( 'enwiki', 'User:Foo' ),
114
				new SiteLink( 'commonswiki', 'User:Foo' ),
115
			],
116
		];
117
	}
118
119
	/**
120
	 * @param ItemId $id
121
	 * @param SiteLink[] $links
122
	 *
123
	 * @return Item
124
	 */
125
	private function newItem( ItemId $id, array $links ) {
126
		$item = new Item( $id );
127
		$item->setSiteLinkList( new SiteLinkList( $links ) );
128
		return $item;
129
	}
130
131
	/**
132
	 * @param array[] $links
133
	 *
134
	 * @return MockRepository
135
	 */
136
	private function getMockRepo( array $links ) {
137
		$repo = new MockRepository();
138
139
		foreach ( $links as $itemKey => $itemLinks ) {
140
			$itemId = new ItemId( $itemKey );
141
			$item = $this->newItem( $itemId, $itemLinks );
142
			$repo->putEntity( $item );
143
		}
144
145
		return $repo;
146
	}
147
148
	/**
149
	 * @return SettingsArray
150
	 */
151
	private function newSettings() {
152
		$defaults = [
153
			'siteGlobalID' => 'enwiki',
154
			'languageLinkSiteGroup' => 'wikipedia',
155
			'namespaces' => [ NS_MAIN, NS_CATEGORY ],
156
			'otherProjectsLinks' => [ 'commonswiki' ],
157
		];
158
159
		return new SettingsArray( $defaults );
160
	}
161
162
	private function newParserOutputUpdateHookHandler( array $siteLinkData ) {
163
		$settings = $this->newSettings();
164
165
		$namespaceChecker = $this->newNamespaceChecker();
166
167
		$mockRepo = $this->getMockRepo( $siteLinkData );
168
		$mockRepo->putEntity( $this->getBadgeItem() );
169
170
		$parserOutputDataUpdater = $this->newParserOutputDataUpdater( $mockRepo, $siteLinkData, $settings );
171
172
		$langLinkHandlerFactory = $this->newLangLinkHandlerFactory( $namespaceChecker, $mockRepo, $settings );
173
174
		return new ParserOutputUpdateHookHandler(
175
			$namespaceChecker,
176
			$langLinkHandlerFactory,
177
			$parserOutputDataUpdater,
178
			new EntityUsageFactory( new BasicEntityIdParser() )
179
		);
180
	}
181
182
	private function getBadgeDisplay() {
183
		$labelDescriptionLookup = $this->createMock( LabelDescriptionLookup::class );
184
185
		$labelDescriptionLookup->expects( $this->any() )
186
			->method( 'getLabel' )
187
			->will( $this->returnValue( new Term( 'en', 'featured' ) ) );
188
189
		return new LanguageLinkBadgeDisplay(
190
			new SidebarLinkBadgeDisplay(
191
				$labelDescriptionLookup,
192
				[ 'Q17' => 'featured' ],
193
				Language::factory( 'en' )
194
			)
195
		);
196
	}
197
198
	private function getEntityLookup( array $siteLinkData ) {
199
		$lookup = new InMemoryEntityLookup();
200
201
		foreach ( $siteLinkData as $itemId => $siteLinks ) {
202
			$item = new Item( new ItemId( $itemId ) );
203
			$item->setSiteLinkList( new SiteLinkList( $siteLinks ) );
204
			$lookup->addEntity( $item );
205
		}
206
207
		return $lookup;
208
	}
209
210
	private function getOtherProjectsSidebarGeneratorFactory(
211
		SettingsArray $settings,
212
		SiteLinkLookup $siteLinkLookup,
213
		array $siteLinkData
214
	) {
215
		$sidebarLinkBadgeDisplay = new SidebarLinkBadgeDisplay(
216
			$this->createMock( LabelDescriptionLookup::class ),
217
			[],
218
			Language::factory( 'en' )
219
		);
220
221
		return new OtherProjectsSidebarGeneratorFactory(
222
			$settings,
223
			$siteLinkLookup,
224
			$this->getSiteLookup(),
225
			$this->getEntityLookup( $siteLinkData ),
226
			$sidebarLinkBadgeDisplay,
227
			$this->createMock( HookContainer::class ),
228
			new NullLogger()
229
		);
230
	}
231
232
	private function newParserOutput( array $pageProps, array $extensionData ) {
233
		$parserOutput = new ParserOutput();
234
235
		foreach ( $pageProps as $name => $value ) {
236
			$parserOutput->setProperty( $name, $value );
237
		}
238
239
		foreach ( $extensionData as $key => $value ) {
240
			$parserOutput->setExtensionData( $key, $value );
241
		}
242
243
		return $parserOutput;
244
	}
245
246
	public function parserAfterParseProvider() {
247
		$commonsOxygen = [
248
			'msg' => 'wikibase-otherprojects-commons',
249
			'class' => 'wb-otherproject-link wb-otherproject-commons',
250
			'href' => 'http://commonswiki.test.com/wiki/Oxygen',
251
			'hreflang' => 'en',
252
		];
253
254
		$badgesQ1 = [
255
			'class' => 'badge-Q17 featured',
256
			'label' => 'featured',
257
		];
258
259
		return [
260
			'repo-links' => [
261
				Title::makeTitle( NS_MAIN, 'Oxygen' ),
262
				'Q1',
263
				[],
264
				[ 'de:Sauerstoff' ],
265
				[ $commonsOxygen ],
266
				[ 'de' => $badgesQ1 ],
267
			],
268
269
			'noexternallanglinks=*' => [
270
				Title::makeTitle( NS_MAIN, 'Oxygen' ),
271
				'Q1',
272
				[ 'noexternallanglinks' => serialize( [ '*' ] ) ],
273
				[],
274
				[ $commonsOxygen ],
275
				null,
276
			],
277
278
			'noexternallanglinks=de' => [
279
				Title::makeTitle( NS_MAIN, 'Oxygen' ),
280
				'Q1',
281
				[ 'noexternallanglinks' => serialize( [ 'de' ] ) ],
282
				[],
283
				[ $commonsOxygen ],
284
				[],
285
			],
286
287
			'noexternallanglinks=ja' => [
288
				Title::makeTitle( NS_MAIN, 'Oxygen' ),
289
				'Q1',
290
				[ 'noexternallanglinks' => serialize( [ 'ja' ] ) ],
291
				[ 'de:Sauerstoff' ],
292
				[ $commonsOxygen ],
293
				[ 'de' => $badgesQ1 ],
294
			],
295
		];
296
	}
297
298
	/**
299
	 * @dataProvider parserAfterParseProvider
300
	 */
301
	public function testDoContentAlterParserOutput(
302
		Title $title,
303
		$expectedItem,
304
		array $pagePropsBefore,
305
		array $expectedLanguageLinks,
306
		array $expectedSisterLinks,
307
		array $expectedBadges = null
308
	) {
309
		$parserOutput = $this->newParserOutput( $pagePropsBefore, [] );
310
		$handler = $this->newParserOutputUpdateHookHandler( $this->getTestSiteLinkData() );
311
312
		$handler->doContentAlterParserOutput( $title, $parserOutput );
313
314
		$expectedUsage = new EntityUsage(
315
			new ItemId( $expectedItem ),
316
			EntityUsage::SITELINK_USAGE
317
		);
318
319
		$this->assertEquals( $expectedItem, $parserOutput->getProperty( 'wikibase_item' ) );
320
		$this->assertLanguageLinks( $expectedLanguageLinks, $parserOutput );
321
		$this->assertSisterLinks( $expectedSisterLinks, $parserOutput->getExtensionData( 'wikibase-otherprojects-sidebar' ) );
322
323
		$actualUsage = $parserOutput->getExtensionData( 'wikibase-entity-usage' );
324
325
		$this->assertEquals( [ $expectedUsage->getIdentityString() ], array_keys( $actualUsage ) );
326
327
		$actualBadges = $parserOutput->getExtensionData( 'wikibase_badges' );
328
329
		// $actualBadges contains info arrays, these are checked by LanguageLinkBadgeDisplayTest and LangLinkHandlerTest
330
		$this->assertSame( $expectedBadges, $actualBadges );
331
	}
332
333
	public function testDoContentAlterParserOutput_sitelinkOfNoItem() {
334
		$title = Title::makeTitle( NS_MAIN, 'Plutonium' );
335
336
		$parserOutput = $this->newParserOutput( [], [] );
337
		$handler = $this->newParserOutputUpdateHookHandler( $this->getTestSiteLinkData() );
338
339
		$handler->doContentAlterParserOutput( $title, $parserOutput );
340
341
		$this->assertFalse( $parserOutput->getProperty( 'wikibase_item' ) );
342
343
		$this->assertSame( [], $parserOutput->getLanguageLinks() );
344
		$this->assertSame( [], $parserOutput->getExtensionData( 'wikibase-otherprojects-sidebar' ) );
345
346
		$this->assertNull( $parserOutput->getExtensionData( 'wikibase-entity-usage' ) );
347
		$this->assertSame( [], $parserOutput->getExtensionData( 'wikibase_badges' ) );
348
	}
349
350
	public function testDoContentAlterParserOutput_sitelinkInNotWikibaseEnabledNamespace() {
351
		$title = Title::makeTitle( NS_USER, 'Foo' );
352
353
		$parserOutput = $this->newParserOutput( [], [] );
354
		$handler = $this->newParserOutputUpdateHookHandler( $this->getTestSiteLinkDataInNotEnabledNamespace() );
355
356
		$handler->doContentAlterParserOutput( $title, $parserOutput );
357
358
		$this->assertFalse( $parserOutput->getProperty( 'wikibase_item' ) );
359
360
		$this->assertSame( [], $parserOutput->getLanguageLinks() );
361
		$this->assertNull( $parserOutput->getExtensionData( 'wikibase-otherprojects-sidebar' ) );
362
363
		$this->assertNull( $parserOutput->getExtensionData( 'wikibase-entity-usage' ) );
364
		$this->assertNull( $parserOutput->getExtensionData( 'wikibase_badges' ) );
365
	}
366
367
	public function testGivenSitelinkHasStatementWithUnknownEntityType_linkDataIsAddedNormally() {
368
		$itemId = 'Q555';
369
		$siteLink = new SiteLink( 'enwiki', 'Foobarium' );
370
371
		$namespaceChecker = $this->newNamespaceChecker();
372
373
		$mockRepo = new MockRepository();
374
		$item = new Item(
375
			new ItemId( $itemId ),
376
			null,
377
			new SiteLinkList( [ $siteLink ] ),
378
			new StatementList( [
379
				new Statement( new PropertyValueSnak(
380
					new PropertyId( 'P100' ),
381
					new UnmappedEntityIdValue( 'X808' )
382
				) )
383
			] )
384
		);
385
		$mockRepo->putEntity( $item );
386
387
		$langLinkHandlerFactory = $this->newLangLinkHandlerFactory( $namespaceChecker, $mockRepo );
388
389
		$handler = new ParserOutputUpdateHookHandler(
390
			$namespaceChecker,
391
			$langLinkHandlerFactory,
392
			$this->newParserOutputDataUpdater( $mockRepo, [ $itemId => [ $siteLink ] ] ),
393
			$this->createMock( EntityUsageFactory::class )
394
		);
395
396
		$title = Title::makeTitle( NS_MAIN, 'Foobarium' );
397
398
		$parserOutput = $this->newParserOutput( [], [] );
399
400
		$handler->doContentAlterParserOutput( $title, $parserOutput );
401
402
		$this->assertEquals( $itemId, $parserOutput->getProperty( 'wikibase_item' ) );
403
	}
404
405
	private function assertLanguageLinks( $links, ParserOutput $parserOutput ) {
406
		$this->assertIsArray( $links );
407
408
		$actualLinks = $parserOutput->getLanguageLinks();
409
410
		foreach ( $links as $link ) {
411
			$this->assertContains( $link, $actualLinks, 'LanguageLink: ' );
412
		}
413
414
		$this->assertSameSize( $links, $actualLinks, 'Unmatched languageLinks!' );
415
	}
416
417
	private function assertSisterLinks( $expectedLinks, $actualLinks ) {
418
		if ( !is_array( $expectedLinks ) ) {
419
			$this->assertEquals( $expectedLinks, $actualLinks );
420
			return;
421
		}
422
423
		$this->assertSameSize( $expectedLinks, $actualLinks, 'SisterLinks' );
424
425
		$actual = reset( $actualLinks );
426
		foreach ( $expectedLinks as $expected ) {
427
			$this->assertEquals( $expected, $actual, 'SisterLink: ' );
428
			$actual = next( $actualLinks );
429
		}
430
	}
431
432
	private function newNamespaceChecker() {
433
		$settings = $this->newSettings();
434
435
		$namespaces = $settings->getSetting( 'namespaces' );
436
		$namespaceChecker = new NamespaceChecker( [], $namespaces );
437
438
		return $namespaceChecker;
439
	}
440
441
	private function newParserOutputDataUpdater( MockRepository $mockRepo, array $siteLinkData ) {
442
		$settings = $this->newSettings();
443
444
		return new ClientParserOutputDataUpdater(
445
			$this->getOtherProjectsSidebarGeneratorFactory( $settings, $mockRepo, $siteLinkData ),
446
			$mockRepo,
447
			$mockRepo,
448
			new EntityUsageFactory( new BasicEntityIdParser() ),
449
			$settings->getSetting( 'siteGlobalID' )
450
		);
451
	}
452
453
	private function newLangLinkHandlerFactory( $namespaceChecker, $mockRepo ) {
454
		$settings = $this->newSettings();
455
456
		return new LangLinkHandlerFactory(
457
			$this->getBadgeDisplay(),
458
			$namespaceChecker,
459
			$mockRepo,
460
			$mockRepo,
461
			$this->getSiteLookup(),
462
			$this->createMock( HookContainer::class ),
463
			new NullLogger(),
464
			$settings->getSetting( 'siteGlobalID' ),
465
			$settings->getSetting( 'languageLinkSiteGroup' )
466
		);
467
	}
468
469
}
470