diff --git a/.travis.yml b/.travis.yml
index 7c84d51e471c238382d76e9e17367042d096af28..0a4764e3327c7bed6585eda94acdc4e509b09aef 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,10 +40,20 @@ before_script:
   # Download Drupal 8 core.
   - travis_retry git clone --branch 8.0.x --depth 1 http://git.drupal.org/project/drupal.git
   - cd drupal
+  # Add a patch for BrowserTestBase
+  - curl -o 2636228-6.patch https://www.drupal.org/files/issues/2636228-6.patch
+  - patch -p1 < 2636228-6.patch
 
   # Reference entity in build site.
   - ln -s $TESTDIR modules/entity
 
+  # Start a web server on port 8888, run in the background; wait for
+  # initialization.
+  - nohup php -S localhost:8888 > /dev/null 2>&1 &
+
+  # Export web server URL for browser tests.
+  - export SIMPLETEST_BASE_URL=http://localhost:8888
+
 script:
   # Run the PHPUnit tests which also include the kernel tests.
-  - ./vendor/phpunit/phpunit/phpunit -c ./core/phpunit.xml.dist ./modules/entity
+  - ./vendor/phpunit/phpunit/phpunit -c ./core/phpunit.xml.dist --verbose ./modules/entity
diff --git a/entity.module b/entity.module
new file mode 100644
index 0000000000000000000000000000000000000000..f1845403d133d6e80fa2581a5d4153f70063eb4b
--- /dev/null
+++ b/entity.module
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @file
+ * Provides expanded entity APIs.
+ */
+
+use Drupal\Core\Url;
+
+/**
+ * Implements hook_theme().
+ */
+function entity_theme() {
+  return [
+    'entity_add_list' => [
+      'variables' => [
+        'bundles' => [],
+        'bundle_type' => NULL,
+      ],
+      'template' => 'entity-add-list',
+    ],
+  ];
+}
+
+/**
+ * Prepares variables for the list of available bundles.
+ *
+ * Default template: entity-add-list.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - bundle_type: The entity type of the bundles.
+ *   - bundles: An array of bundles with the label, description, add_link keys.
+ */
+function template_preprocess_entity_add_list(&$variables) {
+  $bundle_type = \Drupal::entityTypeManager()->getDefinition($variables['bundle_type']);
+  $variables += [
+    'create_bundle_url' => Url::fromRoute('entity.' . $bundle_type->id() . '.add_form')->toString(),
+    'bundle_type_label' => $bundle_type->getLowercaseLabel(),
+  ];
+
+  foreach ($variables['bundles'] as $bundle_name => $bundle_info) {
+    $variables['bundles'][$bundle_name]['description'] = [
+      '#markup' => $bundle_info['description'],
+    ];
+  }
+}
diff --git a/src/Controller/EntityCreateController.php b/src/Controller/EntityCreateController.php
new file mode 100644
index 0000000000000000000000000000000000000000..bece86b969131b8eb7b1329fd686868bacf2f5cc
--- /dev/null
+++ b/src/Controller/EntityCreateController.php
@@ -0,0 +1,220 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity\Controller\EntityCreateController.
+ */
+
+namespace Drupal\entity\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Link;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+/**
+ * A generic controller for creating entities.
+ */
+class EntityCreateController extends ControllerBase {
+
+  /**
+   * The entity type bundle info.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
+   */
+  protected $entityTypeBundleInfo;
+
+  /**
+   * The renderer service.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * Constructs a new EntityCreateController object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
+   *   The entity type bundle info.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
+   */
+  public function __construct(EntityTypeBundleInfoInterface $entity_type_bundle_info, RendererInterface $renderer) {
+    $this->entityTypeBundleInfo = $entity_type_bundle_info;
+    $this->renderer = $renderer;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.bundle.info'),
+      $container->get('renderer')
+    );
+  }
+
+  /**
+   * Displays add links for the available bundles.
+   *
+   * Redirects to the add form if there's only one bundle available.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
+   * @return \Symfony\Component\HttpFoundation\RedirectResponse|array
+   *   If there's only one available bundle, a redirect response.
+   *   Otherwise, a render array with the add links for each bundle.
+   */
+  public function addPage($entity_type_id, Request $request) {
+    $entity_type = $this->entityTypeManager()->getDefinition($entity_type_id);
+    $bundle_type = $entity_type->getBundleEntityType();
+    $bundle_key = $entity_type->getKey('bundle');
+    $form_route_name = 'entity.' . $entity_type_id . '.add_form';
+    $build = [
+      '#theme' => 'entity_add_list',
+      '#cache' => [
+        'tags' => $entity_type->getListCacheTags(),
+      ],
+      '#bundle_type' => $bundle_type,
+    ];
+    $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
+    // Filter out the bundles the user doesn't have access to.
+    $access_control_handler = $this->entityTypeManager()->getAccessControlHandler($bundle_type);
+    foreach ($bundles as $bundle_name => $bundle_info) {
+      $access = $access_control_handler->createAccess($bundle_name, NULL, [], TRUE);
+      if (!$access->isAllowed()) {
+        unset($bundles[$bundle_name]);
+      }
+      $this->renderer->addCacheableDependency($build, $access);
+    }
+    // Redirect if there's only one bundle available.
+    if (count($bundles) == 1) {
+      $bundle_names = array_keys($bundles);
+      $bundle_name = reset($bundle_names);
+      return $this->redirect($form_route_name, [$bundle_key => $bundle_name]);
+    }
+    // Prepare the #bundles array for the template.
+    $bundles = $this->loadBundleDescriptions($bundles, $bundle_type);
+    foreach ($bundles as $bundle_name => $bundle_info) {
+      $build['#bundles'][$bundle_name] = [
+        'label' => $bundle_info['label'],
+        'description' => $bundle_info['description'],
+        'add_link' => Link::createFromRoute($bundle_info['label'], $form_route_name, [$bundle_key => $bundle_name]),
+      ];
+    }
+
+    return $build;
+  }
+
+  /**
+   * The title callback for the add page.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   *
+   * @return string
+   *   The page title.
+   */
+  public function addPageTitle($entity_type_id) {
+    $entity_type = $this->entityTypeManager()->getDefinition($entity_type_id);
+    return $this->t('Add @entity-type', ['@entity-type' => $entity_type->getLowercaseLabel()]);
+  }
+
+  /**
+   * Provides the add form for an entity.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match.
+   *
+   * @return array
+   *   The add form.
+   */
+  public function addForm($entity_type_id, RouteMatchInterface $route_match) {
+    $entity_type = $this->entityTypeManager()->getDefinition($entity_type_id);
+    $values = [];
+    // Entities of this type have bundles, one was provided in the url.
+    if ($bundle_key = $entity_type->getKey('bundle')) {
+      $bundle_name = $route_match->getRawParameter($bundle_key);
+      $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
+      if (empty($bundle_name) || !isset($bundles[$bundle_name])) {
+        // The bundle parameter is invalid.
+        throw new NotFoundHttpException();
+      }
+      $values[$bundle_key] = $bundle_name;
+    }
+    $entity = $this->entityTypeManager()->getStorage($entity_type_id)->create($values);
+
+    return $this->entityFormBuilder()->getForm($entity, 'add');
+  }
+
+  /**
+   * The title callback for the add form.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match.
+   *
+   * @return string
+   *   The page title.
+   */
+  public function addFormTitle($entity_type_id, RouteMatchInterface $route_match) {
+    $entity_type = $this->entityTypeManager()->getDefinition($entity_type_id);
+    $bundle_key = $entity_type->getKey('bundle');
+    $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
+    if ($bundle_key && count($bundles) > 1) {
+      $bundle_name = $route_match->getRawParameter($bundle_key);
+      $title = $this->t('Add @bundle', ['@bundle' => $bundles[$bundle_name]['label']]);
+    }
+    else {
+      $title = $this->t('Add @entity-type', ['@entity-type' => $entity_type->getLowercaseLabel()]);
+    }
+
+    return $title;
+  }
+
+  /**
+   * Expands the bundle information with descriptions, if known.
+   *
+   * @param array $bundles
+   *   An array of bundle information.
+   * @param string $bundle_type
+   *   The id of the bundle entity type.
+   *
+   * @return array
+   *   The expanded array of bundle information.
+   */
+  protected function loadBundleDescriptions(array $bundles, $bundle_type) {
+    // Ensure the presence of the description key.
+    foreach ($bundles as $bundle_name => &$bundle_info) {
+      $bundle_info['description'] = '';
+    }
+    // Only bundles provided by entity types have descriptions.
+    if (empty($bundle_type)) {
+      return $bundles;
+    }
+    $bundle_entity_type = $this->entityTypeManager()->getDefinition($bundle_type);
+    if (!$bundle_entity_type->isSubclassOf('\Drupal\entity\Entity\EntityDescriptionInterface')) {
+      return $bundles;
+    }
+    $bundle_names = array_keys($bundles);
+    $bundle_entities = $this->entityTypeManager->getStorage($bundle_type)->loadMultiple($bundle_names);
+    foreach ($bundles as $bundle_name => &$bundle_info) {
+      if (isset($bundle_entities[$bundle_name])) {
+        $bundle_info['description'] = $bundle_entities[$bundle_name]->getDescription();
+      }
+    }
+
+    return $bundles;
+  }
+
+}
diff --git a/src/Entity/EntityDescriptionInterface.php b/src/Entity/EntityDescriptionInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..eb770e1d8d9c8f6ae1f9622ea6dd2c25c7d0ca1e
--- /dev/null
+++ b/src/Entity/EntityDescriptionInterface.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity\Entity\EntityDescriptionInterface.
+ */
+
+namespace Drupal\entity\Entity;
+
+/**
+ * Defines the interface for entities that have a description.
+ */
+interface EntityDescriptionInterface {
+
+  /**
+   * Gets the entity description.
+   *
+   * @return string
+   *   The entity description.
+   */
+  public function getDescription();
+
+  /**
+   * Sets the entity description.
+   *
+   * @param string $description
+   *   The entity description.
+   *
+   * @return $this
+   */
+  public function setDescription($description);
+
+}
diff --git a/src/Routing/AdminCreateHtmlRouteProvider.php b/src/Routing/AdminCreateHtmlRouteProvider.php
new file mode 100644
index 0000000000000000000000000000000000000000..0161715f55278fdbfcf0a03a3ac84e869f761bc2
--- /dev/null
+++ b/src/Routing/AdminCreateHtmlRouteProvider.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity\Routing\AdminCreateHtmlRouteProvider.
+ */
+
+namespace Drupal\entity\Routing;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Provides HTML routes for creating entities using the administrative theme.
+ */
+class AdminCreateHtmlRouteProvider extends CreateHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function addPageRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::addPageRoute($entity_type)) {
+      $route->setOption('_admin_route', TRUE);
+      return $route;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function addFormRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::addFormRoute($entity_type)) {
+      $route->setOption('_admin_route', TRUE);
+      return $route;
+    }
+  }
+
+}
diff --git a/src/Routing/CreateHtmlRouteProvider.php b/src/Routing/CreateHtmlRouteProvider.php
new file mode 100644
index 0000000000000000000000000000000000000000..221b598e906d7403d4a814d0308f06df87132169
--- /dev/null
+++ b/src/Routing/CreateHtmlRouteProvider.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity\Routing\CreateHtmlRouteProvider.
+ */
+
+namespace Drupal\entity\Routing;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Provides HTML routes for creating entities.
+ *
+ * This class provides the following routes for entities, with title callbacks:
+ * - add-page
+ * - add-form
+ *
+ * @see \Drupal\entity\Routing\AdminCreateHtmlRouteProvider.
+ */
+class CreateHtmlRouteProvider implements EntityRouteProviderInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $routes = new RouteCollection();
+    if ($route = $this->addPageRoute($entity_type)) {
+      $routes->add('entity.' . $entity_type->id() . '.add_page', $route);
+    }
+    if ($route = $this->addFormRoute($entity_type)) {
+      $routes->add('entity.' . $entity_type->id() . '.add_form', $route);
+    }
+
+    return $routes;
+  }
+
+  /**
+   * Returns the add page route.
+   *
+   * Built only for entity types that have bundles.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return \Symfony\Component\Routing\Route|null
+   *   The generated route, if available.
+   */
+  protected function addPageRoute(EntityTypeInterface $entity_type) {
+    if ($entity_type->hasLinkTemplate('add-page') && $entity_type->getKey('bundle')) {
+      $route = new Route($entity_type->getLinkTemplate('add-page'));
+      $route->setDefault('_controller', '\Drupal\entity\Controller\EntityCreateController::addPage');
+      $route->setDefault('_title_callback', '\Drupal\entity\Controller\EntityCreateController::addPageTitle');
+      $route->setDefault('entity_type_id', $entity_type->id());
+      $route->setRequirement('_entity_create_access', $entity_type->id());
+
+      return $route;
+    }
+  }
+
+  /**
+   * Returns the add form route.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return \Symfony\Component\Routing\Route|null
+   *   The generated route, if available.
+   */
+  protected function addFormRoute(EntityTypeInterface $entity_type) {
+    if ($entity_type->hasLinkTemplate('add-form')) {
+      $route = new Route($entity_type->getLinkTemplate('add-form'));
+      $route->setDefault('_controller', '\Drupal\entity\Controller\EntityCreateController::addForm');
+      $route->setDefault('_title_callback', '\Drupal\entity\Controller\EntityCreateController::addFormTitle');
+      $route->setDefault('entity_type_id', $entity_type->id());
+      $route->setRequirement('_entity_create_access', $entity_type->id());
+
+      return $route;
+    }
+  }
+
+}
diff --git a/templates/entity-add-list.html.twig b/templates/entity-add-list.html.twig
new file mode 100644
index 0000000000000000000000000000000000000000..55970efd08b32e55aa292407afe5ee8c392fc9e9
--- /dev/null
+++ b/templates/entity-add-list.html.twig
@@ -0,0 +1,31 @@
+{#
+/**
+ * @file
+ * Default theme implementation to present a list of available bundles.
+ *
+ * Available variables:
+ *   - create_bundle_url: The url to the bundle creation page.
+ *   - bundle_type_label: The lowercase label of the bundle entity type.
+ *   - bundles: A list of bundles, each with the following properties:
+ *     - add_link: link to create an entity of this bundle.
+ *     - description: Bundle description.
+ *
+ * @see template_preprocess_entity_add_list()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if bundles is not empty %}
+  <dl>
+    {% for bundle in bundles %}
+      <dt>{{ bundle.add_link }}</dt>
+      <dd>{{ bundle.description }}</dd>
+    {% endfor %}
+  </dl>
+{% else %}
+  <p>
+    {% trans %}
+      Go to the <a href="{{ create_bundle_url }}">{{ bundle_type_label }} creation page</a> to add a new {{ bundle_type_label }}.
+    {% endtrans %}
+  </p>
+{% endif %}
diff --git a/tests/Functional/CreateUITest.php b/tests/Functional/CreateUITest.php
new file mode 100644
index 0000000000000000000000000000000000000000..dbf467ccf136c0430684a3137378a22793414f0d
--- /dev/null
+++ b/tests/Functional/CreateUITest.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\entity\Functional\CreateUITest.
+ */
+
+namespace Drupal\Tests\entity\Functional;
+
+use Drupal\entity_module_test\Entity\EnhancedEntity;
+use Drupal\entity_module_test\Entity\EnhancedEntityBundle;
+use Drupal\simpletest\BrowserTestBase;
+
+/**
+ * Tests the entity creation UI provided by EntityCreateController.
+ *
+ * @group entity
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class CreateUITest extends BrowserTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_module_test', 'user', 'entity'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    EnhancedEntityBundle::create([
+      'id' => 'first',
+      'label' => 'First',
+      'description' => 'The first bundle',
+    ])->save();
+    $account = $this->drupalCreateUser(['administer entity_test_enhanced']);
+    $this->drupalLogin($account);
+  }
+
+  /**
+   * Tests the add page.
+   */
+  public function testAddPage() {
+    // This test revealed that if the first bundle gets created in testAddPage
+    // before drupalGet(), the built cache won't get reset when the second
+    // bundle is created. This is a BrowserTestBase specific bug.
+    // @todo Remove comment when the bug is fixed.
+
+    // When only one bundle exists, the add page should redirect to the form.
+    $this->drupalGet('/entity_test_enhanced/add');
+    $this->assertSession()->addressEquals('/entity_test_enhanced/add/first');
+
+    EnhancedEntityBundle::create([
+      'id' => 'second',
+      'label' => 'Second',
+      'description' => 'The <b>second</b> bundle',
+    ])->save();
+    $this->drupalGet('/entity_test_enhanced/add');
+    $assert = $this->assertSession();
+    $assert->addressEquals('/entity_test_enhanced/add');
+    $assert->statusCodeEquals(200);
+    $assert->elementTextContains('css', '.page-title', 'Add entity test with enhancements');
+    // Confirm the presence of unescaped descriptions.
+    $assert->responseContains('The first bundle');
+    $assert->responseContains('The <b>second</b> bundle');
+    // Validate the links.
+    $link = $this->getSession()->getPage()->findLink('First');
+    $this->assertEquals('/entity_test_enhanced/add/first', $link->getAttribute('href'));
+    $link = $this->getSession()->getPage()->findLink('Second');
+    $this->assertEquals('/entity_test_enhanced/add/second', $link->getAttribute('href'));
+  }
+
+  /**
+   * Tests the add form.
+   */
+  public function testAddForm() {
+    $this->drupalGet('/entity_test_enhanced/add/first');
+    $assert = $this->assertSession();
+    $assert->elementTextContains('css', '.page-title', 'Add entity test with enhancements');
+    $assert->elementExists('css', 'form.entity-test-enhanced-first-add-form');
+
+    // In case of multiple bundles, the current one is a part of the page title.
+    EnhancedEntityBundle::create([
+      'id' => 'second',
+      'label' => 'Second',
+      'description' => 'The <b>second</b> bundle',
+    ])->save();
+    $this->drupalGet('/entity_test_enhanced/add/first');
+    $this->assertSession()->elementTextContains('css', '.page-title', 'Add First');
+  }
+
+}
diff --git a/tests/Kernel/EntityRevisionLogTraitTest.php b/tests/Kernel/EntityRevisionLogTraitTest.php
index d2c7a87adabbabd5d7098629c6eb605bc12575b1..a0b3b3834c0772b1adc111c36e2954e16e685aee 100644
--- a/tests/Kernel/EntityRevisionLogTraitTest.php
+++ b/tests/Kernel/EntityRevisionLogTraitTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\entity\Kernel;
 
 use Drupal\entity_module_test\Entity\EnhancedEntity;
