diff --git a/search_api_page.info.yml b/search_api_page.info.yml
index 39551b2..694d89e 100644
--- a/search_api_page.info.yml
+++ b/search_api_page.info.yml
@@ -1,7 +1,9 @@
 name: 'Search pages'
 description: 'Create search pages using Search API indexes.'
 dependencies:
-  - search_api
+  - search_api:search_api
+test_dependencies:
+  - facets:facets
 core: 8.x
 package: Search
 type: module
diff --git a/src/Controller/SearchApiPageController.php b/src/Controller/SearchApiPageController.php
index f13d5c3..b39fa77 100644
--- a/src/Controller/SearchApiPageController.php
+++ b/src/Controller/SearchApiPageController.php
@@ -59,8 +59,8 @@ class SearchApiPageController extends ControllerBase {
       $query = $search_api_index->query([
         'limit' => $search_api_page->getLimit(),
         'offset' => !is_null($request->get('page')) ? $request->get('page') * $search_api_page->getLimit() : 0,
-        'search id' => 'search_api_page:' . $search_api_page->id(),
       ]);
+      $query->setSearchID('search_api_page:' . $search_api_page->id());
 
       $parse_mode = \Drupal::getContainer()
         ->get('plugin.manager.search_api.parse_mode')
diff --git a/src/Plugin/facets/facet_source/SearchApiPage.php b/src/Plugin/facets/facet_source/SearchApiPage.php
deleted file mode 100644
index ec22387..0000000
--- a/src/Plugin/facets/facet_source/SearchApiPage.php
+++ /dev/null
@@ -1,162 +0,0 @@
-<?php
-
-namespace Drupal\search_api_page\Plugin\facets\facet_source;
-
-use Drupal\facets\FacetSource\SearchApiFacetSourceInterface;
-use Drupal\facets\Plugin\facets\facet_source\SearchApiBaseFacetSource;
-use Drupal\search_api\Entity\Index;
-use Drupal\search_api\Query\ResultSetInterface;
-use Drupal\search_api_page\Entity\SearchApiPage as SearchApiPageEntity;
-
-
-/**
- * Represents a facet source which represents search_api_page pages.
- *
- * Most of the work of actually getting a page is done in the deriver.
- *
- * @FacetsFacetSource(
- *   id = "search_api_page",
- *   deriver = "Drupal\search_api_page\Plugin\facets\facet_source\SearchApiPageDeriver"
- * )
- */
-class SearchApiPage extends SearchApiBaseFacetSource implements SearchApiFacetSourceInterface {
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityTypeManager|null
-   */
-  protected $entityTypeManager;
-
-  /**
-   * The typed data manager.
-   *
-   * @var \Drupal\Core\TypedData\TypedDataManager|null
-   */
-  protected $typedDataManager;
-
-  /**
-   * The config factory.
-   *
-   * @var \Drupal\Core\Config\ConfigFactoryInterface|null
-   */
-  protected $configFactory;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(array $configuration, $plugin_id, array $plugin_definition, $query_type_plugin_manager, $search_results_cache) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition, $query_type_plugin_manager, $search_results_cache);
-
-    // Load facet plugin definition and depending on those settings; load the
-    // corresponding search api page and load its index.
-    $page_id = $plugin_definition['search_api_page'];
-    /* @var $page \Drupal\search_api_page\SearchApiPageInterface */
-    $page = SearchApiPageEntity::load($page_id);
-    $this->index = Index::load($page->getIndex());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function fillFacetsWithResults($facets) {
-    // Check if there are results in the static cache.
-    $results = $this->searchApiResultsCache->getResults($this->pluginId);
-
-    // If there are no results, execute the search page and check for results
-    // again. This happens when a page or block is cached, so Search API has
-    // not fired an actual search.
-    if (!$results) {
-      list(, $search_api_page) = explode(':', $this->pluginId);
-      /* @var $search_api_page \Drupal\search_api_page\SearchApiPageInterface */
-      $search_api_page = SearchApiPageEntity::load($search_api_page);
-
-      /* @var $search_api_index \Drupal\search_api\IndexInterface */
-      $search_api_index = Index::load($search_api_page->getIndex());
-
-      // Create the query.
-      $query = $search_api_index->query([
-        'parse_mode' => 'direct',
-        'limit' => $search_api_page->getLimit(),
-        'offset' => isset($_GET['page']) ? $_GET['page'] : 0,
-        'search id' => 'search_api_page:' . $search_api_page->id(),
-      ]);
-
-      // Keys.
-      $keys = \Drupal::request()->get('keys');
-      if (!empty($keys)) {
-        $query->keys($keys);
-      }
-
-      // Index fields.
-      $query->setFulltextFields($search_api_page->getSearchedFields());
-
-      // Execute the query.
-      $results = $query->execute();
-    }
-
-    // If we got results from the cache, this is the first code executed in this
-    // method, so it's good to double check that we can actually work with
-    // $results.
-    if ($results instanceof ResultSetInterface) {
-      // Get our facet data from the results.
-      $facet_results = $results->getExtraData('search_api_facets');
-
-      // Loop over each facet and execute the build method from the given query
-      // type.
-      foreach ($facets as $facet) {
-        if (isset($facet_results[$facet->getFieldIdentifier()])) {
-          $configuration = array(
-            'query' => NULL,
-            'facet' => $facet,
-            'results' => $facet_results[$facet->getFieldIdentifier()],
-          );
-
-          // Get the Facet Specific Query Type so we can process the results
-          // using the build() function of the query type.
-          $query_type = $this->queryTypePluginManager->createInstance($facet->getQueryType(), $configuration);
-          $query_type->build();
-        }
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getPath() {
-    /* @var $search_api_page \Drupal\search_api_page\SearchApiPageInterface */
-    list(, $search_api_page_name) = explode(':', $this->pluginId);
-    $search_api_page = SearchApiPageEntity::load($search_api_page_name);
-    if ($search_api_page->getCleanUrl()) {
-      return '/' . $search_api_page->getPath() . '/' . \Drupal::request()->get('keys');
-    }
-    else {
-      return '/' . $search_api_page->getPath();
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isRenderedInCurrentRequest() {
-    $request = \Drupal::requestStack()->getMasterRequest();
-
-    $explode = explode('.', $request->get('_route'));
-    $prefix = isset($explode[0]) ? $explode[0] : '';
-    $id = isset($explode[2]) ? $explode[2] : '';
-    if (!empty($prefix) && !empty($id)) {
-      if ($prefix . ':' . $id == $this->pluginId) {
-        return TRUE;
-      }
-    }
-    return FALSE;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getIndex() {
-    return $this->index;
-  }
-}
diff --git a/src/Plugin/facets/facet_source/SearchApiPageDeriver.php b/src/Plugin/facets/facet_source/SearchApiPageDeriver.php
deleted file mode 100644
index 993f6d5..0000000
--- a/src/Plugin/facets/facet_source/SearchApiPageDeriver.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-namespace Drupal\search_api_page\Plugin\facets\facet_source;
-
-use Drupal\Core\Plugin\PluginBase;
-use Drupal\facets\FacetSource\FacetSourceDeriverBase;
-
-
-/**
- * Derives a facet source plugin definition for every search api page.
- *
- * The definition of this plugin happens in facet_source\SearchApiPage, in this
- * deriver class we're actually getting all possible pages and creating plugins
- * for each of them.
- *
- * @see \Drupal\search_api_page\Plugin\facets\facet_source\SearchApiPage
- */
-class SearchApiPageDeriver extends FacetSourceDeriverBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getDerivativeDefinitions($base_plugin_definition) {
-    $base_plugin_id = $base_plugin_definition['id'];
-
-    if (!isset($this->derivatives[$base_plugin_id])) {
-
-      $plugin_derivatives = [];
-
-      /* @var \Drupal\Core\Entity\EntityStorageInterface $page_storage */
-      $page_storage = $this->entityTypeManager->getStorage('search_api_page');
-      $all_pages = $page_storage->loadMultiple();
-
-      /* @var \Drupal\search_api_page\Entity\SearchApiPage */
-      foreach ($all_pages as $page) {
-        $machine_name = $page->id();
-
-        // Add plugin derivatives, they have 'search_api_page' as a special key
-        // in them, because of this, there needs to happen less explode() magic
-        // in the plugin class.
-        $plugin_derivatives[$machine_name] = [
-          'id' => $base_plugin_id . PluginBase::DERIVATIVE_SEPARATOR . $machine_name,
-          'label' => $this->t('Search api page: %page_name', ['%page_name' => $page->label()]),
-          'description' => $this->t('Provides a facet source.'),
-          'search_api_page' => $page->id(),
-        ] + $base_plugin_definition;
-
-        $sources[] = $this->t('Search api page: %page_name', ['%page_name' => $page->label()]);
-      }
-      uasort($plugin_derivatives, array($this, 'compareDerivatives'));
-
-      $this->derivatives[$base_plugin_id] = $plugin_derivatives;
-    }
-    return $this->derivatives[$base_plugin_id];
-  }
-
-}
diff --git a/src/Plugin/search_api/display/SearchApiPage.php b/src/Plugin/search_api/display/SearchApiPage.php
new file mode 100644
index 0000000..a5ad099
--- /dev/null
+++ b/src/Plugin/search_api/display/SearchApiPage.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Drupal\search_api_page\Plugin\search_api\display;
+
+use Drupal\search_api\Display\DisplayPluginBase;
+
+/**
+ * Represents a Views block display.
+ *
+ * @SearchApiDisplay(
+ *   id = "search_api_page",
+ *   deriver = "Drupal\search_api_page\Plugin\search_api\display\SearchApiPageDeriver"
+ * )
+ */
+class SearchApiPage extends DisplayPluginBase {
+
+}
diff --git a/src/Plugin/search_api/display/SearchApiPageDeriver.php b/src/Plugin/search_api/display/SearchApiPageDeriver.php
new file mode 100644
index 0000000..4a1721d
--- /dev/null
+++ b/src/Plugin/search_api/display/SearchApiPageDeriver.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Drupal\search_api_page\Plugin\search_api\display;
+
+use Drupal\search_api\Display\DisplayDeriverBase;
+use Drupal\search_api_page\Entity\SearchApiPage;
+
+/**
+ * Derives a display plugin definition for all pages.
+ */
+class SearchApiPageDeriver extends DisplayDeriverBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    if (isset($this->derivatives)) {
+      $this->derivatives;
+    }
+    $this->derivatives = array();
+
+    $pages = SearchApiPage::loadMultiple();
+    if (empty($pages)) {
+      return $this->derivatives;
+    }
+
+    $this->derivatives = $this->getDerivatives($pages, $base_plugin_definition);
+
+    return $this->derivatives;
+  }
+
+  /**
+   * Creates derived plugin definitions for pages.
+   *
+   * @param \Drupal\search_api_page\SearchApiPageInterface[] $pages
+   *   The pages to create plugins for.
+   * @param array $base_plugin_definition
+   *   The plugin definition for this plugin.
+   *
+   * @return array
+   *   Returns an array of plugin definitions, keyed by derivative ID.
+   */
+  protected function getDerivatives(array $pages, array $base_plugin_definition) {
+    $plugin_derivatives = [];
+
+    foreach ($pages as $page) {
+      $plugin_derivatives[$page->id()] = [
+        'label' => $page->label(),
+        'description' => $page->label(),
+        'index' => $page->getIndex(),
+        'path' => '/' . $page->getPath(),
+      ] + $base_plugin_definition;
+    }
+
+    return $plugin_derivatives;
+  }
+
+}
diff --git a/src/Tests/SearchApiPage.php b/src/Tests/SearchApiPage.php
deleted file mode 100644
index 382f8b9..0000000
--- a/src/Tests/SearchApiPage.php
+++ /dev/null
@@ -1,220 +0,0 @@
-<?php
-
-namespace Drupal\search_api_page\Tests;
-
-use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\search_api\Tests\ExampleContentTrait;
-use Drupal\search_api\Entity\Index;
-use Drupal\search_api\Entity\Server;
-use Drupal\simpletest\WebTestBase as SimpletestWebTestBase;
-
-/**
- * Provides web tests for Search API Pages.
- *
- * @group search_api_page
- */
-class SearchApiPage extends SimpletestWebTestBase {
-
-  use StringTranslationTrait;
-  use ExampleContentTrait;
-
-  /**
-   * Modules to enable for this test.
-   *
-   * @var string[]
-   */
-  public static $modules = ['search_api_page', 'node', 'search_api', 'search_api_db', 'block'];
-
-  /**
-   * An admin user used for this test.
-   *
-   * @var \Drupal\Core\Session\AccountInterface
-   */
-  protected $adminUser;
-
-  /**
-   * A user without any permission..
-   *
-   * @var \Drupal\Core\Session\AccountInterface
-   */
-  protected $unauthorizedUser;
-
-  /**
-   * The anonymous user used for this test.
-   *
-   * @var \Drupal\Core\Session\AccountInterface
-   */
-  protected $anonymousUser;
-
-  /**
-   * A search database server.
-   *
-   * @var \Drupal\search_api\Entity\Server
-   */
-  protected $server = NULL;
-
-  /**
-   * A search index.
-   *
-   * @var \Drupal\search_api\Entity\Index
-   */
-  protected $index = NULL;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setUp() {
-    parent::setUp();
-
-    // Create the users used for the tests.
-    $this->adminUser = $this->drupalCreateUser([
-      'administer search_api',
-      'administer search_api_page',
-      'access administration pages',
-      'administer nodes',
-      'access content overview',
-      'administer content types',
-      'administer blocks',
-      'view search api pages',
-    ]);
-    $this->unauthorizedUser = $this->drupalCreateUser();
-    $this->anonymousUser = $this->drupalCreateUser(['view search api pages']);
-
-    // Create article content type and content.
-    $this->drupalCreateContentType(array('type' => 'article'));
-    for ($i = 1; $i < 50; $i++) {
-      $this->drupalCreateNode(array(
-        'title' => 'Item number' . $i,
-        'type' => 'article',
-        'body' => [['value' => 'Body number' . $i]]));
-    }
-  }
-
-  /**
-   * Test search api pages.
-   */
-  public function testSearchApiPage() {
-    $this->drupalLogin($this->adminUser);
-
-    // Setup search api server and index.
-    $this->setupSearchAPI();
-
-    $this->drupalGet('admin/config/search/search-api-pages');
-    $this->assertResponse(200);
-
-    $step1 = array(
-      'label' => 'Search',
-      'id' => 'search',
-      'index' => $this->index->id(),
-    );
-    $this->drupalPostForm('admin/config/search/search-api-pages/add', $step1, 'Next');
-
-    $step2 = array(
-      'path' => 'search',
-    );
-    $this->drupalPostForm(NULL, $step2, 'Save');
-
-    $this->drupalGet('search');
-    $this->assertRaw('Enter the terms you wish to search for.');
-    $this->assertNoRaw('Your search yielded no results.');
-    $this->assertResponse(200);
-
-    $this->drupalLogout();
-    $this->drupalLogin($this->unauthorizedUser);
-    $this->drupalGet('search');
-    $this->assertResponse(403);
-
-    $this->drupalLogout();
-    $this->drupalLogin($this->anonymousUser);
-    $this->drupalGet('search');
-    $this->assertResponse(200);
-
-    $this->drupalLogout();
-    $this->drupalLogin($this->adminUser);
-
-    $this->drupalGet('search/nothing-found');
-    $this->assertRaw('Enter the terms you wish to search for.');
-    $this->assertRaw('Your search yielded no results.');
-    $this->drupalGet('search');
-    $this->assertNoRaw('Your search yielded no results.');
-
-    $this->drupalPostForm('admin/config/search/search-api-pages/search', array('show_all_when_no_keys' => TRUE, 'show_search_form' => FALSE), 'Save');
-    $this->drupalGet('search');
-    $this->assertNoRaw('Your search yielded no results.');
-    $this->assertNoRaw('Enter the terms you wish to search for.');
-    $this->assertText('49 results found');
-
-    $this->drupalGet('search/number10');
-    $this->assertText('1 result found');
-
-    $this->drupalPostForm('admin/config/search/search-api-pages/search', array('show_search_form' => TRUE), 'Save');
-
-    $this->drupalGet('search/number11');
-    $this->assertText('1 result found');
-    $this->assertRaw('name="keys" value="number11"');
-
-    // Cache should be cleared after the save.
-    //$this->drupalGet('search/number10');
-    //$this->assertText('1 result found');
-    //$this->assertRaw('name="keys" value="number10"');
-  }
-
-  /**
-   * Private method to setup Search API database and server.
-   */
-  private function setupSearchAPI() {
-    $this->server = Server::create(array(
-      'name' => $this->randomString(64),
-      'id' => $this->randomMachineName(32),
-      'backend' => 'search_api_db',
-      'backend_config' =>
-        array (
-          'database' => 'default:default',
-        ),
-    ));
-    $this->server->save();
-
-    $this->index = Index::create(array(
-      'id' => $this->randomMachineName(32),
-      'name' => $this->randomString(64),
-      'description' => $this->randomString(512),
-      'server' => $this->server->id(),
-      'datasource_settings' => array(
-        'entity:node' => array(
-          'plugin_id' => 'entity:node',
-          'settings' => array(),
-        ),
-      ),
-      'field_settings' =>
-        array (
-          'rendered_item' =>
-            array (
-              'label' => 'Rendered HTML output',
-              'property_path' => 'rendered_item',
-              'type' => 'text',
-              'configuration' =>
-                array (
-                  'roles' =>
-                    array (
-                      'anonymous' => 'anonymous',
-                    ),
-                  'view_mode' =>
-                    array (
-                      'entity:node' =>
-                        array (
-                          'article' => 'default',
-                          'page' => '',
-                        ),
-                    ),
-                ),
-            ),
-        ),
-    ));
-    $this->index->save();
-
-    $task_manager = \Drupal::getContainer()->get('search_api.index_task_manager');
-    $task_manager->addItemsAll(Index::load($this->index->id()));
-    $this->indexItems($this->index->id());
-  }
-
-}
diff --git a/tests/src/Functional/FacetsIntegration.php b/tests/src/Functional/FacetsIntegration.php
new file mode 100644
index 0000000..29e26da
--- /dev/null
+++ b/tests/src/Functional/FacetsIntegration.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Drupal\Tests\search_api_page\Functional;
+
+use Drupal\search_api\Item\Field;
+use Drupal\search_api_page\Entity\SearchApiPage;
+use Drupal\Tests\facets\Functional\BlockTestTrait;
+use Drupal\Tests\facets\Functional\TestHelperTrait;
+
+/**
+ * Provides web tests for Search API Pages's integration with facets.
+ *
+ * @group search_api_page
+ */
+class FacetsIntegration extends FunctionalTestBase {
+
+  use TestHelperTrait;
+  use BlockTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'facets',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser([
+      'administer search_api',
+      'administer search_api_page',
+      'access administration pages',
+      'administer nodes',
+      'access content overview',
+      'administer content types',
+      'administer blocks',
+      'view search api pages',
+      'administer facets',
+    ]);
+
+    $this->drupalLogin($this->adminUser);
+    $this->setupSearchAPI();
+
+    $this->drupalCreateContentType(['type' => 'page']);
+    for ($i = 1; $i < 12; $i++) {
+      $this->drupalCreateNode([
+        'title' => 'Page number' . $i,
+        'type' => 'page',
+        'body' => [['value' => "Page $i body."]],
+      ]);
+    }
+
+    // Index the taxonomy and entity reference fields.
+    $type_field = new Field($this->index, 'type');
+    $type_field->setType('string');
+    $type_field->setPropertyPath('type');
+    $type_field->setDatasourceId('entity:node');
+    $type_field->setLabel('Type');
+
+    $this->index->addField($type_field);
+    $this->index->save();
+    $this->indexItems($this->index->id());
+  }
+
+  /**
+   * Test search api pages.
+   */
+  public function testFacets() {
+    $page = SearchApiPage::create([
+      'label' => 'Owl Display',
+      'id' => 'owl_display',
+      'index' => $this->index->id(),
+      'path' => 'bird_owl',
+      'show_all_when_no_keys' => TRUE,
+    ]);
+    $page->save();
+    $this->createFacet('Eurasian Eagle-Owl', 'eagle_owl', 'type', 'owl_display', 'search_api_page');
+
+    // Clear the caches because creating a search page is not picked up by the
+    // routing otherwise.
+    // @todo: Fix that.
+    $this->resetAll();
+
+    $this->drupalGet('bird_owl');
+    $this->assertFacetBlocksAppear();
+    $this->assertFacetLabel('page');
+    $this->assertFacetLabel('article');
+    $this->assertText('60 results found');
+
+    $this->clickPartialLink('page');
+    $this->assertText('11 results found');
+  }
+
+}
diff --git a/tests/src/Functional/FunctionalTestBase.php b/tests/src/Functional/FunctionalTestBase.php
new file mode 100644
index 0000000..539c357
--- /dev/null
+++ b/tests/src/Functional/FunctionalTestBase.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace Drupal\Tests\search_api_page\Functional;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Tests\BrowserTestBase;
+use Drupal\search_api\Entity\Index;
+use Drupal\search_api\Entity\Server;
+use Drupal\Tests\search_api\Functional\ExampleContentTrait;
+
+/**
+ * Class FunctionalTestBase.
+ */
+abstract class FunctionalTestBase extends BrowserTestBase {
+
+  protected $strictConfigSchema = FALSE;
+
+  use StringTranslationTrait;
+  use ExampleContentTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'search_api_page',
+    'node',
+    'search_api',
+    'search_api_db',
+    'block',
+  ];
+
+  /**
+   * An admin user used for this test.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $adminUser;
+
+  /**
+   * A user without any permission..
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $unauthorizedUser;
+
+  /**
+   * The anonymous user used for this test.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $anonymousUser;
+
+  /**
+   * A search database server.
+   *
+   * @var \Drupal\search_api\Entity\Server
+   */
+  protected $server = NULL;
+
+  /**
+   * A search index.
+   *
+   * @var \Drupal\search_api\Entity\Index
+   */
+  protected $index = NULL;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Create the users used for the tests.
+    $this->adminUser = $this->drupalCreateUser([
+      'administer search_api',
+      'administer search_api_page',
+      'access administration pages',
+      'administer nodes',
+      'access content overview',
+      'administer content types',
+      'administer blocks',
+      'view search api pages',
+    ]);
+    $this->unauthorizedUser = $this->drupalCreateUser();
+    $this->anonymousUser = $this->drupalCreateUser(['view search api pages']);
+
+    // Create article content type and content.
+    $this->drupalCreateContentType(['type' => 'article']);
+    for ($i = 1; $i < 50; $i++) {
+      $this->drupalCreateNode([
+        'title' => 'Item number' . $i,
+        'type' => 'article',
+        'body' => [['value' => 'Body number' . $i]],
+      ]);
+    }
+  }
+
+  /**
+   * Set up Search API database and server.
+   */
+  protected function setupSearchAPI() {
+    $this->server = Server::create([
+      'name' => 'Server',
+      'id' => 'server_1',
+      'backend' => 'search_api_db',
+      'backend_config' => [
+        'database' => 'default:default',
+      ],
+    ]);
+    $this->server->save();
+
+    $this->index = Index::create([
+      'id' => 'Index',
+      'name' => 'index_1',
+      'description' => 'Description for the index.',
+      'server' => $this->server->id(),
+      'datasource_settings' => [
+        'entity:node' => [
+          'plugin_id' => 'entity:node',
+          'settings' => [],
+        ],
+      ],
+      'field_settings' => [
+        'rendered_item' => [
+          'label' => 'Rendered HTML output',
+          'property_path' => 'rendered_item',
+          'type' => 'text',
+          'configuration' => [
+            'roles' => [
+              'anonymous' => 'anonymous',
+            ],
+            'view_mode' => [
+              'entity:node' => [
+                'article' => 'default',
+                'page' => '',
+              ],
+            ],
+          ],
+        ],
+      ],
+    ]);
+    $this->index->save();
+
+    $task_manager = \Drupal::getContainer()->get('search_api.index_task_manager');
+    $task_manager->addItemsAll(Index::load($this->index->id()));
+    $this->indexItems($this->index->id());
+  }
+
+}
diff --git a/tests/src/Functional/SearchApiPage.php b/tests/src/Functional/SearchApiPage.php
new file mode 100644
index 0000000..f30e27a
--- /dev/null
+++ b/tests/src/Functional/SearchApiPage.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Drupal\Tests\search_api_page\Functional;
+
+/**
+ * Provides web tests for Search API Pages.
+ *
+ * @group search_api_page
+ */
+class SearchApiPage extends FunctionalTestBase {
+
+  /**
+   * Test search api pages.
+   */
+  public function testSearchApiPage() {
+    $this->drupalLogin($this->adminUser);
+
+    // Setup search api server and index.
+    $this->setupSearchAPI();
+
+    $this->drupalGet('admin/config/search/search-api-pages');
+    $this->assertResponse(200);
+
+    $step1 = array(
+      'label' => 'Search',
+      'id' => 'search',
+      'index' => $this->index->id(),
+    );
+    $this->drupalPostForm('admin/config/search/search-api-pages/add', $step1, 'Next');
+
+    $step2 = array(
+      'path' => 'search',
+    );
+    $this->drupalPostForm(NULL, $step2, 'Save');
+
+    $this->drupalGet('search');
+    $this->assertRaw('Enter the terms you wish to search for.');
+    $this->assertNoRaw('Your search yielded no results.');
+    $this->assertResponse(200);
+
+    $this->drupalLogout();
+    $this->drupalLogin($this->unauthorizedUser);
+    $this->drupalGet('search');
+    $this->assertResponse(403);
+
+    $this->drupalLogout();
+    $this->drupalLogin($this->anonymousUser);
+    $this->drupalGet('search');
+    $this->assertResponse(200);
+
+    $this->drupalLogout();
+    $this->drupalLogin($this->adminUser);
+
+    $this->drupalGet('search/nothing-found');
+    $this->assertRaw('Enter the terms you wish to search for.');
+    $this->assertRaw('Your search yielded no results.');
+    $this->drupalGet('search');
+    $this->assertNoRaw('Your search yielded no results.');
+
+    $this->drupalPostForm('admin/config/search/search-api-pages/search', array('show_all_when_no_keys' => TRUE, 'show_search_form' => FALSE), 'Save');
+    $this->drupalGet('search');
+    $this->assertNoRaw('Your search yielded no results.');
+    $this->assertNoRaw('Enter the terms you wish to search for.');
+    $this->assertText('49 results found');
+
+    $this->drupalGet('search/number10');
+    $this->assertText('1 result found');
+
+    $this->drupalPostForm('admin/config/search/search-api-pages/search', array('show_search_form' => TRUE), 'Save');
+
+    $this->drupalGet('search/number11');
+    $this->assertText('1 result found');
+    $this->assertRaw('name="keys" value="number11"');
+
+    // Cache should be cleared after the save.
+    //$this->drupalGet('search/number10');
+    //$this->assertText('1 result found');
+    //$this->assertRaw('name="keys" value="number10"');
+  }
+
+}
