diff --git a/src/Form/IndexStatusForm.php b/src/Form/IndexStatusForm.php
index 1bbf17b..33c0db6 100644
--- a/src/Form/IndexStatusForm.php
+++ b/src/Form/IndexStatusForm.php
@@ -54,7 +54,7 @@ class IndexStatusForm extends FormBase {
       // Add the "Index now" form.
       $form['index'] = array(
         '#type' => 'details',
-        '#title' => $this->t('Index now'),
+        '#title' => $this->t('Start indexing now'),
         '#open' => TRUE,
         '#attributes' => array(
           'class' => array('container-inline'),
diff --git a/src/Tests/CacheabilityTest.php b/tests/src/Functional/CacheabilityTest.php
similarity index 87%
rename from src/Tests/CacheabilityTest.php
rename to tests/src/Functional/CacheabilityTest.php
index e4e1077..5fb4c1e 100644
--- a/src/Tests/CacheabilityTest.php
+++ b/tests/src/Functional/CacheabilityTest.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\search_api\Tests;
+namespace Drupal\Tests\search_api\Functional;
 
 use Drupal\search_api\Entity\Index;
 
@@ -58,8 +58,8 @@ class CacheabilityTest extends WebTestBase {
 
     // Verify that the search results are marked as uncacheable.
     $this->drupalGet('search-api-test');
-    $this->assertResponse(200);
-    $this->assertHeader('x-drupal-dynamic-cache', 'UNCACHEABLE');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->responseHeaderEquals('x-drupal-dynamic-cache', 'UNCACHEABLE');
     $this->assertTrue(strpos($this->drupalGetHeader('cache-control'), 'no-cache'));
   }
 
diff --git a/src/Tests/ExampleContentTrait.php b/tests/src/Functional/ExampleContentTrait.php
similarity index 97%
rename from src/Tests/ExampleContentTrait.php
rename to tests/src/Functional/ExampleContentTrait.php
index f20988b..2f134eb 100644
--- a/src/Tests/ExampleContentTrait.php
+++ b/tests/src/Functional/ExampleContentTrait.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\search_api\Tests;
+namespace Drupal\Tests\search_api\Functional;
 
 use Drupal\search_api\Entity\Index;
 use Drupal\search_api\Utility\Utility;
@@ -67,7 +67,7 @@ trait ExampleContentTrait {
       'width' => '2.0',
     ));
     $count = \Drupal::entityQuery('entity_test_mulrev_changed')->count()->execute();
-    $this->assertEqual($count, 5, "$count items inserted.");
+    $this->assertEquals($count, 5, "$count items inserted.");
   }
 
   /**
diff --git a/src/Tests/HooksTest.php b/tests/src/Functional/HooksTest.php
similarity index 73%
rename from src/Tests/HooksTest.php
rename to tests/src/Functional/HooksTest.php
index 3a3c31e..547583c 100644
--- a/src/Tests/HooksTest.php
+++ b/tests/src/Functional/HooksTest.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\search_api\Tests;
+namespace Drupal\Tests\search_api\Functional;
 
 use Drupal\search_api\Entity\Index;
 use Drupal\search_api_test\PluginTestTrait;
@@ -75,71 +75,73 @@ class HooksTest extends WebTestBase {
   public function testHooks() {
     // hook_search_api_backend_info_alter() was invoked.
     $this->drupalGet('admin/config/search/search-api/add-server');
-    $this->assertText('Slims return');
+    $this->assertSession()->pageTextContains('Slims return');
 
     // hook_search_api_datasource_info_alter() was invoked.
     $this->drupalGet('admin/config/search/search-api/add-index');
-    $this->assertText('Distant land');
+    $this->assertSession()->pageTextContains('Distant land');
 
     // hook_search_api_processor_info_alter() was invoked.
     $this->drupalGet($this->getIndexPath('processors'));
-    $this->assertText('Mystic bounce');
+    $this->assertSession()->pageTextContains('Mystic bounce');
 
     // hook_search_api_parse_mode_info_alter was invoked.
     $definition = \Drupal::getContainer()
       ->get('plugin.manager.search_api.parse_mode')
       ->getDefinition('direct');
-    $this->assertEqual('Song for My Father', $definition['label']);
+    $this->assertEquals('Song for My Father', $definition['label']);
 
     // Saving the index should trigger the processor's preIndexSave() method.
-    $this->drupalPostForm(NULL, array(), $this->t('Save'));
+    $this->submitForm([], $this->t('Save'));
     $processor_methods = $this->getCalledMethods('processor');
-    $this->assertEqual(array('preIndexSave'), $processor_methods);
+    $this->assertEquals(['preIndexSave'], $processor_methods);
 
     $this->drupalGet($this->getIndexPath());
-    $this->drupalPostForm(NULL, array(), $this->t('Index now'));
+    // Duplication on value $this->t('Index now')) with summary.
+    $this->submitForm([], $this->t('Index now'));
+    $this->checkForMetaRefresh();
+    $this->assertSession()->pageTextContains('Successfully indexed 4 items.');
 
     // During indexing, alterIndexedItems() and preprocessIndexItems() should be
     // called on the processor.
     $processor_methods = $this->getCalledMethods('processor');
-    $expected = array('alterIndexedItems', 'preprocessIndexItems');
-    $this->assertEqual($expected, $processor_methods);
+    $expected = ['alterIndexedItems', 'preprocessIndexItems'];
+    $this->assertEquals($expected, $processor_methods);
 
     // hook_search_api_index_items_alter() was invoked, this removed node:1.
     // hook_search_api_query_TAG_alter() was invoked, this removed node:3.
-    $this->assertText('There are 2 items indexed on the server for this index.');
-    $this->assertText('Successfully indexed 4 items.');
-    $this->assertText('Stormy');
+    $this->assertSession()->pageTextContains('There are 2 items indexed on the server for this index.');
+    $this->assertSession()->pageTextContains('Stormy');
 
     // hook_search_api_items_indexed() was invoked.
-    $this->assertText('Please set me at ease');
+    $this->assertSession()->pageTextContains('Please set me at ease');
 
     // hook_search_api_index_reindex() was invoked.
     $this->drupalGet($this->getIndexPath('reindex'));
-    $this->drupalPostForm(NULL, array(), $this->t('Confirm'));
-    $this->assertText('Montara');
+    $this->submitForm(array(), $this->t('Confirm'));
+    $this->assertSession()->pageTextContains('Montara');
 
     // hook_search_api_data_type_info_alter() was invoked.
     $this->drupalGet($this->getIndexPath('fields'));
-    $this->assertText('Peace/Dolphin dance');
+    $this->assertSession()->pageTextContains('Peace/Dolphin dance');
     // The implementation of hook_search_api_field_type_mapping_alter() has
     // removed all dates, so we can't see any timestamp anymore in the page.
     $url_options['query']['datasource'] = 'entity:node';
     $this->drupalGet($this->getIndexPath('fields/add'), $url_options);
-    $this->assertNoText('timestamp');
+    $this->assertSession()->pageTextNotContains('timestamp');
 
     $this->drupalGet('search-api-test');
     // hook_search_api_query_alter() was invoked.
-    $this->assertText('Funky blue note');
+    $this->assertSession()->pageTextContains('Funky blue note');
     // hook_search_api_results_alter() was invoked.
-    $this->assertText('Stepping into tomorrow');
+    $this->assertSession()->pageTextContains('Stepping into tomorrow');
     // hook_search_api_results_TAG_alter() was invoked.
-    $this->assertText('Llama');
+    $this->assertSession()->pageTextContains('Llama');
 
     // The query alter methods of the processor were called.
     $processor_methods = $this->getCalledMethods('processor');
     $expected = array('preprocessSearchQuery', 'postprocessSearchResults');
-    $this->assertEqual($expected, $processor_methods);
+    $this->assertEquals($expected, $processor_methods);
 
     // hook_search_api_server_features_alter() is triggered.
     $this->assertTrue($this->server->supportsFeature('welcome_to_the_jungle'));
diff --git a/src/Tests/IntegrationTest.php b/tests/src/Functional/IntegrationTest.php
similarity index 76%
rename from src/Tests/IntegrationTest.php
rename to tests/src/Functional/IntegrationTest.php
index cef9daa..e4889aa 100644
--- a/src/Tests/IntegrationTest.php
+++ b/tests/src/Functional/IntegrationTest.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\search_api\Tests;
+namespace Drupal\Tests\search_api\Functional;
 
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Entity\Entity\EntityFormDisplay;
@@ -144,13 +144,14 @@ class IntegrationTest extends WebTestBase {
       'datasources[entity:node]' => TRUE,
     );
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertResponse(200);
-    $this->assertText($this->t('Please configure the used datasources.'));
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains($this->t('Please configure the used datasources.'));
     $this->drupalPostForm(NULL, array(), $this->t('Save'));
-    $this->assertResponse(200);
-    $this->assertText($this->t('The index was successfully saved.'));
-    $this->assertText($this->t('Successfully executed @count pending task.', array('@count' => 1)));
-    $this->assertEqual(2, $this->countTrackedItems());
+    $this->checkForMetaRefresh();
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('Successfully executed @count pending task.', array('@count' => 1)));
+    $this->assertEquals(2, $this->countTrackedItems());
 
     $this->enableAllProcessors();
     $this->checkFieldLabels();
@@ -185,7 +186,7 @@ class IntegrationTest extends WebTestBase {
     $settings_path = 'admin/config/search/search-api/add-server';
 
     $this->drupalGet($settings_path);
-    $this->assertResponse(200, 'Server add page exists');
+    $this->assertSession()->statusCodeEquals(200, 'Server add page exists');
 
     $edit = array(
       'name' => '',
@@ -195,7 +196,7 @@ class IntegrationTest extends WebTestBase {
     );
 
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('@name field is required.', array('@name' => $this->t('Server name'))));
+    $this->assertSession()->pageTextContains($this->t('@name field is required.', array('@name' => $this->t('Server name'))));
 
     $edit = array(
       'name' => $server_name,
@@ -204,7 +205,7 @@ class IntegrationTest extends WebTestBase {
       'backend' => 'search_api_test',
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('@name field is required.', array('@name' => $this->t('Machine-readable name'))));
+    $this->assertSession()->pageTextContains($this->t('@name field is required.', array('@name' => $this->t('Machine-readable name'))));
 
     $edit = array(
       'name' => $server_name,
@@ -216,7 +217,7 @@ class IntegrationTest extends WebTestBase {
 
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
 
-    $this->assertText($this->t('The server was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The server was successfully saved.'));
     $this->assertUrl('admin/config/search/search-api/server/' . $this->serverId, array(), 'Correct redirect to server page.');
     $this->assertHtmlEscaped($server_name);
     $this->assertHtmlEscaped($server_description);
@@ -241,7 +242,7 @@ class IntegrationTest extends WebTestBase {
 
     // Try to submit an server with a duplicate machine name.
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertText($this->t('The machine-readable name is already in use. It must be unique.'));
+    $this->assertSession()->pageTextContains($this->t('The machine-readable name is already in use. It must be unique.'));
   }
 
   /**
@@ -255,16 +256,16 @@ class IntegrationTest extends WebTestBase {
     $index_datasource = 'entity:node';
 
     $this->drupalGet($settings_path);
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
     $edit = array(
       'status' => 1,
       'description' => $index_description,
     );
 
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertText($this->t('@name field is required.', array('@name' => $this->t('Index name'))));
-    $this->assertText($this->t('@name field is required.', array('@name' => $this->t('Machine-readable name'))));
-    $this->assertText($this->t('@name field is required.', array('@name' => $this->t('Data sources'))));
+    $this->assertSession()->pageTextContains($this->t('@name field is required.', array('@name' => $this->t('Index name'))));
+    $this->assertSession()->pageTextContains($this->t('@name field is required.', array('@name' => $this->t('Machine-readable name'))));
+    $this->assertSession()->pageTextContains($this->t('@name field is required.', array('@name' => $this->t('Data sources'))));
 
     $edit = array(
       'name' => $index_name,
@@ -276,10 +277,12 @@ class IntegrationTest extends WebTestBase {
     );
 
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertText($this->t('Please configure the used datasources.'));
+    $this->assertSession()->pageTextContains($this->t('Please configure the used datasources.'));
 
     $this->drupalPostForm(NULL, array(), $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->checkForMetaRefresh();
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
+
     // @todo Make this work correctly.
     // $this->assertUrl($this->getIndexPath('fields/add'), array(), 'Correct redirect to index page.');
     $this->assertHtmlEscaped($index_name);
@@ -290,12 +293,12 @@ class IntegrationTest extends WebTestBase {
     $index = $this->getIndex(TRUE);
 
     if ($this->assertTrue($index, 'Index was correctly created.')) {
-      $this->assertEqual($index->label(), $edit['name'], 'Name correctly inserted.');
-      $this->assertEqual($index->id(), $edit['id'], 'Index ID correctly inserted.');
+      $this->assertEquals($index->label(), $edit['name'], 'Name correctly inserted.');
+      $this->assertEquals($index->id(), $edit['id'], 'Index ID correctly inserted.');
       $this->assertTrue($index->status(), 'Index status correctly inserted.');
-      $this->assertEqual($index->getDescription(), $edit['description'], 'Index ID correctly inserted.');
-      $this->assertEqual($index->getServerId(), $edit['server'], 'Index server ID correctly inserted.');
-      $this->assertEqual($index->getDatasourceIds()[0], $index_datasource, 'Index datasource id correctly inserted.');
+      $this->assertEquals($index->getDescription(), $edit['description'], 'Index ID correctly inserted.');
+      $this->assertEquals($index->getServerId(), $edit['server'], 'Index server ID correctly inserted.');
+      $this->assertEquals($index->getDatasourceIds()[0], $index_datasource, 'Index datasource id correctly inserted.');
     }
     else {
       // Since none of the other tests would work, bail at this point.
@@ -307,10 +310,10 @@ class IntegrationTest extends WebTestBase {
     $edit['id'] = $index2_id;
     unset($edit['server']);
     $this->drupalPostForm($settings_path, $edit, $this->t('Save and add fields'));
-    $this->assertText($this->t('Please configure the used datasources.'));
+    $this->assertSession()->pageTextContains($this->t('Please configure the used datasources.'));
 
     $this->drupalPostForm(NULL, array(), $this->t('Save and add fields'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
     $this->indexStorage->resetCache(array($index2_id));
     $index = $this->indexStorage->load($index2_id);
     $this->assertUrl($index->toUrl('add-fields'), array(), 'Correct redirect to index fields page.');
@@ -336,19 +339,19 @@ class IntegrationTest extends WebTestBase {
 
     // Try to submit an index with a duplicate machine name.
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertText($this->t('The machine-readable name is already in use. It must be unique.'));
+    $this->assertSession()->pageTextContains($this->t('The machine-readable name is already in use. It must be unique.'));
 
     // Try to submit an index with a duplicate machine name after form
     // rebuilding via datasource submit.
     $this->drupalPostForm(NULL, $edit, array('path' => $index_add_page, 'triggering_element' => array('datasources_configure' => t('Configure'))));
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertText($this->t('The machine-readable name is already in use. It must be unique.'));
+    $this->assertSession()->pageTextContains($this->t('The machine-readable name is already in use. It must be unique.'));
 
     // Try to submit an index with a duplicate machine name after form
     // rebuilding via datasource submit using AJAX.
     $this->drupalPostAjaxForm(NULL, $edit, array('datasources_configure' => t('Configure')));
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertText($this->t('The machine-readable name is already in use. It must be unique.'));
+    $this->assertSession()->pageTextContains($this->t('The machine-readable name is already in use. It must be unique.'));
   }
 
   /**
@@ -360,7 +363,7 @@ class IntegrationTest extends WebTestBase {
 
     // Check if it's possible to change the machine name.
     $elements = $this->xpath('//form[@id="search-api-server-edit-form"]/div[contains(@class, "form-item-id")]/input[@disabled]');
-    $this->assertEqual(count($elements), 1, 'Machine name cannot be changed.');
+    $this->assertEquals(count($elements), 1, 'Machine name cannot be changed.');
 
     $tracked_items_before = $this->countTrackedItems();
 
@@ -372,15 +375,15 @@ class IntegrationTest extends WebTestBase {
     /** @var $index \Drupal\search_api\IndexInterface */
     $index = $this->indexStorage->load($this->indexId);
     $remaining = $index->getTrackerInstance()->getRemainingItemsCount();
-    $this->assertEqual(0, $remaining, 'Index was not scheduled for re-indexing when saving its server.');
+    $this->assertEquals(0, $remaining, 'Index was not scheduled for re-indexing when saving its server.');
 
     $this->setReturnValue('backend', 'postUpdate', TRUE);
     $this->drupalPostForm($path, $edit, $this->t('Save'));
 
     $tracked_items = $this->countTrackedItems();
     $remaining = $index->getTrackerInstance()->getRemainingItemsCount();
-    $this->assertEqual($tracked_items, $remaining, 'Backend could trigger re-indexing upon save.');
-    $this->assertEqual($tracked_items_before, $tracked_items, 'Items are still tracked after re-indexing was triggered.');
+    $this->assertEquals($tracked_items, $remaining, 'Backend could trigger re-indexing upon save.');
+    $this->assertEquals($tracked_items_before, $tracked_items, 'Items are still tracked after re-indexing was triggered.');
   }
 
   /**
@@ -393,15 +396,15 @@ class IntegrationTest extends WebTestBase {
 
     // Check if it's possible to change the machine name.
     $elements = $this->xpath('//form[@id="search-api-index-edit-form"]/div[contains(@class, "form-item-id")]/input[@disabled]');
-    $this->assertEqual(count($elements), 1, 'Machine name cannot be changed.');
+    $this->assertEquals(count($elements), 1, 'Machine name cannot be changed.');
 
     // Test the AJAX functionality for configuring the tracker.
     $edit = array('tracker' => 'search_api_test');
     $this->drupalPostAjaxForm(NULL, $edit, 'tracker_configure');
     $edit['tracker_config[foo]'] = 'foobar';
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertResponse(200);
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     // Verify that everything was changed correctly.
     $index = $this->getIndex(TRUE);
@@ -411,14 +414,14 @@ class IntegrationTest extends WebTestBase {
       'foo' => 'foobar',
       'dependencies' => array(),
     );
-    $this->assertEqual($tracker->getConfiguration(), $configuration, 'Tracker config was successfully saved.');
-    $this->assertEqual($this->countTrackedItems(), $tracked_items, 'Items are still correctly tracked.');
+    $this->assertEquals($tracker->getConfiguration(), $configuration, 'Tracker config was successfully saved.');
+    $this->assertEquals($this->countTrackedItems(), $tracked_items, 'Items are still correctly tracked.');
 
     // Revert back to the default tracker for the rest of the test.
     $edit = array('tracker' => 'default');
     $this->drupalPostForm($edit_path, $edit, $this->t('Save'));
-    $this->assertResponse(200);
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
     $index = $this->getIndex(TRUE);
     $tracker = $index->getTrackerInstance();
     $this->assertTrue($tracker instanceof Basic, 'Tracker was successfully switched.');
@@ -435,13 +438,13 @@ class IntegrationTest extends WebTestBase {
     );
 
     $this->drupalPostForm('admin/config/search/search-api/add-index', $edit, $this->t('Save'));
-    $this->assertResponse(200);
-    $this->assertText($this->t('Please configure the used datasources.'));
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains($this->t('Please configure the used datasources.'));
 
     $this->drupalPostForm(NULL, array(), $this->t('Save'));
-    $this->assertResponse(200);
-    $this->assertText($this->t('The index was successfully saved.'));
-    $this->assertText($edit['name']);
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($edit['name']);
   }
 
   /**
@@ -451,13 +454,13 @@ class IntegrationTest extends WebTestBase {
     $this->drupalGet('admin/config/search/search-api/server/' . $this->serverId . '/edit');
 
     $this->drupalGet('admin/config/search/search-api');
-    $this->assertResponse(200);
-    $this->assertRaw($this->t('Enabled'));
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->responseContains($this->t('Enabled'));
 
     $this->setReturnValue('backend', 'isAvailable', FALSE);
     $this->drupalGet('admin/config/search/search-api');
-    $this->assertResponse(200);
-    $this->assertRaw($this->t('Unavailable'));
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->responseContains($this->t('Unavailable'));
 
     $this->setReturnValue('backend', 'isAvailable', TRUE);
   }
@@ -470,7 +473,7 @@ class IntegrationTest extends WebTestBase {
   protected function checkContentEntityTracking() {
     // Initially there should be no tracked items, because there are no nodes.
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 0, 'No items are tracked yet.');
+    $this->assertEquals($tracked_items, 0, 'No items are tracked yet.');
 
     // Add two articles and two pages (one of them "invisible" to Search API).
     $article1 = $this->drupalCreateNode(array('type' => 'article'));
@@ -493,12 +496,12 @@ class IntegrationTest extends WebTestBase {
     // The 3 new nodes without "search_api_skip_tracking" property set should
     // have been added to the tracking table immediately.
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 3, 'Three items are tracked.');
+    $this->assertEquals($tracked_items, 3, 'Three items are tracked.');
 
     $this->getCalledMethods('backend');
     $page2->delete();
     $methods = $this->getCalledMethods('backend');
-    $this->assertEqual($methods, array(), 'Tracking of a delete operation could successfully be prevented.');
+    $this->assertEquals($methods, array(), 'Tracking of a delete operation could successfully be prevented.');
 
     // Test disabling the index.
     $settings_path = $this->getIndexPath('edit');
@@ -510,10 +513,10 @@ class IntegrationTest extends WebTestBase {
       'datasource_configs[entity:node][bundles][selected][page]' => FALSE,
     );
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 0, 'No items are tracked.');
+    $this->assertEquals($tracked_items, 0, 'No items are tracked.');
 
     // Test re-enabling the index.
     $this->drupalGet($settings_path);
@@ -525,10 +528,10 @@ class IntegrationTest extends WebTestBase {
       'datasource_configs[entity:node][bundles][selected][page]' => TRUE,
     );
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 3, 'Three items are tracked.');
+    $this->assertEquals($tracked_items, 3, 'Three items are tracked.');
 
     // Uncheck "default" and don't select any bundles. This should remove all
     // items from the tracking table.
@@ -539,10 +542,10 @@ class IntegrationTest extends WebTestBase {
       'datasource_configs[entity:node][bundles][selected][page]' => FALSE,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 0, 'No items are tracked.');
+    $this->assertEquals($tracked_items, 0, 'No items are tracked.');
 
     // Leave "default" unchecked and select the "article" bundle. This should
     // re-add the two articles to the tracking table.
@@ -553,10 +556,10 @@ class IntegrationTest extends WebTestBase {
       'datasource_configs[entity:node][bundles][selected][page]' => FALSE,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 2, 'Two items are tracked.');
+    $this->assertEquals($tracked_items, 2, 'Two items are tracked.');
 
     // Leave "default" unchecked and select only the "page" bundle. This should
     // result in only the page being present in the tracking table.
@@ -567,10 +570,10 @@ class IntegrationTest extends WebTestBase {
       'datasource_configs[entity:node][bundles][selected][page]' => TRUE,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 1, 'One item is tracked.');
+    $this->assertEquals($tracked_items, 1, 'One item is tracked.');
 
     // Check "default" again and select the "article" bundle. This shouldn't
     // change the tracking table, which should still only contain the page.
@@ -581,10 +584,10 @@ class IntegrationTest extends WebTestBase {
       'datasource_configs[entity:node][bundles][selected][page]' => FALSE,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 1, 'One item is tracked.');
+    $this->assertEquals($tracked_items, 1, 'One item is tracked.');
 
     // Leave "default" checked but now select only the "page" bundle. This
     // should result in only the articles being tracked.
@@ -595,28 +598,28 @@ class IntegrationTest extends WebTestBase {
       'datasource_configs[entity:node][bundles][selected][page]' => TRUE,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 2, 'Two items are tracked.');
+    $this->assertEquals($tracked_items, 2, 'Two items are tracked.');
 
     // Index items, then check whether updating an article is handled correctly.
     $this->getCalledMethods('backend');
     $article1->save();
     $methods = $this->getCalledMethods('backend');
-    $this->assertEqual($methods, array('indexItems'), 'Update successfully tracked.');
+    $this->assertEquals($methods, array('indexItems'), 'Update successfully tracked.');
 
     $article1->search_api_skip_tracking = TRUE;
     $article1->save();
     $methods = $this->getCalledMethods('backend');
-    $this->assertEqual($methods, array(), 'Tracking of entity update successfully prevented.');
+    $this->assertEquals($methods, array(), 'Tracking of entity update successfully prevented.');
     unset($article1->search_api_skip_tracking);
 
     // Delete an article. That should remove it from the item table.
     $article1->delete();
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 1, 'One item is tracked.');
+    $this->assertEquals($tracked_items, 1, 'One item is tracked.');
   }
 
   /**
@@ -657,7 +660,7 @@ class IntegrationTest extends WebTestBase {
       'status[transliteration]' => 1,
     );
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertText($this->t('The indexing workflow was successfully edited.'));
+    $this->assertSession()->pageTextContains($this->t('The indexing workflow was successfully edited.'));
   }
 
   /**
@@ -672,7 +675,7 @@ class IntegrationTest extends WebTestBase {
       'type' => '_content_',
     );
     $this->drupalGet('admin/structure/types/add');
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
     $this->drupalPostForm(NULL, $edit, $this->t('Save and manage fields'));
 
     // Add a field to that content type with funky chars.
@@ -704,7 +707,7 @@ class IntegrationTest extends WebTestBase {
     $this->drupalGet($this->getIndexPath('edit'));
     $this->assertHtmlEscaped($content_type_name);
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $this->addField(NULL, 'rendered_item');
     $this->assertHtmlEscaped($content_type_name);
@@ -717,7 +720,7 @@ class IntegrationTest extends WebTestBase {
     // Make sure that hidden properties are not displayed.
     $url_options['query']['datasource'] = '';
     $this->drupalGet($this->getIndexPath('fields/add'), $url_options);
-    $this->assertNoText($this->t('Node access information'));
+    $this->assertSession()->pageTextNotContains($this->t('Node access information'));
 
     $fields = array(
       'nid' => $this->t('ID'),
@@ -734,7 +737,7 @@ class IntegrationTest extends WebTestBase {
 
     $this->assertTrue(empty($fields['nid']), 'Field changes have not been persisted.');
     $this->drupalPostForm($this->getIndexPath('fields'), array(), $this->t('Save changes'));
-    $this->assertText($this->t('The changes were successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The changes were successfully saved.'));
 
     $index = $this->getIndex(TRUE);
     $fields = $index->getFields();
@@ -746,24 +749,24 @@ class IntegrationTest extends WebTestBase {
     $path = $this->getIndexPath('fields/add');
     $url_options = array('query' => array('datasource' => 'entity:node'));
     $this->drupalGet($path, $url_options);
-    $this->assertNoRaw('property_path=type', 'Properties of the content type object cannot be indexed.');
+    $this->assertSession()->responseNotContains('property_path=type', 'Properties of the content type object cannot be indexed.');
 
     // The "Content access" processor correctly marked fields as locked.
     if ($this->assertTrue(!empty($fields['uid']), 'uid field is indexed.')) {
       $this->assertTrue($fields['uid']->isIndexedLocked(), 'uid field is locked.');
       $this->assertTrue($fields['uid']->isTypeLocked(), 'uid field is type-locked.');
-      $this->assertEqual($fields['uid']->getType(), 'integer', 'uid field has type integer.');
+      $this->assertEquals($fields['uid']->getType(), 'integer', 'uid field has type integer.');
     }
     if ($this->assertTrue(!empty($fields['status']), 'status field is indexed.')) {
       $this->assertTrue($fields['status']->isIndexedLocked(), 'status field is locked.');
       $this->assertTrue($fields['status']->isTypeLocked(), 'status field is type-locked.');
-      $this->assertEqual($fields['status']->getType(), 'boolean', 'status field has type boolean.');
+      $this->assertEquals($fields['status']->getType(), 'boolean', 'status field has type boolean.');
     }
 
     // Check that a 'parent_data_type.data_type' Search API field type => data
     // type mapping relationship works.
     if ($this->assertTrue(!empty($fields['body']), 'body field is indexed.')) {
-      $this->assertEqual($fields['body']->getType(), 'text', 'Complex field mapping relationship works.');
+      $this->assertEquals($fields['body']->getType(), 'text', 'Complex field mapping relationship works.');
     }
 
     $edit = array(
@@ -774,20 +777,20 @@ class IntegrationTest extends WebTestBase {
       'fields[revision_log][type]' => 'search_api_test',
     );
     $this->drupalPostForm($this->getIndexPath('fields'), $edit, $this->t('Save changes'));
-    $this->assertText($this->t('The changes were successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The changes were successfully saved.'));
 
     $index = $this->getIndex(TRUE);
     $fields = $index->getFields();
 
     if ($this->assertTrue(!empty($fields['new_id']), 'title field is indexed.')) {
-      $this->assertEqual($fields['new_id']->getLabel(), $edit['fields[title][title]'], 'title field title is saved.');
-      $this->assertEqual($fields['new_id']->getFieldIdentifier(), $edit['fields[title][id]'], 'title field id value is saved.');
-      $this->assertEqual($fields['new_id']->getType(), $edit['fields[title][type]'], 'title field type is text.');
-      $this->assertEqual($fields['new_id']->getBoost(), $edit['fields[title][boost]'], 'title field boost value is 21.');
+      $this->assertEquals($fields['new_id']->getLabel(), $edit['fields[title][title]'], 'title field title is saved.');
+      $this->assertEquals($fields['new_id']->getFieldIdentifier(), $edit['fields[title][id]'], 'title field id value is saved.');
+      $this->assertEquals($fields['new_id']->getType(), $edit['fields[title][type]'], 'title field type is text.');
+      $this->assertEquals($fields['new_id']->getBoost(), $edit['fields[title][boost]'], 'title field boost value is 21.');
     }
 
     if ($this->assertTrue(!empty($fields['revision_log']), 'revision_log field is indexed.')) {
-      $this->assertEqual($fields['revision_log']->getType(), $edit['fields[revision_log][type]'], 'revision_log field type is search_api_test.');
+      $this->assertEquals($fields['revision_log']->getType(), $edit['fields[revision_log][type]'], 'revision_log field type is search_api_test.');
     }
 
     // Reset field values to original.
@@ -796,7 +799,7 @@ class IntegrationTest extends WebTestBase {
       'fields[new_id][id]' => 'title',
     );
     $this->drupalPostForm($this->getIndexPath('fields'), $edit, $this->t('Save changes'));
-    $this->assertText($this->t('The changes were successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The changes were successfully saved.'));
   }
 
   /**
@@ -815,12 +818,12 @@ class IntegrationTest extends WebTestBase {
 
       // Make sure we display the right icon and fallback column.
       if (strpos($label, 'Unsupported') === 0) {
-        $this->assertEqual($icon, 'error.svg', 'An error icon is shown for unsupported data types.');
+        $this->assertEquals($icon, 'error.svg', 'An error icon is shown for unsupported data types.');
         $this->assertNotEqual($fallback, '', 'The fallback data type label is not empty for unsupported data types.');
       }
       else {
-        $this->assertEqual($icon, 'check.svg', 'A check icon is shown for supported data types.');
-        $this->assertEqual($fallback, '', 'The fallback data type label is empty for supported data types.');
+        $this->assertEquals($icon, 'check.svg', 'A check icon is shown for supported data types.');
+        $this->assertEquals($fallback, '', 'The fallback data type label is empty for supported data types.');
       }
     }
   }
@@ -850,12 +853,12 @@ class IntegrationTest extends WebTestBase {
     // Unfortunately it doesn't seem possible to specify the clicked button by
     // anything other than label, so we have to pass it as extra POST data.
     $combined_property_path = Utility::createCombinedId($datasource_id, $property_path);
-    $this->assertRaw('name="' . $combined_property_path . '"');
+    $this->assertSession()->responseContains('name="' . $combined_property_path . '"');
     $post = '&' . $this->serializePostValues(array($combined_property_path => $this->t('Add')));
     $this->drupalPostForm(NULL, array(), NULL, array(), array(), NULL, $post);
     if ($label) {
       $args['%label'] = $label;
-      $this->assertRaw($this->t('Field %label was added to the index.', $args));
+      $this->assertSession()->responseContains($this->t('Field %label was added to the index.', $args));
     }
   }
 
@@ -917,8 +920,8 @@ class IntegrationTest extends WebTestBase {
     // Remove a field and make sure that doing so does not remove the search
     // index.
     $this->drupalGet('admin/structure/types/manage/article/fields/node.article.field_link/delete');
-    $this->assertNoText(t('The listed configuration will be deleted.'));
-    $this->assertText(t('Search index'));
+    $this->assertSession()->pageTextNotContains(t('The listed configuration will be deleted.'));
+    $this->assertSession()->pageTextContains(t('Search index'));
 
     $this->drupalPostForm(NULL, array(), t('Delete'));
     $this->drupalGet('admin/structure/types/manage/article/fields/node.article.field_image/delete');
@@ -929,8 +932,8 @@ class IntegrationTest extends WebTestBase {
     }
 
     $this->drupalGet($this->getIndexPath('fields'));
-    $this->assertResponse(200);
-    $this->assertNoText('field_link', 'The Link field was removed from the index.');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextNotContains('field_link', 'The Link field was removed from the index.');
     $this->assertFieldByName('fields[field_image][id]', 'field_image', 'The Image field was not removed from the index.');
 
     $field_dependencies = \Drupal::config('search_api.index.' . $this->indexId)->get('dependencies.config');
@@ -961,7 +964,7 @@ class IntegrationTest extends WebTestBase {
   protected function checkUnsavedChanges() {
     $this->addField('entity:node', 'changed', $this->t('Changed'));
     $this->drupalGet($this->getIndexPath('fields'));
-    $this->assertText($this->t('You have unsaved changes.'));
+    $this->assertSession()->pageTextContains($this->t('You have unsaved changes.'));
 
     // Log in a different admin user.
     $this->drupalLogin($this->adminUser2);
@@ -981,23 +984,23 @@ class IntegrationTest extends WebTestBase {
     $message_parts = explode('@age', $message);
 
     $this->drupalGet($this->getIndexPath('fields/add'));
-    $this->assertRaw($message_parts[0]);
-    $this->assertRaw($message_parts[1]);
+    $this->assertSession()->responseContains($message_parts[0]);
+    $this->assertSession()->responseContains($message_parts[1]);
     $this->assertFalse($this->xpath('//input[not(@disabled)]'));
     $this->drupalGet($this->getIndexPath('fields/rendered_item/edit'));
-    $this->assertRaw($message_parts[0]);
-    $this->assertRaw($message_parts[1]);
+    $this->assertSession()->responseContains($message_parts[0]);
+    $this->assertSession()->responseContains($message_parts[1]);
     $this->assertFalse($this->xpath('//input[not(@disabled)]'));
     $this->drupalGet($this->getIndexPath('fields'));
-    $this->assertRaw($message_parts[0]);
-    $this->assertRaw($message_parts[1]);
+    $this->assertSession()->responseContains($message_parts[0]);
+    $this->assertSession()->responseContains($message_parts[1]);
     $this->assertFalse($this->xpath('//input[not(@disabled)]'));
     if ($this->assertTrue(preg_match('#fields/break-lock">([^<>]*?)</a>#', $message, $m))) {
       $this->clickLink($m[1]);
     }
-    $this->assertRaw($this->t('By breaking this lock, any unsaved changes made by @user will be lost.', $args));
+    $this->assertSession()->responseContains($this->t('By breaking this lock, any unsaved changes made by @user will be lost.', $args));
     $this->drupalPostForm(NULL, array(), $this->t('Break lock'));
-    $this->assertText($this->t('The lock has been broken. You may now edit this search index.'));
+    $this->assertSession()->pageTextContains($this->t('The lock has been broken. You may now edit this search index.'));
     // Make sure the field has not been added to the index.
     $index = $this->getIndex(TRUE);
     $fields = $index->getFields();
@@ -1009,7 +1012,7 @@ class IntegrationTest extends WebTestBase {
       $url_target = $this->getAbsoluteUrl($links[0]['href']);
       $this->drupalGet($url_target);
     }
-    $this->assertText($this->t('You have unsaved changes.'));
+    $this->assertSession()->pageTextContains($this->t('You have unsaved changes.'));
     $this->drupalPostForm(NULL, array(), $this->t('Cancel'));
 
     $this->assertTrue(!empty($fields['title']), 'The title field has not been removed from the index.');
@@ -1055,7 +1058,7 @@ class IntegrationTest extends WebTestBase {
     $edit = array('field__reference_field_[0][target_id]' => 'Something (2)');
     $this->drupalPostForm('node/2/edit', $edit, $this->t('Save and keep published'));
     $indexed_values = \Drupal::state()->get("search_api_test.backend.indexed.{$this->indexId}", array());
-    $this->assertEqual(array(2), $indexed_values['entity:node/2:en']['field__reference_field_'], 'Correct value indexed for nested non-base field.');
+    $this->assertEquals(array(2), $indexed_values['entity:node/2:en']['field__reference_field_'], 'Correct value indexed for nested non-base field.');
   }
 
   /**
@@ -1077,12 +1080,12 @@ class IntegrationTest extends WebTestBase {
           'title',
         ),
       );
-      $this->assertEqual($expected, $configuration, 'Title field enabled for ignore case filter.');
+      $this->assertEquals($expected, $configuration, 'Title field enabled for ignore case filter.');
     }
     catch (SearchApiException $e) {
       $this->fail('"Ignore case" processor not enabled.');
     }
-    $this->assertText($this->t('The indexing workflow was successfully edited.'));
+    $this->assertSession()->pageTextContains($this->t('The indexing workflow was successfully edited.'));
   }
 
   /**
@@ -1091,7 +1094,7 @@ class IntegrationTest extends WebTestBase {
   public function configureFilterPage() {
     $edit = array();
     $this->drupalPostForm($this->getIndexPath('processors'), $edit, $this->t('Save'));
-    $this->assertText('No values were changed.');
+    $this->assertSession()->pageTextContains('No values were changed.');
   }
 
   /**
@@ -1105,17 +1108,17 @@ class IntegrationTest extends WebTestBase {
     // Enable just the ignore case processor, just to have a clean default state
     // before testing.
     $this->drupalPostForm($this->getIndexPath('processors'), $edit, $this->t('Save'));
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
 
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertResponse(200);
-    $this->assertText($this->t('No values were changed.'));
-    $this->assertNoText($this->t('All content was scheduled for reindexing so the new settings can take effect.'));
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains($this->t('No values were changed.'));
+    $this->assertSession()->pageTextNotContains($this->t('All content was scheduled for reindexing so the new settings can take effect.'));
 
     $edit['processors[ignorecase][settings][fields][title]'] = FALSE;
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
-    $this->assertResponse(200);
-    $this->assertText($this->t('All content was scheduled for reindexing so the new settings can take effect.'));
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains($this->t('All content was scheduled for reindexing so the new settings can take effect.'));
   }
 
   /**
@@ -1132,12 +1135,12 @@ class IntegrationTest extends WebTestBase {
     // Change the boost of the field.
     $fields_path = $this->getIndexPath('fields');
     $this->drupalPostForm($fields_path, array('fields[url][boost]' => '8.0'), $this->t('Save changes'));
-    $this->assertText('The changes were successfully saved.');
+    $this->assertSession()->pageTextContains('The changes were successfully saved.');
     $this->assertOptionSelected('edit-fields-url-boost', '8.0', 'Boost is correctly saved.');
 
     // Change the type of the field.
     $this->drupalPostForm($fields_path, array('fields[url][type]' => 'text'), $this->t('Save changes'));
-    $this->assertText('The changes were successfully saved.');
+    $this->assertSession()->pageTextContains('The changes were successfully saved.');
     $this->assertOptionSelected('edit-fields-url-type', 'text', 'Type is correctly saved.');
   }
 
@@ -1164,20 +1167,20 @@ class IntegrationTest extends WebTestBase {
       'datasource_configs[entity:node][bundles][selected][page]' => TRUE,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $index = $this->getIndex(TRUE);
     $remaining_before = $this->countRemainingItems();
 
     $this->drupalGet($index_path);
 
-    $this->assertNoText($this->t('Index now'), 'The "Index now" button is not displayed.');
+    $this->assertSession()->pageTextNotContains($this->t('Index now'), 'The "Index now" button is not displayed.');
 
     // Also try indexing via the API to make sure it is really not possible.
     $indexed = $index->indexItems();
-    $this->assertEqual(0, $indexed, 'No items were indexed after setting the index to "read only".');
+    $this->assertEquals(0, $indexed, 'No items were indexed after setting the index to "read only".');
     $remaining_after = $this->countRemainingItems();
-    $this->assertEqual($remaining_before, $remaining_after, 'No items were indexed after setting the index to "read only".');
+    $this->assertEquals($remaining_before, $remaining_after, 'No items were indexed after setting the index to "read only".');
 
     // Disable "read only" and verify indexing now works again.
     $edit = array(
@@ -1187,12 +1190,13 @@ class IntegrationTest extends WebTestBase {
       'datasource_configs[entity:node][bundles][selected][page]' => FALSE,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $this->drupalPostForm($index_path, array(), $this->t('Index now'));
+    $this->checkForMetaRefresh();
 
     $remaining_after = $index->getTrackerInstance()->getRemainingItemsCount();
-    $this->assertEqual(0, $remaining_after, 'Items were indexed after removing the "read only" flag.');
+    $this->assertEquals(0, $remaining_after, 'Items were indexed after removing the "read only" flag.');
   }
 
   /**
@@ -1210,12 +1214,12 @@ class IntegrationTest extends WebTestBase {
       'status' => FALSE,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual(0, $tracked_items, 'No items are tracked after disabling the index.');
+    $this->assertEquals(0, $tracked_items, 'No items are tracked after disabling the index.');
     $tracked_items = \Drupal::database()->select('search_api_item', 'i')->countQuery()->execute()->fetchField();
-    $this->assertEqual(0, $tracked_items, 'No items left in tracking table.');
+    $this->assertEquals(0, $tracked_items, 'No items left in tracking table.');
 
     // @todo Also try to verify whether the items got deleted from the server.
 
@@ -1224,10 +1228,10 @@ class IntegrationTest extends WebTestBase {
       'status' => TRUE,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual(2, $tracked_items, 'After enabling the index, 2 items are tracked.');
+    $this->assertEquals(2, $tracked_items, 'After enabling the index, 2 items are tracked.');
   }
 
   /**
@@ -1251,12 +1255,12 @@ class IntegrationTest extends WebTestBase {
       'datasources[entity:node]' => TRUE,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('Please configure the used datasources.'));
+    $this->assertSession()->pageTextContains($this->t('Please configure the used datasources.'));
     $this->drupalPostForm(NULL, array(), $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, $user_count + $node_count, 'Correct number of items tracked after enabling the "User" datasource.');
+    $this->assertEquals($tracked_items, $user_count + $node_count, 'Correct number of items tracked after enabling the "User" datasource.');
 
     // Disable indexing of users again.
     $edit = array(
@@ -1264,12 +1268,12 @@ class IntegrationTest extends WebTestBase {
       'datasources[entity:node]' => TRUE,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     $this->executeTasks();
 
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, $node_count, 'Correct number of items tracked after disabling the "User" datasource.');
+    $this->assertEquals($tracked_items, $node_count, 'Correct number of items tracked after disabling the "User" datasource.');
   }
 
   /**
@@ -1283,13 +1287,13 @@ class IntegrationTest extends WebTestBase {
     $index = $this->getIndex(TRUE);
 
     $node_count = \Drupal::entityQuery('node')->count()->execute();
-    $this->assertEqual($node_count, $this->countTrackedItems(), 'All nodes are correctly tracked by the index.');
+    $this->assertEquals($node_count, $this->countTrackedItems(), 'All nodes are correctly tracked by the index.');
 
     // Index all remaining items on the index.
     $index->indexItems();
 
     $remaining_items = $this->countRemainingItems();
-    $this->assertEqual($remaining_items, 0, 'All items have been successfully indexed.');
+    $this->assertEquals($remaining_items, 0, 'All items have been successfully indexed.');
 
     // Create a second search server.
     $this->createServer('test_server_2');
@@ -1300,11 +1304,11 @@ class IntegrationTest extends WebTestBase {
       'server' => $this->serverId,
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
-    $this->assertText($this->t('The index was successfully saved.'));
+    $this->assertSession()->pageTextContains($this->t('The index was successfully saved.'));
 
     // After saving the new index, we should have called reindex.
     $remaining_items = $this->countRemainingItems();
-    $this->assertEqual($remaining_items, $node_count, 'All items still need to be indexed.');
+    $this->assertEquals($remaining_items, $node_count, 'All items still need to be indexed.');
   }
 
   /**
@@ -1315,13 +1319,13 @@ class IntegrationTest extends WebTestBase {
 
     // Load confirmation form.
     $this->drupalGet('admin/config/search/search-api/server/' . $this->serverId . '/delete');
-    $this->assertResponse(200, 'Server delete page exists');
-    $this->assertRaw(t('Are you sure you want to delete the search server %name?', array('%name' => $server->label())), 'Deleting a server sks for confirmation.');
-    $this->assertText(t('Deleting a server will disable all its indexes and their searches.'), 'Correct warning is displayed when deleting a server.');
+    $this->assertSession()->statusCodeEquals(200, 'Server delete page exists');
+    $this->assertSession()->responseContains(t('Are you sure you want to delete the search server %name?', array('%name' => $server->label())), 'Deleting a server sks for confirmation.');
+    $this->assertSession()->pageTextContains(t('Deleting a server will disable all its indexes and their searches.'), 'Correct warning is displayed when deleting a server.');
 
     // Confirm deletion.
     $this->drupalPostForm(NULL, NULL, t('Delete'));
-    $this->assertRaw(t('The search server %name has been deleted.', array('%name' => $server->label())), 'The server was deleted.');
+    $this->assertSession()->responseContains(t('The search server %name has been deleted.', array('%name' => $server->label())), 'The server was deleted.');
     $this->assertFalse(Server::load($this->serverId), 'Server could not be found anymore.');
     $this->assertUrl('admin/config/search/search-api', array(), 'Correct redirect to search api overview page.');
 
@@ -1361,9 +1365,9 @@ class IntegrationTest extends WebTestBase {
    *   The raw string to check for.
    */
   protected function assertHtmlEscaped($string) {
-    $this->assertRaw(Html::escape($string));
-    $this->assertNoRaw(Html::escape(Html::escape($string)));
-    $this->assertNoRaw($string);
+    $this->assertSession()->responseContains(Html::escape($string));
+    $this->assertSession()->responseNotContains(Html::escape(Html::escape($string)));
+    $this->assertSession()->responseNotContains($string);
   }
 
 }
