‪TYPO3CMS  10.4
JsonViewTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
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 
19 
26 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
27 
31 class ‪JsonViewTest extends UnitTestCase
32 {
33  protected ‪$resetSingletonInstances = true;
34 
38  protected ‪$view;
39 
43  protected ‪$controllerContext;
44 
48  protected ‪$response;
49 
53  protected function ‪setUp(): void
54  {
55  parent::setUp();
56  $this->view = $this->getMockBuilder(JsonView::class)
57  ->setMethods(['loadConfigurationFromYamlFile'])
58  ->getMock();
59  $this->controllerContext = $this->createMock(ControllerContext::class);
60  $this->response = $this->createMock(Response::class);
61  $this->controllerContext->expects(self::any())->method('getResponse')->willReturn($this->response);
62  $this->view->setControllerContext($this->controllerContext);
63  }
64 
69  public function ‪jsonViewTestData(): array
70  {
71  ‪$output = [];
72 
73  $object = new \stdClass();
74  $object->value1 = 'foo';
75  $object->value2 = 1;
76  $configuration = [];
77  $expected = ['value1' => 'foo', 'value2' => 1];
78  ‪$output[] = [$object, $configuration, $expected, 'all direct child properties should be serialized'];
79 
80  $configuration = ['_only' => ['value1']];
81  $expected = ['value1' => 'foo'];
82  ‪$output[] = [$object, $configuration, $expected, 'if "only" properties are specified, only these should be serialized'];
83 
84  $configuration = ['_exclude' => ['value1']];
85  $expected = ['value2' => 1];
86  ‪$output[] = [$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 = [];
93  $expected = ['value2' => 1];
94  ‪$output[] = [$object, $configuration, $expected, 'by default, sub objects of objects should not be serialized.'];
95 
96  $object = new \stdClass();
97  $object->value1 = ['subarray' => 'value'];
98  $object->value2 = 1;
99  $configuration = [];
100  $expected = ['value2' => 1];
101  ‪$output[] = [$object, $configuration, $expected, 'by default, sub arrays of objects should not be serialized.'];
102 
103  $object = ['foo' => 'bar', 1 => 'baz', 'deep' => ['test' => 'value']];
104  $configuration = [];
105  $expected = ['foo' => 'bar', 1 => 'baz', 'deep' => ['test' => 'value']];
106  ‪$output[] = [$object, $configuration, $expected, 'associative arrays should be serialized deeply'];
107 
108  $object = ['foo', 'bar'];
109  $configuration = [];
110  $expected = ['foo', 'bar'];
111  ‪$output[] = [$object, $configuration, $expected, 'numeric arrays should be serialized'];
112 
113  $nestedObject = new \stdClass();
114  $nestedObject->value1 = 'foo';
115  $object = [$nestedObject];
116  $configuration = [];
117  $expected = [['value1' => 'foo']];
118  ‪$output[] = [$object, $configuration, $expected, 'array of objects should be serialized'];
119 
120  $properties = ['foo' => 'bar', 'prohibited' => 'xxx'];
121  $nestedObject = new class($properties) {
122  private $properties;
123  private $prohibited;
124  public function __construct($properties)
125  {
126  $this->properties = $properties;
127  }
128  public function getName()
129  {
130  return 'name';
131  }
132 
133  public function getPath()
134  {
135  return 'path';
136  }
137 
138  public function getProperties()
139  {
140  return $this->properties;
141  }
142  };
143  $object = $nestedObject;
144  $configuration = [
145  '_only' => ['name', 'path', 'properties'],
146  '_descend' => [
147  'properties' => [
148  '_exclude' => ['prohibited'],
149  ],
150  ],
151  ];
152  $expected = [
153  'name' => 'name',
154  'path' => 'path',
155  'properties' => ['foo' => 'bar'],
156  ];
157  ‪$output[] = [$object, $configuration, $expected, 'descending into arrays should be possible'];
158 
159  $nestedObject = new \stdClass();
160  $nestedObject->value1 = 'foo';
161  $value = new \SplObjectStorage();
162  $value->attach($nestedObject);
163  $configuration = [];
164  $expected = [['value1' => 'foo']];
165  ‪$output[] = [$value, $configuration, $expected, 'SplObjectStorage with objects should be serialized'];
166 
167  $dateTimeObject = new \DateTime('2011-02-03T03:15:23', new \DateTimeZone('UTC'));
168  $configuration = [];
169  $expected = '2011-02-03T03:15:23+00:00';
170  ‪$output[] = [$dateTimeObject, $configuration, $expected, 'DateTime object in UTC time zone could not be serialized.'];
171 
172  $dateTimeObject = new \DateTime('2013-08-15T15:25:30', new \DateTimeZone('America/Los_Angeles'));
173  $configuration = [];
174  $expected = '2013-08-15T15:25:30-07:00';
175  ‪$output[] = [$dateTimeObject, $configuration, $expected, 'DateTime object in America/Los_Angeles time zone could not be serialized.'];
176 
177  $dateTimeObject = new \DateTimeImmutable('2021-08-29T10:36:24', new \DateTimeZone('UTC'));
178  $configuration = [];
179  $expected = '2021-08-29T10:36:24+00:00';
180  ‪$output[] = [$dateTimeObject, $configuration, $expected, 'DateTimeImmutable object in UTC time zone should not be serialized.'];
181 
182  return ‪$output;
183  }
184 
193  public function ‪testTransformValue($object, array $configuration, $expected, string $description): void
194  {
195  $jsonView = $this->getAccessibleMock(JsonView::class, ['dummy'], [], '', false);
196 
197  $actual = $jsonView->_call('transformValue', $object, $configuration);
198 
199  self::assertSame($expected, $actual, $description);
200  }
201 
206  public function ‪objectIdentifierExposureTestData(): array
207  {
208  ‪$output = [];
209 
210  $dummyIdentifier = 'e4f40dfc-8c6e-4414-a5b1-6fd3c5cf7a53';
211 
212  $object = new \stdClass();
213  $object->value1 = new \stdClass();
214  $configuration = [
215  '_descend' => [
216  'value1' => [
217  '_exposeObjectIdentifier' => true,
218  ],
219  ],
220  ];
221 
222  $expected = ['value1' => ['__identity' => $dummyIdentifier]];
223  ‪$output[] = [$object, $configuration, $expected, $dummyIdentifier, 'boolean TRUE should result in __identity key'];
224 
225  $configuration['_descend']['value1']['_exposedObjectIdentifierKey'] = 'guid';
226  $expected = ['value1' => ['guid' => $dummyIdentifier]];
227  ‪$output[] = [$object, $configuration, $expected, $dummyIdentifier, 'string value should result in string-equal key'];
228 
229  return ‪$output;
230  }
231 
242  object $object,
243  array $configuration,
244  array $expected,
245  string $dummyIdentifier,
246  string $description
247  ): void {
248  $persistenceManagerMock = $this->getMockBuilder(PersistenceManager::class)
249  ->disableOriginalConstructor()
250  ->setMethods(['getIdentifierByObject'])
251  ->getMock();
252  $jsonView = $this->getAccessibleMock(JsonView::class, ['dummy'], [], '', false);
253  $jsonView->_set('persistenceManager', $persistenceManagerMock);
254 
255  $persistenceManagerMock->expects(self::once())->method('getIdentifierByObject')->with($object->value1)->willReturn($dummyIdentifier);
256 
257  $actual = $jsonView->_call('transformValue', $object, $configuration);
258 
259  self::assertSame($expected, $actual, $description);
260  }
261 
265  public function ‪exposeClassNameSettingsAndResults(): array
266  {
267  $className = ‪StringUtility::getUniqueId('DummyClass');
268  $namespace = 'TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\\' . $className;
269  return [
270  [
272  $className,
273  $namespace,
274  ['value1' => ['__class' => $namespace . '\\' . $className]],
275  ],
276  [
278  $className,
279  $namespace,
280  ['value1' => ['__class' => $className]],
281  ],
282  [
283  null,
284  $className,
285  $namespace,
286  ['value1' => []],
287  ]
288  ];
289  }
290 
300  ?int $exposeClassNameSetting,
301  string $className,
302  string $namespace,
303  array $expected
304  ): void {
305  $fullyQualifiedClassName = $namespace . '\\' . $className;
306  if (class_exists($fullyQualifiedClassName) === false) {
307  eval('namespace ' . $namespace . '; class ' . $className . ' {}');
308  }
309 
310  $object = new \stdClass();
311  $object->value1 = new $fullyQualifiedClassName();
312  $configuration = [
313  '_descend' => [
314  'value1' => [
315  '_exposeClassName' => $exposeClassNameSetting,
316  ],
317  ],
318  ];
319  $reflectionService = $this->getMockBuilder(ReflectionService::class)
320  ->setMethods([ 'getClassNameByObject' ])
321  ->getMock();
322  $reflectionService->expects(self::any())->method('getClassNameByObject')->willReturnCallback(function ($object) {
323  return get_class($object);
324  });
325 
326  $jsonView = $this->getAccessibleMock(JsonView::class, ['dummy'], [], '', false);
327  $jsonView->injectReflectionService($reflectionService);
328  $actual = $jsonView->_call('transformValue', $object, $configuration);
329  self::assertSame($expected, $actual);
330  }
331 
335  public function ‪renderSetsContentTypeHeader(): void
336  {
337  $this->response->expects(self::once())->method('setHeader')->with('Content-Type', 'application/json');
338 
339  $this->view->render();
340  }
341 
346  {
347  $object = new \stdClass();
348  $object->foo = 'Foo';
349  $this->view->assign('value', $object);
350 
351  $expectedResult = '{"foo":"Foo"}';
352  $actualResult = $this->view->render();
353  self::assertSame($expectedResult, $actualResult);
354  }
355 
360  {
361  $array = ['foo' => 'Foo', 'bar' => 'Bar'];
362  $this->view->assign('value', $array);
363 
364  $expectedResult = '{"foo":"Foo","bar":"Bar"}';
365  $actualResult = $this->view->render();
366  self::assertSame($expectedResult, $actualResult);
367  }
368 
373  {
374  $value = 'Foo';
375  $this->view->assign('value', $value);
376 
377  $expectedResult = '"Foo"';
378  $actualResult = $this->view->render();
379  self::assertSame($expectedResult, $actualResult);
380  }
381 
385  public function ‪renderKeepsUtf8CharactersUnescaped(): void
386  {
387  $value = 'Gürkchen';
388  $this->view->assign('value', $value);
389 
390  $actualResult = $this->view->render();
391 
392  $expectedResult = '"' . $value . '"';
393  self::assertSame($expectedResult, $actualResult);
394  }
395 
399  public function ‪escapeCharacterDataProvider(): array
400  {
401  return [
402  'backslash' => ['\\'],
403  'double quote' => ['"'],
404  ];
405  }
406 
412  public function ‪renderEscapesEscapeCharacters(string $character): void
413  {
414  $this->view->assign('value', $character);
415 
416  $actualResult = $this->view->render();
417 
418  $expectedResult = '"\\' . $character . '"';
419  self::assertSame($expectedResult, $actualResult);
420  }
421 
426  {
427  $value = 'Foo';
428  $this->view->assign('foo', $value);
429 
430  $expectedResult = 'null';
431  $actualResult = $this->view->render();
432  self::assertSame($expectedResult, $actualResult);
433  }
434 
438  public function ‪renderOnlyRendersVariableWithTheNameValue(): void
439  {
440  $this->view
441  ->assign('value', 'Value')
442  ->assign('someOtherVariable', 'Foo');
443 
444  $expectedResult = '"Value"';
445  $actualResult = $this->view->render();
446  self::assertSame($expectedResult, $actualResult);
447  }
448 
452  public function ‪setVariablesToRenderOverridesValueToRender(): void
453  {
454  $value = 'Foo';
455  $this->view->assign('foo', $value);
456  $this->view->setVariablesToRender(['foo']);
457 
458  $expectedResult = '"Foo"';
459  $actualResult = $this->view->render();
460  self::assertSame($expectedResult, $actualResult);
461  }
462 
467  {
468  $this->view
469  ->assign('value', 'Value1')
470  ->assign('secondValue', 'Value2')
471  ->assign('someOtherVariable', 'Value3');
472  $this->view->setVariablesToRender(['value', 'secondValue']);
473 
474  $expectedResult = '{"value":"Value1","secondValue":"Value2"}';
475  $actualResult = $this->view->render();
476  self::assertSame($expectedResult, $actualResult);
477  }
478 
482  public function ‪renderCanRenderMultipleComplexObjects(): void
483  {
484  $array = ['foo' => ['bar' => 'Baz']];
485  $object = new \stdClass();
486  $object->foo = 'Foo';
487 
488  $this->view
489  ->assign('array', $array)
490  ->assign('object', $object)
491  ->assign('someOtherVariable', 'Value3');
492  $this->view->setVariablesToRender(['array', 'object']);
493 
494  $expectedResult = '{"array":{"foo":{"bar":"Baz"}},"object":{"foo":"Foo"}}';
495  $actualResult = $this->view->render();
496  self::assertSame($expectedResult, $actualResult);
497  }
498 
502  public function ‪renderCanRenderPlainArray(): void
503  {
504  $array = [['name' => 'Foo', 'secret' => true], ['name' => 'Bar', 'secret' => true]];
505 
506  $this->view->assign('value', $array);
507  $this->view->setConfiguration([
508  'value' => [
509  '_descendAll' => [
510  '_only' => ['name'],
511  ],
512  ],
513  ]);
514 
515  $expectedResult = '[{"name":"Foo"},{"name":"Bar"}]';
516  $actualResult = $this->view->render();
517  self::assertSame($expectedResult, $actualResult);
518  }
519 
523  public function ‪renderCanRenderPlainArrayWithNumericKeys(): void
524  {
525  $array = [
526  'items' => [
527  ['name' => 'Foo'],
528  ['name' => 'Bar']
529  ],
530  ];
531 
532  $this->view->assign('value', $array);
533  $this->view->setConfiguration([
534  'value' => [
535  'items' => [
536  // note: this exclude is just here, and should have no effect as the items have numeric keys
537  '_exclude' => ['secret']
538  ]
539  ],
540  ]);
541 
542  $expectedResult = '{"items":[{"name":"Foo"},{"name":"Bar"}]}';
543  $actualResult = $this->view->render();
544  self::assertSame($expectedResult, $actualResult);
545  }
546 
550  public function ‪descendAllKeepsArrayIndexes(): void
551  {
552  $array = [['name' => 'Foo', 'secret' => true], ['name' => 'Bar', 'secret' => true]];
553 
554  $this->view->assign('value', $array);
555  $this->view->setConfiguration([
556  'value' => [
557  '_descendAll' => [
558  '_descendAll' => [],
559  ],
560  ],
561  ]);
562 
563  $expectedResult = '[{"name":"Foo","secret":true},{"name":"Bar","secret":true}]';
564  $actualResult = $this->view->render();
565  self::assertSame($expectedResult, $actualResult);
566  }
567 }
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\jsonViewTestData
‪array jsonViewTestData()
Definition: JsonViewTest.php:66
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderReturnsJsonRepresentationOfAssignedArray
‪renderReturnsJsonRepresentationOfAssignedArray()
Definition: JsonViewTest.php:356
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\testTransformValue
‪testTransformValue($object, array $configuration, $expected, string $description)
Definition: JsonViewTest.php:190
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderOnlyRendersVariableWithTheNameValue
‪renderOnlyRendersVariableWithTheNameValue()
Definition: JsonViewTest.php:435
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderKeepsUtf8CharactersUnescaped
‪renderKeepsUtf8CharactersUnescaped()
Definition: JsonViewTest.php:382
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\$controllerContext
‪ControllerContext $controllerContext
Definition: JsonViewTest.php:41
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\setUp
‪setUp()
Definition: JsonViewTest.php:50
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\testTransformValueWithObjectIdentifierExposure
‪testTransformValueWithObjectIdentifierExposure(object $object, array $configuration, array $expected, string $dummyIdentifier, string $description)
Definition: JsonViewTest.php:238
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\descendAllKeepsArrayIndexes
‪descendAllKeepsArrayIndexes()
Definition: JsonViewTest.php:547
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderSetsContentTypeHeader
‪renderSetsContentTypeHeader()
Definition: JsonViewTest.php:332
‪TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext
Definition: ControllerContext.php:28
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\exposeClassNameSettingsAndResults
‪exposeClassNameSettingsAndResults()
Definition: JsonViewTest.php:262
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\objectIdentifierExposureTestData
‪array objectIdentifierExposureTestData()
Definition: JsonViewTest.php:203
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\$view
‪TYPO3 CMS Extbase Mvc View JsonView $view
Definition: JsonViewTest.php:37
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderCanRenderPlainArray
‪renderCanRenderPlainArray()
Definition: JsonViewTest.php:499
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\$response
‪Response $response
Definition: JsonViewTest.php:45
‪TYPO3\CMS\Extbase\Reflection\ReflectionService
Definition: ReflectionService.php:31
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderReturnsJsonRepresentationOfAssignedObject
‪renderReturnsJsonRepresentationOfAssignedObject()
Definition: JsonViewTest.php:342
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\viewExposesClassNameFullyIfConfiguredSo
‪viewExposesClassNameFullyIfConfiguredSo(?int $exposeClassNameSetting, string $className, string $namespace, array $expected)
Definition: JsonViewTest.php:296
‪TYPO3\CMS\Extbase\Mvc\View\JsonView\EXPOSE_CLASSNAME_FULLY_QUALIFIED
‪const EXPOSE_CLASSNAME_FULLY_QUALIFIED
Definition: JsonView.php:38
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderRendersMultipleValuesIfTheyAreSpecifiedAsVariablesToRender
‪renderRendersMultipleValuesIfTheyAreSpecifiedAsVariablesToRender()
Definition: JsonViewTest.php:463
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\escapeCharacterDataProvider
‪string[][] escapeCharacterDataProvider()
Definition: JsonViewTest.php:396
‪TYPO3\CMS\Extbase\Mvc\Web\Response
Definition: Response.php:26
‪TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager
Definition: PersistenceManager.php:29
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderReturnsNullIfNameOfAssignedVariableIsNotEqualToValue
‪renderReturnsNullIfNameOfAssignedVariableIsNotEqualToValue()
Definition: JsonViewTest.php:422
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderReturnsJsonRepresentationOfAssignedSimpleValue
‪renderReturnsJsonRepresentationOfAssignedSimpleValue()
Definition: JsonViewTest.php:369
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\setVariablesToRenderOverridesValueToRender
‪setVariablesToRenderOverridesValueToRender()
Definition: JsonViewTest.php:449
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderEscapesEscapeCharacters
‪renderEscapesEscapeCharacters(string $character)
Definition: JsonViewTest.php:409
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View
Definition: JsonViewTest.php:18
‪$output
‪$output
Definition: annotationChecker.php:119
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:92
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\$resetSingletonInstances
‪$resetSingletonInstances
Definition: JsonViewTest.php:33
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderCanRenderPlainArrayWithNumericKeys
‪renderCanRenderPlainArrayWithNumericKeys()
Definition: JsonViewTest.php:520
‪TYPO3\CMS\Extbase\Mvc\View\JsonView\EXPOSE_CLASSNAME_UNQUALIFIED
‪const EXPOSE_CLASSNAME_UNQUALIFIED
Definition: JsonView.php:44
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest
Definition: JsonViewTest.php:32
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Extbase\Tests\Unit\Mvc\View\JsonViewTest\renderCanRenderMultipleComplexObjects
‪renderCanRenderMultipleComplexObjects()
Definition: JsonViewTest.php:479
‪TYPO3\CMS\Extbase\Mvc\View\JsonView
Definition: JsonView.php:29