diff --git a/panopoly_test.features.filter.inc b/panopoly_test.features.filter.inc
deleted file mode 100644
index 5aa2346..0000000
--- a/panopoly_test.features.filter.inc
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-/**
- * @file
- * panopoly_test.features.filter.inc
- */
-
-/**
- * Implements hook_filter_default_formats().
- */
-function panopoly_test_filter_default_formats() {
-  $formats = array();
-
-  // Exported format: Panopoly Custom.
-  $formats['_panopoly_custom'] = array(
-    'format' => '_panopoly_custom',
-    'name' => 'Panopoly Custom',
-    'cache' => 1,
-    'status' => 1,
-    'weight' => 0,
-    'filters' => array(),
-  );
-
-  // Exported format: Panopoly Custom Kitchen Sink.
-  $formats['_panopoly_custom_kitchen'] = array(
-    'format' => '_panopoly_custom_kitchen',
-    'name' => 'Panopoly Custom Kitchen Sink',
-    'cache' => 1,
-    'status' => 1,
-    'weight' => 0,
-    'filters' => array(),
-  );
-
-  return $formats;
-}
diff --git a/panopoly_test.features.inc b/panopoly_test.features.inc
index 155d360..a0ff377 100644
--- a/panopoly_test.features.inc
+++ b/panopoly_test.features.inc
@@ -34,7 +34,7 @@ function panopoly_test_node_info() {
       'description' => t('Used for the automated tests.'),
       'has_title' => '1',
       'title_label' => t('Title'),
-      'help' => '',
+      'help' => 'This message appears in the system help region when you create a node.',
     ),
   );
   drupal_alter('node_info', $items);
diff --git a/panopoly_test.features.user_permission.inc b/panopoly_test.features.user_permission.inc
deleted file mode 100644
index f8542f5..0000000
--- a/panopoly_test.features.user_permission.inc
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-/**
- * @file
- * panopoly_test.features.user_permission.inc
- */
-
-/**
- * Implements hook_user_default_permissions().
- */
-function panopoly_test_user_default_permissions() {
-  $permissions = array();
-
-  // Exported permission: 'create panopoly_test_page content'.
-  $permissions['create panopoly_test_page content'] = array(
-    'name' => 'create panopoly_test_page content',
-    'roles' => array(
-      'administrator' => 'administrator',
-      'editor' => 'editor',
-    ),
-    'module' => 'node',
-  );
-
-  // Exported permission: 'delete any panopoly_test_page content'.
-  $permissions['delete any panopoly_test_page content'] = array(
-    'name' => 'delete any panopoly_test_page content',
-    'roles' => array(
-      'administrator' => 'administrator',
-      'editor' => 'editor',
-    ),
-    'module' => 'node',
-  );
-
-  // Exported permission: 'delete own panopoly_test_page content'.
-  $permissions['delete own panopoly_test_page content'] = array(
-    'name' => 'delete own panopoly_test_page content',
-    'roles' => array(
-      'administrator' => 'administrator',
-      'editor' => 'editor',
-    ),
-    'module' => 'node',
-  );
-
-  // Exported permission: 'edit any panopoly_test_page content'.
-  $permissions['edit any panopoly_test_page content'] = array(
-    'name' => 'edit any panopoly_test_page content',
-    'roles' => array(
-      'administrator' => 'administrator',
-      'editor' => 'editor',
-    ),
-    'module' => 'node',
-  );
-
-  // Exported permission: 'use text format panopoly_html_text'.
-  $permissions['use text format panopoly_html_text'] = array(
-    'name' => 'use text format panopoly_html_text',
-    'roles' => array(
-      'administrator' => 'administrator',
-      'editor' => 'editor',
-    ),
-    'module' => 'filter',
-  );
-
-  // Exported permission: 'use text format panopoly_wysiwyg_text'.
-  $permissions['use text format panopoly_wysiwyg_text'] = array(
-    'name' => 'use text format panopoly_wysiwyg_text',
-    'roles' => array(
-      'administrator' => 'administrator',
-      'editor' => 'editor',
-    ),
-    'module' => 'filter',
-  );
-
-  return $permissions;
-}
diff --git a/panopoly_test.features.wysiwyg.inc b/panopoly_test.features.wysiwyg.inc
deleted file mode 100644
index 1cb8663..0000000
--- a/panopoly_test.features.wysiwyg.inc
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-/**
- * @file
- * panopoly_test.features.wysiwyg.inc
- */
-
-/**
- * Implements hook_wysiwyg_default_profiles().
- */
-function panopoly_test_wysiwyg_default_profiles() {
-  $profiles = array();
-
-  // Exported profile: _panopoly_custom
-  $profiles['_panopoly_custom'] = array(
-    'format' => '_panopoly_custom',
-    'editor' => 'tinymce',
-    'settings' => array(
-      'default' => 1,
-      'user_choose' => 0,
-      'show_toggle' => 1,
-      'theme' => 'advanced',
-      'language' => 'en',
-      'buttons' => array(
-        'default' => array(
-          'bold' => 1,
-          'italic' => 1,
-          'underline' => 1,
-          'strikethrough' => 1,
-          'justifyleft' => 1,
-          'justifycenter' => 1,
-          'justifyright' => 1,
-          'justifyfull' => 1,
-          'bullist' => 1,
-          'numlist' => 1,
-          'outdent' => 1,
-          'indent' => 1,
-          'undo' => 1,
-          'redo' => 1,
-          'unlink' => 1,
-          'formatselect' => 1,
-          'forecolor' => 1,
-          'charmap' => 1,
-        ),
-      ),
-      'toolbar_loc' => 'top',
-      'toolbar_align' => 'left',
-      'path_loc' => 'bottom',
-      'resizing' => 1,
-      'verify_html' => 1,
-      'preformatted' => 0,
-      'convert_fonts_to_spans' => 1,
-      'remove_linebreaks' => 1,
-      'apply_source_formatting' => 0,
-      'paste_auto_cleanup_on_paste' => 1,
-      'block_formats' => 'h1,h2,h3,h4,h5,h6',
-      'css_setting' => 'theme',
-      'css_path' => '',
-      'css_classes' => '',
-    ),
-  );
-
-  // Exported profile: _panopoly_custom_kitchen
-  $profiles['_panopoly_custom_kitchen'] = array(
-    'format' => '_panopoly_custom_kitchen',
-    'editor' => 'tinymce',
-    'settings' => array(
-      'default' => 1,
-      'user_choose' => 0,
-      'show_toggle' => 1,
-      'theme' => 'advanced',
-      'language' => 'en',
-      'buttons' => array(
-        'default' => array(
-          'bold' => 1,
-          'italic' => 1,
-          'underline' => 1,
-          'strikethrough' => 1,
-          'justifyleft' => 1,
-          'justifycenter' => 1,
-          'justifyright' => 1,
-          'justifyfull' => 1,
-          'bullist' => 1,
-          'numlist' => 1,
-          'outdent' => 1,
-          'indent' => 1,
-          'undo' => 1,
-          'unlink' => 1,
-          'formatselect' => 1,
-          'forecolor' => 1,
-          'blockquote' => 1,
-          'removeformat' => 1,
-          'charmap' => 1,
-        ),
-        'pdw' => array(
-          'pdw_toggle' => 1,
-        ),
-      ),
-      'toolbar_loc' => 'top',
-      'toolbar_align' => 'left',
-      'path_loc' => 'bottom',
-      'resizing' => 1,
-      'verify_html' => 1,
-      'preformatted' => 0,
-      'convert_fonts_to_spans' => 1,
-      'remove_linebreaks' => 1,
-      'apply_source_formatting' => 0,
-      'paste_auto_cleanup_on_paste' => 1,
-      'block_formats' => 'h1,h2,h3,h4,h5,h6',
-      'css_setting' => 'theme',
-      'css_path' => '',
-      'css_classes' => '',
-    ),
-  );
-
-  return $profiles;
-}
diff --git a/panopoly_test.info b/panopoly_test.info
index 3b04dd9..8bd8298 100644
--- a/panopoly_test.info
+++ b/panopoly_test.info
@@ -23,12 +23,6 @@ features[field_instance][] = node-panopoly_test_page-body
 features[field_instance][] = node-panopoly_test_page-field_featured_image
 features[node][] = panopoly_test_page
 features[panelizer_defaults][] = node:panopoly_test_page:default
