[BUGFIX] Add missing namespace parts
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Tests / Unit / Mvc / View / JsonViewTest.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Tests\Unit\Mvc\View;
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\Mvc\View\JsonView;
18
19 /**
20 * Testcase for the JSON view
21 *
22 */
23 class JsonViewTest extends \TYPO3\Components\TestingFramework\Core\Unit\UnitTestCase
24 {
25 /**
26 * @var \TYPO3\CMS\Extbase\Mvc\View\JsonView
27 */
28 protected $view;
29
30 /**
31 * @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext
32 */
33 protected $controllerContext;
34
35 /**
36 * @var \TYPO3\CMS\Extbase\Mvc\Web\Response
37 */
38 protected $response;
39
40 /**
41 * Sets up this test case
42 * @return void
43 */
44 protected function setUp()
45 {
46 $this->view = $this->getMockBuilder(\TYPO3\CMS\Extbase\Mvc\View\JsonView::class)
47 ->setMethods(['loadConfigurationFromYamlFile'])
48 ->getMock();
49 $this->controllerContext = $this->createMock(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext::class);
50 $this->response = $this->createMock(\TYPO3\CMS\Extbase\Mvc\Web\Response::class);
51 $this->controllerContext->expects($this->any())->method('getResponse')->will($this->returnValue($this->response));
52 $this->view->setControllerContext($this->controllerContext);
53 }
54
55 /**
56 * data provider for testTransformValue()
57 * @return array
58 */
59 public function jsonViewTestData()
60 {
61 $output = [];
62
63 $object = new \stdClass();
64 $object->value1 = 'foo';
65 $object->value2 = 1;
66 $configuration = [];
67 $expected = ['value1' => 'foo', 'value2' => 1];
68 $output[] = [$object, $configuration, $expected, 'all direct child properties should be serialized'];
69
70 $configuration = ['_only' => ['value1']];
71 $expected = ['value1' => 'foo'];
72 $output[] = [$object, $configuration, $expected, 'if "only" properties are specified, only these should be serialized'];
73
74 $configuration = ['_exclude' => ['value1']];
75 $expected = ['value2' => 1];
76 $output[] = [$object, $configuration, $expected, 'if "exclude" properties are specified, they should not be serialized'];
77
78 $object = new \stdClass();
79 $object->value1 = new \stdClass();
80 $object->value1->subvalue1 = 'Foo';
81 $object->value2 = 1;
82 $configuration = [];
83 $expected = ['value2' => 1];
84 $output[] = [$object, $configuration, $expected, 'by default, sub objects of objects should not be serialized.'];
85
86 $object = new \stdClass();
87 $object->value1 = ['subarray' => 'value'];
88 $object->value2 = 1;
89 $configuration = [];
90 $expected = ['value2' => 1];
91 $output[] = [$object, $configuration, $expected, 'by default, sub arrays of objects should not be serialized.'];
92
93 $object = ['foo' => 'bar', 1 => 'baz', 'deep' => ['test' => 'value']];
94 $configuration = [];
95 $expected = ['foo' => 'bar', 1 => 'baz', 'deep' => ['test' => 'value']];
96 $output[] = [$object, $configuration, $expected, 'associative arrays should be serialized deeply'];
97
98 $object = ['foo', 'bar'];
99 $configuration = [];
100 $expected = ['foo', 'bar'];
101 $output[] = [$object, $configuration, $expected, 'numeric arrays should be serialized'];
102
103 $nestedObject = new \stdClass();
104 $nestedObject->value1 = 'foo';
105 $object = [$nestedObject];
106 $configuration = [];
107 $expected = [['value1' => 'foo']];
108 $output[] = [$object, $configuration, $expected, 'array of objects should be serialized'];
109
110 $properties = ['foo' => 'bar', 'prohibited' => 'xxx'];
111 $nestedObject = $this->getMockBuilder($this->getUniqueId('Test'))
112 ->setMethods(['getName', 'getPath', 'getProperties', 'getOther'])
113 ->getMock();
114 $nestedObject->expects($this->any())->method('getName')->will($this->returnValue('name'));
115 $nestedObject->expects($this->any())->method('getPath')->will($this->returnValue('path'));
116 $nestedObject->expects($this->any())->method('getProperties')->will($this->returnValue($properties));
117 $nestedObject->expects($this->never())->method('getOther');
118 $object = $nestedObject;
119 $configuration = [
120 '_only' => ['name', 'path', 'properties'],
121 '_descend' => [
122 'properties' => [
123 '_exclude' => ['prohibited']
124 ]
125 ]
126 ];
127 $expected = [
128 'name' => 'name',
129 'path' => 'path',
130 'properties' => ['foo' => 'bar']
131 ];
132 $output[] = [$object, $configuration, $expected, 'descending into arrays should be possible'];
133
134 $nestedObject = new \stdClass();
135 $nestedObject->value1 = 'foo';
136 $value = new \SplObjectStorage();
137 $value->attach($nestedObject);
138 $configuration = [];
139 $expected = [['value1' => 'foo']];
140 $output[] = [$value, $configuration, $expected, 'SplObjectStorage with objects should be serialized'];
141
142 $dateTimeObject = new \DateTime('2011-02-03T03:15:23', new \DateTimeZone('UTC'));
143 $configuration = [];
144 $expected = '2011-02-03T03:15:23+00:00';
145 $output[] = [$dateTimeObject, $configuration, $expected, 'DateTime object in UTC time zone could not be serialized.'];
146
147 $dateTimeObject = new \DateTime('2013-08-15T15:25:30', new \DateTimeZone('America/Los_Angeles'));
148 $configuration = [];
149 $expected = '2013-08-15T15:25:30-07:00';
150 $output[] = [$dateTimeObject, $configuration, $expected, 'DateTime object in America/Los_Angeles time zone could not be serialized.'];
151 return $output;
152 }
153
154 /**
155 * @test
156 * @dataProvider jsonViewTestData
157 */
158 public function testTransformValue($object, $configuration, $expected, $description)
159 {
160 $jsonView = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Mvc\View\JsonView::class, ['dummy'], [], '', false);
161
162 $actual = $jsonView->_call('transformValue', $object, $configuration);
163
164 $this->assertEquals($expected, $actual, $description);
165 }
166
167 /**
168 * data provider for testTransformValueWithObjectIdentifierExposure()
169 * @return array
170 */
171 public function objectIdentifierExposureTestData()
172 {
173 $output = [];
174
175 $dummyIdentifier = 'e4f40dfc-8c6e-4414-a5b1-6fd3c5cf7a53';
176
177 $object = new \stdClass();
178 $object->value1 = new \stdClass();
179 $configuration = [
180 '_descend' => [
181 'value1' => [
182 '_exposeObjectIdentifier' => true
183 ]
184 ]
185 ];
186
187 $expected = ['value1' => ['__identity' => $dummyIdentifier]];
188 $output[] = [$object, $configuration, $expected, $dummyIdentifier, 'boolean TRUE should result in __identity key'];
189
190 $configuration['_descend']['value1']['_exposedObjectIdentifierKey'] = 'guid';
191 $expected = ['value1' => ['guid' => $dummyIdentifier]];
192 $output[] = [$object, $configuration, $expected, $dummyIdentifier, 'string value should result in string-equal key'];
193
194 return $output;
195 }
196
197 /**
198 * @test
199 * @dataProvider objectIdentifierExposureTestData
200 */
201 public function testTransformValueWithObjectIdentifierExposure($object, $configuration, $expected, $dummyIdentifier, $description)
202 {
203 $persistenceManagerMock = $this->getMockBuilder(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class)
204 ->setMethods(['getIdentifierByObject'])
205 ->getMock();
206 $jsonView = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Mvc\View\JsonView::class, ['dummy'], [], '', false);
207 $jsonView->_set('persistenceManager', $persistenceManagerMock);
208
209 $persistenceManagerMock->expects($this->once())->method('getIdentifierByObject')->with($object->value1)->will($this->returnValue($dummyIdentifier));
210
211 $actual = $jsonView->_call('transformValue', $object, $configuration);
212
213 $this->assertEquals($expected, $actual, $description);
214 }
215
216 /**
217 * A data provider
218 */
219 public function exposeClassNameSettingsAndResults()
220 {
221 $className = $this->getUniqueId('DummyClass');
222 $namespace = 'TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\\' . $className;
223 return [
224 [
225 JsonView::EXPOSE_CLASSNAME_FULLY_QUALIFIED,
226 $className,
227 $namespace,
228 ['value1' => ['__class' => $namespace . '\\' . $className]]
229 ],
230 [
231 JsonView::EXPOSE_CLASSNAME_UNQUALIFIED,
232 $className,
233 $namespace,
234 ['value1' => ['__class' => $className]]
235 ],
236 [
237 null,
238 $className,
239 $namespace,
240 ['value1' => []]
241 ]
242 ];
243 }
244
245 /**
246 * @test
247 * @dataProvider exposeClassNameSettingsAndResults
248 */
249 public function viewExposesClassNameFullyIfConfiguredSo($exposeClassNameSetting, $className, $namespace, $expected)
250 {
251 $fullyQualifiedClassName = $namespace . '\\' . $className;
252 if (class_exists($fullyQualifiedClassName) === false) {
253 eval('namespace ' . $namespace . '; class ' . $className . ' {}');
254 }
255
256 $object = new \stdClass();
257 $object->value1 = new $fullyQualifiedClassName();
258 $configuration = [
259 '_descend' => [
260 'value1' => [
261 '_exposeClassName' => $exposeClassNameSetting
262 ]
263 ]
264 ];
265 $reflectionService = $this->getMockBuilder(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class)
266 ->setMethods([ 'getClassNameByObject' ])
267 ->getMock();
268 $reflectionService->expects($this->any())->method('getClassNameByObject')->will($this->returnCallback(function ($object) {
269 return get_class($object);
270 }));
271
272 $jsonView = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Mvc\View\JsonView::class, ['dummy'], [], '', false);
273 $this->inject($jsonView, 'reflectionService', $reflectionService);
274 $actual = $jsonView->_call('transformValue', $object, $configuration);
275 $this->assertEquals($expected, $actual);
276 }
277
278 /**
279 * @test
280 */
281 public function renderSetsContentTypeHeader()
282 {
283 $this->response->expects($this->once())->method('setHeader')->with('Content-Type', 'application/json');
284
285 $this->view->render();
286 }
287
288 /**
289 * @test
290 */
291 public function renderReturnsJsonRepresentationOfAssignedObject()
292 {
293 $object = new \stdClass();
294 $object->foo = 'Foo';
295 $this->view->assign('value', $object);
296
297 $expectedResult = '{"foo":"Foo"}';
298 $actualResult = $this->view->render();
299 $this->assertEquals($expectedResult, $actualResult);
300 }
301
302 /**
303 * @test
304 */
305 public function renderReturnsJsonRepresentationOfAssignedArray()
306 {
307 $array = ['foo' => 'Foo', 'bar' => 'Bar'];
308 $this->view->assign('value', $array);
309
310 $expectedResult = '{"foo":"Foo","bar":"Bar"}';
311 $actualResult = $this->view->render();
312 $this->assertEquals($expectedResult, $actualResult);
313 }
314
315 /**
316 * @test
317 */
318 public function renderReturnsJsonRepresentationOfAssignedSimpleValue()
319 {
320 $value = 'Foo';
321 $this->view->assign('value', $value);
322
323 $expectedResult = '"Foo"';
324 $actualResult = $this->view->render();
325 $this->assertEquals($expectedResult, $actualResult);
326 }
327
328 /**
329 * @test
330 */
331 public function renderReturnsNullIfNameOfAssignedVariableIsNotEqualToValue()
332 {
333 $value = 'Foo';
334 $this->view->assign('foo', $value);
335
336 $expectedResult = 'null';
337 $actualResult = $this->view->render();
338 $this->assertEquals($expectedResult, $actualResult);
339 }
340
341 /**
342 * @test
343 */
344 public function renderOnlyRendersVariableWithTheNameValue()
345 {
346 $this->view
347 ->assign('value', 'Value')
348 ->assign('someOtherVariable', 'Foo');
349
350 $expectedResult = '"Value"';
351 $actualResult = $this->view->render();
352 $this->assertEquals($expectedResult, $actualResult);
353 }
354
355 /**
356 * @test
357 */
358 public function setVariablesToRenderOverridesValueToRender()
359 {
360 $value = 'Foo';
361 $this->view->assign('foo', $value);
362 $this->view->setVariablesToRender(['foo']);
363
364 $expectedResult = '"Foo"';
365 $actualResult = $this->view->render();
366 $this->assertEquals($expectedResult, $actualResult);
367 }
368
369 /**
370 * @test
371 */
372 public function renderRendersMultipleValuesIfTheyAreSpecifiedAsVariablesToRender()
373 {
374 $this->view
375 ->assign('value', 'Value1')
376 ->assign('secondValue', 'Value2')
377 ->assign('someOtherVariable', 'Value3');
378 $this->view->setVariablesToRender(['value', 'secondValue']);
379
380 $expectedResult = '{"value":"Value1","secondValue":"Value2"}';
381 $actualResult = $this->view->render();
382 $this->assertEquals($expectedResult, $actualResult);
383 }
384
385 /**
386 * @test
387 */
388 public function renderCanRenderMultipleComplexObjects()
389 {
390 $array = ['foo' => ['bar' => 'Baz']];
391 $object = new \stdClass();
392 $object->foo = 'Foo';
393
394 $this->view
395 ->assign('array', $array)
396 ->assign('object', $object)
397 ->assign('someOtherVariable', 'Value3');
398 $this->view->setVariablesToRender(['array', 'object']);
399
400 $expectedResult = '{"array":{"foo":{"bar":"Baz"}},"object":{"foo":"Foo"}}';
401 $actualResult = $this->view->render();
402 $this->assertEquals($expectedResult, $actualResult);
403 }
404
405 /**
406 * @test
407 */
408 public function renderCanRenderPlainArray()
409 {
410 $array = [['name' => 'Foo', 'secret' => true], ['name' => 'Bar', 'secret' => true]];
411
412 $this->view->assign('value', $array);
413 $this->view->setConfiguration([
414 'value' => [
415 '_descendAll' => [
416 '_only' => ['name']
417 ]
418 ]
419 ]);
420
421 $expectedResult = '[{"name":"Foo"},{"name":"Bar"}]';
422 $actualResult = $this->view->render();
423 $this->assertEquals($expectedResult, $actualResult);
424 }
425
426 /**
427 * @test
428 */
429 public function descendAllKeepsArrayIndexes()
430 {
431 $array = [['name' => 'Foo', 'secret' => true], ['name' => 'Bar', 'secret' => true]];
432
433 $this->view->assign('value', $array);
434 $this->view->setConfiguration([
435 'value' => [
436 '_descendAll' => [
437 '_descendAll' => []
438 ]
439 ]
440 ]);
441
442 $expectedResult = '[{"name":"Foo","secret":true},{"name":"Bar","secret":true}]';
443 $actualResult = $this->view->render();
444 $this->assertEquals($expectedResult, $actualResult);
445 }
446 }