From 9e84cb4e522cc851aaccb3d99ceb8da29dae8470 Mon Sep 17 00:00:00 2001
From: Lauri Eskola <lauri.eskola@acquia.com>
Date: Fri, 7 Apr 2023 11:11:22 +0300
Subject: [PATCH] Issue #3293469 by bnjmnm, dmundra, finnsky, Anchal_gupta,
 andypost, lauriii, xjm, nod_, rikki_iki: Automated A11y tests in Nightwatch

---
 .../nightwatch_theme_install_utility.info.yml |   5 +
 ...ghtwatch_theme_install_utility.routing.yml |  13 +++
 .../src/Controller/ThemeInstallController.php |  93 +++++++++++++++
 ...tity_form_display.node.article.default.yml | 106 ++++++++++++++++++
 ....entity_form_display.node.page.default.yml |  76 +++++++++++++
 .../install/field.field.node.article.body.yml |  22 ++++
 .../field.field.node.article.comment.yml      |  32 ++++++
 .../field.field.node.article.field_image.yml  |  37 ++++++
 .../field.field.node.article.field_tags.yml   |  26 +++++
 .../install/field.field.node.page.body.yml    |  22 ++++
 .../install/field.storage.node.comment.yml    |  19 ++++
 .../field.storage.node.field_image.yml        |  31 +++++
 .../install/field.storage.node.field_tags.yml |  19 ++++
 .../install/filter.format.restricted_html.yml |  31 +++++
 .../config/install/node.settings.yml          |   1 +
 .../config/install/node.type.article.yml      |  10 ++
 .../config/install/node.type.page.yml         |  10 ++
 .../config/install/system.theme.yml           |   2 +
 .../install/taxonomy.vocabulary.tags.yml      |   7 ++
 .../install/user.role.administrator.yml       |   8 ++
 .../config/install/user.role.anonymous.yml    |  21 ++++
 .../nightwatch_a11y_testing.info.yml          |  18 +++
 .../Nightwatch/Commands/drupalEnableTheme.js  |  25 +++++
 .../Drupal/Nightwatch/Tests/a11yTestAdmin.js  |  50 +++++++++
 .../Nightwatch/Tests/a11yTestDefault.js       |  67 +++++++++++
 .../Drupal/Nightwatch/nightwatch.conf.js      |   4 +
 core/tests/README.md                          |  10 ++
 27 files changed, 765 insertions(+)
 create mode 100644 core/modules/system/tests/modules/nightwatch_theme_install_utility/nightwatch_theme_install_utility.info.yml
 create mode 100644 core/modules/system/tests/modules/nightwatch_theme_install_utility/nightwatch_theme_install_utility.routing.yml
 create mode 100644 core/modules/system/tests/modules/nightwatch_theme_install_utility/src/Controller/ThemeInstallController.php
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/core.entity_form_display.node.article.default.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/core.entity_form_display.node.page.default.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.body.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.comment.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.field_image.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.field_tags.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/field.field.node.page.body.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.comment.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.field_image.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.field_tags.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/filter.format.restricted_html.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/node.settings.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/node.type.article.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/node.type.page.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/system.theme.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/taxonomy.vocabulary.tags.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/user.role.administrator.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/config/install/user.role.anonymous.yml
 create mode 100644 core/profiles/nightwatch_a11y_testing/nightwatch_a11y_testing.info.yml
 create mode 100644 core/tests/Drupal/Nightwatch/Commands/drupalEnableTheme.js
 create mode 100644 core/tests/Drupal/Nightwatch/Tests/a11yTestAdmin.js
 create mode 100644 core/tests/Drupal/Nightwatch/Tests/a11yTestDefault.js

