diff --git a/core/modules/field_ui/tests/src/Functional/FieldUiTestTrait.php b/core/modules/field_ui/tests/src/Functional/FieldUiTestTrait.php
new file mode 100644
index 0000000..c426add
--- /dev/null
+++ b/core/modules/field_ui/tests/src/Functional/FieldUiTestTrait.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace Drupal\Tests\field_ui\Functional;
+
+/**
+ * Provides common functionality for the Field UI test classes.
+ */
+trait FieldUiTestTrait {
+
+  /**
+   * Creates a new field through the Field UI.
+   *
+   * @param string $bundle_path
+   *   Admin path of the bundle that the new field is to be attached to.
+   * @param string $field_name
+   *   The field name of the new field storage.
+   * @param string $label
+   *   (optional) The label of the new field. Defaults to a random string.
+   * @param string $field_type
+   *   (optional) The field type of the new field storage. Defaults to
+   *   'test_field'.
+   * @param array $storage_edit
+   *   (optional) $edit parameter for drupalPostForm() on the second step
+   *   ('Storage settings' form).
+   * @param array $field_edit
+   *   (optional) $edit parameter for drupalPostForm() on the third step ('Field
+   *   settings' form).
+   */
+  public function fieldUIAddNewField($bundle_path, $field_name, $label = NULL, $field_type = 'test_field', array $storage_edit = array(), array $field_edit = array()) {
+    $label = $label ?: $this->randomString();
+    $initial_edit = array(
+      'new_storage_type' => $field_type,
+      'label' => $label,
+      'field_name' => $field_name,
+    );
+
+    // Allow the caller to set a NULL path in case they navigated to the right
+    // page before calling this method.
+    if ($bundle_path !== NULL) {
+      $bundle_path = "$bundle_path/fields/add-field";
+    }
+
+    // First step: 'Add field' page.
+    $this->drupalPostForm($bundle_path, $initial_edit, t('Save and continue'));
+    $this->assertRaw(t('These settings apply to the %label field everywhere it is used.', array('%label' => $label)), 'Storage settings page was displayed.');
+    // Test Breadcrumbs.
+    $this->assertLink($label, 0, 'Field label is correct in the breadcrumb of the storage settings page.');
+
+    // Second step: 'Storage settings' form.
+    $this->drupalPostForm(NULL, $storage_edit, t('Save field settings'));
+    $this->assertRaw(t('Updated field %label field settings.', array('%label' => $label)), 'Redirected to field settings page.');
+
+    // Third step: 'Field settings' form.
+    $this->drupalPostForm(NULL, $field_edit, t('Save settings'));
+    $this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), 'Redirected to "Manage fields" page.');
+
+    // Check that the field appears in the overview form.
+    $this->assertSession()->elementExists('xpath', '//table[@id="field-overview"]//tr/td[1][text() = "' . $label . '"]');
+  }
+
+  /**
+   * Adds an existing field through the Field UI.
+   *
+   * @param string $bundle_path
+   *   Admin path of the bundle that the field is to be attached to.
+   * @param string $existing_storage_name
+   *   The name of the existing field storage for which we want to add a new
+   *   field.
+   * @param string $label
+   *   (optional) The label of the new field. Defaults to a random string.
+   * @param array $field_edit
+   *   (optional) $edit parameter for drupalPostForm() on the second step
+   *   ('Field settings' form).
+   */
+  public function fieldUIAddExistingField($bundle_path, $existing_storage_name, $label = NULL, array $field_edit = array()) {
+    $label = $label ?: $this->randomString();
+    $initial_edit = array(
+      'existing_storage_name' => $existing_storage_name,
+      'existing_storage_label' => $label,
+    );
+
+    // First step: 'Re-use existing field' on the 'Add field' page.
+    $this->drupalPostForm("$bundle_path/fields/add-field", $initial_edit, t('Save and continue'));
+    // Set the main content to only the content region because the label can
+    // contain HTML which will be auto-escaped by Twig.
+    $main_content = $this->cssSelect('.region-content');
+    $this->setRawContent(reset($main_content)->asXml());
+    $this->assertRaw('field-config-edit-form', 'The field config edit form is present.');
+    $this->assertNoRaw('&amp;lt;', 'The page does not have double escaped HTML tags.');
+
+    // Second step: 'Field settings' form.
+    $this->drupalPostForm(NULL, $field_edit, t('Save settings'));
+    $this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), 'Redirected to "Manage fields" page.');
+
+    // Check that the field appears in the overview form.
+    $this->assertSession()->elementExists('xpath', '//table[@id="field-overview"]//tr/td[1][text() = "' . $label . '"]');
+  }
+
+  /**
+   * Deletes a field through the Field UI.
+   *
+   * @param string $bundle_path
+   *   Admin path of the bundle that the field is to be deleted from.
+   * @param string $field_name
+   *   The name of the field.
+   * @param string $label
+   *   The label of the field.
+   * @param string $bundle_label
+   *   The label of the bundle.
+   */
+  public function fieldUIDeleteField($bundle_path, $field_name, $label, $bundle_label) {
+    // Display confirmation form.
+    $this->drupalGet("$bundle_path/fields/$field_name/delete");
+    $this->assertRaw(t('Are you sure you want to delete the field %label', array('%label' => $label)), 'Delete confirmation was found.');
+
+    // Test Breadcrumbs.
+    $this->assertLink($label, 0, 'Field label is correct in the breadcrumb of the field delete page.');
+
+    // Submit confirmation form.
+    $this->drupalPostForm(NULL, array(), t('Delete'));
+    $this->assertRaw(t('The field %label has been deleted from the %type content type.', array('%label' => $label, '%type' => $bundle_label)), 'Delete message was found.');
+
+    // Check that the field does not appear in the overview form.
+    $this->assertSession()->elementNotExists('xpath', '//table[@id="field-overview"]//tr/td[1][text() = "' . $label . '"]');
+  }
+
+}
diff --git a/core/modules/link/src/Tests/LinkFieldTest.php b/core/modules/link/tests/src/Functional/LinkFieldTest.php
similarity index 82%
rename from core/modules/link/src/Tests/LinkFieldTest.php
rename to core/modules/link/tests/src/Functional/LinkFieldTest.php
index e85ad53..acad40c 100644
--- a/core/modules/link/src/Tests/LinkFieldTest.php
+++ b/core/modules/link/tests/src/Functional/LinkFieldTest.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\link\Tests;
+namespace Drupal\Tests\link\Functional;
 
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\Unicode;
@@ -8,7 +8,7 @@
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\link\LinkItemInterface;
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 use Drupal\field\Entity\FieldStorageConfig;
 
 /**
@@ -16,7 +16,7 @@
  *
  * @group link
  */
