pa11y-reporter-html-plus
Version:
A Pa11y reporter that generates an HTML report with enhanced support for all runners, including links to relevant help, and the ability to filter accessibility issues by type
150 lines (138 loc) • 3.98 kB
JavaScript
;
/**
* Formats issues for the HTML report based on the Pa11y runner type.
*
* @module formatter
*/
/**
* Enum for known runner with special formatting.
*
* @readonly
* @enum {string}
* @static
* @private
*/
const Runners = Object.freeze({
Axe: 'axe',
HtmlCS: 'htmlcs'
});
/**
* Capitalizes a string and ensures the remaining characters are lowercase.
*
* @param {string} value A string.
* @returns {string} The capitalized string.
* @static
* @private
*/
const capitalize = (value) =>
`${value.charAt(0).toUpperCase()}${value.slice(1).toLowerCase()}`;
/**
* Returns a normalized reporter issue object with data reformatted
* for Pa11y runners with no unique formatter.
*
* @param {object} issue An issue from Pa11y results.
* @returns {object} A reporter issue object.
* @static
* @private
*/
const formatGenericIssue = (issue) => ({
code: issue.code,
context: issue.context,
displayedType: capitalize(issue.type),
message: issue.message,
runner: issue.runner,
selector: issue.selector,
type: issue.type
});
/**
* Returns a normalized reporter issue object with data reformatted
* for Pa11y axe runner results.
*
* @param {object} issue An issue from Pa11y results.
* @returns {object} A reporter issue object.
* @static
* @private
*/
const formatAxeIssue = (issue) => {
const axeResult = {
displayedType: `${capitalize(issue.type)}/${capitalize(
issue.runnerExtras.impact || 'null'
)}`,
helpText: issue.runnerExtras.description,
helpUrl: issue.runnerExtras.helpUrl,
message: issue.runnerExtras.help
};
return { ...formatGenericIssue(issue), ...axeResult };
};
// Data used to extract the relevant WCAG success criteria (sc) section
// and link to the appropriate HTML_CodeSniffer documentation.
const scRegex =
// eslint-disable-next-line unicorn/better-regex -- less readable
/^.*\d{1,2}_\d{1,2}\.(?<sc>\d{1,2}_\d{1,2}_\d{1,2})(?:_[A-Z]{1,4}\d{0,3})?[,.].*$/;
const htmlcsUrlBase =
'https://squizlabs.github.io/HTML_CodeSniffer/Standards/WCAG2/#sc';
/**
* Returns a normalized reporter issue object with data reformatted
* for Pa11y htmlcs runner results.
*
* @param {object} issue An issue from Pa11y results.
* @returns {object} A reporter issue object.
* @static
* @private
*/
const formatHtmlcsIssue = (issue) => {
const htmlcsResult = formatGenericIssue(issue);
const scMatch = scRegex.exec(issue.code);
if (scMatch !== null) {
htmlcsResult.helpUrl = `${htmlcsUrlBase}${scMatch.groups.sc}`;
}
return htmlcsResult;
};
/**
* Returns a normalized reporter issue object with data reformatted
* for based on the Pa11y runner type.
*
* @param {object} issue An issue from Pa11y results.
* @returns {object} A reporter issue object.
* @static
* @public
*/
const formatIssue = (issue) => {
if (issue.runner === Runners.Axe) {
return formatAxeIssue(issue);
}
if (issue.runner === Runners.HtmlCS) {
return formatHtmlcsIssue(issue);
}
return formatGenericIssue(issue);
};
/**
* Gets the summary of the issues, including counts for each type (error,
* warning, notice) and runners reporting issues.
*
* @param {Array} issues Array of issues to count.
* @returns {object} The issues summary object.
* @static
* @public
*/
const getIssuesSummary = (issues = []) => {
// Use singular properties here to match issues types
const counts = { error: 0, notice: 0, warning: 0 };
const runners = new Set();
for (const issue of issues) {
counts[issue.type] += 1;
runners.add(issue.runner);
}
return {
counts: {
errors: counts.error,
notices: counts.notice,
warnings: counts.warning
},
runners: [...runners].sort()
};
};
module.exports = {
formatIssue,
getIssuesSummary
};