-features[user_permission][] = create panopoly_test_page content
-features[user_permission][] = delete any panopoly_test_page content
-features[user_permission][] = delete own panopoly_test_page content
-features[user_permission][] = edit any panopoly_test_page content
-features[user_permission][] = use text format panopoly_html_text
-features[user_permission][] = use text format panopoly_wysiwyg_text
 features[variable][] = field_bundle_settings_node__panopoly_test_page
 features[variable][] = menu_options_panopoly_test_page
 features[variable][] = menu_parent_panopoly_test_page
diff --git a/tests/behat.common.yml b/tests/behat.common.yml
index 0461f0c..40271a2 100644
--- a/tests/behat.common.yml
+++ b/tests/behat.common.yml
@@ -1,15 +1,21 @@
 default:
-  paths:
-    features: 'features'
-  filters:
-    tags: "~@chrome"
+  suites:
+    default:
+      paths: 
+        features: 'features'
+      filters:
+        tags: "~@chrome"
+      contexts:
+        - FeatureContext
+        - Drupal\DrupalExtension\Context\DrupalContext
+        - Drupal\DrupalExtension\Context\MinkContext
   extensions:
-    Behat\MinkExtension\Extension:
+    Behat\MinkExtension:
       goutte: ~
       selenium2: ~
       # base_url: http://${drupal.uri}
       files_path: ./
-    Drupal\DrupalExtension\Extension:
+    Drupal\DrupalExtension:
       blackbox: ~
       # drush:
       #  root: ${drush.root}
@@ -59,10 +65,12 @@ default:
 
 # Setup for Google Chrome only tests.
 chrome:
-  filters:
-    tags: "@chrome"
+  suites:
+    default:
+      filters:
+        tags: "@chrome"
   extensions:
-    Behat\MinkExtension\Extension:
+    Behat\MinkExtension:
       selenium2:
         browser: chrome
         capabilities: {"browser": "chrome", "browserName": "chrome", "browserVersion": "ANY", "version": "ANY"}
diff --git a/tests/behat.template.yml b/tests/behat.template.yml
index 42d917a..0369ee9 100644
--- a/tests/behat.template.yml
+++ b/tests/behat.template.yml
@@ -3,9 +3,9 @@ imports:
 
 default:
   extensions:
-    Behat\MinkExtension\Extension:
+    Behat\MinkExtension:
       base_url: http://${drupal.uri}
-    Drupal\DrupalExtension\Extension:
+    Drupal\DrupalExtension:
       drush:
         root: ${drush.root}
       drupal:
diff --git a/tests/composer.json b/tests/composer.json
index 968f720..7843f41 100644
--- a/tests/composer.json
+++ b/tests/composer.json
@@ -1,19 +1,7 @@
 {
   "require": {
-    "drupal/drupal-extension": "1.0.2",
-
-    "symfony/css-selector": "2.4.2",
-    "symfony/finder": "2.4.2",
-    "symfony/yaml": "2.4.2",
-    "symfony/translation": "2.4.2",
-    "symfony/event-dispatcher": "2.4.2",
-    "symfony/filesystem": "2.4.2",
-    "symfony/config": "2.4.2",
-    "symfony/console": "2.4.2",
-    "symfony/process": "2.4.2",
-    "symfony/dom-crawler": "2.4.2",
-    "symfony/browser-kit": "2.4.2"
-  },
+    "drupal/drupal-extension": "~3.0"
+},
   "config": {
     "bin-dir": "bin/"
   }
diff --git a/tests/features/bootstrap/FeatureContext.php b/tests/features/bootstrap/FeatureContext.php
index 1ec5419..38840b5 100644
--- a/tests/features/bootstrap/FeatureContext.php
+++ b/tests/features/bootstrap/FeatureContext.php
@@ -5,30 +5,30 @@ require_once 'PanopolyContext.php';
 /**
  * Features context.
  */