-class LinkFieldTest extends WebTestBase {
+class LinkFieldTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
@@ -195,7 +195,7 @@ protected function assertValidEntries($field_name, array $valid_entries) {
         "{$field_name}[0][uri]" => $uri,
       );
       $this->drupalPostForm('entity_test/add', $edit, t('Save'));
-      preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
+      preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
       $id = $match[1];
       $this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
       $this->assertRaw($string);
@@ -313,13 +313,13 @@ function testLinkTitle() {
       "{$field_name}[0][title]" => '',
     );
     $this->drupalPostForm(NULL, $edit, t('Save'));
-    preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
+    preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
     $id = $match[1];
     $this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
 
-    $this->renderTestEntity($id);
-    $expected_link = \Drupal::l($value, Url::fromUri($value));
-    $this->assertRaw($expected_link);
+    $output = $this->renderTestEntity($id);
+    $expected_link = (string) \Drupal::l($value, Url::fromUri($value));
+    $this->assertContains($expected_link, $output);
 
     // Verify that a link with text is rendered using the link text.
     $title = $this->randomMachineName();
@@ -329,9 +329,9 @@ function testLinkTitle() {
     $this->drupalPostForm("entity_test/manage/$id/edit", $edit, t('Save'));
     $this->assertText(t('entity_test @id has been updated.', array('@id' => $id)));
 
-    $this->renderTestEntity($id);
-    $expected_link = \Drupal::l($title, Url::fromUri($value));
-    $this->assertRaw($expected_link);
+    $output = $this->renderTestEntity($id);
+    $expected_link = (string) \Drupal::l($title, Url::fromUri($value));
+    $this->assertContains($expected_link, $output);
   }
 
   /**
@@ -395,7 +395,7 @@ function testLinkFormatter() {
     // Assert label is shown.
     $this->assertText('Read more about this entity');
     $this->drupalPostForm(NULL, $edit, t('Save'));
-    preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
+    preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
     $id = $match[1];
     $this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
 
@@ -428,56 +428,56 @@ function testLinkFormatter() {
           ->setComponent($field_name, $display_options)
           ->save();
 
-        $this->renderTestEntity($id);
+        $output = $this->renderTestEntity($id);
         switch ($setting) {
           case 'trim_length':
             $url = $url1;
             $title = isset($new_value) ? Unicode::truncate($title1, $new_value, FALSE, TRUE) : $title1;
-            $this->assertRaw('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>');
+            $this->assertContains('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>', $output);
 
             $url = $url2;
             $title = isset($new_value) ? Unicode::truncate($title2, $new_value, FALSE, TRUE) : $title2;
-            $this->assertRaw('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>');
+            $this->assertContains('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>', $output);
 
             $url = $url3;
             $title = isset($new_value) ? Unicode::truncate($title3, $new_value, FALSE, TRUE) : $title3;
-            $this->assertRaw('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>');
+            $this->assertContains('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>', $output);
             break;
 
           case 'rel':
             $rel = isset($new_value) ? ' rel="' . $new_value . '"' : '';
-            $this->assertRaw('<a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($title1) . '</a>');
-            $this->assertRaw('<a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($title2) . '</a>');
-            $this->assertRaw('<a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($title3) . '</a>');
+            $this->assertContains('<a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($title1) . '</a>', $output);
+            $this->assertContains('<a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($title2) . '</a>', $output);
+            $this->assertContains('<a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($title3) . '</a>', $output);
             break;
 
           case 'target':
             $target = isset($new_value) ? ' target="' . $new_value . '"' : '';
-            $this->assertRaw('<a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($title1) . '</a>');
-            $this->assertRaw('<a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($title2) . '</a>');
-            $this->assertRaw('<a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($title3) . '</a>');
+            $this->assertContains('<a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($title1) . '</a>', $output);
+            $this->assertContains('<a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($title2) . '</a>', $output);
+            $this->assertContains('<a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($title3) . '</a>', $output);
             break;
 
           case 'url_only':
             // In this case, $new_value is an array.
             if (!$new_value['url_only']) {
-              $this->assertRaw('<a href="' . Html::escape($url1) . '">' . Html::escape($title1) . '</a>');
-              $this->assertRaw('<a href="' . Html::escape($url2) . '">' . Html::escape($title2) . '</a>');
-              $this->assertRaw('<a href="' . Html::escape($url3) . '">' . Html::escape($title3) . '</a>');
+              $this->assertContains('<a href="' . Html::escape($url1) . '">' . Html::escape($title1) . '</a>', $output);
+              $this->assertContains('<a href="' . Html::escape($url2) . '">' . Html::escape($title2) . '</a>', $output);
+              $this->assertContains('<a href="' . Html::escape($url3) . '">' . Html::escape($title3) . '</a>', $output);
             }
             else {
               if (empty($new_value['url_plain'])) {
-                $this->assertRaw('<a href="' . Html::escape($url1) . '">' . Html::escape($url1) . '</a>');
-                $this->assertRaw('<a href="' . Html::escape($url2) . '">' . Html::escape($url2) . '</a>');
-                $this->assertRaw('<a href="' . Html::escape($url3) . '">' . Html::escape($url3) . '</a>');
+                $this->assertContains('<a href="' . Html::escape($url1) . '">' . Html::escape($url1) . '</a>', $output);
+                $this->assertContains('<a href="' . Html::escape($url2) . '">' . Html::escape($url2) . '</a>', $output);
+                $this->assertContains('<a href="' . Html::escape($url3) . '">' . Html::escape($url3) . '</a>', $output);
               }
               else {
-                $this->assertNoRaw('<a href="' . Html::escape($url1) . '">' . Html::escape($url1) . '</a>');
-                $this->assertNoRaw('<a href="' . Html::escape($url2) . '">' . Html::escape($url2) . '</a>');
-                $this->assertNoRaw('<a href="' . Html::escape($url3) . '">' . Html::escape($url3) . '</a>');
-                $this->assertEscaped($url1);
-                $this->assertEscaped($url2);
-                $this->assertEscaped($url3);
+                $this->assertNotContains('<a href="' . Html::escape($url1) . '">' . Html::escape($url1) . '</a>', $output);
+                $this->assertNotContains('<a href="' . Html::escape($url2) . '">' . Html::escape($url2) . '</a>', $output);
+                $this->assertNotContains('<a href="' . Html::escape($url3) . '">' . Html::escape($url3) . '</a>', $output);
+                $this->assertContains(Html::escape($url1), $output);
+                $this->assertContains(Html::escape($url2), $output);
+                $this->assertContains(Html::escape($url3), $output);
               }
             }
             break;
@@ -544,7 +544,7 @@ function testLinkSeparateFormatter() {
       "{$field_name}[2][title]" => $title3,
     );
     $this->drupalPostForm(NULL, $edit, t('Save'));
-    preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
+    preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
     $id = $match[1];
     $this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
 
@@ -562,7 +562,7 @@ function testLinkSeparateFormatter() {
           ->setComponent($field_name, $display_options)
           ->save();
 
-        $this->renderTestEntity($id);
+        $output = $this->renderTestEntity($id);
         switch ($setting) {
           case 'trim_length':
             $url = $url1;
@@ -570,7 +570,7 @@ function testLinkSeparateFormatter() {
             $expected = '<div class="link-item">';
             $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
             $expected .= '</div>';
-            $this->assertRaw($expected);
+            $this->assertContains($expected, $output);
 
             $url = $url2;
             $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
@@ -579,7 +579,7 @@ function testLinkSeparateFormatter() {
             $expected .= '<div class="link-title">' . Html::escape($title) . '</div>';
             $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
             $expected .= '</div>';
-            $this->assertRaw($expected);
+            $this->assertContains($expected, $output);
 
             $url = $url3;
             $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
@@ -588,21 +588,21 @@ function testLinkSeparateFormatter() {
             $expected .= '<div class="link-title">' . Html::escape($title) . '</div>';
             $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
             $expected .= '</div>';
-            $this->assertRaw($expected);
+            $this->assertContains($expected, $output);
             break;
 
           case 'rel':
             $rel = isset($new_value) ? ' rel="' . $new_value . '"' : '';
-            $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($url1) . '</a></div>');
-            $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($url2) . '</a></div>');
-            $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($url3) . '</a></div>');
+            $this->assertContains('<div class="link-url"><a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($url1) . '</a></div>', $output);
+            $this->assertContains('<div class="link-url"><a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($url2) . '</a></div>', $output);
+            $this->assertContains('<div class="link-url"><a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($url3) . '</a></div>', $output);
             break;
 
           case 'target':
             $target = isset($new_value) ? ' target="' . $new_value . '"' : '';
-            $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($url1) . '</a></div>');
-            $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($url2) . '</a></div>');
-            $this->assertRaw('<div class="link-url"><a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($url3) . '</a></div>');
+            $this->assertContains('<div class="link-url"><a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($url1) . '</a></div>', $output);
+            $this->assertContains('<div class="link-url"><a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($url2) . '</a></div>', $output);
+            $this->assertContains('<div class="link-url"><a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($url3) . '</a></div>', $output);
             break;
         }
       }
@@ -610,7 +610,7 @@ function testLinkSeparateFormatter() {
   }
 
   /**
-   * Renders a test_entity and sets the output in the internal browser.
+   * Renders a test_entity and returns the output.
    *
    * @param int $id
    *   The test_entity ID to render.
@@ -619,6 +619,9 @@ function testLinkSeparateFormatter() {
    * @param bool $reset
    *   (optional) Whether to reset the entity_test storage cache. Defaults to
    *   TRUE to simplify testing.
+   *
+   * @return string
+   *   The rendered HTML output.
    */
   protected function renderTestEntity($id, $view_mode = 'full', $reset = TRUE) {
     if ($reset) {
@@ -627,9 +630,11 @@ protected function renderTestEntity($id, $view_mode = 'full', $reset = TRUE) {
     $entity = EntityTest::load($id);
     $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), $view_mode);
     $content = $display->build($entity);
+
     $output = \Drupal::service('renderer')->renderRoot($content);
-    $this->setRawContent($output);
+    $output = (string) $output;
     $this->verbose($output);
+    return $output;
   }
 
 }
