[TASK] Reduce amount of executed functional frontend tests
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Tests / Functional / SiteHandling / PlainRequestTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Frontend\Tests\Functional\SiteHandling;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use TYPO3\CMS\Core\Core\Bootstrap;
19 use TYPO3\CMS\Core\Error\Http\PageNotFoundException;
20 use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerFactory;
21 use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerWriter;
22 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
23 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequestContext;
24 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\ResponseContent;
25
26 /**
27 * Test case for frontend requests without having site handling configured
28 */
29 class PlainRequestTest extends AbstractTestCase
30 {
31 /**
32 * @var string
33 */
34 private $siteTitle = 'A Company that Manufactures Everything Inc';
35
36 /**
37 * @var InternalRequestContext
38 */
39 private $internalRequestContext;
40
41 protected function setUp()
42 {
43 parent::setUp();
44
45 // these settings are forwarded to the frontend sub-request as well
46 $this->internalRequestContext = (new InternalRequestContext())
47 ->withGlobalSettings(['TYPO3_CONF_VARS' => static::TYPO3_CONF_VARS]);
48
49 $backendUser = $this->setUpBackendUserFromFixture(1);
50 Bootstrap::initializeLanguageObject();
51
52 $scenarioFile = __DIR__ . '/Fixtures/scenario.yaml';
53 $factory = DataHandlerFactory::fromYamlFile($scenarioFile);
54 $writer = DataHandlerWriter::withBackendUser($backendUser);
55 $writer->invokeFactory($factory);
56 static::failIfArrayIsNotEmpty(
57 $writer->getErrors()
58 );
59
60 $this->setUpFrontendRootPage(
61 1000,
62 [
63 'typo3/sysext/core/Tests/Functional/Fixtures/Frontend/JsonRenderer.typoscript',
64 'typo3/sysext/frontend/Tests/Functional/SiteHandling/Fixtures/JsonRenderer.typoscript',
65 ],
66 [
67 'title' => 'ACME Root',
68 'sitetitle' => $this->siteTitle,
69 ]
70 );
71 $this->setUpFrontendRootPage(
72 3000,
73 [
74 'typo3/sysext/core/Tests/Functional/Fixtures/Frontend/JsonRenderer.typoscript',
75 'typo3/sysext/frontend/Tests/Functional/SiteHandling/Fixtures/JsonRenderer.typoscript',
76 ],
77 [
78 'title' => 'ACME Archive',
79 'sitetitle' => $this->siteTitle,
80 ]
81 );
82 }
83
84 protected function tearDown()
85 {
86 unset($this->internalRequestContext);
87 parent::tearDown();
88 }
89
90 /**
91 * @return array
92 */
93 public function requestsAreRedirectedDataProvider(): array
94 {
95 $domainPaths = [
96 '/',
97 'https://localhost/',
98 'https://website.local/',
99 ];
100
101 $queries = [
102 '?',
103 '?id=1000',
104 '?id=acme-root'
105 ];
106
107 return $this->wrapInArray(
108 $this->keysFromValues(
109 $this->meltStrings([$domainPaths, $queries])
110 )
111 );
112 }
113
114 /**
115 * @param string $uri
116 *
117 * @test
118 * @dataProvider requestsAreRedirectedDataProvider
119 */
120 public function requestsAreRedirected(string $uri)
121 {
122 $expectedStatusCode = 307;
123 $expectedHeaders = ['location' => ['index.php?id=acme-first']];
124
125 $response = $this->executeFrontendRequest(
126 new InternalRequest($uri),
127 $this->internalRequestContext
128 );
129 static::assertSame($expectedStatusCode, $response->getStatusCode());
130 static::assertSame($expectedHeaders, $response->getHeaders());
131 }
132
133 /**
134 * @return array
135 */
136 public function pageIsRenderedDataProvider(): array
137 {
138 $domainPaths = [
139 '/',
140 'https://localhost/',
141 'https://website.local/',
142 ];
143
144 $queries = [
145 '?id=1100',
146 '?id=acme-first',
147 ];
148
149 $languageQueries = [
150 '',
151 '&L=0',
152 '&L=1',
153 '&L=2',
154 ];
155
156 return array_map(
157 function (string $uri) {
158 if (strpos($uri, '&L=1') !== false) {
159 $expectedPageTitle = 'FR: Welcome';
160 } elseif (strpos($uri, '&L=2') !== false) {
161 $expectedPageTitle = 'FR-CA: Welcome';
162 } else {
163 $expectedPageTitle = 'EN: Welcome';
164 }
165 return [$uri, $expectedPageTitle];
166 },
167 $this->keysFromValues(
168 $this->meltStrings([$domainPaths, $queries, $languageQueries])
169 )
170 );
171 }
172
173 /**
174 * @param string $uri
175 * @param string $expectedPageTitle
176 *
177 * @test
178 * @dataProvider pageIsRenderedDataProvider
179 */
180 public function pageIsRendered(string $uri, string $expectedPageTitle)
181 {
182 $response = $this->executeFrontendRequest(
183 new InternalRequest($uri),
184 $this->internalRequestContext
185 );
186 $responseStructure = ResponseContent::fromString(
187 (string)$response->getBody()
188 );
189
190 static::assertSame(
191 200,
192 $response->getStatusCode()
193 );
194 static::assertSame(
195 $this->siteTitle,
196 $responseStructure->getScopePath('template/sitetitle')
197 );
198 static::assertSame(
199 $expectedPageTitle,
200 $responseStructure->getScopePath('page/title')
201 );
202 }
203
204 /**
205 * @return array
206 */
207 public function pageIsRenderedWithDomainsDataProvider(): array
208 {
209 $instructions = [
210 ['https://archive.acme.com/?id=3100', 'EN: Statistics'],
211 ['https://archive.acme.com/?id=3110', 'EN: Markets'],
212 ['https://archive.acme.com/?id=3120', 'EN: Products'],
213 ['https://archive.acme.com/?id=3130', 'EN: Partners'],
214 ];
215
216 return $this->keysFromTemplate($instructions, '%1$s');
217 }
218
219 /**
220 * @param string $uri
221 * @param string $expectedPageTitle
222 *
223 * @test
224 * @dataProvider pageIsRenderedWithDomainsDataProvider
225 */
226 public function pageIsRenderedWithDomains(string $uri, string $expectedPageTitle)
227 {
228 $response = $this->executeFrontendRequest(
229 new InternalRequest($uri),
230 $this->internalRequestContext
231 );
232 $responseStructure = ResponseContent::fromString(
233 (string)$response->getBody()
234 );
235
236 static::assertSame(
237 200,
238 $response->getStatusCode()
239 );
240 static::assertSame(
241 $this->siteTitle,
242 $responseStructure->getScopePath('template/sitetitle')
243 );
244 static::assertSame(
245 $expectedPageTitle,
246 $responseStructure->getScopePath('page/title')
247 );
248 }
249
250 /**
251 * @return array
252 */
253 public function restrictedPageIsRenderedDataProvider(): array
254 {
255 $instructions = [
256 // frontend user 1
257 ['https://website.local/?id=1510', 1, 'Whitepapers'],
258 ['https://website.local/?id=1511', 1, 'Products'],
259 ['https://website.local/?id=1512', 1, 'Solutions'],
260 // frontend user 2
261 ['https://website.local/?id=1510', 2, 'Whitepapers'],
262 ['https://website.local/?id=1511', 2, 'Products'],
263 ['https://website.local/?id=1515', 2, 'Research'],
264 ['https://website.local/?id=1520', 2, 'Forecasts'],
265 ['https://website.local/?id=1521', 2, 'Current Year'],
266 // frontend user 3
267 ['https://website.local/?id=1510', 3, 'Whitepapers'],
268 ['https://website.local/?id=1511', 3, 'Products'],
269 ['https://website.local/?id=1512', 3, 'Solutions'],
270 ['https://website.local/?id=1515', 3, 'Research'],
271 ['https://website.local/?id=1520', 3, 'Forecasts'],
272 ['https://website.local/?id=1521', 3, 'Current Year'],
273 ];
274
275 return $this->keysFromTemplate($instructions, '%1$s (user:%2$s)');
276 }
277
278 /**
279 * @param string $uri
280 * @param int $frontendUserId
281 * @param string $expectedPageTitle
282 *
283 * @test
284 * @dataProvider restrictedPageIsRenderedDataProvider
285 */
286 public function restrictedPageIsRendered(string $uri, int $frontendUserId, string $expectedPageTitle)
287 {
288 $response = $this->executeFrontendRequest(
289 new InternalRequest($uri),
290 $this->internalRequestContext
291 ->withFrontendUserId($frontendUserId)
292 );
293 $responseStructure = ResponseContent::fromString(
294 (string)$response->getBody()
295 );
296
297 static::assertSame(
298 200,
299 $response->getStatusCode()
300 );
301 static::assertSame(
302 $this->siteTitle,
303 $responseStructure->getScopePath('template/sitetitle')
304 );
305 static::assertSame(
306 $expectedPageTitle,
307 $responseStructure->getScopePath('page/title')
308 );
309 }
310
311 /**
312 * @return array
313 */
314 public function restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorDataProvider(): array
315 {
316 $instructions = [
317 // no frontend user given
318 ['https://website.local/?id=1510', 0],
319 ['https://website.local/?id=1511', 0],
320 ['https://website.local/?id=1512', 0],
321 ['https://website.local/?id=1515', 0],
322 ['https://website.local/?id=1520', 0],
323 ['https://website.local/?id=1521', 0],
324 // frontend user 1
325 ['https://website.local/?id=1515', 1],
326 ['https://website.local/?id=1520', 1],
327 ['https://website.local/?id=1521', 1],
328 // frontend user 2
329 ['https://website.local/?id=1512', 2],
330 ];
331
332 return $this->keysFromTemplate($instructions, '%1$s (user:%2$s)');
333 }
334
335 /**
336 * @param string $uri
337 * @param int $frontendUserId
338 *
339 * @test
340 * @dataProvider restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorDataProvider
341 */
342 public function restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorUsingDefaultErrorHandling(string $uri, int $frontendUserId)
343 {
344 $response = $this->executeFrontendRequest(
345 new InternalRequest($uri),
346 $this->internalRequestContext
347 ->withFrontendUserId($frontendUserId)
348 );
349
350 static::assertSame(
351 403,
352 $response->getStatusCode()
353 );
354 static::assertThat(
355 (string)$response->getBody(),
356 static::logicalOr(
357 static::stringContains('Reason: ID was not an accessible page'),
358 static::stringContains('Reason: Subsection was found and not accessible')
359 )
360 );
361 }
362
363 /**
364 * @param string $uri
365 * @param int $frontendUserId
366 *
367 * @test
368 * @dataProvider restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorDataProvider
369 */
370 public function restrictedPageSendsForbiddenResponseWithUnauthorizedVisitorUsingCustomErrorHandling(string $uri, int $frontendUserId)
371 {
372 $response = $this->executeFrontendRequest(
373 new InternalRequest($uri),
374 $this->internalRequestContext
375 ->withFrontendUserId($frontendUserId)
376 ->withMergedGlobalSettings([
377 'TYPO3_CONF_VARS' => [
378 'FE' => [
379 'pageNotFound_handling' => 'READFILE:typo3/sysext/frontend/Tests/Functional/SiteHandling/Fixtures/PageError.txt',
380 ]
381 ]
382 ])
383 );
384
385 static::assertSame(
386 403,
387 $response->getStatusCode()
388 );
389 static::assertThat(
390 (string)$response->getBody(),
391 static::logicalOr(
392 static::stringContains('reason: ID was not an accessible page'),
393 static::stringContains('reason: Subsection was found and not accessible')
394 )
395 );
396 }
397
398 /**
399 * @return array
400 */
401 public function pageRenderingStopsWithInvalidCacheHashDataProvider(): array
402 {
403 $domainPaths = [
404 '/',
405 'https://localhost/',
406 'https://website.local/',
407 ];
408
409 $queries = [
410 '?',
411 '?id=1000',
412 '?id=acme-root',
413 '?id=1100',
414 '?id=acme-first',
415 ];
416
417 $customQueries = [
418 '&testing[value]=1',
419 '&testing[value]=1&cHash=',
420 '&testing[value]=1&cHash=WRONG',
421 ];
422
423 return $this->wrapInArray(
424 $this->keysFromValues(
425 $this->meltStrings([$domainPaths, $queries, $customQueries])
426 )
427 );
428 }
429
430 /**
431 * @param string $uri
432 *
433 * @test
434 * @dataProvider pageRenderingStopsWithInvalidCacheHashDataProvider
435 * @todo In TYPO3 v8 this seemed to be rendered, without throwing that exception
436 */
437 public function pageRequestThrowsExceptionWithInvalidCacheHash(string $uri)
438 {
439 $this->expectExceptionCode(1518472189);
440 $this->expectException(PageNotFoundException::class);
441
442 $this->executeFrontendRequest(
443 new InternalRequest($uri),
444 $this->internalRequestContext
445 );
446 }
447
448 /**
449 * @param string $uri
450 *
451 * @test
452 * @dataProvider pageRenderingStopsWithInvalidCacheHashDataProvider
453 */
454 public function pageRequestSendsNotFoundResponseWithInvalidCacheHash(string $uri)
455 {
456 $response = $this->executeFrontendRequest(
457 new InternalRequest($uri),
458 $this->internalRequestContext->withMergedGlobalSettings([
459 'TYPO3_CONF_VARS' => [
460 'FE' => [
461 'pageNotFound_handling' => 'READFILE:typo3/sysext/frontend/Tests/Functional/SiteHandling/Fixtures/PageError.txt',
462 ]
463 ]
464 ])
465 );
466
467 static::assertSame(
468 404,
469 $response->getStatusCode()
470 );
471 static::assertThat(
472 (string)$response->getBody(),
473 static::logicalOr(
474 static::stringContains('reason: Request parameters could not be validated (&amp;cHash empty)'),
475 static::stringContains('reason: Request parameters could not be validated (&amp;cHash comparison failed)')
476 )
477 );
478 }
479
480 /**
481 * @return array
482 */
483 public function pageIsRenderedWithValidCacheHashDataProvider(): array
484 {
485 $domainPaths = [
486 '/',
487 'https://localhost/',
488 'https://website.local/',
489 ];
490
491 // cHash has been calculated with encryption key set to
492 // '4408d27a916d51e624b69af3554f516dbab61037a9f7b9fd6f81b4d3bedeccb6'
493 $queries = [
494 // @todo Currently fails since cHash is verified after(!) redirect to page 1100
495 // '?&cHash=7d1f13fa91159dac7feb3c824936b39d&id=1000',
496 // '?&cHash=7d1f13fa91159dac7feb3c824936b39d=acme-root',
497 '?&cHash=f42b850e435f0cedd366f5db749fc1af&id=1100',
498 '?&cHash=f42b850e435f0cedd366f5db749fc1af&id=acme-first',
499 ];
500
501 $customQueries = [
502 '&testing[value]=1',
503 ];
504
505 $dataSet = $this->wrapInArray(
506 $this->keysFromValues(
507 $this->meltStrings([$domainPaths, $queries, $customQueries])
508 )
509 );
510
511 return $dataSet;
512 }
513
514 /**
515 * @param string $uri
516 *
517 * @test
518 * @dataProvider pageIsRenderedWithValidCacheHashDataProvider
519 */
520 public function pageIsRenderedWithValidCacheHash($uri)
521 {
522 $response = $this->executeFrontendRequest(
523 new InternalRequest($uri),
524 $this->internalRequestContext
525 );
526 $responseStructure = ResponseContent::fromString(
527 (string)$response->getBody()
528 );
529 static::assertSame(
530 '1',
531 $responseStructure->getScopePath('getpost/testing.value')
532 );
533 }
534 }