diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml
index bc45d187..08ed50f9 100644
--- a/.github/workflows/acceptance.yml
+++ b/.github/workflows/acceptance.yml
@@ -32,7 +32,7 @@ jobs:
parallel: false
browser: chrome
working-directory: acceptance
- spec: cypress/tests/*.js
+ spec: cypress/tests/main/**/*.js
install: false
start: |
docker compose -f ci.yml --profile prod up
@@ -51,3 +51,48 @@ jobs:
with:
name: cypress-videos-acceptance
path: acceptance/cypress/videos
+
+ acceptance-a11y:
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Install Cypress
+ run: |
+ cd acceptance
+ yarn
+
+ - name: "Cypress: Acceptance tests"
+ uses: cypress-io/github-action@v6
+ env:
+ BABEL_ENV: production
+ CYPRESS_RETRIES: 2
+ CYPRESS_a11y: 1
+ with:
+ parallel: false
+ browser: chrome
+ working-directory: acceptance
+ spec: cypress/tests/a11y/**/*.js
+ install: false
+ start: |
+ docker compose -f ci-a11y.yml --profile prod up
+ wait-on: 'npx wait-on --httpTimeout 20000 http-get://localhost:8080/Plone http://localhost:3000'
+
+ # Upload Cypress screenshots
+ - uses: actions/upload-artifact@v3
+ if: failure()
+ with:
+ name: cypress-screenshots-acceptance
+ path: acceptance/cypress/screenshots
+
+ # Upload Cypress videos
+ - uses: actions/upload-artifact@v3
+ if: failure()
+ with:
+ name: cypress-videos-acceptance
+ path: acceptance/cypress/videos
diff --git a/Makefile b/Makefile
index b1d5f56b..c1540a0f 100644
--- a/Makefile
+++ b/Makefile
@@ -28,11 +28,13 @@ ADDON_NAME='@kitconcept/volto-light-theme'
ADDON_PATH='volto-light-theme'
COMPOSE_FILE=dockerfiles/docker-compose.yml
ACCEPTANCE_COMPOSE=acceptance/docker-compose.yml
+ACCEPTANCE_COMPOSE_A11Y=acceptance/docker-compose-a11y.yml
CMD=CURRENT_DIR=${CURRENT_DIR} ADDON_NAME=${ADDON_NAME} ADDON_PATH=${ADDON_PATH} VOLTO_VERSION=${VOLTO_VERSION} PLONE_VERSION=${PLONE_VERSION} docker compose
DOCKER_COMPOSE=${CMD} -p ${ADDON_PATH} -f ${COMPOSE_FILE}
DEV_COMPOSE=COMPOSE_PROFILES=dev ${DOCKER_COMPOSE}
LIVE_COMPOSE=COMPOSE_PROFILES=dev ${DOCKER_COMPOSE}
ACCEPTANCE=${CMD} -p ${ADDON_PATH}-acceptance -f ${ACCEPTANCE_COMPOSE}
+ACCEPTANCE_A11Y=${CMD} -p ${ADDON_PATH}-acceptance -f ${ACCEPTANCE_COMPOSE_A11Y}
.PHONY: build-backend
build-backend: ## Build
@@ -124,7 +126,7 @@ start-test-acceptance-server-prod: ## Start acceptance server in prod (used by C
.PHONY: test-acceptance
test-acceptance: ## Start Cypress (for use it while developing)
- (cd acceptance && ./node_modules/.bin/cypress open)
+ (cd acceptance && ./node_modules/.bin/cypress open --config specPattern='cypress/tests/main/**/*.{js,jsx,ts,tsx}')
.PHONY: test-acceptance-headless
test-acceptance-headless: ## Run cypress tests in CI
@@ -138,6 +140,18 @@ stop-test-acceptance-server: ## Stop acceptance server (for use it while finishe
status-test-acceptance-server: ## Status of Acceptance Server (for use it while developing)
${ACCEPTANCE} ps
+.PHONY: start-test-acceptance-server-a11y
+start-test-acceptance-server-a11y: ## Start a11y acceptance server (for use it in while developing)
+ ${ACCEPTANCE_A11Y} --profile dev up
+
+.PHONY: stop-test-acceptance-server-a11y
+stop-test-acceptance-server-a11y: ## Stop a11y acceptance server (for use it while finished developing)
+ ${ACCEPTANCE_A11Y} --profile dev down
+
+.PHONY: test-acceptance-a11y
+test-acceptance-a11y: ## Start Cypress (for use it while developing)
+ (cd acceptance && CYPRESS_a11y=1 ./node_modules/.bin/cypress open --config specPattern='cypress/tests/a11y/**/*.{js,jsx,ts,tsx}')
+
.PHONY: debug-frontend
debug-frontend: ## Run bash in the Frontend container (for debug infrastructure purposes)
${DEV_COMPOSE} run --entrypoint bash addon-dev
diff --git a/README.md b/README.md
index de81af13..83245f80 100644
--- a/README.md
+++ b/README.md
@@ -333,6 +333,38 @@ When finished, don't forget to shutdown the backend server.
make stop-test-acceptance-server
```
+### Accessibility Acceptance tests
+
+Run once
+
+```shell
+make install-acceptance
+```
+
+For starting the servers
+
+Run
+
+```shell
+make start-test-acceptance-server-a11y
+```
+
+The frontend is run in dev mode, so development while writing tests is possible.
+
+Run
+
+```shell
+make test-acceptance-a11y
+```
+
+To run Cypress tests afterwards.
+
+When finished, don't forget to shutdown the backend server.
+
+```shell
+make stop-test-acceptance-server-a11y
+```
+
### Release
Run
diff --git a/acceptance/ci-a11y.yml b/acceptance/ci-a11y.yml
new file mode 100644
index 00000000..83ca3a4e
--- /dev/null
+++ b/acceptance/ci-a11y.yml
@@ -0,0 +1,31 @@
+version: "3"
+
+services:
+
+ addon-acceptance:
+ build:
+ context: ../
+ dockerfile: ./dockerfiles/Dockerfile
+ args:
+ ADDON_NAME: "${ADDON_NAME}"
+ ADDON_PATH: "${ADDON_PATH}"
+ VOLTO_VERSION: ${VOLTO_VERSION:-17}
+ environment:
+ RAZZLE_INTERNAL_API_PATH: http://backend-acceptance-a11y:8080/Plone
+ RAZZLE_API_PATH: http://localhost:8080/Plone
+ ports:
+ - 3000:3000
+ depends_on:
+ - backend-acceptance-a11y
+ profiles:
+ - prod
+
+ backend-acceptance-a11y:
+ image: ghcr.io/kitconcept/voltolighttheme:latest
+ environment:
+ CORS_: true
+ ports:
+ - 8080:8080
+ profiles:
+ - dev
+ - prod
diff --git a/acceptance/cypress.config.js b/acceptance/cypress.config.js
index d841d7ab..4c4f133e 100644
--- a/acceptance/cypress.config.js
+++ b/acceptance/cypress.config.js
@@ -4,6 +4,14 @@ module.exports = defineConfig({
viewportWidth: 1280,
e2e: {
baseUrl: 'http://localhost:3000',
- specPattern: 'cypress/tests/*.cy.{js,jsx}',
+ specPattern: 'cypress/tests/**/*.cy.{js,jsx,ts,tsx}',
+ setupNodeEvents(on, config) {
+ on('task', {
+ table(message) {
+ console.table(message);
+ return null;
+ },
+ });
+ },
},
});
diff --git a/acceptance/cypress/support/commands.js b/acceptance/cypress/support/commands.js
index 44a50003..929e9e04 100644
--- a/acceptance/cypress/support/commands.js
+++ b/acceptance/cypress/support/commands.js
@@ -1 +1,23 @@
import '@plone/volto-testing/cypress/support/commands';
+
+// Print cypress-axe violations to the terminal
+function printAccessibilityViolations(violations) {
+ cy.task(
+ 'table',
+ violations.map(({ id, impact, description, nodes }) => ({
+ impact,
+ description: `${description} (${id})`,
+ nodes: nodes.length,
+ })),
+ );
+}
+
+Cypress.Commands.add(
+ 'checkAccessibility',
+ (subject, { skipFailures = false } = {}) => {
+ cy.checkA11y(subject, null, printAccessibilityViolations, skipFailures);
+ },
+ {
+ prevSubject: 'optional',
+ },
+);
diff --git a/acceptance/cypress/support/e2e.js b/acceptance/cypress/support/e2e.js
index 47dfa732..cb54caed 100644
--- a/acceptance/cypress/support/e2e.js
+++ b/acceptance/cypress/support/e2e.js
@@ -1,6 +1,7 @@
import 'cypress-axe';
import 'cypress-file-upload';
import './commands';
+import 'cypress-axe';
import { setup, teardown } from './reset-fixture';
beforeEach(function () {
@@ -9,10 +10,14 @@ beforeEach(function () {
cy.setCookie('confirm_facebook', '1');
cy.setCookie('confirm_youtube', '1');
cy.log('Setting up API fixture');
- setup();
+ if (!Cypress.env('a11y')) {
+ setup();
+ }
});
afterEach(function () {
cy.log('Tearing down API fixture');
- teardown();
+ if (!Cypress.env('a11y')) {
+ teardown();
+ }
});
diff --git a/acceptance/cypress/tests/a11y/accordionBlock.cy.js b/acceptance/cypress/tests/a11y/accordionBlock.cy.js
new file mode 100644
index 00000000..72428fc5
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/accordionBlock.cy.js
@@ -0,0 +1,30 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Accordion Block
+ it('Accordion Block (/block/block-accordion)', () => {
+ cy.navigate('/block/block-accordion');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe({
+ rules: [
+ // the example page intentionally omits the h1
+ {
+ id: 'page-has-heading-one',
+ enabled: false,
+ },
+ {
+ id: 'duplicate-id',
+ enabled: false,
+ },
+ ],
+ });
+ cy.checkAccessibility();
+ });
+});
diff --git a/acceptance/cypress/tests/a11y/basic.cy.js b/acceptance/cypress/tests/a11y/basic.cy.js
new file mode 100644
index 00000000..1da4e782
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/basic.cy.js
@@ -0,0 +1,26 @@
+
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ it('Home page (/)', () => {
+ cy.navigate('/');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe({
+ rules: [
+ // the example home page intentionally omits the h1
+ {
+ id: 'page-has-heading-one',
+ enabled: false,
+ },
+ ],
+ });
+ cy.checkAccessibility();
+ });
+});
diff --git a/acceptance/cypress/tests/a11y/buttonBlock.cy.js b/acceptance/cypress/tests/a11y/buttonBlock.cy.js
new file mode 100644
index 00000000..0f1fd15c
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/buttonBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ //Button
+ it('Button-Block (/block/button-block)', () => {
+ cy.navigate('/block/button-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/eventContenttype.cy.js b/acceptance/cypress/tests/a11y/eventContenttype.cy.js
new file mode 100644
index 00000000..1e285a6c
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/eventContenttype.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ //Event
+ it('Event (/content-types/event)', () => {
+ cy.navigate('/content-types/event');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/fileContenttype.cy.js b/acceptance/cypress/tests/a11y/fileContenttype.cy.js
new file mode 100644
index 00000000..ea68a01e
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/fileContenttype.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ //File
+ it('File (/content-types/file)', () => {
+ cy.navigate('/content-types/file');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/gridBlock.cy.js b/acceptance/cypress/tests/a11y/gridBlock.cy.js
new file mode 100644
index 00000000..463f938e
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/gridBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // grid block
+ it('Grid-Block (/block/grid-block)', () => {
+ cy.navigate('/block/grid-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/gridImageBlock.cy.js b/acceptance/cypress/tests/a11y/gridImageBlock.cy.js
new file mode 100644
index 00000000..a6287788
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/gridImageBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // grid block Image
+ it('Grid-Block Image (/block/grid-block/image)', () => {
+ cy.navigate('/block/grid-block/image');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/gridListingBlock.cy.js b/acceptance/cypress/tests/a11y/gridListingBlock.cy.js
new file mode 100644
index 00000000..8a2f4228
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/gridListingBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // grid block listing
+ it('Grid-Block Listing (/block/grid-block/listing)', () => {
+ cy.navigate('/block/grid-block/listing');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/gridTeaserBlock.cy.js b/acceptance/cypress/tests/a11y/gridTeaserBlock.cy.js
new file mode 100644
index 00000000..06c3a5dd
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/gridTeaserBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // grid Teaser block
+ it('Grid-Block Teaser (/block/grid-block/teaser)', () => {
+ cy.navigate('/block/grid-block/teaser');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/gridTextBlock.cy.js b/acceptance/cypress/tests/a11y/gridTextBlock.cy.js
new file mode 100644
index 00000000..0937e22f
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/gridTextBlock.cy.js
@@ -0,0 +1,33 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // grid tex block
+ it('Grid-Block text (/block/grid-block/text)', () => {
+ cy.navigate('/block/grid-block/text');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe({
+ rules: [
+ // there are copies of slate h2,
+ // which have with the same id
+ {
+ id: 'duplicate-id-active',
+ enabled: false,
+ },
+ // there are missing heading of grid-block
+ // because we are using multiple grid block
+ {
+ id: 'empty-heading',
+ enabled: false,
+ },
+ ],
+ });
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/headingBlock.cy.js b/acceptance/cypress/tests/a11y/headingBlock.cy.js
new file mode 100644
index 00000000..06fa25bb
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/headingBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Heading
+ it('Heading-Block (/block/heading-block)', () => {
+ cy.navigate('/block/heading-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/highlightBlock.cy.js b/acceptance/cypress/tests/a11y/highlightBlock.cy.js
new file mode 100644
index 00000000..cc918d0a
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/highlightBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Highlight Block
+ it('Highlight-Block (/block/highlight-block)', () => {
+ cy.navigate('/block/highlight-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/imageBlock.cy.js b/acceptance/cypress/tests/a11y/imageBlock.cy.js
new file mode 100644
index 00000000..e0909de8
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/imageBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Image-Block
+ it('Image-Block (/block/image-block)', () => {
+ cy.navigate('/block/image-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/imageContenttype.cy.js b/acceptance/cypress/tests/a11y/imageContenttype.cy.js
new file mode 100644
index 00000000..e93a10a0
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/imageContenttype.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ //Image
+ it('Image (/content-types/image)', () => {
+ cy.navigate('/content-types/image');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/introductionBlock.cy.js b/acceptance/cypress/tests/a11y/introductionBlock.cy.js
new file mode 100644
index 00000000..8900c121
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/introductionBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Introduction-Block
+ it('Introduction-Block (/block/introduction-block)', () => {
+ cy.navigate('/block/introduction-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/linkContenttype.cy.js b/acceptance/cypress/tests/a11y/linkContenttype.cy.js
new file mode 100644
index 00000000..f8e8a8d7
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/linkContenttype.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ //Link
+ it('link (/content-types/internal-link)', () => {
+ cy.navigate('/content-types/internal-link');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/listingBlock.cy.js b/acceptance/cypress/tests/a11y/listingBlock.cy.js
new file mode 100644
index 00000000..7789452f
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/listingBlock.cy.js
@@ -0,0 +1,31 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Listing-block
+ it('Listing-block (/block/listing-block)', () => {
+ cy.navigate('/block/listing-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe({
+ // Disabling 'image-alt'
+ // semantic-ui-react's Embed doesn't include an alt tag for the placeholder image
+ rules: [
+ {
+ id: 'image-alt',
+ enabled: false,
+ },
+ {
+ id: 'nested-interactive',
+ enabled: false,
+ },
+ ],
+ });
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/mapsBlock.cy.js b/acceptance/cypress/tests/a11y/mapsBlock.cy.js
new file mode 100644
index 00000000..e012f2a7
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/mapsBlock.cy.js
@@ -0,0 +1,27 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Maps block
+ it('Maps Block (/block/maps-block)', () => {
+ cy.navigate('/block/maps-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe({
+ rules: [
+ // there are two copies of slate h3,
+ // which have with the same id
+ {
+ id: 'duplicate-id-active',
+ enabled: false,
+ },
+ ],
+ });
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/newsItemContenttype.cy.js b/acceptance/cypress/tests/a11y/newsItemContenttype.cy.js
new file mode 100644
index 00000000..b69ab382
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/newsItemContenttype.cy.js
@@ -0,0 +1,27 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ //news-item
+ it('news-item (/content-types/news-item)', () => {
+ cy.navigate('/content-types/news-item');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe({
+ rules: [
+ // there are two copies of slate h3,
+ // which have with the same id
+ {
+ id: 'duplicate-id-active',
+ enabled: false,
+ },
+ ],
+ });
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/pageContent.cy.js b/acceptance/cypress/tests/a11y/pageContent.cy.js
new file mode 100644
index 00000000..cb9ef0e1
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/pageContent.cy.js
@@ -0,0 +1,27 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ //Page
+ it('Page (/content-types/page)', () => {
+ cy.navigate('/content-types/page');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe({
+ rules: [
+ // there are two copies of slate h3,
+ // which have with the same id
+ {
+ id: 'duplicate-id-active',
+ enabled: false,
+ },
+ ],
+ });
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/searchBlock.cy.js b/acceptance/cypress/tests/a11y/searchBlock.cy.js
new file mode 100644
index 00000000..b7c09e54
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/searchBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Search-block
+ it('Search-block (/block/search-block)', () => {
+ cy.navigate('/block/search-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/separatorBlock.cy.js b/acceptance/cypress/tests/a11y/separatorBlock.cy.js
new file mode 100644
index 00000000..3f095ec3
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/separatorBlock.cy.js
@@ -0,0 +1,28 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // separator-block
+ it('Separator-block (/block/separator-block)', () => {
+ cy.navigate('/block/separator-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe({
+ rules: [
+ // there are copies of slate h3,
+ // which have with the same id
+ {
+ id: 'duplicate-id-active',
+ enabled: false,
+ },
+
+ ],
+ });
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/tableBlock.cy.js b/acceptance/cypress/tests/a11y/tableBlock.cy.js
new file mode 100644
index 00000000..dc755b09
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/tableBlock.cy.js
@@ -0,0 +1,26 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Table-block
+ it('Table-block (/block/table-block)', () => {
+ cy.navigate('/block/table-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe({
+ rules: [
+ // the example page intentionally omits the h1
+ {
+ id: 'page-has-heading-one',
+ enabled: false,
+ },
+ ],
+ });
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/teaserBlock.cy.js b/acceptance/cypress/tests/a11y/teaserBlock.cy.js
new file mode 100644
index 00000000..dbe4b4db
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/teaserBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Teaser-block
+ it('Teaser-block (/block/teaser-block)', () => {
+ cy.navigate('/block/teaser-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/textBlock.cy.js b/acceptance/cypress/tests/a11y/textBlock.cy.js
new file mode 100644
index 00000000..7a4d6ee7
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/textBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Text-block
+ it('Text-block (/block/text-block)', () => {
+ cy.navigate('/block/text-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/tocBlock.cy.js b/acceptance/cypress/tests/a11y/tocBlock.cy.js
new file mode 100644
index 00000000..bc3bb27d
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/tocBlock.cy.js
@@ -0,0 +1,18 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Table of Contents
+ it('Table of Contents (/block/toc-block)', () => {
+ cy.navigate('/block/toc-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe();
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/a11y/videoBlock.cy.js b/acceptance/cypress/tests/a11y/videoBlock.cy.js
new file mode 100644
index 00000000..80ba07e4
--- /dev/null
+++ b/acceptance/cypress/tests/a11y/videoBlock.cy.js
@@ -0,0 +1,27 @@
+describe('a11y tests', () => {
+ beforeEach(() => {
+ // Cypress starts out with a blank slate for each test
+ // so we must tell it to visit our website with the `cy.visit()` command.
+ // Since we want to visit the same URL at the start of all our tests,
+ // we include it in our beforeEach function so that it runs before each test
+ cy.visit('/');
+ });
+
+ // Video Block
+ it('Video Block (/block/video-block)', () => {
+ cy.navigate('/block/video-block');
+ cy.wait(2000);
+ cy.injectAxe();
+ cy.configureAxe({
+ // Disabling 'image-alt'
+ // semantic-ui-react's Embed doesn't include an alt tag for the placeholder image
+ rules: [
+ {
+ id: 'image-alt',
+ enabled: false,
+ },
+ ],
+ });
+ cy.checkAccessibility();
+ });
+});
\ No newline at end of file
diff --git a/acceptance/cypress/tests/basic.cy.js b/acceptance/cypress/tests/main/basic.cy.js
similarity index 98%
rename from acceptance/cypress/tests/basic.cy.js
rename to acceptance/cypress/tests/main/basic.cy.js
index 98bae1dc..edd532f3 100644
--- a/acceptance/cypress/tests/basic.cy.js
+++ b/acceptance/cypress/tests/main/basic.cy.js
@@ -1,7 +1,7 @@
import {
getSlateEditorAndType,
getSelectedSlateEditor,
-} from '../support/slate';
+} from '../../support/slate';
context('Basic Acceptance Tests', () => {
describe('Text Block Tests', () => {
diff --git a/acceptance/cypress/tests/blocks-map.cy.js b/acceptance/cypress/tests/main/blocks-map.cy.js
similarity index 100%
rename from acceptance/cypress/tests/blocks-map.cy.js
rename to acceptance/cypress/tests/main/blocks-map.cy.js
diff --git a/acceptance/cypress/tests/blocks-table.cy.js b/acceptance/cypress/tests/main/blocks-table.cy.js
similarity index 100%
rename from acceptance/cypress/tests/blocks-table.cy.js
rename to acceptance/cypress/tests/main/blocks-table.cy.js
diff --git a/acceptance/cypress/tests/listing-grid.cy.js b/acceptance/cypress/tests/main/listing-grid.cy.js
similarity index 100%
rename from acceptance/cypress/tests/listing-grid.cy.js
rename to acceptance/cypress/tests/main/listing-grid.cy.js
diff --git a/acceptance/cypress/tests/nav.cy.js b/acceptance/cypress/tests/main/nav.cy.js
similarity index 100%
rename from acceptance/cypress/tests/nav.cy.js
rename to acceptance/cypress/tests/main/nav.cy.js
diff --git a/acceptance/docker-compose-a11y.yml b/acceptance/docker-compose-a11y.yml
new file mode 100644
index 00000000..39c14a34
--- /dev/null
+++ b/acceptance/docker-compose-a11y.yml
@@ -0,0 +1,55 @@
+version: "3"
+
+services:
+
+ addon-dev:
+ build:
+ context: ../
+ dockerfile: ./dockerfiles/Dockerfile.acceptance
+ args:
+ ADDON_NAME: "${ADDON_NAME}"
+ ADDON_PATH: "${ADDON_PATH}"
+ VOLTO_VERSION: ${VOLTO_VERSION:-17}
+ volumes:
+ - ${CURRENT_DIR}:/app/src/addons/${ADDON_PATH}/
+ environment:
+ RAZZLE_INTERNAL_API_PATH: http://backend-acceptance-a11y:8080/Plone
+ RAZZLE_API_PATH: http://localhost:8080/Plone
+ HOST: 0.0.0.0
+ ports:
+ - 3000:3000
+ - 3001:3001
+ tty: true
+ depends_on:
+ - backend-acceptance-a11y
+ profiles:
+ - dev
+
+ addon-acceptance:
+ build:
+ context: ../
+ dockerfile: ./dockerfiles/Dockerfile
+ args:
+ ADDON_NAME: "${ADDON_NAME}"
+ ADDON_PATH: "${ADDON_PATH}"
+ VOLTO_VERSION: ${VOLTO_VERSION:-17}
+ environment:
+ RAZZLE_INTERNAL_API_PATH: http://backend-acceptance-a11y:8080/Plone
+ RAZZLE_API_PATH: http://localhost:8080/Plone
+ HOST: 0.0.0.0
+ ports:
+ - 3000:3000
+ depends_on:
+ - backend-acceptance-a11y
+ profiles:
+ - prod
+
+ backend-acceptance-a11y:
+ image: ghcr.io/kitconcept/voltolighttheme:latest
+ environment:
+ CORS_: true
+ ports:
+ - 8080:8080
+ profiles:
+ - dev
+ - prod
diff --git a/news/300.feature b/news/300.feature
new file mode 100644
index 00000000..b8270f0c
--- /dev/null
+++ b/news/300.feature
@@ -0,0 +1 @@
+Added a11y tests infrastructure @sneridagh
diff --git a/src/components/Blocks/Listing/ImageGallery.jsx b/src/components/Blocks/Listing/ImageGallery.jsx
index ebfa0a22..67922fc8 100644
--- a/src/components/Blocks/Listing/ImageGallery.jsx
+++ b/src/components/Blocks/Listing/ImageGallery.jsx
@@ -22,6 +22,7 @@ const renderLeftNav = (onClick, disabled) => {
className="image-gallery-icon image-gallery-left-nav primary basic"
disabled={disabled}
onClick={onClick}
+ aria-label="Go to previous slide"
>
@@ -33,6 +34,7 @@ const renderRightNav = (onClick, disabled) => {
className="image-gallery-icon image-gallery-right-nav primary basic"
disabled={disabled}
onClick={onClick}
+ aria-label="Go to next slide"
>
diff --git a/src/theme/_footer.scss b/src/theme/_footer.scss
index 2256f016..3aae8dde 100644
--- a/src/theme/_footer.scss
+++ b/src/theme/_footer.scss
@@ -7,7 +7,10 @@
background-color: $lightgrey;
font-size: 18px;
- a.powered-by,
+ a.powered-by {
+ color: $blue-for-grey-contrast;
+ font-size: 14px;
+ }
.footer-branding {
font-size: 14px;
}
@@ -15,6 +18,7 @@
.footer-message {
font-weight: $bold;
a {
+ color: $blue-for-grey-contrast;
font-weight: inherit;
text-decoration: underline;
}
@@ -35,6 +39,9 @@
&:last-of-type {
border: none;
}
+ a {
+ color: $blue-for-grey-contrast;
+ }
}
}