diff --git a/core/modules/link/src/Tests/LinkFieldUITest.php b/core/modules/link/tests/src/Functional/LinkFieldUITest.php
similarity index 94%
rename from core/modules/link/src/Tests/LinkFieldUITest.php
rename to core/modules/link/tests/src/Functional/LinkFieldUITest.php
index 855c10b..03c585c 100644
--- a/core/modules/link/src/Tests/LinkFieldUITest.php
+++ b/core/modules/link/tests/src/Functional/LinkFieldUITest.php
@@ -1,18 +1,18 @@
 <?php
 
-namespace Drupal\link\Tests;
+namespace Drupal\Tests\link\Functional;
 
 use Drupal\Component\Utility\Unicode;
-use Drupal\field_ui\Tests\FieldUiTestTrait;
+use Drupal\Tests\field_ui\Functional\FieldUiTestTrait;
 use Drupal\link\LinkItemInterface;
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests link field UI functionality.
  *
  * @group link
  */
-class LinkFieldUITest extends WebTestBase {
+class LinkFieldUITest extends BrowserTestBase {
 
   use FieldUiTestTrait;
 
diff --git a/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php b/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php
index 6ca9f85..aa5fd55 100644
--- a/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php
+++ b/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php
@@ -57,6 +57,9 @@ protected function assertElementNotPresent($css_selector) {
    *   $this->assertSession()->responseContains() instead.
    */
   protected function assertText($text) {
+    // Cast MarkupInterface to string.
+    $text = (string) $text;
+
     $content_type = $this->getSession()->getResponseHeader('Content-type');
     // In case of a Non-HTML response (example: XML) check the original
     // response.
@@ -64,6 +67,10 @@ protected function assertText($text) {
       $this->assertSession()->responseContains($text);
     }
     else {
+      // Mink/BrowserKit uses PHP's DOMElement to get the value of an HTML tag.
+      // It will provide the value HTML entity decoded, so we need to do the
+      // same here for the text to actually match.
+      $text = html_entity_decode($text);
       $this->assertSession()->pageTextContains($text);
     }
   }
@@ -129,6 +136,20 @@ protected function assertFieldByName($name, $value = NULL) {
   }
 
   /**
+   * Asserts that a field does not exist with the given name.
+   *
+   * @param string $name
+   *   Name of field to assert.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.0.
+   *   Use $this->assertSession()->fieldNotExists() or
+   *   $this->assertSession()->fieldValueNotEquals() instead.
+   */
+  protected function assertNoFieldByName($name) {
+    $this->assertSession()->fieldNotExists($name);
+  }
+
+  /**
    * Asserts that a field exists with the given ID and value.
    *
    * @param string $id
