Completed
Branch master (ecb46d)
by Tobias
01:39
created

SetupTest::getCallBackForHook()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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