diff --git a/src/Tests/LanguageIntegrationTest.php b/tests/src/Functional/LanguageIntegrationTest.php
similarity index 79%
rename from src/Tests/LanguageIntegrationTest.php
rename to tests/src/Functional/LanguageIntegrationTest.php
index 7a02c9f..baf739b 100644
--- a/src/Tests/LanguageIntegrationTest.php
+++ b/tests/src/Functional/LanguageIntegrationTest.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\search_api\Tests;
+namespace Drupal\Tests\search_api\Functional;
 
 use Drupal\language\Entity\ConfigurableLanguage;
 use Drupal\search_api\Entity\Index;
@@ -50,7 +50,7 @@ class LanguageIntegrationTest extends WebTestBase {
 
     // Those 2 new nodes should be added to the tracking table immediately.
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 2, 'Two items are tracked.');
+    $this->assertEquals($tracked_items, 2, 'Two items are tracked.');
 
     // Add translations.
     $translation = array('title' => 'test NL', 'body' => 'NL body');
@@ -63,7 +63,7 @@ class LanguageIntegrationTest extends WebTestBase {
     // The translations should be tracked as well, so we have a total of 5
     // indexed items.
     $tracked_items = $this->countTrackedItems();
-    $this->assertEqual($tracked_items, 5, 'Five items are tracked.');
+    $this->assertEquals($tracked_items, 5, 'Five items are tracked.');
 
     // Clear index.
     $this->drupalGet($this->getIndexPath());
@@ -73,7 +73,8 @@ class LanguageIntegrationTest extends WebTestBase {
     // Make sure all 5 items are successfully indexed.
     $this->drupalGet($this->getIndexPath());
     $this->drupalPostForm(NULL, array(), $this->t('Index now'));
-    $this->assertText($this->t('Successfully indexed 5 items'));
+    $this->checkForMetaRefresh();
+    $this->assertSession()->pageTextContains($this->t('Successfully indexed 5 items'));
 
     // Change the datasource to disallow indexing of dutch.
     $form_values = array(
@@ -82,14 +83,16 @@ class LanguageIntegrationTest extends WebTestBase {
     );
     $this->drupalGet($this->getIndexPath('edit'));
     $this->drupalPostForm(NULL, $form_values, $this->t('Save'));
-    $this->assertResponse(200);
-    $this->assertText('The index was successfully saved.');
+    $this->checkForMetaRefresh();
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains('The index was successfully saved.');
 
     // Make sure that we only have 3 indexed items now. The 2 original nodes
     // + 1 translation in lolspeak, the 2 dutch translations should be ignored.
     $this->drupalGet($this->getIndexPath());
     $this->drupalPostForm(NULL, array(), $this->t('Index now'));
-    $this->assertText($this->t('Successfully indexed 3 items'));
+    $this->checkForMetaRefresh();
+    $this->assertSession()->pageTextContains($this->t('Successfully indexed 3 items'));
 
     // Change the datasource to only allow indexing of dutch.
     $form_values = array(
@@ -99,15 +102,18 @@ class LanguageIntegrationTest extends WebTestBase {
       'datasource_configs[entity:node][bundles][selected][article]' => 1,
     );
     $this->drupalGet($this->getIndexPath('edit'));
-    $this->drupalPostForm(NULL, $form_values, $this->t('Save'));
-    $this->assertResponse(200);
-    $this->assertText('The index was successfully saved.');
+    $this->submitForm($form_values, $this->t('Save'));
+    $this->checkForMetaRefresh();
+    $this->assertSession()->statusCodeEquals(200);
+    //Completed 1 of 1.
+    $this->assertSession()->pageTextContains('The index was successfully saved.');
 
     // Make sure that we only have 2 index items. The only indexed items should
     // be the dutch translations.
     $this->drupalGet($this->getIndexPath());
     $this->drupalPostForm(NULL, array(), $this->t('Index now'));
-    $this->assertText($this->t('Successfully indexed 2 items'));
+    $this->checkForMetaRefresh();
+    $this->assertSession()->pageTextContains($this->t('Successfully indexed 2 items'));
   }
 
   /**
diff --git a/src/Tests/WebTestBase.php b/tests/src/Functional/WebTestBase.php
similarity index 67%
rename from src/Tests/WebTestBase.php
rename to tests/src/Functional/WebTestBase.php
index d4fea72..1cef17b 100644
--- a/src/Tests/WebTestBase.php
+++ b/tests/src/Functional/WebTestBase.php
@@ -1,16 +1,17 @@
 <?php
 
-namespace Drupal\search_api\Tests;
+namespace Drupal\Tests\search_api\Functional;
 
+use Drupal\Component\Utility\Html;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\search_api\Entity\Index;
 use Drupal\search_api\Entity\Server;
-use Drupal\simpletest\WebTestBase as SimpletestWebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Provides the base class for web tests for Search API.
  */
-abstract class WebTestBase extends SimpletestWebTestBase {
+abstract class WebTestBase extends BrowserTestBase {
 
   use StringTranslationTrait;
 
@@ -32,6 +33,19 @@ abstract class WebTestBase extends SimpletestWebTestBase {
    */
   protected $adminUser;
 
+  /* The number of meta refresh redirects to follow, or NULL if unlimited.
+   *
+   * @var null|int
+   */
+  protected $maximumMetaRefreshCount = NULL;
+
+  /**
+   * The number of meta refresh redirects followed during ::drupalGet().
+   *
+   * @var int
+   */
+  protected $metaRefreshCount = 0;
+
   /**
    * The permissions of the admin user.
    *
@@ -177,7 +191,44 @@ abstract class WebTestBase extends SimpletestWebTestBase {
   protected function executeTasks() {
     $task_manager = \Drupal::getContainer()->get('search_api.task_manager');
     $task_manager->executeAllTasks();
-    $this->assertEqual(0, $task_manager->getTasksCount(), 'No more pending tasks.');
+    $this->assertEquals(0, $task_manager->getTasksCount(), 'No more pending tasks.');
+  }
+
+  protected function submitFormWithBatch(array $edit, $submit, $form_html_id = NULL) {
+    $original_url = $this->getSession()->getCurrentUrl();
+    $this->submitForm($edit, $submit, $form_html_id);
+    do {
+      $this->drupalGet($this->getSession()->getCurrentUrl());
+    } while ($original_url != $this->getSession()->getCurrentUrl());
+
+  }
+
+
+  /**
+   * Checks for meta refresh tag and if found call drupalGet() recursively.
+   *
+   * This function looks for the http-equiv attribute to be set to "Refresh" and
+   * is case-sensitive.
+   *
+   * @see https://www.drupal.org/node/2757023#comment-11445519
+   *
+   * @return string|false
+   *   Either the new page content or FALSE.
+   */
+  protected function checkForMetaRefresh() {
+    $refresh = $this->cssSelect('meta[http-equiv="Refresh"]');
+    if (!empty($refresh) && (!isset($this->maximumMetaRefreshCount) || $this->metaRefreshCount < $this->maximumMetaRefreshCount)) {
+      // Parse the content attribute of the meta tag for the format:
+      // "[delay]: URL=[page_to_redirect_to]".
+      if (preg_match('/\d+;\s*URL=(?<url>.*)/i', $refresh[0]->getAttribute('content'), $match)) {
+        $this->metaRefreshCount++;
+        $this->drupalGet($this->getAbsoluteUrl(Html::decodeEntities($match['url'])));
+        $this->checkForMetaRefresh();
+      }
+    }
+    // Flusch number of RefreshCount.
+    $this->metaRefreshCount = 0;
+    return FALSE;
   }
 
 }
