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