function FileFieldWidgetTest::testMultiValuedWidget

Same name in this branch
  1. 11.x core/modules/file/tests/src/FunctionalJavascript/FileFieldWidgetTest.php \Drupal\Tests\file\FunctionalJavascript\FileFieldWidgetTest::testMultiValuedWidget()
Same name in other branches
  1. 9 core/modules/file/tests/src/FunctionalJavascript/FileFieldWidgetTest.php \Drupal\Tests\file\FunctionalJavascript\FileFieldWidgetTest::testMultiValuedWidget()
  2. 9 core/modules/file/tests/src/Functional/FileFieldWidgetTest.php \Drupal\Tests\file\Functional\FileFieldWidgetTest::testMultiValuedWidget()
  3. 8.9.x core/modules/file/tests/src/FunctionalJavascript/FileFieldWidgetTest.php \Drupal\Tests\file\FunctionalJavascript\FileFieldWidgetTest::testMultiValuedWidget()
  4. 8.9.x core/modules/file/tests/src/Functional/FileFieldWidgetTest.php \Drupal\Tests\file\Functional\FileFieldWidgetTest::testMultiValuedWidget()
  5. 10 core/modules/file/tests/src/FunctionalJavascript/FileFieldWidgetTest.php \Drupal\Tests\file\FunctionalJavascript\FileFieldWidgetTest::testMultiValuedWidget()
  6. 10 core/modules/file/tests/src/Functional/FileFieldWidgetTest.php \Drupal\Tests\file\Functional\FileFieldWidgetTest::testMultiValuedWidget()

Tests upload and remove buttons for multiple multi-valued File fields.

File

core/modules/file/tests/src/Functional/FileFieldWidgetTest.php, line 123

Class

FileFieldWidgetTest
Tests the file field widget with public and private files.

Namespace

Drupal\Tests\file\Functional

Code

