[TASK] Add framework for frontend rendering functional tests
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Functional / DataHandling / AbstractDataHandlerActionTestCase.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Functional\DataHandling;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2014 Oliver Hader <oliver.hader@typo3.org>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28 use TYPO3\CMS\Core\Tests\Functional\DataHandling\Framework\DataSet;
29 use TYPO3\CMS\Core\Tests\Functional\Framework\Frontend\Response;
30 use TYPO3\CMS\Core\Tests\Functional\Framework\Frontend\ResponseContent;
31
32 /**
33 * Functional test for the DataHandler
34 */
35 abstract class AbstractDataHandlerActionTestCase extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
36
37 const VALUE_BackendUserId = 1;
38
39 /**
40 * @var string
41 */
42 protected $dataSetDirectory;
43
44 /**
45 * @var array
46 */
47 protected $testExtensionsToLoad = array(
48 'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial',
49 // 'typo3conf/ext/datahandler',
50 );
51
52 /**
53 * @var array
54 */
55 protected $pathsToLinkInTestInstance = array(
56 'typo3/sysext/core/Tests/Functional/Fixtures/Frontend/extTables.php' => 'typo3conf/extTables.php',
57 );
58
59 /**
60 * @var \TYPO3\CMS\Core\Tests\Functional\DataHandling\Framework\ActionService
61 */
62 protected $actionService;
63
64 /**
65 * @var \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
66 */
67 protected $backendUser;
68
69 public function setUp() {
70 parent::setUp();
71
72 $this->backendUser = $this->setUpBackendUserFromFixture(self::VALUE_BackendUserId);
73 // By default make tests on live workspace
74 $this->backendUser->workspace = 0;
75
76 $this->actionService = $this->getActionService();
77 \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->initializeLanguageObject();
78 }
79
80 public function tearDown() {
81 unset($this->actionService);
82 }
83
84 /**
85 * @return \TYPO3\CMS\Core\DataHandling\DataHandler
86 */
87 protected function getDataHandler() {
88 $dataHandler = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
89 return $dataHandler;
90 }
91
92 /**
93 * @return \TYPO3\CMS\Core\Tests\Functional\DataHandling\Framework\ActionService
94 */
95 protected function getActionService() {
96 return GeneralUtility::makeInstance(
97 'TYPO3\\CMS\\Core\\Tests\\Functional\\DataHandling\\Framework\\ActionService',
98 $this->getDataHandler()
99 );
100 }
101
102 /**
103 * @param string $dataSetName
104 */
105 protected function importScenarioDataSet($dataSetName) {
106 $fileName = rtrim($this->dataSetDirectory, '/') . '/Scenario/' . $dataSetName . '.csv';
107 $fileName = GeneralUtility::getFileAbsFileName($fileName);
108
109 $dataSet = DataSet::read($fileName);
110
111 foreach ($dataSet->getTableNames() as $tableName) {
112 foreach ($dataSet->getElements($tableName) as $element) {
113 $this->getDatabase()->exec_INSERTquery(
114 $tableName,
115 $element
116 );
117 $sqlError = $this->getDatabase()->sql_error();
118 if (!empty($sqlError)) {
119 $this->fail('SQL Error for table "' . $tableName . '": ' . LF . $sqlError);
120 }
121 }
122 }
123 }
124
125 protected function assertAssertionDataSet($dataSetName) {
126 $fileName = rtrim($this->dataSetDirectory, '/') . '/Assertion/' . $dataSetName . '.csv';
127 $fileName = GeneralUtility::getFileAbsFileName($fileName);
128
129 $dataSet = DataSet::read($fileName);
130
131 foreach ($dataSet->getTableNames() as $tableName) {
132 $hasUidField = ($dataSet->getIdIndex($tableName) !== NULL);
133 $records = $this->getAllRecords($tableName, $hasUidField);
134 foreach ($dataSet->getElements($tableName) as $assertion) {
135 $result = $this->assertInRecords($assertion, $records);
136 if ($result === FALSE) {
137 if ($hasUidField && empty($records[$assertion['uid']])) {
138 $this->fail('Record "' . $tableName . ':' . $assertion['uid'] . '" not found in database');
139 }
140 $recordIdentifier = $tableName . ($hasUidField ? ':' . $assertion['uid'] : '');
141 $additionalInformation = ($hasUidField ? $this->renderRecords($assertion, $records[$assertion['uid']]) : $this->arrayToString($assertion));
142 $this->fail('Assertion in data-set failed for "' . $recordIdentifier . '":' . LF . $additionalInformation);
143 } else {
144 // Unset asserted record
145 unset($records[$result]);
146 // Increase assertion counter
147 $this->assertTrue($result !== FALSE);
148 }
149 }
150 }
151 }
152
153 /**
154 * @param array $assertion
155 * @param array $records
156 * @return bool|int|string
157 */
158 protected function assertInRecords(array $assertion, array $records) {
159 foreach ($records as $index => $record) {
160 $differentFields = $this->getDifferentFields($assertion, $record);
161
162 if (empty($differentFields)) {
163 return $index;
164 }
165 }
166
167 return FALSE;
168 }
169
170 /**
171 * @param string $tableName
172 * @param bool $hasUidField
173 * @return array
174 */
175 protected function getAllRecords($tableName, $hasUidField = FALSE) {
176 $allRecords = array();
177
178 $records = $this->getDatabase()->exec_SELECTgetRows(
179 '*',
180 $tableName,
181 '1=1',
182 '',
183 '',
184 '',
185 ($hasUidField ? 'uid' : '')
186 );
187
188 if (!empty($records)) {
189 $allRecords = $records;
190 }
191
192 return $allRecords;
193 }
194
195 /**
196 * @param array $array
197 * @return string
198 */
199 protected function arrayToString(array $array) {
200 $elements = array();
201 foreach ($array as $key => $value) {
202 if (is_array($value)) {
203 $value = $this->arrayToString($value);
204 }
205 $elements[] = "'" . $key . "' => '" . $value . "'";
206 }
207 return 'array(' . implode(', ', $elements) . ')';
208 }
209
210 /**
211 * @param array $assertion
212 * @param array $record
213 * @return string
214 */
215 protected function renderRecords(array $assertion, array $record) {
216 $differentFields = $this->getDifferentFields($assertion, $record);
217 $columns = array(
218 'fields' => array('Fields'),
219 'assertion' => array('Assertion'),
220 'record' => array('Record'),
221 );
222 $lines = array();
223 $result = '';
224
225 foreach ($differentFields as $differentField) {
226 $columns['fields'][] = $differentField;
227 $columns['assertion'][] = ($assertion[$differentField] === NULL ? 'NULL' : $assertion[$differentField]);
228 $columns['record'][] = ($record[$differentField] === NULL ? 'NULL' : $record[$differentField]);
229 }
230
231 foreach ($columns as $columnIndex => $column) {
232 $columnLength = NULL;
233 foreach ($column as $value) {
234 $valueLength = strlen($value);
235 if (empty($columnLength) || $valueLength > $columnLength) {
236 $columnLength = $valueLength;
237 }
238 }
239 foreach ($column as $valueIndex => $value) {
240 $lines[$valueIndex][$columnIndex] = str_pad($value, $columnLength, ' ');
241 }
242 }
243
244 foreach ($lines as $line) {
245 $result .= implode('|', $line) . PHP_EOL;
246 }
247
248 return $result;
249 }
250
251 /**
252 * @param array $assertion
253 * @param array $record
254 * @return array
255 */
256 protected function getDifferentFields(array $assertion, array $record) {
257 $differentFields = array();
258
259 foreach ($assertion as $field => $value) {
260 if (strpos($value, '\\*') === 0) {
261 continue;
262 } elseif ((string)$record[$field] !== (string)$value) {
263 $differentFields[] = $field;
264 }
265 }
266
267 return $differentFields;
268 }
269
270 /**
271 * @param ResponseContent $responseContent
272 * @param string $structureRecordIdentifier
273 * @param string $structureFieldName
274 * @param string $tableName
275 * @param string $fieldName
276 * @param string|array $values
277 */
278 protected function assertResponseContentStructureHasRecords(ResponseContent $responseContent, $structureRecordIdentifier, $structureFieldName, $tableName, $fieldName, $values) {
279 $nonMatchingVariants = array();
280
281 foreach ($responseContent->findStructures($structureRecordIdentifier, $structureFieldName) as $path => $structure) {
282 $nonMatchingValues = $this->getNonMatchingValuesFrontendResponseRecords($structure, $tableName, $fieldName, $values);
283
284 if (empty($nonMatchingValues)) {
285 // Increase assertion counter
286 $this->assertEmpty($nonMatchingValues);
287 return;
288 }
289
290 $nonMatchingVariants[$path] = $nonMatchingValues;
291 }
292
293 $nonMatchingMessage = '';
294 foreach ($nonMatchingVariants as $path => $nonMatchingValues) {
295 $nonMatchingMessage .= '* ' . $path . ': ' . implode(', ', $nonMatchingValues);
296 }
297
298 $this->fail('Could not assert all values for "' . $tableName . '.' . $fieldName . '"' . LF . $nonMatchingMessage);
299 }
300
301 /**
302 * @param ResponseContent $responseContent
303 * @param string $structureRecordIdentifier
304 * @param string $structureFieldName
305 * @param string $tableName
306 * @param string $fieldName
307 * @param string|array $values
308 */
309 protected function assertResponseContentStructureDoesNotHaveRecords(ResponseContent $responseContent, $structureRecordIdentifier, $structureFieldName, $tableName, $fieldName, $values) {
310 if (is_string($values)) {
311 $values = array($values);
312 }
313
314 $matchingVariants = array();
315
316 foreach ($responseContent->findStructures($structureRecordIdentifier, $structureFieldName) as $path => $structure) {
317 $nonMatchingValues = $this->getNonMatchingValuesFrontendResponseRecords($structure, $tableName, $fieldName, $values);
318 $matchingValues = array_diff($values, $nonMatchingValues);
319
320 if (!empty($matchingValues)) {
321 $matchingVariants[$path] = $matchingValues;
322 }
323 }
324
325 if (empty($matchingVariants)) {
326 // Increase assertion counter
327 $this->assertEmpty($matchingVariants);
328 return;
329 }
330
331 $matchingMessage = '';
332 foreach ($matchingVariants as $path => $matchingValues) {
333 $matchingMessage .= '* ' . $path . ': ' . implode(', ', $matchingValues);
334 }
335
336 $this->fail('Could not assert not having values for "' . $tableName . '.' . $fieldName . '"' . LF . $matchingMessage);
337 }
338
339 /**
340 * @param ResponseContent $responseContent
341 * @param string $tableName
342 * @param string $fieldName
343 * @param string|array $values
344 */
345 protected function assertResponseContentHasRecords(ResponseContent $responseContent, $tableName, $fieldName, $values) {
346 $nonMatchingValues = $this->getNonMatchingValuesFrontendResponseRecords($responseContent->getRecords(), $tableName, $fieldName, $values);
347
348 if (!empty($nonMatchingValues)) {
349 $this->fail('Could not assert all values for "' . $tableName . '.' . $fieldName . '": ' . implode(', ', $nonMatchingValues));
350 }
351
352 // Increase assertion counter
353 $this->assertEmpty($nonMatchingValues);
354 }
355
356 /**
357 * @param ResponseContent $responseContent
358 * @param string $tableName
359 * @param string $fieldName
360 * @param string|array $values
361 */
362 protected function assertResponseContentDoesNotHaveRecords(ResponseContent $responseContent, $tableName, $fieldName, $values) {
363 if (is_string($values)) {
364 $values = array($values);
365 }
366
367 $nonMatchingValues = $this->getNonMatchingValuesFrontendResponseRecords($responseContent->getRecords(), $tableName, $fieldName, $values);
368 $matchingValues = array_diff($values, $nonMatchingValues);
369
370 if (!empty($matchingValues)) {
371 $this->fail('Could not assert not having values for "' . $tableName . '.' . $fieldName . '": ' . implode(', ', $matchingValues));
372 }
373
374 // Increase assertion counter
375 $this->assertTrue(TRUE);
376 }
377
378 /**
379 * @param string|array $data
380 * @param string $tableName
381 * @param string $fieldName
382 * @param string|array $values
383 * @return array
384 */
385 protected function getNonMatchingValuesFrontendResponseRecords($data, $tableName, $fieldName, $values) {
386 if (empty($data) || !is_array($data)) {
387 $this->fail('Frontend Response data does not have any records');
388 }
389
390 if (is_string($values)) {
391 $values = array($values);
392 }
393
394 foreach ($data as $recordIdentifier => $recordData) {
395 if (strpos($recordIdentifier, $tableName . ':') !== 0) {
396 continue;
397 }
398
399 if (($foundValueIndex = array_search($recordData[$fieldName], $values)) !== FALSE) {
400 unset($values[$foundValueIndex]);
401 }
402 }
403
404 return $values;
405 }
406
407 }