-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #707 from rev-doshi/cypress-accessibility
initial changes
- Loading branch information
Showing
9 changed files
with
764 additions
and
231 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
exports.API_URL = 'https://accessibility.browserstack.com/api'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/* Event listeners + custom commands for Cypress */ | ||
|
||
Cypress.on('test:before:run', () => { | ||
try { | ||
if (Cypress.env("IS_ACCESSIBILITY_EXTENSION_LOADED") !== "true") return | ||
const extensionPath = Cypress.env("ACCESSIBILITY_EXTENSION_PATH") | ||
|
||
if (extensionPath !== undefined) { | ||
new Promise((resolve, reject) => { | ||
window.parent.addEventListener('A11Y_TAP_STARTED', () => { | ||
resolve("A11Y_TAP_STARTED"); | ||
}); | ||
const e = new CustomEvent('A11Y_FORCE_START'); | ||
window.parent.dispatchEvent(e); | ||
}) | ||
} | ||
} catch {} | ||
|
||
}); | ||
|
||
Cypress.on('test:after:run', (attributes, runnable) => { | ||
try { | ||
if (Cypress.env("IS_ACCESSIBILITY_EXTENSION_LOADED") !== "true") return | ||
const extensionPath = Cypress.env("ACCESSIBILITY_EXTENSION_PATH") | ||
const isHeaded = Cypress.browser.isHeaded; | ||
if (isHeaded && extensionPath !== undefined) { | ||
|
||
let shouldScanTestForAccessibility = true; | ||
if (Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY") || Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY")) { | ||
|
||
try { | ||
let includeTagArray = []; | ||
let excludeTagArray = []; | ||
if (Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY")) { | ||
includeTagArray = Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY").split(";") | ||
} | ||
if (Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY")) { | ||
excludeTagArray = Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY").split(";") | ||
} | ||
|
||
const fullTestName = attributes.title; | ||
const excluded = excludeTagArray.some((exclude) => fullTestName.includes(exclude)); | ||
const included = includeTagArray.length === 0 || includeTags.some((include) => fullTestName.includes(include)); | ||
shouldScanTestForAccessibility = !excluded && included; | ||
} catch (error) { | ||
console.log("Error while validating test case for accessibility before scanning. Error : ", error); | ||
} | ||
} | ||
let os_data; | ||
if (Cypress.env("OS")) { | ||
os_data = Cypress.env("OS"); | ||
} else { | ||
os_data = Cypress.platform === 'linux' ? 'mac' : "win" | ||
} | ||
let filePath = ''; | ||
if (attributes.invocationDetails !== undefined && attributes.invocationDetails.relativeFile !== undefined) { | ||
filePath = attributes.invocationDetails.relativeFile; | ||
} | ||
const dataForExtension = { | ||
"saveResults": shouldScanTestForAccessibility, | ||
"testDetails": { | ||
"name": attributes.title, | ||
"testRunId": '5058', // variable not consumed, shouldn't matter what we send | ||
"filePath": filePath, | ||
"scopeList": [ | ||
filePath, | ||
attributes.title | ||
] | ||
}, | ||
"platform": { | ||
"os_name": os_data, | ||
"os_version": Cypress.env("OS_VERSION"), | ||
"browser_name": Cypress.browser.name, | ||
"browser_version": Cypress.browser.version | ||
} | ||
}; | ||
return new Promise((resolve, reject) => { | ||
if (dataForExtension.saveResults) { | ||
window.parent.addEventListener('A11Y_TAP_TRANSPORTER', (event) => { | ||
resolve(event.detail); | ||
}); | ||
} | ||
const e = new CustomEvent('A11Y_TEST_END', {detail: dataForExtension}); | ||
window.parent.dispatchEvent(e); | ||
if (dataForExtension.saveResults !== true ) | ||
resolve(); | ||
}); | ||
} | ||
|
||
} catch {} | ||
}); | ||
|
||
Cypress.Commands.add('getAccessibilityResultsSummary', () => { | ||
try { | ||
if (Cypress.env("IS_ACCESSIBILITY_EXTENSION_LOADED") !== "true") { | ||
console.log(`Not a Accessibility Automation session, cannot retrieve Accessibility results.`); | ||
return | ||
} | ||
return new Promise(function (resolve, reject) { | ||
try{ | ||
const e = new CustomEvent('A11Y_TAP_GET_RESULTS_SUMMARY'); | ||
const fn = function (event) { | ||
window.parent.removeEventListener('A11Y_RESULTS_SUMMARY_RESPONSE', fn); | ||
resolve(event.detail.summary); | ||
}; | ||
window.parent.addEventListener('A11Y_RESULTS_SUMMARY_RESPONSE', fn); | ||
window.parent.dispatchEvent(e); | ||
} catch (err) { | ||
console.log("No accessibility results summary was found."); | ||
reject(err); | ||
} | ||
}); | ||
} catch {} | ||
|
||
}); | ||
|
||
Cypress.Commands.add('getAccessibilityResults', () => { | ||
try { | ||
if (Cypress.env("IS_ACCESSIBILITY_EXTENSION_LOADED") !== "true") { | ||
console.log(`Not a Accessibility Automation session, cannot retrieve Accessibility results.`); | ||
return | ||
} | ||
return new Promise(function (resolve, reject) { | ||
try{ | ||
const e = new CustomEvent('A11Y_TAP_GET_RESULTS'); | ||
const fn = function (event) { | ||
window.parent.removeEventListener('A11Y_RESULTS_RESPONSE', fn); | ||
resolve(event.detail.summary); | ||
}; | ||
window.parent.addEventListener('A11Y_RESULTS_RESPONSE', fn); | ||
window.parent.dispatchEvent(e); | ||
} catch (err) { | ||
console.log("No accessibility results were found."); | ||
reject(err); | ||
} | ||
}); | ||
} catch {} | ||
|
||
}); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
const logger = require("../helpers/logger").winstonLogger; | ||
const { API_URL } = require('./constants'); | ||
const utils = require('../helpers/utils'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const request = require('request'); | ||
const os = require('os'); | ||
const glob = require('glob'); | ||
const helper = require('../helpers/helper'); | ||
const { CYPRESS_V10_AND_ABOVE_CONFIG_FILE_EXTENSIONS } = require('../helpers/constants'); | ||
const supportFileContentMap = {} | ||
|
||
exports.checkAccessibilityPlatform = (user_config) => { | ||
let accessibility = false; | ||
try { | ||
user_config.browsers.forEach(browser => { | ||
if (browser.accessibility) { | ||
accessibility = true; | ||
} | ||
}) | ||
} catch {} | ||
|
||
return accessibility; | ||
} | ||
|
||
exports.setAccessibilityCypressCapabilities = async (user_config, accessibilityResponse) => { | ||
if (utils.isUndefined(user_config.run_settings.accessibilityOptions)) { | ||
user_config.run_settings.accessibilityOptions = {} | ||
} | ||
user_config.run_settings.accessibilityOptions["authToken"] = accessibilityResponse.data.accessibilityToken; | ||
user_config.run_settings.accessibilityOptions["auth"] = accessibilityResponse.data.accessibilityToken; | ||
user_config.run_settings.accessibilityOptions["scannerVersion"] = accessibilityResponse.data.scannerVersion; | ||
user_config.run_settings.system_env_vars.push(`ACCESSIBILITY_AUTH=${accessibilityResponse.data.accessibilityToken}`) | ||
user_config.run_settings.system_env_vars.push(`ACCESSIBILITY_SCANNERVERSION=${accessibilityResponse.data.scannerVersion}`) | ||
} | ||
|
||
exports.isAccessibilitySupportedCypressVersion = (cypress_config_filename) => { | ||
const extension = cypress_config_filename.split('.').pop(); | ||
return CYPRESS_V10_AND_ABOVE_CONFIG_FILE_EXTENSIONS.includes(extension); | ||
} | ||
|
||
exports.createAccessibilityTestRun = async (user_config, framework) => { | ||
|
||
try { | ||
if (!this.isAccessibilitySupportedCypressVersion(user_config.run_settings.cypress_config_file) ){ | ||
logger.warn(`Accessibility Testing is not supported on Cypress version 9 and below.`) | ||
process.env.BROWSERSTACK_TEST_ACCESSIBILITY = 'false'; | ||
user_config.run_settings.accessibility = false; | ||
return; | ||
} | ||
const userName = user_config["auth"]["username"]; | ||
const accessKey = user_config["auth"]["access_key"]; | ||
let settings = utils.isUndefined(user_config.run_settings.accessibilityOptions) ? {} : user_config.run_settings.accessibilityOptions | ||
|
||
const { | ||
buildName, | ||
projectName, | ||
buildDescription | ||
} = helper.getBuildDetails(user_config); | ||
|
||
const data = { | ||
'projectName': projectName, | ||
'buildName': buildName, | ||
'startTime': (new Date()).toISOString(), | ||
'description': buildDescription, | ||
'source': { | ||
frameworkName: "Cypress", | ||
frameworkVersion: helper.getPackageVersion('cypress', user_config), | ||
sdkVersion: helper.getAgentVersion() | ||
}, | ||
'settings': settings, | ||
'versionControl': await helper.getGitMetaData(), | ||
'ciInfo': helper.getCiInfo(), | ||
'hostInfo': { | ||
hostname: os.hostname(), | ||
platform: os.platform(), | ||
type: os.type(), | ||
version: os.version(), | ||
arch: os.arch() | ||
}, | ||
'browserstackAutomation': process.env.BROWSERSTACK_AUTOMATION === 'true' | ||
}; | ||
|
||
const config = { | ||
auth: { | ||
user: userName, | ||
pass: accessKey | ||
}, | ||
headers: { | ||
'Content-Type': 'application/json' | ||
} | ||
}; | ||
|
||
const response = await nodeRequest( | ||
'POST', 'test_runs', data, config, API_URL | ||
); | ||
if(!utils.isUndefined(response.data)) { | ||
process.env.BS_A11Y_JWT = response.data.data.accessibilityToken; | ||
process.env.BS_A11Y_TEST_RUN_ID = response.data.data.id; | ||
} | ||
if (process.env.BS_A11Y_JWT) { | ||
process.env.BROWSERSTACK_TEST_ACCESSIBILITY = 'true'; | ||
} | ||
logger.debug(`BrowserStack Accessibility Automation Test Run ID: ${response.data.data.id}`); | ||
|
||
this.setAccessibilityCypressCapabilities(user_config, response.data); | ||
setAccessibilityEventListeners(); | ||
helper.setBrowserstackCypressCliDependency(user_config); | ||
|
||
} catch (error) { | ||
if (error.response) { | ||
logger.error( | ||
`Exception while creating test run for BrowserStack Accessibility Automation: ${ | ||
error.response.status | ||
} ${error.response.statusText} ${JSON.stringify(error.response.data)}` | ||
); | ||
} else { | ||
if(error.message === 'Invalid configuration passed.') { | ||
logger.error( | ||
`Exception while creating test run for BrowserStack Accessibility Automation: ${ | ||
error.message || error.stack | ||
}` | ||
); | ||
for(const errorkey of error.errors){ | ||
logger.error(errorkey.message); | ||
} | ||
|
||
} else { | ||
logger.error( | ||
`Exception while creating test run for BrowserStack Accessibility Automation: ${ | ||
error.message || error.stack | ||
}` | ||
); | ||
} | ||
// since create accessibility session failed | ||
process.env.BROWSERSTACK_TEST_ACCESSIBILITY = 'false'; | ||
user_config.run_settings.accessibility = false; | ||
} | ||
} | ||
} | ||
|
||
const nodeRequest = (type, url, data, config) => { | ||
return new Promise(async (resolve, reject) => { | ||
const options = {...config,...{ | ||
method: type, | ||
url: `${API_URL}/${url}`, | ||
body: data, | ||
json: config.headers['Content-Type'] === 'application/json', | ||
}}; | ||
|
||
request(options, function callback(error, response, body) { | ||
if(error) { | ||
logger.info("error in nodeRequest", error); | ||
reject(error); | ||
} else if(!(response.statusCode == 201 || response.statusCode == 200)) { | ||
logger.info("response.statusCode in nodeRequest", response.statusCode); | ||
reject(response && response.body ? response.body : `Received response from BrowserStack Server with status : ${response.statusCode}`); | ||
} else { | ||
try { | ||
if(typeof(body) !== 'object') body = JSON.parse(body); | ||
} catch(e) { | ||
if(!url.includes('/stop')) { | ||
reject('Not a JSON response from BrowserStack Server'); | ||
} | ||
} | ||
resolve({ | ||
data: body | ||
}); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
exports.supportFileCleanup = () => { | ||
logger.debug("Cleaning up support file changes added for accessibility. ") | ||
Object.keys(supportFileContentMap).forEach(file => { | ||
try { | ||
fs.writeFileSync(file, supportFileContentMap[file], {encoding: 'utf-8'}); | ||
} catch(e) { | ||
logger.debug(`Error while replacing file content for ${file} with it's original content with error : ${e}`, true, e); | ||
} | ||
}); | ||
} | ||
|
||
const getAccessibilityCypressCommandEventListener = () => { | ||
return ( | ||
`require('browserstack-cypress-cli/bin/accessibility-automation/cypress');` | ||
); | ||
} | ||
|
||
const setAccessibilityEventListeners = () => { | ||
try { | ||
const cypressCommandEventListener = getAccessibilityCypressCommandEventListener(); | ||
glob(process.cwd() + '/cypress/support/*.js', {}, (err, files) => { | ||
if(err) return logger.debug('EXCEPTION IN BUILD START EVENT : Unable to parse cypress support files'); | ||
files.forEach(file => { | ||
try { | ||
if(!file.includes('commands.js')) { | ||
const defaultFileContent = fs.readFileSync(file, {encoding: 'utf-8'}); | ||
|
||
if(!defaultFileContent.includes(cypressCommandEventListener)) { | ||
let newFileContent = defaultFileContent + | ||
'\n' + | ||
cypressCommandEventListener + | ||
'\n' | ||
fs.writeFileSync(file, newFileContent, {encoding: 'utf-8'}); | ||
supportFileContentMap[file] = defaultFileContent; | ||
} | ||
} | ||
} catch(e) { | ||
logger.debug(`Unable to modify file contents for ${file} to set event listeners with error ${e}`, true, e); | ||
} | ||
}); | ||
}); | ||
} catch(e) { | ||
logger.debug(`Unable to parse support files to set event listeners with error ${e}`, true, e); | ||
} | ||
} |
Oops, something went wrong.