diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module
index f971b5453a..737e59d6ca 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.module
+++ b/core/modules/system/tests/modules/theme_test/theme_test.module
@@ -178,7 +178,7 @@ function theme_test_theme_suggestions_theme_test_suggestion_provided(array $vari
  * Implements hook_theme_suggestions_alter().
  */
 function theme_test_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
-  \Drupal::messenger()->addStatus(__FUNCTION__ . '() executed.');
+  \Drupal::messenger()->addStatus(__FUNCTION__ . '() executed for '. $hook . '.');
 }
 
 /**
diff --git a/core/modules/system/src/Tests/Theme/EngineTwigTest.php b/core/modules/system/tests/src/Functional/Theme/EngineTwigTest.php
similarity index 95%
rename from core/modules/system/src/Tests/Theme/EngineTwigTest.php
rename to core/modules/system/tests/src/Functional/Theme/EngineTwigTest.php
index 2ca831cfd1..07b30ee30f 100644
--- a/core/modules/system/src/Tests/Theme/EngineTwigTest.php
+++ b/core/modules/system/tests/src/Functional/Theme/EngineTwigTest.php
@@ -1,17 +1,20 @@
 <?php
 
-namespace Drupal\system\Tests\Theme;
+namespace Drupal\Tests\system\Functional\Theme;
 
 use Drupal\Core\Render\Markup;
 use Drupal\Core\Url;
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
 
 /**
  * Tests Twig-specific theme functionality.
  *
  * @group Theme
  */
