diff --git a/cypress/integration/breadcrumbs.spec.ts b/cypress/integration/breadcrumbs.spec.ts index 8fed83bacb..62b9a8ad1d 100644 --- a/cypress/integration/breadcrumbs.spec.ts +++ b/cypress/integration/breadcrumbs.spec.ts @@ -1,23 +1,15 @@ -import { Options } from "cypress-axe"; -import { TEST_ENTITY_PUBLICATION } from "cypress/support"; +import { TEST_ENTITY_PUBLICATION } from 'cypress/support'; +import { testA11y } from 'cypress/support/utils'; describe('Breadcrumbs', () => { it('should pass accessibility tests', () => { // Visit an Item, as those have more breadcrumbs cy.visit('/entities/publication/' + TEST_ENTITY_PUBLICATION); - cy.injectAxe(); // Wait for breadcrumbs to be visible cy.get('ds-breadcrumbs').should('be.visible'); // Analyze for accessibility - // Disable color-contrast checks until #1149 is fixed - cy.checkA11y('ds-breadcrumbs', - { - rules: { - 'color-contrast': { enabled: false } - } - } as Options - ); + testA11y('ds-breadcrumbs'); }); }); diff --git a/cypress/integration/collection-page.spec.ts b/cypress/integration/collection-page.spec.ts index 11babe76d2..a0140d8faf 100644 --- a/cypress/integration/collection-page.spec.ts +++ b/cypress/integration/collection-page.spec.ts @@ -1,23 +1,15 @@ -import { Options } from "cypress-axe"; -import { TEST_COLLECTION } from "cypress/support"; +import { TEST_COLLECTION } from 'cypress/support'; +import { testA11y } from 'cypress/support/utils'; describe('Collection Page', () => { it('should pass accessibility tests', () => { cy.visit('/collections/' + TEST_COLLECTION); - cy.injectAxe(); // tag must be loaded cy.get('ds-collection-page').should('exist'); // Analyze for accessibility issues - // Disable color-contrast checks until it is fixed (see #1202 and #1178) - cy.checkA11y('ds-collection-page', - { - rules: { - 'color-contrast': { enabled: false } - } - } as Options - ); + testA11y('ds-collection-page'); }); }); diff --git a/cypress/integration/collection-statistics.spec.ts b/cypress/integration/collection-statistics.spec.ts index c6f6082101..90b569c824 100644 --- a/cypress/integration/collection-statistics.spec.ts +++ b/cypress/integration/collection-statistics.spec.ts @@ -1,4 +1,5 @@ -import { TEST_COLLECTION } from "cypress/support"; +import { TEST_COLLECTION } from 'cypress/support'; +import { testA11y } from 'cypress/support/utils'; describe('Collection Statistics Page', () => { const COLLECTIONSTATISTICSPAGE = '/statistics/collections/' + TEST_COLLECTION; @@ -21,12 +22,11 @@ describe('Collection Statistics Page', () => { it('should pass accessibility tests', () => { cy.visit(COLLECTIONSTATISTICSPAGE); - cy.injectAxe(); // tag must be loaded cy.get('ds-collection-statistics-page').should('exist'); // Analyze for accessibility issues - cy.checkA11y('ds-collection-statistics-page'); + testA11y('ds-collection-statistics-page'); }); }); diff --git a/cypress/integration/community-list.spec.ts b/cypress/integration/community-list.spec.ts index 1d6f9ff849..a7ba72b74a 100644 --- a/cypress/integration/community-list.spec.ts +++ b/cypress/integration/community-list.spec.ts @@ -1,10 +1,10 @@ -import { Options } from "cypress-axe"; +import { Options } from 'cypress-axe'; +import { testA11y } from 'cypress/support/utils'; describe('Community List Page', () => { it('should pass accessibility tests', () => { cy.visit('/community-list'); - cy.injectAxe(); // tag must be loaded cy.get('ds-community-list-page').should('exist'); @@ -14,7 +14,7 @@ describe('Community List Page', () => { // Analyze for accessibility issues // Disable heading-order checks until it is fixed - cy.checkA11y('ds-community-list-page', + testA11y('ds-community-list-page', { rules: { 'heading-order': { enabled: false } diff --git a/cypress/integration/community-page.spec.ts b/cypress/integration/community-page.spec.ts index 60641120a0..79e21431ad 100644 --- a/cypress/integration/community-page.spec.ts +++ b/cypress/integration/community-page.spec.ts @@ -1,23 +1,15 @@ -import { Options } from "cypress-axe"; -import { TEST_COMMUNITY } from "cypress/support"; +import { TEST_COMMUNITY } from 'cypress/support'; +import { testA11y } from 'cypress/support/utils'; describe('Community Page', () => { it('should pass accessibility tests', () => { cy.visit('/communities/' + TEST_COMMUNITY); - cy.injectAxe(); // tag must be loaded cy.get('ds-community-page').should('exist'); // Analyze for accessibility issues - // Disable color-contrast checks until it is fixed (see #1202 and #1178) - cy.checkA11y('ds-community-page', - { - rules: { - 'color-contrast': { enabled: false } - } - } as Options - ); + testA11y('ds-community-page',); }); }); diff --git a/cypress/integration/community-statistics.spec.ts b/cypress/integration/community-statistics.spec.ts index 555667a96e..cbf1783c0b 100644 --- a/cypress/integration/community-statistics.spec.ts +++ b/cypress/integration/community-statistics.spec.ts @@ -1,4 +1,5 @@ -import { TEST_COMMUNITY } from "cypress/support"; +import { TEST_COMMUNITY } from 'cypress/support'; +import { testA11y } from 'cypress/support/utils'; describe('Community Statistics Page', () => { const COMMUNITYSTATISTICSPAGE = '/statistics/communities/' + TEST_COMMUNITY; @@ -21,12 +22,11 @@ describe('Community Statistics Page', () => { it('should pass accessibility tests', () => { cy.visit(COMMUNITYSTATISTICSPAGE); - cy.injectAxe(); // tag must be loaded cy.get('ds-community-statistics-page').should('exist'); // Analyze for accessibility issues - cy.checkA11y('ds-community-statistics-page'); + testA11y('ds-community-statistics-page'); }); }); diff --git a/cypress/integration/footer.spec.ts b/cypress/integration/footer.spec.ts index 25d1ec8c94..656e9d4701 100644 --- a/cypress/integration/footer.spec.ts +++ b/cypress/integration/footer.spec.ts @@ -1,12 +1,13 @@ +import { testA11y } from 'cypress/support/utils'; + describe('Footer', () => { it('should pass accessibility tests', () => { cy.visit('/'); - cy.injectAxe(); // Footer must first be visible cy.get('ds-footer').should('be.visible'); // Analyze for accessibility - cy.checkA11y('ds-footer'); + testA11y('ds-footer'); }); -}); \ No newline at end of file +}); diff --git a/cypress/integration/header.spec.ts b/cypress/integration/header.spec.ts index 75e1837c36..236208db68 100644 --- a/cypress/integration/header.spec.ts +++ b/cypress/integration/header.spec.ts @@ -1,13 +1,14 @@ +import { testA11y } from 'cypress/support/utils'; + describe('Header', () => { it('should pass accessibility tests', () => { cy.visit('/'); - cy.injectAxe(); // Header must first be visible cy.get('ds-header').should('be.visible'); // Analyze for accessibility - cy.checkA11y({ + testA11y({ include: ['ds-header'], exclude: [ ['#search-navbar-container'], // search in navbar has duplicative ID. Will be fixed in #1174 @@ -15,4 +16,4 @@ describe('Header', () => { ], }); }); -}); \ No newline at end of file +}); diff --git a/cypress/integration/homepage-statistics.spec.ts b/cypress/integration/homepage-statistics.spec.ts index b479d7b300..fe0311f87e 100644 --- a/cypress/integration/homepage-statistics.spec.ts +++ b/cypress/integration/homepage-statistics.spec.ts @@ -1,3 +1,5 @@ +import { testA11y } from 'cypress/support/utils'; + describe('Site Statistics Page', () => { it('should load if you click on "Statistics" from homepage', () => { cy.visit('/'); @@ -7,12 +9,11 @@ describe('Site Statistics Page', () => { it('should pass accessibility tests', () => { cy.visit('/statistics'); - cy.injectAxe(); // tag must be loaded cy.get('ds-site-statistics-page').should('exist'); // Analyze for accessibility issues - cy.checkA11y('ds-site-statistics-page'); + testA11y('ds-site-statistics-page'); }); }); diff --git a/cypress/integration/homepage.spec.ts b/cypress/integration/homepage.spec.ts index 25494d4164..ddde260bc7 100644 --- a/cypress/integration/homepage.spec.ts +++ b/cypress/integration/homepage.spec.ts @@ -1,3 +1,5 @@ +import { testA11y } from 'cypress/support/utils'; + describe('Homepage', () => { beforeEach(() => { // All tests start with visiting homepage @@ -21,12 +23,10 @@ describe('Homepage', () => { }); it('should pass accessibility tests', () => { - cy.injectAxe(); - // Wait for homepage tag to appear cy.get('ds-home-page').should('be.visible'); // Analyze for accessibility issues - cy.checkA11y('ds-home-page'); + testA11y('ds-home-page'); }); }); diff --git a/cypress/integration/item-page.spec.ts b/cypress/integration/item-page.spec.ts index 2009623dce..6a454b678d 100644 --- a/cypress/integration/item-page.spec.ts +++ b/cypress/integration/item-page.spec.ts @@ -1,5 +1,6 @@ -import { Options } from "cypress-axe"; -import { TEST_ENTITY_PUBLICATION } from "cypress/support"; +import { Options } from 'cypress-axe'; +import { TEST_ENTITY_PUBLICATION } from 'cypress/support'; +import { testA11y } from 'cypress/support/utils'; describe('Item Page', () => { const ITEMPAGE = '/items/' + TEST_ENTITY_PUBLICATION; @@ -13,14 +14,13 @@ describe('Item Page', () => { it('should pass accessibility tests', () => { cy.visit(ENTITYPAGE); - cy.injectAxe(); // tag must be loaded cy.get('ds-item-page').should('exist'); // Analyze for accessibility issues // Disable heading-order checks until it is fixed - cy.checkA11y('ds-item-page', + testA11y('ds-item-page', { rules: { 'heading-order': { enabled: false } diff --git a/cypress/integration/item-statistics.spec.ts b/cypress/integration/item-statistics.spec.ts index 1d60a015dc..66ebc228db 100644 --- a/cypress/integration/item-statistics.spec.ts +++ b/cypress/integration/item-statistics.spec.ts @@ -1,4 +1,5 @@ -import { TEST_ENTITY_PUBLICATION } from "cypress/support"; +import { TEST_ENTITY_PUBLICATION } from 'cypress/support'; +import { testA11y } from 'cypress/support/utils'; describe('Item Statistics Page', () => { const ITEMSTATISTICSPAGE = '/statistics/items/' + TEST_ENTITY_PUBLICATION; @@ -27,12 +28,11 @@ describe('Item Statistics Page', () => { it('should pass accessibility tests', () => { cy.visit(ITEMSTATISTICSPAGE); - cy.injectAxe(); // tag must be loaded cy.get('ds-item-statistics-page').should('exist'); // Analyze for accessibility issues - cy.checkA11y('ds-item-statistics-page'); + testA11y('ds-item-statistics-page'); }); }); diff --git a/cypress/plugins/index.ts b/cypress/plugins/index.ts index c106e08011..c6eb874232 100644 --- a/cypress/plugins/index.ts +++ b/cypress/plugins/index.ts @@ -1,5 +1,16 @@ // Plugins enable you to tap into, modify, or extend the internal behavior of Cypress // For more info, visit https://on.cypress.io/plugins-api -/* tslint:disable:no-empty */ -module.exports = (on, config) => { }; -/* tslint:enable:no-empty */ +module.exports = (on, config) => { + // Define "log" and "table" tasks, used for logging accessibility errors during CI + // Borrowed from https://github.com/component-driven/cypress-axe#in-cypress-plugins-file + on('task', { + log(message: string) { + console.log(message); + return null; + }, + table(message: string) { + console.table(message); + return null; + } + }); +}; diff --git a/cypress/support/index.ts b/cypress/support/index.ts index 479212333a..e8b10b9cfb 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -23,4 +23,4 @@ import 'cypress-axe'; // Global constants used in tests export const TEST_COLLECTION = '282164f5-d325-4740-8dd1-fa4d6d3e7200'; export const TEST_COMMUNITY = '0958c910-2037-42a9-81c7-dca80e3892b4'; -export const TEST_ENTITY_PUBLICATION = 'e98b0f27-5c19-49a0-960d-eb6ad5287067'; \ No newline at end of file +export const TEST_ENTITY_PUBLICATION = 'e98b0f27-5c19-49a0-960d-eb6ad5287067'; diff --git a/cypress/support/utils.ts b/cypress/support/utils.ts new file mode 100644 index 0000000000..96575969e8 --- /dev/null +++ b/cypress/support/utils.ts @@ -0,0 +1,44 @@ +import { Result } from 'axe-core'; +import { Options } from 'cypress-axe'; + +// Log violations to terminal/commandline in a table format. +// Uses 'log' and 'table' tasks defined in ../plugins/index.ts +// Borrowed from https://github.com/component-driven/cypress-axe#in-your-spec-file +function terminalLog(violations: Result[]) { + cy.task( + 'log', + `${violations.length} accessibility violation${violations.length === 1 ? '' : 's'} ${violations.length === 1 ? 'was' : 'were'} detected` + ); + // pluck specific keys to keep the table readable + const violationData = violations.map( + ({ id, impact, description, helpUrl, nodes }) => ({ + id, + impact, + description, + helpUrl, + nodes: nodes.length, + html: nodes.map(node => node.html) + }) + ); + + // Print violations as an array, since 'node.html' above often breaks table alignment + cy.task('log', violationData); + // Optionally, uncomment to print as a table + // cy.task('table', violationData); + +} + +// Custom "testA11y()" method which checks accessibility using cypress-axe +// while also ensuring any violations are logged to the terminal (see terminalLog above) +// This method MUST be called after cy.visit(), as cy.injectAxe() must be called after page load +export const testA11y = (context?: any, options?: Options) => { + cy.injectAxe(); + cy.configureAxe({ + rules: [ + // Disable color contrast checks as they are inaccurate / result in a lot of false positives + // See also open issues in axe-core: https://github.com/dequelabs/axe-core/labels/color%20contrast + { id: 'color-contrast', enabled: false }, + ] + }); + cy.checkA11y(context, options, terminalLog); +}; diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json index 39f88f8210..58083003cd 100644 --- a/cypress/tsconfig.json +++ b/cypress/tsconfig.json @@ -6,7 +6,8 @@ "compilerOptions": { "types": [ "cypress", - "cypress-axe" + "cypress-axe", + "node" ] } } \ No newline at end of file