diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9f273dedd8fbaa5386589c0b0ae9d1c43ee0c8af..5af14491fcc2b103c6d54b206b262cb766c4069d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,7 +38,6 @@ phpstan: variables: _CSPELL_WORDS: 'Kravchuk,ptmkenny,Taras,tarik' OPT_IN_TEST_NEXT_MINOR: '1' - OPT_IN_TEST_PREV_MAJOR: '1' # SKIP_ESLINT: '1' # OPT_IN_TEST_NEXT_MAJOR: '1' # _CURL_TEMPLATES_REF: 'main' diff --git a/phpstan.neon b/phpstan.neon index 6db079dfdf32debb54da85ba86a1c5f5642d084c..aa2de20e0cde4ef8f86ecc5d0cc3a909958a2c13 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,3 +5,5 @@ parameters: ignoreErrors: # Drupal does not define its own arrays. - '#no value type specified in iterable type array#' + # TranslatableMarkup is equivalent to string + - '#TranslatableMarkup given#' diff --git a/tests/src/Functional/JsonApiLinksFunctionalTest.php b/tests/src/Functional/JsonApiLinksFunctionalTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c996c4207f3eb1bb54e8a5c7a57fb20f0506afd8 --- /dev/null +++ b/tests/src/Functional/JsonApiLinksFunctionalTest.php @@ -0,0 +1,191 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\jsonapi\Functional; + +use Drupal\jsonapi_links\Exception\JsonApiLinksException; +use Drupal\user\Entity\User; + +/** + * General functional test class. + * + * @group jsonapi_links + */ +class JsonApiLinksFunctionalTest extends JsonApiFunctionalTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'basic_auth', + 'jsonapi_links', + ]; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * The module config identifier. + * + * @var string + */ + protected string $configName = 'jsonapi_links.settings'; + + /** + * The name of the config for removing links. + * + * @var string + */ + protected string $configRemoveLinks = 'remove_links'; + + /** + * Test admin user. + * + * @var \Drupal\user\Entity\User + * + * @todo Remove when 11.2 is released. + */ + protected $adminUser; + + /** + * {@inheritdoc} + * + * @todo Remove when 11.2 is released. + */ + protected function setUp(): void { + parent::setUp(); + + if (!($this->adminUser instanceof User)) { + $admin_user = $this->drupalCreateUser([ + 'create article content', + 'edit any article content', + 'delete any article content', + ], + 'jsonapi_admin_user', + TRUE, + ); + if ($admin_user === FALSE) { + throw new JsonApiLinksException("Failed to create admin user!"); + } + $this->adminUser = $admin_user; + } + } + + /** + * Tests link removal. + * + * Inspired by testRead() in JsonApiFunctionalTest. + */ + public function testLinkRemoval(): void { + // JSON:API Links does not remove anything by default. + $this->createDefaultContent(61, 5, TRUE, TRUE, static::IS_NOT_MULTILINGUAL, FALSE); + // Unpublish the last entity, so we can check access. + $this->nodes[60]->setUnpublished()->save(); + + $uuid = $this->nodes[0]->uuid(); + if (!is_string($uuid)) { + throw new JsonApiLinksException("Failed to get UUID for node!"); + } + + // 0. HEAD request allows a client to verify that JSON:API is installed. + $this->httpClient->request('HEAD', $this->buildUrl('/jsonapi/node/article')); + $this->assertSession()->statusCodeEquals(200); + + // Confirm the default behavior (no change from core). + $this->checkForLinksPresent($uuid, FALSE); + + $this->drupalLogin($this->adminUser); + $this->drupalGet('/admin/config/services/jsonapi/links'); + $form_values = [ + 'edit-remove-links' => 1, + ]; + $this->submitForm($form_values, t('Save configuration')); + + // Verify that the configuration value is set. + $this->assertTrue($this->config($this->configName)->get($this->configRemoveLinks)); + + // Ensure links are removed as expected. + $this->checkForLinksPresent($uuid, TRUE); + + // Disable link removal with JSON:API Links. + $this->drupalGet('/admin/config/services/jsonapi/links'); + $form_values = [ + 'edit-remove-links' => 0, + ]; + $this->submitForm($form_values, t('Save configuration')); + + // Verify that the configuration value is set. + $this->assertFalse($this->config($this->configName)->get($this->configRemoveLinks)); + // Logout to refresh cache. + $this->drupalLogout(); + + // Ensure that the prior behavior has been restored. + $this->checkForLinksPresent($uuid, FALSE); + } + + /** + * Confirms that link attributes are present or absent. + * + * @param string $uuid + * The uuid of the article. + * @param bool $links_removed + * TRUE if links should be removed; FALSE otherwise. + */ + private function checkForLinksPresent(string $uuid, bool $links_removed): void { + // 6. Single relationship item. + $drupal_response1 = $this->drupalGet('/jsonapi/node/article/' . $uuid); + $single_output = json_decode($drupal_response1, TRUE); + if (!is_array($single_output)) { + throw new JsonApiLinksException("Failed to decode JSON:API response!"); + } + $this->assertSession()->statusCodeEquals(200); + $this->assertLinksRemoved($single_output, $links_removed); + // 11. Includes with relationships. + $drupal_response2 = $this->drupalGet('/jsonapi/node/article/' . $uuid . '/relationships/uid', [ + 'query' => ['include' => 'uid'], + ]); + $output_with_relationships = json_decode($drupal_response2, TRUE); + if (!is_array($output_with_relationships)) { + throw new JsonApiLinksException("Failed to decode JSON:API response!"); + } + $this->assertSession()->statusCodeEquals(200); + $this->assertLinksRemoved($output_with_relationships, $links_removed); + } + + /** + * Asserts that links are removed or present depending on the module setting. + * + * @param array $drupal_jsonapi_response + * The Drupal JSON:API response to check. + * @param bool $links_should_be_removed + * TRUE if links attributes should be removed. + */ + private function assertLinksRemoved(array $drupal_jsonapi_response, bool $links_should_be_removed): void { + // Merge the includes into the response data because we want to make sure + // that the links attribute has also been removed from includes. + $response_content = $drupal_jsonapi_response['data']; + if (!is_array($response_content)) { + throw new JsonApiLinksException("Failed to parse 'data' in JSON:API response!"); + } + if (isset($drupal_jsonapi_response['included'])) { + $response_content = array_merge($response_content, $drupal_jsonapi_response['included']); + } + $first_item = json_encode($response_content); + if (!is_string($first_item)) { + throw new JsonApiLinksException("Failed to encode first item!"); + } + if ($links_should_be_removed) { + // Links attribute should be removed from each item. + $this->assertDoesNotMatchRegularExpression('/links/', $first_item); + } + else { + $this->assertMatchesRegularExpression('/links/', $first_item); + } + // Links attribute of response should never be removed. + $this->assertArrayHasKey('links', $drupal_jsonapi_response); + } + +} diff --git a/tests/src/Kernel/JsonapiLinksInstallTest.php b/tests/src/Kernel/JsonApiLinksInstallTest.php similarity index 95% rename from tests/src/Kernel/JsonapiLinksInstallTest.php rename to tests/src/Kernel/JsonApiLinksInstallTest.php index ac4420183bd5fd91bee8e0f02f84d413b57dba51..39a5fa96742e19b73b6fee76b6df5659c86a4bf3 100644 --- a/tests/src/Kernel/JsonapiLinksInstallTest.php +++ b/tests/src/Kernel/JsonApiLinksInstallTest.php @@ -11,7 +11,7 @@ use Drupal\KernelTests\KernelTestBase; * * @group jsonapi_links */ -class JsonapiLinksInstallTest extends KernelTestBase { +class JsonApiLinksInstallTest extends KernelTestBase { private const string MODULE_NAME = 'jsonapi_links';