[TASK] Use FQCN's when registering plugins/modules
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Tests / Unit / Utility / ExtensionUtilityTest.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Tests\Unit\Utility;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Extbase\Core\Bootstrap;
18 use TYPO3\CMS\Extbase\Tests\Unit\Utility\Fixtures\MyExtension\Controller\FirstController;
19 use TYPO3\CMS\Extbase\Tests\Unit\Utility\Fixtures\MyExtension\Controller\SecondController;
20 use TYPO3\CMS\Extbase\Tests\Unit\Utility\Fixtures\MyExtension\Controller\ThirdController;
21 use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
22 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
23
24 /**
25 * Testcase for class \TYPO3\CMS\Extbase\Utility\ExtensionUtility
26 */
27 class ExtensionUtilityTest extends UnitTestCase
28 {
29 protected function setUp()
30 {
31 $GLOBALS['TSFE'] = new \stdClass();
32 $GLOBALS['TSFE']->tmpl = new \stdClass();
33 $GLOBALS['TSFE']->tmpl->setup = [];
34 $GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.'] = [
35 '9' => 'CASE',
36 '9.' => [
37 'key.' => [
38 'field' => 'layout'
39 ],
40 0 => '< plugin.tt_news'
41 ],
42 'extensionname_someplugin' => 'USER',
43 'extensionname_someplugin.' => [
44 'userFunc' => Bootstrap::class . '->run',
45 'extensionName' => 'ExtensionName',
46 'pluginName' => 'SomePlugin'
47 ],
48 'someotherextensionname_secondplugin' => 'USER',
49 'someotherextensionname_secondplugin.' => [
50 'userFunc' => Bootstrap::class . '->run',
51 'extensionName' => 'SomeOtherExtensionName',
52 'pluginName' => 'SecondPlugin'
53 ],
54 'extensionname_thirdplugin' => 'USER',
55 'extensionname_thirdplugin.' => [
56 'userFunc' => Bootstrap::class . '->run',
57 'extensionName' => 'ExtensionName',
58 'pluginName' => 'ThirdPlugin'
59 ]
60 ];
61 }
62
63 /**
64 * @test
65 * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
66 */
67 public function configurePluginWorksForMinimalisticSetup()
68 {
69 $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
70 ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [FirstController::class => 'index']);
71 $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
72 $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
73 $this->assertContains('
74 userFunc = TYPO3\\CMS\\Extbase\\Core\\Bootstrap->run
75 extensionName = MyExtension
76 pluginName = Pi1', $staticTypoScript);
77 $this->assertNotContains('USER_INT', $staticTypoScript);
78 }
79
80 /**
81 * @test
82 * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
83 */
84 public function configurePluginCreatesCorrectDefaultTypoScriptSetup()
85 {
86 $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
87 ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [FirstController::class => 'index']);
88 $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
89 $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
90 }
91
92 /**
93 * @test
94 * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
95 */
96 public function configurePluginWorksForASingleControllerAction()
97 {
98 $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
99 ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
100 FirstController::class => 'index'
101 ]);
102 $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
103 $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
104 $this->assertContains('
105 extensionName = MyExtension
106 pluginName = Pi1', $staticTypoScript);
107 $expectedResult = [
108 'controllers' => [
109 FirstController::class => [
110 'className' => FirstController::class,
111 'alias' => 'First',
112 'actions' => ['index']
113 ]
114 ],
115 'pluginType' => 'list_type'
116 ];
117 $this->assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
118 }
119
120 /**
121 * @test
122 * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
123 */
124 public function configurePluginThrowsExceptionIfExtensionNameIsEmpty()
125 {
126 $this->expectException(\InvalidArgumentException::class);
127 $this->expectExceptionCode(1239891990);
128 ExtensionUtility::configurePlugin('', 'SomePlugin', [
129 'FirstController' => 'index'
130 ]);
131 }
132
133 /**
134 * @test
135 * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
136 */
137 public function configurePluginThrowsExceptionIfPluginNameIsEmpty()
138 {
139 $this->expectException(\InvalidArgumentException::class);
140 $this->expectExceptionCode(1239891988);
141 ExtensionUtility::configurePlugin('MyExtension', '', [
142 'FirstController' => 'index'
143 ]);
144 }
145
146 /**
147 * @test
148 * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
149 */
150 public function configurePluginRespectsDefaultActionAsANonCacheableAction()
151 {
152 $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
153 ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
154 FirstController::class => 'index,show,new, create,delete,edit,update'
155 ], [
156 FirstController::class => 'index,show'
157 ]);
158 $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
159 $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
160 $this->assertContains('
161 extensionName = MyExtension
162 pluginName = Pi1', $staticTypoScript);
163 $expectedResult = [
164 'controllers' => [
165 FirstController::class => [
166 'className' => FirstController::class,
167 'alias' => 'First',
168 'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
169 'nonCacheableActions' => ['index', 'show']
170 ]
171 ],
172 'pluginType' => 'list_type'
173 ];
174 $this->assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
175 }
176
177 /**
178 * @test
179 * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
180 */
181 public function configurePluginRespectsNonDefaultActionAsANonCacheableAction()
182 {
183 $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
184 ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
185 FirstController::class => 'index,show,new, create,delete,edit,update'
186 ], [
187 FirstController::class => 'new,show'
188 ]);
189 $staticTypoScript = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
190 $this->assertContains('tt_content.list.20.myextension_pi1 = USER', $staticTypoScript);
191 $this->assertContains('
192 extensionName = MyExtension
193 pluginName = Pi1', $staticTypoScript);
194 $expectedResult = [
195 'controllers' => [
196 FirstController::class => [
197 'className' => FirstController::class,
198 'alias' => 'First',
199 'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
200 'nonCacheableActions' => ['new', 'show']
201 ]
202 ],
203 'pluginType' => 'list_type'
204 ];
205 $this->assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
206 }
207
208 /**
209 * @test
210 * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
211 */
212 public function configurePluginWorksForMultipleControllerActionsWithCacheableActionAsDefault()
213 {
214 $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
215 ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
216 FirstController::class => 'index,show,new,create,delete,edit,update',
217 SecondController::class => 'index,show,delete',
218 ThirdController::class => 'create'
219 ], [
220 FirstController::class => 'new,create,edit,update',
221 ThirdController::class => 'create'
222 ]);
223 $expectedResult = [
224 'controllers' => [
225 FirstController::class => [
226 'className' => FirstController::class,
227 'alias' => 'First',
228 'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
229 'nonCacheableActions' => ['new', 'create', 'edit', 'update']
230 ],
231 SecondController::class => [
232 'className' => SecondController::class,
233 'alias' => 'Second',
234 'actions' => ['index', 'show', 'delete']
235 ],
236 ThirdController::class => [
237 'className' => ThirdController::class,
238 'alias' => 'Third',
239 'actions' => ['create'],
240 'nonCacheableActions' => ['create']
241 ]
242 ],
243 'pluginType' => 'list_type'
244 ];
245 $this->assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
246 }
247
248 /**
249 * @test
250 * @see \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin
251 */
252 public function configurePluginWorksForMultipleControllerActionsWithNonCacheableActionAsDefault()
253 {
254 $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'] = [];
255 ExtensionUtility::configurePlugin('MyExtension', 'Pi1', [
256 FirstController::class => 'index,show,new,create,delete,edit,update',
257 SecondController::class => 'index,show,delete',
258 ThirdController::class => 'create'
259 ], [
260 FirstController::class => 'index,new,create,edit,update',
261 SecondController::class => 'delete',
262 ThirdController::class => 'create'
263 ]);
264 $expectedResult = [
265 'controllers' => [
266 FirstController::class => [
267 'className' => FirstController::class,
268 'alias' => 'First',
269 'actions' => ['index', 'show', 'new', 'create', 'delete', 'edit', 'update'],
270 'nonCacheableActions' => ['index', 'new', 'create', 'edit', 'update']
271 ],
272 SecondController::class => [
273 'className' => SecondController::class,
274 'alias' => 'Second',
275 'actions' => ['index', 'show', 'delete'],
276 'nonCacheableActions' => ['delete']
277 ],
278 ThirdController::class => [
279 'className' => ThirdController::class,
280 'alias' => 'Third',
281 'actions' => ['create'],
282 'nonCacheableActions' => ['create']
283 ]
284 ],
285 'pluginType' => 'list_type'
286 ];
287 $this->assertEquals($expectedResult, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['MyExtension']['plugins']['Pi1']);
288 }
289
290 /**
291 * Tests method combination of registerPlugin() and its dependency addPlugin() to
292 * verify plugin icon path resolving works.
293 *
294 * @test
295 */
296 public function registerPluginTriggersAddPluginWhichSetsPluginIconPathIfUsingUnderscoredExtensionNameAndIconPathNotGiven()
297 {
298 $GLOBALS['TCA']['tt_content']['columns']['list_type']['config']['items'] = [];
299 ExtensionUtility::registerPlugin(
300 'indexed_search',
301 'Pi2',
302 'Testing'
303 );
304 $this->assertEquals(
305 'EXT:indexed_search/Resources/Public/Icons/Extension.png',
306 $GLOBALS['TCA']['tt_content']['columns']['list_type']['config']['items'][0][2]
307 );
308 }
309
310 /**
311 * Tests method combination of registerPlugin() and its dependency addPlugin() to
312 * verify plugin icon path resolving works.
313 *
314 * @test
315 */
316 public function registerPluginTriggersAddPluginWhichSetsPluginIconPathIfUsingUpperCameCasedExtensionNameAndIconPathNotGiven()
317 {
318 $GLOBALS['TCA']['tt_content']['columns']['list_type']['config']['items'] = [];
319 ExtensionUtility::registerPlugin(
320 'IndexedSearch',
321 'Pi2',
322 'Testing'
323 );
324 $this->assertEquals(
325 'EXT:indexed_search/Resources/Public/Icons/Extension.png',
326 $GLOBALS['TCA']['tt_content']['columns']['list_type']['config']['items'][0][2]
327 );
328 }
329
330 /**
331 * Tests method combination of registerPlugin() and its dependency addPlugin() to
332 * verify plugin icon path resolving works.
333 *
334 * @test
335 */
336 public function registerPluginTriggersAddPluginWhichSetsPluginIconPathIfIconPathIsGiven()
337 {
338 $GLOBALS['TCA']['tt_content']['columns']['list_type']['config']['items'] = [];
339 ExtensionUtility::registerPlugin(
340 'IndexedSearch',
341 'Pi2',
342 'Testing',
343 'EXT:indexed_search/foo.gif'
344 );
345 $this->assertEquals(
346 'EXT:indexed_search/foo.gif',
347 $GLOBALS['TCA']['tt_content']['columns']['list_type']['config']['items'][0][2]
348 );
349 }
350
351 /**
352 * A type converter added several times with the exact same class name must
353 * not be added more than once to the global array.
354 *
355 * @test
356 */
357 public function sameTypeConvertersRegisteredAreAddedOnlyOnce()
358 {
359 $typeConverterClassName = \TYPO3\CMS\Extbase\Property\TypeConverter\ArrayConverter::class;
360
361 // the Extbase EXTCONF is not set at all at this point
362 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'] = [];
363
364 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter($typeConverterClassName);
365
366 $this->assertContains($typeConverterClassName, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters']);
367 $this->assertEquals(1, count($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters']));
368
369 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter($typeConverterClassName);
370 $this->assertEquals(1, count($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters']));
371 }
372
373 /**
374 * DataProvider for explodeObjectControllerName
375 *
376 * @return array
377 */
378 public function controllerArgumentsAndExpectedObjectName()
379 {
380 return [
381 'Vendor TYPO3\CMS, extension, controller given' => [
382 [
383 'vendorName' => 'TYPO3\\CMS',
384 'extensionName' => 'Ext',
385 'subpackageKey' => '',
386 'controllerName' => 'Foo',
387 ],
388 'TYPO3\\CMS\\Ext\\Controller\\FooController',
389 ],
390 'Vendor TYPO3\CMS, extension, subpackage, controlle given' => [
391 [
392 'vendorName' => 'TYPO3\\CMS',
393 'extensionName' => 'Fluid',
394 'subpackageKey' => 'ViewHelpers\\Widget',
395 'controllerName' => 'Paginate',
396 ],
397 \TYPO3\CMS\Fluid\ViewHelpers\Widget\Controller\PaginateController::class,
398 ],
399 'Vendor VENDOR, extension, controller given' => [
400 [
401 'vendorName' => 'VENDOR',
402 'extensionName' => 'Ext',
403 'subpackageKey' => '',
404 'controllerName' => 'Foo',
405 ],
406 'VENDOR\\Ext\\Controller\\FooController',
407 ],
408 'Vendor VENDOR, extension subpackage, controller given' => [
409 [
410 'vendorName' => 'VENDOR',
411 'extensionName' => 'Ext',
412 'subpackageKey' => 'ViewHelpers\\Widget',
413 'controllerName' => 'Foo',
414 ],
415 'VENDOR\\Ext\\ViewHelpers\\Widget\\Controller\\FooController',
416 ],
417 ];
418 }
419
420 /**
421 * @dataProvider controllerArgumentsAndExpectedObjectName
422 *
423 * @param array $controllerArguments
424 * @param string $controllerObjectName
425 * @test
426 */
427 public function getControllerObjectNameResolvesControllerObjectNameCorrectly($controllerArguments, $controllerObjectName)
428 {
429 $this->assertEquals(
430 $controllerObjectName,
431 ExtensionUtility::getControllerClassName(
432 $controllerArguments['vendorName'],
433 $controllerArguments['extensionName'],
434 $controllerArguments['subpackageKey'],
435 $controllerArguments['controllerName']
436 )
437 );
438 }
439
440 /**
441 * @return array
442 */
443 public function checkResolveControllerAliasFromControllerClassNameDataProvider()
444 {
445 return [
446 'Class in root namespace without controller suffix' => [
447 '',
448 'Foo',
449 ],
450 'Class in root namespace without controller suffix (2)' => [
451 '',
452 'FooBarBazQuxBlaBlub',
453 ],
454 'Controller in root namespace' => [
455 'Foo',
456 'FooController',
457 ],
458 'Controller in root namespace (lowercase)' => [
459 'foo',
460 'fooController',
461 ],
462 'Controller in namespace' => [
463 'Foo',
464 'TYPO3\\CMS\\Ext\\Controller\\FooController',
465 ],
466 'Controller in arbitrary namespace' => [
467 'Foo',
468 'Foo\\Bar\\baz\\qUx\\FooController',
469 ],
470 'Controller with lowercase suffix' => [
471 '',
472 'Foo\\Bar\\baz\\qUx\\Foocontroller',
473 ],
474 ];
475 }
476
477 /**
478 * @dataProvider checkResolveControllerAliasFromControllerClassNameDataProvider
479 *
480 * @param string $expectedControllerAlias
481 * @param string $controllerClassName
482 * @test
483 */
484 public function checkResolveControllerAliasFromControllerClassName(string $expectedControllerAlias, string $controllerClassName)
485 {
486 $this->assertEquals(
487 $expectedControllerAlias,
488 ExtensionUtility::resolveControllerAliasFromControllerClassName(
489 $controllerClassName
490 )
491 );
492 }
493
494 /**
495 * @return array
496 */
497 public function checkResolveVendorFromExtensionAndControllerClassNameDataProvider()
498 {
499 return [
500 'Class in root namespace' => [
501 '',
502 'IndexedSearch',
503 'Foo',
504 ],
505 'Namespaced class without extension name as namespace part' => [
506 '',
507 'IndexedSearch',
508 'Foo\\Bar\\Baz\\Qux',
509 ],
510 'Namespaced class without vendor part before extension name part' => [
511 '',
512 'IndexedSearch',
513 'IndexedSearch\\Controller\\SearchController',
514 ],
515 'Namespaced class with single vendor part' => [
516 'Foo',
517 'IndexedSearch',
518 'Foo\\IndexedSearch\\Controller\\SearchController',
519 ],
520 'Namespaced class with multiple vendor parts' => [
521 'TYPO\\CMS',
522 'IndexedSearch',
523 'TYPO\\CMS\\IndexedSearch\\Controller\\SearchController',
524 ],
525 ];
526 }
527
528 /**
529 * @dataProvider checkResolveVendorFromExtensionAndControllerClassNameDataProvider
530 *
531 * @param string $expectedVendor
532 * @param string $extensionName
533 * @param string $controllerClassName
534 * @test
535 */
536 public function checkResolveVendorFromExtensionAndControllerClassName(
537 string $expectedVendor,
538 string $extensionName,
539 string $controllerClassName
540 ) {
541 $this->assertEquals(
542 $expectedVendor,
543 ExtensionUtility::resolveVendorFromExtensionAndControllerClassName(
544 $extensionName,
545 $controllerClassName
546 )
547 );
548 }
549 }