diff --git a/core/modules/file/src/Tests/FileFieldTestBase.php b/core/modules/file/src/Tests/FileFieldTestBase.php index b7b90d7d37..91bb58ca7d 100644 --- a/core/modules/file/src/Tests/FileFieldTestBase.php +++ b/core/modules/file/src/Tests/FileFieldTestBase.php @@ -2,6 +2,8 @@ namespace Drupal\file\Tests; +@trigger_error('The ' . __NAMESPACE__ . '\FileFieldTestBase is scheduled for removal in Drupal 9.0.0. Instead, use \Drupal\Tests\file\Functional\FileFieldTestBase', E_USER_DEPRECATED); + use Drupal\field\Entity\FieldStorageConfig; use Drupal\field\Entity\FieldConfig; use Drupal\file\FileInterface; diff --git a/core/modules/file/src/Tests/FileManagedTestBase.php b/core/modules/file/src/Tests/FileManagedTestBase.php index 879e1c4e94..fde651dca6 100644 --- a/core/modules/file/src/Tests/FileManagedTestBase.php +++ b/core/modules/file/src/Tests/FileManagedTestBase.php @@ -2,6 +2,8 @@ namespace Drupal\file\Tests; +@trigger_error('The ' . __NAMESPACE__ . '\FileManagedTestBase is scheduled for removal in Drupal 9.0.0. Instead, use \Drupal\Tests\file\Functional\FileManagedTestBase', E_USER_DEPRECATED); + use Drupal\file\Entity\File; use Drupal\file\FileInterface; use Drupal\simpletest\WebTestBase; diff --git a/core/modules/file/src/Tests/DownloadTest.php b/core/modules/file/tests/src/Functional/DownloadTest.php similarity index 82% rename from core/modules/file/src/Tests/DownloadTest.php rename to core/modules/file/tests/src/Functional/DownloadTest.php index 239155cf40..113af2071d 100644 --- a/core/modules/file/src/Tests/DownloadTest.php +++ b/core/modules/file/tests/src/Functional/DownloadTest.php @@ -1,6 +1,9 @@ getViaScheme('public')->getDirectoryPath() . '/' . rawurlencode($file->getFilename()); $this->assertEqual($filename, $url, 'Correctly generated a URL for a created file.'); - $this->drupalHead($url); - $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the created file.'); - + $http_client = \Drupal::httpClient(); + $response = $http_client->head($url); + $this->assertEquals(200, $response->getStatusCode(), 'Confirmed that the generated URL is correct by downloading the created file.'); // Test generating a URL to a shipped file (i.e. a file that is part of // Drupal core, a module or a theme, for example a JavaScript file). $filepath = 'core/assets/vendor/jquery/jquery.min.js'; $url = file_create_url($filepath); $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath, $url, 'Correctly generated a URL for a shipped file.'); - $this->drupalHead($url); - $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.'); + $response = $http_client->head($url); + $this->assertEquals(200, $response->getStatusCode(), 'Confirmed that the generated URL is correct by downloading the shipped file.'); } /** @@ -70,17 +73,28 @@ protected function doPrivateFileTransferTest() { $this->assertResponse(200, 'Correctly allowed access to a file when file_test provides headers.'); // Test that the file transferred correctly. - $this->assertEqual($contents, $this->content, 'Contents of the file are correct.'); + $this->assertSame($contents, $this->getSession()->getPage()->getContent(), 'Contents of the file are correct.'); // Deny access to all downloads via a -1 header. file_test_set_return('download', -1); - $this->drupalHead($url); - $this->assertResponse(403, 'Correctly denied access to a file when file_test sets the header to -1.'); + $http_client = \Drupal::httpClient(); + try { + $http_client->head($url); + $this->fail('Not correctly denied access to a file when file_test sets the header to -1.'); + } + catch (RequestException $e) { + $this->assertSame(403, $e->getCode()); + } // Try non-existent file. $url = file_create_url('private://' . $this->randomMachineName()); - $this->drupalHead($url); - $this->assertResponse(404, 'Correctly returned 404 response for a non-existent file.'); + try { + $http_client->head($url); + $this->fail('Not correctly returned 404 response for a non-existent file.'); + } + catch (RequestException $e) { + $this->assertSame(404, $e->getCode()); + } } /** @@ -172,4 +186,20 @@ private function checkUrl($scheme, $directory, $filename, $expected_url) { $file->delete(); } + /** + * Changes in memory settings. + * + * @param $name + * The name of the setting to return. + * @param $value + * The value of the setting. + * + * @see \Drupal\Core\Site\Settings::get() + */ + protected function settingsSet($name, $value) { + $settings = Settings::getAll(); + $settings[$name] = $value; + new Settings($settings); + } + } diff --git a/core/modules/file/src/Tests/FileFieldAnonymousSubmissionTest.php b/core/modules/file/tests/src/Functional/FileFieldAnonymousSubmissionTest.php similarity index 99% rename from core/modules/file/src/Tests/FileFieldAnonymousSubmissionTest.php rename to core/modules/file/tests/src/Functional/FileFieldAnonymousSubmissionTest.php index 66d87cd7e8..9f004abba7 100644 --- a/core/modules/file/src/Tests/FileFieldAnonymousSubmissionTest.php +++ b/core/modules/file/tests/src/Functional/FileFieldAnonymousSubmissionTest.php @@ -1,6 +1,6 @@ drupalPostForm("node/$nid/edit", $edit, t('Preview')); + $this->drupalPostFormWithInvalidOptions("node/$nid/edit", $edit, t('Preview')); $this->clickLink(t('Back to content editing')); $this->assertRaw($field_name . '[0][display]', 'First file appears as expected.'); $this->assertRaw($field_name . '[1][display]', 'Second file appears as expected.'); diff --git a/core/modules/file/src/Tests/FileFieldFormatterAccessTest.php b/core/modules/file/tests/src/Functional/FileFieldFormatterAccessTest.php similarity index 95% rename from core/modules/file/src/Tests/FileFieldFormatterAccessTest.php rename to core/modules/file/tests/src/Functional/FileFieldFormatterAccessTest.php index 68bb0473ef..5fbbdf14ff 100644 --- a/core/modules/file/src/Tests/FileFieldFormatterAccessTest.php +++ b/core/modules/file/tests/src/Functional/FileFieldFormatterAccessTest.php @@ -1,6 +1,6 @@ drupalGet('rss.xml'); $uploaded_filename = str_replace('public://', '', $node_file->getFileUri()); $selector = sprintf( - 'enclosure[url="%s"][length="%s"][type="%s"]', + 'enclosure[@url="%s"][@length="%s"][@type="%s"]', file_create_url("public://$uploaded_filename", ['absolute' => TRUE]), $node_file->getSize(), $node_file->getMimeType() ); - $this->assertTrue(!empty($this->cssSelect($selector)), 'File field RSS enclosure is displayed when viewing the RSS feed.'); + $this->assertNotNull($this->getSession()->getDriver()->find('xpath', $selector), 'File field RSS enclosure is displayed when viewing the RSS feed.'); } } diff --git a/core/modules/file/src/Tests/FileFieldRevisionTest.php b/core/modules/file/tests/src/Functional/FileFieldRevisionTest.php similarity index 99% rename from core/modules/file/src/Tests/FileFieldRevisionTest.php rename to core/modules/file/tests/src/Functional/FileFieldRevisionTest.php index 04fbcb8767..6f05136cca 100644 --- a/core/modules/file/src/Tests/FileFieldRevisionTest.php +++ b/core/modules/file/tests/src/Functional/FileFieldRevisionTest.php @@ -1,6 +1,6 @@ drupalCreateContentType(['type' => 'article', 'name' => 'Article']); } - /** - * Retrieves a sample file of the specified type. - * - * @return \Drupal\file\FileInterface - */ - public function getTestFile($type_name, $size = NULL) { - // Get a file to upload. - $file = current($this->drupalGetTestFiles($type_name, $size)); - - // Add a filesize property to files as would be read by - // \Drupal\file\Entity\File::load(). - $file->filesize = filesize($file->uri); - - return File::create((array) $file); - } - - /** - * Retrieves the fid of the last inserted file. - */ - public function getLastFileId() { - return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField(); - } - - /** - * Creates a new file field. - * - * @param string $name - * The name of the new field (all lowercase), exclude the "field_" prefix. - * @param string $entity_type - * The entity type. - * @param string $bundle - * The bundle that this field will be added to. - * @param array $storage_settings - * A list of field storage settings that will be added to the defaults. - * @param array $field_settings - * A list of instance settings that will be added to the instance defaults. - * @param array $widget_settings - * A list of widget settings that will be added to the widget defaults. - */ - public function createFileField($name, $entity_type, $bundle, $storage_settings = [], $field_settings = [], $widget_settings = []) { - $field_storage = FieldStorageConfig::create([ - 'entity_type' => $entity_type, - 'field_name' => $name, - 'type' => 'file', - 'settings' => $storage_settings, - 'cardinality' => !empty($storage_settings['cardinality']) ? $storage_settings['cardinality'] : 1, - ]); - $field_storage->save(); - - $this->attachFileField($name, $entity_type, $bundle, $field_settings, $widget_settings); - return $field_storage; - } - - /** - * Attaches a file field to an entity. - * - * @param string $name - * The name of the new field (all lowercase), exclude the "field_" prefix. - * @param string $entity_type - * The entity type this field will be added to. - * @param string $bundle - * The bundle this field will be added to. - * @param array $field_settings - * A list of field settings that will be added to the defaults. - * @param array $widget_settings - * A list of widget settings that will be added to the widget defaults. - */ - public function attachFileField($name, $entity_type, $bundle, $field_settings = [], $widget_settings = []) { - $field = [ - 'field_name' => $name, - 'label' => $name, - 'entity_type' => $entity_type, - 'bundle' => $bundle, - 'required' => !empty($field_settings['required']), - 'settings' => $field_settings, - ]; - FieldConfig::create($field)->save(); - - entity_get_form_display($entity_type, $bundle, 'default') - ->setComponent($name, [ - 'type' => 'file_generic', - 'settings' => $widget_settings, - ]) - ->save(); - // Assign display settings. - entity_get_display($entity_type, $bundle, 'default') - ->setComponent($name, [ - 'label' => 'hidden', - 'type' => 'file_default', - ]) - ->save(); - } - - /** - * Updates an existing file field with new settings. - */ - public function updateFileField($name, $type_name, $field_settings = [], $widget_settings = []) { - $field = FieldConfig::loadByName('node', $type_name, $name); - $field->setSettings(array_merge($field->getSettings(), $field_settings)); - $field->save(); - - entity_get_form_display('node', $type_name, 'default') - ->setComponent($name, [ - 'settings' => $widget_settings, - ]) - ->save(); - } - - /** - * Uploads a file to a node. - * - * @param \Drupal\file\FileInterface $file - * The File to be uploaded. - * @param string $field_name - * The name of the field on which the files should be saved. - * @param $nid_or_type - * A numeric node id to upload files to an existing node, or a string - * indicating the desired bundle for a new node. - * @param bool $new_revision - * The revision number. - * @param array $extras - * Additional values when a new node is created. - * - * @return int - * The node id. - */ - public function uploadNodeFile(FileInterface $file, $field_name, $nid_or_type, $new_revision = TRUE, array $extras = []) { - return $this->uploadNodeFiles([$file], $field_name, $nid_or_type, $new_revision, $extras); - } - - /** - * Uploads multiple files to a node. - * - * @param \Drupal\file\FileInterface[] $files - * The files to be uploaded. - * @param string $field_name - * The name of the field on which the files should be saved. - * @param $nid_or_type - * A numeric node id to upload files to an existing node, or a string - * indicating the desired bundle for a new node. - * @param bool $new_revision - * The revision number. - * @param array $extras - * Additional values when a new node is created. - * - * @return int - * The node id. - */ - public function uploadNodeFiles(array $files, $field_name, $nid_or_type, $new_revision = TRUE, array $extras = []) { - $edit = [ - 'title[0][value]' => $this->randomMachineName(), - 'revision' => (string) (int) $new_revision, - ]; - - $node_storage = $this->container->get('entity.manager')->getStorage('node'); - if (is_numeric($nid_or_type)) { - $nid = $nid_or_type; - $node_storage->resetCache([$nid]); - $node = $node_storage->load($nid); - } - else { - // Add a new node. - $extras['type'] = $nid_or_type; - $node = $this->drupalCreateNode($extras); - $nid = $node->id(); - // Save at least one revision to better simulate a real site. - $node->setNewRevision(); - $node->save(); - $node_storage->resetCache([$nid]); - $node = $node_storage->load($nid); - $this->assertNotEqual($nid, $node->getRevisionId(), 'Node revision exists.'); - } - - // Attach files to the node. - $field_storage = FieldStorageConfig::loadByName('node', $field_name); - // File input name depends on number of files already uploaded. - $field_num = count($node->{$field_name}); - $name = 'files[' . $field_name . "_$field_num]"; - if ($field_storage->getCardinality() != 1) { - $name .= '[]'; - } - foreach ($files as $file) { - $file_path = $this->container->get('file_system')->realpath($file->getFileUri()); - if (count($files) == 1) { - $edit[$name] = $file_path; - } - else { - $edit[$name][] = $file_path; - } - } - $this->drupalPostForm("node/$nid/edit", $edit, t('Save and keep published')); - - return $nid; - } - - /** - * Removes a file from a node. - * - * Note that if replacing a file, it must first be removed then added again. - */ - public function removeNodeFile($nid, $new_revision = TRUE) { - $edit = [ - 'revision' => (string) (int) $new_revision, - ]; - - $this->drupalPostForm('node/' . $nid . '/edit', [], t('Remove')); - $this->drupalPostForm(NULL, $edit, t('Save and keep published')); - } - - /** - * Replaces a file within a node. - */ - public function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE) { - $edit = [ - 'files[' . $field_name . '_0]' => drupal_realpath($file->getFileUri()), - 'revision' => (string) (int) $new_revision, - ]; - - $this->drupalPostForm('node/' . $nid . '/edit', [], t('Remove')); - $this->drupalPostForm(NULL, $edit, t('Save and keep published')); - } - - /** - * Asserts that a file exists physically on disk. - * - * Overrides PHPUnit\Framework\Assert::assertFileExists() to also work with - * file entities. - * - * @param \Drupal\File\FileInterface|string $file - * Either the file entity or the file URI. - * @param string $message - * (optional) A message to display with the assertion. - */ - public static function assertFileExists($file, $message = NULL) { - $message = isset($message) ? $message : format_string('File %file exists on the disk.', ['%file' => $file->getFileUri()]); - $filename = $file instanceof FileInterface ? $file->getFileUri() : $file; - parent::assertFileExists($filename, $message); - } - - /** - * Asserts that a file exists in the database. - */ - public function assertFileEntryExists($file, $message = NULL) { - $this->container->get('entity.manager')->getStorage('file')->resetCache(); - $db_file = File::load($file->id()); - $message = isset($message) ? $message : format_string('File %file exists in database at the correct path.', ['%file' => $file->getFileUri()]); - $this->assertEqual($db_file->getFileUri(), $file->getFileUri(), $message); - } - - /** - * Asserts that a file does not exist on disk. - * - * Overrides PHPUnit\Framework\Assert::assertFileExists() to also work with - * file entities. - * - * @param \Drupal\File\FileInterface|string $file - * Either the file entity or the file URI. - * @param string $message - * (optional) A message to display with the assertion. - */ - public static function assertFileNotExists($file, $message = NULL) { - $message = isset($message) ? $message : format_string('File %file exists on the disk.', ['%file' => $file->getFileUri()]); - $filename = $file instanceof FileInterface ? $file->getFileUri() : $file; - parent::assertFileNotExists($filename, $message); - } - - /** - * Asserts that a file does not exist in the database. - */ - public function assertFileEntryNotExists($file, $message) { - $this->container->get('entity.manager')->getStorage('file')->resetCache(); - $message = isset($message) ? $message : format_string('File %file exists in database at the correct path.', ['%file' => $file->getFileUri()]); - $this->assertFalse(File::load($file->id()), $message); - } - - /** - * Asserts that a file's status is set to permanent in the database. - */ - public function assertFileIsPermanent(FileInterface $file, $message = NULL) { - $message = isset($message) ? $message : format_string('File %file is permanent.', ['%file' => $file->getFileUri()]); - $this->assertTrue($file->isPermanent(), $message); - } - } diff --git a/core/modules/file/src/Tests/FileFieldValidateTest.php b/core/modules/file/tests/src/Functional/FileFieldValidateTest.php similarity index 99% rename from core/modules/file/src/Tests/FileFieldValidateTest.php rename to core/modules/file/tests/src/Functional/FileFieldValidateTest.php index 4698185c4f..c307ce0fb2 100644 --- a/core/modules/file/src/Tests/FileFieldValidateTest.php +++ b/core/modules/file/tests/src/Functional/FileFieldValidateTest.php @@ -1,6 +1,6 @@ sumUsages($file_usage->listUsage($file)); $this->assertRaw('admin/content/files/usage/' . $file->id() . '">' . $usage); - $result = $this->xpath("//td[contains(@class, 'views-field-status') and contains(text(), :value)]", [':value' => t('Temporary')]); + $result = $this->xpath("//td[contains(@class, 'views-field-status') and contains(text(), :value)]", [':value' => 'Temporary']); $this->assertEqual(1, count($result), 'Unused file marked as temporary.'); // Test file usage page. diff --git a/core/modules/file/src/Tests/FileOnTranslatedEntityTest.php b/core/modules/file/tests/src/Functional/FileOnTranslatedEntityTest.php similarity index 99% rename from core/modules/file/src/Tests/FileOnTranslatedEntityTest.php rename to core/modules/file/tests/src/Functional/FileOnTranslatedEntityTest.php index b8a22ae01b..508ead4742 100644 --- a/core/modules/file/src/Tests/FileOnTranslatedEntityTest.php +++ b/core/modules/file/tests/src/Functional/FileOnTranslatedEntityTest.php @@ -1,6 +1,6 @@ drupalPostForm('node/add/' . $type_name, $edit, t('Save')); $new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']); $edit[$field_name . '[0][fids]'] = $node_file->id(); - $this->drupalPostForm('node/' . $new_node->id() . '/edit', $edit, t('Save')); + $this->drupalPostFormWithInvalidOptions('node/' . $new_node->id() . '/edit', $edit, t('Save')); // Make sure the form submit failed - we stayed on the edit form. $this->assertUrl('node/' . $new_node->id() . '/edit'); // Check that we got the expected constraint form error. @@ -91,7 +91,7 @@ public function testPrivateFile() { $edit = []; $edit['title[0][value]'] = $this->randomMachineName(); $edit[$field_name . '[0][fids]'] = $node_file->id(); - $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save')); + $this->drupalPostFormWithInvalidOptions('node/add/' . $type_name, $edit, t('Save')); $new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']); $this->assertTrue(empty($new_node), 'Node was not created.'); $this->assertUrl('node/add/' . $type_name); @@ -142,9 +142,7 @@ public function testPrivateFile() { $this->drupalGet($file_url); $this->assertResponse(200, 'Confirmed that the anonymous uploader has access to the temporary file.'); // Close the prior connection and remove the session cookie. - $this->curlClose(); - $this->curlCookies = []; - $this->cookies = []; + $this->getSession()->reset(); $this->drupalGet($file_url); $this->assertResponse(403, 'Confirmed that another anonymous user cannot access the temporary file.'); @@ -172,9 +170,7 @@ public function testPrivateFile() { $this->drupalGet($file_url); $this->assertResponse(200, 'Confirmed that the anonymous uploader has access to the file whose references were removed.'); // Close the prior connection and remove the session cookie. - $this->curlClose(); - $this->curlCookies = []; - $this->cookies = []; + $this->getSession()->reset(); $this->drupalGet($file_url); $this->assertResponse(403, 'Confirmed that another anonymous user cannot access the file whose references were removed.'); @@ -195,9 +191,7 @@ public function testPrivateFile() { $this->drupalGet($file_url); $this->assertResponse(200, 'Confirmed that the anonymous uploader has access to the permanent file that is referenced by a published node.'); // Close the prior connection and remove the session cookie. - $this->curlClose(); - $this->curlCookies = []; - $this->cookies = []; + $this->getSession()->reset(); $this->drupalGet($file_url); $this->assertResponse(200, 'Confirmed that another anonymous user also has access to the permanent file that is referenced by a published node.'); @@ -222,9 +216,7 @@ public function testPrivateFile() { $this->drupalGet($file_url); $this->assertResponse(403, 'Confirmed that the anonymous uploader cannot access the permanent file when it is referenced by an unpublished node.'); // Close the prior connection and remove the session cookie. - $this->curlClose(); - $this->curlCookies = []; - $this->cookies = []; + $this->getSession()->reset(); $this->drupalGet($file_url); $this->assertResponse(403, 'Confirmed that another anonymous user cannot access the permanent file when it is referenced by an unpublished node.'); } diff --git a/core/modules/file/src/Tests/FileTokenReplaceTest.php b/core/modules/file/tests/src/Functional/FileTokenReplaceTest.php similarity index 99% rename from core/modules/file/src/Tests/FileTokenReplaceTest.php rename to core/modules/file/tests/src/Functional/FileTokenReplaceTest.php index 5bfa07b9ab..fd955913b9 100644 --- a/core/modules/file/src/Tests/FileTokenReplaceTest.php +++ b/core/modules/file/tests/src/Functional/FileTokenReplaceTest.php @@ -1,6 +1,6 @@ 'jpeg', ]; - $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); + $this->drupalPostFormWithInvalidOptions('file-test/save_upload_from_form_test', $edit, t('Submit')); $this->assertResponse(200, 'Received a 200 response for posted test file.'); $this->assertRaw(t('Epic upload FAIL!'), 'Found the failure message.'); diff --git a/core/modules/file/src/Tests/SaveUploadTest.php b/core/modules/file/tests/src/Functional/SaveUploadTest.php similarity index 98% rename from core/modules/file/src/Tests/SaveUploadTest.php rename to core/modules/file/tests/src/Functional/SaveUploadTest.php index a9f69c2fb7..9382c8b4a6 100644 --- a/core/modules/file/src/Tests/SaveUploadTest.php +++ b/core/modules/file/tests/src/Functional/SaveUploadTest.php @@ -1,8 +1,9 @@ setOwner($user); + } + else { + $file->setOwner($this->adminUser); + } + // Change the file status to be temporary. + $file->setTemporary(); + // Save the changes. + $file->save(); + } - protected function setUp() { - parent::setUp(); - $this->adminUser = $this->drupalCreateUser(['access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer permissions', 'administer content types', 'administer node fields', 'administer node display', 'administer nodes', 'bypass node access']); - $this->drupalLogin($this->adminUser); - $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + return $file; } /** @@ -182,7 +196,7 @@ public function uploadNodeFile(FileInterface $file, $field_name, $nid_or_type, $ * @return int * The node id. */ - public function uploadNodeFiles(array $files, $field_name, $nid_or_type, $new_revision = TRUE, array $extras = []) { + public function uploadNodeFiles(array $files, $field_name, $nid_or_type, $new_revision = TRUE, array $extras = [], $is_hack = FALSE) { $edit = [ 'title[0][value]' => $this->randomMachineName(), 'revision' => (string) (int) $new_revision, @@ -224,7 +238,12 @@ public function uploadNodeFiles(array $files, $field_name, $nid_or_type, $new_re $edit[$name][] = $file_path; } } - $this->drupalPostForm("node/$nid/edit", $edit, t('Save and keep published')); + if ($is_hack) { + $this->drupalPostFormWithInvalidOptions("node/$nid/edit", $edit, t('Save')); + } + else { + $this->drupalPostForm("node/$nid/edit", $edit, t('Save')); + } return $nid; } @@ -240,7 +259,7 @@ public function removeNodeFile($nid, $new_revision = TRUE) { ]; $this->drupalPostForm('node/' . $nid . '/edit', [], t('Remove')); - $this->drupalPostForm(NULL, $edit, t('Save and keep published')); + $this->drupalPostForm(NULL, $edit, t('Save')); } /** @@ -253,7 +272,7 @@ public function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE) ]; $this->drupalPostForm('node/' . $nid . '/edit', [], t('Remove')); - $this->drupalPostForm(NULL, $edit, t('Save and keep published')); + $this->drupalPostForm(NULL, $edit, t('Save')); } /** diff --git a/core/tests/Drupal/Tests/DrupalPostFormWithInvalidOptionsTraitTest.php b/core/tests/Drupal/Tests/DrupalPostFormWithInvalidOptionsTraitTest.php new file mode 100644 index 0000000000..9752ddac74 --- /dev/null +++ b/core/tests/Drupal/Tests/DrupalPostFormWithInvalidOptionsTraitTest.php @@ -0,0 +1,207 @@ +admin = $this->drupalCreateUser([], 'admin user', TRUE); + $this->user = $this->drupalCreateUser(['access content']); + + // Create Basic page and Article node types. + $this->drupalCreateContentType([ + 'type' => 'page', + 'name' => 'Page', + ]); + + // Create test node instance. + $this->drupalCreateNode([ + 'type' => 'page', + ]); + + $this->drupalLogin($this->admin); + } + + /** + * Tests post form request with valid values. + */ + public function testParameters() { + // Check post request with set field value. + $edit = ['title[0][value]' => 'Test1']; + $this->drupalPostFormWithInvalidOptions('node/1/edit', $edit, t('Save')); + $this->assertSession()->pageTextContains('Page Test1 has been updated'); + + // Check post request without edit values. + $this->drupalGet('node/1'); + $this->assertSession()->pageTextNotContains('Page Test1 has been updated'); + $this->drupalPostFormWithInvalidOptions('node/1/edit'); + $this->assertSession()->pageTextContains('Page Test1 has been updated'); + + // Check post request without any arguments. + $this->drupalGet('node/1'); + $this->assertSession()->pageTextNotContains('Page Test1 has been updated'); + $this->drupalGet('node/1/edit'); + $this->drupalPostFormWithInvalidOptions(); + $this->assertSession()->pageTextContains('Page Test1 has been updated'); + + // Check post request with options. + $options = ['query' => ['destination' => 'node/add/page']]; + $this->drupalPostFormWithInvalidOptions('node/1/edit', [], t('Save'), $options); + $this->assertSession()->pageTextContains('Page Test1 has been updated'); + $this->assertSession()->pageTextContains('Create Page'); + + // Check post request with set header value. + try { + $headers = ['HTTP_HOST' => 'invalid-host']; + $this->drupalPostFormWithInvalidOptions('node/1/edit', [], t('Save'), [], $headers); + $this->assertSession()->pageTextContains('The requested URL was not found on this server.'); + } + catch (ConnectException $e) { + $this->assertContains('cURL error 6: Could not resolve host: invalid-host', $e->getMessage()); + } + // Check post request with with find form by ID. + $edit = ['title[0][value]' => 'Test by ID']; + $form_html_id = 'node-page-edit-form'; + $this->drupalPostFormWithInvalidOptions('node/1/edit', $edit, NULL, [], [], $form_html_id); + $this->assertSession()->pageTextContains('Page Test by ID has been updated'); + + // Check that raw arguments have more prior than $edit. + $edit = ['title[0][value]' => 'Test2']; + $raw_edit = ['form_params' => ['title' => [['value' => 'Test3']]]]; + $this->drupalPostFormWithInvalidOptions('node/1/edit', $edit, t('Save'), [], [], NULL, $raw_edit); + $this->assertSession()->pageTextContains('Page Test3 has been updated'); + } + + /** + * Tests post form request with non existent field. + */ + public function testNonExistentField() { + // Check that edit params can contains values of nonexistent fields. + $edit = [ + 'title[0][value]' => 'Chell', + 'cake' => 'is a lie', + ]; + $this->drupalPostFormWithInvalidOptions('node/1/edit', $edit, t('Save')); + $this->assertSession()->pageTextContains('Page Chell has been updated'); + + // Check that drupalPostForm() can not do this request. + try { + $this->drupalPostForm('node/1/edit', $edit, t('Save')); + $this->fail('drupalPostForm() does not work with nonexistent field'); + } + catch (ElementNotFoundException $e) { + $this->assertSame('Form field with id|name|label|value "cake" not found.', $e->getMessage()); + } + } + + /** + * Tests post form request with hidden field. + */ + public function testHiddenField() { + // Check post request with change hidden field. + $edit = ['changed' => 1234567890]; + $this->drupalPostFormWithInvalidOptions('node/1/edit', $edit, t('Save')); + $this->assertSession()->pageTextContains('The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.'); + + // Check that drupalPostForm() can not do this request. + try { + $this->drupalPostForm('node/1/edit', $edit, t('Save')); + $this->fail('drupalPostForm() does not work with hidden field'); + } + catch (ElementNotFoundException $e) { + $this->assertSame('Form field with id|name|label|value "changed" not found.', $e->getMessage()); + } + } + + /** + * Tests post form request with invalid field. + */ + public function testInvalidFileField() { + $file = current($this->getTestFiles('text')); + $path = \Drupal::service('file_system')->realpath($file->uri); + + // Check post request with invalid options. + $edit = [ + 'files[file_test_upload][]' => [ + $path, + $path, + ], + 'allow_all_extensions' => FALSE, + 'is_image_file' => TRUE, + 'extensions' => 'jpeg', + ]; + $this->drupalPostFormWithInvalidOptions('file-test/save_upload_from_form_test', $edit, t('Submit')); + $this->assertSession()->pageTextContains('Epic upload FAIL!'); + + // Check that drupalPostForm() can not do this request. + try { + $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit')); + $this->fail('drupalPostForm() does not work with array instead of string'); + } + catch (\PHPUnit_Framework_Error_Warning $e) { + $this->assertSame('is_readable() expects parameter 1 to be a valid path, array given', $e->getMessage()); + } + } + + /** + * Tests post form request with multi file field. + */ + public function testMultiFileFieldWithLimit() { + $this->createFileField('field_file_multi', 'node', 'page', ['cardinality' => 2]); + + $file = current($this->getTestFiles('text')); + $path = \Drupal::service('file_system')->realpath($file->uri); + + // Check post request with more files, that limit of field. + $edit = ['files[field_file_multi_0][]' => [$path, $path, $path]]; + $this->drupalPostFormWithInvalidOptions('node/1/edit', $edit, t('Save')); + $this->assertSession()->pageTextContains('Field field_file_multi can only hold 2 values but there were 3 uploaded.'); + + // Check that drupalPostForm() can not do this request. + try { + $this->drupalPostForm('node/1/edit', $edit, t('Save')); + $this->fail('drupalPostForm() does not work with more files that cardinality of field'); + } + catch (ElementNotFoundException $e) { + $this->assertSame('Form field with id|name|label|value "files[field_file_multi_0][]" not found.', $e->getMessage()); + } + } + +} diff --git a/core/tests/Drupal/Tests/Traits/DrupalPostFormWithInvalidOptionsTrait.php b/core/tests/Drupal/Tests/Traits/DrupalPostFormWithInvalidOptionsTrait.php new file mode 100644 index 0000000000..9c9e3a32ce --- /dev/null +++ b/core/tests/Drupal/Tests/Traits/DrupalPostFormWithInvalidOptionsTrait.php @@ -0,0 +1,186 @@ + ['title' => [['value' => 'Test']]], + * 'form_multipart' => [['files[field]' => 'path']], + * ]; + * This is used when $edit values can not be correct divided. Like case when + * it is need to edit values via multipart group, but the form does not have + * file fields with such names, or need test several values for single file + * fields. + */ + protected function drupalPostFormWithInvalidOptions($path = NULL, array $edit = [], $submit = NULL, array $options = [], array $headers = [], $form_html_id = NULL, $raw_edit = []) { + if ($path !== NULL) { + $this->drupalGet($path, $options); + } + + if (is_object($submit)) { + $submit = (string) $submit; + } + + /** @var \Drupal\Tests\WebAssert $assert */ + $assert = $this->assertSession(); + + // Get form and submit button. + if ($form_html_id !== NULL || $submit === NULL) { + $form_selector = '//form' . ($form_html_id ? "[@id='$form_html_id']" : ''); + $form_element = $assert->elementExists('xpath', $form_selector); + $submit_button = ($submit !== NULL) ? $form_element->findField($submit) : $form_element->find('css', '[type="submit"]'); + } + else { + $submit_button = $assert->buttonExists($submit); + $form_element = $assert->elementExists('xpath', './ancestor::form', $submit_button); + } + + // Get form values. + $element = $submit_button ?: $form_element; + $crawler = $this->getSession()->getDriver()->getClient()->getCrawler(); + $form = $crawler->filterXPath($element->getXpath())->form(); + $form_params = $form->getPhpValues(); + $form_multipart = $form->getPhpFiles(); + + // Get edit values. + $edit = $this->castSafeStrings($edit); + // Decouple multipart values (files) from other params. + $edit_params = []; + $edit_multipart = []; + foreach ($edit as $name => $value) { + $field = $form_element->findField($name); + if ($field && $field->getAttribute('type') === 'file') { + $edit_multipart[$name] = $value; + } + else { + $edit_params[$name] = $value; + } + } + $edit_multipart = $this->prepareFilesValue($edit_multipart); + $edit_params = $this->getPhpValues($edit_params); + $edit_multipart = $this->getPhpValues($edit_multipart); + + // Get raw edit values. + $raw_edit_params = isset($raw_edit['form_params']) ? $raw_edit['form_params'] : []; + $raw_edit_multipart = isset($raw_edit['multipart']) ? $raw_edit['multipart'] : []; + + // Combine form, edit and raw values. + $params = array_replace_recursive($form_params, $edit_params, $raw_edit_params); + $multipart = array_replace_recursive($form_multipart, $edit_multipart, $raw_edit_multipart); + + $this->prepareRequest(); + + $client = $this->getSession()->getDriver()->getClient(); + $method = $form_element->getAttribute('method') ?: 'POST'; + $action = $form_element->getAttribute('action') ?: $this->getUrl(); + $client->request($method, $action, $params, $multipart, $headers); + + $this->refreshVariables(); + + // Check if there are any meta refresh redirects (like Batch API pages). + if ($this->checkForMetaRefresh()) { + // We are finished with all meta refresh redirects. + $this->metaRefreshCount = 0; + } + } + + /** + * Expands the list of file values, and replaces paths on absolute path. + * + * Example: + * [ + * 'files[field_single]' => 'path0', + * 'files[field_multi][]' => ['path1', 'path2'] + * ] + * will be converted to + * [ + * 'files[field_single]' => 'real/path0', + * 'files[field_multi][0]' => 'real/path1', + * 'files[field_multi][1]' => 'real/path2', + * ] + * + * @param array $files + * List of files to prepare, each item as 'field_name' => value. + * + * @return array + * Prepared array of files. + */ + protected function prepareFilesValue($files) { + foreach ($files as $key => $paths) { + if (is_array($paths)) { + foreach ($paths as $i => $path) { + $new_key = str_replace('[]', "[$i]", $key); + $files[$new_key] = $path; + } + unset($files[$key]); + } + } + foreach ($files as $key => $path) { + $files[$key] = $this->container->get('file_system')->realpath($path); + } + return $files; + } + + /** + * Gets the field values as PHP. + * + * Copied from \Symfony\Component\DomCrawler\Form::getPhpValues() + * + * This method converts fields with the array notation. + * Example: + * ['foo[0][bar]' => 'value'] to ['foo' => [['bar' => 'value']]. + * + * @return array + * An array of field values. + */ + public function getPhpValues($post) { + $values = array(); + foreach ($post as $name => $value) { + $qs = http_build_query(array($name => $value), '', '&'); + if (!empty($qs)) { + parse_str($qs, $expandedValue); + $varName = substr($name, 0, strlen(key($expandedValue))); + $values = array_replace_recursive($values, array($varName => current($expandedValue))); + } + } + + return $values; + } + +}