-class FeatureContext extends PanopolyContext
-{
 
+/**
+ * Define application features from the specific context.
+ */
+class FeatureContext extends PanopolyContext {
   /**
    * Initializes context.
    * Every scenario gets its own context object.
    *
-   * @param array $parameters context parameters (set them up through behat.yml)
+   * @param array $parameters
+   *   Context parameters (set them in behat.yml)
    */
-  public function __construct(array $parameters) {
+  public function __construct() {
     // Initialize your context here
-
   }
 
 //
 // Place your definition and hook methods here:
 //
-//    /**
-//     * @Given /^I have done something with "([^"]*)"$/
-//     */
-//    public function iHaveDoneSomethingWith($argument)
-//    {
-//        doSomethingWith($argument);
-//    }
+//  /**
+//   * @Given I have done something with :stuff
+//   */
+//  public function iHaveDoneSomethingWith($stuff) {
+//    doSomethingWith($stuff);
+//  }
 //
-
-}
+}
\ No newline at end of file
diff --git a/tests/features/bootstrap/PanopolyContext.php b/tests/features/bootstrap/PanopolyContext.php
index ab39cc4..82b4dca 100644
--- a/tests/features/bootstrap/PanopolyContext.php
+++ b/tests/features/bootstrap/PanopolyContext.php
@@ -1,13 +1,16 @@
 <?php
 
+use Behat\Behat\Context\Context;
+use Behat\Behat\Context\SnippetAcceptingContext;
+use Behat\Gherkin\Node\PyStringNode;
+use Behat\Gherkin\Node\TableNode;
+
+use Drupal\DrupalExtension\Context\RawDrupalContext;
+
 use Behat\Behat\Context\ClosuredContextInterface,
     Behat\Behat\Context\TranslatedContextInterface,
-    Behat\Behat\Context\BehatContext,
     Behat\Behat\Event\ScenarioEvent,
     Behat\Behat\Exception\PendingException;
-use Behat\Gherkin\Node\PyStringNode,
-    Behat\Gherkin\Node\TableNode;
-use Drupal\DrupalExtension\Context\DrupalContext;
 use Drupal\Component\Utility\Random;
 
 //
@@ -20,7 +23,7 @@ use Drupal\Component\Utility\Random;
 /**
  * Features context.
  */
-class PanopolyContext extends DrupalContext
+class PanopolyContext extends RawDrupalContext implements SnippetAcceptingContext
 {
 
   /**
@@ -33,18 +36,195 @@ class PanopolyContext extends DrupalContext
     // Initialize your context here
 
   }
+  protected $menu_rebuilt = FALSE;
 
-//
-// Place your definition and hook methods here:
-//
-//    /**
-//     * @Given /^I have done something with "([^"]*)"$/
-//     */
-//    public function iHaveDoneSomethingWith($argument)
-//    {
-//        doSomethingWith($argument);
-//    }
-//
+  /**
+   * Holds a list of temporary users created by our steps.
+   *
+   * @var array
+   */
+  protected $users = array();
+
+  protected $landing_pages = array();
+  protected $private_path_overridden = FALSE;
+
+  // getRegion has to go here
+  public function getRegion($region) {
+    $session = $this->getSession();
+    $regionObj = $session->getPage()->find('region', $region);
+    if (!$regionObj) {
+      throw new \Exception(sprintf('No region "%s" found on the page %s.', $region, $session->getCurrentUrl()));
+    }
+    return $regionObj;
+  }
+
+  /**
+   * @BeforeScenario @api
+   *
+   * Bootstrap Drupal so that all Drupal API functions work.
+   */
+  public function drupalMenuRebuild($event) {
+    if (!$this->menu_rebuilt) {
+      $drupal = $this->getDriver('drupal');
+
+      // Drupal is having a cache or registry issue that is causing tests to
+      // fail (certain CTools plugin functions won't exist.) Menu rebuild does
+      // the trick and fixes any fatal errors due to missing functions!
+      $current_path = getcwd();
+      chdir(DRUPAL_ROOT);
+      menu_rebuild();
+      chdir($current_path);
+
+      // We occasionally get errors about not finding the ctools_get_export_ui()
+      // function, so we force it to be loaded. No idea what's causing this.
+      ctools_include('export-ui');
+
+      $this->menu_rebuilt = TRUE;
+    }
+  }
+
+  /**
+   * Wait for the jQuery AJAX loading to finish. ONLY USE FOR DEBUGGING!
+   * Updated with criteria from MinkContext
+   */
+  public function iWaitForAJAX() {
+    $this->getSession()->wait(5000, '(typeof(jQuery)=="undefined" || (0 === jQuery.active && 0 === jQuery(\':animated\').length))');
+  }
+
+  /**
+   * @BeforeScenario @javascript
+   *
+   * Set a variable so AfterStep knows this is a javascript scenario.
+   */
+  public function setJavascript() {
+    $this->javascript = true;
+  }
+
+  /**
+   * @AfterScenario @javascript
+   *
+   * Unset the variable so other scenarios don't run the javascript AfterStep.
+   */
+  public function unsetJavascript() {
+      $this->javascript = false;
+    }
+
+  /**
+   * @BeforeScenario @api&&@drupal_private_files
+   *
+   * Configure a private files path if one isn't already configured.
+   */
+  public function configurePrivateFiles($event) {
+    $file_private_path = variable_get('file_private_path', '');
+    if (empty($file_private_path)) {
+      $file_public_path = variable_get('file_public_path', conf_path() . '/files');
+      if (empty($file_public_path)) {
+        throw new \Exception('Files must be configured for @drupal_private_files tests to work!');
+      }
+
+      // Set flag for later cleanup.
+      $this->private_path_overridden = TRUE;
+
+      // Create and setup the private path.
+      $file_private_path = $file_public_path . '/' . 'private';
+      variable_set('file_private_path', $file_private_path);
+    }
+  }
+
+  /**
+   * @AfterScenario @api&&@drupal_private_files
+   *
+   * Clean up our temporary private files path.
+   */
+  public function cleanupPrivateFiles($event) {
+    if ($this->private_path_overridden) {
+      variable_del('file_private_path');
+    }
+  }
+
+  /**
+   * @AfterScenario
+   *
+   * Clean up users that were created during testing
+   */
+  public function removeUsers() {
+    if(!empty($this->users)){
+      foreach ($this->users as $user) {
+        $account = user_load_by_name($user->name);
+        user_delete($account->uid);
+      }
+      $this->users = array();
+    }
+  }
+
+  /**
+   * @AfterStep
+   */
+  public function afterStep($scope) {
+    if ($scope->getTestResult()->getResultCode() === 0 && isset($this->javascript)) {
+      $this->iWaitForAJAX();
+    }
+  }
+
+
+  /**
+   * Create a new Landing Page.
+   *
+   * @param string $name
+   *   The internal machine name for the new Page.
+   * @param string $path
+   *   The path for the new Page.
+   * @param string $title
+   *   The title for the new Page.
+   */
+  protected function createPage($name, $path, $title) {
+    $page_task = page_manager_get_task('page');
+
+    $subtask = page_manager_page_new();
+    $subtask->name = $name;
+    $subtask->path = $path;
+    $subtask->admin_title = $title;
+    $subtask->admin_description = '';
+    $subtask->menu = array(
+      'type' => 'none',
+    );
+
+    $display = new stdClass();
+    $display->layout = 'boxton';
+    $display->title = $title;
+    $display->panels = array();
+    $display->content = array();
+
+    $plugin = page_manager_get_task_handler('panel_context');
+    $handler = page_manager_new_task_handler($plugin);
+    $handler->conf['title'] = t('Landing page');
+    $handler->conf['display'] = $display;
+    $handler->conf['pipeline'] = 'ipe';
+
+    // Assemble a new $page cache and assign it to our page subtask and task
+    // handler.
+    $page = new stdClass();
+    page_manager_page_new_page_cache($subtask, $page);
+    page_manager_handler_add_to_page($page, $handler);
+    page_manager_save_page_cache($page);
+
+    // Mark this page for deletion at the end of the scenario.
+    $this->landing_pages[] = $name;
+  }
+
+  /**
+   * @AfterScenario @api
+   *
+   * Delete landing pages after a Scenario has finished.
+   */
+  public function cleanupPages() {
+    foreach ($this->landing_pages as $name) {
+      if ($page = page_manager_page_load($name)) {
+        page_manager_page_delete($page);
+      }
+    }
+    $this->landing_pages = array();
+  }
 
   /**
    * Override MinkContext::fixStepArgument().
diff --git a/tests/features/contentitem_widget.feature b/tests/features/contentitem_widget.feature
index 0847618..513ae7f 100644
--- a/tests/features/contentitem_widget.feature
+++ b/tests/features/contentitem_widget.feature
@@ -7,18 +7,18 @@ Feature: Add content item
   Scenario: Add content item
     Given I am logged in as a user with the "administrator" role
       And Panopoly magic live previews are disabled
-      And "panopoly_test_page" nodes:
+      And "panopoly_test_page" content:
       | title       | body      | created            | status |
       | Test Page 1 | Test body | 01/01/2001 11:00am |      1 |
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add content item" in the "CTools modal" region
+      And I add an "Add content item" widget in the Panels IPE
     Then I should see "Configure new Add content item"
     When I fill in the following:
       | exposed[title]        | Test Page 1 |
     When I select "Test Page" from "exposed[type]"
-      And I press "Save" in the "CTools modal" region
+      And I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Test Page 1"
diff --git a/tests/features/contentlist_widget.feature b/tests/features/contentlist_widget.feature
index 38541ac..74d212c 100644
--- a/tests/features/contentlist_widget.feature
+++ b/tests/features/contentlist_widget.feature
@@ -7,7 +7,7 @@ Feature: Add content list widget
   Scenario: Add a content list
     Given I am logged in as a user with the "administrator" role
       And Panopoly magic live previews are disabled
-      And "panopoly_test_page" nodes:
+      And "panopoly_test_page" content:
       | title       | body      | created            | status |
       | Test Page 3 | Test body | 01/01/2001 11:00am |      1 |
       | Test Page 1 | Test body | 01/02/2001 11:00am |      1 |
@@ -15,7 +15,7 @@ Feature: Add content list widget
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add content list" in the "CTools modal" region
+      And I add an "Add content list" widget in the Panels IPE
     Then I should see "Configure new Add content list"
       When I fill in the following:
        | widget_title    | Content Page List Asc 1 |
@@ -25,7 +25,7 @@ Feature: Add content list widget
       And I select "Asc" from "exposed[sort_order]"
       And I select "Title" from "exposed[sort_by]"
       And I wait 5 seconds
-      And I press "Save" in the "CTools modal" region
+      And I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
       And I wait 5 seconds
diff --git a/tests/features/file_widget.feature b/tests/features/file_widget.feature
index 1a9f4d4..86a6565 100644
--- a/tests/features/file_widget.feature
+++ b/tests/features/file_widget.feature
@@ -9,7 +9,7 @@ Feature: Add a file to a page
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add file" in the "CTools modal" region
+      And I add an "Add file" widget in the Panels IPE
     Then I should see "Configure new Add file"
 
   @api @javascript @panopoly_widgets
@@ -22,7 +22,7 @@ Feature: Add a file to a page
       And I attach the file "test.txt" to "files[field_basic_file_file_und_0]"
       And I press "Upload"
     Then I should see "test.txt"
-    When I press "Save" in the "CTools modal" region
+    When I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Testing file title"
diff --git a/tests/features/help.feature b/tests/features/help.feature
new file mode 100644
index 0000000..8a4ccec
--- /dev/null
+++ b/tests/features/help.feature
@@ -0,0 +1,10 @@
+Feature: View system help
+  In order to understand how to administer the site
+  As a site owner
+  I need to be able to view content in the system help region
+
+  @api @panopoly_demo
+  Scenario: Create Test Page
+    Given I am logged in as a user with the "administrator" role
+    When I visit "/node/add/panopoly-test-page"
+    Then I should see "This message appears in the system help region when you create a node."
diff --git a/tests/features/image_widget.feature b/tests/features/image_widget.feature
index 18c451f..96b2bc2 100644
--- a/tests/features/image_widget.feature
+++ b/tests/features/image_widget.feature
@@ -10,7 +10,7 @@ Feature: Add image widget
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add image" in the "CTools modal" region
+      And I add an "Add image" widget in the Panels IPE
     Then I should see "Configure new Add image"
     When I fill in the following:
       | Title   | Testing image widget title |
@@ -19,7 +19,7 @@ Feature: Add image widget
       And I attach the file "test-sm.png" to "files[field_basic_image_image_und_0]"
       And I press "Upload"
       And I fill in "Alternate text" with "Testing alt text"
-      And I press "Save" in the "CTools modal" region
+      And I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Testing image widget title"
diff --git a/tests/features/landingpage.feature b/tests/features/landingpage.feature
index e9320e8..ffdd309 100644
--- a/tests/features/landingpage.feature
+++ b/tests/features/landingpage.feature
@@ -9,7 +9,15 @@ Feature: Add landing page
     Given I am logged in as a user with the "administrator" role
     When I visit "/node/add/landing_page"
       And I fill in the following:
-        | Title | Testing [random] landing page title |
-        | URL   | lp-[random:1]             |
+        | Title | Testing landing page title |
+        | URL   | lp-testing                 |
       And I press "Create Page"
-    Then the "h1" element should contain "Testing [random:1] landing page title"
+    Then the "h1" element should contain "Testing landing page title"
+
+  @api @javascript @panopoly_pages
+  Scenario: Delete a landing page
+    Given I am logged in as a user with the "administrator" role
+    When I visit "/admin/structure/pages/edit/page-testing_landing_page_title"
+      And I click "Delete"
+      And I press "Delete"
+    Then I should not see "Testing landing page title"
\ No newline at end of file
diff --git a/tests/features/linkit.feature b/tests/features/linkit.feature
index 67f763c..cdd8463 100644
--- a/tests/features/linkit.feature
+++ b/tests/features/linkit.feature
@@ -5,7 +5,7 @@ Feature: Link to page on the site
 
   Background:
     Given I am logged in as a user with the "administrator" role
-      And a "panopoly_test_page" node with the title "Linkit Target"
+      And a "Test Page" with the title "Linkit Target"
     When I visit "/node/add/panopoly-test-page"
       And I fill in the following:
         | Title  | Testing Linkit       |
@@ -17,8 +17,7 @@ Feature: Link to page on the site
       And I fill in "edit-linkit-search" with "target"
       And I wait 1 seconds
       And I press the "Tab" key in the "edit-linkit-search" field
-    Then the "edit-linkit-path" field should contain "/node/"
-    When I click "Options" in the "Linkit modal" region
+      And I click "Options" in the "Linkit modal" region
       And I fill in "edit-linkit-title" with "Testing title"
       And I press "Insert link"
       # Normally, here we'd press "Publish", however some child distribtions
diff --git a/tests/features/links_widget.feature b/tests/features/links_widget.feature
index da09348..bd2f2c1 100644
--- a/tests/features/links_widget.feature
+++ b/tests/features/links_widget.feature
@@ -10,13 +10,13 @@ Feature: Add links widget
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add links" in the "CTools modal" region
+      And I add an "Add links" widget in the Panels IPE
     Then I should see "Configure new Add links"
     When I fill in the following:
       | title                                  | Testing link title  |      
       | field_quick_links_links[und][0][title] | Testing url title   |
       | field_quick_links_links[und][0][url]   | http://drupal.org   |
-      And I press "Save" in the "CTools modal" region
+    When I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Testing link title"
diff --git a/tests/features/livepreview.feature b/tests/features/livepreview.feature
index be9be57..013e4eb 100644
--- a/tests/features/livepreview.feature
+++ b/tests/features/livepreview.feature
@@ -10,7 +10,7 @@ Feature: Live preview
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add table" in the "CTools modal" region
+      And I add an "Add table" widget in the Panels IPE
     Then I should see "Configure new Add table"
     When I fill in "Title" with "Widget title"
       And I wait for live preview to finish
@@ -30,7 +30,7 @@ Feature: Live preview
       And I fill in "path" with "http://drupal.org"
       And I wait for live preview to finish
     Then I should see the link "Widget title" in the "Live preview" region
-    When I press "Save" in the "CTools modal" region
+    When I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Widget title"
@@ -41,7 +41,7 @@ Feature: Live preview
   @api @javascript @panopoly_magic @panopoly_widgets
   Scenario: Live preview should work with views
     Given I am logged in as a user with the "administrator" role
-      And "panopoly_test_page" nodes:
+      And "panopoly_test_page" content:
       | title       | body      | created            | status |
       | Test Page 3 | Test body | 01/01/2001 11:00am |      1 |
       | Test Page 1 | Test body | 01/02/2001 11:00am |      1 |
@@ -50,7 +50,7 @@ Feature: Live preview
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add content list" in the "CTools modal" region
+      And I add an "Add content list" widget in the Panels IPE
     Then I should see "Configure new Add content list"
     When I fill in "widget_title" with "Content list widget"
       And I wait for live preview to finish
@@ -120,7 +120,7 @@ Feature: Live preview
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add text" in the "CTools modal" region
+      And I add an "Add text" widget in the Panels IPE
     Then I should see "Configure new Add text"
     When I fill in "Title" with "Widget title"
     Then I should not see "Widget title" in the "Live preview" region
@@ -135,7 +135,7 @@ Feature: Live preview
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add spotlight" in the "CTools modal" region
+      And I add an "Add spotlight" widget in the Panels IPE
     Then I should see "Configure new Add spotlight"
     When I fill in "Description" with "Testing description"
       And I wait for live preview to finish
diff --git a/tests/features/login.feature b/tests/features/login.feature
index 30184b6..746acb7 100644
--- a/tests/features/login.feature
+++ b/tests/features/login.feature
@@ -6,7 +6,7 @@ I want to be able to login
   Background:
     Given users:
     | name     | pass      | mail             | roles    |
-    | TestUser | ChangeMe! | foo@example.com  | 4        |
+    | TestUser | ChangeMe! | foo@example.com  | editor   |
  
   @standard_login @api
   Scenario: Editor is able to login
diff --git a/tests/features/maps_widget.feature b/tests/features/maps_widget.feature
index 2cfc082..63370ef 100644
--- a/tests/features/maps_widget.feature
+++ b/tests/features/maps_widget.feature
@@ -10,14 +10,14 @@ Feature: Add map widget
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add map" in the "CTools modal" region
+      And I add an "Add map" widget in the Panels IPE
     Then I should see "Configure new Add map"
     When I fill in the following:
       | Title       | Widget title            |
       | Editor      | plain_text              |
       | Information | Testing text body field |
       | Address     | Ørnebjergvej 28, Vejle  |
-      And I press "Save" in the "CTools modal" region
+      And I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Widget title"
diff --git a/tests/features/pathauto.feature b/tests/features/pathauto.feature
new file mode 100644
index 0000000..51fbe3c
--- /dev/null
+++ b/tests/features/pathauto.feature
@@ -0,0 +1,48 @@
+Feature: Test pathauto
+  In order to get nice urls
+  As a site administrator
+  I need to be able to trust that pathauto works consistently
+
+  Background:
+    Given I am logged in as a user with the "administrator" role
+      And Panopoly magic live previews are disabled
+    When I visit "/node/add/panopoly-test-page"
+      And I fill in the following:
+        | Title  | Testing title |
+        | Editor | plain_text    |
+        | Body   | Testing body  |
+    # Normally, here we'd press "Publish", however some child distribtions
+    # don't use 'save_draft', and this makes this test compatible with them.
+    #When I press "Publish"
+    When I press "edit-submit"
+    Then the "h1" element should contain "Testing title"
+
+  @api @panopoly_admin
+  Scenario: Pathauto should automatically assign an url
+    Then the url should match "testing-title"
+
+  @api @panopoly_admin
+  Scenario: Pathauto should automatically assign a new url when changing the title
+    When I click "Edit" in the "Tabs" region
+      And I fill in the following:
+        | Title               | Completely other title |
+      And I press "Save"
+    Then the url should match "completely-other-title"
+    # But visiting the old URL should continue to work
+    When I visit "/content/testing-title"
+    Then the "h1" element should contain "Completely other title"
+
+  @api @panopoly_admin
+  Scenario: My own permalink should be kept even if changing title
+    When I click "Edit" in the "Tabs" region
+      And I fill in the following:
+        | Permalink           | my-custom-permalink |
+      And I press "Save"
+    Then the url should match "my-custom-permalink"
+    When I click "Edit" in the "Tabs" region
+      And I fill in the following:
+        | Title               | Saving Title Again  |
+      And I press "Save"
+    Then the url should match "my-custom-permalink"
+    Given I go to "my-custom-permalink"
+    Then the response status code should be 200
diff --git a/tests/features/pathauto_administrator.feature b/tests/features/pathauto_administrator.feature
deleted file mode 100644
index deadd93..0000000
--- a/tests/features/pathauto_administrator.feature
+++ /dev/null
@@ -1,49 +0,0 @@
-Feature: Test pathauto for administrator users
-  In order to get nice urls
-  As a site administrator
-  I need to be able to trust that pathauto works consistently
-
-  Background:
-    Given I am logged in as a user with the "administrator" role
-      And Panopoly magic live previews are disabled
-    When I visit "/node/add/panopoly-test-page"
-      And I fill in the following:
-        | Title  | Testing title |
-        | Editor | plain_text    |
-        | Body   | Testing body  |
-    # Normally, here we'd press "Publish", however some child distribtions
-    # don't use 'save_draft', and this makes this test compatible with them.
-    #When I press "Publish"
-    When I press "edit-submit"
-    Then the "h1" element should contain "Testing title"
-
-  @api @panopoly_admin
-  Scenario: Pathauto should automatically assign an url
-    Then the url should match "testing-title"
-
-  @api @panopoly_admin
-  Scenario: Pathauto should automatically assign a new url when changing the title
-    When I click "Edit" in the "Tabs" region
-      And I fill in the following:
-        | Title               | Completely other title |
-      And I press "Save"
-    Then the url should match "completely-other-title"
-    # But visiting the old URL should continue to work
-    When I visit "/content/testing-title"
-    Then the "h1" element should contain "Completely other title"
-
-  @api @panopoly_admin
-  Scenario: My own permalink should be kept even if changing title
-    When I click "Edit" in the "Tabs" region
-      And I fill in the following:
-        | Permalink           | my-custom-permalink |
-      And I press "Save"
-    Then the url should match "my-custom-permalink"
-    When I click "Edit" in the "Tabs" region
-      And I fill in the following:
-        | Title               | Saving Title Again  |
-      And I press "Save"
-    Then the url should match "my-custom-permalink"
-    Given I go to "my-custom-permalink"
-    Then the response status code should be 200
-
diff --git a/tests/features/pathauto_editor.feature b/tests/features/pathauto_editor.feature
deleted file mode 100644
index 7356926..0000000
--- a/tests/features/pathauto_editor.feature
+++ /dev/null
@@ -1,33 +0,0 @@
-Feature: Test pathauto for editor users
-  In order to get nice urls
-  As a site editor
-  I need to be able to trust that pathauto works consistently
-
-  Background:
-    Given I am logged in as a user with the "editor" role
-      And Panopoly magic live previews are disabled
-    When I visit "/node/add/panopoly-test-page"
-      And I fill in the following:
-        | Title  | Testing title |
-        | Editor | plain_text    |
-        | Body   | Testing body  |
-    # Normally, here we'd press "Publish", however some child distribtions
-    # don't use 'save_draft', and this makes this test compatible with them.
-    #When I press "Publish"
-    When I press "edit-submit"
-    Then the "h1" element should contain "Testing title"
-
-  @api @panopoly_admin
-  Scenario: Pathauto should automatically assign an url
-    Then the url should match "testing-title"
-
-  @api @panopoly_admin
-  Scenario: Pathauto should automatically assign a new url when changing the title
-    When I click "Edit" in the "Tabs" region
-      And I fill in the following:
-        | Title               | Completely other title |
-      And I press "Save"
-    Then the url should match "completely-other-title"
-    # But visiting the old URL should continue to work
-    When I visit "/content/testing-title"
-    Then the "h1" element should contain "Completely other title"
diff --git a/tests/features/search.feature b/tests/features/search.feature
deleted file mode 100644
index 5cb49a8..0000000
--- a/tests/features/search.feature
+++ /dev/null
@@ -1,27 +0,0 @@
-Feature: Search
-  In order to find content on the site
-  As an anonymous user
-  I should be able to find content using the site search
-
-  Background:
-    Given I am on "/demo"
-
-  Scenario: Trying an empty search should yield a message
-    When I press "Search"
-    Then I should see "Search Results"
-    And I should see "Enter your keywords"
-
-  Scenario: Trying a search with no results
-    When I enter "stuff" for "Enter your keywords"
-    And I press "Search"
-    Then I should see "Search Results"
-    And I should see "0 items matched stuff"
-    And I should see "Your search did not return any results."
-
-  Scenario: Performing a search with results
-    When I enter "Vegetables" for "Enter your keywords"
-    And I press "Search"
-    Then I should see "Search Results"
-    And I should see "3 items matched Vegetables"
-    And I should see "Filter by Type"
-    
\ No newline at end of file
diff --git a/tests/features/spotlight_widget.feature b/tests/features/spotlight_widget.feature
index 0b01e50..15a87d8 100644
--- a/tests/features/spotlight_widget.feature
+++ b/tests/features/spotlight_widget.feature
@@ -9,7 +9,7 @@ Feature: Add spotlight widget
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add spotlight" in the "CTools modal" region
+      And I add an "Add spotlight" widget in the Panels IPE
     Then I should see "Configure new Add spotlight"
 
   @api @javascript @panopoly_widgets
@@ -22,7 +22,7 @@ Feature: Add spotlight widget
       And I attach the file "test-sm.png" to "files[field_basic_spotlight_items_und_0_fid]"
       And I press the "Upload" button
     #Then I should see the "Crop" button in the "CTools modal" region
-    When I press "Save" in the "CTools modal" region
+    When I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Testing description"
diff --git a/tests/features/table_widget.feature b/tests/features/table_widget.feature
index fffedd4..b6d1b80 100644
--- a/tests/features/table_widget.feature
+++ b/tests/features/table_widget.feature
@@ -9,7 +9,7 @@ Feature: Add table widget
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add table" in the "CTools modal" region
+      And I add an "Add table" widget in the Panels IPE
     Then I should see "Configure new Add table"
 
   @api @javascript
@@ -22,7 +22,7 @@ Feature: Add table widget
       | tablefield_0_cell_1_1 | c-2-r-2      |
       | tablefield_0_cell_2_0 | c-1-r-3      |
       | tablefield_0_cell_2_1 | c-2-r-3      |
-      And I press "Save" in the "CTools modal" region
+      And I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Widget title"
@@ -43,7 +43,7 @@ Feature: Add table widget
       | tablefield_0_cell_1_0 | c-1-r-2 |
       | tablefield_0_cell_1_1 | c-2-r-2 |
       | tablefield_0_cell_1_2 | c-3-r-2 |
-      And I press "Save" in the "CTools modal" region
+      And I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Widget title"
diff --git a/tests/features/text_widget.feature b/tests/features/text_widget.feature
index 32b4f4f..662c063 100644
--- a/tests/features/text_widget.feature
+++ b/tests/features/text_widget.feature
@@ -10,13 +10,13 @@ Feature: Add text widget
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane"
-      And I click "Add text" in the "CTools modal" region
+      And I add an "Add text" widget in the Panels IPE
     Then I should see "Configure new Add text"
     When I fill in the following:
       | Title   | Text widget title       |
       | Editor  | plain_text              |
       | Text    | Testing text body field |
-      And I press "Save" in the "CTools modal" region
+      And I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Text widget title"
diff --git a/tests/features/video_widget.feature b/tests/features/video_widget.feature
index d6a6af0..9038e27 100644
--- a/tests/features/video_widget.feature
+++ b/tests/features/video_widget.feature
@@ -9,7 +9,9 @@ Feature: Add video widget
       And I am viewing a landing page
     When I customize this page with the Panels IPE
       And I click "Add new pane" in the "Boxton Content" region
-      And I click "Add video" in the "CTools modal" region
+      And I wait 2 seconds
+      And I add an "Add video" widget in the Panels IPE
+      And I wait 2 seconds
     Then I should see "Configure new Add video"
 
   # TODO: About 10% of the time this test will hang with Firefox, so for now,
@@ -27,7 +29,7 @@ Feature: Add video widget
     Then I should see the "Remove" button in the "CTools modal" region
       # TODO: Disabled until #2264187 is fixed!
       #And I should see "Edit"
-    When I press "Save" in the "CTools modal" region
+    When I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Testing video"
@@ -48,7 +50,7 @@ Feature: Add video widget
     Then I should see the "Remove" button in the "CTools modal" region
       # TODO: Disabled until #2264187 is fixed!
       #And I should see "Edit"
-    When I press "Save" in the "CTools modal" region
+    When I save the widget in the Panels IPE
       And I press "Save"
       And I wait for the Panels IPE to deactivate
     Then I should see "Testing video"
diff --git a/tests/steps/panopoly_test.behat.inc b/tests/steps/panopoly_test.behat.inc
index a9babd9..c7b7517 100644
--- a/tests/steps/panopoly_test.behat.inc
+++ b/tests/steps/panopoly_test.behat.inc
@@ -4,42 +4,26 @@
  * Provide Behat step-definitions for generic Panopoly tests.
  *
  */
-
+require_once(dirname(__FILE__).'/../features/bootstrap/PanopolyContext.php');
 use Drupal\DrupalExtension\Context\DrupalSubContextInterface;
-use Behat\Behat\Context\BehatContext;
+use Drupal\DrupalDriverManager;
 use Drupal\Component\Utility\Random;
-use Behat\Behat\Event\StepEvent;
-
-class TestSubContext extends BehatContext implements DrupalSubContextInterface {
 
-  /**
-   * Holds a list of temporary users created by our steps.
-   *
-   * @var array
-   */
-  protected $users = array();
+class TestSubContext extends PanopolyContext implements DrupalSubContextInterface {
 
   /**
    * Initializes context.
    */
-  public function __construct(array $parameters = array()) {
+  private $drupal;
+
+  public function __construct(DrupalDriverManager $drupal) {
+          $this->drupal = $drupal;
   }
 
   public static function getAlias() {
     return 'test';
   }
 
-  protected $menu_rebuilt = FALSE;
-  protected $landing_pages = array();
-  protected $private_path_overridden = FALSE;
-
-  /**
-   * Get the session from the parent context.
-   */
-  protected function getSession() {
-    return $this->getMainContext()->getSession();
-  }
-
   /**
    * @Given /^I switch to the frame "([^"]*)"$/
    */
@@ -53,8 +37,7 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
   public function iSwitchOutOfAllFrames() {
     $this->getSession()->switchToIFrame();
   }
-
-  /**
+   /**
    * @Then /^I should see the "([^"]*)" button$/
    */
   public function assertButton($label) {
@@ -77,7 +60,7 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
    * @Then /^I should see the "([^"]*)" button in the "([^"]*)" region$/
    */
   public function assertRegionButton($label, $region) {
-    $regionObj = $this->getMainContext()->getRegion($region);
+    $regionObj = $this->getRegion($region);
     $results = $regionObj->findAll('css', "input[type=submit],input[type=button],button");
     if (!empty($results)) {
       foreach ($results as $result) {
@@ -96,7 +79,7 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
    * @Then /^I should see the "([^"]*)" element in the "([^"]*)" region$/
    */
   public function assertRegionElement($tag, $region) {
-    $regionObj = $this->getMainContext()->getRegion($region);
+    $regionObj = $this->getRegion($region);
     $elements = $regionObj->findAll('css', $tag);
     if (!empty($elements)) {
       return;
@@ -108,18 +91,17 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
    * @Then /^I should not see the "([^"]*)" element in the "([^"]*)" region$/
    */
   public function assertNotRegionElement($tag, $region) {
-    $regionObj = $this->getMainContext()->getRegion($region);
+    $regionObj = $this->getRegion($region);
     $result = $regionObj->findAll('css', $tag);
     if (!empty($result)) {
       throw new \Exception(sprintf('The element "%s" was found in the "%s" region on the page %s', $tag, $region, $this->getSession()->getCurrentUrl()));
     }
   }
-
-  /**
+   /**
    * @Then /^I should see "([^"]*)" in the "([^"]*)" element in the "([^"]*)" region$/
    */
   public function assertRegionElementText($text, $tag, $region) {
-    $regionObj = $this->getMainContext()->getRegion($region);
+    $regionObj = $this()->getRegion($region);
     $results = $regionObj->findAll('css', $tag);
     if (!empty($results)) {
       foreach ($results as $result) {
@@ -135,7 +117,7 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
    * @Then /^I should see "([^"]*)" in the "([^"]*)" element with the "([^"]*)" CSS property set to "([^"]*)" in the "([^"]*)" region$/
    */
   public function assertRegionElementTextCss($text, $tag, $property, $value, $region) {
-    $regionObj = $this->getMainContext()->getRegion($region);
+    $regionObj = $this->getRegion($region);
     $elements = $regionObj->findAll('css', $tag);
     if (empty($elements)) {
       throw new \Exception(sprintf('The element "%s" was not found in the "%s" region on the page %s', $tag, $region, $this->getSession()->getCurrentUrl()));
@@ -154,7 +136,7 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
 
     if (!empty($property)) {
       $style = $element->getAttribute('style');
-      if (strpos($style, "$property: $value") === FALSE) {
+      if (strpos($style, "$property:$value") === FALSE) {
         throw new \Exception(sprintf('The "%s" property does not equal "%s" on the element "%s" in the "%s" region on the page %s', $property, $value, $tag, $region, $this->getSession()->getCurrentUrl()));
       }
     }
@@ -164,7 +146,7 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
    * @Then /^I should see "([^"]*)" in the "([^"]*)" element with the "([^"]*)" attribute set to "([^"]*)" in the "([^"]*)" region$/
    */
   public function assertRegionElementTextAttribute($text, $tag, $attribute, $value, $region) {
-    $regionObj = $this->getMainContext()->getRegion($region);
+    $regionObj = $this->getRegion($region);
     $elements = $regionObj->findAll('css', $tag);
     if (empty($elements)) {
       throw new \Exception(sprintf('The element "%s" was not found in the "%s" region on the page %s', $tag, $region, $this->getSession()->getCurrentUrl()));
@@ -191,12 +173,11 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
       }
     }
   }
-
   /**
    * @Then /^I should not see "([^"]*)" in the "([^"]*)" element in the "([^"]*)" region$/
    */
   public function assertNotRegionElementText($text, $tag, $region) {
-    $regionObj = $this->getMainContext()->getRegion($region);
+    $regionObj = $this->getRegion($region);
     $results = $regionObj->findAll('css', $tag);
     if (!empty($results)) {
       foreach ($results as $result) {
@@ -208,13 +189,39 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
   }
 
   /**
+   * @Then I( should) see the :tag element with the :attribute attribute set to :value in the :region( region)
+   */
+  public function assertRegionElementAttribute($tag, $attribute, $value, $region) {
+    $regionObj = $this->getRegion($region);
+    $elements = $regionObj->findAll('css', $tag);
+    if (empty($elements)) {
+      throw new \Exception(sprintf('The element "%s" was not found in the "%s" region on the page %s', $tag, $region, $this->getSession()->getCurrentUrl()));
+    }
+    if (!empty($attribute)) {
+      $found = FALSE;
+      foreach ($elements as $element) {
+      $attr = $element->getAttribute($attribute);
+        if (!empty($attr)) {
+          $found = TRUE;
+          break;
+        }
+        if (strpos($attr, "$value") === FALSE) {
+          throw new \Exception(sprintf('The "%s" attribute does not equal "%s" on the element "%s" in the "%s" region on the page %s', $attribute, $value, $tag, $region, $this->getSession()->getCurrentUrl()));
+        }
+      }
+      if (!$found) {
+        throw new \Exception(sprintf('The "%s" attribute is not present on the element "%s" in the "%s" region on the page %s', $attribute, $tag, $region, $this->getSession()->getCurrentUrl()));
+      }
+    }
+  }
+  /**
    * Checks, that the region contains text matching specified pattern.
    *
    * @Then /^(?:|I )should see text matching "(?P<pattern>(?:[^"]|\\")*)" in the "(?P<region>[^"]*)"(?:| region)$/
    */
   public function assertRegionMatchesText($pattern, $region)
   {
-    $regionObj = $this->getMainContext()->getRegion($region);
+    $regionObj = $this->getRegion($region);
 
     // Find the text within the region
     $regionText = $regionObj->getText();
@@ -230,7 +237,7 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
    */
   public function assertNotRegionMatchesText($pattern, $region)
   {
-    $regionObj = $this->getMainContext()->getRegion($region);
+    $regionObj = $this->getRegion($region);
 
     // Find the text within the region
     $regionText = $regionObj->getText();
@@ -243,8 +250,8 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
    * @Then /^I should see the image alt "(?P<alt>(?:[^"]|\\")*)" in the "(?P<region>[^"]*)"(?:| region)$/
    */
   public function assertAltRegion($alt, $region) {
-    $alt = $this->getMainContext()->fixStepArgument($alt);
-    $regionObj = $this->getMainContext()->getRegion($region);
+    $alt = $this->fixStepArgument($alt);
+    $regionObj = $this->getRegion($region);
     $element = $regionObj->find('css', 'img');
     $tmp = $element->getAttribute('alt');
     if ($alt == $tmp) {
@@ -256,91 +263,6 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
   }
 
   /**
-   * @BeforeScenario @api
-   *
-   * Bootstrap Drupal so that all Drupal API functions work.
-   */
-  public function drupalMenuRebuild($event) {
-    if (!$this->menu_rebuilt) {
-      $drupal = $this->getMainContext()->getDriver('drupal');
-
-      // Drupal is having a cache or registry issue that is causing tests to
-      // fail (certain CTools plugin functions won't exist.) Menu rebuild does
-      // the trick and fixes any fatal errors due to missing functions!
-      $current_path = getcwd();
-      chdir(DRUPAL_ROOT);
-      menu_rebuild();
-      chdir($current_path);
-
-      // We occasionally get errors about not finding the ctools_get_export_ui()
-      // function, so we force it to be loaded. No idea what's causing this.
-      ctools_include('export-ui');
-
-      $this->menu_rebuilt = TRUE;
-    }
-  }
-
-  /**
-   * @BeforeScenario @api&&@drupal_private_files
-   *
-   * Configure a private files path if one isn't already configured.
-   */
-  public function configurePrivateFiles($event) {
-    $file_private_path = variable_get('file_private_path', '');
-    if (empty($file_private_path)) {
-      $file_public_path = variable_get('file_public_path', conf_path() . '/files');
-      if (empty($file_public_path)) {
-        throw new \Exception('Files must be configured for @drupal_private_files tests to work!');
-      }
-
-      // Set flag for later cleanup.
-      $this->private_path_overridden = TRUE;
-
-      // Create and setup the private path.
-      $file_private_path = $file_public_path . '/' . 'private';
-      variable_set('file_private_path', $file_private_path);
-    }
-  }
-
-  /**
-   * @AfterScenario @api&&@drupal_private_files
-   *
-   * Clean up our temporary private files path.
-   */
-  public function cleanupPrivateFiles($event) {
-    if ($this->private_path_overridden) {
-      variable_del('file_private_path');
-    }
-  }
-
-  /**
-   * @AfterScenario
-   *
-   * Clean up users that were created during testing
-   */
-  public function removeUsers() {
-    if(!empty($this->users)){
-      foreach ($this->users as $user) {
-        $account = user_load_by_name($user->name);
-        user_delete($account->uid);
-      }
-      $this->users = array();
-    }
-  }
-
-  /**
-   * @AfterStep @javascript
-   *
-   * After every step in a @javascript scenario, we want to wait for AJAX
-   * loading to finish.
-   */
-  public function afterStep(StepEvent $event) {
-    if ($event->getResult() === 0) {
-      $this->iWaitForAJAX();
-    }
-  }
-
-  /**
    * @Given /^Panopoly magic live previews are disabled$/
    *
    * Disable live previews via Panopoly Magic.
@@ -398,20 +320,11 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
   }
 
   /**
-   * @Given /^(?:|I )wait for AJAX loading to finish$/
-   *
-   * Wait for the jQuery AJAX loading to finish. ONLY USE FOR DEBUGGING!
-   */
-  public function iWaitForAJAX() {
-    $this->getSession()->wait(5000, 'typeof jQuery !== "undefined" && jQuery.active === 0');
-  }
-
-  /**
    * @Given /^I log in with the One Time Login Url$/
    */
   public function iLogInWithTheOneTimeLoginUrl() {
-    if ($this->getMainContext()->loggedIn()) {
-      $this->getMainContext()->logOut();
+    if ($this->loggedIn()) {
+      $this->logOut();
     }
 
     $random = new Random;
@@ -425,12 +338,12 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
     $user->mail = "{$user->name}@example.com";
 
     // Create a new user.
-    $this->getMainContext()->getDriver()->userCreate($user);
+    $this->getDriver()->userCreate($user);
 
     $this->users[$user->name] = $this->user = $user;
 
-    $base_url = rtrim($this->getMainContext()->locatePath('/'), '/');
-    $login_link = $this->getMainContext()->getDriver('drush')->drush('uli', array(
+    $base_url = rtrim($this->locatePath('/'), '/');
+    $login_link = $this->getDriver('drush')->drush('uli', array(
       "'$user->name'",
       '--browser=0',
       "--uri=${base_url}",
@@ -438,70 +351,11 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
     // Trim EOL characters. Required or else visiting the link won't work.
     $login_link = trim($login_link);
     $login_link = str_replace("/login", '', $login_link);
-    $this->getSession()->visit($this->getMainContext()->locatePath($login_link));
+    $this->getSession()->visit($this->locatePath($login_link));
     return TRUE;
   }
 
   /**
-   * Create a new Landing Page.
-   *
-   * @param string $name
-   *   The internal machine name for the new Page.
-   * @param string $path
-   *   The path for the new Page.
-   * @param string $title
-   *   The title for the new Page.
-   */
-  protected function createPage($name, $path, $title) {
-    $page_task = page_manager_get_task('page');
-
-    $subtask = page_manager_page_new();
-    $subtask->name = $name;
-    $subtask->path = $path;
-    $subtask->admin_title = $title;
-    $subtask->admin_description = '';
-    $subtask->menu = array(
-      'type' => 'none',
-    );
-
-    $display = new stdClass();
-    $display->layout = 'boxton';
-    $display->title = $title;
-    $display->panels = array();
-    $display->content = array();
-
-    $plugin = page_manager_get_task_handler('panel_context');
-    $handler = page_manager_new_task_handler($plugin);
-    $handler->conf['title'] = t('Landing page');
-    $handler->conf['display'] = $display;
-    $handler->conf['pipeline'] = 'ipe';
-
-    // Assemble a new $page cache and assign it to our page subtask and task
-    // handler.
-    $page = new stdClass();
-    page_manager_page_new_page_cache($subtask, $page);
-    page_manager_handler_add_to_page($page, $handler);
-    page_manager_save_page_cache($page);
-
-    // Mark this page for deletion at the end of the scenario.
-    $this->landing_pages[] = $name;
-  }
-
-  /**
-   * @AfterScenario @api
-   *
-   * Delete landing pages after a Scenario has finished.
-   */
-  public function cleanupPages() {
-    foreach ($this->landing_pages as $name) {
-      if ($page = page_manager_page_load($name)) {
-        page_manager_page_delete($page);
-      }
-    }
-    $this->landing_pages = array();
-  }
-
-  /**
    * @Given /^I am viewing a landing page$/
    */
   public function iAmViewingALandingPage() {
@@ -511,7 +365,7 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
 
     $this->createPage($name, $path, 'Testing Landing Page');
 
-    $this->getSession()->visit($this->getMainContext()->locatePath($path));
+    $this->getSession()->visit($this->locatePath($path));
   }
 
   /**
@@ -538,4 +392,81 @@ class TestSubContext extends BehatContext implements DrupalSubContextInterface {
       throw new \Exception('General exception from '.__FUNCTION__);
     }
   }
+
+  /**
+   * @When /^(?:|I )wait for the Panels IPE to activate$/
+   *
+   * Wait until the Panels IPE is activated.
+   */
+  public function waitForIPEtoActivate() {
+    $this->getSession()->wait(5000, 'jQuery(".panels-ipe-editing").length > 0');
+  }
+
+  /**
+   * @When /^(?:|I )wait for the Panels IPE to deactivate$/
+   *
+   * Wait until the Panels IPE is deactivated.
+   */
+  public function waitForIPEtoDeactivate() {
+    $this->getSession()->wait(5000, 'jQuery(".panels-ipe-editing").length === 0');
+  }
+
+  /**
+   * @When /^(?:|I )customize this page with the Panels IPE$/
+   *
+   * Enable the Panels IPE if it's available on the current page.
+   */
+  public function customizeThisPageIPE() {
+    $this->getSession()->wait(5000, 'jQuery(".panels-ipe-customize-page").length > 0');
+    $this->getSession()->getPage()->clickLink('Customize this page');
+    $this->waitForIPEtoActivate();
+  }
+
+/**
+   * @When I add a/an :widget widget in the Panels IPE
+   *
+   * Click the Save button in the IPE and wait until the modal goes away.
+   */
+  public function addWidget($widget) {
+    //wait
+    $this->getSession()->wait(5000, 'jQuery(".panels-section-column-categories").length > 0');
+    //click the link
+    $regionObj = $this->getRegion("CTools modal");
+
+    $link = $regionObj->findLink($widget);
+    if (empty($link)) {
+      throw new \Exception(sprintf("The widget '%s' was not found in the region 'CTools modal' on the page %s", $widget, $this->getSession()->getCurrentUrl()));
+    }
+    $link->click();
+    //wait again
+    $this->getSession()->wait(5000, 'jQuery(".modal-content .edit-buttons").length > 0');
+    
+  }
+
+/**
+   * @When I save the widget in the Panels IPE
+   *
+   * Click the Save button in the IPE and wait until the modal goes away.
+   */
+  public function saveWidgetSettings() {
+    //click the button
+    $regionObj = $this->getRegion("CTools modal");
+
+    $buttonObj = $regionObj->findButton("Save");
+    if (empty($buttonObj)) {
+      throw new \Exception(sprintf("The button 'Save' was not found in the region 'CTools modal' on the page %s", $this->getSession()->getCurrentUrl()));
+    }
+    $regionObj->pressButton('Save');
+    //wait
+    $this->getSession()->wait(5000, 'jQuery(".panels-ipe-pseudobutton-container").length === 0');
+  }
+
+ /**
+   * @When /^(?:|I )wait for live preview to finish$/
+   *
+   * Wait until the live preview to finish.
+   */
+  public function waitForLivePreview() {
+    $this->getSession()->wait(5000, 'jQuery(".form-submit").value == "Save"');
+  }
 }
diff --git a/tests/steps/panopoly_test_media.behat.inc b/tests/steps/panopoly_test_media.behat.inc
index 5701acf..0856de0 100644
--- a/tests/steps/panopoly_test_media.behat.inc
+++ b/tests/steps/panopoly_test_media.behat.inc
@@ -5,15 +5,19 @@
  *
  * @todo Also, add a cleanup function to delete files uploaded.
  */
-
+require_once(dirname(__FILE__).'/../features/bootstrap/PanopolyContext.php');
 use Drupal\DrupalExtension\Context\DrupalSubContextInterface;
-use Behat\Behat\Context\BehatContext;
+use Drupal\DrupalDriverManager;
+use Drupal\DrupalExtension\Context\RawDrupalContext;
 
-class MediaSubContext extends BehatContext implements DrupalSubContextInterface {
+class MediaSubContext extends PanopolyContext implements DrupalSubContextInterface {
   /**
    * Initializes context.
    */
-  public function __construct(array $parameters = array()) {
+  private $drupal;
+  
+  public function __construct(DrupalDriverManager $drupal) {
+          $this->drupal = $drupal;
   }
 
   public static function getAlias() {
@@ -21,13 +25,6 @@ class MediaSubContext extends BehatContext implements DrupalSubContextInterface
   }
 
   /**
-   * Get the session from the parent context.
-   */
-  protected function getSession() {
-    return $this->getMainContext()->getSession();
-  }
-
-  /**
    * @Given /^I click the fake "([^"]*)" button$/
    */
   public function iClickTheFakeButton($text) {
diff --git a/tests/steps/panopoly_test_panels.behat.inc b/tests/steps/panopoly_test_panels.behat.inc
index 593c8db..126e6e2 100644
--- a/tests/steps/panopoly_test_panels.behat.inc
+++ b/tests/steps/panopoly_test_panels.behat.inc
@@ -5,63 +5,23 @@
  *
  * @todo This should become panels.behat.inc in the Panels module!
  */
-
+require_once(dirname(__FILE__).'/../features/bootstrap/PanopolyContext.php');
 use Drupal\DrupalExtension\Context\DrupalSubContextInterface;
-use Behat\Behat\Context\BehatContext;
+use Drupal\DrupalDriverManager;
+use Drupal\DrupalExtension\Context\RawDrupalContext;
 
-class PanelsSubContext extends BehatContext implements DrupalSubContextInterface {
+class PanelsSubContext extends PanopolyContext implements DrupalSubContextInterface {
   /**
    * Initializes context.
    */
-  public function __construct(array $parameters = array()) {
+  private $drupal;
+  
+  public function __construct(DrupalDriverManager $drupal) {
+          $this->drupal = $drupal;
   }
 
   public static function getAlias() {
     return 'panels';
   }
 
-  /**
-   * Get the session from the parent context.
-   */
-  protected function getSession() {
-    return $this->getMainContext()->getSession();
-  }
-
-  /**
-   * @When /^(?:|I )wait for the Panels IPE to activate$/
-   *
-   * Wait until the Panels IPE is activated.
-   */
-  public function waitForIPEtoActivate() {
-    $this->getSession()->wait(5000, 'jQuery(".panels-ipe-editing").length > 0');
-  }
-
-  /**
-   * @When /^(?:|I )wait for the Panels IPE to deactivate$/
-   *
-   * Wait until the Panels IPE is deactivated.
-   */
-  public function waitForIPEtoDeactivate() {
-    $this->getSession()->wait(5000, 'jQuery(".panels-ipe-editing").length === 0');
-  }
-
-  /**
-   * @When /^(?:|I )customize this page with the Panels IPE$/
-   *
-   * Enable the Panels IPE if it's available on the current page.
-   */
-  public function customizeThisPageIPE() {
-    $this->getSession()->getPage()->clickLink('Customize this page');
-    $this->waitForIPEtoActivate();
-  }
-
- /**
-   * @When /^(?:|I )wait for live preview to finish$/
-   *
-   * Wait until the live preview to finish.
-   */
-  public function waitForLivePreview() {
-    $this->getSession()->wait(5000, 'jQuery(".form-submit").value == "Save"');
-  }
-
 }
diff --git a/tests/steps/panopoly_test_wysiwyg.behat.inc b/tests/steps/panopoly_test_wysiwyg.behat.inc
index 8749ff0..683252b 100644
--- a/tests/steps/panopoly_test_wysiwyg.behat.inc
+++ b/tests/steps/panopoly_test_wysiwyg.behat.inc
@@ -5,15 +5,19 @@
  *
  * @todo This should move to the WYSIWYG module eventually
  */
-
+require_once(dirname(__FILE__).'/../features/bootstrap/PanopolyContext.php');
 use Drupal\DrupalExtension\Context\DrupalSubContextInterface;
-use Behat\Behat\Context\BehatContext;
+use Drupal\DrupalDriverManager;
+use Drupal\DrupalExtension\Context\RawDrupalContext;
 
-class WysiwygSubContext extends BehatContext implements DrupalSubContextInterface {
+class WysiwygSubContext extends PanopolyContext implements DrupalSubContextInterface {
   /**
    * Initializes context.
    */
-  public function __construct(array $parameters = array()) {
+  private $drupal;
+  
+  public function __construct(DrupalDriverManager $drupal) {
+          $this->drupal = $drupal;
   }
 
   public static function getAlias() {
@@ -21,13 +25,6 @@ class WysiwygSubContext extends BehatContext implements DrupalSubContextInterfac
   }
 
   /**
-   * Get the session from the parent context.
-   */
-  protected function getSession() {
-    return $this->getMainContext()->getSession();
-  }
-
-  /**
    * Get the instance variable to use in Javascript.
    *
    * @param string $instanceId