+use Drupal\entity_module_test\Entity\EnhancedEntityBundle;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\user\Entity\User;
 
@@ -29,6 +30,12 @@ class EntityRevisionLogTraitTest extends KernelTestBase {
 
     $this->installEntitySchema('user');
     $this->installSchema('system', 'sequences');
+
+    $bundle = EnhancedEntityBundle::create([
+      'id' => 'default',
+      'label' => 'Default',
+    ]);
+    $bundle->save();
   }
 
   public function testEntityRevisionLog() {
@@ -43,6 +50,7 @@ class EntityRevisionLogTraitTest extends KernelTestBase {
 
     /** @var \Drupal\entity\Revision\EntityRevisionLogInterface $entity */
     $entity = EnhancedEntity::create([
+      'type' => 'default',
       'revision_user' => $user->id(),
       'revision_created' => 1447941735,
       'revision_log_message' => 'Test message',
diff --git a/tests/Kernel/RevisionBasicUITest.php b/tests/Kernel/RevisionBasicUITest.php
index e19fdefae5f3f86b8047cc58cb6c0b20ecff8028..b716c66b51182e95fec0e62e9407f423f7dc2ae3 100644
--- a/tests/Kernel/RevisionBasicUITest.php
+++ b/tests/Kernel/RevisionBasicUITest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\entity\Kernel;
 
 use Drupal\entity_module_test\Entity\EnhancedEntity;
+use Drupal\entity_module_test\Entity\EnhancedEntityBundle;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\user\Entity\Role;
 use Drupal\user\Entity\User;
@@ -33,12 +34,19 @@ class RevisionBasicUITest extends KernelTestBase {
     $this->installEntitySchema('entity_test_enhanced');
     $this->installSchema('system', 'router');
 
+    $bundle = EnhancedEntityBundle::create([
+      'id' => 'default',
+      'label' => 'Default',
+    ]);
+    $bundle->save();
+
     \Drupal::service('router.builder')->rebuild();
   }
 
   public function testRevisionView() {
     $entity = EnhancedEntity::create([
       'name' => 'rev 1',
+      'type' => 'default',
     ]);
     $entity->save();
 
diff --git a/tests/modules/entity_module_test/config/schema/entity_module_test.schema.yml b/tests/modules/entity_module_test/config/schema/entity_module_test.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c20ef335f0c3530f2704266f24cb8c123b2e1b8b
--- /dev/null
+++ b/tests/modules/entity_module_test/config/schema/entity_module_test.schema.yml
@@ -0,0 +1,13 @@
+entity_module_test.entity_test_enhanced_bundle.*:
+  type: config_entity
+  label: 'Entity test with enhancments - Bundle'
+  mapping:
+    id:
+      type: string
+      label: 'Type'
+    label:
+      type: label
+      label: 'Label'
+    description:
+      type: text
+      label: 'Description'
diff --git a/tests/modules/entity_module_test/entity_module_test.permissions.yml b/tests/modules/entity_module_test/entity_module_test.permissions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b41f84b6331b2323beb46675a974cf1ee8f9790b
--- /dev/null
+++ b/tests/modules/entity_module_test/entity_module_test.permissions.yml
@@ -0,0 +1,3 @@
+'administer entity_test_enhanced':
+  title: 'Administer entity_test_enhanced'
+  'restrict access': TRUE
diff --git a/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php b/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php
index 9cefdace06b340505575e0b7b63c857b49a607be..765c43ea017daa4f06e2f988fe74ccda00952ab3 100644
--- a/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php
+++ b/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php
@@ -21,8 +21,14 @@ use Drupal\entity\Revision\EntityRevisionLogTrait;
  *   label = @Translation("Entity test with enhancements"),
  *   handlers = {
  *     "storage" = "\Drupal\Core\Entity\Sql\SqlContentEntityStorage",
+ *     "form" = {
+ *       "add" = "\Drupal\Core\Entity\ContentEntityForm",
+ *       "edit" = "\Drupal\Core\Entity\ContentEntityForm",
+ *       "delete" = "\Drupal\Core\Entity\EntityDeleteForm",
+ *     },
  *     "route_provider" = {
  *       "revision" = "\Drupal\entity\Routing\RevisionRouteProvider",
+ *       "create" = "\Drupal\entity\Routing\CreateHtmlRouteProvider",
  *     },
  *   },
  *   base_table = "entity_test_enhanced",
@@ -34,12 +40,16 @@ use Drupal\entity\Revision\EntityRevisionLogTrait;
  *   admin_permission = "administer entity_test_enhanced",
  *   entity_keys = {
  *     "id" = "id",
+ *     "bundle" = "type",
  *     "revision" = "vid",
  *     "langcode" = "langcode",
  *   },
  *   links = {
+ *     "add-page" = "/entity_test_enhanced/add",
+ *     "add-form" = "/entity_test_enhanced/add/{type}",
  *     "revision" = "/entity_test_enhanced/{entity_test_enhanced}/revisions/{entity_test_enhanced_revision}/view",
- *   }
+ *   },
+ *   bundle_entity_type = "entity_test_enhanced_bundle"
  * )
  */
 class EnhancedEntity extends ContentEntityBase {
diff --git a/tests/modules/entity_module_test/src/Entity/EnhancedEntityBundle.php b/tests/modules/entity_module_test/src/Entity/EnhancedEntityBundle.php
new file mode 100644
index 0000000000000000000000000000000000000000..afe49a7d4b231d4072d6751f7ba8f067c4c6e179
--- /dev/null
+++ b/tests/modules/entity_module_test/src/Entity/EnhancedEntityBundle.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity_module_test\Entity\EnhancedEntityBundle.
+ */
+
+namespace Drupal\entity_module_test\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
+use Drupal\entity\Entity\EntityDescriptionInterface;
+
+/**
+ * Provides bundles for the test entity.
+ *
+ * @ConfigEntityType(
+ *   id = "entity_test_enhanced_bundle",
+ *   label = @Translation("Entity test with enhancments - Bundle"),
+ *   handlers = {
+ *     "route_provider" = {
+ *       "create" = "\Drupal\entity\Routing\CreateHtmlRouteProvider",
+ *     },
+ *   },
+ *   admin_permission = "administer entity_test_enhanced",
+ *   config_prefix = "entity_test_enhanced_bundle",
+ *   bundle_of = "entity_test_enhanced",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label"
+ *   },
+ *   config_export = {
+ *     "id",
+ *     "label",
+ *     "description"
+ *   },
+ *   links = {
+ *     "add-form" = "/entity_test_enhanced_bundle/add",
+ *   },
+ * )
+ */
+class EnhancedEntityBundle extends ConfigEntityBundleBase implements EntityDescriptionInterface {
+
+  /**
+   * The bundle ID.
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * The bundle label.
+   *
+   * @var string
+   */
+  protected $label;
+
+  /**
+   * The bundle description.
+   *
+   * @var string
+   */
+  protected $description;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return $this->description;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDescription($description) {
+    $this->description = $description;
+    return $this;
+  }
+
+}