UNPKG

accessibility-checker

Version:

An automated testing tools for accessibility testing using Puppeteer, Selenium, or Zombie

526 lines 30.6 kB
"use strict"; 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()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getComplianceHelper = void 0; var ACBrowserManager_1 = require("./ACBrowserManager"); var ACConfigManager_1 = require("./ACConfigManager"); var ACEngineManager_1 = require("./ACEngineManager"); var ACReportManager_1 = require("./ACReportManager"); var loggerCreate = function (type) { return logger; }; var logger = { debug: function () { var output = []; for (var _i = 0; _i < arguments.length; _i++) { output[_i] = arguments[_i]; } Config && Config.DEBUG && console.debug.apply(console, output); }, info: function () { var output = []; for (var _i = 0; _i < arguments.length; _i++) { output[_i] = arguments[_i]; } Config && Config.DEBUG && console.info.apply(console, output); }, error: function () { var output = []; for (var _i = 0; _i < arguments.length; _i++) { output[_i] = arguments[_i]; } Config && Config.DEBUG && console.error.apply(console, output); }, warn: function () { var output = []; for (var _i = 0; _i < arguments.length; _i++) { output[_i] = arguments[_i]; } Config && Config.DEBUG && console.warn.apply(console, output); }, create: loggerCreate }; var Config; var checkPolicy = false; function initialize() { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (Config) return [2 /*return*/]; return [4 /*yield*/, ACConfigManager_1.ACConfigManager.getConfigUnsupported()]; case 1: Config = _a.sent(); return [4 /*yield*/, ACReportManager_1.ACReportManager.initialize(logger)]; case 2: _a.sent(); return [2 /*return*/, ACEngineManager_1.ACEngineManager.loadEngineLocal()]; } }); }); } try { // If cucumber is the platform... var AfterAll = require('cucumber').AfterAll; AfterAll(function (done) { var rulePack = "".concat(Config.rulePack); initialize().then(function () { return ACReportManager_1.ACReportManager.metricsLogger.sendLogsV2(function () { return ACBrowserManager_1.ACBrowserManager.close().then(done); }, rulePack); }); }); } catch (e) { if (typeof (after) !== "undefined") { after(function (done) { if (Config) { var rulePack_1 = "".concat(Config.rulePack, "/ace"); initialize().then(function () { return ACReportManager_1.ACReportManager.metricsLogger.sendLogsV2(function () { return ACBrowserManager_1.ACBrowserManager.close().then(done); }, rulePack_1); }); } else { done(); } }); } else { process.on('beforeExit', function () { return __awaiter(this, void 0, void 0, function () { var rulePack_2; return __generator(this, function (_a) { if (Config) { rulePack_2 = "".concat(Config.rulePack, "/ace"); initialize().then(function () { return ACReportManager_1.ACReportManager.metricsLogger.sendLogsV2(null, rulePack_2); }); ACBrowserManager_1.ACBrowserManager.close(); } return [2 /*return*/]; }); }); }); } } function areValidPolicy(valPolicies, curPol) { var isValPol = false; var errorPolicy = ""; for (var 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 \"".concat(errorPolicy, "\". Valid policy ids are: ").concat(valPolicies)); } if (!isValPol) { var errStr = "[ERROR] ValidPoliciesMissing: No valid policy has been provided. Valid policy ids for the specified archive are: ".concat(valPolicies); console.error(errStr); throw new Error(errStr); } } function getComplianceHelper(content, label) { return __awaiter(this, void 0, void 0, function () { // 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 () { var isURLRegex; return __generator(this, function (_a) { if (!content) return [2 /*return*/, null]; // Handle local file and URL's if (typeof content === "string") { 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 [2 /*return*/, ACBrowserManager_1.ACBrowserManager.buildIframeAndGetDoc(content)]; } else if (ACEngineManager_1.ACEngineManager.isSelenium(content) || ACEngineManager_1.ACEngineManager.isPuppeteer(content) || ACEngineManager_1.ACEngineManager.isPlaywright(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 [2 /*return*/, content]; }); }); } var URL, parsed, testcaseWhichIsMissingRequiredLabel, generalErrorMessageLabelNotUnique, labelUnique, testcaseDoesNotUseUniqueLabel, generalErrorMessageLabelNotUnique, policies, curPol; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, initialize()]; case 1: _a.sent(); Config.DEBUG && console.log("START 'aChecker.getCompliance' function"); if (!content) { console.error("aChecker: Unable to get compliance of null or undefined object"); return [2 /*return*/, null]; } return [4 /*yield*/, getParsed(content)]; case 2: parsed = _a.sent(); if (parsed === null) return [2 /*return*/, null]; return [4 /*yield*/, ACEngineManager_1.ACEngineManager.loadEngine(parsed)]; case 3: _a.sent(); // In the case that the label is null or undefined, throw an error using the karma API // console.error with the message of the error. if (label === null || typeof label === "undefined" || label === undefined) { testcaseWhichIsMissingRequiredLabel = null; generalErrorMessageLabelNotUnique = "\n[Error] labelNotProvided: Label must be provided when calling aChecker.getCompliance."; // Get the caller of the aChecker.getCompliance function which will be the testcase that is calling this function // This way we can make it the error more descriptive and would help the user identify where the issues is. // We have to build and throw an Error() object and then using the try/catch to catch this error and then extract the // stack and parse it to get the 2nd element in the stack which will be the caller of this function which will be the // testcase which called this function. try { // Throw Error() object throw new Error(); } catch (exception) { // Extract the stack trace from the error object and parse it to get the single one caller up which will be the 2nd index testcaseWhichIsMissingRequiredLabel = exception.stack.split("\n")[1]; // Call the Karma error API, to send message to the Karma server that there was an error on the client side console.error("Label was not provided at: " + testcaseWhichIsMissingRequiredLabel + generalErrorMessageLabelNotUnique); } } labelUnique = ACReportManager_1.ACReportManager.isLabelUnique(label); // In the case that the label is not unique if (!labelUnique) { testcaseDoesNotUseUniqueLabel = null; generalErrorMessageLabelNotUnique = "\n[Error] labelNotUnique: Label provided to aChecker.getCompliance should be unique across all testcases in a single accessibility-checker session."; // Get the caller of the aChecker.getCompliance function which will be the testcase that is calling this function // This way we can make it the error more descriptive and would help the user identify where the issues is. // We have to build and throw an Error() object and then using the try/catch to catch this error and then extract the // stack and parse it to get the 2nd element in the stack which will be the caller of this function which will be the // testcase which called this function. try { // Throw Error() object throw new Error(); } catch (exception) { // Extract the stack trace from the error object and parse it to get the single one caller up which will be the 2nd index testcaseDoesNotUseUniqueLabel = exception.stack.split("\n")[1]; // Call the Karma error API, to send message to the Karma server that there was an error on the client side console.error("Label \"" + label + "\" provided at: " + testcaseDoesNotUseUniqueLabel + " is not unique." + generalErrorMessageLabelNotUnique); } } policies = Config.policies; curPol = null; if (policies) { curPol = JSON.parse(JSON.stringify(policies)); } if (!ACEngineManager_1.ACEngineManager.isSelenium(parsed)) return [3 /*break*/, 5]; Config.DEBUG && console.log("getComplianceHelper:Selenium"); return [4 /*yield*/, getComplianceHelperSelenium(label, parsed, curPol)]; case 4: return [2 /*return*/, _a.sent()]; case 5: if (!ACEngineManager_1.ACEngineManager.isPuppeteer(parsed)) return [3 /*break*/, 7]; Config.DEBUG && console.log("ACHelper.ts:getComplianceHelper:Puppeteer"); return [4 /*yield*/, getComplianceHelperPuppeteer(label, parsed, curPol)]; case 6: return [2 /*return*/, _a.sent()]; case 7: if (!ACEngineManager_1.ACEngineManager.isPlaywright(parsed)) return [3 /*break*/, 9]; Config.DEBUG && console.log("ACHelper.ts:getComplianceHelper:Playwright"); return [4 /*yield*/, getComplianceHelperPuppeteer(label, parsed, curPol)]; case 8: return [2 /*return*/, _a.sent()]; case 9: Config.DEBUG && console.log("ACHelper.ts:getComplianceHelper:Local"); return [4 /*yield*/, getComplianceHelperLocal(label, parsed, curPol)]; case 10: return [2 /*return*/, _a.sent()]; } }); }); } exports.getComplianceHelper = getComplianceHelper; function getComplianceHelperSelenium(label, parsed, curPol) { return __awaiter(this, void 0, void 0, function () { var startScan, browser, scriptStr, manage, report, getPolicies, valPolicies, _a, _b, finalReport, url, origReport, counts, image, screenshotResult, err_1; return __generator(this, function (_c) { switch (_c.label) { case 0: _c.trys.push([0, 7, , 8]); startScan = Date.now(); browser = parsed; scriptStr = "let cb = arguments[arguments.length - 1];\ntry {\nlet policies = ".concat(JSON.stringify(Config.policies), ";\n\nlet checker = new window.ace_ibma.Checker();\nlet customRulesets = ").concat(JSON.stringify(ACEngineManager_1.ACEngineManager.customRulesets), ";\ncustomRulesets.forEach((rs) => checker.addRuleset(rs));\nsetTimeout(function() {\n checker.check(document, policies).then(function(report) {\n for (const result of report.results) {\n delete result.node;\n }\n cb(report);\n })\n},0)\n} catch (e) {\ncb(e);\n}"); manage = browser.manage(); if (manage.timeouts) { manage.timeouts().setScriptTimeout(60000); } else if (manage.setTimeouts) { manage.setTimeouts({ "script": 60000 }); } return [4 /*yield*/, browser.executeAsyncScript(scriptStr)]; case 1: report = _c.sent(); report = ACReportManager_1.ACReportManager.setLevels(report); getPolicies = "return new window.ace_ibma.Checker().rulesetIds;"; if (!(curPol != null && !checkPolicy)) return [3 /*break*/, 3]; checkPolicy = true; _b = (_a = ACEngineManager_1.ACEngineManager.customRulesets.map(function (rs) { return rs.id; })).concat; return [4 /*yield*/, browser.executeScript(getPolicies)]; case 2: valPolicies = _b.apply(_a, [_c.sent()]); areValidPolicy(valPolicies, curPol); _c.label = 3; case 3: finalReport = void 0; if (!report.results) return [3 /*break*/, 6]; return [4 /*yield*/, browser.getCurrentUrl()]; case 4: url = _c.sent(); origReport = JSON.parse(JSON.stringify(report)); origReport = ACReportManager_1.ACReportManager.buildReport(origReport, {}, url, label, startScan); // Filter the violations based on the reportLevels report = ACReportManager_1.ACReportManager.filterViolations(report); counts = ACReportManager_1.ACReportManager.getCounts(report); // Add the violation count to global summary object ACReportManager_1.ACReportManager.addToSummaryCount(counts); // Build the report object for this scan, to follow a specific format. Refer to the // function prolog for more information on the object creation. finalReport = ACReportManager_1.ACReportManager.buildReport(report, counts, url, label, startScan); // Add the scan results to global karma result object which can be accessed when users testcase // finishes, user can also access it to alter it for any reason. ACReportManager_1.ACReportManager.addResultsToGlobal(finalReport); // Need to call a karma API to send the results of a single scan to the accessibility-checker reporter so that they can be // saved to a file by the server side reporter. ACReportManager_1.ACReportManager.sendResultsToReporter(origReport, finalReport, "Selenium"); if (!(Config.captureScreenshots && browser.takeScreenshot)) return [3 /*break*/, 6]; return [4 /*yield*/, browser.takeScreenshot()]; case 5: image = _c.sent(); screenshotResult = { image: image, label: label, scanID: finalReport.scanID }; ACReportManager_1.ACReportManager.sendScreenShotToReporter(screenshotResult); _c.label = 6; case 6: return [2 /*return*/, { "report": finalReport, "webdriver": parsed }]; case 7: err_1 = _c.sent(); console.error(err_1); return [2 /*return*/, Promise.reject(err_1)]; case 8: ; return [2 /*return*/]; } }); }); } function getComplianceHelperPuppeteer(label, parsed, curPol) { return __awaiter(this, void 0, void 0, function () { var startScan, page, report, valPolicies, _a, _b, finalReport, url, origReport, counts, image, screenshotResult, err_2; return __generator(this, function (_c) { switch (_c.label) { case 0: _c.trys.push([0, 7, , 8]); startScan = Date.now(); page = parsed; return [4 /*yield*/, page.evaluate(function (_a) { var policies = _a.policies, customRulesets = _a.customRulesets; var checker = new window.ace_ibma.Checker(); customRulesets.forEach(function (rs) { return checker.addRuleset(rs); }); return new Promise(function (resolve, reject) { setTimeout(function () { checker.check(document, policies).then(function (report) { for (var _i = 0, _a = report.results; _i < _a.length; _i++) { var result = _a[_i]; delete result.node; } resolve(report); }); }, 0); }); }, { policies: Config.policies, customRulesets: ACEngineManager_1.ACEngineManager.customRulesets })]; case 1: report = _c.sent(); report = ACReportManager_1.ACReportManager.setLevels(report); if (!(curPol != null && !checkPolicy)) return [3 /*break*/, 3]; _b = (_a = ACEngineManager_1.ACEngineManager.customRulesets.map(function (rs) { return rs.id; })).concat; return [4 /*yield*/, page.evaluate("new window.ace_ibma.Checker().rulesetIds")]; case 2: valPolicies = _b.apply(_a, [_c.sent()]); checkPolicy = true; areValidPolicy(valPolicies, curPol); _c.label = 3; case 3: finalReport = void 0; if (!report.results) return [3 /*break*/, 6]; return [4 /*yield*/, page.evaluate("document.location.href")]; case 4: url = _c.sent(); origReport = JSON.parse(JSON.stringify(report)); origReport = ACReportManager_1.ACReportManager.buildReport(origReport, {}, url, label, startScan); // Filter the violations based on the reporLevels report = ACReportManager_1.ACReportManager.filterViolations(report); counts = ACReportManager_1.ACReportManager.getCounts(report); // Add the violation count to global summary object ACReportManager_1.ACReportManager.addToSummaryCount(counts); // Build the report object for this scan, to follow a specific format. Refer to the // function prolog for more information on the object creation. finalReport = ACReportManager_1.ACReportManager.buildReport(report, counts, url, label, startScan); // Add the scan results to global karma result object which can be accessed when users testcase // finishes, user can also access it to alter it for any reason. ACReportManager_1.ACReportManager.addResultsToGlobal(finalReport); // Need to call a karma API to send the results of a single scan to the accessibility-checker reporter so that they can be // saved to a file by the server side reporter. ACReportManager_1.ACReportManager.sendResultsToReporter(origReport, finalReport, "Puppeteer"); if (!Config.captureScreenshots) return [3 /*break*/, 6]; return [4 /*yield*/, page.screenshot({ fullPage: true, encoding: "base64" })]; case 5: image = _c.sent(); screenshotResult = { image: image, label: label, scanID: Config.scanID }; ACReportManager_1.ACReportManager.sendScreenShotToReporter(screenshotResult); _c.label = 6; case 6: page.aceBusy = false; return [2 /*return*/, { "report": finalReport, "puppeteer": parsed }]; case 7: err_2 = _c.sent(); console.error(err_2); return [2 /*return*/, Promise.reject(err_2)]; case 8: ; return [2 /*return*/]; } }); }); } function getComplianceHelperLocal(label, parsed, curPol) { return __awaiter(this, void 0, void 0, function () { var startScan, checker_1, report, valPolicies, finalReport, url, origReport, counts, err_3; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); startScan = Date.now(); checker_1 = ACEngineManager_1.ACEngineManager.getChecker(); ACEngineManager_1.ACEngineManager.customRulesets.forEach(function (rs) { return checker_1.addRuleset(rs); }); return [4 /*yield*/, checker_1.check(parsed, Config.policies) .then(function (report) { for (var _i = 0, _a = report.results; _i < _a.length; _i++) { var result = _a[_i]; delete result.node; } return report; })]; case 1: report = _a.sent(); report = ACReportManager_1.ACReportManager.setLevels(report); if (curPol != null && !checkPolicy) { valPolicies = checker_1.rulesetIds; checkPolicy = true; areValidPolicy(valPolicies, curPol); } finalReport = void 0; // If there is something to report... if (report.results) { url = parsed.location && parsed.location.href; origReport = JSON.parse(JSON.stringify(report)); origReport = ACReportManager_1.ACReportManager.buildReport(origReport, {}, url, label, startScan); // Filter the violations based on the reporLevels report = ACReportManager_1.ACReportManager.filterViolations(report); counts = ACReportManager_1.ACReportManager.getCounts(report); // Add the violation count to global summary object ACReportManager_1.ACReportManager.addToSummaryCount(counts); // Build the report object for this scan, to follow a specific format. Refer to the // function prolog for more information on the object creation. finalReport = ACReportManager_1.ACReportManager.buildReport(report, counts, URL, label, startScan); // Add the scan results to global karma result object which can be accessed when users testcase // finishes, user can also access it to alter it for any reason. ACReportManager_1.ACReportManager.addResultsToGlobal(finalReport); // Need to call a karma API to send the results of a single scan to the accessibility-checker reporter so that they can be // saved to a file by the server side reporter. ACReportManager_1.ACReportManager.sendResultsToReporter(origReport, finalReport, "Native"); } return [2 /*return*/, { "report": finalReport }]; case 2: err_3 = _a.sent(); console.error(err_3); return [2 /*return*/, Promise.reject(err_3)]; case 3: ; return [2 /*return*/]; } }); }); } //# sourceMappingURL=ACHelper.js.map