diff --git CHANGELOG.txt CHANGELOG.txt
index 85febd0..00adbe5 100644
--- CHANGELOG.txt
+++ CHANGELOG.txt
@@ -1,6 +1,8 @@
 
 Skinr 7.x-2.x, xxxx-xx-xx
 -------------------------
+Issue #1149726 by moonray: Created an 'add skin configuration' form to edit any
+  element on the site.
 Issue #1044222 by sun, moonray: Fixed tests.
 Issue #1044222 by sun, moonray: Removed skin configuration functionality from
   third-party forms.
diff --git js/skinr_ui.js js/skinr_ui.js
new file mode 100644
index 0000000..42b93aa
--- /dev/null
+++ js/skinr_ui.js
@@ -0,0 +1,86 @@
+/**
+ * @file
+ * UI enhancements for Skinr UI.
+ */
+
+(function($) {
+
+Drupal.behaviors.skinrUIAdd = {
+  attach: function (context, settings) {
+    $('form#skinr-ui-add', context).once('skinr-ui-add', function () {
+      Drupal.skinrUIAdd.attachUpdateSelects(this, settings);
+    });
+  }
+};
+
+Drupal.skinrUIAdd = {
+  /**
+   * Implements dependent select dropdowns on the 'Manage fields' screen.
+   */
+  attachUpdateSelects: function(context, settings) {
+    var elementOptions = settings.elementOptions;
+
+    // Store the default text of widget selects.
+    $('#edit-element', context).each(function () {
+      this.initialValue = this.options[0].text;
+    });
+
+    // 'Type' select updates 'Element' select.
+    $('#edit-type', context).each(function () {
+      this.targetSelect = $('#edit-element', context);
+
+      $(this).bind('change keyup', function () {
+        var selectedType = this.options[this.selectedIndex].value;
+        var options = (selectedType in elementOptions ? elementOptions[selectedType] : []);
+        this.targetSelect.skinrUIPopulateOptions(options);
+      });
+
+      // Trigger change on initial pageload to get the right element options
+      // when type comes pre-selected (on failed validation).
+      $(this).trigger('change', false);
+    });
+  }
+};
+
+/**
+ * Populates options in a select input.
+ */
+jQuery.fn.skinrUIPopulateOptions = function (options, selected) {
+  return this.each(function () {
+    var disabled = false;
+    if (options.length == 0) {
+      options = [this.initialValue];
+      disabled = true;
+    }
+
+    // If possible, keep the same element selected when changing type.
+    // This is based on textual value, since the internal value might be
+    // different.
+    var previousSelectedText = this.options[this.selectedIndex].text;
+
+    var html = '';
+    jQuery.each(options, function (value, text) {
+      if (typeof(text) == 'object') {
+        // Add option group and strip label indent.
+        html += '<optgroup label="' + value.replace(/^(&#160;)*\s*/, '') + '">';
+        jQuery.each(text, function (value, text) {
+          // Figure out which value should be selected. The 'selected' param
+          // takes precedence.
+          var is_selected = ((typeof selected != 'undefined' && value == selected) || (typeof selected == 'undefined' && text == previousSelectedText));
+          html += '<option value="' + value + '"' + (is_selected ? ' selected="selected"' : '') + '>' + text + '</option>';
+        });
+        html += '</optgroup>';
+      }
+      else {
+        // Figure out which value should be selected. The 'selected' param
+        // takes precedence.
+        var is_selected = ((typeof selected != 'undefined' && value == selected) || (typeof selected == 'undefined' && text == previousSelectedText));
+        html += '<option value="' + value + '"' + (is_selected ? ' selected="selected"' : '') + '>' + text + '</option>';
+      }
+    });
+
+    $(this).html(html).attr('disabled', disabled ? 'disabled' : '');
+  });
+};
+
+})(jQuery);
diff --git modules/block.skinr.inc modules/block.skinr.inc
index 7721b6b..e506b45 100644
--- modules/block.skinr.inc
+++ modules/block.skinr.inc
@@ -30,30 +30,43 @@ function block_skinr_config_info() {
 }
 
 /**
- * Skinr form index handler.
- *
- * @param $op
- *   What kind of action is being performed. Possible values:
- *   - 'form': the form elements for Skinr are being inserted in a form.
- *   - 'submit': the form has been submitted.
- * @param &$form
- *   - For 'form', passes in the $form parameter from hook_form_alter().
- *   - For 'submit', passes in the $form parameter from hook_form_submit().
- * @param $form_state
- *   - For 'form', passes in the $form_state parameter from hook_form_alter().
- *   - For 'submit', passes in the $form_state parameter from hook_form_submit().
- *
- * @return
- *   The index where we can find our values in Skinr's data structure.
+ * Implements hook_skinr_ui_element_options().
  */
-function block_skinr_form_index_handler($op, &$form, $form_state) {
-  switch ($op) {
-    case 'form':
-      return $form['module']['#value'] . '__' . $form['delta']['#value'];
+function block_skinr_ui_element_options($theme_name = NULL) {
+  $options = array('block' => array());
+
+  $themes = array();
+  if ($theme_name) {
+    // Only process the given theme.
+    $themes[] = $theme_name;
+  }
+  else {
+    // Process all enabled themes.
+    $options = array();
+    foreach (list_themes() as $theme) {
+      if (!$theme->status) {
+        continue;
+      }
+      $themes[] = $theme->name;
+    }
+  }
 
-    case 'submit':
-      return $form_state['values']['module'] . '__' . $form_state['values']['delta'];
+  // Load all enabled blocks.
+  foreach ($themes as $theme) {
+    $blocks = _block_rehash($theme);
+    foreach ($blocks as $block) {
+      // @todo Should we only return enabled blocks, or should we return
+      //   them all?
+      if (!$block['status']) {
+        continue;
+      }
+      $name = $block['module'] . '__' . $block['delta'];
+      $options['block'][$name] = $block['info'];
+    }
   }
+  asort($options['block']);
+
+  return $options;
 }
 
 /**
diff --git modules/comment.skinr.inc modules/comment.skinr.inc
index 0e17809..bbada37 100644
--- modules/comment.skinr.inc
+++ modules/comment.skinr.inc
@@ -31,6 +31,21 @@ function comment_skinr_config_info() {
 }
 
 /**
+ * Implements hook_skinr_ui_element_options().
+ */
+function comment_skinr_ui_element_options($theme_name = NULL) {
+  $options = array('comment' => array());
+
+  $types = node_type_get_types();
+  foreach ($types as $type) {
+    $options['comment'][$type->type] = $type->name;
+  }
+  asort($options['comment']);
+
+  return $options;
+}
+
+/**
  * Skinr form index handler.
  *
  * @param $op
diff --git modules/node.skinr.inc modules/node.skinr.inc
index d64fee0..dd589dd 100644
--- modules/node.skinr.inc
+++ modules/node.skinr.inc
@@ -30,6 +30,21 @@ function node_skinr_config_info() {
 }
 
 /**
+ * Implements hook_skinr_ui_element_options().
+ */
+function node_skinr_ui_element_options($theme_name = NULL) {
+  $options = array('node' => array());
+
+  $types = node_type_get_types();
+  foreach ($types as $type) {
+    $options['node'][$type->type] = $type->name;
+  }
+  asort($options['node']);
+
+  return $options;
+}
+
+/**
  * Skinr form index handler.
  *
  * @param $op
diff --git modules/panels.skinr.inc modules/panels.skinr.inc
index 7ac69b5..febce56 100644
--- modules/panels.skinr.inc
+++ modules/panels.skinr.inc
@@ -27,6 +27,137 @@ function panels_skinr_config_info() {
 }
 
 /**
+ * Implements hook_skinr_ui_element_options().
+ */
+function panels_skinr_ui_element_options($theme_name = NULL) {
+  $options = array('panels' => array());
+
+  // Load displays for pages.
+  if (module_exists('page_manager')) {
+    module_load_include('inc', 'page_manager', 'page_manager.admin');
+    ctools_include('plugins', 'panels');
+
+    $tasks = page_manager_get_tasks_by_type('page');
+    $pages = array('operations' => array());
+
+    page_manager_get_pages($tasks, $pages);
+
+    foreach ($pages['rows'] as $task_name => $page) {
+      // Skip disabled pages.
+      if ($pages['disabled'][$task_name]) {
+        continue;
+      }
+
+      list($task_id, $subtask_id) = page_manager_get_task_id($task_name);
+
+      $task = $tasks[$task_id];
+      if (empty($task)) {
+        continue;
+      }
+
+      if ($subtask_id) {
+        $subtask = page_manager_get_task_subtask($task, $subtask_id);
+        if (empty($subtask)) {
+          continue;
+        }
+      }
+      else {
+        $subtask = $task;
+        $subtask['name'] = '';
+      }
+
+      $handlers = page_manager_load_sorted_handlers($task, $subtask_id);
+      $dids = array();
+      foreach ($handlers as $handler) {
+        if ($handler->handler != 'panel_context') {
+          continue;
+        }
+        $display = panels_load_display($handler->conf['did']);
+
+        // Output display.
+        /*
+        // No use in outputting display if we can't add classes to it. It's
+        // only possible with a custom template override.
+        $name = 'display__' . $display->did;
+        $title = t('Display');
+        $options['panels']['&#160;&#160;&#160;' . $page['data']['title']['data']][$name] = $title;
+        */
+
+        // Output regions.
+        /*
+        // No use in outputting regions if we can't add classes to them.
+        $layout = panels_get_layout($display->layout);
+        foreach ($layout['regions'] as $region_id => $title) {
+          $name = 'region__' . $display->did . '__' . $region_id;
+          $title = t('Region: @region', array('@region' => $title));
+          $options['panels']['&#160;&#160;&#160;' . $page['data']['title']['data']][$name] = $title;
+        }
+        */
+
+        // Output panes.
+        foreach ($display->content as $pid => $pane) {
+          $name = 'pane__' . $display->did . '__' . $pid;
+          $title = t('Pane: @pane_id', array('@pane_id' => $pid));
+          $options['panels']['&#160;&#160;&#160;' . $page['data']['title']['data']][$name] = $title;
+        }
+      }
+    }
+  }
+  ksort($options['panels']);
+
+  // Load displays for panel nodes.
+  if (module_exists('panels_node')) {
+    $query = new EntityFieldQuery;
+    $nids = $query
+      ->entityCondition('entity_type', 'node')
+      ->entityCondition('bundle', 'panel')
+      ->propertyCondition('status', '1')
+      ->propertyOrderBy('title', 'ASC')
+      ->execute();
+    if ($nids) {
+      foreach (node_load_multiple(array_keys($nids['node'])) as $node) {
+        $display = panels_load_display($node->panels_node['did']);
+        $category = t('Panel node: @title', array('@title' => $node->title));
+
+        // Output display.
+        /*
+        // No use in outputting display if we can't add classes to it. It's
+        // only possible with a custom template override.
+        $name = 'display__' . $display->did;
+        $title = t('Display');
+        $options['panels']['&#160;&#160;&#160;' . $category][$name] = $title;
+        */
+
+        // Output regions.
+        /*
+        // No use in outputting regions if we can't add classes to them.
+        $layout = panels_get_layout($display->layout);
+        foreach ($layout['regions'] as $region_id => $title) {
+          $name = 'region__' . $display->did . '__' . $region_id;
+          $title = t('Region: @region', array('@region' => $title));
+          $options['panels']['&#160;&#160;&#160;' . $category][$name] = $title;
+        }
+        */
+
+        // Output panes.
+        foreach ($display->content as $pid => $pane) {
+          $name = 'pane__' . $display->did . '__' . $pid;
+          $title = t('Pane: @pane_id', array('@pane_id' => $pid));
+          $options['panels']['&#160;&#160;&#160;' . $category][$name] = $title;
+        }
+      }
+    }
+  }
+
+  // Load displays for mini panels.
+  if (module_exists('panels_mini')) {
+
+  }
+
+  return $options;
+}
+
+/**
  * Skinr preprocess hook callback.
  *
  * @param &$form
@@ -77,7 +208,7 @@ function panels_panels_pane_content_alter(&$content, $pane, $display_args, $cont
     $element = 'pane__' . $pane->did . '__' . $pane->pid;
     $content->admin_links[] = array(
       'title' => t('Edit skin'),
-      'href' => 'admin/appearance/skinr/edit/nojs/panels/' . $element,
+      'href' => 'admin/appearance/skinr/edit/nojs/panels/' . $element . '/configure',
       'query' => drupal_get_destination(),
     );
   }
diff --git modules/views.skinr.inc modules/views.skinr.inc
index ea34136..d94b778 100644
--- modules/views.skinr.inc
+++ modules/views.skinr.inc
@@ -31,6 +31,26 @@ function views_skinr_config_info() {
 }
 
 /**
+ * Implements hook_skinr_ui_element_options().
+ */
+function views_skinr_ui_element_options($theme_name = NULL) {
+  $options = array('views' => array());
+
+  $views = views_get_all_views();
+  // Load all enabled blocks.
+  foreach ($views as $view) {
+    foreach ($view->display as $display) {
+      $name = $view->name . '__' . $display->id;
+      // Fake indentation for views sub-items for better visual result.
+      $options['views']['&#160;&#160;&#160;' . $view->human_name][$name] = $display->display_title;
+    }
+  }
+  ksort($options['views']);
+
+  return $options;
+}
+
+/**
  * Skinr form preprocess hook callback.
  *
  * @param &$form
diff --git skinr.skinr.inc skinr.skinr.inc
index e304698..3895bfe 100644
--- skinr.skinr.inc
+++ skinr.skinr.inc
@@ -67,6 +67,21 @@ function skinr_skinr_config_info() {
 }
 
 /**
+ * Implements hook_skinr_ui_element_options().
+ */
+function skinr_skinr_ui_element_options($theme_name = NULL) {
+  $options = array('rules' => array());
+
+  $rules = skinr_rule_load_multiple();
+  foreach ($rules as $rule) {
+    $options['rules'][$rule->rid] = $rule->title;
+  }
+  asort($options['rules']);
+
+  return $options;
+}
+
+/**
  * Skinr form index handler.
  *
  * @param $op
diff --git skinr_ui.admin.inc skinr_ui.admin.inc
index 6e117df..528a5e2 100644
--- skinr_ui.admin.inc
+++ skinr_ui.admin.inc
@@ -1134,6 +1134,118 @@ function skinr_ui_import_form_submit(&$form, &$form_state) {
 }
 
 /**
+ * Return an array of element options for a module.
+ *
+ * If no field type is provided, returns a nested array of all element options,
+ * keyed by module.
+ */
+function skinr_ui_element_options($module = NULL) {
+  $cache = &drupal_static(__FUNCTION__);
+
+  if (!isset($cache)) {
+    $cache = array();
+
+    foreach (skinr_implements() as $name => $extension) {
+      $function = $name . '_skinr_ui_element_options';
+      if (function_exists($function)) {
+        $options = $function();
+        if (isset($options) && is_array($options)) {
+          $cache += $options;
+        }
+      }
+    }
+  }
+
+  return $cache;
+}
+
+/**
+ * Form builder for the add a Skinr configuration form.
+ *
+ * @ingroup forms
+ */
+function skinr_ui_add($form, &$form_state) {
+  /*
+  $themes = list_themes();
+  ksort($themes);
+
+  $theme_options = array();
+  foreach ($themes as $theme) {
+    if (!$theme->status) {
+      continue;
+    }
+    $theme_options[$theme->name] = $theme->info['name'];
+  }
+  $form['theme'] = array(
+    '#type' => 'select',
+    '#title' => t('Theme'),
+    '#options' => $theme_options,
+    '#default_value' => skinr_current_theme(),
+    '#required' => TRUE,
+  );
+  */
+
+  $config = skinr_get_config_info();
+  $modules = system_rebuild_module_data();
+
+  $type_options = array();
+  foreach ($config as $module => $ignore) {
+    $name = $module;
+    if ($module == 'rules') {
+      // Exception for rules.
+      $name = t('Rules');
+    }
+    elseif (isset($modules[$module])) {
+      $name = $modules[$module]->info['name'];
+    }
+    $type_options[$module] = $name;
+  }
+
+  $form['type'] = array(
+    '#type' => 'select',
+    '#title' => t('Type'),
+    '#options' => $type_options,
+    '#required' => TRUE,
+  );
+
+  $element_options = skinr_ui_element_options();
+  $form['element'] = array(
+    '#type' => 'select',
+    '#title' => t('Element'),
+    '#options' => $element_options,
+    '#required' => TRUE,
+  );
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add'),
+  );
+
+  $form['#attached']['js'][] = drupal_get_path('module', 'skinr_ui') . '/js/skinr_ui.js';
+
+  // Add settings for the update selects behavior.
+  $form['#attached']['js'][] = array(
+    'type' => 'setting',
+    'data' => array('elementOptions' => skinr_ui_element_options()),
+  );
+
+  return $form;
+}
+
+function skinr_ui_add_validate($form, &$form_state) {
+  // @todo Do some validation.
+}
+
+function skinr_ui_add_submit($form, &$form_state) {
+  $destination = drupal_get_destination();
+  if ($destination['destination'] == 'admin/appearance/skinr/add') {
+    $destination['destination'] = 'admin/appearance/skinr';
+  }
+  $form_state['redirect'] = array('admin/appearance/skinr/edit/nojs/' . check_plain($form_state['values']['type']) . '/' . check_plain($form_state['values']['element']), array('query' => $destination));
+}
+
+/**
  * Form builder for the skinr settings delete confirmation form.
  *
  * @param $theme
diff --git skinr_ui.api.php skinr_ui.api.php
new file mode 100644
index 0000000..a047a64
--- /dev/null
+++ skinr_ui.api.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * @file
+ * This file contains no working PHP code; it exists to provide additional documentation
+ * for doxygen as well as to document hooks in the standard Drupal manner.
+ */
+
+/**
+ * @mainpage Skinr UI API Manual
+ *
+ * Topics:
+ * - @ref skinr_ui_hooks
+ */
+
+/**
+ * @defgroup skinr_ui_hooks Skinr UIs hooks
+ * @{
+ * Hooks that can be implemented by other modules in order to implement the
+ * Skinr UI API.
+ */
+
+/**
+ * Provide available element options for a module.
+ *
+ * @param $theme_name
+ *   The name of the theme to provide available options for.
+ *
+ * @return
+ *   An array consting of arrays of element options, keyed by the implementing
+ *   module name. Each of these array consists of element name => title pairs.
+ */
+function hook_skinr_ui_element_options($theme_name = NULL) {
+  $options['block'] = array(
+    'system__main_menu' => 'Main Menu',
+    'system__navigation' => 'Navigation',
+    'search__form' => 'Search form',
+  );
+
+  return $options;
+}
+
+/**
+ * @}
+ */
diff --git skinr_ui.module skinr_ui.module
index 4016c48..b603633 100644
--- skinr_ui.module
+++ skinr_ui.module
@@ -128,6 +128,16 @@ function skinr_ui_menu() {
     'file' => 'skinr_ui.admin.inc',
   );
 
+  // Add skin settings for an element.
+  $items['admin/appearance/skinr/add'] = array(
+    'title' => 'Add skin configuration',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('skinr_ui_add'),
+    'type' => MENU_LOCAL_ACTION,
+    'access arguments' => array('edit skin settings'),
+    'file' => 'skinr_ui.admin.inc',
+  );
+
   // Configure skin settings for an element.
   $items['admin/appearance/skinr/edit/%skinr_js/%/%'] = array(
     'title' => 'Edit skin',
@@ -147,13 +157,13 @@ function skinr_ui_menu() {
     'context' => MENU_CONTEXT_INLINE,
   );
 
-  // Delete a skin configuration.
+  // Delete skin settings.
   $items['admin/appearance/skinr/delete/%skinr_skin'] = array(
-    'title' => 'Delete skin',
+    'title' => 'Delete skin configuration',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('skinr_ui_delete_confirm', 4),
     'type' => MENU_CALLBACK,
-    'access arguments' => array('administer skinr'),
+    'access arguments' => array('edit skin settings'),
     'file' => 'skinr_ui.admin.inc',
   );
 
diff --git tests/skinr_ui.test tests/skinr_ui.test
index 26a26a1..8aecc9f 100644
--- tests/skinr_ui.test
+++ tests/skinr_ui.test
@@ -274,3 +274,290 @@ class SkinrUIAdminTestCase extends SkinrUITestCase {
     // @todo Should we check the filtering and update options functionality?
   }
 }
+
+/**
+ * Tests UI functionality for Block plugin.
+ */
+class SkinrUIPluginTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Plugins UI',
+      'description' => 'Tests Skinr UI functionality for functionality plugin.',
+      'group' => 'Skinr',
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('block', 'php', 'page_manager', 'panels_node', 'panels_mini', 'views_ui', 'skinr', 'skinr_ui'));
+
+    $this->admin_user = $this->drupalCreateUser(array(
+      'administer blocks',
+      'administer views',
+      'access comments',
+      'access content',
+      'post comments',
+      'skip comment approval',
+
+      'use PHP for settings',
+      'use page manager',
+      'administer page manager',
+
+      'access contextual links',
+      'administer skinr',
+      'edit skin settings',
+      'edit advanced skin settings',
+      'bypass node access',
+    ));
+    $this->drupalLogin($this->admin_user);
+  }
+
+  /**
+   * Asserts that a select option in the current page exists.
+   *
+   * @param $name
+   *   Id of select field to assert.
+   * @param $option
+   *   Option to assert.
+   * @param $message
+   *   Message to display.
+   * @return
+   *   TRUE on pass, FALSE on fail.
+   */
+  protected function assertOptionExists($name, $option, $message = '') {
+    $elements = $this->xpath('//select[@name=:name]//option[@value=:option]', array(':name' => $name, ':option' => $option));
+    return $this->assertTrue(isset($elements[0]), $message ? $message : t('Option @option for field @name exists.', array('@option' => $option, '@name' => $name)), t('Browser'));
+  }
+
+  /**
+   * Tests block plugin.
+   */
+  function testBlock() {
+    // Enable user menu block for the first sidebar.
+    // @see http://drupal.org/node/913086
+    $default_theme = variable_get('theme_default', 'bartik');
+    db_merge('block')
+      ->key(array(
+        'theme' => $default_theme,
+        'module' => 'system',
+        'delta' => 'user-menu',
+      ))
+      ->fields(array(
+        'status' => 1,
+        'region' => 'sidebar_first',
+        'pages' => '',
+      ))
+      ->execute();
+
+    // Get front page.
+    $this->drupalGet('');
+
+    // Make sure our contextual link appears on the page.
+    $this->assertLinkByHref('admin/appearance/skinr/edit/nojs/block/system__user-menu/configure', 0, 'Contexual link to edit block\'s skin configuration was found.');
+
+    // Make sure this block's options are returned.
+    $this->drupalGet('admin/appearance/skinr/add');
+    $this->assertOptionExists('element', 'system__user-menu', 'User menu block was returned by block_skinr_ui_element_options().');
+  }
+
+  /**
+   * Tests comment plugin.
+   */
+  function testComment() {
+    // Create a node.
+    $node1 = $this->drupalCreateNode(array('type' => 'article'));
+
+    // Go to node.
+    $uri = entity_uri('node', $node1);
+    $this->drupalGet($uri['path']);
+
+    // Add a comment to the node. With bartik the contextual links won't
+    // display until there is at least one comment.
+    $edit = array(
+      'comment_body[und][0][value]' => $this->randomString(128),
+    );
+    $this->drupalPost(NULL, $edit, t('Save'));
+
+    // Make sure our contextual link appears on the page.
+    $this->assertLinkByHref('admin/appearance/skinr/edit/nojs/comment/article/configure', 0, 'Contexual link to edit comment\'s skin configuration was found.');
+
+    // Make sure this block's options are returned.
+    $this->drupalGet('admin/appearance/skinr/add');
+    $this->assertOptionExists('element', 'article', 'Node type was returned by node_skinr_ui_element_options().');
+  }
+
+  /**
+   * Tests node plugin.
+   */
+  function testNode() {
+    // Create a node.
+    $node1 = $this->drupalCreateNode(array('type' => 'article'));
+
+    // Go to node.
+    $uri = entity_uri('node', $node1);
+    $this->drupalGet($uri['path']);
+
+    // Make sure our contextual link appears on the page.
+    $this->assertLinkByHref('admin/appearance/skinr/edit/nojs/node/article/configure', 0, 'Contexual link to edit node\'s skin configuration was found.');
+
+    // Make sure this block's options are returned.
+    $this->drupalGet('admin/appearance/skinr/add');
+    $this->assertOptionExists('element', 'article', 'Node type was returned by node_skinr_ui_element_options().');
+  }
+
+  protected function panelData() {
+    $object = <<<END
+    \$page = new stdClass;
+    \$page->disabled = FALSE;
+    \$page->api_version = 1;
+    \$page->name = 'skinr_ui_panel';
+    \$page->task = 'page';
+    \$page->admin_title = 'Skinr UI test panel';
+    \$page->admin_description = '';
+    \$page->path = 'skinr-ui-panel';
+    \$page->access = array();
+    \$page->menu = array();
+    \$page->arguments = array();
+    \$page->conf = array(
+      'admin_paths' => FALSE,
+    );
+    \$page->default_handlers = array();
+    \$handler = new stdClass;
+    \$handler->disabled = FALSE;
+    \$handler->api_version = 1;
+    \$handler->name = 'page_test_panel_panel_context';
+    \$handler->task = 'page';
+    \$handler->subtask = 'test_panel';
+    \$handler->handler = 'panel_context';
+    \$handler->weight = 0;
+    \$handler->conf = array(
+      'title' => 'Panel',
+      'no_blocks' => 0,
+      'pipeline' => 'standard',
+      'css_id' => '',
+      'css' => '',
+      'contexts' => array(),
+      'relationships' => array(),
+    );
+    \$display = new panels_display;
+    \$display->layout = 'onecol';
+    \$display->layout_settings = array();
+    \$display->panel_settings = array(
+      'style_settings' => array(
+        'default' => NULL,
+        'middle' => NULL,
+      ),
+    );
+    \$display->cache = array();
+    \$display->title = '';
+    \$display->content = array();
+    \$display->panels = array();
+      \$pane = new stdClass;
+      \$pane->pid = 'new-1';
+      \$pane->panel = 'middle';
+      \$pane->type = 'block';
+      \$pane->subtype = 'system-user-menu';
+      \$pane->shown = TRUE;
+      \$pane->access = array();
+      \$pane->configuration = array(
+        'override_title' => 0,
+        'override_title_text' => '',
+      );
+      \$pane->cache = array();
+      \$pane->style = array(
+        'settings' => NULL,
+      );
+      \$pane->css = array();
+      \$pane->extras = array();
+      \$pane->position = 0;
+      \$display->content['new-1'] = \$pane;
+      \$display->panels['middle'][0] = 'new-1';
+    \$display->hide_title = PANELS_TITLE_FIXED;
+    \$display->title_pane = 'new-1';
+    \$handler->conf['display'] = \$display;
+    \$page->default_handlers[\$handler->name] = \$handler;
+END;
+    return $object;
+  }
+
+  /**
+   * Tests panels plugin.
+   *
+   * @todo Test a panel node (panels_node).
+   * @todo Test a mini panel (panels_mini).
+   */
+  function testPanels() {
+    // Import a simple panel page.
+    $edit = array(
+      'object' => $this->panelData(),
+    );
+    $this->drupalPost('admin/structure/pages/import', $edit, t('Import'));
+    // This is a multi-form.
+    $edit = array();
+    $this->drupalPost(NULL, $edit, t('Save'));
+
+    // Go to panel page.
+    $this->drupalGet('skinr-ui-panel');
+
+    // Make sure our contextual link appears on the page.
+    // @todo Is there a better way to determine did and pid used for this panel?
+    $this->assertLinkByHref('admin/appearance/skinr/edit/nojs/panels/pane__1__1/configure', 0, 'Contexual link to edit pane\'s skin configuration was found.');
+
+    // Make sure this panel's options are returned.
+    $this->drupalGet('admin/appearance/skinr/add');
+    $this->assertOptionExists('element', 'pane__1__1', 'Panel pane was returned by panels_skinr_ui_element_options().');
+
+
+    // Create a simple panel node.
+    $node1 = $this->drupalCreateNode(array(
+      'type' => 'panel',
+      'panels_node' => array(
+        'layout' => 'onecol',
+        'css_id' => '',
+        'pipeline' => 'standard',
+      ),
+    ));
+
+    // Go to node.
+    $uri = entity_uri('node', $node1);
+    $this->drupalGet($uri['path']);
+
+    // Add a block to our panel node.
+    // @todo It seems impossible to add a pane without ajax.
+
+    // Make sure our contextual link appears on the page.
+    // @todo Is there a better way to determine did and pid used for this panel?
+
+    // Make sure this panel's options are returned.
+
+
+    // Import a simple mini panel.
+
+  }
+
+  /**
+   * Tests views plugin.
+   */
+  function testViews() {
+    // Create a simple view with page.
+    $view = array();
+    $view['human_name'] = $this->randomName(16);
+    $view['name'] = strtolower($this->randomName(16));
+    $view['description'] = $this->randomName(16);
+    $view['page[create]'] = 1;
+    $view['page[title]'] = $this->randomName(16);
+    $view['page[path]'] = $this->randomName(16);
+    $this->drupalPost('admin/structure/views/add', $view, t('Save & exit'));
+
+    // Since the view has a page, we expect to be automatically redirected to
+    // it.
+    $this->assertUrl($view['page[path]']);
+
+    // Make sure our contextual link appears on the page.
+    $this->assertLinkByHref('admin/appearance/skinr/edit/nojs/views/' . $view['name'] . '__page/configure', 0, 'Contexual link to edit view\'s skin configuration was found.');
+
+    // Make sure this view's options are returned.
+    $this->drupalGet('admin/appearance/skinr/add');
+    $this->assertOptionExists('element', $view['name'] . '__default', 'Default display for our view was returned by views_skinr_ui_element_options().');
+    $this->assertOptionExists('element', $view['name'] . '__page', 'Page display for our view was returned by views_skinr_ui_element_options().');
+  }
+}

