Mercurial > hg > cmmr2012-drupal-site
comparison core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php @ 5:12f9dff5fda9 tip
Update to Drupal core 8.7.1
author | Chris Cannam |
---|---|
date | Thu, 09 May 2019 15:34:47 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
4:a9cd425dd02b | 5:12f9dff5fda9 |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Tests\jsonapi\Functional; | |
4 | |
5 use Drupal\Component\Serialization\Json; | |
6 use Drupal\Core\Url; | |
7 use Drupal\language\Entity\ConfigurableLanguage; | |
8 use Drupal\language\Entity\ContentLanguageSettings; | |
9 use Drupal\node\Entity\Node; | |
10 use Drupal\user\Entity\Role; | |
11 use Drupal\user\RoleInterface; | |
12 use GuzzleHttp\RequestOptions; | |
13 | |
14 /** | |
15 * Tests JSON:API multilingual support. | |
16 * | |
17 * @group jsonapi | |
18 * @group legacy | |
19 * | |
20 * @internal | |
21 */ | |
22 class JsonApiFunctionalMultilingualTest extends JsonApiFunctionalTestBase { | |
23 | |
24 /** | |
25 * {@inheritdoc} | |
26 */ | |
27 public static $modules = [ | |
28 'language', | |
29 ]; | |
30 | |
31 /** | |
32 * {@inheritdoc} | |
33 */ | |
34 protected function setUp() { | |
35 parent::setUp(); | |
36 $language = ConfigurableLanguage::createFromLangcode('ca'); | |
37 $language->save(); | |
38 ConfigurableLanguage::createFromLangcode('ca-fr')->save(); | |
39 | |
40 // In order to reflect the changes for a multilingual site in the container | |
41 // we have to rebuild it. | |
42 $this->rebuildContainer(); | |
43 | |
44 \Drupal::configFactory()->getEditable('language.negotiation') | |
45 ->set('url.prefixes.ca', 'ca') | |
46 ->set('url.prefixes.ca-fr', 'ca-fr') | |
47 ->save(); | |
48 | |
49 $this->createDefaultContent(5, 5, TRUE, TRUE, static::IS_MULTILINGUAL, FALSE); | |
50 } | |
51 | |
52 /** | |
53 * Tests reading multilingual content. | |
54 */ | |
55 public function testReadMultilingual() { | |
56 // Different databases have different sort orders, so a sort is required so | |
57 // test expectations do not need to vary per database. | |
58 $default_sort = ['sort' => 'drupal_internal__nid']; | |
59 | |
60 // Test reading an individual entity translation. | |
61 $output = Json::decode($this->drupalGet('/ca/jsonapi/node/article/' . $this->nodes[0]->uuid(), ['query' => ['include' => 'field_tags,field_image'] + $default_sort])); | |
62 $this->assertEquals($this->nodes[0]->getTranslation('ca')->getTitle(), $output['data']['attributes']['title']); | |
63 $this->assertSame('ca', $output['data']['attributes']['langcode']); | |
64 $included_tags = array_filter($output['included'], function ($entry) { | |
65 return $entry['type'] === 'taxonomy_term--tags'; | |
66 }); | |
67 $tag_name = $this->nodes[0]->get('field_tags')->entity | |
68 ->getTranslation('ca')->getName(); | |
69 $this->assertEquals($tag_name, reset($included_tags)['attributes']['name']); | |
70 $alt = $this->nodes[0]->getTranslation('ca')->get('field_image')->alt; | |
71 $this->assertSame($alt, $output['data']['relationships']['field_image']['data']['meta']['alt']); | |
72 | |
73 // Test reading an individual entity fallback. | |
74 $output = Json::decode($this->drupalGet('/ca-fr/jsonapi/node/article/' . $this->nodes[0]->uuid())); | |
75 $this->assertEquals($this->nodes[0]->getTranslation('ca')->getTitle(), $output['data']['attributes']['title']); | |
76 | |
77 $output = Json::decode($this->drupalGet('/ca/jsonapi/node/article/' . $this->nodes[0]->uuid(), ['query' => $default_sort])); | |
78 $this->assertEquals($this->nodes[0]->getTranslation('ca')->getTitle(), $output['data']['attributes']['title']); | |
79 | |
80 // Test reading a collection of entities. | |
81 $output = Json::decode($this->drupalGet('/ca/jsonapi/node/article', ['query' => $default_sort])); | |
82 $this->assertEquals($this->nodes[0]->getTranslation('ca')->getTitle(), $output['data'][0]['attributes']['title']); | |
83 } | |
84 | |
85 /** | |
86 * Tests updating a translation. | |
87 */ | |
88 public function testPatchTranslation() { | |
89 $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); | |
90 $node = $this->nodes[0]; | |
91 $uuid = $node->uuid(); | |
92 | |
93 // Assert the precondition: the 'ca' translation has a different title. | |
94 $document = Json::decode($this->drupalGet('/jsonapi/node/article/' . $uuid)); | |
95 $document_ca = Json::decode($this->drupalGet('/ca/jsonapi/node/article/' . $uuid)); | |
96 $this->assertSame('en', $document['data']['attributes']['langcode']); | |
97 $this->assertSame('ca', $document_ca['data']['attributes']['langcode']); | |
98 $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); | |
99 $this->assertSame($node->getTitle() . ' (ca)', $document_ca['data']['attributes']['title']); | |
100 | |
101 // PATCH the 'ca' translation. | |
102 $this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), [ | |
103 'bypass node access', | |
104 ]); | |
105 $request_options = []; | |
106 $request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/vnd.api+json'; | |
107 $request_options[RequestOptions::BODY] = Json::encode([ | |
108 'data' => [ | |
109 'type' => 'node--article', | |
110 'id' => $uuid, | |
111 'attributes' => [ | |
112 'title' => $document_ca['data']['attributes']['title'] . ' UPDATED', | |
113 ], | |
114 ], | |
115 ]); | |
116 $response = $this->request('PATCH', Url::fromUri('base:/ca/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); | |
117 $this->assertSame(200, $response->getStatusCode()); | |
118 | |
119 // Assert the postcondition: only the 'ca' translation has an updated title. | |
120 $document_updated = Json::decode($this->drupalGet('/jsonapi/node/article/' . $uuid)); | |
121 $document_ca_updated = Json::decode($this->drupalGet('/ca/jsonapi/node/article/' . $uuid)); | |
122 $this->assertSame('en', $document_updated['data']['attributes']['langcode']); | |
123 $this->assertSame('ca', $document_ca_updated['data']['attributes']['langcode']); | |
124 $this->assertSame($node->getTitle(), $document_updated['data']['attributes']['title']); | |
125 $this->assertSame($node->getTitle() . ' (ca) UPDATED', $document_ca_updated['data']['attributes']['title']); | |
126 | |
127 // Specifying a langcode is not allowed by default. | |
128 $request_options[RequestOptions::BODY] = Json::encode([ | |
129 'data' => [ | |
130 'type' => 'node--article', | |
131 'id' => $uuid, | |
132 'attributes' => [ | |
133 'langcode' => 'ca-fr', | |
134 ], | |
135 ], | |
136 ]); | |
137 $response = $this->request('PATCH', Url::fromUri('base:/ca/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); | |
138 $this->assertSame(403, $response->getStatusCode()); | |
139 | |
140 // Specifying a langcode is allowed once configured to be alterable. But | |
141 // modifying the language of a non-default translation is still not allowed. | |
142 ContentLanguageSettings::create([ | |
143 'target_entity_type_id' => 'node', | |
144 'target_bundle' => 'article', | |
145 ])->setLanguageAlterable(TRUE) | |
146 ->save(); | |
147 $response = $this->request('PATCH', Url::fromUri('base:/ca/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); | |
148 $this->assertSame(500, $response->getStatusCode()); | |
149 $document = Json::decode((string) $response->getBody()); | |
150 $this->assertSame('The translation language cannot be changed (ca).', $document['errors'][0]['detail']); | |
151 | |
152 // Changing the langcode of the default ('en') translation is possible: | |
153 // first verify that it currently is 'en', then change it to 'ca-fr', and | |
154 // verify that the the title is unchanged, but the langcode is updated. | |
155 $response = $this->request('GET', Url::fromUri('base:/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); | |
156 $this->assertSame(200, $response->getStatusCode()); | |
157 $document = Json::decode((string) $response->getBody()); | |
158 $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); | |
159 $this->assertSame('en', $document['data']['attributes']['langcode']); | |
160 $response = $this->request('PATCH', Url::fromUri('base:/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); | |
161 $this->assertSame(200, $response->getStatusCode()); | |
162 $document = Json::decode((string) $response->getBody()); | |
163 $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); | |
164 $this->assertSame('ca-fr', $document['data']['attributes']['langcode']); | |
165 | |
166 // Finally: assert the postcondition of all installed languages. | |
167 // - When GETting the 'en' translation, we get 'ca-fr', since the 'en' | |
168 // translation doesn't exist anymore. | |
169 $response = $this->request('GET', Url::fromUri('base:/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); | |
170 $document = Json::decode((string) $response->getBody()); | |
171 $this->assertSame('ca-fr', $document['data']['attributes']['langcode']); | |
172 $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); | |
173 // - When GETting the 'ca' translation, we still get the 'ca' one. | |
174 $response = $this->request('GET', Url::fromUri('base:/ca/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); | |
175 $document = Json::decode((string) $response->getBody()); | |
176 $this->assertSame('ca', $document['data']['attributes']['langcode']); | |
177 $this->assertSame($node->getTitle() . ' (ca) UPDATED', $document['data']['attributes']['title']); | |
178 // - When GETting the 'ca-fr' translation, we now get the default | |
179 // translation. | |
180 $response = $this->request('GET', Url::fromUri('base:/ca-fr/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); | |
181 $document = Json::decode((string) $response->getBody()); | |
182 $this->assertSame('ca-fr', $document['data']['attributes']['langcode']); | |
183 $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); | |
184 } | |
185 | |
186 /** | |
187 * Tests updating a translation fallback. | |
188 */ | |
189 public function testPatchTranslationFallback() { | |
190 $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); | |
191 $node = $this->nodes[0]; | |
192 $uuid = $node->uuid(); | |
193 | |
194 // Assert the precondition: 'ca-fr' falls back to the 'ca' translation which | |
195 // has a different title. | |
196 $document = Json::decode($this->drupalGet('/jsonapi/node/article/' . $uuid)); | |
197 $document_ca = Json::decode($this->drupalGet('/ca/jsonapi/node/article/' . $uuid)); | |
198 $document_cafr = Json::decode($this->drupalGet('/ca-fr/jsonapi/node/article/' . $uuid)); | |
199 $this->assertSame('en', $document['data']['attributes']['langcode']); | |
200 $this->assertSame('ca', $document_ca['data']['attributes']['langcode']); | |
201 $this->assertSame('ca', $document_cafr['data']['attributes']['langcode']); | |
202 $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); | |
203 $this->assertSame($node->getTitle() . ' (ca)', $document_ca['data']['attributes']['title']); | |
204 $this->assertSame($node->getTitle() . ' (ca)', $document_cafr['data']['attributes']['title']); | |
205 | |
206 // PATCH the 'ca-fr' translation. | |
207 $this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), [ | |
208 'bypass node access', | |
209 ]); | |
210 $request_options = []; | |
211 $request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/vnd.api+json'; | |
212 $request_options[RequestOptions::BODY] = Json::encode([ | |
213 'data' => [ | |
214 'type' => 'node--article', | |
215 'id' => $uuid, | |
216 'attributes' => [ | |
217 'title' => $document_cafr['data']['attributes']['title'] . ' UPDATED', | |
218 ], | |
219 ], | |
220 ]); | |
221 $response = $this->request('PATCH', Url::fromUri('base:/ca-fr/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); | |
222 $this->assertSame(405, $response->getStatusCode()); | |
223 $document = Json::decode((string) $response->getBody()); | |
224 $this->assertSame('The requested translation of the resource object does not exist, instead modify one of the translations that do exist: ca, en.', $document['errors'][0]['detail']); | |
225 } | |
226 | |
227 /** | |
228 * Tests creating a translation. | |
229 */ | |
230 public function testPostTranslation() { | |
231 $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); | |
232 $this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), [ | |
233 'bypass node access', | |
234 ]); | |
235 | |
236 $title = 'Llamas FTW (ca)'; | |
237 $request_document = [ | |
238 'data' => [ | |
239 'type' => 'node--article', | |
240 'attributes' => [ | |
241 'title' => $title, | |
242 'langcode' => 'ca', | |
243 ], | |
244 ], | |
245 ]; | |
246 | |
247 $request_options = []; | |
248 $request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/vnd.api+json'; | |
249 | |
250 // Specifying a langcode is forbidden by language_entity_field_access(). | |
251 $request_options[RequestOptions::BODY] = Json::encode($request_document); | |
252 $response = $this->request('POST', Url::fromUri('base:/ca/jsonapi/node/article/'), $request_options); | |
253 $this->assertSame(403, $response->getStatusCode()); | |
254 $document = Json::decode((string) $response->getBody()); | |
255 $this->assertSame('The current user is not allowed to POST the selected field (langcode).', $document['errors'][0]['detail']); | |
256 | |
257 // Omitting a langcode results in an entity in 'en': the default language of | |
258 // the site. | |
259 unset($request_document['data']['attributes']['langcode']); | |
260 $request_options[RequestOptions::BODY] = Json::encode($request_document); | |
261 $response = $this->request('POST', Url::fromUri('base:/ca/jsonapi/node/article/'), $request_options); | |
262 $this->assertSame(201, $response->getStatusCode()); | |
263 $document = Json::decode((string) $response->getBody()); | |
264 $this->assertSame($title, $document['data']['attributes']['title']); | |
265 $this->assertSame('en', $document['data']['attributes']['langcode']); | |
266 $this->assertSame(['en'], array_keys(Node::load($document['data']['attributes']['drupal_internal__nid'])->getTranslationLanguages())); | |
267 | |
268 // Specifying a langcode is allowed once configured to be alterable. Now an | |
269 // entity can be created with the specified langcode. | |
270 ContentLanguageSettings::create([ | |
271 'target_entity_type_id' => 'node', | |
272 'target_bundle' => 'article', | |
273 ])->setLanguageAlterable(TRUE) | |
274 ->save(); | |
275 $request_document['data']['attributes']['langcode'] = 'ca'; | |
276 $request_options[RequestOptions::BODY] = Json::encode($request_document); | |
277 $response = $this->request('POST', Url::fromUri('base:/ca/jsonapi/node/article/'), $request_options); | |
278 $this->assertSame(201, $response->getStatusCode()); | |
279 $document = Json::decode((string) $response->getBody()); | |
280 $this->assertSame($title, $document['data']['attributes']['title']); | |
281 $this->assertSame('ca', $document['data']['attributes']['langcode']); | |
282 $this->assertSame(['ca'], array_keys(Node::load($document['data']['attributes']['drupal_internal__nid'])->getTranslationLanguages())); | |
283 | |
284 // Same request, but sent to the URL without the language prefix. | |
285 $response = $this->request('POST', Url::fromUri('base:/jsonapi/node/article/'), $request_options); | |
286 $this->assertSame(201, $response->getStatusCode()); | |
287 $document = Json::decode((string) $response->getBody()); | |
288 $this->assertSame($title, $document['data']['attributes']['title']); | |
289 $this->assertSame('ca', $document['data']['attributes']['langcode']); | |
290 $this->assertSame(['ca'], array_keys(Node::load($document['data']['attributes']['drupal_internal__nid'])->getTranslationLanguages())); | |
291 } | |
292 | |
293 /** | |
294 * Tests deleting multilingual content. | |
295 */ | |
296 public function testDeleteMultilingual() { | |
297 $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); | |
298 $this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), [ | |
299 'bypass node access', | |
300 ]); | |
301 | |
302 $response = $this->request('DELETE', Url::fromUri('base:/ca/jsonapi/node/article/' . $this->nodes[0]->uuid()), []); | |
303 $this->assertSame(405, $response->getStatusCode()); | |
304 $document = Json::decode((string) $response->getBody()); | |
305 $this->assertSame('Deleting a resource object translation is not yet supported. See https://www.drupal.org/docs/8/modules/jsonapi/translations.', $document['errors'][0]['detail']); | |
306 | |
307 $response = $this->request('DELETE', Url::fromUri('base:/ca-fr/jsonapi/node/article/' . $this->nodes[0]->uuid()), []); | |
308 $this->assertSame(405, $response->getStatusCode()); | |
309 $document = Json::decode((string) $response->getBody()); | |
310 $this->assertSame('Deleting a resource object translation is not yet supported. See https://www.drupal.org/docs/8/modules/jsonapi/translations.', $document['errors'][0]['detail']); | |
311 | |
312 $response = $this->request('DELETE', Url::fromUri('base:/jsonapi/node/article/' . $this->nodes[0]->uuid()), []); | |
313 $this->assertSame(204, $response->getStatusCode()); | |
314 $this->assertFalse(Node::load($this->nodes[0]->id())); | |
315 } | |
316 | |
317 } |