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 = / ^ ( h t t p | h t t p s ) : $ / . 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 = / ^ ( h t t p | h t t p s ) : $ / . 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 = / ^ ( h t t p | h t t p s ) : $ / . 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 = / ^ ( h t t p | h t t p s ) : $ / . 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