[BUGFIX] Handle config.absRefPrefix on JS/CSS files correctly again
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Tests / Functional / Rendering / UriPrefixRenderingTest.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Frontend\Tests\Functional\Rendering;
5
6 use PHPUnit\Util\PHP\AbstractPhpProcess;
7 use TYPO3\CMS\Core\Database\ConnectionPool;
8 use TYPO3\CMS\Core\Utility\GeneralUtility;
9 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Response;
10 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
11
12 class UriPrefixRenderingTest extends FunctionalTestCase
13 {
14 /**
15 * @var string[]
16 */
17 private $definedResources = [
18 'absoluteCSS' => '/typo3/sysext/backend/Resources/Public/Css/backend.css',
19 'relativeCSS' => 'typo3/sysext/core/Resources/Public/Css/errorpage.css',
20 'extensionCSS' => 'EXT:rte_ckeditor/Resources/Public/Css/contents.css',
21 'externalCSS' => 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css',
22 'absoluteJS' => '/typo3/sysext/backend/Resources/Public/JavaScript/backend.js',
23 'relativeJS' => 'typo3/sysext/core/Resources/Public/JavaScript/Contrib/autosize.js',
24 'extensionJS' => 'EXT:core/Resources/Public/JavaScript/Contrib/jquery.autocomplete.js',
25 'externalJS' => 'https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js',
26 'localImage' => 'typo3/sysext/frontend/Resources/Public/Icons/Extension.png',
27 'localLink' => '1',
28 ];
29
30 /**
31 * @var string[]
32 */
33 private $resolvedResources = [
34 'relativeCSS' => 'typo3/sysext/core/Resources/Public/Css/errorpage.css',
35 'extensionCSS' => 'typo3/sysext/rte_ckeditor/Resources/Public/Css/contents.css',
36 'externalCSS' => 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css',
37 'relativeJS' => 'typo3/sysext/core/Resources/Public/JavaScript/Contrib/autosize.js',
38 'extensionJS' => 'typo3/sysext/core/Resources/Public/JavaScript/Contrib/jquery.autocomplete.js',
39 'externalJS' => 'https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js',
40 'localImage' => 'typo3/sysext/frontend/Resources/Public/Icons/Extension.png',
41 'localLink' => 'index.php?id=1',
42 ];
43
44 /**
45 * @var string[]
46 */
47 protected $coreExtensionsToLoad = [
48 'core', 'frontend', 'backend', 'rte_ckeditor',
49 ];
50
51 /**
52 * @var string[]
53 */
54 protected $pathsToLinkInTestInstance = [
55 'typo3/sysext/core/Tests/Functional/Fixtures/Frontend/AdditionalConfiguration.php' => 'typo3conf/AdditionalConfiguration.php',
56 ];
57
58 protected function setUp()
59 {
60 parent::setUp();
61 $this->importDataSet('EXT:frontend/Tests/Functional/Fixtures/pages.xml');
62 $this->setUpFrontendRootPage(
63 1,
64 ['EXT:frontend/Tests/Functional/Rendering/Fixtures/UriPrefixRenderingTest.typoscript']
65 );
66 $this->setTypoScriptConstantsToTemplateRecord(
67 1,
68 $this->compileTypoScriptConstants($this->definedResources)
69 );
70 }
71
72 public function urisAreRenderedUsingAbsRefPrefixDataProvider(): array
73 {
74 return [
75 // no compression settings
76 'none - none' => [
77 'none', 'none',
78 [
79 'absolute' => '"{{CANDIDATE}}"',
80 'local' => '"{{CANDIDATE}}"',
81 'relative' => '"{{CANDIDATE}}\?\d+"',
82 'extension' => '"{{CANDIDATE}}\?\d+"',
83 'external' => '"{{CANDIDATE}}"',
84 ],
85 ],
86 'auto - none' => [
87 'auto', 'none',
88 [
89 'absolute' => '"{{CANDIDATE}}"',
90 'local' => '"/{{CANDIDATE}}"',
91 'relative' => '"/{{CANDIDATE}}\?\d+"',
92 'extension' => '"/{{CANDIDATE}}\?\d+"',
93 'external' => '"{{CANDIDATE}}"',
94 ],
95 ],
96 'absolute-with-host - none' => [
97 'absolute-with-host', 'none',
98 [
99 'absolute' => '"{{CANDIDATE}}"',
100 'local' => '"http://localhost/{{CANDIDATE}}"',
101 'relative' => '"http://localhost/{{CANDIDATE}}\?\d+"',
102 'extension' => '"http://localhost/{{CANDIDATE}}\?\d+"',
103 'external' => '"{{CANDIDATE}}"',
104 ],
105 ],
106 'absolute-without-host - none' => [
107 'absolute-without-host', 'none',
108 [
109 'absolute' => '"{{CANDIDATE}}"',
110 'local' => '"/{{CANDIDATE}}"',
111 'relative' => '"/{{CANDIDATE}}\?\d+"',
112 'extension' => '"/{{CANDIDATE}}\?\d+"',
113 'external' => '"{{CANDIDATE}}"',
114 ],
115 ],
116 // concatenation
117 'none - concatenate' => [
118 'none', 'concatenate',
119 [
120 '!absolute' => '{{CANDIDATE}}',
121 '!relative' => '{{CANDIDATE}}',
122 '!extension' => '{{CANDIDATE}}',
123 'absolute' => '"typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
124 'local' => '"{{CANDIDATE}}"',
125 'relative' => '"typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
126 'extension' => '"typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
127 'external' => '"{{CANDIDATE}}"',
128 ],
129 ],
130 'auto - concatenate' => [
131 'auto', 'concatenate',
132 [
133 '!absolute' => '{{CANDIDATE}}',
134 '!relative' => '{{CANDIDATE}}',
135 '!extension' => '{{CANDIDATE}}',
136 'absolute' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
137 'local' => '"/{{CANDIDATE}}"',
138 'relative' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
139 'extension' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
140 'external' => '"{{CANDIDATE}}"',
141 ],
142 ],
143 'absolute-with-host - concatenate' => [
144 'absolute-with-host', 'concatenate',
145 [
146 '!absolute' => '{{CANDIDATE}}',
147 '!relative' => '{{CANDIDATE}}',
148 '!extension' => '{{CANDIDATE}}',
149 'absolute' => '"http://localhost/typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
150 'local' => '"http://localhost/{{CANDIDATE}}"',
151 'relative' => '"http://localhost/typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
152 'extension' => '"http://localhost/typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
153 'external' => '"{{CANDIDATE}}"',
154 ],
155 ],
156 'absolute-without-host - concatenate' => [
157 'absolute-without-host', 'concatenate',
158 [
159 '!absolute' => '{{CANDIDATE}}',
160 '!relative' => '{{CANDIDATE}}',
161 '!extension' => '{{CANDIDATE}}',
162 'absolute' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
163 'local' => '"/{{CANDIDATE}}"',
164 'relative' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
165 'extension' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
166 'external' => '"{{CANDIDATE}}"',
167 ],
168 ],
169 // compression
170 'none - compress' => [
171 'none', 'compress',
172 [
173 '!absolute' => '{{CANDIDATE}}',
174 '!relative' => '{{CANDIDATE}}',
175 '!extension' => '{{CANDIDATE}}',
176 'absolute' => '"typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
177 'local' => '"{{CANDIDATE}}"',
178 'relative' => '"typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
179 'extension' => '"typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
180 'external' => '"{{CANDIDATE}}"',
181 ],
182 ],
183 'auto - compress' => [
184 'auto', 'compress',
185 [
186 '!absolute' => '{{CANDIDATE}}',
187 '!relative' => '{{CANDIDATE}}',
188 '!extension' => '{{CANDIDATE}}',
189 'absolute' => '"/typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
190 'local' => '"/{{CANDIDATE}}"',
191 'relative' => '"/typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
192 'extension' => '"/typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
193 'external' => '"{{CANDIDATE}}"',
194 ],
195 ],
196 'absolute-with-host - compress' => [
197 'absolute-with-host', 'compress',
198 [
199 '!absolute' => '{{CANDIDATE}}',
200 '!relative' => '{{CANDIDATE}}',
201 '!extension' => '{{CANDIDATE}}',
202 'absolute' => '"http://localhost/typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
203 'local' => '"http://localhost/{{CANDIDATE}}"',
204 'relative' => '"http://localhost/typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
205 'extension' => '"http://localhost/typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
206 'external' => '"{{CANDIDATE}}"',
207 ],
208 ],
209 'absolute-without-host - compress' => [
210 'absolute-without-host', 'compress',
211 [
212 '!absolute' => '{{CANDIDATE}}',
213 '!relative' => '{{CANDIDATE}}',
214 '!extension' => '{{CANDIDATE}}',
215 'absolute' => '"/typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
216 'local' => '"/{{CANDIDATE}}"',
217 'relative' => '"/typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
218 'extension' => '"/typo3temp/assets/compressed/{{CANDIDATE-FILENAME}}-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
219 'external' => '"{{CANDIDATE}}"',
220 ],
221 ],
222 // concatenation & compression
223 'none - concatenate-and-compress' => [
224 'none', 'concatenate-and-compress',
225 [
226 '!absolute' => '{{CANDIDATE}}',
227 '!relative' => '{{CANDIDATE}}',
228 '!extension' => '{{CANDIDATE}}',
229 'absolute' => '"typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
230 'local' => '"{{CANDIDATE}}"',
231 'relative' => '"typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
232 'extension' => '"typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
233 'external' => '"{{CANDIDATE}}"',
234 ],
235 ],
236 'auto - concatenate-and-compress' => [
237 'auto', 'concatenate-and-compress',
238 [
239 '!absolute' => '{{CANDIDATE}}',
240 '!relative' => '{{CANDIDATE}}',
241 '!extension' => '{{CANDIDATE}}',
242 'absolute' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
243 'local' => '"/{{CANDIDATE}}"',
244 'relative' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
245 'extension' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
246 'external' => '"{{CANDIDATE}}"',
247 ],
248 ],
249 'absolute-with-host - concatenate-and-compress' => [
250 'absolute-with-host', 'concatenate-and-compress',
251 [
252 '!absolute' => '{{CANDIDATE}}',
253 '!relative' => '{{CANDIDATE}}',
254 '!extension' => '{{CANDIDATE}}',
255 'absolute' => '"http://localhost/typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
256 'local' => '"http://localhost/{{CANDIDATE}}"',
257 'relative' => '"http://localhost/typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
258 'extension' => '"http://localhost/typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
259 'external' => '"{{CANDIDATE}}"',
260 ],
261 ],
262 'absolute-without-host - concatenate-and-compress' => [
263 'absolute-without-host', 'concatenate-and-compress',
264 [
265 '!absolute' => '{{CANDIDATE}}',
266 '!relative' => '{{CANDIDATE}}',
267 '!extension' => '{{CANDIDATE}}',
268 'absolute' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
269 'local' => '"/{{CANDIDATE}}"',
270 'relative' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
271 'extension' => '"/typo3temp/assets/compressed/merged-[a-z0-9]+-[a-z0-9]+\.{{CANDIDATE-EXTENSION}}\?\d+"',
272 'external' => '"{{CANDIDATE}}"',
273 ],
274 ],
275 ];
276 }
277
278 /**
279 * @param string $absRefPrefixAspect
280 * @param string $compressorAspect
281 * @param array $expectations
282 * @test
283 * @dataProvider urisAreRenderedUsingAbsRefPrefixDataProvider
284 */
285 public function urisAreRenderedUsingAbsRefPrefix(string $absRefPrefixAspect, string $compressorAspect, array $expectations)
286 {
287 $content = $this->executeFrontendRequest(
288 1,
289 [
290 'testAbsRefPrefix' => $absRefPrefixAspect,
291 'testCompressor' => $compressorAspect
292 ]
293 );
294
295 foreach ($expectations as $type => $expectation) {
296 $shallExist = true;
297 if (strpos($type, '!') === 0) {
298 $shallExist = false;
299 $type = substr($type, 1);
300 }
301 $candidates = array_map(
302 function (string $candidateKey) {
303 return $this->resolvedResources[$candidateKey];
304 },
305 array_filter(
306 array_keys($this->resolvedResources),
307 function (string $candidateKey) use ($type) {
308 return strpos($candidateKey, $type) === 0;
309 }
310 )
311 );
312 foreach ($candidates as $candidate) {
313 $pathInfo = pathinfo($candidate);
314 $pattern = str_replace(
315 [
316 '{{CANDIDATE}}',
317 '{{CANDIDATE-FILENAME}}',
318 '{{CANDIDATE-EXTENSION}}',
319 ],
320 [
321 preg_quote($candidate, '#'),
322 preg_quote($pathInfo['filename'], '#'),
323 preg_quote($pathInfo['extension'], '#'),
324 ],
325 $expectation
326 );
327
328 if ($shallExist) {
329 $this->assertRegExp(
330 '#' . $pattern . '#',
331 $content
332 );
333 } else {
334 $this->assertNotRegExp(
335 '#' . $pattern . '#',
336 $content
337 );
338 }
339 }
340 }
341 }
342
343 /**
344 * Executes frontend request by invoking PHP sub-process.
345 *
346 * @param int $pageId
347 * @param array $queryArguments
348 * @return string
349 */
350 protected function executeFrontendRequest(int $pageId, array $queryArguments = []): string
351 {
352 $query = array_merge(
353 $queryArguments,
354 ['id' => (int)$pageId]
355 );
356 $arguments = [
357 'documentRoot' => $this->instancePath,
358 'requestUrl' => 'http://localhost/?' . http_build_query($query),
359 ];
360
361 $template = new \Text_Template(TYPO3_PATH_PACKAGES . 'typo3/testing-framework/Resources/Core/Functional/Fixtures/Frontend/request.tpl');
362 $template->setVar(
363 [
364 'arguments' => var_export($arguments, true),
365 'originalRoot' => ORIGINAL_ROOT,
366 'vendorPath' => TYPO3_PATH_PACKAGES
367 ]
368 );
369
370 $php = AbstractPhpProcess::factory();
371 $response = $php->runJob($template->render());
372 $result = json_decode($response['stdout'], true);
373
374 if ($result === null) {
375 $this->fail('Frontend Response is empty');
376 }
377
378 if ($result['status'] === Response::STATUS_Failure) {
379 $this->fail('Frontend Response has failure:' . LF . $result['error']);
380 }
381
382 return $result['content'];
383 }
384
385 /**
386 * Adds TypoScript constants snippet to the existing template record
387 *
388 * @param int $pageId
389 * @param string $constants
390 * @param bool $append
391 */
392 protected function setTypoScriptConstantsToTemplateRecord(int $pageId, string $constants, bool $append = false)
393 {
394 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_template');
395
396 $template = $connection->select(['uid', 'constants'], 'sys_template', ['pid' => $pageId, 'root' => 1])->fetch();
397 if (empty($template)) {
398 $this->fail('Cannot find root template on page with id: "' . $pageId . '"');
399 }
400 $updateFields['constants'] = ($append ? $template['constants'] . LF : '') . $constants;
401 $connection->update(
402 'sys_template',
403 $updateFields,
404 ['uid' => $template['uid']]
405 );
406 }
407
408 /**
409 * @param array $constants
410 * @return string
411 */
412 protected function compileTypoScriptConstants(array $constants): string
413 {
414 $lines = [];
415 foreach ($constants as $constantName => $constantValue) {
416 $lines[] = $constantName . ' = ' . $constantValue;
417 }
418 return implode(PHP_EOL, $lines);
419 }
420 }