Skip to content

Commit f6733e9

Browse files
authored
Merge pull request #321 from HRanjan-11/CYP-974
cypress accessibility plugin
2 parents 4d61af0 + 9e41248 commit f6733e9

File tree

5 files changed

+260
-1
lines changed

5 files changed

+260
-1
lines changed

accessibility/plugin/index.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const fs = require("fs");
2+
const Accessibility = (on, config) => {
3+
4+
on('task', {
5+
lambdatest_log(message) {
6+
console.log(message)
7+
return null
8+
},
9+
initializeFile(filePath) {
10+
if (!fs.existsSync(filePath)) {
11+
fs.writeFileSync(filePath, '[]');
12+
}
13+
return filePath;
14+
}
15+
})
16+
17+
let browser_validation = true;
18+
19+
on('before:browser:launch', (browser = {}, launchOptions) => {
20+
try {
21+
if (process.env.ACCESSIBILITY_EXTENSION_PATH !== undefined) {
22+
if (browser.name !== 'chrome') {
23+
console.log(`Accessibility Automation will run only on Chrome browsers.`);
24+
browser_validation = false;
25+
}
26+
if (browser.name === 'chrome' && browser.majorVersion <= 94) {
27+
console.log(`Accessibility Automation will run only on Chrome browser version greater than 94.`);
28+
browser_validation = false;
29+
}
30+
if (browser.isHeadless === true) {
31+
console.log(`Accessibility Automation will not run on legacy headless mode. Switch to new headless mode or avoid using headless mode.`);
32+
browser_validation = false;
33+
}
34+
if (browser_validation) {
35+
36+
const accessibility_ext_path = process.env.ACCESSIBILITY_EXTENSION_PATH
37+
38+
launchOptions.args.push(`--load-extension=` + accessibility_ext_path)
39+
return launchOptions
40+
}
41+
}
42+
} catch(err) {
43+
console.log(`Error in loading Accessibility Automation extension: ${err.message}`);
44+
}
45+
46+
})
47+
config.env.WCAG_CRITERIA= process.env.WCAG_CRITERIA;
48+
config.env.BEST_PRACTICE= process.env.BEST_PRACTICE;
49+
config.env.NEEDS_REVIEW= process.env.NEEDS_REVIEW;
50+
config.env.ACCESSIBILITY_REPORT_PATH = process.env.ACCESSIBILITY_REPORT_PATH;
51+
console.log(`parameter for accessibility report WCAG_CRITERIA - ` + config.env.WCAG_CRITERIA)
52+
console.log(`parameter for accessibility report BEST_PRACTICE -` + config.env.BEST_PRACTICE)
53+
console.log(`parameter for accessibility report NEEDS_REVIEW -` + config.env.NEEDS_REVIEW)
54+
55+
return config;
56+
}
57+
58+
module.exports = Accessibility;

