diff --git a/core/modules/system/src/Tests/Ajax/MultiFormTest.php b/core/modules/system/src/Tests/Ajax/MultiFormTest.php
deleted file mode 100644
index 96844ed051a1bdd4c4cdd1b06c278173f127983d..0000000000000000000000000000000000000000
--- a/core/modules/system/src/Tests/Ajax/MultiFormTest.php
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php
-
-namespace Drupal\system\Tests\Ajax;
-
-use Drupal\Core\Field\FieldStorageDefinitionInterface;
-use Drupal\field\Entity\FieldConfig;
-use Drupal\field\Entity\FieldStorageConfig;
-
-/**
- * Tests that AJAX-enabled forms work when multiple instances of the same form
- * are on a page.
- *
- * @group Ajax
- */
-class MultiFormTest extends AjaxTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = ['form_test'];
-
-  protected function setUp() {
-    parent::setUp();
-
-    $this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']);
-
-    // Create a multi-valued field for 'page' nodes to use for Ajax testing.
-    $field_name = 'field_ajax_test';
-    FieldStorageConfig::create([
-      'entity_type' => 'node',
-      'field_name' => $field_name,
-      'type' => 'text',
-      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
-    ])->save();
-    FieldConfig::create([
-      'field_name' => $field_name,
-      'entity_type' => 'node',
-      'bundle' => 'page',
-    ])->save();
-    entity_get_form_display('node', 'page', 'default')
-      ->setComponent($field_name, ['type' => 'text_textfield'])
-      ->save();
-
-    // Log in a user who can create 'page' nodes.
-    $this->drupalLogin($this->drupalCreateUser(['create page content']));
-  }
-
-  /**
-   * Tests that pages with the 'node_page_form' included twice work correctly.
-   */
-  public function testMultiForm() {
-    // HTML IDs for elements within the field are potentially modified with
-    // each Ajax submission, but these variables are stable and help target the
-    // desired elements.
-    $field_name = 'field_ajax_test';
-
-    $form_xpath = '//form[starts-with(@id, "node-page-form")]';
-    $field_xpath = '//div[contains(@class, "field--name-field-ajax-test")]';
-    $button_name = $field_name . '_add_more';
-    $button_value = t('Add another item');
-    $button_xpath_suffix = '//input[@name="' . $button_name . '"]';
-    $field_items_xpath_suffix = '//input[@type="text"]';
-
-    // Ensure the initial page contains both node forms and the correct number
-    // of field items and "add more" button for the multi-valued field within
-    // each form.
-    $this->drupalGet('form-test/two-instances-of-same-form');
-
-    $fields = $this->xpath($form_xpath . $field_xpath);
-    $this->assertEqual(count($fields), 2);
-    foreach ($fields as $field) {
-      $this->assertEqual(count($field->xpath('.' . $field_items_xpath_suffix)), 1, 'Found the correct number of field items on the initial page.');
-      $this->assertFieldsByValue($field->xpath('.' . $button_xpath_suffix), NULL, 'Found the "add more" button on the initial page.');
-    }
-
-    $this->assertNoDuplicateIds(t('Initial page contains unique IDs'), 'Other');
-
-    // Submit the "add more" button of each form twice. After each corresponding
-    // page update, ensure the same as above.
-
-    for ($i = 0; $i < 2; $i++) {
-      $forms = $this->xpath($form_xpath);
-      foreach ($forms as $offset => $form) {
-        $form_html_id = (string) $form['id'];
-        $this->drupalPostAjaxForm(NULL, [], [$button_name => $button_value], NULL, [], [], $form_html_id);
-        $form = $this->xpath($form_xpath)[$offset];
-        $field = $form->xpath('.' . $field_xpath);
-
-        $this->assertEqual(count($field[0]->xpath('.' . $field_items_xpath_suffix)), $i + 2, 'Found the correct number of field items after an AJAX submission.');
-        $this->assertFieldsByValue($field[0]->xpath('.' . $button_xpath_suffix), NULL, 'Found the "add more" button after an AJAX submission.');
-        $this->assertNoDuplicateIds(t('Updated page contains unique IDs'), 'Other');
-      }
-    }
-  }
-
-}
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/MultiFormTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/MultiFormTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..fe3fcf2f5084bd3ec01ed0efb9228ff7522bf78a
--- /dev/null
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/MultiFormTest.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace Drupal\FunctionalJavascriptTests\Ajax;
+
+use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+
+/**
+ * Tests that AJAX-enabled forms work when multiple instances of the same form
+ * are on a page.
+ *
+ * @group Ajax
+ */
+class MultiFormTest extends WebDriverTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['node', 'form_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']);
+
+    // Create a multi-valued field for 'page' nodes to use for Ajax testing.
+    $field_name = 'field_ajax_test';
+    FieldStorageConfig::create([
+      'entity_type' => 'node',
+      'field_name' => $field_name,
+      'type' => 'text',
+      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+    ])->save();
+    FieldConfig::create([
+      'field_name' => $field_name,
+      'entity_type' => 'node',
+      'bundle' => 'page',
+    ])->save();
+    entity_get_form_display('node', 'page', 'default')
+      ->setComponent($field_name, ['type' => 'text_textfield'])
+      ->save();
+
+    // Log in a user who can create 'page' nodes.
+    $this->drupalLogin($this->drupalCreateUser(['create page content']));
+  }
+
+  /**
+   * Tests that pages with the 'node_page_form' included twice work correctly.
+   */
+  public function testMultiForm() {
+    // HTML IDs for elements within the field are potentially modified with
+    // each Ajax submission, but these variables are stable and help target the
+    // desired elements.
+    $field_name = 'field_ajax_test';
+
+    $form_xpath = '//form[starts-with(@id, "node-page-form")]';
+    $field_xpath = '//div[contains(@class, "field--name-field-ajax-test")]';
+    $button_name = $field_name . '_add_more';
+    $button_value = t('Add another item');
+    $button_xpath_suffix = '//input[@name="' . $button_name . '"]';
+    $field_items_xpath_suffix = '//input[@type="text"]';
+
+    // Ensure the initial page contains both node forms and the correct number
+    // of field items and "add more" button for the multi-valued field within
+    // each form.
+    $this->drupalGet('form-test/two-instances-of-same-form');
+
+    // Wait for javascript on the page to prepare the form attributes.
+    $this->assertSession()->assertWaitOnAjaxRequest();
+
+    $session = $this->getSession();
+    $page = $session->getPage();
+    $fields = $page->findAll('xpath', $form_xpath . $field_xpath);
+    $this->assertEqual(count($fields), 2);
+    foreach ($fields as $field) {
+      $this->assertEqual(count($field->find('xpath', '.' . $field_items_xpath_suffix)), 1, 'Found the correct number of field items on the initial page.');
+      $this->assertFieldsByValue($field->find('xpath', '.' . $button_xpath_suffix), NULL, 'Found the "add more" button on the initial page.');
+    }
+
+    $this->assertNoDuplicateIds();
+
+    // Submit the "add more" button of each form twice. After each corresponding
+    // page update, ensure the same as above.
+
+    for ($i = 0; $i < 2; $i++) {
+      $forms = $page->find('xpath', $form_xpath);
+      foreach ($forms as $offset => $form) {
+        $button = $form->findButton($button_value);
+        $this->assertNotNull($button, 'Add Another Item button exists');
+        $button->press();
+
+        // Wait for page update.
+        $this->assertSession()->assertWaitOnAjaxRequest();
+
+        // After AJAX request and response page will update.
+        $page_updated = $session->getPage();
+        $field = $page_updated->findAll('xpath', '.' . $field_xpath);
+        $this->assertEqual(count($field[0]->find('xpath', '.' . $field_items_xpath_suffix)), $i + 2, 'Found the correct number of field items after an AJAX submission.');
+        $this->assertFieldsByValue($field[0]->find('xpath', '.' . $button_xpath_suffix), NULL, 'Found the "add more" button after an AJAX submission.');
+        $this->assertNoDuplicateIds();
+      }
+    }
+  }
+
+  /**
+   * Asserts that each HTML ID is used for just a single element on the page.
+   *
+   * @param string $message
+   *   (optional) A message to display with the assertion.
+   */
+  protected function assertNoDuplicateIds($message = '') {
+    $args = ['@url' => $this->getUrl()];
+
+    if (!$elements = $this->xpath('//*[@id]')) {
+      $this->fail(new FormattableMarkup('The page @url contains no HTML IDs.', $args));
+      return;
+    }
+
+    $message = $message ?: new FormattableMarkup('The page @url does not contain duplicate HTML IDs', $args);
+
+    $seen_ids = [];
+    foreach ($elements as $element) {
+      $id = $element->getAttribute('id');
+      if (isset($seen_ids[$id])) {
+        $this->fail($message);
+        return;
+      }
+      $seen_ids[$id] = TRUE;
+    }
+    $this->assertTrue(TRUE, $message);
+  }
+
+}