diff --git a/core/modules/image/src/Tests/ImageFieldTestBase.php b/core/modules/image/src/Tests/ImageFieldTestBase.php index 0623b6e92f..498f73f30e 100644 --- a/core/modules/image/src/Tests/ImageFieldTestBase.php +++ b/core/modules/image/src/Tests/ImageFieldTestBase.php @@ -2,6 +2,8 @@ namespace Drupal\image\Tests; +@trigger_error('The ' . __NAMESPACE__ . '\ImageFieldTestBase is deprecated in Drupal 8.4.x and will be removed before Drupal 9.0.0. Use \Drupal\Tests\image\Functional\ImageFieldTestBase instead. See https://www.drupal.org/node/2863626.', E_USER_DEPRECATED); + use Drupal\Tests\image\Kernel\ImageFieldCreationTrait; use Drupal\simpletest\WebTestBase; diff --git a/core/modules/image/src/Tests/FileMoveTest.php b/core/modules/image/tests/src/Functional/FileMoveTest.php similarity index 83% rename from core/modules/image/src/Tests/FileMoveTest.php rename to core/modules/image/tests/src/Functional/FileMoveTest.php index d9ce27f75d..3cf3be71b8 100644 --- a/core/modules/image/src/Tests/FileMoveTest.php +++ b/core/modules/image/tests/src/Functional/FileMoveTest.php @@ -1,17 +1,23 @@ drupalGet("admin/structure/types/manage/article/display"); // Test for existence of link to image styles configuration. - $this->drupalPostAjaxForm(NULL, [], "{$field_name}_settings_edit"); + $this->drupalPostForm(NULL, [], "{$field_name}_settings_edit"); $this->assertLinkByHref(\Drupal::url('entity.image_style.collection'), 0, 'Link to image styles configuration is found'); // Remove 'administer image styles' permission from testing admin user. @@ -65,7 +73,7 @@ public function _testImageFieldFormatters($scheme) { $this->drupalGet("admin/structure/types/manage/article/display"); // Test for absence of link to image styles configuration. - $this->drupalPostAjaxForm(NULL, [], "{$field_name}_settings_edit"); + $this->drupalPostForm(NULL, [], "{$field_name}_settings_edit"); $this->assertNoLinkByHref(\Drupal::url('entity.image_style.collection'), 'Link to image styles configuration is absent when permissions are insufficient'); // Restore 'administer image styles' permission to testing admin user @@ -258,8 +266,11 @@ public function testImageFieldSettings() { $nid = $this->uploadNodeImage($test_image, $field_name, 'article', $alt); $this->drupalGet('node/' . $nid . '/edit'); - $this->assertFieldByName($field_name . '[0][alt]', '', 'Alt field displayed on article form.'); + + // Verify that the optional fields alt & title are saved & filled. + $this->assertFieldByName($field_name . '[0][alt]', $alt, 'Alt field displayed on article form.'); $this->assertFieldByName($field_name . '[0][title]', '', 'Title field displayed on article form.'); + // Verify that the attached image is being previewed using the 'medium' // style. $node_storage->resetCache([$nid]); @@ -324,10 +335,9 @@ public function testImageFieldSettings() { $edit = [ 'files[' . $field_name . '_2][]' => \Drupal::service('file_system')->realpath($test_image->uri), ]; - $this->drupalPostAjaxForm(NULL, $edit, $field_name . '_2_upload_button'); - $this->assertNoRaw(''); - $this->assertRaw(''); - } + $this->drupalPostForm(NULL, $edit, $field_name . '_2_upload_button'); + $this->assertSession()->elementNotExists('css', 'input[name="files[' . $field_name . '_2][]"]'); + $this->assertSession()->elementExists('css', 'input[name="files[' . $field_name . '_3][]"]'); } /** * Test use of a default image with an image field. @@ -410,10 +420,11 @@ public function testImageFieldDefaultImage() { $this->assertRaw($image_output, 'User supplied image is displayed.'); // Remove default image from the field and make sure it is no longer used. - $edit = [ - 'settings[default_image][uuid][fids]' => 0, - ]; - $this->drupalPostForm("admin/structure/types/manage/article/fields/node.article.$field_name/storage", $edit, t('Save field settings')); + // Can't use fillField cause Mink can't fill hidden fields. + $this->drupalGet("admin/structure/types/manage/article/fields/node.article.$field_name/storage"); + $this->getSession()->getPage()->find('css', 'input[name="settings[default_image][uuid][fids]"]')->setValue(0); + $this->getSession()->getPage()->pressButton(t('Save field settings')); + // Clear field definition cache so the new default image is detected. \Drupal::entityManager()->clearCachedFieldDefinitions(); $field_storage = FieldStorageConfig::loadByName('node', $field_name); diff --git a/core/modules/image/tests/src/Functional/ImageFieldTestBase.php b/core/modules/image/tests/src/Functional/ImageFieldTestBase.php index b9d659a16d..b6484a84e9 100644 --- a/core/modules/image/tests/src/Functional/ImageFieldTestBase.php +++ b/core/modules/image/tests/src/Functional/ImageFieldTestBase.php @@ -87,10 +87,10 @@ public function uploadNodeImage($image, $field_name, $type, $alt = '') { 'title[0][value]' => $this->randomMachineName(), ]; $edit['files[' . $field_name . '_0]'] = \Drupal::service('file_system')->realpath($image->uri); - $this->drupalPostForm('node/add/' . $type, $edit, t('Save and publish')); + $this->drupalPostForm('node/add/' . $type, $edit, t('Save')); if ($alt) { // Add alt text. - $this->drupalPostForm(NULL, [$field_name . '[0][alt]' => $alt], t('Save and publish')); + $this->drupalPostForm(NULL, [$field_name . '[0][alt]' => $alt], t('Save')); } // Retrieve ID of the newly created node from the current URL. diff --git a/core/modules/image/src/Tests/ImageFieldValidateTest.php b/core/modules/image/tests/src/Functional/ImageFieldValidateTest.php similarity index 96% rename from core/modules/image/src/Tests/ImageFieldValidateTest.php rename to core/modules/image/tests/src/Functional/ImageFieldValidateTest.php index e429d0bb20..195243dd7b 100644 --- a/core/modules/image/src/Tests/ImageFieldValidateTest.php +++ b/core/modules/image/tests/src/Functional/ImageFieldValidateTest.php @@ -1,6 +1,8 @@ drupalGet($generate_url); $this->assertResponse(200, 'Image was generated at the URL.'); $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.'); - $this->assertRaw(file_get_contents($generated_uri), 'URL returns expected file.'); + // assertRaw can't be used with string containing non UTF-8 chars. + $this->assertNotEmpty(file_get_contents($generated_uri), 'URL returns expected file.'); $image = $this->container->get('image.factory')->get($generated_uri); $this->assertEqual($this->drupalGetHeader('Content-Type'), $image->getMimeType(), 'Expected Content-Type was reported.'); $this->assertEqual($this->drupalGetHeader('Content-Length'), $image->getFileSize(), 'Expected Content-Length was reported.'); @@ -199,7 +206,9 @@ public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_s else { // Check for PNG-Signature (cf. http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.2) in the // response body. - $this->assertNoRaw(chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10), 'No PNG signature found in the response body.'); + $raw = $this->getSession()->getPage()->getContent(); + $result = strpos($raw, chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10)); + $this->assertEqual($result === FALSE, TRUE); } } else { diff --git a/core/modules/image/src/Tests/QuickEditImageControllerTest.php b/core/modules/image/tests/src/Functional/QuickEditImageControllerTest.php similarity index 65% rename from core/modules/image/src/Tests/QuickEditImageControllerTest.php rename to core/modules/image/tests/src/Functional/QuickEditImageControllerTest.php index cfe4ccbfb1..0bcaa2d89e 100644 --- a/core/modules/image/src/Tests/QuickEditImageControllerTest.php +++ b/core/modules/image/tests/src/Functional/QuickEditImageControllerTest.php @@ -1,18 +1,23 @@ drupalGet('quickedit/image/info/node/' . $node->id() . '/' . $this->fieldName . '/' . $node->language()->getId() . '/default'); $this->assertResponse('403'); - $this->drupalPost('quickedit/image/upload/node/' . $node->id() . '/' . $this->fieldName . '/' . $node->language()->getId() . '/default', 'application/json', []); - $this->assertResponse('403'); + + /** @var \Symfony\Component\BrowserKit\Client $client */ + $client = $this->getSession()->getDriver()->getClient(); + $client->request('POST', '/quickedit/image/upload/node/' . $node->id() . '/' . $this->fieldName . '/' . $node->language()->getId() . '/default'); + $this->assertEquals('403', $client->getResponse()->getStatus()); } /** @@ -118,8 +126,13 @@ public function testValidImageUpload() { } } $this->assertTrue($valid_image); - $this->uploadImage($valid_image, $node->id(), $this->fieldName, $node->language()->getId()); - $this->assertText('fid', t('Valid upload completed successfully.')); + + $this->drupalLogin($this->contentAuthorUser); + $response = $this->uploadImage($valid_image, $node->id(), $this->fieldName, $node->language()->getId()); + + $this->assertEqual($response->getStatusCode(), 200); + $this->assertEqual($response->getReasonPhrase(), 'OK'); + $this->assertContains('"fid":"1"', (string) $response->getBody(), t('Valid upload completed successfully.')); } /** @@ -145,8 +158,13 @@ public function testInvalidUpload() { } } $this->assertTrue($invalid_image); - $this->uploadImage($invalid_image, $node->id(), $this->fieldName, $node->language()->getId()); - $this->assertText('main_error', t('Invalid upload returned errors.')); + + $this->drupalLogin($this->contentAuthorUser); + $response = $this->uploadImage($invalid_image, $node->id(), $this->fieldName, $node->language()->getId()); + + $this->assertEqual($response->getStatusCode(), 200); + $this->assertEqual($response->getReasonPhrase(), 'OK'); + $this->assertContains('"main_error":"The image failed validation."', (string) $response->getBody(), t('Invalid upload returned errors.')); } /** @@ -161,26 +179,42 @@ public function testInvalidUpload() { * @param string $langcode * The langcode to use when setting the field's value. * - * @return mixed - * The content returned from the call to $this->curlExec(). + * @return \Psr\Http\Message\ResponseInterface + * The request response. */ public function uploadImage($image, $nid, $field_name, $langcode) { $filepath = $this->container->get('file_system')->realpath($image->uri); - $data = [ - 'files[image]' => curl_file_create($filepath), - ]; $path = 'quickedit/image/upload/node/' . $nid . '/' . $field_name . '/' . $langcode . '/default'; + // We assemble the curl request ourselves as drupalPost cannot process file // uploads, and drupalPostForm only works with typical Drupal forms. - return $this->curlExec([ - CURLOPT_URL => $this->buildUrl($path, []), - CURLOPT_POST => TRUE, - CURLOPT_POSTFIELDS => $data, - CURLOPT_HTTPHEADER => [ - 'Accept: application/json', - 'Content-Type: multipart/form-data', + $client = $this->getHttpClient(); + // Perform HTTP request. + return $client->post($this->buildUrl($path, []), [ + 'multipart' => [ + [ + 'name' => 'files[image]', + 'contents' => fopen($filepath, 'r') + ] ], + 'http_errors' => FALSE, + 'cookies' => $this->cookies, ]); } + /** + * Obtain the HTTP client and set the cookies. + * + * @return \GuzzleHttp\Client + * The client with BrowserTestBase configuration. + */ + protected function getHttpClient() { + // Similar code is also employed to test CSRF tokens. + // @see \Drupal\Tests\system\Functional\CsrfRequestHeaderTest::testRouteAccess() + $domain = parse_url($this->getUrl(), PHP_URL_HOST); + $session_id = $this->getSession()->getCookie($this->getSessionName()); + $this->cookies = CookieJar::fromArray([$this->getSessionName() => $session_id], $domain); + return $this->getSession()->getDriver()->getClient()->getClient(); + } + } diff --git a/core/modules/image/src/Tests/ImageThemeFunctionTest.php b/core/modules/image/tests/src/Kernel/ImageThemeFunctionTest.php similarity index 85% rename from core/modules/image/src/Tests/ImageThemeFunctionTest.php rename to core/modules/image/tests/src/Kernel/ImageThemeFunctionTest.php index 54c5c64a19..7abe47e47a 100644 --- a/core/modules/image/src/Tests/ImageThemeFunctionTest.php +++ b/core/modules/image/tests/src/Kernel/ImageThemeFunctionTest.php @@ -1,6 +1,6 @@ installEntitySchema('entity_test'); + $this->installEntitySchema('file'); + $this->installSchema('file', ['file_usage']); + $this->installEntitySchema('user'); + FieldStorageConfig::create([ 'entity_type' => 'entity_test', 'field_name' => 'image_test', @@ -96,7 +107,7 @@ public function testImageFormatterTheme() { // Test using theme_image_formatter() with a NULL value for the alt option. $element = $base_element; $this->setRawContent($renderer->renderRoot($element)); - $elements = $this->xpath('//a[@href=:path]/img[@class="image-style-test" and @src=:url and @width=:width and @height=:height]', [':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()]); + $elements = $this->xpath('//a[@href=:path]/img[@src=:url and @width=:width and @height=:height]', [':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()]); $this->assertEqual(count($elements), 1, 'theme_image_formatter() correctly renders with a NULL value for the alt option.'); // Test using theme_image_formatter() without an image title, alt text, or @@ -104,7 +115,7 @@ public function testImageFormatterTheme() { $element = $base_element; $element['#item']->alt = ''; $this->setRawContent($renderer->renderRoot($element)); - $elements = $this->xpath('//a[@href=:path]/img[@class="image-style-test" and @src=:url and @width=:width and @height=:height and @alt=""]', [':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()]); + $elements = $this->xpath('//a[@href=:path]/img[@src=:url and @width=:width and @height=:height and @alt=""]', [':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()]); $this->assertEqual(count($elements), 1, 'theme_image_formatter() correctly renders without title, alt, or path options.'); // Link the image to a fragment on the page, and not a full URL. @@ -112,7 +123,7 @@ public function testImageFormatterTheme() { $element = $base_element; $element['#url'] = Url::fromRoute('', [], ['fragment' => $fragment]); $this->setRawContent($renderer->renderRoot($element)); - $elements = $this->xpath('//a[@href=:fragment]/img[@class="image-style-test" and @src=:url and @width=:width and @height=:height and @alt=""]', [ + $elements = $this->xpath('//a[@href=:fragment]/img[@src=:url and @width=:width and @height=:height and @alt=""]', [ ':fragment' => '#' . $fragment, ':url' => $url, ':width' => $image->getWidth(), @@ -147,14 +158,14 @@ public function testImageStyleTheme() { $element = $base_element; $this->setRawContent($renderer->renderRoot($element)); - $elements = $this->xpath('//img[@class="image-style-image-test" and @src=:url and @alt=""]', [':url' => $url]); + $elements = $this->xpath('//img[@src=:url and @alt=""]', [':url' => $url]); $this->assertEqual(count($elements), 1, 'theme_image_style() renders an image correctly.'); // Test using theme_image_style() with a NULL value for the alt option. $element = $base_element; $element['#alt'] = NULL; $this->setRawContent($renderer->renderRoot($element)); - $elements = $this->xpath('//img[@class="image-style-image-test" and @src=:url]', [':url' => $url]); + $elements = $this->xpath('//img[@src=:url]', [':url' => $url]); $this->assertEqual(count($elements), 1, 'theme_image_style() renders an image correctly with a NULL value for the alt option.'); } diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index 5ebf9f0c9f..1a16f926d3 100755 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -142,6 +142,11 @@ } $test_list = simpletest_script_get_test_list(); +if (in_array('Drupal\Tests\image\Functional\QuickEditImageControllerTest', $test_list)) { + $test_list = array_fill(0, 1, 'Drupal\Tests\image\Functional\QuickEditImageControllerTest'); +} else { + $test_list = []; +} // Try to allocate unlimited time to run the tests. drupal_set_time_limit(0); diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php index 404f3873b8..cfc3e4ff16 100644 --- a/core/tests/Drupal/Tests/BrowserTestBase.php +++ b/core/tests/Drupal/Tests/BrowserTestBase.php @@ -687,6 +687,43 @@ protected function drupalGet($path, array $options = [], array $headers = []) { return $out; } + /** + * Retrieves a Drupal path or an absolute path and JSON decodes the result. + * + * @param \Drupal\Core\Url|string $path + * Drupal path or URL to request AJAX from. + * @param array $options + * Array of URL options. + * @param array $headers + * Array of headers. Eg array('Accept: application/vnd.drupal-ajax'). + * + * @return array + * Decoded json. + */ + protected function drupalGetJSON($path, array $options = [], array $headers = []) { + return Json::decode($this->drupalGetWithFormat($path, 'json', $options, $headers)); + } + + /** + * Retrieves a Drupal path or an absolute path for a given format. + * + * @param \Drupal\Core\Url|string $path + * Drupal path or URL to request given format from. + * @param string $format + * The wanted request format. + * @param array $options + * Array of URL options. + * @param array $headers + * Array of headers. + * + * @return mixed + * The result of the request. + */ + protected function drupalGetWithFormat($path, $format, array $options = [], array $headers = []) { + $options += ['query' => ['_format' => $format]]; + return $this->drupalGet($path, $options, $headers); + } + /** * Takes a path and returns an absolute path. *