diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
index e92930a..ae6574f 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
@@ -91,16 +91,21 @@ public function createWithSampleValues($bundle = FALSE, array $values = []) {
       $forbidden_keys[] = $revision_key;
     }
     if ($bundle_key = $this->entityType->getKey('bundle')) {
-      if (!$bundle) {
-        throw new EntityStorageException("No entity bundle was specified");
+      if ($bundle) {
+        if (!array_key_exists($bundle, $this->entityManager->getBundleInfo($this->entityTypeId))) {
+          throw new EntityStorageException(sprintf("Missing entity bundle. The \"%s\" bundle does not exist", $bundle));
+        }
+        $values[$bundle_key] = $bundle;
       }
-      if (!array_key_exists($bundle, $this->entityManager->getBundleInfo($this->entityTypeId))) {
-        throw new EntityStorageException(sprintf("Missing entity bundle. The \"%s\" bundle does not exist", $bundle));
-      }
-      $values[$bundle_key] = $bundle;
-      // Bundle is already set
+      // Bundle should not be autogenerated.
       $forbidden_keys[] = $bundle_key;
     }
+
+    // Ensure that default_langcode is TRUE.
+    if ($default_langcode_key = $this->entityType->getKey('default_langcode')) {
+      $values[$default_langcode_key] = TRUE;
+    }
+
     // Forbid sample generation on any keys whose values were submitted.
     $forbidden_keys = array_merge($forbidden_keys, array_keys($values));
     /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */
diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php
index 5d13a5e..4bfcbf9 100644
--- a/core/lib/Drupal/Core/Field/FieldItemList.php
+++ b/core/lib/Drupal/Core/Field/FieldItemList.php
@@ -260,8 +260,11 @@ public function view($display_options = []) {
   public function generateSampleItems($count = 1) {
     $field_definition = $this->getFieldDefinition();
     $field_type_class = $field_definition->getItemDefinition()->getClass();
+    $values = $this->getValue();
     for ($delta = 0; $delta < $count; $delta++) {
-      $values[$delta] = $field_type_class::generateSampleValue($field_definition);
+      if ($value = $field_type_class::generateSampleValue($field_definition)) {
+        $values[$delta] = $value;
+      }
     }
     $this->setValue($values);
   }
diff --git a/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php b/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php
index 07c3149..a9c6512 100644
--- a/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php
+++ b/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php
@@ -17,7 +17,6 @@
  * @code
  * $form['pass'] = array(
  *   '#type' => 'password_confirm',
- *   '#title' => $this->t('Password'),
  *   '#size' => 25,
  * );
  * @endcode
@@ -68,6 +67,9 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
    * Expand a password_confirm field into two text boxes.
    */
   public static function processPasswordConfirm(&$element, FormStateInterface $form_state, &$complete_form) {
+    // Remove the type from the wrapper of the expanded elements.
+    unset($element['#type']);
+
     $element['pass1'] = [
       '#type' => 'password',
       '#title' => t('Password'),
diff --git a/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php b/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php
index 4270a1f..b526363 100644
--- a/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php
+++ b/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php
@@ -153,7 +153,7 @@ protected function updateModeratedEntity($moderation_state_id) {
 
     // Change the entity's default revision flag and the publishing status only
     // if the new workflow state is a valid one.
-    if ($workflow->getTypePlugin()->hasState($moderation_state_id)) {
+    if ($workflow && $workflow->getTypePlugin()->hasState($moderation_state_id)) {
       /** @var \Drupal\content_moderation\ContentModerationState $current_state */
       $current_state = $workflow->getTypePlugin()->getState($moderation_state_id);
 
diff --git a/core/modules/content_moderation/src/StateTransitionValidation.php b/core/modules/content_moderation/src/StateTransitionValidation.php
index fc09e5e..2a06229 100644
--- a/core/modules/content_moderation/src/StateTransitionValidation.php
+++ b/core/modules/content_moderation/src/StateTransitionValidation.php
@@ -40,6 +40,10 @@ public function __construct(ModerationInformationInterface $moderation_info) {
    */
   public function getValidTransitions(ContentEntityInterface $entity, AccountInterface $user) {
     $workflow = $this->moderationInfo->getWorkflowForEntity($entity);
+    if (!$workflow) {
+      return [];
+    }
+
     $current_state = $entity->moderation_state->value ? $workflow->getTypePlugin()->getState($entity->moderation_state->value) : $workflow->getTypePlugin()->getInitialState($entity);
 
     return array_filter($current_state->getTransitions(), function(Transition $transition) use ($workflow, $user) {
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 8dbd170..869f982 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -5,6 +5,7 @@
  * Allows entities to be translated into different languages.
  */
 
+use Drupal\content_translation\Form\ContentTranslationDeleteForm;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Entity\ContentEntityFormInterface;
 use Drupal\Core\Entity\ContentEntityInterface;
@@ -128,9 +129,6 @@ function content_translation_entity_type_alter(array &$entity_types) {
       if (!$entity_type->get('content_translation_metadata')) {
         $entity_type->set('content_translation_metadata', 'Drupal\content_translation\ContentTranslationMetadataWrapper');
       }
-      if (!$entity_type->getFormClass('content_translation_deletion')) {
-        $entity_type->setFormClass('content_translation_deletion', '\Drupal\content_translation\Form\ContentTranslationDeleteForm');
-      }
 
       $translation = $entity_type->get('translation');
       if (!$translation || !isset($translation['content_translation'])) {
@@ -151,6 +149,10 @@ function content_translation_entity_type_alter(array &$entity_types) {
         $translation['content_translation'] += [
           'access_callback' => 'content_translation_translate_access',
         ];
+
+        if (!$entity_type->getFormClass('content_translation_deletion')) {
+          $entity_type->setFormClass('content_translation_deletion', ContentTranslationDeleteForm::class);
+        }
       }
       $entity_type->set('translation', $translation);
     }
diff --git a/core/modules/image/src/Form/ImageStyleEditForm.php b/core/modules/image/src/Form/ImageStyleEditForm.php
index 53d40da..cdd156f 100644
--- a/core/modules/image/src/Form/ImageStyleEditForm.php
+++ b/core/modules/image/src/Form/ImageStyleEditForm.php
@@ -55,11 +55,13 @@ public function form(array $form, FormStateInterface $form_state) {
     $form['#attached']['library'][] = 'image/admin';
 
     // Show the thumbnail preview.
-    $preview_arguments = ['#theme' => 'image_style_preview', '#style' => $this->entity];
     $form['preview'] = [
       '#type' => 'item',
       '#title' => $this->t('Preview'),
-      '#markup' => \Drupal::service('renderer')->render($preview_arguments),
+      'image_style_preview' => [
+        '#theme' => 'image_style_preview',
+        '#style' => $this->entity,
+      ],
       // Render preview above parent elements.
       '#weight' => -5,
     ];
diff --git a/core/modules/language/src/Element/LanguageConfiguration.php b/core/modules/language/src/Element/LanguageConfiguration.php
index 6920b0d..89ef5f9 100644
--- a/core/modules/language/src/Element/LanguageConfiguration.php
+++ b/core/modules/language/src/Element/LanguageConfiguration.php
@@ -34,7 +34,7 @@ public static function processLanguageConfiguration(&$element, FormStateInterfac
     $options = isset($element['#options']) ? $element['#options'] : [];
     // Avoid validation failure since we are moving the '#options' key in the
     // nested 'language' select element.
-    unset($element['#options']);
+    unset($element['#options'], $element['#type']);
     /** @var \Drupal\language\Entity\ContentLanguageSettings $default_config */
     $default_config = $element['#default_value'];
     $element['langcode'] = [
diff --git a/core/modules/language/src/Form/ContentLanguageSettingsForm.php b/core/modules/language/src/Form/ContentLanguageSettingsForm.php
index 38aaffa..e248749 100644
--- a/core/modules/language/src/Form/ContentLanguageSettingsForm.php
+++ b/core/modules/language/src/Form/ContentLanguageSettingsForm.php
@@ -117,6 +117,8 @@ public function buildForm(array $form, FormStateInterface $form_state) {
         $form['settings'][$entity_type_id][$bundle]['settings'] = [
           '#type' => 'item',
           '#label' => $bundle_info['label'],
+          '#title' => $bundle_info['label'],
+          '#title_display' => 'invisible',
           'language' => [
             '#type' => 'language_configuration',
             '#entity_information' => [
diff --git a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
index 9b5b67d..63c7a0d 100644
--- a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
+++ b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php
@@ -149,7 +149,7 @@ public function postSave($update) {
    */
   public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
     $random = new Random();
-    $values['alias'] = str_replace(' ', '-', strtolower($random->sentences(3)));
+    $values['alias'] = '/' . str_replace(' ', '-', strtolower($random->sentences(3)));
     return $values;
   }
 
diff --git a/core/modules/system/src/Form/ThemeAdminForm.php b/core/modules/system/src/Form/ThemeAdminForm.php
index e8d17ed..af605b0 100644
--- a/core/modules/system/src/Form/ThemeAdminForm.php
+++ b/core/modules/system/src/Form/ThemeAdminForm.php
@@ -27,7 +27,7 @@ protected function getEditableConfigNames() {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, array $theme_options = NULL) {
+  public function buildForm(array $form, FormStateInterface $form_state, array $theme_options = []) {
     // Administration theme settings.
     $form['admin_theme'] = [
       '#type' => 'details',
diff --git a/core/modules/update/src/Form/UpdateManagerUpdate.php b/core/modules/update/src/Form/UpdateManagerUpdate.php
index 0b31968..272b4f4 100644
--- a/core/modules/update/src/Form/UpdateManagerUpdate.php
+++ b/core/modules/update/src/Form/UpdateManagerUpdate.php
@@ -64,13 +64,10 @@ public static function create(ContainerInterface $container) {
   public function buildForm(array $form, FormStateInterface $form_state) {
     $this->moduleHandler->loadInclude('update', 'inc', 'update.manager');
 
-    $last_markup = [
+    $form['last_check'] = [
       '#theme' => 'update_last_check',
       '#last' => $this->state->get('update.last_check') ?: 0,
     ];
-    $form['last_check'] = [
-      '#markup' => \Drupal::service('renderer')->render($last_markup),
-    ];
 
     if (!_update_manager_check_backends($form, 'update')) {
       return $form;
diff --git a/core/modules/views/src/Form/ViewsForm.php b/core/modules/views/src/Form/ViewsForm.php
index 575b923..bf3f128 100644
--- a/core/modules/views/src/Form/ViewsForm.php
+++ b/core/modules/views/src/Form/ViewsForm.php
@@ -95,7 +95,7 @@ public function __construct(ClassResolverInterface $class_resolver, UrlGenerator
   /**
    * {@inheritdoc}
    */
-  public static function create(ContainerInterface $container, $view_id = NULL, $view_display_id = NULL, array $view_args = NULL) {
+  public static function create(ContainerInterface $container, $view_id = NULL, $view_display_id = NULL, array $view_args = []) {
     return new static(
       $container->get('class_resolver'),
       $container->get('url_generator'),
diff --git a/core/modules/workflows/src/Entity/Workflow.php b/core/modules/workflows/src/Entity/Workflow.php
index 2450fb7..672c271 100644
--- a/core/modules/workflows/src/Entity/Workflow.php
+++ b/core/modules/workflows/src/Entity/Workflow.php
@@ -25,10 +25,8 @@
  *       "delete" = "Drupal\workflows\Form\WorkflowDeleteForm",
  *       "add-state" = "Drupal\workflows\Form\WorkflowStateAddForm",
  *       "edit-state" = "Drupal\workflows\Form\WorkflowStateEditForm",
- *       "delete-state" = "Drupal\workflows\Form\WorkflowStateDeleteForm",
  *       "add-transition" = "Drupal\workflows\Form\WorkflowTransitionAddForm",
  *       "edit-transition" = "Drupal\workflows\Form\WorkflowTransitionEditForm",
- *       "delete-transition" = "Drupal\workflows\Form\WorkflowTransitionDeleteForm",
  *     },
  *     "route_provider" = {
  *       "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php b/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php
index b5bcd48..5bbce65 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php
@@ -2,7 +2,8 @@
 
 namespace Drupal\KernelTests\Core\Entity;
 
-use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Entity\ContentEntityStorageInterface;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\node\Entity\NodeType;
 use Drupal\taxonomy\Entity\Vocabulary;
@@ -25,7 +26,21 @@ class CreateSampleEntityTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  public static $modules = ['system', 'field', 'filter', 'text', 'file', 'user', 'node', 'comment', 'taxonomy'];
+  public static $modules = [
+    'system',
+    'field',
+    'filter',
+    'text',
+    'file',
+    'user',
+    'node',
+    'comment',
+    'taxonomy',
+    'menu_link_content',
+    'link',
+    'content_translation',
+    'language',
+  ];
 
   /**
    * {@inheritdoc}
@@ -33,6 +48,8 @@ class CreateSampleEntityTest extends KernelTestBase {
   protected function setUp() {
     parent::setup();
 
+    $this->installSchema('system', ['sequences']);
+
     $this->installEntitySchema('file');
     $this->installEntitySchema('user');
     $this->installEntitySchema('node');
@@ -42,6 +59,7 @@ protected function setUp() {
     $this->installEntitySchema('comment_type');
     $this->installEntitySchema('taxonomy_vocabulary');
     $this->installEntitySchema('taxonomy_term');
+    $this->installEntitySchema('menu_link_content');
     $this->entityTypeManager = $this->container->get('entity_type.manager');
     NodeType::create(['type' => 'article', 'name' => 'Article'])->save();
     NodeType::create(['type' => 'page', 'name' => 'Page'])->save();
@@ -55,31 +73,32 @@ protected function setUp() {
    */
   public function testSampleValueContentEntity() {
     foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $definition) {
-      if ($definition->entityClassImplements(FieldableEntityInterface::class)) {
-        $label = $definition->getKey('label');
+      $entity_storage = $this->entityTypeManager->getStorage($entity_type_id);
+      if ($entity_storage instanceof ContentEntityStorageInterface) {
         $values = [];
-        if ($label) {
-          $title = $this->randomString();
-          $values[$label] = $title;
+        if ($label = $definition->getKey('label')) {
+          $values[$label] = $this->randomString();
         }
+
         // Create sample entities with bundles.
         if ($bundle_type = $definition->getBundleEntityType()) {
-          foreach ($this->entityTypeManager->getStorage($bundle_type)->loadMultiple() as $bundle) {
-            $entity = $this->entityTypeManager->getStorage($entity_type_id)->createWithSampleValues($bundle->id(), $values);
-            $violations = $entity->validate();
-            $this->assertCount(0, $violations);
-            if ($label) {
-              $this->assertEquals($title, $entity->label());
-            }
-          }
+          $bundles = array_map(function (EntityInterface $entity_type) {
+            return $entity_type->id();
+          }, $this->entityTypeManager->getStorage($bundle_type)->loadMultiple());
         }
         // Create sample entities without bundles.
         else {
-          $entity = $this->entityTypeManager->getStorage($entity_type_id)->createWithSampleValues(FALSE, $values);
+          $bundles = [FALSE];
+        }
+        foreach ($bundles as $bundle) {
+          $entity = $entity_storage->createWithSampleValues($bundle, $values);
+          $entity->save();
+          $entity_storage->resetCache([$entity->id()]);
+          $entity = $entity_storage->load($entity->id());
           $violations = $entity->validate();
-          $this->assertCount(0, $violations);
+          $this->assertCount(0, $violations, (string) $violations);
           if ($label) {
-            $this->assertEquals($title, $entity->label());
+            $this->assertEquals($values[$label], $entity->label());
           }
         }
       }
diff --git a/core/tests/Drupal/KernelTests/Core/Form/FormElementTitleTest.php b/core/tests/Drupal/KernelTests/Core/Form/FormElementTitleTest.php
new file mode 100644
index 0000000..6edaa22
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Form/FormElementTitleTest.php
@@ -0,0 +1,751 @@
+<?php
+
+namespace Drupal\KernelTests\Core\Form;
+
+use Drupal\comment\CommentInterface;
+use Drupal\Component\Utility\ArgumentsResolver;
+use Drupal\Component\Utility\Crypt;
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceModifierInterface;
+use Drupal\Core\Entity\ContentEntityStorageInterface;
+use Drupal\Core\Entity\EntityFormInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityListBuilderInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\Form\FormInterface;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element;
+use Drupal\Core\Routing\RouteMatch;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\media\Entity\MediaType;
+use Drupal\Tests\Core\Menu\MenuLinkMock;
+use Drupal\update\UpdateProcessorInterface;
+use Drupal\user\Entity\User;
+use Drupal\views\ViewEntityInterface;
+use Drupal\views_ui\Form\Ajax\ViewsFormInterface;
+use Drupal\views_ui\ViewUI;
+use Drupal\workflows\Entity\Workflow;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Ensures all form elements have a #title defined.
+ *
+ * @group Form
+ */
+class FormElementTitleTest extends KernelTestBase implements ServiceModifierInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['system', 'user'];
+
+  /**
+   * The render element types to ignore.
+   *
+   * These elements do not receive input, but are marked with #input => TRUE.
+   *
+   * @var string[]
+   */
+  protected $typesToSkip = [
+    'field_ui_table',
+    'table',
+    'tableselect',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Install the system and user modules.
+    $this->installSchema('system', ['key_value_expire', 'sequences']);
+    $this->installConfig(['system']);
+    $this->installEntitySchema('user');
+    $this->installSchema('user', ['users_data']);
+
+    // Set up a theme.
+    $this->container->get('theme_installer')->install(['stark']);
+
+    // Set up a current user.
+    $user = User::create(['name' => 'Clu']);
+    $user->save();
+    $this->container->get('current_user')->setAccount($user);
+
+    // Install all non-test modules.
+    $all_modules = array_filter(system_rebuild_module_data(), function ($module) {
+      return $module->info['package'] !== 'Testing';
+    });
+    $this->container->get('module_installer')->install(array_keys($all_modules));
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function alter(ContainerBuilder $container) {
+    // Spoof the update processor to prevent it from dialing out.
+    $update_processor = $this->prophesize(UpdateProcessorInterface::class);
+    $container->set('update.processor', $update_processor->reveal());
+  }
+
+  /**
+   * Performs accessibility tests on all forms provided by modules.
+   */
+  public function testFormAccessibility() {
+    // Find all forms.
+    $forms = $this->findForms();
+
+    // Create an entity of each type needed by forms.
+    $entities = $this->createEntities();
+
+    // Prepare the additional parameters for special form classes.
+    $this->prepareForms($entities);
+
+    // Loop over each entity.
+    foreach ($entities as $entity) {
+      $this->checkEntityListForm($entity, $entities);
+
+      if (!empty($entity->getEntityType()->getHandlerClasses()['form'])) {
+        $this->checkForms($entity->getEntityType()->getHandlerClasses()['form'], $entities, $entity);
+      }
+    }
+
+    // Check all non-entity forms.
+    $this->checkForms($forms, $entities, NULL);
+  }
+
+  /**
+   * Gets the directories for all namespaces that may contain forms.
+   *
+   * @return string[]
+   *   An associative array of directories, keyed by their namespace.
+   */
+  protected function getDirectories() {
+    // This contains the namespaces of all enabled modules, as well as some
+    // arbitrary portion of \Drupal\Core.
+    $directories = $this->container->get('container.namespaces');
+
+    // Add in the remaining namespaces for \Drupal\Core.
+    foreach (new \FilesystemIterator($this->root . '/core/lib/Drupal/Core', \FilesystemIterator::SKIP_DOTS) as $component) {
+      if ($component->isDir()) {
+        $directories['Drupal\\Core\\' . $component->getFilename()] = 'core/lib/Drupal/Core/' . $component->getFilename();
+      }
+    }
+
+    return $directories;
+  }
+
+  /**
+   * Finds all defined forms.
+   *
+   * @return string[]
+   *   An array of fully qualified class names of forms.
+   */
+  protected function findForms() {
+    $forms = [];
+    foreach ($this->getDirectories() as $namespace => $directory) {
+      if (is_dir($directory)) {
+        $iterator = new \RecursiveIteratorIterator(
+          new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS)
+        );
+        foreach ($iterator as $file_info) {
+          if ($file_info->getExtension() == 'php') {
+            $sub_path = $iterator->getSubIterator()->getSubPath();
+            $sub_path = $sub_path ? str_replace(DIRECTORY_SEPARATOR, '\\', $sub_path) . '\\' : '';
+            $class = $namespace . '\\' . $sub_path . $file_info->getBasename('.php');
+
+            if (is_subclass_of($class, FormInterface::class)) {
+              // Entity forms are handled directly.
+              if (is_subclass_of($class, EntityFormInterface::class)) {
+                continue;
+              }
+
+              // Entity list builders that provide forms are handled directly.
+              if (is_subclass_of($class, EntityListBuilderInterface::class)) {
+                continue;
+              }
+
+              // Don't attempt to instantiate abstract forms.
+              if ((new \ReflectionClass($class))->isAbstract()) {
+                continue;
+              }
+
+              $forms[] = $class;
+            }
+          }
+        }
+      }
+    }
+
+    return $forms;
+  }
+
+  /**
+   * Gets the entity values needed to create an entity of each entity type.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
+   *   The available entity types.
+   *
+   * @return array
+   *   An associative array keyed by entity type ID, containing the necessary
+   *   values to create each entity.
+   */
+  protected function getEntityValues(array $entity_types) {
+    $entity_values = [];
+    // Define values for the three entity types that need to be created first.
+    $entity_values['field_storage_config'] = [
+      'field_name' => 'comment',
+      'type' => 'comment',
+      'entity_type' => 'user',
+      'settings' => ['comment_type' => 'comment'],
+    ];
+    $entity_values['field_config'] = [
+      'field_name' => 'comment',
+      'entity_type' => 'user',
+      'bundle' => 'user',
+    ];
+    $entity_values['filter_format'] = [
+      'format' => 'my_filter_format',
+    ];
+
+    // Sort the entity types, since config entities need to exist before content
+    // entities can reference them.
+    uasort($entity_types, function (EntityTypeInterface $entity_type) {
+      return $entity_type->entityClassImplements(ConfigEntityInterface::class) ? -1 : 1;
+    });
+
+    // Create empty entity values for each entity type that provides a form.
+    foreach ($entity_types as $entity_type_id => $entity_type) {
+      if (!empty($entity_type->getHandlerClasses()['form']) && !isset($entity_values[$entity_type_id])) {
+        $entity_values[$entity_type_id] = [];
+      }
+    }
+
+    // Fill in the necessary values for each entity type.
+    $entity_values['action'] = [
+      'plugin' => 'user_block_user_action',
+    ];
+    $entity_values['block'] = [
+      'plugin' => 'page_title_block',
+    ];
+    $entity_values['comment'] = [
+      'field_name' => 'comment',
+      'entity_type' => 'user',
+      'entity_id' => 1,
+      'uid' => 1,
+      'pid' => 0,
+      'status' => CommentInterface::PUBLISHED,
+    ];
+    $entity_values['comment_type'] = [
+      'id' => 'comment',
+      'target_entity_type_id' => 'user',
+    ];
+    $entity_values['editor'] = [
+      'format' => 'my_filter_format',
+      'editor' => 'ckeditor',
+      'image_upload' => [
+        'status' => FALSE,
+        'scheme' => file_default_scheme(),
+        'directory' => 'inline-images',
+        'max_size' => '',
+        'max_dimensions' => [
+          'width' => '',
+          'height' => '',
+        ],
+      ],
+    ];
+    $entity_values['entity_form_display'] = [
+      'targetEntityType' => 'user',
+      'bundle' => 'user',
+      'mode' => 'default',
+      'status' => TRUE,
+      'content' => [
+        'comment' => [
+          'type' => 'comment_default',
+          'region' => 'content',
+          'weight' => 0,
+          'settings' => [],
+          'third_party_settings' => [],
+        ],
+      ],
+    ];
+    $entity_values['entity_view_mode'] = [
+      'id' => 'user.default',
+      'targetEntityType' => 'user',
+    ];
+    $entity_values['entity_form_mode'] = [
+      'id' => 'user.default',
+      'targetEntityType' => 'user',
+    ];
+    $entity_values['entity_view_display'] = [
+      'targetEntityType' => 'user',
+      'bundle' => 'user',
+      'mode' => 'default',
+    ];
+    $entity_values['image_style'] = [
+      'effects' => [
+        'some-uuid' => [
+          'uuid' => 'some-uuid',
+          'id' => 'image_scale',
+          'weight' => 0,
+          'data' => [
+            'width' => 420,
+            'height' => 420,
+            'upscale' => FALSE,
+          ],
+        ],
+      ],
+    ];
+    $entity_values['media'] = [
+      'bundle' => 'file',
+    ];
+    $entity_values['menu'] = [
+      'id' => 'mock',
+    ];
+    $entity_values['menu_link_content'] = [
+      'menu_name' => 'mock',
+    ];
+    $entity_values['node'] = [
+      'uid' => 1,
+      'status' => TRUE,
+      'published' => TRUE,
+      'type' => 'my_book',
+      'title' => 'My book',
+      'book' => [
+        'bid' => 'new',
+      ],
+    ];
+    $entity_values['node_type'] = [
+      'type' => 'my_book',
+    ];
+    $entity_values['search_page'] = [
+      'plugin' => 'user_search',
+    ];
+    return $entity_values;
+  }
+
+  /**
+   * Creates an entity for each entity type.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface[]
+   *   An array of entities.
+   */
+  protected function createEntities() {
+    $entity_type_manager = $this->container->get('entity_type.manager');
+
+    /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
+    $entity_types = $entity_type_manager->getDefinitions();
+    $entity_values = $this->getEntityValues($entity_types);
+
+    // Load existing entities for use by forms.
+    $entities = [];
+    $entities['configurable_language'] = ConfigurableLanguage::load('en');
+    $entities['media_type'] = MediaType::load('file');
+    $entities['workflow'] = Workflow::load('editorial');
+    // Remove any entity types with existing entities from the list to process.
+    $entity_values = array_diff_key($entity_values, $entities);
+
+    foreach ($entity_values as $entity_type_id => $entity_value) {
+      $entity_type = $entity_types[$entity_type_id];
+      $entity_storage = $entity_type_manager->getStorage($entity_type_id);
+
+      // Content entities can have sample values created.
+      if ($entity_storage instanceof ContentEntityStorageInterface) {
+        $bundle_entity_type_id = $entity_type->getBundleEntityType();
+        $bundle = $bundle_entity_type_id ? $entities[$bundle_entity_type_id]->id() : FALSE;
+        $entity = $entity_storage->createWithSampleValues($bundle, $entity_values[$entity_type_id]);
+      }
+      else {
+        $values = $entity_values[$entity_type_id];
+        // Config entities must have a valid ID.
+        $values += [$entity_type->getKey('id') => "id__$entity_type_id"];
+        $entity = $entity_storage->create($values);
+      }
+
+      $entity->save();
+
+      // If the storage supports it, reload the entity to ensure any changes
+      // made during save are present.
+      if ($entity_storage->hasData()) {
+        $entity_storage->resetCache([$entity->id()]);
+        $entity = $entity_storage->load($entity->id());
+      }
+
+      // Views forms expect the view entity to be decorated as a ViewUI object.
+      if ($entity instanceof ViewEntityInterface) {
+        $entity = new ViewUI($entity);
+      }
+
+      $entities[$entity_type_id] = $entity;
+    }
+    return $entities;
+  }
+
+  /**
+   * Performs any operations needed to process forms.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface[] $entities
+   *   An array of entities keyed by entity type ID.
+   */
+  protected function prepareForms(array $entities) {
+    // Needed for \Drupal\ban\Form\BanDelete.
+    $this->container->get('ban.ip_manager')->banIp('86.7.5.309');
+
+    // Needed for Drupal\system\Form\CronForm.
+    $this->container->get('state')->set('system.cron_key', Crypt::randomBytesBase64(55));
+
+    // Needed for \Drupal\update\Form\UpdateManagerInstall.
+    if (!defined('DRUPAL_TEST_IN_CHILD_SITE')) {
+      define('DRUPAL_TEST_IN_CHILD_SITE', FALSE);
+    }
+
+    // Needed for \Drupal\Core\Installer\Form\SelectLanguageForm.
+    require_once $this->root . '/core/includes/install.core.inc';
+
+    $tempstore = $this->container->get('user.private_tempstore');
+
+    // Needed for \Drupal\user\Form\UserMultipleCancelConfirm.
+    $tempstore->get('user_user_operations_cancel')->set(1, [User::load(2)]);
+
+    // Needed for \Drupal\node\Form\DeleteMultiple.
+    $tempstore->get('node_multiple_delete_confirm')->set(1, [
+      $entities['node']->id() => [
+        $entities['node']->language()->getId(),
+      ],
+    ]);
+
+    // Needed for \Drupal\comment\Form\ConfirmDeleteMultiple.
+    $tempstore->get('comment_multiple_delete_confirm')->set(1, [
+      $entities['comment']->id() => [
+        $entities['comment']->language()->getId(),
+      ],
+    ]);
+
+    // Needed for \Drupal\media\Form\MediaDeleteMultipleConfirmForm.
+    $tempstore->get('media_multiple_delete_confirm')->set(1, [
+      $entities['media']->id() => [
+        $entities['media']->language()->getId(),
+      ],
+    ]);
+
+    $keyvalue_expirable = $this->container->get('keyvalue.expirable');
+
+    // Needed for \Drupal\system\Form\ModulesUninstallConfirmForm.
+    $keyvalue_expirable->get('modules_uninstall')->set(1, ['telephone']);
+
+    // Needed for Drupal\system\Form\ModulesListConfirmForm.
+    $keyvalue_expirable->get('module_list')->set(1, [
+      'install' => [
+        'telephone' => 'Telephone',
+      ],
+      'experimental' => [],
+      'dependencies' => [],
+    ]);
+  }
+
+  /**
+   * Checks each entity list form.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   * @param \Drupal\Core\Entity\EntityInterface[] $entities
+   *   An array of entities keyed by entity type ID.
+   */
+  protected function checkEntityListForm(EntityInterface $entity, array $entities) {
+    $entity_type_manager = $this->container->get('entity_type.manager');
+
+    $entity_type = $entity->getEntityType();
+    $entity_type_id = $entity->getEntityTypeId();
+
+    $class = $entity_type->getListBuilderClass();
+    if (is_subclass_of($class, FormInterface::class)) {
+      $list_builder = $entity_type_manager->getListBuilder($entity_type_id);
+
+      $arguments = $this->buildArguments($list_builder, 'render', $entities);
+
+      $form = call_user_func_array([$list_builder, 'render'], $arguments);
+      $this->ensureFormElementTitle($form, $class);
+    }
+  }
+
+  /**
+   * Builds up the arguments needed by a given form method.
+   *
+   * @param object|string $object
+   *   An object instance or a class name.
+   * @param string $method
+   *   The method name.
+   * @param \Drupal\Core\Entity\EntityInterface[] $entities
+   *   An array of entities keyed by entity type ID.
+   *
+   * @return array
+   *   An array of arguments that satisfy the parameters of the given method.
+   */
+  protected function buildArguments($object, $method, array $entities) {
+    if (is_string($object)) {
+      $object = $this->container->get('class_resolver')->getInstanceFromDefinition($object);
+    }
+
+    $reflection_method = new \ReflectionMethod($object, $method);
+    $number_of_parameters = $reflection_method->getNumberOfParameters();
+    $number_of_required_parameters = $reflection_method->getNumberOfRequiredParameters();
+
+    // If this class adds no additional parameters, nothing more to do.
+    if ($number_of_parameters == $number_of_required_parameters) {
+      return [];
+    }
+
+    $unfiltered_arguments = $this->getArguments($object, $method, $entities);
+
+    // NULL values are the standard way of adding additional parameters to an
+    // interface method. Filter out NULL values returned by the resolver.
+    $filtered_arguments = array_filter($unfiltered_arguments, function ($argument) {
+      return !is_null($argument);
+    });
+
+    // Handle any arguments with unsatisfied classed parameters.
+    if (count($filtered_arguments) !== $number_of_parameters) {
+      $parameters = $reflection_method->getParameters();
+      for ($i = count($filtered_arguments); $i < $number_of_parameters; $i++) {
+        $parameter = $parameters[$i];
+        if ($parameter->getClass()) {
+          $this->fail(sprintf('%s::%s() missing value for parameter %s: %s $%s', get_class($object), $method, $i + 1, $parameter->getType(), $parameter->getName()));
+        }
+      }
+    }
+
+    // Remove any arguments for required parameters.
+    return array_slice($filtered_arguments, $number_of_required_parameters);
+  }
+
+  /**
+   * Gets arguments suitable for passing to the given method.
+   *
+   * @param object $object
+   *   An object instance that implements \Drupal\Core\Form\FormInterface.
+   * @param string $method
+   *   The method name.
+   * @param \Drupal\Core\Entity\EntityInterface[] $entities
+   *   An array of entities keyed by entity type ID.
+   *
+   * @return array
+   *   An array of arguments to pass to the given method.
+   */
+  protected function getArguments($object, $method, array $entities) {
+    // Add in an empty form array.
+    $scalars = ['form' => []];
+    // Add in the entities of every entity type as possible objects, as well as
+    // an empty form state.
+    $objects = [
+      'form_state' => new FormState(),
+    ];
+    $objects += $entities;
+
+    switch (get_class($object)) {
+      case 'Drupal\views_ui\Form\Ajax\ConfigHandler':
+        $objects['request'] = new Request();
+        break;
+
+      case 'Drupal\content_translation\Form\ContentTranslationDeleteForm':
+        $objects['language'] = $entities['configurable_language'];
+        break;
+
+      case 'Drupal\config_translation\Form\ConfigTranslationAddForm':
+      case 'Drupal\config_translation\Form\ConfigTranslationEditForm':
+      case 'Drupal\config_translation\Form\ConfigTranslationDeleteForm':
+        $scalars['plugin_id'] = 'node_type';
+        $scalars['langcode'] = 'en';
+        $objects['route_match'] = new RouteMatch('the_route_name', new Route('/the/{node_type}/path'), ['node_type' => $entities['node_type']]);
+        break;
+
+      case 'Drupal\views_ui\ViewAddForm':
+      case 'Drupal\views_ui\ViewDuplicateForm':
+      case 'Drupal\views_ui\ViewEditForm':
+      case 'Drupal\views_ui\ViewPreviewForm':
+        $scalars['display_id'] = 'default';
+        break;
+
+      case 'Drupal\quickedit\Form\QuickEditFieldForm':
+        $scalars['field_name'] = 'comment';
+        $objects['entity'] = $entities['user'];
+        break;
+
+      case 'Drupal\content_moderation\Form\ContentModerationConfigureEntityTypesForm':
+        $scalars['entity_type_id'] = 'node';
+        break;
+
+      case 'Drupal\user\Form\UserPasswordResetForm':
+        $scalars['expiration_date'] = 'Sun, 11/19/1978 - 05:00';
+        $scalars['timestamp'] = 280299600;
+        $scalars['hash'] = user_pass_rehash($entities['user'], $this->container->get('datetime.time')->getRequestTime());
+        break;
+
+      case 'Drupal\image\Form\ImageEffectAddForm':
+        $scalars['image_effect'] = 'image_crop';
+        break;
+
+      case 'Drupal\image\Form\ImageEffectDeleteForm':
+      case 'Drupal\image\Form\ImageEffectEditForm':
+        $scalars['image_effect'] = 'some-uuid';
+        break;
+
+      case 'Drupal\workflows\Form\WorkflowStateDeleteForm':
+      case 'Drupal\workflows\Form\WorkflowStateEditForm':
+        $scalars['workflow_state'] = 'published';
+        break;
+
+      case 'Drupal\workflows\Form\WorkflowTransitionDeleteForm':
+      case 'Drupal\workflows\Form\WorkflowTransitionEditForm':
+        $scalars['workflow_transition'] = 'publish';
+        break;
+
+      case 'Drupal\block\BlockListBuilder':
+        $scalars['theme'] = 'stark';
+        $objects['request'] = new Request();
+        break;
+
+      case 'Drupal\field_ui\Form\EntityFormModeAddForm':
+      case 'Drupal\field_ui\Form\EntityDisplayModeAddForm':
+        $scalars['entity_type_id'] = 'node';
+        break;
+
+      case 'Drupal\system\Form\PrepareModulesEntityUninstallForm':
+        $scalars['entity_type_id'] = 'node';
+        break;
+
+      case 'Drupal\ban\Form\BanDelete':
+        $scalars['ban_id'] = 1;
+        break;
+
+      case 'Drupal\search\Form\SearchPageAddForm':
+        $scalars['search_plugin_id'] = 'user_search';
+        break;
+
+      case 'Drupal\field_ui\Form\FieldStorageAddForm':
+        $scalars['entity_type_id'] = 'user';
+        $scalars['bundle'] = 'user';
+        break;
+
+      case 'Drupal\field_ui\Form\FieldStorageConfigEditForm':
+        $scalars['field_config'] = 'user.user.comment';
+        break;
+
+      case 'Drupal\views\Form\ViewsForm':
+      case 'Drupal\views\Form\ViewsFormMainForm':
+        $executable_view = $entities['view']->getExecutable();
+        $executable_view->initHandlers();
+        $objects['view'] = $executable_view;
+        break;
+
+      case 'Drupal\menu_ui\Form\MenuLinkEditForm':
+      case 'Drupal\menu_ui\Form\MenuLinkResetForm':
+        $objects['menu_link_plugin'] = MenuLinkMock::create(['id' => 'test']);
+        break;
+
+      case 'Drupal\content_moderation\Form\EntityModerationForm':
+        $objects['entity'] = $entities['node'];
+        break;
+
+      case 'Drupal\Core\Installer\Form\SelectProfileForm':
+        $scalars['install_state'] = ['profiles' => []];
+        break;
+
+    }
+
+    return (new ArgumentsResolver($scalars, $objects, []))->getArguments([$object, $method]);
+  }
+
+  /**
+   * Handles the form state manipulation needed by the Views UI.
+   *
+   * @param string $class
+   *   The name of the class.
+   * @param \Drupal\Core\Entity\EntityInterface[] $entities
+   *   An array of entities keyed by entity type ID.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   */
+  protected function handleViewsFormState($class, array $entities, FormStateInterface $form_state) {
+    if (is_subclass_of($class, ViewsFormInterface::class)) {
+      $form_state->set('view', $entities['view']);
+      $form_state->set('display_id', 'default');
+      $form_state->loadInclude('views_ui', 'inc', 'admin');
+    }
+
+    switch ($class) {
+      case 'Drupal\views\Form\ViewsExposedForm':
+        $executable_view = $entities['view']->getExecutable();
+        $executable_view->initHandlers();
+        $form_state->set('view', $executable_view);
+        break;
+
+      case 'Drupal\views_ui\Form\Ajax\AddHandler':
+      case 'Drupal\views_ui\Form\Ajax\ConfigHandler':
+      case 'Drupal\views_ui\Form\Ajax\ConfigHandlerExtra':
+      case 'Drupal\views_ui\Form\Ajax\ConfigHandlerGroup':
+      case 'Drupal\views_ui\Form\Ajax\Rearrange':
+        $form_state->set('type', 'sort');
+        break;
+    }
+  }
+
+  /**
+   * Checks each form.
+   *
+   * @param string[] $form_classes
+   *   An array of fully qualified class names of forms.
+   * @param \Drupal\Core\Entity\EntityInterface[] $entities
+   *   An array of entities.
+   * @param \Drupal\Core\Entity\EntityInterface|null $entity
+   *   The entity, if it is relevant to the forms being checked.
+   */
+  protected function checkForms(array $form_classes, array $entities, EntityInterface $entity = NULL) {
+    $entity_form_builder = $this->container->get('entity.form_builder');
+    $form_builder = $this->container->get('form_builder');
+
+    // Perform tests on all forms.
+    foreach ($form_classes as $operation => $class) {
+      $form_state = new FormState();
+      $form_state->addBuildInfo('args', $this->buildArguments($class, 'buildForm', $entities));
+
+      $this->handleViewsFormState($class, $entities, $form_state);
+
+      if ($entity) {
+        $form = $entity_form_builder->getForm($entity, $operation, $form_state->getCacheableArray());
+      }
+      else {
+        $form = $form_builder->buildForm($class, $form_state);
+      }
+
+      $this->ensureFormElementTitle($form, $class);
+    }
+  }
+
+  /**
+   * Tests that all form elements of a form have a title.
+   *
+   * @param array $element
+   *   An associative array containing a form element.
+   * @param string $class
+   *   The class name used to build the form.
+   */
+  protected function ensureFormElementTitle(array $element, $class) {
+    foreach (Element::children($element) as $key) {
+      $this->ensureFormElementTitle($element[$key], $class);
+
+      // Skip any element without a valid #type or that does not accept input.
+      if (!isset($element['#type']) || in_array($element['#type'], $this->typesToSkip) || empty($element['#input'])) {
+        continue;
+      }
+
+      $this->assertArrayHasKey('#title', $element, sprintf('%s: %s (%s)', $class, implode('][', $element['#parents']), $element['#type']));
+    }
+  }
+
+}
