You are here

public function FileFieldWidgetTest::testMultiValuedWidget in Drupal 9

Same name in this branch
  1. 9 core/modules/file/tests/src/Functional/FileFieldWidgetTest.php \Drupal\Tests\file\Functional\FileFieldWidgetTest::testMultiValuedWidget()
  2. 9 core/modules/file/tests/src/FunctionalJavascript/FileFieldWidgetTest.php \Drupal\Tests\file\FunctionalJavascript\FileFieldWidgetTest::testMultiValuedWidget()
Same name and namespace in other branches
  1. 8 core/modules/file/tests/src/Functional/FileFieldWidgetTest.php \Drupal\Tests\file\Functional\FileFieldWidgetTest::testMultiValuedWidget()
  2. 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 120

Class

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

Namespace

Drupal\Tests\file\Functional

Code

public function testMultiValuedWidget() {
  $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
  // SimpleTest triggers the last button with a given name, so 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, new FormattableMarkup('There are %n "Remove" buttons displayed.', [
        '%n' => $num_expected_remove_buttons,
      ]));
      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
    ->assertTrue(empty($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, [], TRUE);
  $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.');
}