Completed
Push — master ( 7aacb2...2b39b6 )
by
unknown
06:55
created

HookRegistryTest::testCanInitializeApplications()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace BootstrapComponents\Tests\Unit;
4
5
use BootstrapComponents\ParserOutputHelper;
6
use BootstrapComponents\HookRegistry as HookRegistry;
7
use \PHPUnit_Framework_TestCase;
8
9
/**
10
 * @covers  \BootstrapComponents\HookRegistry
11
 *
12
 * @ingroup Test
13
 *
14
 * @group   extension-bootstrap-components
15
 * @group   mediawiki-databaseless
16
 *
17
 * @license GNU GPL v3+
18
 *
19
 * @since   1.0
20
 * @author  Tobias Oetterer
21
 */
22
class HookRegistryTest extends PHPUnit_Framework_TestCase {
23
	/**
24
	 * @throws \ConfigException
25
	 * @throws \MWException
26
	 */
27
	public function testCanConstruct() {
28
29
		$this->assertInstanceOf(
30
			'BootstrapComponents\\HookRegistry',
31
			new HookRegistry()
32
		);
33
	}
34
35
	/**
36
	 * @param string[] $hookList
37
	 *
38
	 * @throws \ConfigException
39
	 * @throws \MWException
40
	 *
41
	 * @dataProvider buildHookCallbackListForProvider
42
	 */
43
	public function testCanBuildHookCallbackListFor( $hookList ) {
44
45
		$instance = new HookRegistry();
46
47
		$hookCallbackList = $instance->buildHookCallbackListFor( $hookList );
48
		list ( $expectedHookList, $invertedHookList ) = $this->buildHookListsForCanBuildHookListCheck( $hookList );
49
50
		foreach ( $expectedHookList as $hook ) {
51
			$this->doTestHookIsRegistered( $instance, $hookCallbackList, $hook, false );
52
		}
53
		foreach ( $invertedHookList as $hook ) {
54
			$this->doTestHookIsNotRegistered( $hookCallbackList, $hook );
55
		}
56
	}
57
58
	/**
59
	 * @throws \ConfigException
60
	 * @throws \MWException
61
	 */
62
	public function testCanClear() {
63
64
		$instance = new HookRegistry();
65
		$instance->register(
66
			$instance->buildHookCallbackListFor( HookRegistry::AVAILABLE_HOOKS )
67
		);
68
		foreach ( HookRegistry::AVAILABLE_HOOKS as $hook ) {
69
			$this->assertTrue(
70
				$instance->isRegistered( $hook ),
71
				'Hook ' . $hook . ' is not registered!'
72
			);
73
		}
74
		$instance->clear();
75
		foreach ( [ 'GalleryGetModes', 'ImageBeforeProduceHTML' ] as $hook ) {
76
			$this->assertTrue(
77
				!$instance->isRegistered( $hook ),
78
				'Hook ' . $hook . ' is still registered!'
79
			);
80
		}
81
	}
82
83
	/**
84
	 * @param string[] $listOfConfigSettingsSet
85
	 * @param string[] $expectedHookList
86
	 *
87
	 * @throws \ConfigException
88
	 * @throws \MWException
89
	 *
90
	 * @dataProvider hookRegistryProvider
91
	 */
92
	public function testCanCompileRequestedHooksListFor( $listOfConfigSettingsSet, $expectedHookList ) {
93
		$myConfig = $this->getMockBuilder( 'Config' )
94
			->disableOriginalConstructor()
95
			->getMock();
96
		$myConfig->expects( $this->any() )
97
			->method( 'has' )
98
			->will( $this->returnCallback(
99
				function( $configSetting ) use ( $listOfConfigSettingsSet )
100
				{
101
					return in_array( $configSetting, $listOfConfigSettingsSet );
102
				}
103
			) );
104
		$myConfig->expects( $this->any() )
105
			->method( 'get' )
106
			->will( $this->returnCallback(
107
				function( $configSetting ) use ( $listOfConfigSettingsSet )
108
				{
109
					return in_array( $configSetting, $listOfConfigSettingsSet );
110
				}
111
			) );
112
113
		$instance = new HookRegistry();
114
115
		/** @noinspection PhpParamsInspection */
116
		$compiledHookList = $instance->compileRequestedHooksListFor( $myConfig );
117
118
		$this->assertEquals(
119
			$expectedHookList,
120
			$compiledHookList
121
		);
122
	}
123
124
	/**
125
	 * @throws \ConfigException
126
	 * @throws \MWException
127
	 */
128
	public function testCanGetCompleteHookDefinitionList() {
129
130
		$myConfig = $this->getMockBuilder( 'Config' )
131
			->disableOriginalConstructor()
132
			->getMock();
133
		$componentLibrary = $this->getMockBuilder( 'BootstrapComponents\\ComponentLibrary' )
134
			->disableOriginalConstructor()
135
			->getMock();
136
		$nestingController = $this->getMockBuilder( 'BootstrapComponents\\NestingController' )
137
			->disableOriginalConstructor()
138
			->getMock();
139
140
		$instance = new HookRegistry();
141
142
		/** @noinspection PhpParamsInspection */
143
		$completeHookDefinitionList = $instance->getCompleteHookDefinitionList( $myConfig, $componentLibrary, $nestingController );
144
		$this->assertEquals(
145
			HookRegistry::AVAILABLE_HOOKS,
146
			array_keys( $completeHookDefinitionList )
147
		);
148
149
		foreach ( $completeHookDefinitionList as $callback ) {
150
			$this->assertTrue(
151
				is_callable( $callback )
152
			);
153
		}
154
	}
155
156
	/**
157
	 * @throws \ConfigException
158
	 * @throws \MWException
159
	 */
160
	public function testCanInitializeApplications() {
161
162
		$myConfig = $this->getMockBuilder( 'Config' )
163
			->disableOriginalConstructor()
164
			->getMock();
165
166
		$instance = new HookRegistry();
167
168
		/** @noinspection PhpParamsInspection */
169
		list ( $componentLibrary, $nestingController ) = $instance->initializeApplications( $myConfig );
170
171
		$this->assertInstanceOf(
172
			'BootstrapComponents\\ComponentLibrary',
173
			$componentLibrary
174
		);
175
		$this->assertInstanceOf(
176
			'BootstrapComponents\\NestingController',
177
			$nestingController
178
		);
179
	}
180
181
	/**
182
	 * @param array $listOfConfigSettingsSet
183
	 * @param array $expectedRegisteredHooks
184
	 * @param array $expectedNotRegisteredHooks
185
	 *
186
	 * @throws \ConfigException cascading {@see \Config::get}
187
	 * @throws \MWException
188
	 *
189
	 * @dataProvider hookRegistryProvider
190
	 */
191
	public function testHookRegistrationProcess( $listOfConfigSettingsSet, $expectedRegisteredHooks, $expectedNotRegisteredHooks ) {
192
193
		$instance = new HookRegistry();
194
195
		$hookCallbackList = $instance->buildHookCallbackListFor(
196
			$expectedRegisteredHooks
197
		);
198
199
		$this->assertTrue(
200
			is_array( $listOfConfigSettingsSet )
201
		);
202
203
		$this->assertEquals(
204
			count( $expectedRegisteredHooks ),
205
			$instance->register( $hookCallbackList )
206
		);
207
208
		foreach ( $expectedRegisteredHooks as $expectedHook ) {
209
			$this->doTestHookIsRegistered( $instance, $hookCallbackList, $expectedHook );
210
		}
211
212
		foreach ( $expectedNotRegisteredHooks as $notExpectedHook ) {
213
			$this->doTestHookIsNotRegistered( $hookCallbackList, $notExpectedHook );
214
		}
215
	}
216
217
	/**
218
	 * @throws \ConfigException cascading {@see \Config::get}
219
	 * @throws \MWException
220
	 */
221
	public function testCanRun() {
222
223
		$instance = new HookRegistry();
224
225
		$this->assertInternalType(
226
			'integer',
227
			$instance->run()
228
		);
229
	}
230
231
	/*
232
	 * Here end the tests for all the public methods.
233
	 * Following one test per hook function and one test for all the parser hook registrations.
234
	 */
235
236
	/**
237
	 * @throws \ConfigException
238
	 * @throws \MWException
239
	 */
240
	public function testHookGalleryGetModes() {
241
242
		$callback = $this->getCallBackForHook( 'GalleryGetModes' );
243
		$modesForTest = [ 'default' => 'TestGallery' ];
244
245
		$callback( $modesForTest );
246
		$this->assertEquals(
247
			2,
248
			count( $modesForTest )
249
		);
250
		$this->assertArrayHasKey(
251
			'carousel',
252
			$modesForTest
253
		);
254
		$this->assertEquals(
255
			'BootstrapComponents\\CarouselGallery',
256
			$modesForTest['carousel']
257
		);
258
	}
259
260
	/**
261
	 * @throws \ConfigException
262
	 * @throws \MWException
263
	 */
264
	public function testHookImageBeforeProduceHTML() {
265
		$callback = $this->getCallBackForHook( 'ImageBeforeProduceHTML' );
266
		$linker = $title = $file = $frameParams = $handlerParams = $time = $res = false;
267
268
		$this->assertTrue(
269
			$callback( $linker, $title, $file, $frameParams, $handlerParams, $time, $res )
270
		);
271
	}
272
273
	/**
274
	 * @throws \ConfigException
275
	 * @throws \MWException
276
	 */
277
	public function testHookInternalParseBeforeLinks() {
278
		$parserOutput = $this->getMockBuilder( 'ParserOutput' )
279
			->disableOriginalConstructor()
280
			->getMock();
281
		$parserOutput->expects( $this->exactly( 2 ) )
282
			->method( 'setExtensionData' )
283
			->with(
284
				$this->stringContains( 'bsc_no_image_modal' ),
285
				$this->isType( 'boolean' )
286
			);
287
288
		$parser = $this->getMockBuilder( 'Parser' )
289
			->disableOriginalConstructor()
290
			->getMock();
291
		$parser->expects( $this->exactly( 2 ) )
292
			->method( 'getOutput' )
293
			->willReturn( $parserOutput );
294
295
		$callback = $this->getCallBackForHook( 'InternalParseBeforeLinks' );
296
297
		$text = '';
298
		$this->assertTrue(
299
			$callback( $parser, $text )
300
		);
301
		$this->assertEquals(
302
			'',
303
			$text
304
		);
305
		$text = '__NOIMAGEMODAL__';
306
		$this->assertTrue(
307
			$callback( $parser, $text )
308
		);
309
		$this->assertEquals(
310
			'',
311
			$text
312
		);
313
	}
314
315
	/**
316
	 * @throws \ConfigException
317
	 * @throws \MWException
318
	 */
319
	public function testHookOutputPageParserOutput() {
320
		$content = 'CONTENT';
321
		$parser = $this->getMockBuilder( 'Parser' )
322
			->disableOriginalConstructor()
323
			->getMock();
324
		$parserOutput = $this->getMockBuilder( 'ParserOutput' )
325
			->disableOriginalConstructor()
326
			->getMock();
327
		$parserOutput->expects( $this->exactly( 2 ) )
328
			->method( 'getExtensionData' )
329
			->with(
330
				$this->stringContains( 'bsc_deferredContent' )
331
			)
332
			->willReturnOnConsecutiveCalls( [], [ 'call2' ] );
333
		$parserOutput->expects( $this->exactly( 2 ) )
334
			->method( 'getText' )
335
			->will( $this->returnCallback( function() use ( &$content ) {
336
				return $content;
337
			} ) );
338
		$parserOutput->expects( $this->exactly( 2 ) )
339
			->method( 'setText' )
340
			->will( $this->returnCallback( function( $injection ) use ( &$content ) {
341
				$content = $injection;
342
			} ) );
343
		$outputPage = $this->getMockBuilder( 'OutputPage' )
344
			->disableOriginalConstructor()
345
			->getMock();
346
347
		/** @noinspection PhpParamsInspection */
348
		$parserOutputHelper = new ParserOutputHelper( $parser );
349
350
		$callback = $this->getCallBackForHook( 'OutputPageParserOutput' );
351
352
		$this->assertTrue(
353
			$callback( $outputPage, $parserOutput, $parserOutputHelper )
354
		);
355
		$this->assertEquals(
356
			'CONTENT',
357
			$content
358
		);
359
		$this->assertTrue(
360
			$callback( $outputPage, $parserOutput, $parserOutputHelper )
361
		);
362
		$this->assertEquals(
363
			'CONTENT<!-- injected by Extension:BootstrapComponents -->call2<!-- /injected by Extension:BootstrapComponents -->',
364
			$content
365
		);
366
	}
367
368
	/**
369
	 * Note: Hook ParserFirstCallInit is tested in detail in {@see \BootstrapComponents\Tests\Unit\Hooks\ParserFirstCallInitTest}.
370
	 *
371
	 * @throws \ConfigException
372
	 * @throws \MWException
373
	 */
374
	public function testHookParserFirstCallInit() {
375
		$parser = $this->getMockBuilder( 'Parser' )
376
			->disableOriginalConstructor()
377
			->getMock();
378
379
		$callback = $this->getCallBackForHook( 'ParserFirstCallInit' );
380
381
		$this->assertTrue(
382
			$callback( $parser )
383
		);
384
	}
385
386
387
	/**
388
	 * @throws \ConfigException
389
	 * @throws \MWException
390
	 */
391
	public function testHookScribuntoExternalLibraries() {
392
		$callback = $this->getCallBackForHook( 'ScribuntoExternalLibraries' );
393
394
		$libraries = [];
395
		$this->assertTrue(
396
			$callback( '', $libraries )
397
		);
398
		$this->assertEquals(
399
			[],
400
			$libraries
401
		);
402
		$this->assertTrue(
403
			$callback( 'lua', $libraries )
404
		);
405
		$this->assertArrayHasKey(
406
			'mw.bootstrap',
407
			$libraries
408
		);
409
		$this->assertEquals(
410
			'BootstrapComponents\\LuaLibrary',
411
			$libraries['mw.bootstrap']
412
		);
413
	}
414
415
	/**
416
	 * @throws \ConfigException
417
	 * @throws \MWException
418
	 */
419
	public function testHookSetupAfterCache() {
420
		$callback = $this->getCallBackForHook( 'SetupAfterCache' );
421
		$this->assertTrue(
422
			$callback()
423
		);
424
	}
425
426
	/**
427
	 * @return array
428
	 */
429
	public function buildHookCallbackListForProvider() {
430
		return [
431
			'empty'               => [ [] ],
432
			'default'             => [ [ 'OutputPageParserOutput', 'ParserAfterParse', 'ParserFirstCallInit', 'ScribuntoExternalLibraries', 'SetupAfterCache' ] ],
433
			'alsoImageModal'      => [ [ 'ImageBeforeProduceHTML', 'InternalParseBeforeLinks', 'OutputPageParserOutput', 'ParserAfterParse', 'ParserFirstCallInit', 'ScribuntoExternalLibraries', 'SetupAfterCache' ] ],
434
			'alsoCarouselGallery' => [ [ 'GalleryGetModes', 'OutputPageParserOutput', 'ParserAfterParse', 'ParserFirstCallInit', 'ScribuntoExternalLibraries', 'SetupAfterCache' ] ],
435
			'all'                 => [ [ 'GalleryGetModes', 'ImageBeforeProduceHTML', 'InternalParseBeforeLinks', 'OutputPageParserOutput', 'ParserAfterParse', 'ParserFirstCallInit', 'ScribuntoExternalLibraries', 'SetupAfterCache' ] ],
436
			'invalid'             => [ [ 'nonExistingHook', 'PageContentSave' ] ],
437
		];
438
	}
439
440
	/**
441
	 * @return string[]
442
	 */
443
	public function hookRegistryProvider() {
444
		return [
445
			'onlydefault' => [
446
				[],
447
				[ 'OutputPageParserOutput', 'ParserAfterParse', 'ParserFirstCallInit', 'ScribuntoExternalLibraries', 'SetupAfterCache', ],
448
				[ 'GalleryGetModes', 'ImageBeforeProduceHTML', 'InternalParseBeforeLinks', ],
449
			],
450
			'gallery activated' => [
451
				[ 'BootstrapComponentsEnableCarouselGalleryMode' ],
452
				[ 'OutputPageParserOutput', 'ParserAfterParse', 'ParserFirstCallInit', 'ScribuntoExternalLibraries', 'SetupAfterCache', 'GalleryGetModes', ],
453
				[ 'ImageBeforeProduceHTML', 'InternalParseBeforeLinks', ],
454
			],
455
			'image replacement activated' => [
456
				[ 'BootstrapComponentsModalReplaceImageTag' ],
457
				[ 'OutputPageParserOutput', 'ParserAfterParse', 'ParserFirstCallInit', 'ScribuntoExternalLibraries', 'SetupAfterCache', 'ImageBeforeProduceHTML', 'InternalParseBeforeLinks', ],
458
				[ 'GalleryGetModes', ],
459
			],
460
			'both activated' => [
461
				[ 'BootstrapComponentsEnableCarouselGalleryMode', 'BootstrapComponentsModalReplaceImageTag' ],
462
				[ 'OutputPageParserOutput', 'ParserAfterParse', 'ParserFirstCallInit', 'ScribuntoExternalLibraries', 'SetupAfterCache', 'GalleryGetModes', 'ImageBeforeProduceHTML', 'InternalParseBeforeLinks', ],
463
				[],
464
			],
465
		];
466
	}
467
468
	/**
469
	 * @param $hookList
470
	 *
471
	 * @return array $expectedHookList, $invertedHookList
472
	 */
473
	private function buildHookListsForCanBuildHookListCheck( $hookList ) {
474
		$expectedHookList = [];
475
		$invertedHookList = [];
476
		foreach ( $hookList as $hook ) {
477
			if ( in_array( $hook, HookRegistry::AVAILABLE_HOOKS ) ) {
478
				$expectedHookList[] = $hook;
479
			}
480
		}
481
		foreach ( HookRegistry::AVAILABLE_HOOKS as $availableHook ) {
482
			if ( !in_array( $availableHook, $hookList ) ) {
483
				$invertedHookList[] = $availableHook;
484
			}
485
		}
486
		return [ $expectedHookList, $invertedHookList ];
487
	}
488
489
	/**
490
	 * @param HookRegistry $instance
491
	 * @param array        $registeredHooks
492
	 * @param string       $expectedHook
493
	 * @param bool         $hardRegisterTest
494
	 */
495
	private function doTestHookIsRegistered( HookRegistry $instance, $registeredHooks, $expectedHook, $hardRegisterTest = true ) {
496
		if ( $hardRegisterTest ) {
497
			$this->assertTrue(
498
				$instance->isRegistered( $expectedHook )
499
			);
500
		}
501
		$this->assertArrayHasKey(
502
			$expectedHook,
503
			$registeredHooks,
504
			'Expected hook "' . $expectedHook . '" to be registered but was not! '
505
		);
506
		$this->assertTrue(
507
			is_callable( $registeredHooks[$expectedHook] )
508
		);
509
	}
510
511
	/**
512
	 * @param array  $registeredHooks
513
	 * @param string $notExpectedHook
514
	 */
515
	private function doTestHookIsNotRegistered( $registeredHooks, $notExpectedHook ) {
516
		$this->assertArrayNotHasKey(
517
			$notExpectedHook,
518
			$registeredHooks,
519
			'Expected hook "' . $notExpectedHook . '" to not be registered but was! '
520
		);
521
	}
522
523
	/**
524
	 * @param string $hook
525
	 *
526
	 * @return \Closure
527
	 */
528
	private function getCallBackForHook( $hook ) {
529
		$instance = new HookRegistry();
530
		$hookCallbackList = $instance->buildHookCallbackListFor(
531
			[ $hook ]
532
		);
533
		$this->assertArrayHasKey(
534
			$hook,
535
			$hookCallbackList
536
		);
537
		$this->assertTrue(
538
			is_callable( $hookCallbackList[$hook] )
539
		);
540
		return $hookCallbackList[$hook];
541
	}
542
}
543