Skip to content

Commit c101a57

Browse files
authored
Merge pull request #907 from browserstack/ats_fixes
Ats fixes
2 parents 0ea68a9 + 074b95f commit c101a57

File tree

28 files changed

+3325
-53
lines changed

28 files changed

+3325
-53
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
exports.API_URL = 'https://accessibility.browserstack.com/api';
Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
/* Event listeners + custom commands for Cypress */
2+
3+
const browserStackLog = (message) => {
4+
if (!Cypress.env('BROWSERSTACK_LOGS')) return;
5+
cy.task('browserstack_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 performScan = (win, payloadToSend) =>
11+
new Promise(async (resolve, reject) => {
12+
const isHttpOrHttps = /^(http|https):$/.test(win.location.protocol);
13+
if (!isHttpOrHttps) {
14+
resolve();
15+
}
16+
17+
function findAccessibilityAutomationElement() {
18+
return win.document.querySelector("#accessibility-automation-element");
19+
}
20+
21+
function waitForScannerReadiness(retryCount = 30, retryInterval = 100) {
22+
return new Promise(async (resolve, reject) => {
23+
let count = 0;
24+
const intervalID = setInterval(async () => {
25+
if (count > retryCount) {
26+
clearInterval(intervalID);
27+
reject(
28+
new Error(
29+
"Accessibility Automation Scanner is not ready on the page."
30+
)
31+
);
32+
} else if (findAccessibilityAutomationElement()) {
33+
clearInterval(intervalID);
34+
resolve("Scanner set");
35+
} else {
36+
count += 1;
37+
}
38+
}, retryInterval);
39+
});
40+
}
41+
42+
function startScan() {
43+
function onScanComplete() {
44+
win.removeEventListener("A11Y_SCAN_FINISHED", onScanComplete);
45+
resolve();
46+
}
47+
48+
win.addEventListener("A11Y_SCAN_FINISHED", onScanComplete);
49+
const e = new CustomEvent("A11Y_SCAN", { detail: payloadToSend });
50+
win.dispatchEvent(e);
51+
}
52+
53+
if (findAccessibilityAutomationElement()) {
54+
startScan();
55+
} else {
56+
waitForScannerReadiness()
57+
.then(startScan)
58+
.catch(async (err) => {
59+
resolve("Scanner is not ready on the page after multiple retries. performscan");
60+
});
61+
}
62+
})
63+
64+
const getAccessibilityResultsSummary = (win) =>
65+
new Promise((resolve) => {
66+
const isHttpOrHttps = /^(http|https):$/.test(window.location.protocol);
67+
if (!isHttpOrHttps) {
68+
resolve();
69+
}
70+
71+
function findAccessibilityAutomationElement() {
72+
return win.document.querySelector("#accessibility-automation-element");
73+
}
74+
75+
function waitForScannerReadiness(retryCount = 30, retryInterval = 100) {
76+
return new Promise((resolve, reject) => {
77+
let count = 0;
78+
const intervalID = setInterval(() => {
79+
if (count > retryCount) {
80+
clearInterval(intervalID);
81+
reject(
82+
new Error(
83+
"Accessibility Automation Scanner is not ready on the page."
84+
)
85+
);
86+
} else if (findAccessibilityAutomationElement()) {
87+
clearInterval(intervalID);
88+
resolve("Scanner set");
89+
} else {
90+
count += 1;
91+
}
92+
}, retryInterval);
93+
});
94+
}
95+
96+
function getSummary() {
97+
function onReceiveSummary(event) {
98+
win.removeEventListener("A11Y_RESULTS_SUMMARY", onReceiveSummary);
99+
resolve(event.detail);
100+
}
101+
102+
win.addEventListener("A11Y_RESULTS_SUMMARY", onReceiveSummary);
103+
const e = new CustomEvent("A11Y_GET_RESULTS_SUMMARY");
104+
win.dispatchEvent(e);
105+
}
106+
107+
if (findAccessibilityAutomationElement()) {
108+
getSummary();
109+
} else {
110+
waitForScannerReadiness()
111+
.then(getSummary)
112+
.catch((err) => {
113+
resolve();
114+
});
115+
}
116+
})
117+
118+
const getAccessibilityResults = (win) =>
119+
new Promise((resolve) => {
120+
const isHttpOrHttps = /^(http|https):$/.test(window.location.protocol);
121+
if (!isHttpOrHttps) {
122+
resolve();
123+
}
124+
125+
function findAccessibilityAutomationElement() {
126+
return win.document.querySelector("#accessibility-automation-element");
127+
}
128+
129+
function waitForScannerReadiness(retryCount = 30, retryInterval = 100) {
130+
return new Promise((resolve, reject) => {
131+
let count = 0;
132+
const intervalID = setInterval(() => {
133+
if (count > retryCount) {
134+
clearInterval(intervalID);
135+
reject(
136+
new Error(
137+
"Accessibility Automation Scanner is not ready on the page."
138+
)
139+
);
140+
} else if (findAccessibilityAutomationElement()) {
141+
clearInterval(intervalID);
142+
resolve("Scanner set");
143+
} else {
144+
count += 1;
145+
}
146+
}, retryInterval);
147+
});
148+
}
149+
150+
function getResults() {
151+
function onReceivedResult(event) {
152+
win.removeEventListener("A11Y_RESULTS_RESPONSE", onReceivedResult);
153+
resolve(event.detail);
154+
}
155+
156+
win.addEventListener("A11Y_RESULTS_RESPONSE", onReceivedResult);
157+
const e = new CustomEvent("A11Y_GET_RESULTS");
158+
win.dispatchEvent(e);
159+
}
160+
161+
if (findAccessibilityAutomationElement()) {
162+
getResults();
163+
} else {
164+
waitForScannerReadiness()
165+
.then(getResults)
166+
.catch((err) => {
167+
resolve();
168+
});
169+
}
170+
});
171+
172+
const saveTestResults = (win, payloadToSend) =>
173+
new Promise( (resolve, reject) => {
174+
try {
175+
const isHttpOrHttps = /^(http|https):$/.test(win.location.protocol);
176+
if (!isHttpOrHttps) {
177+
resolve("Unable to save accessibility results, Invalid URL.");
178+
}
179+
180+
function findAccessibilityAutomationElement() {
181+
return win.document.querySelector("#accessibility-automation-element");
182+
}
183+
184+
function waitForScannerReadiness(retryCount = 30, retryInterval = 100) {
185+
return new Promise((resolve, reject) => {
186+
let count = 0;
187+
const intervalID = setInterval(async () => {
188+
if (count > retryCount) {
189+
clearInterval(intervalID);
190+
reject(
191+
new Error(
192+
"Accessibility Automation Scanner is not ready on the page."
193+
)
194+
);
195+
} else if (findAccessibilityAutomationElement()) {
196+
clearInterval(intervalID);
197+
resolve("Scanner set");
198+
} else {
199+
count += 1;
200+
}
201+
}, retryInterval);
202+
});
203+
}
204+
205+
function saveResults() {
206+
function onResultsSaved(event) {
207+
resolve();
208+
}
209+
win.addEventListener("A11Y_RESULTS_SAVED", onResultsSaved);
210+
const e = new CustomEvent("A11Y_SAVE_RESULTS", {
211+
detail: payloadToSend,
212+
});
213+
win.dispatchEvent(e);
214+
}
215+
216+
if (findAccessibilityAutomationElement()) {
217+
saveResults();
218+
} else {
219+
waitForScannerReadiness()
220+
.then(saveResults)
221+
.catch(async (err) => {
222+
resolve("Scanner is not ready on the page after multiple retries. after run");
223+
});
224+
}
225+
} catch(er) {
226+
resolve()
227+
}
228+
229+
})
230+
231+
const shouldScanForAccessibility = (attributes) => {
232+
if (Cypress.env("IS_ACCESSIBILITY_EXTENSION_LOADED") !== "true") return false;
233+
234+
const extensionPath = Cypress.env("ACCESSIBILITY_EXTENSION_PATH");
235+
const isHeaded = Cypress.browser.isHeaded;
236+
237+
if (!isHeaded || (extensionPath === undefined)) return false;
238+
239+
let shouldScanTestForAccessibility = true;
240+
241+
if (Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY") || Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY")) {
242+
try {
243+
let includeTagArray = [];
244+
let excludeTagArray = [];
245+
if (Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY")) {
246+
includeTagArray = Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY").split(";")
247+
}
248+
if (Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY")) {
249+
excludeTagArray = Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY").split(";")
250+
}
251+
252+
const fullTestName = attributes.title;
253+
const excluded = excludeTagArray.some((exclude) => fullTestName.includes(exclude));
254+
const included = includeTagArray.length === 0 || includeTags.some((include) => fullTestName.includes(include));
255+
shouldScanTestForAccessibility = !excluded && included;
256+
} catch (error) {
257+
browserStackLog("Error while validating test case for accessibility before scanning. Error : ", error);
258+
}
259+
}
260+
261+
return shouldScanTestForAccessibility;
262+
}
263+
264+
Cypress.on('command:start', async (command) => {
265+
if(!command || !command.attributes) return;
266+
if(command.attributes.name == 'window' || command.attributes.name == 'then' || command.attributes.name == 'wrap') {
267+
return;
268+
}
269+
270+
if (!commandsToWrap.includes(command.attributes.name)) return;
271+
272+
const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable;
273+
274+
let shouldScanTestForAccessibility = shouldScanForAccessibility(attributes);
275+
if (!shouldScanTestForAccessibility) return;
276+
277+
cy.window().then((win) => {
278+
browserStackLog('Performing scan form command ' + command.attributes.name);
279+
cy.wrap(performScan(win, {method: command.attributes.name}), {timeout: 30000});
280+
})
281+
})
282+
283+
afterEach(() => {
284+
const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest;
285+
cy.window().then(async (win) => {
286+
let shouldScanTestForAccessibility = shouldScanForAccessibility(attributes);
287+
if (!shouldScanTestForAccessibility) return cy.wrap({});
288+
289+
cy.wrap(performScan(win), {timeout: 30000}).then(() => {
290+
try {
291+
let os_data;
292+
if (Cypress.env("OS")) {
293+
os_data = Cypress.env("OS");
294+
} else {
295+
os_data = Cypress.platform === 'linux' ? 'mac' : "win"
296+
}
297+
let filePath = '';
298+
if (attributes.invocationDetails !== undefined && attributes.invocationDetails.relativeFile !== undefined) {
299+
filePath = attributes.invocationDetails.relativeFile;
300+
}
301+
const payloadToSend = {
302+
"saveResults": shouldScanTestForAccessibility,
303+
"testDetails": {
304+
"name": attributes.title,
305+
"testRunId": '5058', // variable not consumed, shouldn't matter what we send
306+
"filePath": filePath,
307+
"scopeList": [
308+
filePath,
309+
attributes.title
310+
]
311+
},
312+
"platform": {
313+
"os_name": os_data,
314+
"os_version": Cypress.env("OS_VERSION"),
315+
"browser_name": Cypress.browser.name,
316+
"browser_version": Cypress.browser.version
317+
}
318+
};
319+
browserStackLog(`Saving accessibility test results`);
320+
cy.wrap(saveTestResults(win, payloadToSend), {timeout: 30000}).then(() => {
321+
browserStackLog(`Saved accessibility test results`);
322+
})
323+
324+
} catch (er) {
325+
}
326+
})
327+
});
328+
})
329+
330+
Cypress.Commands.add('performScan', () => {
331+
try {
332+
const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable;
333+
const shouldScanTestForAccessibility = shouldScanForAccessibility(attributes);
334+
if (!shouldScanTestForAccessibility) {
335+
browserStackLog(`Not a Accessibility Automation session, cannot perform scan.`);
336+
return cy.wrap({});
337+
}
338+
cy.window().then(async (win) => {
339+
browserStackLog(`Performing accessibility scan`);
340+
await performScan(win);
341+
});
342+
} catch {}
343+
})
344+
345+
Cypress.Commands.add('getAccessibilityResultsSummary', () => {
346+
try {
347+
const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable;
348+
const shouldScanTestForAccessibility = shouldScanForAccessibility(attributes);
349+
if (!shouldScanTestForAccessibility) {
350+
browserStackLog(`Not a Accessibility Automation session, cannot retrieve Accessibility results summary.`);
351+
return cy.wrap({});
352+
}
353+
cy.window().then(async (win) => {
354+
await performScan(win);
355+
browserStackLog('Getting accessibility results summary');
356+
return await getAccessibilityResultsSummary(win);
357+
});
358+
} catch {}
359+
360+
});
361+
362+
Cypress.Commands.add('getAccessibilityResults', () => {
363+
try {
364+
const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable;
365+
const shouldScanTestForAccessibility = shouldScanForAccessibility(attributes);
366+
if (!shouldScanTestForAccessibility) {
367+
browserStackLog(`Not a Accessibility Automation session, cannot retrieve Accessibility results.`);
368+
return cy.wrap({});
369+
}
370+
371+
/* browserstack_accessibility_automation_script */
372+
373+
cy.window().then(async (win) => {
374+
await performScan(win);
375+
browserStackLog('Getting accessibility results');
376+
return await getAccessibilityResults(win);
377+
});
378+
379+
} catch {}
380+
381+
});

0 commit comments

Comments
 (0)