3 namespace Drupal\Tests\rest\Functional\Views;
5 use Drupal\Component\Serialization\Json;
6 use Drupal\Core\Cache\Cache;
7 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
8 use Drupal\Core\Field\FieldStorageDefinitionInterface;
9 use Drupal\entity_test\Entity\EntityTest;
10 use Drupal\field\Entity\FieldConfig;
11 use Drupal\field\Entity\FieldStorageConfig;
12 use Drupal\language\Entity\ConfigurableLanguage;
13 use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
14 use Drupal\Tests\views\Functional\ViewTestBase;
15 use Drupal\views\Entity\View;
16 use Drupal\views\Plugin\views\display\DisplayPluginBase;
17 use Drupal\views\Views;
18 use Drupal\views\Tests\ViewTestData;
19 use Symfony\Component\HttpFoundation\Request;
22 * Tests the serializer style plugin.
25 * @see \Drupal\rest\Plugin\views\display\RestExport
26 * @see \Drupal\rest\Plugin\views\style\Serializer
27 * @see \Drupal\rest\Plugin\views\row\DataEntityRow
28 * @see \Drupal\rest\Plugin\views\row\DataFieldRow
30 class StyleSerializerTest extends ViewTestBase {
32 use AssertPageCacheContextsAndTagsTrait;
39 public static $modules = ['views_ui', 'entity_test', 'hal', 'rest_test_views', 'node', 'text', 'field', 'language', 'basic_auth'];
42 * Views used by this test.
46 public static $testViews = ['test_serializer_display_field', 'test_serializer_display_entity', 'test_serializer_display_entity_translated', 'test_serializer_node_display_field', 'test_serializer_node_exposed_filter'];
49 * A user with administrative privileges to look at test entity and configure views.
53 protected function setUp($import_test_views = TRUE) {
54 parent::setUp($import_test_views);
56 ViewTestData::createTestViews(get_class($this), ['rest_test_views']);
58 $this->adminUser = $this->drupalCreateUser(['administer views', 'administer entity_test content', 'access user profiles', 'view test entity']);
60 // Save some entity_test entities.
61 for ($i = 1; $i <= 10; $i++) {
62 EntityTest::create(['name' => 'test_' . $i, 'user_id' => $this->adminUser->id()])->save();
65 $this->enableViewsTestModule();
69 * Checks that the auth options restricts access to a REST views display.
71 public function testRestViewsAuthentication() {
72 // Assume the view is hidden behind a permission.
73 $this->drupalGet('test/serialize/auth_with_perm', ['query' => ['_format' => 'json']]);
74 $this->assertResponse(401);
76 // Not even logging in would make it possible to see the view, because then
77 // we are denied based on authentication method (cookie).
78 $this->drupalLogin($this->adminUser);
79 $this->drupalGet('test/serialize/auth_with_perm', ['query' => ['_format' => 'json']]);
80 $this->assertResponse(403);
81 $this->drupalLogout();
83 // But if we use the basic auth authentication strategy, we should be able
85 $url = $this->buildUrl('test/serialize/auth_with_perm');
86 $response = \Drupal::httpClient()->get($url, [
87 'auth' => [$this->adminUser->getUsername(), $this->adminUser->pass_raw],
90 // Ensure that any changes to variables in the other thread are picked up.
91 $this->refreshVariables();
93 $this->assertResponse(200);
97 * Checks the behavior of the Serializer callback paths and row plugins.
99 public function testSerializerResponses() {
100 // Test the serialize callback.
101 $view = Views::getView('test_serializer_display_field');
102 $view->initDisplay();
103 $this->executeView($view);
105 $actual_json = $this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
106 $this->assertResponse(200);
107 $this->assertCacheTags($view->getCacheTags());
108 $this->assertCacheContexts(['languages:language_interface', 'theme', 'request_format']);
109 // @todo Due to https://www.drupal.org/node/2352009 we can't yet test the
110 // propagation of cache max-age.
112 // Test the http Content-type.
113 $headers = $this->drupalGetHeaders();
114 $this->assertSame(['application/json'], $headers['Content-Type']);
117 foreach ($view->result as $row) {
119 foreach ($view->field as $id => $field) {
120 $expected_row[$id] = $field->render($row);
122 $expected[] = $expected_row;
125 $this->assertIdentical($actual_json, json_encode($expected), 'The expected JSON output was found.');
127 // Test that the rendered output and the preview output are the same.
129 $view->setDisplay('rest_export_1');
130 // Mock the request content type by setting it on the display handler.
131 $view->display_handler->setContentType('json');
132 $output = $view->preview();
133 $this->assertIdentical($actual_json, (string) drupal_render_root($output), 'The expected JSON preview output was found.');
135 // Test a 403 callback.
136 $this->drupalGet('test/serialize/denied');
137 $this->assertResponse(403);
139 // Test the entity rows.
140 $view = Views::getView('test_serializer_display_entity');
141 $view->initDisplay();
142 $this->executeView($view);
144 // Get the serializer service.
145 $serializer = $this->container->get('serializer');
148 foreach ($view->result as $row) {
149 $entities[] = $row->_entity;
152 $expected = $serializer->serialize($entities, 'json');
154 $actual_json = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]);
155 $this->assertResponse(200);
156 $this->assertIdentical($actual_json, $expected, 'The expected JSON output was found.');
157 $expected_cache_tags = $view->getCacheTags();
158 $expected_cache_tags[] = 'entity_test_list';
159 /** @var \Drupal\Core\Entity\EntityInterface $entity */
160 foreach ($entities as $entity) {
161 $expected_cache_tags = Cache::mergeTags($expected_cache_tags, $entity->getCacheTags());
163 $this->assertCacheTags($expected_cache_tags);
164 $this->assertCacheContexts(['languages:language_interface', 'theme', 'entity_test_view_grants', 'request_format']);
166 $expected = $serializer->serialize($entities, 'hal_json');
167 $actual_json = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'hal_json']]);
168 $this->assertIdentical($actual_json, $expected, 'The expected HAL output was found.');
169 $this->assertCacheTags($expected_cache_tags);
171 // Change the default format to xml.
172 $view->setDisplay('rest_export_1');
173 $view->getDisplay()->setOption('style', [
174 'type' => 'serializer',
176 'uses_fields' => FALSE,
183 $expected = $serializer->serialize($entities, 'xml');
184 $actual_xml = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'xml']]);
185 $this->assertSame(trim($expected), $actual_xml);
186 $this->assertCacheContexts(['languages:language_interface', 'theme', 'entity_test_view_grants', 'request_format']);
188 // Allow multiple formats.
189 $view->setDisplay('rest_export_1');
190 $view->getDisplay()->setOption('style', [
191 'type' => 'serializer',
193 'uses_fields' => FALSE,
201 $expected = $serializer->serialize($entities, 'json');
202 $actual_json = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]);
203 $this->assertIdentical($actual_json, $expected, 'The expected JSON output was found.');
204 $expected = $serializer->serialize($entities, 'xml');
205 $actual_xml = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'xml']]);
206 $this->assertSame(trim($expected), $actual_xml);
210 * Verifies site maintenance mode functionality.
212 public function testSiteMaintenance() {
213 $view = Views::getView('test_serializer_display_field');
214 $view->initDisplay();
215 $this->executeView($view);
217 // Set the site to maintenance mode.
218 $this->container->get('state')->set('system.maintenance_mode', TRUE);
220 $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]);
221 // Verify that the endpoint is unavailable for anonymous users.
222 $this->assertResponse(503);
226 * Sets up a request on the request stack with a specified format.
228 * @param string $format
229 * The new request format.
231 protected function addRequestWithFormat($format) {
232 $request = \Drupal::request();
233 $request = clone $request;
234 $request->setRequestFormat($format);
236 \Drupal::requestStack()->push($request);
240 * Tests REST export with views render caching enabled.
242 public function testRestRenderCaching() {
243 $this->drupalLogin($this->adminUser);
244 /** @var \Drupal\Core\Render\RenderCacheInterface $render_cache */
245 $render_cache = \Drupal::service('render_cache');
247 // Enable render caching for the views.
248 /** @var \Drupal\views\ViewEntityInterface $storage */
249 $storage = View::load('test_serializer_display_entity');
250 $options = &$storage->getDisplay('default');
251 $options['display_options']['cache'] = [
256 $original = DisplayPluginBase::buildBasicRenderable('test_serializer_display_entity', 'rest_export_1');
258 // Ensure that there is no corresponding render cache item yet.
259 $original['#cache'] += ['contexts' => []];
260 $original['#cache']['contexts'] = Cache::mergeContexts($original['#cache']['contexts'], $this->container->getParameter('renderer.config')['required_cache_contexts']);
263 'config:views.view.test_serializer_display_entity',
277 'entity_test_view_grants',
278 'languages:language_interface',
283 $this->assertFalse($render_cache->get($original));
285 // Request the page, once in XML and once in JSON to ensure that the caching
287 $result1 = Json::decode($this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]));
288 $this->addRequestWithFormat('json');
289 $this->assertHeader('content-type', 'application/json');
290 $this->assertCacheContexts($cache_contexts);
291 $this->assertCacheTags($cache_tags);
292 $this->assertTrue($render_cache->get($original));
294 $result_xml = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'xml']]);
295 $this->addRequestWithFormat('xml');
296 $this->assertHeader('content-type', 'text/xml; charset=UTF-8');
297 $this->assertCacheContexts($cache_contexts);
298 $this->assertCacheTags($cache_tags);
299 $this->assertTrue($render_cache->get($original));
301 // Ensure that the XML output is different from the JSON one.
302 $this->assertNotEqual($result1, $result_xml);
304 // Ensure that the cached page works.
305 $result2 = Json::decode($this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]));
306 $this->addRequestWithFormat('json');
307 $this->assertHeader('content-type', 'application/json');
308 $this->assertEqual($result2, $result1);
309 $this->assertCacheContexts($cache_contexts);
310 $this->assertCacheTags($cache_tags);
311 $this->assertTrue($render_cache->get($original));
313 // Create a new entity and ensure that the cache tags are taken over.
314 EntityTest::create(['name' => 'test_11', 'user_id' => $this->adminUser->id()])->save();
315 $result3 = Json::decode($this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]));
316 $this->addRequestWithFormat('json');
317 $this->assertHeader('content-type', 'application/json');
318 $this->assertNotEqual($result3, $result2);
320 // Add the new entity cache tag and remove the first one, because we just
321 // show 10 items in total.
322 $cache_tags[] = 'entity_test:11';
323 unset($cache_tags[array_search('entity_test:1', $cache_tags)]);
325 $this->assertCacheContexts($cache_contexts);
326 $this->assertCacheTags($cache_tags);
327 $this->assertTrue($render_cache->get($original));
331 * Tests the response format configuration.
333 public function testResponseFormatConfiguration() {
334 $this->drupalLogin($this->adminUser);
336 $style_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/style_options';
338 // Select only 'xml' as an accepted format.
339 $this->drupalPostForm($style_options, ['style_options[formats][xml]' => 'xml'], t('Apply'));
340 $this->drupalPostForm(NULL, [], t('Save'));
342 // Should return a 406.
343 $this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
344 $this->assertHeader('content-type', 'application/json');
345 $this->assertResponse(406, 'A 406 response was returned when JSON was requested.');
346 // Should return a 200.
347 $this->drupalGet('test/serialize/field', ['query' => ['_format' => 'xml']]);
348 $this->assertHeader('content-type', 'text/xml; charset=UTF-8');
349 $this->assertResponse(200, 'A 200 response was returned when XML was requested.');
351 // Add 'json' as an accepted format, so we have multiple.
352 $this->drupalPostForm($style_options, ['style_options[formats][json]' => 'json'], t('Apply'));
353 $this->drupalPostForm(NULL, [], t('Save'));
355 // Should return a 200.
356 // @todo This should be fixed when we have better content negotiation.
357 $this->drupalGet('test/serialize/field');
358 $this->assertHeader('content-type', 'application/json');
359 $this->assertResponse(200, 'A 200 response was returned when any format was requested.');
361 // Should return a 200. Emulates a sample Firefox header.
362 $this->drupalGet('test/serialize/field', [], ['Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8']);
363 $this->assertHeader('content-type', 'application/json');
364 $this->assertResponse(200, 'A 200 response was returned when a browser accept header was requested.');
366 // Should return a 200.
367 $this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
368 $this->assertHeader('content-type', 'application/json');
369 $this->assertResponse(200, 'A 200 response was returned when JSON was requested.');
370 $headers = $this->drupalGetHeaders();
371 $this->assertEqual($headers['Content-Type'], ['application/json'], 'The header Content-type is correct.');
372 // Should return a 200.
373 $this->drupalGet('test/serialize/field', ['query' => ['_format' => 'xml']]);
374 $this->assertHeader('content-type', 'text/xml; charset=UTF-8');
375 $this->assertResponse(200, 'A 200 response was returned when XML was requested');
376 $headers = $this->drupalGetHeaders();
377 $this->assertSame(['text/xml; charset=UTF-8'], $headers['Content-Type']);
378 // Should return a 406.
379 $this->drupalGet('test/serialize/field', ['query' => ['_format' => 'html']]);
380 // We want to show the first format by default, see
381 // \Drupal\rest\Plugin\views\style\Serializer::render.
382 $this->assertHeader('content-type', 'application/json');
383 $this->assertResponse(200, 'A 200 response was returned when HTML was requested.');
385 // Now configure now format, so all of them should be allowed.
386 $this->drupalPostForm($style_options, ['style_options[formats][json]' => '0', 'style_options[formats][xml]' => '0'], t('Apply'));
388 // Should return a 200.
389 $this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
390 $this->assertHeader('content-type', 'application/json');
391 $this->assertResponse(200, 'A 200 response was returned when JSON was requested.');
392 // Should return a 200.
393 $this->drupalGet('test/serialize/field', ['query' => ['_format' => 'xml']]);
394 $this->assertHeader('content-type', 'text/xml; charset=UTF-8');
395 $this->assertResponse(200, 'A 200 response was returned when XML was requested');
396 // Should return a 200.
397 $this->drupalGet('test/serialize/field', ['query' => ['_format' => 'html']]);
398 // We want to show the first format by default, see
399 // \Drupal\rest\Plugin\views\style\Serializer::render.
400 $this->assertHeader('content-type', 'application/json');
401 $this->assertResponse(200, 'A 200 response was returned when HTML was requested.');
405 * Test the field ID alias functionality of the DataFieldRow plugin.
407 public function testUIFieldAlias() {
408 $this->drupalLogin($this->adminUser);
410 // Test the UI settings for adding field ID aliases.
411 $this->drupalGet('admin/structure/views/view/test_serializer_display_field/edit/rest_export_1');
412 $row_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/row_options';
413 $this->assertLinkByHref($row_options);
415 // Test an empty string for an alias, this should not be used. This also
416 // tests that the form can be submitted with no aliases.
417 $this->drupalPostForm($row_options, ['row_options[field_options][name][alias]' => ''], t('Apply'));
418 $this->drupalPostForm(NULL, [], t('Save'));
420 $view = Views::getView('test_serializer_display_field');
421 $view->setDisplay('rest_export_1');
422 $this->executeView($view);
425 foreach ($view->result as $row) {
427 foreach ($view->field as $id => $field) {
428 $expected_row[$id] = $field->render($row);
430 $expected[] = $expected_row;
433 $this->assertIdentical(Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])), $this->castSafeStrings($expected));
435 // Test a random aliases for fields, they should be replaced.
437 'name' => $this->randomMachineName(),
438 // Use # to produce an invalid character for the validation.
439 'nothing' => '#' . $this->randomMachineName(),
440 'created' => 'created',
443 $edit = ['row_options[field_options][name][alias]' => $alias_map['name'], 'row_options[field_options][nothing][alias]' => $alias_map['nothing']];
444 $this->drupalPostForm($row_options, $edit, t('Apply'));
445 $this->assertText(t('The machine-readable name must contain only letters, numbers, dashes and underscores.'));
447 // Change the map alias value to a valid one.
448 $alias_map['nothing'] = $this->randomMachineName();
450 $edit = ['row_options[field_options][name][alias]' => $alias_map['name'], 'row_options[field_options][nothing][alias]' => $alias_map['nothing']];
451 $this->drupalPostForm($row_options, $edit, t('Apply'));
453 $this->drupalPostForm(NULL, [], t('Save'));
455 $view = Views::getView('test_serializer_display_field');
456 $view->setDisplay('rest_export_1');
457 $this->executeView($view);
460 foreach ($view->result as $row) {
462 foreach ($view->field as $id => $field) {
463 $expected_row[$alias_map[$id]] = $field->render($row);
465 $expected[] = $expected_row;
468 $this->assertIdentical(Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])), $this->castSafeStrings($expected));
472 * Tests the raw output options for row field rendering.
474 public function testFieldRawOutput() {
475 $this->drupalLogin($this->adminUser);
477 // Test the UI settings for adding field ID aliases.
478 $this->drupalGet('admin/structure/views/view/test_serializer_display_field/edit/rest_export_1');
479 $row_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/row_options';
480 $this->assertLinkByHref($row_options);
482 // Test an empty string for an alias, this should not be used. This also
483 // tests that the form can be submitted with no aliases.
485 'row_options[field_options][created][raw_output]' => '1',
486 'row_options[field_options][name][raw_output]' => '1',
488 $this->drupalPostForm($row_options, $values, t('Apply'));
489 $this->drupalPostForm(NULL, [], t('Save'));
491 $view = Views::getView('test_serializer_display_field');
492 $view->setDisplay('rest_export_1');
493 $this->executeView($view);
495 $storage = $this->container->get('entity_type.manager')->getStorage('entity_test');
497 // Update the name for each to include a script tag.
498 foreach ($storage->loadMultiple() as $entity_test) {
499 $name = $entity_test->name->value;
500 $entity_test->set('name', "<script>$name</script>");
501 $entity_test->save();
504 // Just test the raw 'created' value against each row.
505 foreach (Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])) as $index => $values) {
506 $this->assertIdentical($values['created'], $view->result[$index]->views_test_data_created, 'Expected raw created value found.');
507 $this->assertIdentical($values['name'], $view->result[$index]->views_test_data_name, 'Expected raw name value found.');
510 // Test result with an excluded field.
511 $view->setDisplay('rest_export_1');
512 $view->displayHandlers->get('rest_export_1')->overrideOption('fields', [
515 'table' => 'views_test_data',
517 'relationship' => 'none',
522 'table' => 'views_test_data',
523 'field' => 'created',
524 'relationship' => 'none',
528 $this->executeView($view);
529 foreach (Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])) as $index => $values) {
530 $this->assertTrue(!isset($values['created']), 'Excluded value not found.');
532 // Test that the excluded field is not shown in the row options.
533 $this->drupalGet('admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/row_options');
534 $this->assertNoText('created');
538 * Tests the live preview output for json output.
540 public function testLivePreview() {
541 // We set up a request so it looks like an request in the live preview.
542 $request = new Request();
543 $request->query->add([MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']);
544 /** @var \Symfony\Component\HttpFoundation\RequestStack $request_stack */
545 $request_stack = \Drupal::service('request_stack');
546 $request_stack->push($request);
548 $view = Views::getView('test_serializer_display_entity');
549 $view->setDisplay('rest_export_1');
550 $this->executeView($view);
552 // Get the serializer service.
553 $serializer = $this->container->get('serializer');
556 foreach ($view->result as $row) {
557 $entities[] = $row->_entity;
560 $expected = $serializer->serialize($entities, 'json');
562 $view->live_preview = TRUE;
564 $build = $view->preview();
565 $rendered_json = $build['#plain_text'];
566 $this->assertTrue(!isset($build['#markup']) && $rendered_json == $expected, 'Ensure the previewed json is escaped.');
569 $expected = $serializer->serialize($entities, 'xml');
571 // Change the request format to xml.
572 $view->setDisplay('rest_export_1');
573 $view->getDisplay()->setOption('style', [
574 'type' => 'serializer',
576 'uses_fields' => FALSE,
583 $this->executeView($view);
584 $build = $view->preview();
585 $rendered_xml = $build['#plain_text'];
586 $this->assertEqual($rendered_xml, $expected, 'Ensure we preview xml when we change the request format.');
590 * Tests the views interface for REST export displays.
592 public function testSerializerViewsUI() {
593 $this->drupalLogin($this->adminUser);
594 // Click the "Update preview button".
595 $this->drupalPostForm('admin/structure/views/view/test_serializer_display_field/edit/rest_export_1', $edit = [], t('Update preview'));
596 $this->assertResponse(200);
597 // Check if we receive the expected result.
598 $result = $this->xpath('//div[@id="views-live-preview"]/pre');
599 $json_preview = $result[0]->getText();
600 $this->assertSame($json_preview, $this->drupalGet('test/serialize/field'), 'The expected JSON preview output was found.');
604 * Tests the field row style using fieldapi fields.
606 public function testFieldapiField() {
607 $this->drupalCreateContentType(['type' => 'page']);
608 $node = $this->drupalCreateNode();
610 $result = Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']]));
611 $this->assertEqual($result[0]['nid'], $node->id());
612 $this->assertEqual($result[0]['body'], $node->body->processed);
614 // Make sure that serialized fields are not exposed to XSS.
615 $node = $this->drupalCreateNode();
617 'value' => '<script type="text/javascript">alert("node-body");</script>' . $this->randomMachineName(32),
618 'format' => filter_default_format(),
621 $result = Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']]));
622 $this->assertEqual($result[1]['nid'], $node->id());
623 $this->assertTrue(strpos($this->getRawContent(), "<script") === FALSE, "No script tag is present in the raw page contents.");
625 $this->drupalLogin($this->adminUser);
627 // Add an alias and make the output raw.
628 $row_options = 'admin/structure/views/nojs/display/test_serializer_node_display_field/rest_export_1/row_options';
630 // Test an empty string for an alias, this should not be used. This also
631 // tests that the form can be submitted with no aliases.
632 $this->drupalPostForm($row_options, ['row_options[field_options][title][raw_output]' => '1'], t('Apply'));
633 $this->drupalPostForm(NULL, [], t('Save'));
635 $view = Views::getView('test_serializer_node_display_field');
636 $view->setDisplay('rest_export_1');
637 $this->executeView($view);
639 // Test the raw 'created' value against each row.
640 foreach (Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']])) as $index => $values) {
641 $this->assertIdentical($values['title'], $view->result[$index]->_entity->title->value, 'Expected raw title value found.');
644 // Test that multiple raw body fields are shown.
645 // Make the body field unlimited cardinatlity.
646 $storage_definition = $node->getFieldDefinition('body')->getFieldStorageDefinition();
647 $storage_definition->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
648 $storage_definition->save();
650 $this->drupalPostForm($row_options, ['row_options[field_options][body][raw_output]' => '1'], t('Apply'));
651 $this->drupalPostForm(NULL, [], t('Save'));
653 $node = $this->drupalCreateNode();
656 'value' => '<script type="text/javascript">alert("node-body");</script>' . $this->randomMachineName(32),
657 'format' => filter_default_format(),
659 // Add two body items.
660 $node->body = [$body, $body];
663 $view = Views::getView('test_serializer_node_display_field');
664 $view->setDisplay('rest_export_1');
665 $this->executeView($view);
667 $result = Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']]));
668 $this->assertEqual(count($result[2]['body']), $node->body->count(), 'Expected count of values');
669 $this->assertEqual($result[2]['body'], array_map(function ($item) {
670 return $item['value'];
671 }, $node->body->getValue()), 'Expected raw body values found.');
675 * Tests the "Grouped rows" functionality.
677 public function testGroupRows() {
678 /** @var \Drupal\Core\Render\RendererInterface $renderer */
679 $renderer = $this->container->get('renderer');
680 $this->drupalCreateContentType(['type' => 'page']);
681 // Create a text field with cardinality set to unlimited.
682 $field_name = 'field_group_rows';
683 $field_storage = FieldStorageConfig::create([
684 'field_name' => $field_name,
685 'entity_type' => 'node',
687 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
689 $field_storage->save();
690 // Create an instance of the text field on the content type.
691 $field = FieldConfig::create([
692 'field_storage' => $field_storage,
696 $grouped_field_values = ['a', 'b', 'c'];
698 'title' => $this->randomMachineName(),
699 $field_name => $grouped_field_values,
701 $this->drupalCreateNode($edit);
702 $view = Views::getView('test_serializer_node_display_field');
703 $view->setDisplay('rest_export_1');
704 // Override the view's fields to include the field_group_rows field, set the
705 // group_rows setting to true.
709 'table' => 'node__' . $field_name,
710 'field' => $field_name,
712 'group_rows' => TRUE,
715 $view->displayHandlers->get('default')->overrideOption('fields', $fields);
716 $build = $view->preview();
717 // Get the serializer service.
718 $serializer = $this->container->get('serializer');
719 // Check if the field_group_rows field is grouped.
721 $expected[] = [$field_name => implode(', ', $grouped_field_values)];
722 $this->assertEqual($serializer->serialize($expected, 'json'), (string) $renderer->renderRoot($build));
723 // Set the group rows setting to false.
724 $view = Views::getView('test_serializer_node_display_field');
725 $view->setDisplay('rest_export_1');
726 $fields[$field_name]['group_rows'] = FALSE;
727 $view->displayHandlers->get('default')->overrideOption('fields', $fields);
728 $build = $view->preview();
729 // Check if the field_group_rows field is ungrouped and displayed per row.
731 foreach ($grouped_field_values as $grouped_field_value) {
732 $expected[] = [$field_name => $grouped_field_value];
734 $this->assertEqual($serializer->serialize($expected, 'json'), (string) $renderer->renderRoot($build));
738 * Tests the exposed filter works.
740 * There is an exposed filter on the title field which takes a title query
741 * parameter. This is set to filter nodes by those whose title starts with
742 * the value provided.
744 public function testRestViewExposedFilter() {
745 $this->drupalCreateContentType(['type' => 'page']);
746 $node0 = $this->drupalCreateNode(['title' => 'Node 1']);
747 $node1 = $this->drupalCreateNode(['title' => 'Node 11']);
748 $node2 = $this->drupalCreateNode(['title' => 'Node 111']);
750 // Test that no filter brings back all three nodes.
751 $result = Json::decode($this->drupalGet('test/serialize/node-exposed-filter', ['query' => ['_format' => 'json']]));
755 'nid' => $node0->id(),
756 'body' => $node0->body->processed,
759 'nid' => $node1->id(),
760 'body' => $node1->body->processed,
763 'nid' => $node2->id(),
764 'body' => $node2->body->processed,
768 $this->assertEqual($result, $expected, 'Querying a view with no exposed filter returns all nodes.');
770 // Test that title starts with 'Node 11' query finds 2 of the 3 nodes.
771 $result = Json::decode($this->drupalGet('test/serialize/node-exposed-filter', ['query' => ['_format' => 'json', 'title' => 'Node 11']]));
775 'nid' => $node1->id(),
776 'body' => $node1->body->processed,
779 'nid' => $node2->id(),
780 'body' => $node2->body->processed,
785 'languages:language_content',
786 'languages:language_interface',
789 'user.node_grants:view',
793 $this->assertEqual($result, $expected, 'Querying a view with a starts with exposed filter on the title returns nodes whose title starts with value provided.');
794 $this->assertCacheContexts($cache_contexts);
798 * Test multilingual entity rows.
800 public function testMulEntityRows() {
801 // Create some languages.
802 ConfigurableLanguage::createFromLangcode('l1')->save();
803 ConfigurableLanguage::createFromLangcode('l2')->save();
805 // Create an entity with no translations.
806 $storage = \Drupal::entityTypeManager()->getStorage('entity_test_mul');
807 $storage->create(['langcode' => 'l1', 'name' => 'mul-none'])->save();
809 // Create some entities with translations.
810 $entity = $storage->create(['langcode' => 'l1', 'name' => 'mul-l1-orig']);
812 $entity->addTranslation('l2', ['name' => 'mul-l1-l2'])->save();
813 $entity = $storage->create(['langcode' => 'l2', 'name' => 'mul-l2-orig']);
815 $entity->addTranslation('l1', ['name' => 'mul-l2-l1'])->save();
817 // Get the names of the output.
818 $json = $this->drupalGet('test/serialize/translated_entity', ['query' => ['_format' => 'json']]);
819 $decoded = $this->container->get('serializer')->decode($json, 'hal_json');
821 foreach ($decoded as $item) {
822 $names[] = $item['name'][0]['value'];
826 // Check that the names are correct.
827 $expected = ['mul-l1-l2', 'mul-l1-orig', 'mul-l2-l1', 'mul-l2-orig', 'mul-none'];
828 $this->assertIdentical($names, $expected, 'The translated content was found in the JSON.');