[TASK] Move CSV fixtures handling to FunctionalTestCase
[Packages/TYPO3.CMS.git] / components / testing_framework / Classes / Core / Functional / FunctionalTestCase.php
1 <?php
2 namespace TYPO3\TestingFramework\Core\Functional;
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 Doctrine\DBAL\DBALException;
18 use Doctrine\DBAL\Platforms\MySqlPlatform;
19 use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
20 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
21 use TYPO3\CMS\Core\Cache\Backend\NullBackend;
22 use TYPO3\CMS\Core\Database\ConnectionPool;
23 use TYPO3\CMS\Core\Tests\Functional\DataHandling\Framework\DataSet;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25 use TYPO3\TestingFramework\Core\BaseTestCase;
26 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Response;
27 use TYPO3\TestingFramework\Core\Testbase;
28
29 /**
30 * Base test case class for functional tests, all TYPO3 CMS
31 * functional tests should extend from this class!
32 *
33 * If functional tests need additional setUp() and tearDown() code,
34 * they *must* call parent::setUp() and parent::tearDown() to properly
35 * set up and destroy the test system.
36 *
37 * The functional test system creates a full new TYPO3 CMS instance
38 * within typo3temp/ of the base system and the bootstraps this TYPO3 instance.
39 * This abstract class takes care of creating this instance with its
40 * folder structure and a LocalConfiguration, creates an own database
41 * for each test run and imports tables of loaded extensions.
42 *
43 * Functional tests must be run standalone (calling native phpunit
44 * directly) and can not be executed by eg. the ext:phpunit backend module.
45 * Additionally, the script must be called from the document root
46 * of the instance, otherwise path calculation is not successfully.
47 *
48 * Call whole functional test suite, example:
49 * - cd /var/www/t3master/foo # Document root of CMS instance, here is index.php of frontend
50 * - typo3/../bin/phpunit -c components/testing_framework/core/Build/FunctionalTests.xml
51 *
52 * Call single test case, example:
53 * - cd /var/www/t3master/foo # Document root of CMS instance, here is index.php of frontend
54 * - typo3/../bin/phpunit \
55 * --process-isolation \
56 * --bootstrap components/testing_framework/core/Build/FunctionalTestsBootstrap.php \
57 * typo3/sysext/core/Tests/Functional/DataHandling/DataHandlerTest.php
58 */
59 abstract class FunctionalTestCase extends BaseTestCase
60 {
61 const DATABASE_PLATFORM_MYSQL = 'MySQL';
62 const DATABASE_PLATFORM_PDO = 'PDO';
63
64 /**
65 * Path to a XML fixture dependent on the current database.
66 * @var string
67 */
68 protected $fixturePath = '';
69
70 /**
71 * @var string
72 */
73 protected $databasePlatform;
74
75 /**
76 * An unique identifier for this test case. Location of the test
77 * instance and database name depend on this. Calculated early in setUp()
78 *
79 * @var string
80 */
81 protected $identifier;
82
83 /**
84 * Absolute path to test instance document root. Depends on $identifier.
85 * Calculated early in setUp()
86 *
87 * @var string
88 */
89 protected $instancePath;
90
91 /**
92 * Core extensions to load.
93 *
94 * If the test case needs additional core extensions as requirement,
95 * they can be noted here and will be added to LocalConfiguration
96 * extension list and ext_tables.sql of those extensions will be applied.
97 *
98 * This property will stay empty in this abstract, so it is possible
99 * to just overwrite it in extending classes. Extensions noted here will
100 * be loaded for every test of a test case and it is not possible to change
101 * the list of loaded extensions between single tests of a test case.
102 *
103 * A default list of core extensions is always loaded.
104 *
105 * @see FunctionalTestCaseUtility $defaultActivatedCoreExtensions
106 * @var array
107 */
108 protected $coreExtensionsToLoad = [];
109
110 /**
111 * Array of test/fixture extensions paths that should be loaded for a test.
112 *
113 * This property will stay empty in this abstract, so it is possible
114 * to just overwrite it in extending classes. Extensions noted here will
115 * be loaded for every test of a test case and it is not possible to change
116 * the list of loaded extensions between single tests of a test case.
117 *
118 * Given path is expected to be relative to your document root, example:
119 *
120 * array(
121 * 'typo3conf/ext/some_extension/Tests/Functional/Fixtures/Extensions/test_extension',
122 * 'typo3conf/ext/base_extension',
123 * );
124 *
125 * Extensions in this array are linked to the test instance, loaded
126 * and their ext_tables.sql will be applied.
127 *
128 * @var array
129 */
130 protected $testExtensionsToLoad = [];
131
132 /**
133 * Array of test/fixture folder or file paths that should be linked for a test.
134 *
135 * This property will stay empty in this abstract, so it is possible
136 * to just overwrite it in extending classes. Path noted here will
137 * be linked for every test of a test case and it is not possible to change
138 * the list of folders between single tests of a test case.
139 *
140 * array(
141 * 'link-source' => 'link-destination'
142 * );
143 *
144 * Given paths are expected to be relative to the test instance root.
145 * The array keys are the source paths and the array values are the destination
146 * paths, example:
147 *
148 * array(
149 * 'typo3/sysext/impext/Tests/Functional/Fixtures/Folders/fileadmin/user_upload' =>
150 * 'fileadmin/user_upload',
151 * 'typo3conf/ext/my_own_ext/Tests/Functional/Fixtures/Folders/uploads/tx_myownext' =>
152 * 'uploads/tx_myownext'
153 * );
154 *
155 * To be able to link from my_own_ext the extension path needs also to be registered in
156 * property $testExtensionsToLoad
157 *
158 * @var array
159 */
160 protected $pathsToLinkInTestInstance = [];
161
162 /**
163 * This configuration array is merged with TYPO3_CONF_VARS
164 * that are set in default configuration and factory configuration
165 *
166 * @var array
167 */
168 protected $configurationToUseInTestInstance = [];
169
170 /**
171 * Array of folders that should be created inside the test instance document root.
172 *
173 * This property will stay empty in this abstract, so it is possible
174 * to just overwrite it in extending classes. Path noted here will
175 * be linked for every test of a test case and it is not possible to change
176 * the list of folders between single tests of a test case.
177 *
178 * Per default the following folder are created
179 * /fileadmin
180 * /typo3temp
181 * /typo3conf
182 * /typo3conf/ext
183 * /uploads
184 *
185 * To create additional folders add the paths to this array. Given paths are expected to be
186 * relative to the test instance root and have to begin with a slash. Example:
187 *
188 * array(
189 * 'fileadmin/user_upload'
190 * );
191 *
192 * @var array
193 */
194 protected $additionalFoldersToCreate = [];
195
196 /**
197 * The fixture which is used when initializing a backend user
198 *
199 * @var string
200 */
201 protected $backendUserFixture = 'components/testing_framework/Resources/Core/Functional/Fixtures/be_users.xml';
202
203 /**
204 * Set up creates a test instance and database.
205 *
206 * This method should be called with parent::setUp() in your test cases!
207 *
208 * @return void
209 * @throws \Doctrine\DBAL\DBALException
210 */
211 protected function setUp()
212 {
213 if (!defined('ORIGINAL_ROOT')) {
214 $this->markTestSkipped('Functional tests must be called through phpunit on CLI');
215 }
216
217 // Use a 7 char long hash of class name as identifier
218 $this->identifier = substr(sha1(get_class($this)), 0, 7);
219 $this->instancePath = ORIGINAL_ROOT . 'typo3temp/var/tests/functional-' . $this->identifier;
220
221 $testbase = new Testbase();
222 $testbase->defineTypo3ModeBe();
223 $testbase->setTypo3TestingContext();
224 if ($testbase->recentTestInstanceExists($this->instancePath)) {
225 // Reusing an existing instance. This typically happens for the second, third, ... test
226 // in a test case, so environment is set up only once per test case.
227 $testbase->setUpBasicTypo3Bootstrap($this->instancePath);
228 $testbase->initializeTestDatabaseAndTruncateTables();
229 $testbase->loadExtensionTables();
230 } else {
231 $testbase->removeOldInstanceIfExists($this->instancePath);
232 // Basic instance directory structure
233 $testbase->createDirectory($this->instancePath . '/fileadmin');
234 $testbase->createDirectory($this->instancePath . '/typo3temp/var/transient');
235 $testbase->createDirectory($this->instancePath . '/typo3temp/assets');
236 $testbase->createDirectory($this->instancePath . '/typo3conf/ext');
237 $testbase->createDirectory($this->instancePath . '/uploads');
238 // Additionally requested directories
239 foreach ($this->additionalFoldersToCreate as $directory) {
240 $testbase->createDirectory($this->instancePath . '/' . $directory);
241 }
242 $testbase->createLastRunTextfile($this->instancePath);
243 $testbase->setUpInstanceCoreLinks($this->instancePath);
244 $testbase->linkTestExtensionsToInstance($this->instancePath, $this->testExtensionsToLoad);
245 $testbase->linkPathsInTestInstance($this->instancePath, $this->pathsToLinkInTestInstance);
246 $localConfiguration['DB'] = $testbase->getOriginalDatabaseSettingsFromEnvironmentOrLocalConfiguration();
247 $originalDatabaseName = $localConfiguration['DB']['Connections']['Default']['dbname'];
248 // Append the unique identifier to the base database name to end up with a single database per test case
249 $localConfiguration['DB']['Connections']['Default']['dbname'] = $originalDatabaseName . '_ft' . $this->identifier;
250 $testbase->testDatabaseNameIsNotTooLong($originalDatabaseName, $localConfiguration);
251 // Set some hard coded base settings for the instance. Those could be overruled by
252 // $this->configurationToUseInTestInstance if needed again.
253 $localConfiguration['SYS']['isInitialInstallationInProgress'] = false;
254 $localConfiguration['SYS']['isInitialDatabaseImportDone'] = true;
255 $localConfiguration['SYS']['displayErrors'] = '1';
256 $localConfiguration['SYS']['debugExceptionHandler'] = '';
257 $localConfiguration['SYS']['trustedHostsPattern'] = '.*';
258 // @todo: This should be moved over to DB/Connections/Default/initCommands
259 $localConfiguration['SYS']['setDBinit'] = 'SET SESSION sql_mode = \'STRICT_ALL_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_VALUE_ON_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY\';';
260 $localConfiguration['SYS']['caching']['cacheConfigurations']['extbase_object']['backend'] = NullBackend::class;
261 $testbase->setUpLocalConfiguration($this->instancePath, $localConfiguration, $this->configurationToUseInTestInstance);
262 $defaultCoreExtensionsToLoad = [
263 'core',
264 'backend',
265 'frontend',
266 'lang',
267 'extbase',
268 'install',
269 ];
270 $testbase->setUpPackageStates($this->instancePath, $defaultCoreExtensionsToLoad, $this->coreExtensionsToLoad, $this->testExtensionsToLoad);
271 $testbase->setUpBasicTypo3Bootstrap($this->instancePath);
272 $testbase->setUpTestDatabase($localConfiguration['DB']['Connections']['Default']['dbname'], $originalDatabaseName);
273 $testbase->loadExtensionTables();
274 $testbase->createDatabaseStructure();
275 }
276
277 $databasePlatform = $this->getConnectionPool()
278 ->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME)
279 ->getDatabasePlatform();
280
281 if ($databasePlatform instanceof MySqlPlatform) {
282 $this->setDatabasePlatform(static::DATABASE_PLATFORM_MYSQL);
283 } else {
284 $this->setDatabasePlatform(static::DATABASE_PLATFORM_PDO);
285 }
286 }
287
288 /**
289 * Get DatabaseConnection instance - $GLOBALS['TYPO3_DB']
290 *
291 * This method should be used instead of direct access to
292 * $GLOBALS['TYPO3_DB'] for easy IDE auto completion.
293 *
294 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
295 * @deprecated since TYPO3 v8, will be removed in TYPO3 v9
296 */
297 protected function getDatabaseConnection()
298 {
299 GeneralUtility::logDeprecatedFunction();
300 return $GLOBALS['TYPO3_DB'];
301 }
302
303 /**
304 * @return ConnectionPool
305 */
306 protected function getConnectionPool()
307 {
308 return GeneralUtility::makeInstance(ConnectionPool::class);
309 }
310
311 /**
312 * Initialize backend user
313 *
314 * @param int $userUid uid of the user we want to initialize. This user must exist in the fixture file
315 * @return BackendUserAuthentication
316 * @throws Exception
317 */
318 protected function setUpBackendUserFromFixture($userUid)
319 {
320 $this->importDataSet(ORIGINAL_ROOT . $this->backendUserFixture);
321
322 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users');
323 $queryBuilder->getRestrictions()->removeAll();
324
325 $userRow = $queryBuilder->select('*')
326 ->from('be_users')
327 ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($userUid, \PDO::PARAM_INT)))
328 ->execute()
329 ->fetch();
330
331 /** @var $backendUser BackendUserAuthentication */
332 $backendUser = GeneralUtility::makeInstance(BackendUserAuthentication::class);
333 $sessionId = $backendUser->createSessionId();
334 $_COOKIE['be_typo_user'] = $sessionId;
335 $backendUser->id = $sessionId;
336 $backendUser->sendNoCacheHeaders = false;
337 $backendUser->dontSetCookie = true;
338 $backendUser->createUserSession($userRow);
339
340 $GLOBALS['BE_USER'] = $backendUser;
341 $GLOBALS['BE_USER']->start();
342 if (!is_array($GLOBALS['BE_USER']->user) || !$GLOBALS['BE_USER']->user['uid']) {
343 throw new Exception(
344 'Can not initialize backend user',
345 1377095807
346 );
347 }
348 $GLOBALS['BE_USER']->backendCheckLogin();
349
350 return $backendUser;
351 }
352
353 /**
354 * Imports a data set represented as XML into the test database,
355 *
356 * @param string $path Absolute path to the XML file containing the data set to load
357 * @return void
358 * @throws Exception
359 */
360 protected function importDataSet($path)
361 {
362 $testbase = new Testbase();
363 $testbase->importXmlDatabaseFixture($path);
364 }
365
366 /**
367 * Import data from a CSV file to database
368 * Single file can contain data from multiple tables
369 *
370 * @param string $path absolute path to the CSV file containing the data set to load
371 */
372 public function importCSVDataSet($path)
373 {
374 $dataSet = DataSet::read($path, true);
375
376 foreach ($dataSet->getTableNames() as $tableName) {
377 $connection = $this->getConnectionPool()->getConnectionForTable($tableName);
378 foreach ($dataSet->getElements($tableName) as $element) {
379 try {
380 $connection->insert($tableName, $element);
381 } catch (DBALException $e) {
382 $this->fail('SQL Error for table "' . $tableName . '": ' . LF . $e->getMessage());
383 }
384 }
385 Testbase::resetTableSequences($connection, $tableName);
386 }
387 }
388
389 /**
390 * Compare data in database with CSV file
391 *
392 * @param string $path absolute path to the CSV file
393 */
394 protected function assertCSVDataSet($path)
395 {
396 $fileName = GeneralUtility::getFileAbsFileName($path);
397
398 $dataSet = DataSet::read($fileName);
399 $failMessages = [];
400
401 foreach ($dataSet->getTableNames() as $tableName) {
402 $hasUidField = ($dataSet->getIdIndex($tableName) !== null);
403 $records = $this->getAllRecords($tableName, $hasUidField);
404 foreach ($dataSet->getElements($tableName) as $assertion) {
405 $result = $this->assertInRecords($assertion, $records);
406 if ($result === false) {
407 if ($hasUidField && empty($records[$assertion['uid']])) {
408 $failMessages[] = 'Record "' . $tableName . ':' . $assertion['uid'] . '" not found in database';
409 continue;
410 }
411 $recordIdentifier = $tableName . ($hasUidField ? ':' . $assertion['uid'] : '');
412 $additionalInformation = ($hasUidField ? $this->renderRecords($assertion, $records[$assertion['uid']]) : $this->arrayToString($assertion));
413 $failMessages[] = 'Assertion in data-set failed for "' . $recordIdentifier . '":' . LF . $additionalInformation;
414 // Unset failed asserted record
415 if ($hasUidField) {
416 unset($records[$assertion['uid']]);
417 }
418 } else {
419 // Unset asserted record
420 unset($records[$result]);
421 // Increase assertion counter
422 $this->assertTrue($result !== false);
423 }
424 }
425 if (!empty($records)) {
426 foreach ($records as $record) {
427 $recordIdentifier = $tableName . ':' . $record['uid'];
428 $emptyAssertion = array_fill_keys($dataSet->getFields($tableName), '[none]');
429 $reducedRecord = array_intersect_key($record, $emptyAssertion);
430 $additionalInformation = ($hasUidField ? $this->renderRecords($emptyAssertion, $reducedRecord) : $this->arrayToString($reducedRecord));
431 $failMessages[] = 'Not asserted record found for "' . $recordIdentifier . '":' . LF . $additionalInformation;
432 }
433 }
434 }
435
436 if (!empty($failMessages)) {
437 $this->fail(implode(LF, $failMessages));
438 }
439 }
440
441 /**
442 * Check if $expectedRecord is present in $actualRecords array
443 * and compares if all column values from matches
444 *
445 * @param array $expectedRecord
446 * @param array $actualRecords
447 * @return bool|int|string false if record is not found or some column value doesn't match
448 */
449 protected function assertInRecords(array $expectedRecord, array $actualRecords)
450 {
451 foreach ($actualRecords as $index => $record) {
452 $differentFields = $this->getDifferentFields($expectedRecord, $record);
453
454 if (empty($differentFields)) {
455 return $index;
456 }
457 }
458
459 return false;
460 }
461
462 /**
463 * Fetches all records from a database table
464 * Helper method for assertCSVDataSet
465 *
466 * @param string $tableName
467 * @param bool $hasUidField
468 * @return array
469 */
470 protected function getAllRecords($tableName, $hasUidField = false)
471 {
472 $queryBuilder = $this->getConnectionPool()
473 ->getQueryBuilderForTable($tableName);
474 $queryBuilder->getRestrictions()->removeAll();
475 $statement = $queryBuilder
476 ->select('*')
477 ->from($tableName)
478 ->execute();
479
480 if (!$hasUidField) {
481 return $statement->fetchAll();
482 }
483
484 $allRecords = [];
485 while ($record = $statement->fetch()) {
486 $index = $record['uid'];
487 $allRecords[$index] = $record;
488 }
489
490 return $allRecords;
491 }
492
493 /**
494 * Format array as human readable string. Used to format verbose error messages in assertCSVDataSet
495 *
496 * @param array $array
497 * @return string
498 */
499 protected function arrayToString(array $array)
500 {
501 $elements = [];
502 foreach ($array as $key => $value) {
503 if (is_array($value)) {
504 $value = $this->arrayToString($value);
505 }
506 $elements[] = "'" . $key . "' => '" . $value . "'";
507 }
508 return 'array(' . PHP_EOL . ' ' . implode(', ' . PHP_EOL . ' ', $elements) . PHP_EOL . ')' . PHP_EOL;
509 }
510
511 /**
512 * Format output showing difference between expected and actual db row in a human readable way
513 * Used to format verbose error messages in assertCSVDataSet
514 *
515 * @param array $assertion
516 * @param array $record
517 * @return string
518 */
519 protected function renderRecords(array $assertion, array $record)
520 {
521 $differentFields = $this->getDifferentFields($assertion, $record);
522 $columns = [
523 'fields' => ['Fields'],
524 'assertion' => ['Assertion'],
525 'record' => ['Record'],
526 ];
527 $lines = [];
528 $linesFromXmlValues = [];
529 $result = '';
530
531 foreach ($differentFields as $differentField) {
532 $columns['fields'][] = $differentField;
533 $columns['assertion'][] = ($assertion[$differentField] === null ? 'NULL' : $assertion[$differentField]);
534 $columns['record'][] = ($record[$differentField] === null ? 'NULL' : $record[$differentField]);
535 }
536
537 foreach ($columns as $columnIndex => $column) {
538 $columnLength = null;
539 foreach ($column as $value) {
540 if (strpos($value, '<?xml') === 0) {
541 $value = '[see diff]';
542 }
543 $valueLength = strlen($value);
544 if (empty($columnLength) || $valueLength > $columnLength) {
545 $columnLength = $valueLength;
546 }
547 }
548 foreach ($column as $valueIndex => $value) {
549 if (strpos($value, '<?xml') === 0) {
550 if ($columnIndex === 'assertion') {
551 try {
552 $this->assertXmlStringEqualsXmlString((string)$value, (string)$record[$columns['fields'][$valueIndex]]);
553 } catch (\PHPUnit_Framework_ExpectationFailedException $e) {
554 $linesFromXmlValues[] = 'Diff for field "' . $columns['fields'][$valueIndex] . '":' . PHP_EOL . $e->getComparisonFailure()->getDiff();
555 }
556 }
557 $value = '[see diff]';
558 }
559 $lines[$valueIndex][$columnIndex] = str_pad($value, $columnLength, ' ');
560 }
561 }
562
563 foreach ($lines as $line) {
564 $result .= implode('|', $line) . PHP_EOL;
565 }
566
567 foreach ($linesFromXmlValues as $lineFromXmlValues) {
568 $result .= PHP_EOL . $lineFromXmlValues . PHP_EOL;
569 }
570
571 return $result;
572 }
573
574 /**
575 * Compares two arrays containing db rows and returns array containing column names which don't match
576 * It's a helper method used in assertCSVDataSet
577 *
578 * @param array $assertion
579 * @param array $record
580 * @return array
581 */
582 protected function getDifferentFields(array $assertion, array $record)
583 {
584 $differentFields = [];
585
586 foreach ($assertion as $field => $value) {
587 if (strpos($value, '\\*') === 0) {
588 continue;
589 } elseif (strpos($value, '<?xml') === 0) {
590 try {
591 $this->assertXmlStringEqualsXmlString((string)$value, (string)$record[$field]);
592 } catch (\PHPUnit_Framework_ExpectationFailedException $e) {
593 $differentFields[] = $field;
594 }
595 } elseif ($value === null && $record[$field] !== $value) {
596 $differentFields[] = $field;
597 } elseif ((string)$record[$field] !== (string)$value) {
598 $differentFields[] = $field;
599 }
600 }
601
602 return $differentFields;
603 }
604
605 /**
606 * @param int $pageId
607 * @param array $typoScriptFiles
608 */
609 protected function setUpFrontendRootPage($pageId, array $typoScriptFiles = [])
610 {
611 $pageId = (int)$pageId;
612
613 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
614 $page = $connection->select(['*'], 'pages', ['uid' => $pageId])->fetch();
615
616 if (empty($page)) {
617 $this->fail('Cannot set up frontend root page "' . $pageId . '"');
618 }
619
620 $databasePlatform = 'mysql';
621 if ($connection->getDatabasePlatform() instanceof PostgreSqlPlatform) {
622 $databasePlatform = 'postgresql';
623 }
624
625 $connection->update(
626 'pages',
627 ['is_siteroot' => 1],
628 ['uid' => $pageId]
629 );
630
631 $templateFields = [
632 'pid' => $pageId,
633 'title' => '',
634 'constants' => 'databasePlatform = ' . $databasePlatform . LF,
635 'config' => '',
636 'clear' => 3,
637 'root' => 1,
638 ];
639
640 foreach ($typoScriptFiles as $typoScriptFile) {
641 $templateFields['config'] .= '<INCLUDE_TYPOSCRIPT: source="FILE:' . $typoScriptFile . '">' . LF;
642 }
643 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_template');
644 $connection->delete('sys_template', ['pid' => $pageId]);
645 $connection->insert(
646 'sys_template',
647 $templateFields
648 );
649 }
650
651 /**
652 * @param int $pageId
653 * @param int $languageId
654 * @param int $backendUserId
655 * @param int $workspaceId
656 * @param bool $failOnFailure
657 * @param int $frontendUserId
658 * @return Response
659 */
660 protected function getFrontendResponse($pageId, $languageId = 0, $backendUserId = 0, $workspaceId = 0, $failOnFailure = true, $frontendUserId = 0)
661 {
662 $pageId = (int)$pageId;
663 $languageId = (int)$languageId;
664
665 $additionalParameter = '';
666
667 if (!empty($frontendUserId)) {
668 $additionalParameter .= '&frontendUserId=' . (int)$frontendUserId;
669 }
670 if (!empty($backendUserId)) {
671 $additionalParameter .= '&backendUserId=' . (int)$backendUserId;
672 }
673 if (!empty($workspaceId)) {
674 $additionalParameter .= '&workspaceId=' . (int)$workspaceId;
675 }
676
677 $arguments = [
678 'documentRoot' => $this->instancePath,
679 'requestUrl' => 'http://localhost/?id=' . $pageId . '&L=' . $languageId . $additionalParameter,
680 ];
681
682 $template = new \Text_Template(ORIGINAL_ROOT . 'typo3/sysext/core/Tests/Functional/Fixtures/Frontend/request.tpl');
683 $template->setVar(
684 [
685 'arguments' => var_export($arguments, true),
686 'originalRoot' => ORIGINAL_ROOT,
687 ]
688 );
689
690 $php = \PHPUnit_Util_PHP::factory();
691 $response = $php->runJob($template->render());
692 $result = json_decode($response['stdout'], true);
693
694 if ($result === null) {
695 $this->fail('Frontend Response is empty');
696 }
697
698 if ($failOnFailure && $result['status'] === Response::STATUS_Failure) {
699 $this->fail('Frontend Response has failure:' . LF . $result['error']);
700 }
701
702 $response = new Response($result['status'], $result['content'], $result['error']);
703 return $response;
704 }
705
706 /**
707 * Return the path to a XML fixture dependent on the current database platform that tests are run against.
708 *
709 * @param string $fileName
710 *
711 * @return string
712 * @throws \Exception
713 */
714 protected function getXmlFilePath(string $fileName): string
715 {
716 $baseDir = $this->fixturePath . $this->databasePlatform . '/';
717 $xmlFilePath = $baseDir . $fileName;
718
719 if (!file_exists($xmlFilePath)) {
720 throw new \Exception(
721 'XML fixture file "' . $xmlFilePath . '" not found for database platform: ' . $this->databasePlatform,
722 1487620903
723 );
724 }
725
726 return $xmlFilePath;
727 }
728
729 /**
730 * @return string
731 */
732 public function getDatabasePlatform(): string
733 {
734 return $this->databasePlatform;
735 }
736
737 /**
738 * @param string $databasePlatform
739 *
740 * @return $this
741 */
742 public function setDatabasePlatform(string $databasePlatform)
743 {
744 $this->databasePlatform = $databasePlatform;
745 return $this;
746 }
747
748 /**
749 * @return string
750 */
751 public function getFixturePath(): string
752 {
753 return $this->fixturePath;
754 }
755
756 /**
757 * @param string $fixturePath
758 *
759 * @return $this
760 */
761 public function setFixturePath(string $fixturePath)
762 {
763 $this->fixturePath = $fixturePath;
764 return $this;
765 }
766 }