accessibility/scanner/index.js

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
const fs = require("fs")
2+
3+
const LambdatestLog = (message) => {
4+
if (!Cypress.env('LAMBDATEST_LOGS')) return;
5+
cy.task('lambdatest_log', message);
6+
}
7+
8+
const commandsToWrap = ['visit', 'click', 'type', 'request', 'dblclick', 'rightclick', 'clear', 'check', 'uncheck', 'select', 'trigger', 'selectFile', 'scrollIntoView', 'scroll', 'scrollTo', 'blur', 'focus', 'go', 'reload', 'submit', 'viewport', 'origin'];
9+
10+
const setScanConfig = (win, payload) =>
11+
new Promise(async (resolve, reject) => {
12+
const isHttpOrHttps = /^(http|https):$/.test(win.location.protocol);
13+
if (!isHttpOrHttps) {
14+
resolve();
15+
}
16+
17+
function startScan() {
18+
console.log('log', "Accessibility setting scan config")
19+
function onScanComplete(event) {
20+
win.document.removeEventListener("automation-custom-event", onScanComplete);
21+
console.log('log', "Recieved scan config data " + event.detail)
22+
resolve(event.detail);
23+
}
24+
25+
win.document.addEventListener("automation-custom-event", onScanComplete);
26+
const e = new CustomEvent("accessibility-extension-custom-event", { detail: payload });
27+
win.document.dispatchEvent(e);
28+
29+
30+
setTimeout(() => {
31+
reject(new Error('automation-custom-event not received within timeout'));
32+
}, 9000);
33+
}
34+
startScan();
35+
36+
})
37+
38+
const getScanData = (win, payload) =>
39+
new Promise( async (resolve,reject) => {
40+
const isHttpOrHttps = /^(http|https):$/.test(window.location.protocol);
41+
if (!isHttpOrHttps) {
42+
resolve();
43+
}
44+
45+
46+
function getSummary() {
47+
function onReceiveSummary(event) {
48+
49+
win.document.removeEventListener("automation-custom-event", onReceiveSummary);
50+
resolve(event.detail);
51+
}
52+
53+
54+
win.document.addEventListener("automation-custom-event", onReceiveSummary);
55+
const e = new CustomEvent("accessibility-extension-custom-event", { detail: payload });
56+
win.document.dispatchEvent(e);
57+
58+
setTimeout(() => {
59+
reject(new Error('automation-custom-event not received within timeout'));
60+
}, 9000);
61+
62+
}
63+
64+
65+
getSummary();
66+
67+
})
68+
69+
const shouldScanForAccessibility = (attributes) => {
70+
if (Cypress.env("IS_ACCESSIBILITY_EXTENSION_LOADED") !== "true") return false;
71+
72+
const extensionPath = Cypress.env("ACCESSIBILITY_EXTENSION_PATH");
73+
const isHeaded = Cypress.browser.isHeaded;
74+
75+
if (!isHeaded || (extensionPath === undefined)) return false;
76+
77+
let shouldScanTestForAccessibility = true;
78+
79+
if (Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY") || Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY")) {
80+
try {
81+
let includeTagArray = [];
82+
let excludeTagArray = [];
83+
if (Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY")) {
84+
includeTagArray = Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY").split(";")
85+
}
86+
if (Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY")) {
87+
excludeTagArray = Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY").split(";")
88+
}
89+
90+
const fullTestName = attributes.title;
91+
const excluded = excludeTagArray.some((exclude) => fullTestName.includes(exclude));
92+
const included = includeTagArray.length === 0 || includeTags.some((include) => fullTestName.includes(include));
93+
shouldScanTestForAccessibility = !excluded && included;
94+
} catch (error) {
95+
LambdatestLog("Error while validating test case for accessibility before scanning. Error : ", error);
96+
}
97+
}
98+
99+
return shouldScanTestForAccessibility;
100+
}
101+
102+
Cypress.on('command:start', async (command) => {
103+
if(!command || !command.attributes) return;
104+
if(command.attributes.name == 'window' || command.attributes.name == 'then' || command.attributes.name == 'wrap' || command.attributes.name == 'wait') {
105+
return;
106+
}
107+
108+
if (!commandsToWrap.includes(command.attributes.name)) return;
109+
110+
// const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable;
111+
112+
// let shouldScanTestForAccessibility = shouldScanForAccessibility(attributes);
113+
// if (!shouldScanTestForAccessibility) return;
114+
// console.log('log', "debugging scan form command " + command.attributes.name);
115+
console.log('log', "debugging scan form command " + command.attributes.name);
116+
cy.window().then((win) => {
117+
// LambdatestLog('Performing scan form command ' + command.attributes.name);
118+
let wcagCriteriaValue = Cypress.env("WCAG_CRITERIA") || "wcag21a";
119+
let bestPracticeValue = Cypress.env("BEST_PRACTICE") || false;
120+
let needsReviewValue = Cypress.env("NEEDS_REVIEW") || true;
121+
122+
const payloadToSend = {
123+
message: 'SET_CONFIG',
124+
wcagCriteria: wcagCriteriaValue,
125+
bestPractice: bestPracticeValue,
126+
needsReview: needsReviewValue
127+
}
128+
let testId = Cypress.env("TEST_ID") || ""
129+
// const filePath = 'cypress/reports/accessibilityReport_' + testId + '.json';
130+
const filePath = Cypress.env("ACCESSIBILITY_REPORT_PATH") || 'cypress/results/accessibilityReport_' + testId + '.json';
131+
132+
cy.wrap(setScanConfig(win, payloadToSend), {timeout: 30000}).then((res) => {
133+
// LambdatestLog('log', "logging report **************")
134+
console.log('logging config reponse', res);
135+
136+
const payload = {
137+
message: 'GET_LATEST_SCAN_DATA',
138+
}
139+
// cy.wait(5000);
140+
cy.wrap(getScanData(win, payload), {timeout: 30000}).then((res) => {
141+
LambdatestLog('log', "logging report **************")
142+
143+
144+
cy.task('initializeFile', filePath).then((filePath) => {
145+
cy.readFile(filePath, { log: true, timeout: 30000 }).then((fileContent) => {
146+
let resultsArray = [{}];
147+
console.log('logging report', res);
148+
// If the file is not empty, parse the existing content
149+
if (fileContent) {
150+
try {
151+
resultsArray = JSON.parse(JSON.stringify(fileContent));
152+
} catch (e) {
153+
console.log("parsing error for content " , fileContent)
154+
console.log('Error parsing JSON file:', e);
155+
return;
156+
}
157+
}
158+
console.log('debugging res', res.message);
159+
if (res.message == "GET_LATEST_SCAN_DATA") {
160+
// Append the new result
161+
resultsArray.push(res);
162+
console.log('resultsarray logging', resultsArray);
163+
}
164+
165+
// Write the updated content back to the file
166+
cy.writeFile(filePath, resultsArray, { log: true, timeout: 30000 });
167+
});
168+
});
169+
});
170+
171+
});
172+
})
173+
})
174+
175+
176+
Cypress.on('command:end', (command) => {
177+
178+
return;
179+
})

