Mercurial > hg > isophonics-drupal-site
diff core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php @ 18:af1871eacc83
Update to Drupal core 8.7.1
author | Chris Cannam |
---|---|
date | Thu, 09 May 2019 15:33:08 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php Thu May 09 15:33:08 2019 +0100 @@ -0,0 +1,317 @@ +<?php + +namespace Drupal\Tests\jsonapi\Functional; + +use Drupal\Component\Serialization\Json; +use Drupal\Core\Url; +use Drupal\language\Entity\ConfigurableLanguage; +use Drupal\language\Entity\ContentLanguageSettings; +use Drupal\node\Entity\Node; +use Drupal\user\Entity\Role; +use Drupal\user\RoleInterface; +use GuzzleHttp\RequestOptions; + +/** + * Tests JSON:API multilingual support. + * + * @group jsonapi + * @group legacy + * + * @internal + */ +class JsonApiFunctionalMultilingualTest extends JsonApiFunctionalTestBase { + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'language', + ]; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + $language = ConfigurableLanguage::createFromLangcode('ca'); + $language->save(); + ConfigurableLanguage::createFromLangcode('ca-fr')->save(); + + // In order to reflect the changes for a multilingual site in the container + // we have to rebuild it. + $this->rebuildContainer(); + + \Drupal::configFactory()->getEditable('language.negotiation') + ->set('url.prefixes.ca', 'ca') + ->set('url.prefixes.ca-fr', 'ca-fr') + ->save(); + + $this->createDefaultContent(5, 5, TRUE, TRUE, static::IS_MULTILINGUAL, FALSE); + } + + /** + * Tests reading multilingual content. + */ + public function testReadMultilingual() { + // Different databases have different sort orders, so a sort is required so + // test expectations do not need to vary per database. + $default_sort = ['sort' => 'drupal_internal__nid']; + + // Test reading an individual entity translation. + $output = Json::decode($this->drupalGet('/ca/jsonapi/node/article/' . $this->nodes[0]->uuid(), ['query' => ['include' => 'field_tags,field_image'] + $default_sort])); + $this->assertEquals($this->nodes[0]->getTranslation('ca')->getTitle(), $output['data']['attributes']['title']); + $this->assertSame('ca', $output['data']['attributes']['langcode']); + $included_tags = array_filter($output['included'], function ($entry) { + return $entry['type'] === 'taxonomy_term--tags'; + }); + $tag_name = $this->nodes[0]->get('field_tags')->entity + ->getTranslation('ca')->getName(); + $this->assertEquals($tag_name, reset($included_tags)['attributes']['name']); + $alt = $this->nodes[0]->getTranslation('ca')->get('field_image')->alt; + $this->assertSame($alt, $output['data']['relationships']['field_image']['data']['meta']['alt']); + + // Test reading an individual entity fallback. + $output = Json::decode($this->drupalGet('/ca-fr/jsonapi/node/article/' . $this->nodes[0]->uuid())); + $this->assertEquals($this->nodes[0]->getTranslation('ca')->getTitle(), $output['data']['attributes']['title']); + + $output = Json::decode($this->drupalGet('/ca/jsonapi/node/article/' . $this->nodes[0]->uuid(), ['query' => $default_sort])); + $this->assertEquals($this->nodes[0]->getTranslation('ca')->getTitle(), $output['data']['attributes']['title']); + + // Test reading a collection of entities. + $output = Json::decode($this->drupalGet('/ca/jsonapi/node/article', ['query' => $default_sort])); + $this->assertEquals($this->nodes[0]->getTranslation('ca')->getTitle(), $output['data'][0]['attributes']['title']); + } + + /** + * Tests updating a translation. + */ + public function testPatchTranslation() { + $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); + $node = $this->nodes[0]; + $uuid = $node->uuid(); + + // Assert the precondition: the 'ca' translation has a different title. + $document = Json::decode($this->drupalGet('/jsonapi/node/article/' . $uuid)); + $document_ca = Json::decode($this->drupalGet('/ca/jsonapi/node/article/' . $uuid)); + $this->assertSame('en', $document['data']['attributes']['langcode']); + $this->assertSame('ca', $document_ca['data']['attributes']['langcode']); + $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); + $this->assertSame($node->getTitle() . ' (ca)', $document_ca['data']['attributes']['title']); + + // PATCH the 'ca' translation. + $this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), [ + 'bypass node access', + ]); + $request_options = []; + $request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/vnd.api+json'; + $request_options[RequestOptions::BODY] = Json::encode([ + 'data' => [ + 'type' => 'node--article', + 'id' => $uuid, + 'attributes' => [ + 'title' => $document_ca['data']['attributes']['title'] . ' UPDATED', + ], + ], + ]); + $response = $this->request('PATCH', Url::fromUri('base:/ca/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); + $this->assertSame(200, $response->getStatusCode()); + + // Assert the postcondition: only the 'ca' translation has an updated title. + $document_updated = Json::decode($this->drupalGet('/jsonapi/node/article/' . $uuid)); + $document_ca_updated = Json::decode($this->drupalGet('/ca/jsonapi/node/article/' . $uuid)); + $this->assertSame('en', $document_updated['data']['attributes']['langcode']); + $this->assertSame('ca', $document_ca_updated['data']['attributes']['langcode']); + $this->assertSame($node->getTitle(), $document_updated['data']['attributes']['title']); + $this->assertSame($node->getTitle() . ' (ca) UPDATED', $document_ca_updated['data']['attributes']['title']); + + // Specifying a langcode is not allowed by default. + $request_options[RequestOptions::BODY] = Json::encode([ + 'data' => [ + 'type' => 'node--article', + 'id' => $uuid, + 'attributes' => [ + 'langcode' => 'ca-fr', + ], + ], + ]); + $response = $this->request('PATCH', Url::fromUri('base:/ca/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); + $this->assertSame(403, $response->getStatusCode()); + + // Specifying a langcode is allowed once configured to be alterable. But + // modifying the language of a non-default translation is still not allowed. + ContentLanguageSettings::create([ + 'target_entity_type_id' => 'node', + 'target_bundle' => 'article', + ])->setLanguageAlterable(TRUE) + ->save(); + $response = $this->request('PATCH', Url::fromUri('base:/ca/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); + $this->assertSame(500, $response->getStatusCode()); + $document = Json::decode((string) $response->getBody()); + $this->assertSame('The translation language cannot be changed (ca).', $document['errors'][0]['detail']); + + // Changing the langcode of the default ('en') translation is possible: + // first verify that it currently is 'en', then change it to 'ca-fr', and + // verify that the the title is unchanged, but the langcode is updated. + $response = $this->request('GET', Url::fromUri('base:/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); + $this->assertSame(200, $response->getStatusCode()); + $document = Json::decode((string) $response->getBody()); + $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); + $this->assertSame('en', $document['data']['attributes']['langcode']); + $response = $this->request('PATCH', Url::fromUri('base:/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); + $this->assertSame(200, $response->getStatusCode()); + $document = Json::decode((string) $response->getBody()); + $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); + $this->assertSame('ca-fr', $document['data']['attributes']['langcode']); + + // Finally: assert the postcondition of all installed languages. + // - When GETting the 'en' translation, we get 'ca-fr', since the 'en' + // translation doesn't exist anymore. + $response = $this->request('GET', Url::fromUri('base:/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); + $document = Json::decode((string) $response->getBody()); + $this->assertSame('ca-fr', $document['data']['attributes']['langcode']); + $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); + // - When GETting the 'ca' translation, we still get the 'ca' one. + $response = $this->request('GET', Url::fromUri('base:/ca/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); + $document = Json::decode((string) $response->getBody()); + $this->assertSame('ca', $document['data']['attributes']['langcode']); + $this->assertSame($node->getTitle() . ' (ca) UPDATED', $document['data']['attributes']['title']); + // - When GETting the 'ca-fr' translation, we now get the default + // translation. + $response = $this->request('GET', Url::fromUri('base:/ca-fr/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); + $document = Json::decode((string) $response->getBody()); + $this->assertSame('ca-fr', $document['data']['attributes']['langcode']); + $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); + } + + /** + * Tests updating a translation fallback. + */ + public function testPatchTranslationFallback() { + $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); + $node = $this->nodes[0]; + $uuid = $node->uuid(); + + // Assert the precondition: 'ca-fr' falls back to the 'ca' translation which + // has a different title. + $document = Json::decode($this->drupalGet('/jsonapi/node/article/' . $uuid)); + $document_ca = Json::decode($this->drupalGet('/ca/jsonapi/node/article/' . $uuid)); + $document_cafr = Json::decode($this->drupalGet('/ca-fr/jsonapi/node/article/' . $uuid)); + $this->assertSame('en', $document['data']['attributes']['langcode']); + $this->assertSame('ca', $document_ca['data']['attributes']['langcode']); + $this->assertSame('ca', $document_cafr['data']['attributes']['langcode']); + $this->assertSame($node->getTitle(), $document['data']['attributes']['title']); + $this->assertSame($node->getTitle() . ' (ca)', $document_ca['data']['attributes']['title']); + $this->assertSame($node->getTitle() . ' (ca)', $document_cafr['data']['attributes']['title']); + + // PATCH the 'ca-fr' translation. + $this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), [ + 'bypass node access', + ]); + $request_options = []; + $request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/vnd.api+json'; + $request_options[RequestOptions::BODY] = Json::encode([ + 'data' => [ + 'type' => 'node--article', + 'id' => $uuid, + 'attributes' => [ + 'title' => $document_cafr['data']['attributes']['title'] . ' UPDATED', + ], + ], + ]); + $response = $this->request('PATCH', Url::fromUri('base:/ca-fr/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options); + $this->assertSame(405, $response->getStatusCode()); + $document = Json::decode((string) $response->getBody()); + $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']); + } + + /** + * Tests creating a translation. + */ + public function testPostTranslation() { + $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); + $this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), [ + 'bypass node access', + ]); + + $title = 'Llamas FTW (ca)'; + $request_document = [ + 'data' => [ + 'type' => 'node--article', + 'attributes' => [ + 'title' => $title, + 'langcode' => 'ca', + ], + ], + ]; + + $request_options = []; + $request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/vnd.api+json'; + + // Specifying a langcode is forbidden by language_entity_field_access(). + $request_options[RequestOptions::BODY] = Json::encode($request_document); + $response = $this->request('POST', Url::fromUri('base:/ca/jsonapi/node/article/'), $request_options); + $this->assertSame(403, $response->getStatusCode()); + $document = Json::decode((string) $response->getBody()); + $this->assertSame('The current user is not allowed to POST the selected field (langcode).', $document['errors'][0]['detail']); + + // Omitting a langcode results in an entity in 'en': the default language of + // the site. + unset($request_document['data']['attributes']['langcode']); + $request_options[RequestOptions::BODY] = Json::encode($request_document); + $response = $this->request('POST', Url::fromUri('base:/ca/jsonapi/node/article/'), $request_options); + $this->assertSame(201, $response->getStatusCode()); + $document = Json::decode((string) $response->getBody()); + $this->assertSame($title, $document['data']['attributes']['title']); + $this->assertSame('en', $document['data']['attributes']['langcode']); + $this->assertSame(['en'], array_keys(Node::load($document['data']['attributes']['drupal_internal__nid'])->getTranslationLanguages())); + + // Specifying a langcode is allowed once configured to be alterable. Now an + // entity can be created with the specified langcode. + ContentLanguageSettings::create([ + 'target_entity_type_id' => 'node', + 'target_bundle' => 'article', + ])->setLanguageAlterable(TRUE) + ->save(); + $request_document['data']['attributes']['langcode'] = 'ca'; + $request_options[RequestOptions::BODY] = Json::encode($request_document); + $response = $this->request('POST', Url::fromUri('base:/ca/jsonapi/node/article/'), $request_options); + $this->assertSame(201, $response->getStatusCode()); + $document = Json::decode((string) $response->getBody()); + $this->assertSame($title, $document['data']['attributes']['title']); + $this->assertSame('ca', $document['data']['attributes']['langcode']); + $this->assertSame(['ca'], array_keys(Node::load($document['data']['attributes']['drupal_internal__nid'])->getTranslationLanguages())); + + // Same request, but sent to the URL without the language prefix. + $response = $this->request('POST', Url::fromUri('base:/jsonapi/node/article/'), $request_options); + $this->assertSame(201, $response->getStatusCode()); + $document = Json::decode((string) $response->getBody()); + $this->assertSame($title, $document['data']['attributes']['title']); + $this->assertSame('ca', $document['data']['attributes']['langcode']); + $this->assertSame(['ca'], array_keys(Node::load($document['data']['attributes']['drupal_internal__nid'])->getTranslationLanguages())); + } + + /** + * Tests deleting multilingual content. + */ + public function testDeleteMultilingual() { + $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); + $this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), [ + 'bypass node access', + ]); + + $response = $this->request('DELETE', Url::fromUri('base:/ca/jsonapi/node/article/' . $this->nodes[0]->uuid()), []); + $this->assertSame(405, $response->getStatusCode()); + $document = Json::decode((string) $response->getBody()); + $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']); + + $response = $this->request('DELETE', Url::fromUri('base:/ca-fr/jsonapi/node/article/' . $this->nodes[0]->uuid()), []); + $this->assertSame(405, $response->getStatusCode()); + $document = Json::decode((string) $response->getBody()); + $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']); + + $response = $this->request('DELETE', Url::fromUri('base:/jsonapi/node/article/' . $this->nodes[0]->uuid()), []); + $this->assertSame(204, $response->getStatusCode()); + $this->assertFalse(Node::load($this->nodes[0]->id())); + } + +}