public function testMultiValuedWidget() : void {
    $node_storage = $this->container
        ->get('entity_type.manager')
        ->getStorage('node');
    $type_name = 'article';
    // Use explicit names instead of random names for those fields, because of a
    // bug in submitForm() with multiple file uploads in one form, where the
    // order of uploads depends on the order in which the upload elements are
    // added to the $form (which, in the current implementation of
    // FileStorage::listAll(), comes down to the alphabetical order on field
    // names).
    $field_name = 'test_file_field_1';
    $field_name2 = 'test_file_field_2';
    $cardinality = 3;
    $this->createFileField($field_name, 'node', $type_name, [
        'cardinality' => $cardinality,
    ]);
    $this->createFileField($field_name2, 'node', $type_name, [
        'cardinality' => $cardinality,
    ]);
    $test_file = $this->getTestFile('text');
    // Visit the node creation form, and upload 3 files for each field. Since
    // the field has cardinality of 3, ensure the "Upload" button is displayed
    // until after the 3rd file, and after that, isn't displayed. Because
    // the last button with a given name is triggered by default, upload to the
    // second field first.
    $this->drupalGet("node/add/{$type_name}");
    foreach ([
        $field_name2,
        $field_name,
    ] as $each_field_name) {
        for ($delta = 0; $delta < 3; $delta++) {
            $edit = [
                'files[' . $each_field_name . '_' . $delta . '][]' => \Drupal::service('file_system')->realpath($test_file->getFileUri()),
            ];
            // If the Upload button doesn't exist, submitForm() will
            // automatically fail with an assertion message.
            $this->submitForm($edit, 'Upload');
        }
    }
    $this->assertSession()
        ->buttonNotExists('Upload');
    $num_expected_remove_buttons = 6;
    foreach ([
        $field_name,
        $field_name2,
    ] as $current_field_name) {
        // How many uploaded files for the current field are remaining.
        $remaining = 3;
        // Test clicking each "Remove" button. For extra robustness, test them out
        // of sequential order. They are 0-indexed, and get renumbered after each
        // iteration, so array(1, 1, 0) means:
        // - First remove the 2nd file.
        // - Then remove what is then the 2nd file (was originally the 3rd file).
        // - Then remove the first file.
        foreach ([
            1,
            1,
            0,
        ] as $delta) {
            // Ensure we have the expected number of Remove buttons, and that they
            // are numbered sequentially.
            $buttons = $this->xpath('//input[@type="submit" and @value="Remove"]');
            $this->assertCount($num_expected_remove_buttons, $buttons, "There are {$num_expected_remove_buttons} \"Remove\" buttons displayed.");
            foreach ($buttons as $i => $button) {
                $key = $i >= $remaining ? $i - $remaining : $i;
                $check_field_name = $field_name2;
                if ($current_field_name == $field_name && $i < $remaining) {
                    $check_field_name = $field_name;
                }
                $this->assertSame($check_field_name . '_' . $key . '_remove_button', $button->getAttribute('name'));
            }
            // "Click" the remove button (emulating either a nojs or js submission).
            $button_name = $current_field_name . '_' . $delta . '_remove_button';
            $this->getSession()
                ->getPage()
                ->findButton($button_name)
                ->press();
            $num_expected_remove_buttons--;
            $remaining--;
            // Ensure an "Upload" button for the current field is displayed with the
            // correct name.
            $upload_button_name = $current_field_name . '_' . $remaining . '_upload_button';
            $button = $this->assertSession()
                ->buttonExists($upload_button_name);
            $this->assertSame('Upload', $button->getValue());
            // Ensure only at most one button per field is displayed.
            $expected = $current_field_name == $field_name ? 1 : 2;
            $this->assertSession()
                ->elementsCount('xpath', '//input[@type="submit" and @value="Upload"]', $expected);
        }
    }
    // Ensure the page now has no Remove buttons.
    $this->assertSession()
        ->buttonNotExists('Remove');
    // Save the node and ensure it does not have any files.
    $this->submitForm([
        'title[0][value]' => $this->randomMachineName(),
    ], 'Save');
    preg_match('/node\\/([0-9])/', $this->getUrl(), $matches);
    $nid = $matches[1];
    $node = $node_storage->loadUnchanged($nid);
    $this->assertEmpty($node->{$field_name}->target_id, 'Node was successfully saved without any files.');
    // Try to upload more files than allowed on revision.
    $upload_files_node_revision = [
        $test_file,
        $test_file,
        $test_file,
        $test_file,
    ];
    foreach ($upload_files_node_revision as $i => $file) {
        $edit['files[test_file_field_1_0][' . $i . ']'] = \Drupal::service('file_system')->realpath($test_file->getFileUri());
    }
    // @todo Replace after https://www.drupal.org/project/drupal/issues/2917885
    $this->drupalGet('node/' . $node->id() . '/edit');
    $this->assertSession()
        ->fieldExists('files[test_file_field_1_0][]');
    $submit_xpath = $this->assertSession()
        ->buttonExists('Save')
        ->getXpath();
    $client = $this->getSession()
        ->getDriver()
        ->getClient();
    $form = $client->getCrawler()
        ->filterXPath($submit_xpath)
        ->form();
    $client->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $edit);
    $node = $node_storage->loadUnchanged($nid);
    $this->assertCount($cardinality, $node->{$field_name}, 'More files than allowed could not be saved to node.');
    $upload_files_node_creation = [
        $test_file,
        $test_file,
    ];
    // Try to upload multiple files, but fewer than the maximum.
    $nid = $this->uploadNodeFiles($upload_files_node_creation, $field_name, $type_name, TRUE, []);
    $node = $node_storage->loadUnchanged($nid);
    $this->assertSameSize($upload_files_node_creation, $node->{$field_name}, 'Node was successfully saved with multiple files.');
    // Try to upload exactly the allowed number of files on revision.
    $this->uploadNodeFile($test_file, $field_name, $node->id(), 1);
    $node = $node_storage->loadUnchanged($nid);
    $this->assertCount($cardinality, $node->{$field_name}, 'Node was successfully revised to maximum number of files.');
    // Try to upload exactly the allowed number of files, new node.
    $upload_files = [
        $test_file,
        $test_file,
        $test_file,
    ];
    $nid = $this->uploadNodeFiles($upload_files, $field_name, $type_name, TRUE, []);
    $node = $node_storage->loadUnchanged($nid);
    $this->assertCount($cardinality, $node->{$field_name}, 'Node was successfully saved with maximum number of files.');
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.