diff --git a/core/modules/system/tests/modules/nightwatch_theme_install_utility/nightwatch_theme_install_utility.info.yml b/core/modules/system/tests/modules/nightwatch_theme_install_utility/nightwatch_theme_install_utility.info.yml
new file mode 100644
index 000000000000..3be0e1acdd41
--- /dev/null
+++ b/core/modules/system/tests/modules/nightwatch_theme_install_utility/nightwatch_theme_install_utility.info.yml
@@ -0,0 +1,5 @@
+name: 'Nightwatch theme install utility'
+type: module
+description: 'Helper module facilitate theme installation in Nightwatch tests'
+package: Testing
+version: VERSION
diff --git a/core/modules/system/tests/modules/nightwatch_theme_install_utility/nightwatch_theme_install_utility.routing.yml b/core/modules/system/tests/modules/nightwatch_theme_install_utility/nightwatch_theme_install_utility.routing.yml
new file mode 100644
index 000000000000..77e3e0fa0539
--- /dev/null
+++ b/core/modules/system/tests/modules/nightwatch_theme_install_utility/nightwatch_theme_install_utility.routing.yml
@@ -0,0 +1,13 @@
+nightwatch_utility.theme_install_default:
+  path: '/admin/theme/install_default/{theme}'
+  defaults:
+    _controller: '\Drupal\nightwatch_theme_install_utility\Controller\ThemeInstallController::installDefault'
+  requirements:
+    _permission: 'administer themes'
+
+nightwatch_utility.theme_install_admin:
+  path: '/admin/theme/install_admin/{theme}'
+  defaults:
+    _controller: '\Drupal\nightwatch_theme_install_utility\Controller\ThemeInstallController::installAdmin'
+  requirements:
+    _permission: 'administer themes'
diff --git a/core/modules/system/tests/modules/nightwatch_theme_install_utility/src/Controller/ThemeInstallController.php b/core/modules/system/tests/modules/nightwatch_theme_install_utility/src/Controller/ThemeInstallController.php
new file mode 100644
index 000000000000..d81935ad8ae9
--- /dev/null
+++ b/core/modules/system/tests/modules/nightwatch_theme_install_utility/src/Controller/ThemeInstallController.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace Drupal\nightwatch_theme_install_utility\Controller;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Extension\ThemeInstallerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides an easier way for Nightwatch tests to install themes.
+ */
+class ThemeInstallController extends ControllerBase {
+
+  /**
+   * The theme installer service.
+   *
+   * @var \Drupal\Core\Extension\ThemeInstallerInterface
+   */
+  protected $themeInstaller;
+
+  /**
+   * Constructs a new ThemeInstallController.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   * @param \Drupal\Core\Extension\ThemeInstallerInterface $theme_installer
+   *   The theme installer.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, ThemeInstallerInterface $theme_installer) {
+    $this->configFactory = $config_factory;
+    $this->themeInstaller = $theme_installer;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('theme_installer')
+    );
+  }
+
+  /**
+   * Install a theme as default.
+   *
+   * @param string $theme
+   *   The theme to install as the default theme.
+   *
+   * @return array
+   *   A render array confirming installation.
+   */
+  public function installDefault(string $theme) {
+    return $this->installTheme($theme, 'default');
+  }
+
+  /**
+   * Install a theme as the admin theme.
+   * @param string $theme
+   *   The theme to install as the admin theme.
+   *
+   * @return array
+   *   A render array confirming installation.
+   */
+  public function installAdmin($theme) {
+    return $this->installTheme($theme, 'admin');
+  }
+
+  /**
+   * Installs a theme.
+   *
+   * @param string $theme
+   *   The theme to install.
+   * @param string $default_or_admin
+   *   Which type of theme to install, can be `default` or `admin`.
+   *
+   * @return array
+   *   A render array confirming installation.
+   */
+  private function installTheme($theme, $default_or_admin): array {
+    assert(in_array($default_or_admin, ['default', 'admin']), 'The $default_or_admin parameter must be `default` or `admin`');
+    $config = $this->configFactory->getEditable('system.theme');
+    $this->themeInstaller->install([$theme]);
+    $config->set($default_or_admin, $theme)->save();
+    return [
+      '#type' => 'container',
+      '#attributes' => ['id' => 'theme-installed'],
+      '#markup' => "Installed $theme as $default_or_admin theme",
+    ];
+  }
+
+}
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/core.entity_form_display.node.article.default.yml b/core/profiles/nightwatch_a11y_testing/config/install/core.entity_form_display.node.article.default.yml
new file mode 100644
index 000000000000..a0333f736a43
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/core.entity_form_display.node.article.default.yml
@@ -0,0 +1,106 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.field.node.article.body
+    - field.field.node.article.comment
+    - field.field.node.article.field_image
+    - field.field.node.article.field_tags
+    - image.style.thumbnail
+    - node.type.article
+  module:
+    - comment
+    - image
+    - path
+    - text
+id: node.article.default
+targetEntityType: node
+bundle: article
+mode: default
+content:
+  body:
+    type: text_textarea_with_summary
+    weight: 1
+    region: content
+    settings:
+      rows: 9
+      summary_rows: 3
+      placeholder: ''
+      show_summary: false
+    third_party_settings: {  }
+  comment:
+    type: comment_default
+    weight: 20
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+  created:
+    type: datetime_timestamp
+    weight: 10
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+  field_image:
+    type: image_image
+    weight: 4
+    region: content
+    settings:
+      progress_indicator: throbber
+      preview_image_style: thumbnail
+    third_party_settings: {  }
+  field_tags:
+    type: entity_reference_autocomplete_tags
+    weight: 3
+    region: content
+    settings:
+      match_operator: CONTAINS
+      match_limit: 10
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+  path:
+    type: path
+    weight: 30
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+  promote:
+    type: boolean_checkbox
+    weight: 15
+    region: content
+    settings:
+      display_label: true
+    third_party_settings: {  }
+  status:
+    type: boolean_checkbox
+    weight: 120
+    region: content
+    settings:
+      display_label: true
+    third_party_settings: {  }
+  sticky:
+    type: boolean_checkbox
+    weight: 16
+    region: content
+    settings:
+      display_label: true
+    third_party_settings: {  }
+  title:
+    type: string_textfield
+    weight: 0
+    region: content
+    settings:
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+  uid:
+    type: entity_reference_autocomplete
+    weight: 5
+    region: content
+    settings:
+      match_operator: CONTAINS
+      match_limit: 10
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+hidden: {  }
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/core.entity_form_display.node.page.default.yml b/core/profiles/nightwatch_a11y_testing/config/install/core.entity_form_display.node.page.default.yml
new file mode 100644
index 000000000000..edb853ed3de0
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/core.entity_form_display.node.page.default.yml
@@ -0,0 +1,76 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.field.node.page.body
+    - node.type.page
+  module:
+    - path
+    - text
+id: node.page.default
+targetEntityType: node
+bundle: page
+mode: default
+content:
+  body:
+    type: text_textarea_with_summary
+    weight: 31
+    region: content
+    settings:
+      rows: 9
+      summary_rows: 3
+      placeholder: ''
+      show_summary: false
+    third_party_settings: {  }
+  created:
+    type: datetime_timestamp
+    weight: 10
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+  path:
+    type: path
+    weight: 30
+    region: content
+    settings: {  }
+    third_party_settings: {  }
+  promote:
+    type: boolean_checkbox
+    weight: 15
+    region: content
+    settings:
+      display_label: true
+    third_party_settings: {  }
+  status:
+    type: boolean_checkbox
+    weight: 120
+    region: content
+    settings:
+      display_label: true
+    third_party_settings: {  }
+  sticky:
+    type: boolean_checkbox
+    weight: 16
+    region: content
+    settings:
+      display_label: true
+    third_party_settings: {  }
+  title:
+    type: string_textfield
+    weight: -5
+    region: content
+    settings:
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+  uid:
+    type: entity_reference_autocomplete
+    weight: 5
+    region: content
+    settings:
+      match_operator: CONTAINS
+      match_limit: 10
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+hidden: {  }
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.body.yml b/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.body.yml
new file mode 100644
index 000000000000..b36fbd584493
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.body.yml
@@ -0,0 +1,22 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.node.body
+    - node.type.article
+  module:
+    - text
+id: node.article.body
+field_name: body
+entity_type: node
+bundle: article
+label: Body
+description: ''
+required: false
+translatable: true
+default_value: {  }
+default_value_callback: ''
+settings:
+  display_summary: true
+  required_summary: false
+field_type: text_with_summary
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.comment.yml b/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.comment.yml
new file mode 100644
index 000000000000..cf3b12af98f5
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.comment.yml
@@ -0,0 +1,32 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.node.comment
+    - node.type.article
+  module:
+    - comment
+id: node.article.comment
+field_name: comment
+entity_type: node
+bundle: article
+label: Comments
+description: ''
+required: false
+translatable: true
+default_value:
+  -
+    status: 2
+    cid: 0
+    last_comment_timestamp: 0
+    last_comment_name: null
+    last_comment_uid: 0
+    comment_count: 0
+default_value_callback: ''
+settings:
+  default_mode: 1
+  per_page: 50
+  anonymous: 0
+  form_location: true
+  preview: 1
+field_type: comment
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.field_image.yml b/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.field_image.yml
new file mode 100644
index 000000000000..ab7b9940a74a
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.field_image.yml
@@ -0,0 +1,37 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.node.field_image
+    - node.type.article
+  module:
+    - image
+id: node.article.field_image
+field_name: field_image
+entity_type: node
+bundle: article
+label: Image
+description: ''
+required: false
+translatable: true
+default_value: {  }
+default_value_callback: ''
+settings:
+  handler: 'default:file'
+  handler_settings: {  }
+  file_directory: '[date:custom:Y]-[date:custom:m]'
+  file_extensions: 'png gif jpg jpeg'
+  max_filesize: ''
+  max_resolution: ''
+  min_resolution: ''
+  alt_field: true
+  alt_field_required: true
+  title_field: false
+  title_field_required: false
+  default_image:
+    uuid: null
+    alt: ''
+    title: ''
+    width: null
+    height: null
+field_type: image
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.field_tags.yml b/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.field_tags.yml
new file mode 100644
index 000000000000..1b9c4cc4ee1e
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.article.field_tags.yml
@@ -0,0 +1,26 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.node.field_tags
+    - node.type.article
+    - taxonomy.vocabulary.tags
+id: node.article.field_tags
+field_name: field_tags
+entity_type: node
+bundle: article
+label: Tags
+description: 'Enter a comma-separated list. For example: Amsterdam, Mexico City, "Cleveland, Ohio"'
+required: false
+translatable: true
+default_value: {  }
+default_value_callback: ''
+settings:
+  handler: 'default:taxonomy_term'
+  handler_settings:
+    target_bundles:
+      tags: tags
+    sort:
+      field: _none
+    auto_create: true
+field_type: entity_reference
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.page.body.yml b/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.page.body.yml
new file mode 100644
index 000000000000..4ff17d0e711a
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/field.field.node.page.body.yml
@@ -0,0 +1,22 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.node.body
+    - node.type.page
+  module:
+    - text
+id: node.page.body
+field_name: body
+entity_type: node
+bundle: page
+label: Body
+description: ''
+required: false
+translatable: true
+default_value: {  }
+default_value_callback: ''
+settings:
+  display_summary: true
+  required_summary: false
+field_type: text_with_summary
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.comment.yml b/core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.comment.yml
new file mode 100644
index 000000000000..c5eee2c2841d
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.comment.yml
@@ -0,0 +1,19 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - comment
+    - node
+id: node.comment
+field_name: comment
+entity_type: node
+type: comment
+settings:
+  comment_type: comment
+module: comment
+locked: false
+cardinality: 1
+translatable: true
+indexes: {  }
+persist_with_no_fields: false
+custom_storage: false
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.field_image.yml b/core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.field_image.yml
new file mode 100644
index 000000000000..a6964d3b0aab
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.field_image.yml
@@ -0,0 +1,31 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - file
+    - image
+    - node
+id: node.field_image
+field_name: field_image
+entity_type: node
+type: image
+settings:
+  target_type: file
+  display_field: false
+  display_default: false
+  uri_scheme: public
+  default_image:
+    uuid: null
+    alt: ''
+    title: ''
+    width: null
+    height: null
+module: image
+locked: false
+cardinality: 1
+translatable: true
+indexes:
+  target_id:
+    - target_id
+persist_with_no_fields: false
+custom_storage: false
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.field_tags.yml b/core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.field_tags.yml
new file mode 100644
index 000000000000..73f821f2c0de
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/field.storage.node.field_tags.yml
@@ -0,0 +1,19 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - node
+    - taxonomy
+id: node.field_tags
+field_name: field_tags
+entity_type: node
+type: entity_reference
+settings:
+  target_type: taxonomy_term
+module: core
+locked: false
+cardinality: -1
+translatable: true
+indexes: {  }
+persist_with_no_fields: false
+custom_storage: false
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/filter.format.restricted_html.yml b/core/profiles/nightwatch_a11y_testing/config/install/filter.format.restricted_html.yml
new file mode 100644
index 000000000000..6b51f37035d1
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/filter.format.restricted_html.yml
@@ -0,0 +1,31 @@
+langcode: en
+status: true
+dependencies: {  }
+name: 'Restricted HTML'
+format: restricted_html
+weight: 1
+roles:
+  - anonymous
+filters:
+  filter_html:
+    id: filter_html
+    provider: filter
+    status: true
+    weight: -10
+    settings:
+      allowed_html: '<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>'
+      filter_html_help: true
+      filter_html_nofollow: false
+  filter_autop:
+    id: filter_autop
+    provider: filter
+    status: true
+    weight: 0
+    settings: {  }
+  filter_url:
+    id: filter_url
+    provider: filter
+    status: true
+    weight: 0
+    settings:
+      filter_url_length: 72
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/node.settings.yml b/core/profiles/nightwatch_a11y_testing/config/install/node.settings.yml
new file mode 100644
index 000000000000..6cb95cbc42a2
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/node.settings.yml
@@ -0,0 +1 @@
+use_admin_theme: true
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/node.type.article.yml b/core/profiles/nightwatch_a11y_testing/config/install/node.type.article.yml
new file mode 100644
index 000000000000..1fd439ce71bf
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/node.type.article.yml
@@ -0,0 +1,10 @@
+langcode: en
+status: true
+dependencies: {  }
+name: Article
+type: article
+description: 'Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.'
+help: ''
+new_revision: true
+preview_mode: 1
+display_submitted: true
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/node.type.page.yml b/core/profiles/nightwatch_a11y_testing/config/install/node.type.page.yml
new file mode 100644
index 000000000000..57dcc0c9926f
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/node.type.page.yml
@@ -0,0 +1,10 @@
+langcode: en
+status: true
+dependencies: {  }
+name: 'Basic page'
+type: page
+description: 'Use <em>basic pages</em> for your static content, such as an ''About us'' page.'
+help: ''
+new_revision: true
+preview_mode: 1
+display_submitted: false
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/system.theme.yml b/core/profiles/nightwatch_a11y_testing/config/install/system.theme.yml
new file mode 100644
index 000000000000..24b0bae04ee6
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/system.theme.yml
@@ -0,0 +1,2 @@
+admin: claro
+default: olivero
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/taxonomy.vocabulary.tags.yml b/core/profiles/nightwatch_a11y_testing/config/install/taxonomy.vocabulary.tags.yml
new file mode 100644
index 000000000000..4c754e86c715
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/taxonomy.vocabulary.tags.yml
@@ -0,0 +1,7 @@
+langcode: en
+status: true
+dependencies: {  }
+name: Tags
+vid: tags
+description: 'Use tags to group articles on similar topics into categories.'
+weight: 0
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/user.role.administrator.yml b/core/profiles/nightwatch_a11y_testing/config/install/user.role.administrator.yml
new file mode 100644
index 000000000000..ca48a58b4eed
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/user.role.administrator.yml
@@ -0,0 +1,8 @@
+langcode: en
+status: true
+dependencies: {  }
+id: administrator
+label: Administrator
+weight: 3
+is_admin: true
+permissions: {  }
diff --git a/core/profiles/nightwatch_a11y_testing/config/install/user.role.anonymous.yml b/core/profiles/nightwatch_a11y_testing/config/install/user.role.anonymous.yml
new file mode 100644
index 000000000000..5674329ecfb2
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/config/install/user.role.anonymous.yml
@@ -0,0 +1,21 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - filter.format.restricted_html
+  module:
+    - comment
+    - contact
+    - filter
+    - search
+    - system
+id: anonymous
+label: 'Anonymous user'
+weight: 0
+is_admin: false
+permissions:
+  - 'access comments'
+  - 'access content'
+  - 'access site-wide contact form'
+  - 'search content'
+  - 'use text format restricted_html'
diff --git a/core/profiles/nightwatch_a11y_testing/nightwatch_a11y_testing.info.yml b/core/profiles/nightwatch_a11y_testing/nightwatch_a11y_testing.info.yml
new file mode 100644
index 000000000000..0eab6d666ad8
--- /dev/null
+++ b/core/profiles/nightwatch_a11y_testing/nightwatch_a11y_testing.info.yml
@@ -0,0 +1,18 @@
+name: Nightwatch A11y Testing
+type: profile
+description: 'Minimal profile for running A11y Nightwatch tests. Includes absolutely required modules only.'
+version: VERSION
+hidden: true
+install:
+  - block
+  - comment
+  - contact
+  - image
+  - node
+  - path
+  - search
+  - taxonomy
+  - nightwatch_theme_install_utility
+themes:
+  - claro
+  - olivero
diff --git a/core/tests/Drupal/Nightwatch/Commands/drupalEnableTheme.js b/core/tests/Drupal/Nightwatch/Commands/drupalEnableTheme.js
new file mode 100644
index 000000000000..4741fbabe86b
--- /dev/null
+++ b/core/tests/Drupal/Nightwatch/Commands/drupalEnableTheme.js
@@ -0,0 +1,25 @@
+/**
+ * Enable a given theme.
+ *
+ * @param themeMachineName
+ *   The theme machine name to enable
+ * @param adminTheme
+ *   If true, install the theme as the admin theme instead of default.
+ * @return {object}
+ *   The drupalEnableTheme command.
+ */
+exports.command = function drupalEnableTheme(
+  themeMachineName,
+  adminTheme = false,
+) {
+  this.drupalLoginAsAdmin(() => {
+    const path = adminTheme
+      ? '/admin/theme/install_admin/'
+      : '/admin/theme/install_default/';
+    this.drupalRelativeURL(`${path}${themeMachineName}`).waitForElementPresent(
+      '#theme-installed',
+      10000,
+    );
+  });
+  return this;
+};
diff --git a/core/tests/Drupal/Nightwatch/Tests/a11yTestAdmin.js b/core/tests/Drupal/Nightwatch/Tests/a11yTestAdmin.js
new file mode 100644
index 000000000000..df738037fbcd
--- /dev/null
+++ b/core/tests/Drupal/Nightwatch/Tests/a11yTestAdmin.js
@@ -0,0 +1,50 @@
+const argv = require('minimist')(process.argv.slice(2));
+
+const adminTest = {
+  '@tags': ['core', 'a11y', 'a11y:admin'],
+
+  before(browser) {
+    browser.drupalInstall({ installProfile: 'nightwatch_a11y_testing' });
+    // If an admin theme other than Claro is being used for testing, install it.
+    if (argv.adminTheme && argv.adminTheme !== browser.globals.adminTheme) {
+      browser.drupalEnableTheme(argv.adminTheme, true);
+    }
+  },
+  after(browser) {
+    browser.drupalUninstall();
+  },
+};
+const testCases = [
+  { name: 'User Edit', path: '/user/1/edit' },
+  { name: 'Create Article', path: '/user/1/edit' },
+  { name: 'Create Page', path: '/node/add/page?destination=/admin/content' },
+  { name: 'Content Page', path: '/admin/content' },
+  { name: 'Structure Page', path: '/admin/structure' },
+  { name: 'Add content type', path: '/admin/structure/types/add' },
+  { name: 'Add vocabulary', path: '/admin/structure/taxonomy/add' },
+  // @todo remove the skipped rules below in https://drupal.org/i/3318394.
+  {
+    name: 'Structure | Block',
+    path: '/admin/structure/block',
+    options: {
+      rules: {
+        'color-contrast': { enabled: false },
+        'duplicate-id-active': { enabled: false },
+        region: { enabled: false },
+      },
+    },
+  },
+];
+
+testCases.forEach((testCase) => {
+  adminTest[`Accessibility - Admin Theme: ${testCase.name}`] = (browser) => {
+    browser.drupalLoginAsAdmin(() => {
+      browser
+        .drupalRelativeURL(testCase.path)
+        .axeInject()
+        .axeRun('body', testCase.options || {});
+    });
+  };
+});
+
+module.exports = adminTest;
diff --git a/core/tests/Drupal/Nightwatch/Tests/a11yTestDefault.js b/core/tests/Drupal/Nightwatch/Tests/a11yTestDefault.js
new file mode 100644
index 000000000000..213e5ded4971
--- /dev/null
+++ b/core/tests/Drupal/Nightwatch/Tests/a11yTestDefault.js
@@ -0,0 +1,67 @@
+const argv = require('minimist')(process.argv.slice(2));
+
+const a11yThemeTest = {
+  '@tags': ['core', 'a11y', 'a11y:default'],
+
+  before(browser) {
+    browser.drupalInstall({ installProfile: 'nightwatch_a11y_testing' });
+    // If the default theme is set to something other than Olivero, install it.
+    if (
+      argv.defaultTheme &&
+      argv.defaultTheme !== browser.globals.defaultTheme
+    ) {
+      browser.drupalEnableTheme(argv.defaultTheme);
+    }
+  },
+  after(browser) {
+    browser.drupalUninstall();
+  },
+};
+
+const testCases = [
+  {
+    name: 'Homepage',
+    path: '/',
+    // @todo remove the disabled 'region' rule in https://drupal.org/i/3318396.
+    options: {
+      rules: {
+        region: { enabled: false },
+      },
+    },
+  },
+  {
+    name: 'Login',
+    path: '/user/login',
+    // @todo remove the disabled 'region' rule in https://drupal.org/i/3318396.
+    options: {
+      rules: {
+        region: { enabled: false },
+      },
+    },
+  },
+  // @todo remove the heading and duplicate id rules below in
+  //   https://drupal.org/i/3318398.
+  {
+    name: 'Search',
+    path: '/search/node',
+    options: {
+      rules: {
+        'heading-order': { enabled: false },
+        'duplicate-id-aria': { enabled: false },
+      },
+    },
+  },
+];
+
+testCases.forEach((testCase) => {
+  a11yThemeTest[`Accessibility - Default Theme: ${testCase.name}`] = (
+    browser,
+  ) => {
+    browser
+      .drupalRelativeURL(testCase.path)
+      .axeInject()
+      .axeRun('body', testCase.options || {});
+  };
+});
+
+module.exports = a11yThemeTest;
diff --git a/core/tests/Drupal/Nightwatch/nightwatch.conf.js b/core/tests/Drupal/Nightwatch/nightwatch.conf.js
index f4440e7053f9..e09d973503ac 100644
--- a/core/tests/Drupal/Nightwatch/nightwatch.conf.js
+++ b/core/tests/Drupal/Nightwatch/nightwatch.conf.js
@@ -56,6 +56,10 @@ module.exports = {
   },
   test_settings: {
     default: {
+      globals: {
+        defaultTheme: 'olivero',
+        adminTheme: 'claro',
+      },
       selenium_port: process.env.DRUPAL_TEST_WEBDRIVER_PORT,
       selenium_host: process.env.DRUPAL_TEST_WEBDRIVER_HOSTNAME,
       default_path_prefix: process.env.DRUPAL_TEST_WEBDRIVER_PATH_PREFIX || '',
diff --git a/core/tests/README.md b/core/tests/README.md
index 36655e3f123d..5c9613b97a0e 100644
--- a/core/tests/README.md
+++ b/core/tests/README.md
@@ -114,6 +114,16 @@ chromedriver using port 4444, and keep it running:
 * To skip running core tests, run `yarn test:nightwatch --skiptags core`
 * To run a single test, run e.g.
   `yarn test:nightwatch tests/Drupal/Nightwatch/Tests/exampleTest.js`
+* To run a11y tests for both the admin and default themes.
+  `yarn test:nightwatch --tag a11y`
+* To run a11y tests for the admin theme only.
+  `yarn test:nightwatch --tag a11y:admin`
+* To run a11y tests for the default theme only.
+  `yarn test:nightwatch --tag a11y:default`
+* To run an a11y test for a custom theme used as the default theme.
+  `yarn test:nightwatch --tag a11y:default --defaultTheme bartik`
+* To run an a11y test for a custom admin theme.
+  `yarn test:nightwatch --tag a11y:admin --adminTheme seven`
 
 Nightwatch tests, as well as custom commands, assertions and pages, can be
 placed in any folder with the pattern
-- 
GitLab