diff --git a/core/modules/field/src/Tests/FieldAssertTrait.php b/core/modules/field/src/Tests/FieldAssertTrait.php
new file mode 100644
index 0000000..7889a41
--- /dev/null
+++ b/core/modules/field/src/Tests/FieldAssertTrait.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Drupal\field\Tests;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\LanguageInterface;
+
+trait FieldAssertTrait {
+
+  /**
+   * Generate random values for a field_test field.
+   *
+   * @param $cardinality
+   *   Number of values to generate.
+   * @return
+   *   An array of random values, in the format expected for field values.
+   */
+  function _generateTestFieldValues($cardinality) {
+    $values = array();
+    for ($i = 0; $i < $cardinality; $i++) {
+      // field_test fields treat 0 as 'empty value'.
+      $values[$i]['value'] = mt_rand(1, 127);
+    }
+    return $values;
+  }
+
+  /**
+   * Assert that a field has the expected values in an entity.
+   *
+   * This function only checks a single column in the field values.
+   *
+   * @param EntityInterface $entity
+   *   The entity to test.
+   * @param $field_name
+   *   The name of the field to test
+   * @param $expected_values
+   *   The array of expected values.
+   * @param $langcode
+   *   (Optional) The language code for the values. Defaults to
+   *   \Drupal\Core\Language\LanguageInterface::LANGCODE_DEFAULT.
+   * @param $column
+   *   (Optional) The name of the column to check. Defaults to 'value'.
+   */
+  function assertFieldValues(EntityInterface $entity, $field_name, $expected_values, $langcode = LanguageInterface::LANGCODE_DEFAULT, $column = 'value') {
+    // Re-load the entity to make sure we have the latest changes.
+    $storage = $this->container->get('entity_type.manager')
+      ->getStorage($entity->getEntityTypeId());
+    $storage->resetCache([$entity->id()]);
+    $e = $storage->load($entity->id());
+
+    $field = $values = $e->getTranslation($langcode)->$field_name;
+    // Filter out empty values so that they don't mess with the assertions.
+    $field->filterEmptyItems();
+    $values = $field->getValue();
+    $this->assertEqual(count($values), count($expected_values), 'Expected number of values were saved.');
+    foreach ($expected_values as $key => $value) {
+      $this->assertEqual($values[$key][$column], $value, format_string('Value @value was saved correctly.', array('@value' => $value)));
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/core/modules/field/src/Tests/NestedFormTest.php b/core/modules/field/src/Tests/NestedFormTest.php
index 8b3402d..78c68f6 100644
--- a/core/modules/field/src/Tests/NestedFormTest.php
+++ b/core/modules/field/src/Tests/NestedFormTest.php
@@ -50,122 +50,6 @@ protected function setUp() {
   }
 
   /**
-   * Tests Field API form integration within a subform.
-   */
-  function testNestedFieldForm() {
-    // Add two fields on the 'entity_test'
-    FieldStorageConfig::create($this->fieldStorageSingle)->save();
-    FieldStorageConfig::create($this->fieldStorageUnlimited)->save();
-    $this->field['field_name'] = 'field_single';
-    $this->field['label'] = 'Single field';
-    FieldConfig::create($this->field)->save();
-    entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
-      ->setComponent($this->field['field_name'])
-      ->save();
-    $this->field['field_name'] = 'field_unlimited';
-    $this->field['label'] = 'Unlimited field';
-    FieldConfig::create($this->field)->save();
-    entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
-      ->setComponent($this->field['field_name'])
-      ->save();
-
-    // Create two entities.
-    $entity_type = 'entity_test';
-    $storage = $this->container->get('entity_type.manager')
-      ->getStorage($entity_type);
-
-    $entity_1 = $storage->create(['id' => 1]);
-    $entity_1->enforceIsNew();
-    $entity_1->field_single->value = 0;
-    $entity_1->field_unlimited->value = 1;
-    $entity_1->save();
-
-    $entity_2 = $storage->create(array('id' => 2));
-    $entity_2->enforceIsNew();
-    $entity_2->field_single->value = 10;
-    $entity_2->field_unlimited->value = 11;
-    $entity_2->save();
-
-    // Display the 'combined form'.
-    $this->drupalGet('test-entity/nested/1/2');
-    $this->assertFieldByName('field_single[0][value]', 0, 'Entity 1: field_single value appears correctly is the form.');
-    $this->assertFieldByName('field_unlimited[0][value]', 1, 'Entity 1: field_unlimited value 0 appears correctly is the form.');
-    $this->assertFieldByName('entity_2[field_single][0][value]', 10, 'Entity 2: field_single value appears correctly is the form.');
-    $this->assertFieldByName('entity_2[field_unlimited][0][value]', 11, 'Entity 2: field_unlimited value 0 appears correctly is the form.');
-
-    // Submit the form and check that the entities are updated accordingly.
-    $edit = array(
-      'field_single[0][value]' => 1,
-      'field_unlimited[0][value]' => 2,
-      'field_unlimited[1][value]' => 3,
-      'entity_2[field_single][0][value]' => 11,
-      'entity_2[field_unlimited][0][value]' => 12,
-      'entity_2[field_unlimited][1][value]' => 13,
-    );
-    $this->drupalPostForm(NULL, $edit, t('Save'));
-    $entity_1 = $storage->load(1);
-    $entity_2 = $storage->load(2);
-    $this->assertFieldValues($entity_1, 'field_single', array(1));
-    $this->assertFieldValues($entity_1, 'field_unlimited', array(2, 3));
-    $this->assertFieldValues($entity_2, 'field_single', array(11));
-    $this->assertFieldValues($entity_2, 'field_unlimited', array(12, 13));
-
-    // Submit invalid values and check that errors are reported on the
-    // correct widgets.
-    $edit = array(
-      'field_unlimited[1][value]' => -1,
-    );
-    $this->drupalPostForm('test-entity/nested/1/2', $edit, t('Save'));
-    $this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), 'Entity 1: the field validation error was reported.');
-    $error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-field-unlimited-1-value'));
-    $this->assertTrue($error_field, 'Entity 1: the error was flagged on the correct element.');
-    $edit = array(
-      'entity_2[field_unlimited][1][value]' => -1,
-    );
-    $this->drupalPostForm('test-entity/nested/1/2', $edit, t('Save'));
-    $this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), 'Entity 2: the field validation error was reported.');
-    $error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-entity-2-field-unlimited-1-value'));
-    $this->assertTrue($error_field, 'Entity 2: the error was flagged on the correct element.');
-
-    // Test that reordering works on both entities.
-    $edit = array(
-      'field_unlimited[0][_weight]' => 0,
-      'field_unlimited[1][_weight]' => -1,
-      'entity_2[field_unlimited][0][_weight]' => 0,
-      'entity_2[field_unlimited][1][_weight]' => -1,
-    );
-    $this->drupalPostForm('test-entity/nested/1/2', $edit, t('Save'));
-    $this->assertFieldValues($entity_1, 'field_unlimited', array(3, 2));
-    $this->assertFieldValues($entity_2, 'field_unlimited', array(13, 12));
-
-    // Test the 'add more' buttons. Only Ajax submission is tested, because
-    // the two 'add more' buttons present in the form have the same #value,
-    // which confuses drupalPostForm().
-    // 'Add more' button in the first entity:
-    $this->drupalGet('test-entity/nested/1/2');
-    $this->drupalPostAjaxForm(NULL, array(), 'field_unlimited_add_more');
-    $this->assertFieldByName('field_unlimited[0][value]', 3, 'Entity 1: field_unlimited value 0 appears correctly is the form.');
-    $this->assertFieldByName('field_unlimited[1][value]', 2, 'Entity 1: field_unlimited value 1 appears correctly is the form.');
-    $this->assertFieldByName('field_unlimited[2][value]', '', 'Entity 1: field_unlimited value 2 appears correctly is the form.');
-    $this->assertFieldByName('field_unlimited[3][value]', '', 'Entity 1: an empty widget was added for field_unlimited value 3.');
-    // 'Add more' button in the first entity (changing field values):
-    $edit = array(
-      'entity_2[field_unlimited][0][value]' => 13,
-      'entity_2[field_unlimited][1][value]' => 14,
-      'entity_2[field_unlimited][2][value]' => 15,
-    );
-    $this->drupalPostAjaxForm(NULL, $edit, 'entity_2_field_unlimited_add_more');
-    $this->assertFieldByName('entity_2[field_unlimited][0][value]', 13, 'Entity 2: field_unlimited value 0 appears correctly is the form.');
-    $this->assertFieldByName('entity_2[field_unlimited][1][value]', 14, 'Entity 2: field_unlimited value 1 appears correctly is the form.');
-    $this->assertFieldByName('entity_2[field_unlimited][2][value]', 15, 'Entity 2: field_unlimited value 2 appears correctly is the form.');
-    $this->assertFieldByName('entity_2[field_unlimited][3][value]', '', 'Entity 2: an empty widget was added for field_unlimited value 3.');
-    // Save the form and check values are saved correctly.
-    $this->drupalPostForm(NULL, array(), t('Save'));
-    $this->assertFieldValues($entity_1, 'field_unlimited', array(3, 2));
-    $this->assertFieldValues($entity_2, 'field_unlimited', array(13, 14, 15));
-  }
-
-  /**
    * Tests entity level validation within subforms.
    */
   public function testNestedEntityFormEntityLevelValidation() {
diff --git a/core/modules/field/tests/src/FunctionalJavascript/NestedFormTest.php b/core/modules/field/tests/src/FunctionalJavascript/NestedFormTest.php
new file mode 100644
index 0000000..3f217c5
--- /dev/null
+++ b/core/modules/field/tests/src/FunctionalJavascript/NestedFormTest.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace Drupal\Tests\field\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\field\Tests\FieldAssertTrait;
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+
+/**
+ * Tests the JavaScript library caching on consecutive requests.
+ *
+ * @group views_ui
+ */
+class NestedFormTest extends JavascriptTestBase {
+
+  use FieldAssertTrait;
+
+  /**
+   * An array of values defining a field with unlimited cardinality.
+   *
+   * @var array
+   */
+  protected $fieldStorageUnlimited;
+
+  /**
+   * An array of values defining a field.
+   *
+   * @var array
+   */
+  protected $field;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['field_test', 'entity_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $account = $this->drupalCreateUser([
+      'view test entity',
+      'administer entity_test content'
+    ]);
+    $this->drupalLogin($account);
+
+    $this->fieldStorageSingle = array(
+      'field_name' => 'field_single',
+      'entity_type' => 'entity_test',
+      'type' => 'test_field',
+    );
+    $this->fieldStorageUnlimited = array(
+      'field_name' => 'field_unlimited',
+      'entity_type' => 'entity_test',
+      'type' => 'test_field',
+      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+    );
+
+    $this->field = array(
+      'entity_type' => 'entity_test',
+      'bundle' => 'entity_test',
+      'label' => $this->randomMachineName() . '_label',
+      'description' => '[site:name]_description',
+      'weight' => mt_rand(0, 127),
+      'settings' => array(
+        'test_field_setting' => $this->randomMachineName(),
+      ),
+    );
+
+    // Add two fields on the 'entity_test'
+    FieldStorageConfig::create($this->fieldStorageSingle)->save();
+    FieldStorageConfig::create($this->fieldStorageUnlimited)->save();
+    $this->field['field_name'] = 'field_single';
+    $this->field['label'] = 'Single field';
+    FieldConfig::create($this->field)->save();
+    $entity_form_display = EntityFormDisplay::load($this->field['entity_type'] . '.' . $this->field['bundle'] . '.' . 'default');
+    $entity_form_display->setComponent($this->field['field_name'])
+      ->save();
+    $this->field['field_name'] = 'field_unlimited';
+    $this->field['label'] = 'Unlimited field';
+    FieldConfig::create($this->field)->save();
+    $entity_form_display = EntityFormDisplay::load($this->field['entity_type'] . '.' . $this->field['bundle'] . '.' . 'default');
+    $entity_form_display->setComponent($this->field['field_name'])
+      ->save();
+  }
+
+  /**
+   * Tests Field API form integration within a subform.
+   */
+  function testNestedFieldForm() {
+
+    // Create two entities.
+    $entity_type = 'entity_test';
+    $storage = $this->container->get('entity_type.manager')
+      ->getStorage($entity_type);
+
+    $entity_1 = $storage->create(['id' => 1]);
+    $entity_1->enforceIsNew();
+    $entity_1->field_single->value = 0;
+    $entity_1->field_unlimited->value = 1;
+    $entity_1->save();
+
+    $entity_2 = $storage->create(array('id' => 2));
+    $entity_2->enforceIsNew();
+    $entity_2->field_single->value = 10;
+    $entity_2->field_unlimited->value = 11;
+    $entity_2->save();
+
+    // Display the 'combined form'.
+    $this->drupalGet('test-entity/nested/1/2');
+
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+
+    // Find the fields in the page/
+    $field_single = $page->findField('field_single[0][value]');
+    $field_unlimited_0 = $page->findField('field_unlimited[0][value]');
+    $field_unlimited_1 = $page->findField('field_unlimited[1][value]');
+    $field_entity_2_single = $page->findField('entity_2[field_single][0][value]');
+    $field_entity_2_unlimited_0 = $page->findField('entity_2[field_unlimited][0][value]');
+    $field_entity_2_unlimited_1 = $page->findField('entity_2[field_unlimited][1][value]');
+
+    // Validate the fields are present.
+    $this->assertNotEmpty($field_single, 'Entity 1: field_single value appears correctly in the form.');
+    $this->assertNotEmpty($field_unlimited_0, 'Entity 1: field_unlimited value appears correctly in the form.');
+    $this->assertNotEmpty($field_entity_2_single, 'Entity 2: field_single value appears correctly in the form.');
+    $this->assertNotEmpty($field_entity_2_unlimited_0, 'Entity 2: field_unlimited value appears correctly in the form.');
+
+    // Set the values for the fields.
+    $field_single->setValue('1');
+    $field_unlimited_0->setValue('2');
+    $field_unlimited_1->setValue('3');
+    $field_entity_2_single->setValue('11');
+    $field_entity_2_unlimited_0->setValue('12');
+    $field_entity_2_unlimited_1->setValue('13');
+
+    // Submit the page.
+    $page->findButton('Save')->click();
+
+    // Validate the values in the entities are as expected.
+    $entity_1 = $storage->load(1);
+    $entity_2 = $storage->load(2);
+    $this->assertFieldValues($entity_1, 'field_single', array(1));
+    $this->assertFieldValues($entity_1, 'field_unlimited', array(2, 3));
+    $this->assertFieldValues($entity_2, 'field_single', array(11));
+    $this->assertFieldValues($entity_2, 'field_unlimited', array(12, 13));
+
+    // Submit invalid values and check that errors are reported on the
+    // correct widgets.
+    $field_unlimited_1->setValue('-1');
+
+    $page->findButton('Save')->click();
+    $assert_session->responseContains(t('%label does not accept the value -1', array('%label' => 'Unlimited field')));
+    $this->assertTrue($field_unlimited_1->hasClass('error'), 'Entity 1: the error was flagged on the correct element.');
+
+    $field_entity_2_unlimited_1->setValue('-1');
+
+    $page->findButton('Save')->click();
+
+    $assert_session->responseContains(t('%label does not accept the value -1', array('%label' => 'Unlimited field')));
+    $this->assertTrue($field_entity_2_unlimited_1->hasClass('error'), 'Entity 2: the error was flagged on the correct element.');
+  }
+
+}
