diff --git a/core/modules/help/src/Tests/ExperimentalHelpTest.php b/core/modules/help/tests/src/Functional/ExperimentalHelpTest.php
similarity index 90%
rename from core/modules/help/src/Tests/ExperimentalHelpTest.php
rename to core/modules/help/tests/src/Functional/ExperimentalHelpTest.php
index 416b69f..9d1a74e 100644
--- a/core/modules/help/src/Tests/ExperimentalHelpTest.php
+++ b/core/modules/help/tests/src/Functional/ExperimentalHelpTest.php
@@ -1,15 +1,15 @@
 <?php
 
-namespace Drupal\help\Tests;
+namespace Drupal\Tests\help\Functional;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Verifies help for experimental modules.
  *
  * @group help
  */
-class ExperimentalHelpTest extends WebTestBase {
+class ExperimentalHelpTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
diff --git a/core/modules/help/src/Tests/HelpBlockTest.php b/core/modules/help/tests/src/Functional/HelpBlockTest.php
similarity index 90%
rename from core/modules/help/src/Tests/HelpBlockTest.php
rename to core/modules/help/tests/src/Functional/HelpBlockTest.php
index f1ba622..81c4fc1 100644
--- a/core/modules/help/src/Tests/HelpBlockTest.php
+++ b/core/modules/help/tests/src/Functional/HelpBlockTest.php
@@ -1,15 +1,15 @@
 <?php
 
-namespace Drupal\help\Tests;
+namespace Drupal\Tests\help\Functional;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests display of help block.
  *
  * @group help
  */
-class HelpBlockTest extends WebTestBase {
+class HelpBlockTest extends BrowserTestBase {
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/help/src/Tests/HelpTest.php b/core/modules/help/tests/src/Functional/HelpTest.php
similarity index 95%
rename from core/modules/help/src/Tests/HelpTest.php
rename to core/modules/help/tests/src/Functional/HelpTest.php
index 76e2ee3..2e82499 100644
--- a/core/modules/help/src/Tests/HelpTest.php
+++ b/core/modules/help/tests/src/Functional/HelpTest.php
@@ -1,15 +1,15 @@
 <?php
 
-namespace Drupal\help\Tests;
+namespace Drupal\Tests\help\Functional;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Verify help display and user access to help based on permissions.
  *
  * @group help
  */
-class HelpTest extends WebTestBase {
+class HelpTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
@@ -40,8 +40,6 @@ class HelpTest extends WebTestBase {
   protected function setUp() {
     parent::setUp();
 
-    $this->getModuleList();
-
     // Create users.
     $this->adminUser = $this->drupalCreateUser(array('access administration pages', 'view the administration theme', 'administer permissions'));
     $this->anyUser = $this->drupalCreateUser(array());
@@ -122,7 +120,7 @@ protected function verifyHelp($response = 200) {
       $this->assertResponse($response);
       if ($response == 200) {
         $this->assertTitle($name . ' | Drupal', format_string('%module title was displayed', array('%module' => $module)));
-        $this->assertEqual($this->cssSelect('h1.page-title')[0], t($name), format_string('%module heading was displayed', array('%module' => $module)));
+        $this->assertEquals($name, $this->cssSelect('h1.page-title')[0]->getText(), "$module heading was displayed");
         $admin_tasks = system_get_module_admin_tasks($module, system_get_info('module', $module));
         if (!empty($admin_tasks)) {
           $this->assertText(t('@module administration pages', array('@module' => $name)));
diff --git a/core/modules/help/src/Tests/NoHelpTest.php b/core/modules/help/tests/src/Functional/NoHelpTest.php
similarity index 91%
rename from core/modules/help/src/Tests/NoHelpTest.php
rename to core/modules/help/tests/src/Functional/NoHelpTest.php
index 7a6fb7a..49c768d 100644
--- a/core/modules/help/src/Tests/NoHelpTest.php
+++ b/core/modules/help/tests/src/Functional/NoHelpTest.php
@@ -1,15 +1,15 @@
 <?php
 
-namespace Drupal\help\Tests;
+namespace Drupal\Tests\help\Functional;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Verify no help is displayed for modules not providing any help.
  *
  * @group help
  */
-class NoHelpTest extends WebTestBase {
+class NoHelpTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
diff --git a/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php b/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php
new file mode 100644
index 0000000..257c92e
--- /dev/null
+++ b/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace Drupal\FunctionalTests;
+
+use Drupal\KernelTests\AssertLegacyTrait as BaseAssertLegacyTrait;
+
+/**
+ * Provides convenience methods for assertions in browser tests.
+ *
+ * @deprecated Scheduled for removal in Drupal 9.0.0. Use the methods on
+ *   \Drupal\Tests\WebAssert instead, for example
+ * @code
+ *    $this->assertSession()->statusCodeEquals(200);
+ * @endcode
+ */
+trait AssertLegacyTrait {
+
+  use BaseAssertLegacyTrait;
+
+  /**
+   * Asserts that the element with the given CSS selector is present.
+   *
+   * @param string $css_selector
+   *   The CSS selector identifying the element to check.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.0.
+   *   Use $this->assertSession()->elementExists() instead.
+   */
+  protected function assertElementPresent($css_selector) {
+    $this->assertSession()->elementExists('css', $css_selector);
+  }
+
+  /**
+   * Asserts that the element with the given CSS selector is not present.
+   *
+   * @param string $css_selector
+   *   The CSS selector identifying the element to check.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.0.
+   *   Use $this->assertSession()->elementNotExists() instead.
+   */
+  protected function assertElementNotPresent($css_selector) {
+    $this->assertSession()->elementNotExists('css', $css_selector);
+  }
+
+  /**
+   * Passes if the page (with HTML stripped) contains the text.
+   *
+   * Note that stripping HTML tags also removes their attributes, such as
+   * the values of text fields.
+   *
+   * @param string $text
+   *   Plain text to look for.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.0.
+   *   Use $this->assertSession()->responseContains() instead.
+   */
+  protected function assertText($text) {
+    $this->assertSession()->responseContains($text);
+  }
+
+  /**
+   * Passes if the page (with HTML stripped) does not contains the text.
+   *
+   * Note that stripping HTML tags also removes their attributes, such as
+   * the values of text fields.
+   *
+   * @param string $text
+   *   Plain text to look for.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.0.
+   *   Use $this->assertSession()->responseNotContains() instead.
+   */
+  protected function assertNoText($text) {
+    $this->assertSession()->responseNotContains($text);
+  }
+
+  /**
+   * Asserts the page responds with the specified response code.
+   *
+   * @param int $code
+   *   Response code. For example 200 is a successful page request. For a list
+   *   of all codes see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.0.
+   *   Use $this->assertSession()->statusCodeEquals() instead.
+   */
+  protected function assertResponse($code) {
+    $this->assertSession()->statusCodeEquals($code);
+  }
+
+  /**
+   * Asserts that a field exists with the given name and value.
+   *
+   * @param string $name
+   *   Name of field to assert.
+   * @param string $value
+   *   (optional) Value of the field to assert. You may pass in NULL (default)
+   *   to skip checking the actual value, while still checking that the field
+   *   exists.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.0.
+   *   Use $this->assertSession()->fieldExists() or
+   *   $this->assertSession()->fieldValueEquals() instead.
+   */
+  protected function assertFieldByName($name, $value = NULL) {
+    $this->assertSession()->fieldExists($name);
+    if ($value !== NULL) {
+      $this->assertSession()->fieldValueEquals($name, $value);
+    }
+  }
+
+  /**
+   * Passes if the raw text IS found on the loaded page, fail otherwise.
+   *
+   * Raw text refers to the raw HTML that the page generated.
+   *
+   * @param string $raw
+   *   Raw (HTML) string to look for.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.0.
+   *   Use $this->assertSession()->responseContains() instead.
+   */
+  protected function assertRaw($raw) {
+    $this->assertSession()->responseContains($raw);
+  }
+
+  /**
+   * Pass if the page title is the given string.
+   *
+   * @param string $expected_title
+   *   The string the page title should be.
+   */
+  protected function assertTitle($expected_title) {
+    return $this->assertSession()->titleEquals($expected_title);
+  }
+
+  /**
+   * Passes if a link with the specified label is found.
+   *
+   * An optional link index may be passed.
+   *
+   * @param string|\Drupal\Component\Render\MarkupInterface $label
+   *   Text between the anchor tags.
+   * @param int $index
+   *   Link position counting from zero.
+   */
+  public function assertLink($label, $index = 0) {
+    return $this->assertSession()->linkExists($label, $index);
+  }
+
+  /**
+   * Passes if the raw text IS NOT found escaped on the loaded page.
+   *
+   * Raw text refers to the raw HTML that the page generated.
+   *
+   * @param string $raw
+   *   Raw (HTML) string to look for.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.0.
+   *   Use $this->assertSession()->assertNoEscaped() instead.
+   */
+  public function assertNoEscaped($raw) {
+    $this->assertSession()->assertNoEscaped($raw);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php
index a8700b4..5a9b0c0 100644
--- a/core/tests/Drupal/Tests/BrowserTestBase.php
+++ b/core/tests/Drupal/Tests/BrowserTestBase.php
@@ -20,9 +20,12 @@
 use Drupal\Core\Test\TestRunnerKernel;
 use Drupal\Core\Url;
 use Drupal\Core\Test\TestDatabase;
+use Drupal\FunctionalTests\AssertLegacyTrait;
+use Drupal\simpletest\AssertHelperTrait;
+use Drupal\simpletest\BlockCreationTrait;
+use Drupal\simpletest\NodeCreationTrait;
 use Drupal\user\Entity\Role;
 use Drupal\user\Entity\User;
-use Drupal\user\UserInterface;
 use Symfony\Component\CssSelector\CssSelectorConverter;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -36,8 +39,14 @@
  * @ingroup testing
  */
 abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
+  use AssertHelperTrait;
+  use BlockCreationTrait;
+  use AssertLegacyTrait;
   use RandomGeneratorTrait;
   use SessionTestTrait;
+  use NodeCreationTrait {
+    createNode as drupalCreateNode;
+  }
 
   /**
    * Class loader.
@@ -274,6 +283,13 @@
   protected $baseUrl;
 
   /**
+   * The original array of shutdown function callbacks.
+   *
+   * @var array
+   */
+  protected $originalShutdownCallbacks = [];
+
+  /**
    * Initializes Mink sessions.
    */
   protected function initMink() {
@@ -505,6 +521,10 @@ protected function tearDown() {
     if ($this->mink) {
       $this->mink->stopSessions();
     }
+
+    // Restore original shutdown callbacks.
+    $callbacks = &drupal_register_shutdown_function();
+    $callbacks = $this->originalShutdownCallbacks;
   }
 
   /**
@@ -894,6 +914,90 @@ protected function submitForm(array $edit, $submit, $form_html_id = NULL) {
   }
 
   /**
+   * Executes a form submission.
+   *
+   * It will be done as usual POST request with Mink.
+   *
+   * @param \Drupal\Core\Url|string $path
+   *   Location of the post form. Either a Drupal path or an absolute path or
+   *   NULL to post to the current page. For multi-stage forms you can set the
+   *   path to NULL and have it post to the last received page. Example:
+   *
+   *   @code
+   *   // First step in form.
+   *   $edit = array(...);
+   *   $this->drupalPostForm('some_url', $edit, t('Save'));
+   *
+   *   // Second step in form.
+   *   $edit = array(...);
+   *   $this->drupalPostForm(NULL, $edit, t('Save'));
+   *   @endcode
+   * @param array $edit
+   *   Field data in an associative array. Changes the current input fields
+   *   (where possible) to the values indicated.
+   *
+   *   When working with form tests, the keys for an $edit element should match
+   *   the 'name' parameter of the HTML of the form. For example, the 'body'
+   *   field for a node has the following HTML:
+   *   @code
+   *   <textarea id="edit-body-und-0-value" class="text-full form-textarea
+   *    resize-vertical" placeholder="" cols="60" rows="9"
+   *    name="body[0][value]"></textarea>
+   *   @endcode
+   *   When testing this field using an $edit parameter, the code becomes:
+   *   @code
+   *   $edit["body[0][value]"] = 'My test value';
+   *   @endcode
+   *
+   *   A checkbox can be set to TRUE to be checked and should be set to FALSE to
+   *   be unchecked. Multiple select fields can be tested using 'name[]' and
+   *   setting each of the desired values in an array:
+   *   @code
+   *   $edit = array();
+   *   $edit['name[]'] = array('value1', 'value2');
+   *   @endcode
+   * @param string $submit
+   *   Value of the submit button whose click is to be emulated. For example,
+   *   t('Save'). The processing of the request depends on this value. For
+   *   example, a form may have one button with the value t('Save') and another
+   *   button with the value t('Delete'), and execute different code depending
+   *   on which one is clicked.
+   *
+   *   This function can also be called to emulate an Ajax submission. In this
+   *   case, this value needs to be an array with the following keys:
+   *   - path: A path to submit the form values to for Ajax-specific processing.
+   *   - triggering_element: If the value for the 'path' key is a generic Ajax
+   *     processing path, this needs to be set to the name of the element. If
+   *     the name doesn't identify the element uniquely, then this should
+   *     instead be an array with a single key/value pair, corresponding to the
+   *     element name and value. The \Drupal\Core\Form\FormAjaxResponseBuilder
+   *     uses this to find the #ajax information for the element, including
+   *     which specific callback to use for processing the request.
+   *
+   *   This can also be set to NULL in order to emulate an Internet Explorer
+   *   submission of a form with a single text field, and pressing ENTER in that
+   *   textfield: under these conditions, no button information is added to the
+   *   POST data.
+   * @param array $options
+   *   Options to be forwarded to the url generator.
+   */
+  protected function drupalPostForm($path, array $edit, $submit, array $options = array()) {
+    if (is_object($submit)) {
+      // Cast MarkupInterface objects to string.
+      $submit = (string) $submit;
+    }
+    if (is_array($edit)) {
+      $edit = $this->castSafeStrings($edit);
+    }
+
+    if (isset($path)) {
+      $this->drupalGet($path, $options);
+    }
+
+    $this->submitForm($edit, $submit);
+  }
+
+  /**
    * Helper function to get the options of select field.
    *
    * @param \Behat\Mink\Element\NodeElement|string $select
@@ -951,6 +1055,10 @@ public function installDrupal() {
       'value' => $this->publicFilesDirectory,
       'required' => TRUE,
     );
+    $settings['settings']['file_private_path'] = (object) [
+      'value' => $this->privateFilesDirectory,
+      'required' => TRUE,
+    ];
     $this->writeSettings($settings);
     // Allow for test-specific overrides.
     $settings_testing_file = DRUPAL_ROOT . '/' . $this->originalSiteDirectory . '/settings.testing.php';
@@ -1000,13 +1108,13 @@ public function installDrupal() {
     $config = $container->get('config.factory');
 
     // Manually create and configure private and temporary files directories.
-    // While these could be preset/enforced in settings.php like the public
-    // files directory above, some tests expect them to be configurable in the
-    // UI. If declared in settings.php, they would no longer be configurable.
     file_prepare_directory($this->privateFilesDirectory, FILE_CREATE_DIRECTORY);
     file_prepare_directory($this->tempFilesDirectory, FILE_CREATE_DIRECTORY);
+    // While the temporary files path could be preset/enforced in settings.php
+    // like the public files directory above, some tests expect it to be
+    // configurable in the UI. If declared in settings.php, it would no longer
+    // be configurable.
     $config->getEditable('system.file')
-      ->set('path.private', $this->privateFilesDirectory)
       ->set('path.temporary', $this->tempFilesDirectory)
       ->save();
 
@@ -1241,6 +1349,14 @@ protected function prepareEnvironment() {
     ));
 
     drupal_set_time_limit($this->timeLimit);
+
+    // Save and clean the shutdown callbacks array because it is static cached
+    // and will be changed by the test run. Otherwise it will contain callbacks
+    // from both environments and the testing environment will try to call the
+    // handlers defined by the original one.
+    $callbacks = &drupal_register_shutdown_function();
+    $this->originalShutdownCallbacks = $callbacks;
+    $callbacks = [];
   }
 
   /**
@@ -1402,13 +1518,13 @@ protected function refreshVariables() {
   /**
    * Returns whether a given user account is logged in.
    *
-   * @param \Drupal\user\UserInterface $account
+   * @param \Drupal\Core\Session\AccountInterface $account
    *   The user account object to check.
    *
    * @return bool
    *   Return TRUE if the user is logged in, FALSE otherwise.
    */
-  protected function drupalUserIsLoggedIn(UserInterface $account) {
+  protected function drupalUserIsLoggedIn(AccountInterface $account) {
     $logged_in = FALSE;
 
     if (isset($account->sessionId)) {
@@ -1420,30 +1536,6 @@ protected function drupalUserIsLoggedIn(UserInterface $account) {
   }
 
   /**
-   * Asserts that the element with the given CSS selector is present.
-   *
-   * @param string $css_selector
-   *   The CSS selector identifying the element to check.
-   * @param string $message
-   *   Optional message to show alongside the assertion.
-   */
-  protected function assertElementPresent($css_selector, $message = '') {
-    $this->assertNotEmpty($this->getSession()->getDriver()->find($this->cssSelectToXpath($css_selector)), $message);
-  }
-
-  /**
-   * Asserts that the element with the given CSS selector is not present.
-   *
-   * @param string $css_selector
-   *   The CSS selector identifying the element to check.
-   * @param string $message
-   *   Optional message to show alongside the assertion.
-   */
-  protected function assertElementNotPresent($css_selector, $message = '') {
-    $this->assertEmpty($this->getSession()->getDriver()->find($this->cssSelectToXpath($css_selector)), $message);
-  }
-
-  /**
    * Clicks the element with the given CSS selector.
    *
    * @param string $css_selector
@@ -1529,4 +1621,156 @@ protected function cssSelectToXpath($selector, $html = TRUE, $prefix = 'descenda
     return (new CssSelectorConverter($html))->toXPath($selector, $prefix);
   }
 
+  /**
+   * Searches elements using a CSS selector in the raw content.
+   *
+   * The search is relative to the root element (HTML tag normally) of the page.
+   *
+   * @param string $selector
+   *   CSS selector to use in the search.
+   *
+   * @return \Behat\Mink\Element\NodeElement[]
+   *   The list of elements on the page that match the selector.
+   */
+  protected function cssSelect($selector) {
+    return $this->getSession()->getPage()->findAll('css', $selector);
+  }
+
+  /**
+   * Follows a link by complete name.
+   *
+   * Will click the first link found with this link text.
+   *
+   * If the link is discovered and clicked, the test passes. Fail otherwise.
+   *
+   * @param string|\Drupal\Component\Render\MarkupInterface $label
+   *   Text between the anchor tags.
+   */
+  protected function clickLink($label) {
+    $label = (string) $label;
+    $this->getSession()->getPage()->clickLink($label);
+  }
+
+  /**
+   * Retrieves the plain-text content from the current page.
+   */
+  protected function getTextContent() {
+    return $this->getSession()->getPage()->getContent();
+  }
+
+  /**
+   * Performs an xpath search on the contents of the internal browser.
+   *
+   * The search is relative to the root element (HTML tag normally) of the page.
+   *
+   * @param string $xpath
+   *   The xpath string to use in the search.
+   * @param array $arguments
+   *   An array of arguments with keys in the form ':name' matching the
+   *   placeholders in the query. The values may be either strings or numeric
+   *   values.
+   *
+   * @return \Behat\Mink\Element\NodeElement[]
+   *   The list of elements matching the xpath expression.
+   */
+  protected function xpath($xpath, array $arguments = []) {
+    $xpath = $this->buildXPathQuery($xpath, $arguments);
+    return $this->getSession()->getPage()->findAll('xpath', $xpath);
+  }
+
+  /**
+   * Builds an XPath query.
+   *
+   * Builds an XPath query by replacing placeholders in the query by the value
+   * of the arguments.
+   *
+   * XPath 1.0 (the version supported by libxml2, the underlying XML library
+   * used by PHP) doesn't support any form of quotation. This function
+   * simplifies the building of XPath expression.
+   *
+   * @param string $xpath
+   *   An XPath query, possibly with placeholders in the form ':name'.
+   * @param array $args
+   *   An array of arguments with keys in the form ':name' matching the
+   *   placeholders in the query. The values may be either strings or numeric
+   *   values.
+   *
+   * @return string
+   *   An XPath query with arguments replaced.
+   */
+  protected function buildXPathQuery($xpath, array $args = array()) {
+    // Replace placeholders.
+    foreach ($args as $placeholder => $value) {
+      // Cast MarkupInterface objects to string.
+      if (is_object($value)) {
+        $value = (string) $value;
+      }
+      // XPath 1.0 doesn't support a way to escape single or double quotes in a
+      // string literal. We split double quotes out of the string, and encode
+      // them separately.
+      if (is_string($value)) {
+        // Explode the text at the quote characters.
+        $parts = explode('"', $value);
+
+        // Quote the parts.
+        foreach ($parts as &$part) {
+          $part = '"' . $part . '"';
+        }
+
+        // Return the string.
+        $value = count($parts) > 1 ? 'concat(' . implode(', \'"\', ', $parts) . ')' : $parts[0];
+      }
+
+      // Use preg_replace_callback() instead of preg_replace() to prevent the
+      // regular expression engine from trying to substitute backreferences.
+      $replacement = function ($matches) use ($value) {
+        return $value;
+      };
+      $xpath = preg_replace_callback('/' . preg_quote($placeholder) . '\b/', $replacement, $xpath);
+    }
+    return $xpath;
+  }
+
+  /**
+   * Configuration accessor for tests. Returns non-overridden configuration.
+   *
+   * @param string $name
+   *   Configuration name.
+   *
+   * @return \Drupal\Core\Config\Config
+   *   The configuration object with original configuration data.
+   */
+  protected function config($name) {
+    return $this->container->get('config.factory')->getEditable($name);
+  }
+
+  /**
+   * Gets the value of an HTTP response header.
+   *
+   * If multiple requests were required to retrieve the page, only the headers
+   * from the last request will be checked by default.
+   *
+   * @param string $name
+   *   The name of the header to retrieve. Names are case-insensitive (see RFC
+   *   2616 section 4.2).
+   *
+   * @return string|null
+   *   The HTTP header value or NULL if not found.
+   */
+  protected function drupalGetHeader($name) {
+    return $this->getSession()->getResponseHeader($name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function assertEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE) {
+    // Cast objects implementing MarkupInterface to string instead of
+    // relying on PHP casting them to string depending on what they are being
+    // comparing with.
+    $expected = static::castSafeStrings($expected);
+    $actual = static::castSafeStrings($actual);
+    parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase);
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/WebAssert.php b/core/tests/Drupal/Tests/WebAssert.php
index d863de2..84fc744 100644
--- a/core/tests/Drupal/Tests/WebAssert.php
+++ b/core/tests/Drupal/Tests/WebAssert.php
@@ -2,9 +2,11 @@
 
 namespace Drupal\Tests;
 
+use Behat\Mink\Exception\ExpectationException;
 use Behat\Mink\WebAssert as MinkWebAssert;
 use Behat\Mink\Element\TraversableElement;
 use Behat\Mink\Exception\ElementNotFoundException;
+use Drupal\Component\Utility\Html;
 
 /**
  * Defines a class with methods for asserting presence of elements during tests.
@@ -64,4 +66,83 @@ public function selectExists($select, TraversableElement $container = NULL) {
     return $node;
   }
 
+  /**
+   * Pass if the page title is the given string.
+   *
+   * @param string $expected_title
+   *   The string the page title should be.
+   *
+   * @throws \Behat\Mink\Exception\ExpectationException
+   *   Thrown when element doesn't exist, or the title is a different one.
+   */
+  public function titleEquals($expected_title) {
+    $title_element = $this->session->getPage()->find('css', 'title');
+    if (!$title_element) {
+      throw new ExpectationException('No title element found on the page', $this->session);
+    }
+    $actual_title = $title_element->getText();
+    $this->assert($expected_title === $actual_title, 'Title found');
+  }
+
+  /**
+   * Passes if a link with the specified label is found.
+   *
+   * An optional link index may be passed.
+   *
+   * @param string|\Drupal\Component\Render\MarkupInterface $label
+   *   Text between the anchor tags.
+   * @param int $index
+   *   Link position counting from zero.
+   * @param string $message
+   *   (optional) A message to display with the assertion. Do not translate
+   *   messages: use strtr() to embed variables in the message text, not
+   *   t(). If left blank, a default message will be displayed.
+   *
+   * @throws \Behat\Mink\Exception\ExpectationException
+   *   Thrown when element doesn't exist, or the link label is a different one.
+   */
+  public function linkExists($label, $index = 0, $message = '') {
+    // Cast MarkupInterface objects to string.
+    $label = (string) $label;
+    $message = ($message ? $message : strtr('Link with label %label found.', ['%label' => $label]));
+    $links = $this->session->getPage()->findAll('named', ['link', $label]);
+    if (empty($links[$index])) {
+      throw new ExpectationException($message);
+    }
+    $this->assert($links[$index] !== NULL, $message);
+  }
+
+  /**
+   * Passes if the raw text IS NOT found escaped on the loaded page.
+   *
+   * Raw text refers to the raw HTML that the page generated.
+   *
+   * @param string $raw
+   *   Raw (HTML) string to look for.
+   */
+  public function assertNoEscaped($raw) {
+    $this->pageTextNotContains(Html::escape($raw));
+  }
+
+  /**
+   * Asserts a condition.
+   *
+   * The parent method is overridden because it is a private method.
+   *
+   * @param bool $condition
+   *   The condition.
+   * @param string $message
+   *   The success message.
+   *
+   * @throws \Behat\Mink\Exception\ExpectationException
+   *   When the condition is not fulfilled.
+   */
+  public function assert($condition, $message) {
+    if ($condition) {
+      return;
+    }
+
+    throw new ExpectationException($message, $this->session->getDriver());
+  }
+
 }
