-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #370 from creativedotdesign/dev
Dev
- Loading branch information
Showing
11 changed files
with
446 additions
and
386 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import 'cypress-axe'; | ||
import resolveConfig from 'tailwindcss/resolveConfig'; | ||
import tailwindConfig from '../../tailwind.config.ts'; | ||
|
||
const fullConfig = resolveConfig(tailwindConfig); | ||
const screens = fullConfig.theme.screens; | ||
|
||
let allViolations = []; // Holds violations from all pages | ||
|
||
before(() => { | ||
allViolations = []; // Reset before the test suite runs | ||
}); | ||
|
||
import { screenshotViolations, cypressLog, terminalLog } from '../support/helpers'; | ||
|
||
export const createAccessibilityCallback = (pageName, breakpointName) => { | ||
cy.task('log', `Running accessibility checks for ${pageName} at ${breakpointName} breakpoint`); | ||
|
||
return (violations) => { | ||
cypressLog(violations); | ||
terminalLog(violations); | ||
|
||
if (Cypress.config('enableScreenshots')) { | ||
screenshotViolations(violations, pageName, breakpointName); | ||
} | ||
|
||
allViolations.push(...violations); | ||
}; | ||
}; | ||
|
||
const viewportSizes = [ | ||
{ | ||
name: 'Mobile', | ||
width: 320, | ||
height: 812, | ||
}, | ||
{ | ||
name: 'Tablet', | ||
width: parseInt(screens.md, 10), | ||
height: 1024, | ||
}, | ||
{ | ||
name: 'Desktop', | ||
width: parseInt(screens.lg, 10), | ||
height: 660, | ||
}, | ||
]; | ||
|
||
describe('Accessibility Tests', () => { | ||
it('should be accessible', () => { | ||
cy.task('sitemapLocations').then((pages) => { | ||
pages.forEach((page) => { | ||
cy.visit(page); | ||
cy.injectAxe(); | ||
|
||
if (Cypress.config('axeIgnoreContrast')) { | ||
cy.configureAxe({ | ||
rules: [ | ||
{ | ||
id: 'color-contrast', | ||
enabled: false, | ||
}, | ||
], | ||
}); | ||
} | ||
|
||
const url = new URL(page); | ||
const path = url.pathname; | ||
|
||
viewportSizes.forEach((viewport) => { | ||
cy.viewport(viewport.width, viewport.height); | ||
|
||
cy.checkA11y( | ||
null, | ||
null, | ||
// { | ||
// runOnly: { | ||
// type: 'tag', | ||
// values: ['wcag2a', 'wcag2aa', 'best-practice', 'section508'], | ||
// }, | ||
// }, | ||
createAccessibilityCallback(path, viewport.name), | ||
true // Do not fail the test when there are accessibility failures | ||
); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
||
after(() => { | ||
// Send the accumulated violations to the custom task | ||
cy.task('processAccessibilityViolations', allViolations); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
export const screenshotViolations = (violations, pageName, breakpointName) => { | ||
cy.task('log', `Screenshotting violations for ${pageName} at ${breakpointName} breakpoint`); | ||
|
||
violations.forEach((violation, index) => { | ||
violation.nodes.forEach((node, nodeIndex) => { | ||
const elementSelector = node.target[0]; | ||
|
||
if (Cypress.$(elementSelector).length) { | ||
cy.get(elementSelector).then(($el) => { | ||
// Check selector is not :root | ||
if ($el.is(':root')) { | ||
return; | ||
} | ||
|
||
// Scroll to the element | ||
cy.get($el).scrollIntoView(); | ||
|
||
$el.addClass('highlight-violation'); | ||
|
||
if (pageName === '/') { | ||
pageName = 'home'; | ||
} | ||
|
||
// Remove leading slash | ||
if (pageName.charAt(0) === '/') { | ||
pageName = pageName.substr(1); | ||
} | ||
|
||
// Remove trailing slash | ||
if (pageName.charAt(pageName.length - 1) === '/') { | ||
pageName = pageName.substr(0, pageName.length - 1); | ||
} | ||
|
||
// convert the pageName to a valid filename | ||
pageName = pageName.replace(/\//g, '-'); | ||
|
||
// Ensure the element is visible | ||
cy.get($el).then(() => { | ||
const screenshotName = `${pageName}-${ | ||
violation.id | ||
}-${breakpointName.toLowerCase()}-${index}-${nodeIndex}`; | ||
cy.screenshot(screenshotName, { | ||
capture: 'viewport', | ||
onAfterScreenshot($el) { | ||
$el.removeClass('highlight-violation'); // Remove highlight class | ||
}, | ||
}); | ||
}); | ||
}); | ||
} else { | ||
cy.log(`No element selector found for violation ${violation.id}. Skipping screenshot.`); | ||
} | ||
}); | ||
}); | ||
}; | ||
|
||
const extractHtmlDetails = (htmlString) => { | ||
// Using regular expressions to find the tag name and text content | ||
const tagNameRegex = /<(\w+)/; | ||
const textContentRegex = />([^<]+)</; | ||
|
||
// Extracting tag name | ||
const tagNameMatch = htmlString.match(tagNameRegex); | ||
const tagName = tagNameMatch ? tagNameMatch[1] : null; | ||
|
||
// Extracting text content | ||
const textContentMatch = htmlString.match(textContentRegex); | ||
let textContent = textContentMatch ? textContentMatch[1].trim() : null; | ||
|
||
// Replacing HTML entities with their character equivalents for text content | ||
if (textContent) { | ||
const htmlEntities = { | ||
'&': '&', | ||
'<': '<', | ||
'>': '>', | ||
'"': '"', | ||
''': "'", | ||
}; | ||
textContent = textContent.replace(/&|<|>|"|'/g, function (match) { | ||
return htmlEntities[match]; | ||
}); | ||
} | ||
|
||
return { tagName, textContent }; | ||
}; | ||
|
||
/** | ||
* Display the accessibility violation table in the terminal | ||
* @param violations array of results returned by Axe | ||
* @link https://github.com/component-driven/cypress-axe#in-your-spec-file | ||
*/ | ||
export const terminalLog = (violations) => { | ||
cy.task('log', 'Violations: ' + violations.length); | ||
|
||
// pluck specific keys to keep the table readable | ||
const violationData = violations.map(({ description, id, impact, nodes }) => ({ | ||
description, | ||
id, | ||
impact, | ||
nodes: nodes.length, | ||
domNodes: nodes.map(({ html }) => { | ||
const { tagName, textContent } = extractHtmlDetails(html); | ||
return `${tagName}: ${textContent}`; | ||
}), | ||
})); | ||
|
||
cy.task('table', violationData); | ||
}; | ||
|
||
const severityIndicators = { | ||
minor: '⚪️', | ||
moderate: '🟡', | ||
serious: '🟠', | ||
critical: '🔴', | ||
}; | ||
|
||
export const cypressLog = (violations) => { | ||
violations.forEach((violation) => { | ||
const targets = violation.nodes.map(({ target }) => target); | ||
const nodes = Cypress.$(targets.join(',')); | ||
const consoleProps = () => violation; | ||
const { help, helpUrl, impact } = violation; | ||
|
||
Cypress.log({ | ||
$el: nodes, | ||
consoleProps, | ||
message: `[${help}](${helpUrl})`, | ||
name: `${severityIndicators[impact]} A11Y`, | ||
}); | ||
|
||
targets.forEach((target) => { | ||
const el = Cypress.$(target.join(',')); | ||
|
||
Cypress.log({ | ||
$el: el, | ||
consoleProps, | ||
message: target, | ||
name: '🔧', | ||
}); | ||
}); | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './accessibility-helper'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.