-class EngineTwigTest extends WebTestBase {
+class EngineTwigTest extends BrowserTestBase {
+
+  use AssertPageCacheContextsAndTagsTrait;
 
   /**
    * Modules to enable.
diff --git a/core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php b/core/modules/system/tests/src/Functional/Theme/EntityFilteringThemeTest.php
similarity index 96%
rename from core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php
rename to core/modules/system/tests/src/Functional/Theme/EntityFilteringThemeTest.php
index c6ae012d90..6e4bd0a0b3 100644
--- a/core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php
+++ b/core/modules/system/tests/src/Functional/Theme/EntityFilteringThemeTest.php
@@ -1,15 +1,15 @@
 <?php
 
-namespace Drupal\system\Tests\Theme;
+namespace Drupal\Tests\system\Functional\Theme;
 
 use Drupal\comment\Tests\CommentTestTrait;
 use Drupal\Core\Extension\ExtensionDiscovery;
 use Drupal\comment\CommentInterface;
 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
 use Drupal\node\NodeInterface;
-use Drupal\simpletest\WebTestBase;
 use Drupal\comment\Entity\Comment;
 use Drupal\taxonomy\Entity\Term;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests themed output for each entity type in all available themes to ensure
@@ -17,7 +17,7 @@
  *
  * @group Theme
  */
-class EntityFilteringThemeTest extends WebTestBase {
+class EntityFilteringThemeTest extends BrowserTestBase {
 
   use CommentTestTrait;
 
diff --git a/core/modules/system/src/Tests/Theme/HtmlAttributesTest.php b/core/modules/system/tests/src/Functional/Theme/HtmlAttributesTest.php
similarity index 52%
rename from core/modules/system/src/Tests/Theme/HtmlAttributesTest.php
rename to core/modules/system/tests/src/Functional/Theme/HtmlAttributesTest.php
index b2018c777e..5aa4be9cc9 100644
--- a/core/modules/system/src/Tests/Theme/HtmlAttributesTest.php
+++ b/core/modules/system/tests/src/Functional/Theme/HtmlAttributesTest.php
@@ -1,15 +1,15 @@
 <?php
 
-namespace Drupal\system\Tests\Theme;
+namespace Drupal\Tests\system\Functional\Theme;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests attributes inserted in the 'html' and 'body' elements on the page.
  *
  * @group Theme
  */
-class HtmlAttributesTest extends WebTestBase {
+class HtmlAttributesTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
@@ -23,9 +23,8 @@ class HtmlAttributesTest extends WebTestBase {
    */
   public function testThemeHtmlAttributes() {
     $this->drupalGet('');
-    $attributes = $this->xpath('/html[@theme_test_html_attribute="theme test html attribute value"]');
-    $this->assertTrue(count($attributes) == 1, "Attribute set in the 'html' element via hook_preprocess_HOOK() found.");
-    $attributes = $this->xpath('/html/body[@theme_test_body_attribute="theme test body attribute value"]');
+    $this->assertSession()->responseContains('<html lang="en" dir="ltr" theme_test_html_attribute="theme test html attribute value">');
+    $attributes = $this->xpath('/body[@theme_test_body_attribute="theme test body attribute value"]');
     $this->assertTrue(count($attributes) == 1, "Attribute set in the 'body' element via hook_preprocess_HOOK() found.");
   }
 
diff --git a/core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php b/core/modules/system/tests/src/Functional/Theme/ThemeSuggestionsAlterTest.php
similarity index 93%
rename from core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php
rename to core/modules/system/tests/src/Functional/Theme/ThemeSuggestionsAlterTest.php
index 7337dae402..5f2668ef0a 100644
--- a/core/modules/system/src/Tests/Theme/ThemeSuggestionsAlterTest.php
+++ b/core/modules/system/tests/src/Functional/Theme/ThemeSuggestionsAlterTest.php
@@ -1,16 +1,16 @@
 <?php
 
-namespace Drupal\system\Tests\Theme;
+namespace Drupal\Tests\system\Functional\Theme;
 
 use Drupal\Component\Utility\Xss;
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests theme suggestion alter hooks.
  *
  * @group Theme
  */
-class ThemeSuggestionsAlterTest extends WebTestBase {
+class ThemeSuggestionsAlterTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
@@ -170,16 +170,20 @@ public function testExecutionOrder() {
     $this->drupalGet('theme-test/suggestion-alter');
     // Ensure that the order is first by extension, then for a given extension,
     // the hook-specific one after the generic one.
-    $expected = [
+    $expected_order = [
       'theme_suggestions_test_theme_suggestions_alter() executed.',
       'theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter() executed.',
-      'theme_test_theme_suggestions_alter() executed.',
+      'theme_test_theme_suggestions_alter() executed for theme_test_suggestions.',
       'theme_test_theme_suggestions_theme_test_suggestions_alter() executed.',
       'test_theme_theme_suggestions_alter() executed.',
       'test_theme_theme_suggestions_theme_test_suggestions_alter() executed.',
     ];
-    $content = preg_replace('/\s+/', ' ', Xss::filter($this->content, []));
-    $this->assert(strpos($content, implode(' ', $expected)) !== FALSE, 'Suggestion alter hooks executed in the expected order.');
+    $content = preg_replace('/\s+/', ' ', Xss::filter($this->getSession()->getPage()->getContent(), []));
+    $order = 0;
+    foreach ($expected_order as $expected_string) {
+      $this->assertGreaterThan($order, strpos($content, $expected_string));
+      $order = strpos($content, $expected_string);
+    }
   }
 
 }
diff --git a/core/modules/system/tests/src/Functional/Theme/ThemeTest.php b/core/modules/system/tests/src/Functional/Theme/ThemeTest.php
new file mode 100644
index 0000000000..fff7bdfa4e
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Theme/ThemeTest.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace Drupal\Tests\system\Functional\Theme;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Tests\BrowserTestBase;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Tests low-level theme functions.
+ *
+ * @group Theme
+ */
+class ThemeTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['theme_test', 'node'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    \Drupal::service('theme_handler')->install(['test_theme']);
+  }
+
+  /**
+   * Ensures preprocess functions run even for suggestion implementations.
+   *
+   * The theme hook used by this test has its base preprocess function in a
+   * separate file, so this test also ensures that that file is correctly loaded
+   * when needed.
+   */
+  public function testPreprocessForSuggestions() {
+    // Test with both an unprimed and primed theme registry.
+    drupal_theme_rebuild();
+    for ($i = 0; $i < 2; $i++) {
+      $this->drupalGet('theme-test/suggestion');
+      $this->assertText('Theme hook implementor=test_theme_theme_test__suggestion(). Foo=template_preprocess_theme_test', 'Theme hook suggestion ran with data available from a preprocess function for the base hook.');
+    }
+  }
+
+  /**
+   * Tests the priority of some theme negotiators.
+   */
+  public function testNegotiatorPriorities() {
+    $this->drupalGet('theme-test/priority');
+
+    // Ensure that the custom theme negotiator was not able to set the theme.
+    $this->assertNoText('Theme hook implementor=test_theme_theme_test__suggestion(). Foo=template_preprocess_theme_test', 'Theme hook suggestion ran with data available from a preprocess function for the base hook.');
+  }
+
+  /**
+   * Ensures that non-HTML requests never initialize themes.
+   */
+  public function testThemeOnNonHtmlRequest() {
+    $this->drupalGet('theme-test/non-html');
+    $json = Json::decode($this->getRawContent());
+    $this->assertFalse($json['theme_initialized']);
+  }
+
+  /**
+   * Ensure page-front template suggestion is added when on front page.
+   */
+  public function testFrontPageThemeSuggestion() {
+    // Set the current route to user.login because theme_get_suggestions() will
+    // query it to see if we are on the front page.
+    $request = Request::create('/user/login');
+    $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'user.login');
+    $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/user/login'));
+    \Drupal::requestStack()->push($request);
+    $this->config('system.site')->set('page.front', '/user/login')->save();
+    $suggestions = theme_get_suggestions(['user', 'login'], 'page');
+    // Set it back to not annoy the batch runner.
+    \Drupal::requestStack()->pop();
+    $this->assertTrue(in_array('page__front', $suggestions), 'Front page template was suggested.');
+  }
+
+  /**
+   * Ensures a theme's .info.yml file is able to override a module CSS file from being added to the page.
+   *
+   * @see test_theme.info.yml
+   */
+  public function testCSSOverride() {
+    // Reuse the same page as in testPreprocessForSuggestions(). We're testing
+    // what is output to the HTML HEAD based on what is in a theme's .info.yml
+    // file, so it doesn't matter what page we get, as long as it is themed with
+    // the test theme. First we test with CSS aggregation disabled.
+    $config = $this->config('system.performance');
+    $config->set('css.preprocess', 0);
+    $config->save();
+    $this->drupalGet('theme-test/suggestion');
+    $this->assertNoText('js.module.css', 'The theme\'s .info.yml file is able to override a module CSS file from being added to the page.');
+
+    // Also test with aggregation enabled, simply ensuring no PHP errors are
+    // triggered during drupal_build_css_cache() when a source file doesn't
+    // exist. Then allow remaining tests to continue with aggregation disabled
+    // by default.
+    $config->set('css.preprocess', 1);
+    $config->save();
+    $this->drupalGet('theme-test/suggestion');
+    $config->set('css.preprocess', 0);
+    $config->save();
+  }
+
+  /**
+   * Ensures a themes template is overridable based on the 'template' filename.
+   */
+  public function testTemplateOverride() {
+    $this->config('system.theme')
+      ->set('default', 'test_theme')
+      ->save();
+    $this->drupalGet('theme-test/template-test');
+    $this->assertText('Success: Template overridden.', 'Template overridden by defined \'template\' filename.');
+  }
+
+  /**
+   * Ensures a theme template can override a theme function.
+   */
+  public function testFunctionOverride() {
+    $this->drupalGet('theme-test/function-template-overridden');
+    $this->assertText('Success: Template overrides theme function.', 'Theme function overridden by test_theme template.');
+  }
+
+  /**
+   * Tests that the page variable is not prematurely flattened.
+   *
+   * Some modules check the page array in template_preprocess_html(), so we
+   * ensure that it has not been rendered prematurely.
+   */
+  public function testPreprocessHtml() {
+    $this->drupalGet('');
+    $attributes = $this->xpath('/body[@theme_test_page_variable="Page variable is an array."]');
+    $this->assertTrue(count($attributes) == 1, 'In template_preprocess_html(), the page variable is still an array (not rendered yet).');
+    $this->assertText('theme test page bottom markup', 'Modules are able to set the page bottom region.');
+  }
+
+  /**
+   * Tests that region attributes can be manipulated via preprocess functions.
+   */
+  public function testRegionClass() {
+    \Drupal::service('module_installer')->install(['block', 'theme_region_test']);
+
+    // Place a block.
+    $this->drupalPlaceBlock('system_main_block');
+    $this->drupalGet('');
+    $elements = $this->cssSelect(".region-sidebar-first.new_class");
+    $this->assertEqual(count($elements), 1, 'New class found.');
+  }
+
+  /**
+   * Ensures suggestion preprocess functions run for default implementations.
+   *
+   * The theme hook used by this test has its base preprocess function in a
+   * separate file, so this test also ensures that that file is correctly loaded
+   * when needed.
+   */
+  public function testSuggestionPreprocessForDefaults() {
+    $this->config('system.theme')->set('default', 'test_theme')->save();
+    // Test with both an unprimed and primed theme registry.
+    drupal_theme_rebuild();
+    for ($i = 0; $i < 2; $i++) {
+      $this->drupalGet('theme-test/preprocess-suggestions');
+      $items = $this->cssSelect('.suggestion');
+      $expected_values = [
+        'Suggestion',
+        'Kitten',
+        'Monkey',
+        'Kitten',
+        'Flamingo',
+      ];
+      foreach ($expected_values as $key => $value) {
+        $this->assertEqual((string) $value, $items[$key]->getText());
+      }
+    }
+  }
+
+}
diff --git a/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php b/core/modules/system/tests/src/Functional/Theme/TwigDebugMarkupTest.php
similarity index 93%
rename from core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
rename to core/modules/system/tests/src/Functional/Theme/TwigDebugMarkupTest.php
index 457fbad8bf..9fe89fd58b 100644
--- a/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
+++ b/core/modules/system/tests/src/Functional/Theme/TwigDebugMarkupTest.php
@@ -1,15 +1,16 @@
 <?php
 
-namespace Drupal\system\Tests\Theme;
+namespace Drupal\Tests\system\Functional\Theme;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Component\Utility\Html;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests for Twig debug markup.
  *
  * @group Theme
  */
-class TwigDebugMarkupTest extends WebTestBase {
+class TwigDebugMarkupTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
@@ -45,10 +46,9 @@ public function testTwigDebugMarkup() {
     $build = node_view($node);
     $output = $renderer->renderRoot($build);
     $this->assertTrue(strpos($output, '<!-- THEME DEBUG -->') !== FALSE, 'Twig debug markup found in theme output when debug is enabled.');
-    $this->setRawContent($output);
     $this->assertTrue(strpos($output, "THEME HOOK: 'node'") !== FALSE, 'Theme call information found.');
     $this->assertTrue(strpos($output, '* node--1--full' . $extension . PHP_EOL . '   x node--1' . $extension . PHP_EOL . '   * node--page--full' . $extension . PHP_EOL . '   * node--page' . $extension . PHP_EOL . '   * node--full' . $extension . PHP_EOL . '   * node' . $extension) !== FALSE, 'Suggested template files found in order and node ID specific template shown as current template.');
-    $this->assertEscaped('node--<script type="text/javascript">alert(\'yo\');</script>');
+    $this->assertContains(Html::escape('node--<script type="text/javascript">alert(\'yo\');</script>'), (string) $output);
     $template_filename = $templates['node__1']['path'] . '/' . $templates['node__1']['template'] . $extension;
     $this->assertTrue(strpos($output, "BEGIN OUTPUT from '$template_filename'") !== FALSE, 'Full path to current template file found.');
 
diff --git a/core/modules/system/src/Tests/Theme/TwigFilterTest.php b/core/modules/system/tests/src/Functional/Theme/TwigFilterTest.php
similarity index 94%
rename from core/modules/system/src/Tests/Theme/TwigFilterTest.php
rename to core/modules/system/tests/src/Functional/Theme/TwigFilterTest.php
index 653e4fa96a..4425cd8d0b 100644
--- a/core/modules/system/src/Tests/Theme/TwigFilterTest.php
+++ b/core/modules/system/tests/src/Functional/Theme/TwigFilterTest.php
@@ -1,15 +1,15 @@
 <?php
 
-namespace Drupal\system\Tests\Theme;
+namespace Drupal\Tests\system\Functional\Theme;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests Drupal's Twig filters.
  *
  * @group Theme
  */
-class TwigFilterTest extends WebTestBase {
+class TwigFilterTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
@@ -35,8 +35,7 @@ public function testTwigWithoutFilter() {
         'class' => ['red', 'green', 'blue'],
       ],
     ];
-    $rendered = \Drupal::service('renderer')->renderRoot($filter_test);
-    $this->setRawContent($rendered);
+    $rendered = (string) \Drupal::service('renderer')->renderRoot($filter_test);
 
     $elements = [
       [
@@ -116,7 +115,7 @@ public function testTwigWithoutFilter() {
     ];
 
     foreach ($elements as $element) {
-      $this->assertRaw($element['expected'], $element['message']);
+      $this->assertContains($element['expected'], $rendered);
     }
   }
 
diff --git a/core/modules/system/src/Tests/Theme/TwigTransTest.php b/core/modules/system/tests/src/Functional/Theme/TwigTransTest.php
similarity index 98%
rename from core/modules/system/src/Tests/Theme/TwigTransTest.php
rename to core/modules/system/tests/src/Functional/Theme/TwigTransTest.php
index 5cd34b6bd9..aeafe7aeb1 100644
--- a/core/modules/system/src/Tests/Theme/TwigTransTest.php
+++ b/core/modules/system/tests/src/Functional/Theme/TwigTransTest.php
@@ -1,16 +1,16 @@
 <?php
 
-namespace Drupal\system\Tests\Theme;
+namespace Drupal\Tests\system\Functional\Theme;
 
 use Drupal\Core\Language\LanguageInterface;
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests Twig "trans" tags.
  *
  * @group Theme
  */
-class TwigTransTest extends WebTestBase {
+class TwigTransTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
diff --git a/core/modules/system/src/Tests/Theme/FunctionsTest.php b/core/modules/system/tests/src/Kernel/Theme/FunctionsTest.php
similarity index 84%
rename from core/modules/system/src/Tests/Theme/FunctionsTest.php
rename to core/modules/system/tests/src/Kernel/Theme/FunctionsTest.php
index 6cc28fef17..b66efc0807 100644
--- a/core/modules/system/src/Tests/Theme/FunctionsTest.php
+++ b/core/modules/system/tests/src/Kernel/Theme/FunctionsTest.php
@@ -1,27 +1,30 @@
 <?php
 
-namespace Drupal\system\Tests\Theme;
+namespace Drupal\Tests\system\Kernel\Theme;
 
 use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Core\Session\UserSession;
 use Drupal\Core\Url;
-use Drupal\simpletest\WebTestBase;
+use Drupal\KernelTests\AssertContentTrait;
+use Drupal\KernelTests\KernelTestBase;
 
 /**
  * Tests for common theme functions.
  *
  * @group Theme
  */
-class FunctionsTest extends WebTestBase {
+class FunctionsTest extends KernelTestBase {
+
+  use AssertContentTrait;
 
   /**
    * Modules to enable.
    *
    * @var array
    */
-  public static $modules = ['router_test'];
+  public static $modules = ['router_test', 'system'];
 
   /**
    * Tests item-list.html.twig.
@@ -41,21 +44,21 @@ public function testItemList() {
     // Verify that empty items produce the empty string.
     $variables = [];
     $variables['empty'] = 'No items found.';
-    $expected = '<div class="item-list">No items found.</div>';
+    $expected = 'No items found.';
     $this->assertThemeOutput('item_list', $variables, $expected, 'Empty %callback generates empty string.');
 
     // Verify that empty items produce the empty string with title.
     $variables = [];
     $variables['title'] = 'Some title';
     $variables['empty'] = 'No items found.';
-    $expected = '<div class="item-list"><h3>Some title</h3>No items found.</div>';
+    $expected = '<h3>Some title</h3>No items found.';
     $this->assertThemeOutput('item_list', $variables, $expected, 'Empty %callback generates empty string with title.');
 
     // Verify that title set to 0 is output.
     $variables = [];
     $variables['title'] = 0;
     $variables['empty'] = 'No items found.';
-    $expected = '<div class="item-list"><h3>0</h3>No items found.</div>';
+    $expected = '<h3>0</h3>No items found.';
     $this->assertThemeOutput('item_list', $variables, $expected, '%callback with title set to 0 generates a title.');
 
     // Verify that title set to a render array is output.
@@ -64,7 +67,7 @@ public function testItemList() {
       '#markup' => '<span>Render array</span>',
     ];
     $variables['empty'] = 'No items found.';
-    $expected = '<div class="item-list"><h3><span>Render array</span></h3>No items found.</div>';
+    $expected = '<h3><span>Render array</span></h3>No items found.';
     $this->assertThemeOutput('item_list', $variables, $expected, '%callback with title set to a render array generates a title.');
 
     // Verify that empty text is not displayed when there are list items.
@@ -72,7 +75,7 @@ public function testItemList() {
     $variables['title'] = 'Some title';
     $variables['empty'] = 'No items found.';
     $variables['items'] = ['Un', 'Deux', 'Trois'];
-    $expected = '<div class="item-list"><h3>Some title</h3><ul><li>Un</li><li>Deux</li><li>Trois</li></ul></div>';
+    $expected = '<h3>Some title</h3><ul><li>Un</li><li>Deux</li><li>Trois</li></ul>';
     $this->assertThemeOutput('item_list', $variables, $expected, '%callback does not print empty text when there are list items.');
 
     // Verify nested item lists.
@@ -133,24 +136,23 @@ public function testItemList() {
       'f',
     ];
 
-    $inner_b = '<div class="item-list"><ol id="blist">';
+    $inner_b = '<ol id="blist">';
     $inner_b .= '<li>ba</li>';
     $inner_b .= '<li class="item-class-bb">bb</li>';
-    $inner_b .= '</ol></div>';
+    $inner_b .= '</ol>';
 
-    $inner_cb = '<div class="item-list"><ul>';
+    $inner_cb = '<ul>';
     $inner_cb .= '<li>cba</li>';
     $inner_cb .= '<li>cbb</li>';
-    $inner_cb .= '</ul></div>';
+    $inner_cb .= '</ul>';
 
-    $inner_c = '<div class="item-list"><ul id="clist">';
+    $inner_c = '<ul id="clist">';
     $inner_c .= '<li>ca</li>';
     $inner_c .= '<li class="item-class-cb">cb' . $inner_cb . '</li>';
     $inner_c .= '<li>cc</li>';
-    $inner_c .= '</ul></div>';
+    $inner_c .= '</ul>';
 
-    $expected = '<div class="item-list">';
-    $expected .= '<h3>Some title</h3>';
+    $expected = '<h3>Some title</h3>';
     $expected .= '<ul id="parentlist">';
     $expected .= '<li>a</li>';
     $expected .= '<li id="item-id-b">b' . $inner_b . '</li>';
@@ -158,7 +160,7 @@ public function testItemList() {
     $expected .= '<li id="item-id-d">d</li>';
     $expected .= '<li id="item-id-e"></li>';
     $expected .= '<li>f</li>';
-    $expected .= '</ul></div>';
+    $expected .= '</ul>';
 
     $this->assertThemeOutput('item_list', $variables, $expected);
   }
@@ -215,13 +217,13 @@ public function testLinks() {
 
     $expected_links = '';
     $expected_links .= '<ul id="somelinks">';
-    $expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . Html::escape('A <link>') . '</a></li>';
-    $expected_links .= '<li class="plain-text">' . Html::escape('Plain "text"') . '</li>';
-    $expected_links .= '<li class="html-text"><span class="unescaped">' . Html::escape('potentially unsafe text that <should> be escaped') . '</span></li>';
-    $expected_links .= '<li class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '">' . Html::escape('Front page') . '</a></li>';
-    $expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . Html::escape('Test route') . '</a></li>';
+    $expected_links .= '<li><a href="' . Url::fromUri('base:a/link')->toString() . '">' . Html::escape('A <link>') . '</a></li>';
+    $expected_links .= '<li>' . Html::escape('Plain "text"') . '</li>';
+    $expected_links .= '<li><span class="unescaped">' . Html::escape('potentially unsafe text that <should> be escaped') . '</span></li>';
+    $expected_links .= '<li><a href="' . Url::fromRoute('<front>')->toString() . '">' . Html::escape('Front page') . '</a></li>';
+    $expected_links .= '<li><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . Html::escape('Test route') . '</a></li>';
     $query = ['key' => 'value'];
-    $expected_links .= '<li class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . Html::escape('Query test route') . '</a></li>';
+    $expected_links .= '<li><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . Html::escape('Query test route') . '</a></li>';
     $expected_links .= '</ul>';
 
     // Verify that passing a string as heading works.
@@ -255,13 +257,13 @@ public function testLinks() {
     ];
     $expected_links = '';
     $expected_links .= '<ul id="somelinks">';
-    $expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . Html::escape('A <link>') . '</a></li>';
-    $expected_links .= '<li class="plain-text"><span class="a/class">' . Html::escape('Plain "text"') . '</span></li>';
-    $expected_links .= '<li class="html-text"><span class="unescaped">' . Html::escape('potentially unsafe text that <should> be escaped') . '</span></li>';
-    $expected_links .= '<li class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '">' . Html::escape('Front page') . '</a></li>';
-    $expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . Html::escape('Test route') . '</a></li>';
+    $expected_links .= '<li><a href="' . Url::fromUri('base:a/link')->toString() . '">' . Html::escape('A <link>') . '</a></li>';
+    $expected_links .= '<li><span class="a/class">' . Html::escape('Plain "text"') . '</span></li>';
+    $expected_links .= '<li><span class="unescaped">' . Html::escape('potentially unsafe text that <should> be escaped') . '</span></li>';
+    $expected_links .= '<li><a href="' . Url::fromRoute('<front>')->toString() . '">' . Html::escape('Front page') . '</a></li>';
+    $expected_links .= '<li><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . Html::escape('Test route') . '</a></li>';
     $query = ['key' => 'value'];
-    $expected_links .= '<li class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . Html::escape('Query test route') . '</a></li>';
+    $expected_links .= '<li><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . Html::escape('Query test route') . '</a></li>';
     $expected_links .= '</ul>';
     $expected = $expected_heading . $expected_links;
     $this->assertThemeOutput('links', $variables, $expected);
@@ -271,14 +273,14 @@ public function testLinks() {
     $variables['set_active_class'] = TRUE;
     $expected_links = '';
     $expected_links .= '<ul id="somelinks">';
-    $expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . Html::escape('A <link>') . '</a></li>';
-    $expected_links .= '<li class="plain-text"><span class="a/class">' . Html::escape('Plain "text"') . '</span></li>';
-    $expected_links .= '<li class="html-text"><span class="unescaped">' . Html::escape('potentially unsafe text that <should> be escaped') . '</span></li>';
-    $expected_links .= '<li data-drupal-link-system-path="&lt;front&gt;" class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '" data-drupal-link-system-path="&lt;front&gt;">' . Html::escape('Front page') . '</a></li>';
-    $expected_links .= '<li data-drupal-link-system-path="router_test/test1" class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '" data-drupal-link-system-path="router_test/test1">' . Html::escape('Test route') . '</a></li>';
+    $expected_links .= '<li><a href="' . Url::fromUri('base:a/link')->toString() . '">' . Html::escape('A <link>') . '</a></li>';
+    $expected_links .= '<li><span class="a/class">' . Html::escape('Plain "text"') . '</span></li>';
+    $expected_links .= '<li><span class="unescaped">' . Html::escape('potentially unsafe text that <should> be escaped') . '</span></li>';
+    $expected_links .= '<li data-drupal-link-system-path="&lt;front&gt;"><a href="' . Url::fromRoute('<front>')->toString() . '" data-drupal-link-system-path="&lt;front&gt;">' . Html::escape('Front page') . '</a></li>';
+    $expected_links .= '<li data-drupal-link-system-path="router_test/test1"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '" data-drupal-link-system-path="router_test/test1">' . Html::escape('Test route') . '</a></li>';
     $query = ['key' => 'value'];
     $encoded_query = Html::escape(Json::encode($query));
-    $expected_links .= '<li data-drupal-link-query="' . $encoded_query . '" data-drupal-link-system-path="router_test/test1" class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '" data-drupal-link-query="' . $encoded_query . '" data-drupal-link-system-path="router_test/test1">' . Html::escape('Query test route') . '</a></li>';
+    $expected_links .= '<li data-drupal-link-query="' . $encoded_query . '" data-drupal-link-system-path="router_test/test1"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '" data-drupal-link-query="' . $encoded_query . '" data-drupal-link-system-path="router_test/test1">' . Html::escape('Query test route') . '</a></li>';
     $expected_links .= '</ul>';
     $expected = $expected_heading . $expected_links;
     $this->assertThemeOutput('links', $variables, $expected);
diff --git a/core/modules/system/src/Tests/Theme/ThemeTest.php b/core/modules/system/tests/src/Kernel/Theme/ThemeTest.php
similarity index 50%
rename from core/modules/system/src/Tests/Theme/ThemeTest.php
rename to core/modules/system/tests/src/Kernel/Theme/ThemeTest.php
index 64f330e942..8e37a5f63a 100644
--- a/core/modules/system/src/Tests/Theme/ThemeTest.php
+++ b/core/modules/system/tests/src/Kernel/Theme/ThemeTest.php
@@ -1,29 +1,26 @@
 <?php
 
-namespace Drupal\system\Tests\Theme;
+namespace Drupal\Tests\system\Kernel\Theme;
 
-use Drupal\Component\Serialization\Json;
-use Drupal\simpletest\WebTestBase;
-use Drupal\test_theme\ThemeClass;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Route;
+use Drupal\KernelTests\KernelTestBase;
 use Drupal\Component\Render\MarkupInterface;
+use Drupal\test_theme\ThemeClass;
 
 /**
  * Tests low-level theme functions.
  *
  * @group Theme
  */
-class ThemeTest extends WebTestBase {
+class ThemeTest extends KernelTestBase {
 
   /**
-   * Modules to enable.
-   *
-   * @var array
+   * {@inheritdoc}
    */
-  public static $modules = ['theme_test', 'node'];
+  public static $modules = ['theme_test', 'node', 'system'];
 
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     parent::setUp();
     \Drupal::service('theme_handler')->install(['test_theme']);
@@ -103,105 +100,6 @@ public function testThemeSuggestions() {
     $this->assertEqual($suggestions, $result, 'Found expected page suggestions for paths containing hyphens.');
   }
 
-  /**
-   * Ensures preprocess functions run even for suggestion implementations.
-   *
-   * The theme hook used by this test has its base preprocess function in a
-   * separate file, so this test also ensures that that file is correctly loaded
-   * when needed.
-   */
-  public function testPreprocessForSuggestions() {
-    // Test with both an unprimed and primed theme registry.
-    drupal_theme_rebuild();
-    for ($i = 0; $i < 2; $i++) {
-      $this->drupalGet('theme-test/suggestion');
-      $this->assertText('Theme hook implementor=test_theme_theme_test__suggestion(). Foo=template_preprocess_theme_test', 'Theme hook suggestion ran with data available from a preprocess function for the base hook.');
-    }
-  }
-
-  /**
-   * Tests the priority of some theme negotiators.
-   */
-  public function testNegotiatorPriorities() {
-    $this->drupalGet('theme-test/priority');
-
-    // Ensure that the custom theme negotiator was not able to set the theme.
-
-    $this->assertNoText('Theme hook implementor=test_theme_theme_test__suggestion(). Foo=template_preprocess_theme_test', 'Theme hook suggestion ran with data available from a preprocess function for the base hook.');
-  }
-
-  /**
-   * Ensures that non-HTML requests never initialize themes.
-   */
-  public function testThemeOnNonHtmlRequest() {
-    $this->drupalGet('theme-test/non-html');
-    $json = Json::decode($this->getRawContent());
-    $this->assertFalse($json['theme_initialized']);
-  }
-
-  /**
-   * Ensure page-front template suggestion is added when on front page.
-   */
-  public function testFrontPageThemeSuggestion() {
-    // Set the current route to user.login because theme_get_suggestions() will
-    // query it to see if we are on the front page.
-    $request = Request::create('/user/login');
-    $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'user.login');
-    $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/user/login'));
-    \Drupal::requestStack()->push($request);
-    $this->config('system.site')->set('page.front', '/user/login')->save();
-    $suggestions = theme_get_suggestions(['user', 'login'], 'page');
-    // Set it back to not annoy the batch runner.
-    \Drupal::requestStack()->pop();
-    $this->assertTrue(in_array('page__front', $suggestions), 'Front page template was suggested.');
-  }
-
-  /**
-   * Ensures a theme's .info.yml file is able to override a module CSS file from being added to the page.
-   *
-   * @see test_theme.info.yml
-   */
-  public function testCSSOverride() {
-    // Reuse the same page as in testPreprocessForSuggestions(). We're testing
-    // what is output to the HTML HEAD based on what is in a theme's .info.yml
-    // file, so it doesn't matter what page we get, as long as it is themed with
-    // the test theme. First we test with CSS aggregation disabled.
-    $config = $this->config('system.performance');
-    $config->set('css.preprocess', 0);
-    $config->save();
-    $this->drupalGet('theme-test/suggestion');
-    $this->assertNoText('js.module.css', 'The theme\'s .info.yml file is able to override a module CSS file from being added to the page.');
-
-    // Also test with aggregation enabled, simply ensuring no PHP errors are
-    // triggered during drupal_build_css_cache() when a source file doesn't
-    // exist. Then allow remaining tests to continue with aggregation disabled
-    // by default.
-    $config->set('css.preprocess', 1);
-    $config->save();
-    $this->drupalGet('theme-test/suggestion');
-    $config->set('css.preprocess', 0);
-    $config->save();
-  }
-
-  /**
-   * Ensures a themes template is overridable based on the 'template' filename.
-   */
-  public function testTemplateOverride() {
-    $this->config('system.theme')
-      ->set('default', 'test_theme')
-      ->save();
-    $this->drupalGet('theme-test/template-test');
-    $this->assertText('Success: Template overridden.', 'Template overridden by defined \'template\' filename.');
-  }
-
-  /**
-   * Ensures a theme template can override a theme function.
-   */
-  public function testFunctionOverride() {
-    $this->drupalGet('theme-test/function-template-overridden');
-    $this->assertText('Success: Template overrides theme function.', 'Theme function overridden by test_theme template.');
-  }
-
   /**
    * Test the listInfo() function.
    */
@@ -248,13 +146,6 @@ public function testDrupalRenderChildren() {
     $this->assertThemeOutput('theme_test_render_element_children', $element, 'Foo', 'drupal_render() avoids #theme_wrappers recursion loop when rendering a render element.');
   }
 
-  /**
-   * Tests theme can provide classes.
-   */
-  public function testClassLoading() {
-    new ThemeClass();
-  }
-
   /**
    * Tests drupal_find_theme_templates().
    */
@@ -265,56 +156,10 @@ public function testFindThemeTemplates() {
   }
 
   /**
-   * Tests that the page variable is not prematurely flattened.
-   *
-   * Some modules check the page array in template_preprocess_html(), so we
-   * ensure that it has not been rendered prematurely.
-   */
-  public function testPreprocessHtml() {
-    $this->drupalGet('');
-    $attributes = $this->xpath('/html/body[@theme_test_page_variable="Page variable is an array."]');
-    $this->assertTrue(count($attributes) == 1, 'In template_preprocess_html(), the page variable is still an array (not rendered yet).');
-    $this->assertText('theme test page bottom markup', 'Modules are able to set the page bottom region.');
-  }
-
-  /**
-   * Tests that region attributes can be manipulated via preprocess functions.
-   */
-  public function testRegionClass() {
-    \Drupal::service('module_installer')->install(['block', 'theme_region_test']);
-
-    // Place a block.
-    $this->drupalPlaceBlock('system_main_block');
-    $this->drupalGet('');
-    $elements = $this->cssSelect(".region-sidebar-first.new_class");
-    $this->assertEqual(count($elements), 1, 'New class found.');
-  }
-
-  /**
-   * Ensures suggestion preprocess functions run for default implementations.
-   *
-   * The theme hook used by this test has its base preprocess function in a
-   * separate file, so this test also ensures that that file is correctly loaded
-   * when needed.
+   * Tests theme can provide classes.
    */
-  public function testSuggestionPreprocessForDefaults() {
-    $this->config('system.theme')->set('default', 'test_theme')->save();
-    // Test with both an unprimed and primed theme registry.
-    drupal_theme_rebuild();
-    for ($i = 0; $i < 2; $i++) {
-      $this->drupalGet('theme-test/preprocess-suggestions');
-      $items = $this->cssSelect('.suggestion');
-      $expected_values = [
-        'Suggestion',
-        'Kitten',
-        'Monkey',
-        'Kitten',
-        'Flamingo',
-      ];
-      foreach ($expected_values as $key => $value) {
-        $this->assertEqual((string) $value, $items[$key]);
-      }
-    }
+  public function testClassLoading() {
+    new ThemeClass();
   }
 
-}
+}
\ No newline at end of file
diff --git a/core/modules/system/src/Tests/Theme/TwigNamespaceTest.php b/core/modules/system/tests/src/Kernel/Theme/TwigNamespaceTest.php
similarity index 92%
rename from core/modules/system/src/Tests/Theme/TwigNamespaceTest.php
rename to core/modules/system/tests/src/Kernel/Theme/TwigNamespaceTest.php
index ab62889b9a..4ffd2558f0 100644
--- a/core/modules/system/src/Tests/Theme/TwigNamespaceTest.php
+++ b/core/modules/system/tests/src/Kernel/Theme/TwigNamespaceTest.php
@@ -1,15 +1,15 @@
 <?php
 
-namespace Drupal\system\Tests\Theme;
+namespace Drupal\Tests\system\Kernel\Theme;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\KernelTests\KernelTestBase;
 
 /**
  * Tests Twig namespaces.
  *
  * @group Theme
  */
-class TwigNamespaceTest extends WebTestBase {
+class TwigNamespaceTest extends KernelTestBase {
 
   /**
    * Modules to enable.
diff --git a/core/modules/system/src/Tests/Theme/TwigRawTest.php b/core/modules/system/tests/src/Kernel/Theme/TwigRawTest.php
similarity index 90%
rename from core/modules/system/src/Tests/Theme/TwigRawTest.php
rename to core/modules/system/tests/src/Kernel/Theme/TwigRawTest.php
index 8be63dbb51..970e9d5140 100644
--- a/core/modules/system/src/Tests/Theme/TwigRawTest.php
+++ b/core/modules/system/tests/src/Kernel/Theme/TwigRawTest.php
@@ -1,15 +1,15 @@
 <?php
 
-namespace Drupal\system\Tests\Theme;
+namespace Drupal\Tests\system\Kernel\Theme;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\KernelTests\KernelTestBase;
 
 /**
  * Tests Twig 'raw' filter.
  *
  * @group Theme
  */
-class TwigRawTest extends WebTestBase {
+class TwigRawTest extends KernelTestBase {
 
   /**
    * Modules to enable.