commands/utils/set_args.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,16 @@ function sync_args_from_cmd(args) {
383383
lt_config["run_settings"]["useNode18"] = false;
384384
}
385385

386+
if ("accessibility" in args) {
387+
if (args["accessibility"] == "true") {
388+
lt_config.run_settings.accessibility = true;
389+
} else {
390+
lt_config.run_settings.accessibility = false;
391+
}
392+
} else if (lt_config["run_settings"]["accessibility"] && !lt_config["run_settings"]["accessibility"]) {
393+
lt_config["run_settings"]["accessibility"] = false;
394+
}
395+
386396
if ("network_ws" in args) {
387397
if (args["network_ws"] == "true") {
388398
lt_config.run_settings.network_ws = true;

commands/utils/validate.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,14 @@ module.exports = validate_config = function (lt_config, validation_configs) {
198198
}
199199
}
200200

201-
//validate if network_http2 field contains expected value
201+
//validate if accessibility field contains expected value
202+
if ("accessibility" in lt_config["run_settings"]) {
203+
if (![true, false].includes(lt_config["run_settings"]["accessibility"])) {
204+
reject("Error!! boolean value is expected in accessibility key");
205+
}
206+
}
207+
208+
//validate if useNode18 field contains expected value
202209
if ("useNode18" in lt_config["run_settings"]) {
203210
if (![true, false].includes(lt_config["run_settings"]["useNode18"])) {
204211
reject("Error!! boolean value is expected in useNode18 key");

index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,11 @@ const argv = require("yargs")
245245
alias: "network_sse",
246246
describe: "Bypass sse events calls for Network logs",
247247
type: "bool",
248+
})
249+
.option("cypress_accessibility", {
250+
alias: "accessibility",
251+
describe: "enable accessibility testing for cypress.",
252+
type: "bool",
248253
});
249254
},
250255
function (argv) {

0 commit comments

Comments
 (0)