accessibility-checker
Version:
An automated testing tools for accessibility testing using Puppeteer, Selenium, or Zombie
678 lines (673 loc) • 30.9 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getComplianceHelper = getComplianceHelper;
const fs_1 = require("fs");
const path_1 = require("path");
const ACBrowserManager_js_1 = require("./ACBrowserManager.js");
const ACEngineManager_js_1 = require("./ACEngineManager.js");
const ACConfigManager_js_1 = require("./common/config/ACConfigManager.js");
const ReporterManager_js_1 = require("./common/report/ReporterManager.js");
const BaselineManager_js_1 = require("./common/report/BaselineManager.js");
let loggerCreate = function (type) {
return logger;
};
let logger = {
debug: (...output) => { Config && Config.DEBUG && console.debug(...output); },
info: (...output) => { Config && Config.DEBUG && console.info(...output); },
error: (...output) => { Config && Config.DEBUG && console.error(...output); },
warn: (...output) => { Config && Config.DEBUG && console.warn(...output); },
create: loggerCreate
};
let Config;
let checkPolicy = false;
class MyFS {
writeFileSync(filePath, data) {
let outFile = this.prepFileSync(filePath);
(0, fs_1.writeFileSync)(outFile, data);
}
prepFileSync(filePath) {
let outDir = (0, path_1.resolve)(Config.outputFolder);
let outFile = (0, path_1.join)(outDir, filePath);
if (!(0, fs_1.existsSync)((0, path_1.dirname)(outFile))) {
(0, fs_1.mkdirSync)((0, path_1.dirname)(outFile), { recursive: true });
}
return outFile;
}
log(...output) { Config && Config.DEBUG && console.debug(...output); }
info(...output) { Config && Config.DEBUG && console.info(...output); }
error(...output) { Config && Config.DEBUG && console.error(...output); }
loadBaseline(label) {
let baselineFile = (0, path_1.join)((0, path_1.join)(process.cwd(), Config.baselineFolder), label + ".json");
if (!(0, fs_1.existsSync)(baselineFile))
return null;
if (typeof require !== "undefined") {
return require(baselineFile);
}
else {
return JSON.parse((0, fs_1.readFileSync)(baselineFile).toString());
}
}
getChecker() {
return ACEngineManager_js_1.ACEngineManager.getChecker();
}
}
function initialize() {
return __awaiter(this, void 0, void 0, function* () {
if (Config)
return;
Config = yield ACConfigManager_js_1.ACConfigManager.getConfigUnsupported();
yield ACEngineManager_js_1.ACEngineManager.loadEngineLocal();
let absAPI = new MyFS();
let refactorMap = {};
let rules = ACEngineManager_js_1.ACEngineManager.getRulesSync();
for (const rule of rules) {
if (rule.refactor) {
for (const key in rule.refactor) {
refactorMap[key] = rule;
}
}
}
ReporterManager_js_1.ReporterManager.initialize(Config, absAPI, yield ACEngineManager_js_1.ACEngineManager.getRulesets());
BaselineManager_js_1.BaselineManager.initialize(Config, absAPI, refactorMap);
});
}
(() => __awaiter(void 0, void 0, void 0, function* () {
try {
// If cucumber is the platform...
let module = (yield Promise.resolve(`${"cucumber"}`).then(s => __importStar(require(s))));
let { AfterAll } = require('cucumber');
if (module.default.AfterAll) {
module.default.AfterAll(function (done) {
// const rulePack = `${Config.rulePack}`;
initialize()
.then(() => ReporterManager_js_1.ReporterManager.generateSummaries())
.then(() => ACBrowserManager_js_1.ACBrowserManager.close())
.then(done);
});
}
}
catch (e) {
if (typeof (after) !== "undefined") {
after(function (done) {
if (Config) {
if (this.timeout) {
this.timeout(300000);
}
// const rulePack = `${Config.rulePack}/ace`;
initialize()
.then(() => ReporterManager_js_1.ReporterManager.generateSummaries())
.then(() => ACBrowserManager_js_1.ACBrowserManager.close())
.then(done);
}
else {
done();
}
});
}
else {
process.on('beforeExit', function () {
return __awaiter(this, void 0, void 0, function* () {
if (Config) {
initialize()
.then(() => ReporterManager_js_1.ReporterManager.generateSummaries());
ACBrowserManager_js_1.ACBrowserManager.close();
}
});
});
}
}
}))();
function areValidPolicy(valPolicies, curPol) {
let isValPol = false;
let errorPolicy = "";
for (let i = 0; i < curPol.length; ++i) {
if (valPolicies.indexOf(curPol[i]) === -1) {
errorPolicy += "" + curPol[i] + ",";
}
else {
isValPol = true;
}
}
if (errorPolicy.length > 0) {
errorPolicy = errorPolicy.substr(0, errorPolicy.length - 1);
console.log(`[WARN] InvalidPolicies: Invalid policies "${errorPolicy}". Valid policy ids are: ${valPolicies}`);
}
if (!isValPol) {
const errStr = `[ERROR] ValidPoliciesMissing: No valid policy has been provided. Valid policy ids for the specified archive are: ${valPolicies}`;
console.error(errStr);
throw new Error(errStr);
}
}
function getComplianceHelper(content, label) {
return __awaiter(this, void 0, void 0, function* () {
yield initialize();
Config.DEBUG && console.log("START 'aChecker.getCompliance' function");
if (!content) {
console.error("aChecker: Unable to get compliance of null or undefined object");
//return null;
throw new Error("aChecker: Unable to get compliance of null or undefined object");
}
// Variable Decleration
let URL;
// Since we need to handle multiple variation of possible ways to scan items, we need to handle
// each one differently as each one requires specific actions/setup.
// Handle the following:
// Single node (HTMLElement)
// Multiple node (Array of HTMLElements)
// Local file (String)
// URL (String)
// document
function getParsed(content) {
return __awaiter(this, void 0, void 0, function* () {
if (!content)
return null;
// Handle local file and URL's
if (typeof content === "string") {
let isURLRegex = /^(ftp|http|https):\/\//;
if (isURLRegex.test(content)) {
URL = content;
}
// Since this is a string, we consider this as either URL or local file
// so build an iframe based on this and get the frame doc and then scan this.
return ACBrowserManager_js_1.ACBrowserManager.buildIframeAndGetDoc(content);
}
else if (ACEngineManager_js_1.ACEngineManager.isSelenium(content) || ACEngineManager_js_1.ACEngineManager.isPuppeteer(content) || ACEngineManager_js_1.ACEngineManager.isPlaywright(content) || ACEngineManager_js_1.ACEngineManager.isWebDriverIO(content)) {
}
// Handle Array of nodes
else if (content instanceof Array) {
// TODO: Supporting Array of nodes, possible future enhancenment
}
// Handle single node (HTMLElement)
else if (content.nodeType === 1) {
// In the case this is a node, there is nothing special that needs to be done at this time,
// the engine will be able to handle this. Adding this block here as we may need to add some filtering
// of rules or rule sets for this case depending on if a special ruleset needs to be created or not.
content = content;
}
// handle scanning document
else if (content.nodeType === 9) {
// In the case this is a document element, simply send the document object to the engine for now
// we will need to do some filtering to remove any karma related aspects, which requires to do a
// document clone, and then string the karma scripts that are added and then send this document
// to the engine.
// TODO: Investigate best approach to perform filtering
content = content;
}
return content;
});
}
let parsed = yield getParsed(content);
if (!parsed) {
console.error("Invalid content: " + content);
throw new Error("Invalid content: " + content);
}
yield ACEngineManager_js_1.ACEngineManager.loadEngine(parsed);
// Get the Data when the scan is started
// Start time will be in milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now.
let policies = Config.policies;
let curPol = null;
if (policies) {
curPol = JSON.parse(JSON.stringify(policies));
}
if (ACEngineManager_js_1.ACEngineManager.isSelenium(parsed)) {
Config.DEBUG && console.log("getComplianceHelper:Selenium");
return yield getComplianceHelperSelenium(label, parsed, curPol);
}
else if (ACEngineManager_js_1.ACEngineManager.isPuppeteer(parsed)) {
Config.DEBUG && console.log("ACHelper.ts:getComplianceHelper:Puppeteer");
return yield getComplianceHelperPuppeteer(label, parsed, curPol);
}
else if (ACEngineManager_js_1.ACEngineManager.isPlaywright(parsed)) {
Config.DEBUG && console.log("ACHelper.ts:getComplianceHelper:Playwright");
return yield getComplianceHelperPuppeteer(label, parsed, curPol);
}
else if (ACEngineManager_js_1.ACEngineManager.isWebDriverIO(parsed)) {
Config.DEBUG && console.log("ACHelper.ts:getComplianceHelper:Playwright");
return yield getComplianceHelperWebDriverIO(label, parsed, curPol);
}
else {
Config.DEBUG && console.log("ACHelper.ts:getComplianceHelper:Local");
return yield getComplianceHelperLocal(label, parsed, curPol);
}
});
}
function getComplianceHelperSelenium(label, parsed, curPol) {
return __awaiter(this, void 0, void 0, function* () {
try {
let startScan = Date.now();
// NOTE: Engine should already be loaded
let browser = parsed;
// Selenium
let scriptStr = `let cb = arguments[arguments.length - 1];
try {
const valueToLevel = (reportValue) => {
let reportLevel;
if (reportValue[1] === "PASS") {
reportLevel = "pass";
}
else if ((reportValue[0] === "VIOLATION" || reportValue[0] === "RECOMMENDATION") && reportValue[1] === "MANUAL") {
reportLevel = "manual";
}
else if (reportValue[0] === "VIOLATION") {
if (reportValue[1] === "FAIL") {
reportLevel = "violation";
}
else if (reportValue[1] === "POTENTIAL") {
reportLevel = "potentialviolation";
}
}
else if (reportValue[0] === "RECOMMENDATION") {
if (reportValue[1] === "FAIL") {
reportLevel = "recommendation";
}
else if (reportValue[1] === "POTENTIAL") {
reportLevel = "potentialrecommendation";
}
}
return reportLevel;
}
const getCounts = (engineReport) => {
let counts = {
violation: 0,
potentialviolation: 0,
recommendation: 0,
potentialrecommendation: 0,
manual: 0,
pass: 0
}
for (const issue of engineReport.results) {
++counts[issue.level];
}
return counts;
}
let policies = ${JSON.stringify(Config.policies)};
let checker = new window.ace_ibma.Checker();
let customRulesets = ${JSON.stringify(ACEngineManager_js_1.ACEngineManager.customRulesets)};
customRulesets.forEach((rs) => checker.addRuleset(rs));
setTimeout(function() {
checker.check(document, policies).then(function(report) {
for (const result of report.results) {
delete result.node;
result.level = valueToLevel(result.value)
}
report.summary ||= {};
report.summary.counts ||= getCounts(report);
let reportLevels = ${JSON.stringify((Config.reportLevels || []).concat(Config.failLevels || []).map(lvl => lvl.toString()))};
// Filter out pass results unless they asked for them in reports
// We don't want to mess with baseline functions, but pass results can break the response object
report.results = report.results.filter(result => reportLevels.includes(result.level) || result.level !== "pass");
cb(report);
})
},0)
} catch (e) {
cb(e);
}`;
let manage = browser.manage();
if (manage.timeouts) {
manage.timeouts().setScriptTimeout(60000);
}
else if (manage.setTimeouts) {
manage.setTimeouts({
"script": 60000
});
}
let report = yield browser.executeAsyncScript(scriptStr);
const getPolicies = "return new window.ace_ibma.Checker().rulesetIds;";
if (curPol != null && !checkPolicy) {
checkPolicy = true;
const valPolicies = ACEngineManager_js_1.ACEngineManager.customRulesets.map(rs => rs.id).concat(yield browser.executeScript(getPolicies));
areValidPolicy(valPolicies, curPol);
}
// If there is something to report...
let finalReport;
if (report.results) {
// Add URL to the result object
const url = yield browser.getCurrentUrl();
const title = yield browser.getTitle();
let origReport = JSON.parse(JSON.stringify(report));
if (Config.captureScreenshots && browser.takeScreenshot) {
const image = yield browser.takeScreenshot();
origReport.screenshot = image;
}
finalReport = ReporterManager_js_1.ReporterManager.addEngineReport("Selenium", startScan, url, title, label, origReport);
}
return {
"report": finalReport,
"webdriver": parsed
};
}
catch (err) {
console.error(err);
return Promise.reject(err);
}
;
});
}
function getComplianceHelperWebDriverIO(label, parsed, curPol) {
return __awaiter(this, void 0, void 0, function* () {
try {
const startScan = Date.now();
// NOTE: Engine should already be loaded
const page = parsed;
let report = yield page.executeAsync(({ policies, customRulesets, reportLevels }, done) => {
const valueToLevel = (reportValue) => {
let reportLevel;
if (reportValue[1] === "PASS") {
reportLevel = "pass";
}
else if ((reportValue[0] === "VIOLATION" || reportValue[0] === "RECOMMENDATION") && reportValue[1] === "MANUAL") {
reportLevel = "manual";
}
else if (reportValue[0] === "VIOLATION") {
if (reportValue[1] === "FAIL") {
reportLevel = "violation";
}
else if (reportValue[1] === "POTENTIAL") {
reportLevel = "potentialviolation";
}
}
else if (reportValue[0] === "RECOMMENDATION") {
if (reportValue[1] === "FAIL") {
reportLevel = "recommendation";
}
else if (reportValue[1] === "POTENTIAL") {
reportLevel = "potentialrecommendation";
}
}
return reportLevel;
};
const getCounts = (engineReport) => {
let counts = {
violation: 0,
potentialviolation: 0,
recommendation: 0,
potentialrecommendation: 0,
manual: 0,
pass: 0
};
for (const issue of engineReport.results) {
++counts[issue.level];
}
return counts;
};
let checker = new window.ace_ibma.Checker();
customRulesets.forEach((rs) => checker.addRuleset(rs));
return new Promise((resolve, reject) => {
setTimeout(function () {
checker.check(document, policies).then(function (report) {
var _a;
for (const result of report.results) {
delete result.node;
result.level = valueToLevel(result.value);
}
report.summary || (report.summary = {});
(_a = report.summary).counts || (_a.counts = getCounts(report));
// Filter out pass results unless they asked for them in reports
// We don't want to mess with baseline functions, but pass results can break the response object
report.results = report.results.filter(result => reportLevels.includes(result.level) || result.level !== "pass");
resolve(report);
done(report);
});
}, 0);
});
}, { policies: Config.policies, customRulesets: ACEngineManager_js_1.ACEngineManager.customRulesets, reportLevels: (Config.reportLevels || []).concat(Config.failLevels || []).map(lvl => lvl.toString()) });
if (curPol != null && !checkPolicy) {
const valPolicies = ACEngineManager_js_1.ACEngineManager.customRulesets.map(rs => rs.id).concat(yield page.execute(() => (new window.ace_ibma.Checker().rulesetIds)));
checkPolicy = true;
areValidPolicy(valPolicies, curPol);
}
let finalReport;
// If there is something to report...
if (report.results) {
let url = yield page.execute(() => document.location.href);
let title = yield page.execute(() => document.location.title);
let origReport = JSON.parse(JSON.stringify(report));
if (Config.captureScreenshots) {
let image = yield page.screenshot({
fullPage: true,
encoding: "base64"
});
origReport.screenshot = image;
}
finalReport = ReporterManager_js_1.ReporterManager.addEngineReport("Puppeteer", startScan, url, title, label, origReport);
}
page.aceBusy = false;
return {
"report": finalReport,
"puppeteer": parsed
};
}
catch (err) {
console.error(err);
return Promise.reject(err);
}
;
});
}
function getComplianceHelperPuppeteer(label, parsed, curPol) {
return __awaiter(this, void 0, void 0, function* () {
try {
const startScan = Date.now();
// NOTE: Engine should already be loaded
const page = parsed;
let report = yield page.evaluate(({ policies, customRulesets, reportLevels }) => {
const valueToLevel = (reportValue) => {
let reportLevel;
if (reportValue[1] === "PASS") {
reportLevel = "pass";
}
else if ((reportValue[0] === "VIOLATION" || reportValue[0] === "RECOMMENDATION") && reportValue[1] === "MANUAL") {
reportLevel = "manual";
}
else if (reportValue[0] === "VIOLATION") {
if (reportValue[1] === "FAIL") {
reportLevel = "violation";
}
else if (reportValue[1] === "POTENTIAL") {
reportLevel = "potentialviolation";
}
}
else if (reportValue[0] === "RECOMMENDATION") {
if (reportValue[1] === "FAIL") {
reportLevel = "recommendation";
}
else if (reportValue[1] === "POTENTIAL") {
reportLevel = "potentialrecommendation";
}
}
return reportLevel;
};
const getCounts = (engineReport) => {
let counts = {
violation: 0,
potentialviolation: 0,
recommendation: 0,
potentialrecommendation: 0,
manual: 0,
pass: 0
};
for (const issue of engineReport.results) {
++counts[issue.level];
}
return counts;
};
let checker = new window.ace_ibma.Checker();
customRulesets.forEach((rs) => checker.addRuleset(rs));
return new Promise((resolve, reject) => {
setTimeout(function () {
checker.check(document, policies).then(function (report) {
var _a;
for (const result of report.results) {
delete result.node;
result.level = valueToLevel(result.value);
}
report.summary || (report.summary = {});
(_a = report.summary).counts || (_a.counts = getCounts(report));
// Filter out pass results unless they asked for them in reports
// We don't want to mess with baseline functions, but pass results can break the response object
report.results = report.results.filter(result => reportLevels.includes(result.level) || result.level !== "pass");
resolve(report);
});
}, 0);
});
}, { policies: Config.policies, customRulesets: ACEngineManager_js_1.ACEngineManager.customRulesets, reportLevels: (Config.reportLevels || []).concat(Config.failLevels || []).map(lvl => lvl.toString()) });
if (curPol != null && !checkPolicy) {
const valPolicies = ACEngineManager_js_1.ACEngineManager.customRulesets.map(rs => rs.id).concat(yield page.evaluate("new window.ace_ibma.Checker().rulesetIds"));
checkPolicy = true;
areValidPolicy(valPolicies, curPol);
}
let finalReport;
// If there is something to report...
if (report.results) {
let url = yield page.evaluate("document.location.href");
let title = yield page.evaluate("document.location.title");
let origReport = JSON.parse(JSON.stringify(report));
if (Config.captureScreenshots) {
let image = yield page.screenshot({
fullPage: true,
encoding: "base64"
});
origReport.screenshot = image;
}
finalReport = ReporterManager_js_1.ReporterManager.addEngineReport("Puppeteer", startScan, url, title, label, origReport);
}
page.aceBusy = false;
return {
"report": finalReport,
"puppeteer": parsed
};
}
catch (err) {
console.error(err);
return Promise.reject(err);
}
;
});
}
function getComplianceHelperLocal(label, parsed, curPol) {
return __awaiter(this, void 0, void 0, function* () {
try {
const valueToLevel = (reportValue) => {
let reportLevel;
if (reportValue[1] === "PASS") {
reportLevel = "pass";
}
else if ((reportValue[0] === "VIOLATION" || reportValue[0] === "RECOMMENDATION") && reportValue[1] === "MANUAL") {
reportLevel = "manual";
}
else if (reportValue[0] === "VIOLATION") {
if (reportValue[1] === "FAIL") {
reportLevel = "violation";
}
else if (reportValue[1] === "POTENTIAL") {
reportLevel = "potentialviolation";
}
}
else if (reportValue[0] === "RECOMMENDATION") {
if (reportValue[1] === "FAIL") {
reportLevel = "recommendation";
}
else if (reportValue[1] === "POTENTIAL") {
reportLevel = "potentialrecommendation";
}
}
return reportLevel;
};
const getCounts = (engineReport) => {
let counts = {
violation: 0,
potentialviolation: 0,
recommendation: 0,
potentialrecommendation: 0,
manual: 0,
pass: 0
};
for (const issue of engineReport.results) {
++counts[issue.level];
}
return counts;
};
let startScan = Date.now();
let checker = ACEngineManager_js_1.ACEngineManager.getChecker();
ACEngineManager_js_1.ACEngineManager.customRulesets.forEach((rs) => checker.addGuideline(rs));
let report = yield checker.check(parsed, Config.policies)
.then(function (report) {
var _a;
for (const result of report.results) {
delete result.node;
result.level = valueToLevel(result.value);
}
report.summary || (report.summary = {});
(_a = report.summary).counts || (_a.counts = getCounts(report));
let reportLevels = (Config.reportLevels || []).concat(Config.failLevels || []).map(lvl => lvl.toString());
// Filter out pass results unless they asked for them in reports
// We don't want to mess with baseline functions, but pass results can break the response object
report.results = report.results.filter(result => reportLevels.includes(result.level) || result.level !== "pass");
return report;
});
if (curPol != null && !checkPolicy) {
let valPolicies = checker.getGuidelineIds();
checkPolicy = true;
areValidPolicy(valPolicies, curPol);
}
let finalReport;
// If there is something to report...
if (report.results) {
let url = parsed.location && parsed.location.href;
let origReport = JSON.parse(JSON.stringify(report));
finalReport = ReporterManager_js_1.ReporterManager.addEngineReport("Native", startScan, url, parsed.title, label, origReport);
}
return {
"report": finalReport
};
}
catch (err) {
console.error(err);
return Promise.reject(err);
}
;
});
}
//# sourceMappingURL=ACHelper.js.map
;