Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draft: Build unification for Cypress #839

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 49 additions & 40 deletions bin/accessibility-automation/cypress/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ const browserStackLog = (message) => {
cy.task('browserstack_log', message);
}

const sendToReporter = (data) => {
cy.task('test_accessibility_data', data);
}

const commandsToWrap = ['visit', 'click', 'type', 'request', 'dblclick', 'rightclick', 'clear', 'check', 'uncheck', 'select', 'trigger', 'selectFile', 'scrollIntoView', 'scroll', 'scrollTo', 'blur', 'focus', 'go', 'reload', 'submit', 'viewport', 'origin'];

const performScan = (win, payloadToSend) =>
Expand Down Expand Up @@ -250,8 +254,11 @@ const shouldScanForAccessibility = (attributes) => {
if (Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY")) {
excludeTagArray = Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY").split(";")
}
browserStackLog("EXCLUDE_TAGS_FOR_ACCESSIBILITY = " + excludeTagArray)
browserStackLog("INCLUDE_TAGS_FOR_ACCESSIBILITY = " + includeTagArray)

const fullTestName = attributes.title;
browserStackLog("fullTestName = " + fullTestName)
const excluded = excludeTagArray.some((exclude) => fullTestName.includes(exclude));
const included = includeTagArray.length === 0 || includeTags.some((include) => fullTestName.includes(include));
shouldScanTestForAccessibility = !excluded && included;
Expand All @@ -274,6 +281,7 @@ Cypress.on('command:start', async (command) => {
const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable;

let shouldScanTestForAccessibility = shouldScanForAccessibility(attributes);
sendToReporter({[attributes.title]: shouldScanTestForAccessibility})
if (!shouldScanTestForAccessibility) return;

cy.window().then((win) => {
Expand All @@ -284,47 +292,48 @@ Cypress.on('command:start', async (command) => {

afterEach(() => {
const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest;
cy.window().then(async (win) => {
let shouldScanTestForAccessibility = shouldScanForAccessibility(attributes);
if (!shouldScanTestForAccessibility) return cy.wrap({});

cy.wrap(performScan(win), {timeout: 30000}).then(() => {
try {
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 payloadToSend = {
"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
}
};
browserStackLog(`Saving accessibility test results`);
cy.wrap(saveTestResults(win, payloadToSend), {timeout: 30000}).then(() => {
browserStackLog(`Saved accessibility test results`);
})
cy.task('readFileMaybe', 'testDetails.json').then(data => {
browserStackLog('FILE CONTENT ::::: ' + data)
if (data === null) return;

} catch (er) {
}
let testDetails = {}
try {
testDetails = JSON.parse(data);
} catch (err) {
browserStackLog('Error while parsing json for testDetails:' + err)
}
const testId = testDetails[attributes.fullTitle()]
browserStackLog('TestId : ' + testId)
cy.window().then(async (win) => {
let shouldScanTestForAccessibility = shouldScanForAccessibility(attributes);
sendToReporter({[attributes.title]: shouldScanTestForAccessibility})
if (!shouldScanTestForAccessibility) return cy.wrap({});

cy.wrap(performScan(win), {timeout: 30000}).then(() => {
try {
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 payloadToSend = {
'thTestRunUuid': testId,
'thBuildUuid': Cypress.env("BROWSERSTACK_TESTHUB_UUID"),
'thJwtToken': Cypress.env('BROWSERSTACK_TESTHUB_JWT')
};
browserStackLog(`Saving accessibility test results`);
cy.wrap(saveTestResults(win, payloadToSend), {timeout: 30000}).then(() => {
browserStackLog(`Saved accessibility test results`);
})

} catch (er) {
}
})
})
});
})
Expand Down
21 changes: 21 additions & 0 deletions bin/accessibility-automation/plugin/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@

const path = require("node:path");
const fs = require('node:fs');
const ipc = require('node-ipc');
const { connectIPCClient } = require('../../testObservability/plugin/ipcClient');
const { IPC_EVENTS } = require('../../testObservability/helper/constants');

const browserstackAccessibility = (on, config) => {
connectIPCClient(config);

let browser_validation = true;
if (process.env.BROWSERSTACK_ACCESSIBILITY_DEBUG === 'true') {
config.env.BROWSERSTACK_LOGS = 'true';
Expand All @@ -13,6 +19,19 @@ const browserstackAccessibility = (on, config) => {

return null
},

test_accessibility_data(data) {
ipc.of.browserstackTestObservability.emit(IPC_EVENTS.ACCESSIBILITY_DATA, data);
return null;
},

readFileMaybe(filename) {
if (fs.existsSync(filename)) {
return fs.readFileSync(filename, 'utf8')
}

return null
}
})
on('before:browser:launch', (browser = {}, launchOptions) => {
try {
Expand Down Expand Up @@ -41,6 +60,8 @@ const browserstackAccessibility = (on, config) => {
config.env.ACCESSIBILITY_EXTENSION_PATH = process.env.ACCESSIBILITY_EXTENSION_PATH
config.env.OS_VERSION = process.env.OS_VERSION
config.env.OS = process.env.OS
config.env.BROWSERSTACK_TESTHUB_UUID = process.env.BROWSERSTACK_TESTHUB_UUID
config.env.BROWSERSTACK_TESTHUB_JWT = process.env.BROWSERSTACK_TESTHUB_JWT

config.env.IS_ACCESSIBILITY_EXTENSION_LOADED = browser_validation.toString()

Expand Down
13 changes: 7 additions & 6 deletions bin/commands/runs.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const {
supportFileCleanup
} = require('../accessibility-automation/helper');
const { isTurboScaleSession, getTurboScaleGridDetails, patchCypressConfigFileContent, atsFileCleanup } = require('../helpers/atsHelper');
const TestHubHandler = require('../testhub/testhubHandler');
const { shouldProcessEventForTesthub, checkAndSetAccessibility } = require('../testhub/utils');

module.exports = function run(args, rawArgs) {

Expand Down Expand Up @@ -75,6 +77,7 @@ module.exports = function run(args, rawArgs) {
const turboScaleSession = isTurboScaleSession(bsConfig);
Constants.turboScaleObj.enabled = turboScaleSession;

checkAndSetAccessibility(bsConfig);
utils.setUsageReportingFlag(bsConfig, args.disableUsageReporting);

utils.setDefaults(bsConfig, args);
Expand Down Expand Up @@ -114,10 +117,11 @@ module.exports = function run(args, rawArgs) {
utils.setBuildTags(bsConfig, args);

/*
Send build start to Observability
Send build start to testHub
*/
if(isTestObservabilitySession) {
await launchTestSession(bsConfig, bsConfigPath);
if(shouldProcessEventForTesthub()) {
await TestHubHandler.launchBuild(bsConfig, bsConfigPath);
// await launchTestSession(bsConfig, bsConfigPath);
utils.setO11yProcessHooks(null, bsConfig, args, null, buildReportData);
}

Expand Down Expand Up @@ -149,9 +153,6 @@ module.exports = function run(args, rawArgs) {
// add cypress dependency if missing
utils.setCypressNpmDependency(bsConfig);

if (isAccessibilitySession && isBrowserstackInfra) {
await createAccessibilityTestRun(bsConfig);
}

if (turboScaleSession) {
// Local is only required in case user is running on trial grid and wants to access private website.
Expand Down
13 changes: 12 additions & 1 deletion bin/helpers/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,17 @@ exports.getGitMetaData = () => {
}
})
}

exports.getHostInfo = () => {
return {
hostname: os.hostname(),
platform: os.platform(),
type: os.type(),
version: os.version(),
arch: os.arch()
}
}

exports.getCiInfo = () => {
var env = process.env;
// Jenkins
Expand Down Expand Up @@ -318,7 +329,7 @@ exports.setBrowserstackCypressCliDependency = (bsConfig) => {
typeof runSettings.npm_dependencies === 'object') {
if (!("browserstack-cypress-cli" in runSettings.npm_dependencies)) {
logger.warn("Missing browserstack-cypress-cli not found in npm_dependencies");
runSettings.npm_dependencies['browserstack-cypress-cli'] = this.getAgentVersion() || "latest";
runSettings.npm_dependencies['browserstack-cypress-cli'] = "/Users/saurav/Home/Browserstack/Automate/cypress_cli_main/browserstack-cypress-cli/browserstack-cypress-cli-1.29.1.tgz";
logger.warn(`Adding browserstack-cypress-cli version ${runSettings.npm_dependencies['browserstack-cypress-cli']} in npm_dependencies`);
}
}
Expand Down
3 changes: 2 additions & 1 deletion bin/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const usageReporting = require("./usageReporting"),
{ OBSERVABILITY_ENV_VARS, TEST_OBSERVABILITY_REPORTER } = require('../testObservability/helper/constants');

const request = require('request');
const { shouldProcessEventForTesthub } = require("../testhub/utils");

exports.validateBstackJson = (bsConfigPath) => {
return new Promise(function (resolve, reject) {
Expand Down Expand Up @@ -1465,7 +1466,7 @@ exports.splitStringByCharButIgnoreIfWithinARange = (str, splitChar, leftLimiter,

// blindly send other passed configs with run_settings and handle at backend
exports.setOtherConfigs = (bsConfig, args) => {
if(o11yHelpers.isTestObservabilitySession() && process.env.BS_TESTOPS_JWT) {
if(shouldProcessEventForTesthub()) {
bsConfig["run_settings"]["reporter"] = TEST_OBSERVABILITY_REPORTER;
return;
}
Expand Down
3 changes: 2 additions & 1 deletion bin/testObservability/helper/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ exports.IPC_EVENTS = {
SCREENSHOT: 'testObservability:cypressScreenshot',
COMMAND: 'testObservability:cypressCommand',
CUCUMBER: 'testObservability:cypressCucumberStep',
PLATFORM_DETAILS: 'testObservability:cypressPlatformDetails'
PLATFORM_DETAILS: 'testObservability:cypressPlatformDetails',
ACCESSIBILITY_DATA: 'accessibility:cypressAccessibilityData'
};

exports.OBSERVABILITY_ENV_VARS = [
Expand Down
38 changes: 25 additions & 13 deletions bin/testObservability/helper/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ exports.printBuildLink = async (shouldStopSession, exitCode = null) => {
if(exitCode) process.exit(exitCode);
}

const nodeRequest = (type, url, data, config) => {
exports.nodeRequest = (type, url, data, config) => {
return new Promise(async (resolve, reject) => {
const options = {...config,...{
method: type,
Expand Down Expand Up @@ -242,7 +242,7 @@ exports.getPackageVersion = (package_, bsConfig = null) => {
return packageVersion;
}

const setEnvironmentVariablesForRemoteReporter = (BS_TESTOPS_JWT, BS_TESTOPS_BUILD_HASHED_ID, BS_TESTOPS_ALLOW_SCREENSHOTS, OBSERVABILITY_LAUNCH_SDK_VERSION) => {
exports.setEnvironmentVariablesForRemoteReporter = (BS_TESTOPS_JWT, BS_TESTOPS_BUILD_HASHED_ID, BS_TESTOPS_ALLOW_SCREENSHOTS, OBSERVABILITY_LAUNCH_SDK_VERSION) => {
process.env.BS_TESTOPS_JWT = BS_TESTOPS_JWT;
process.env.BS_TESTOPS_BUILD_HASHED_ID = BS_TESTOPS_BUILD_HASHED_ID;
process.env.BS_TESTOPS_ALLOW_SCREENSHOTS = BS_TESTOPS_ALLOW_SCREENSHOTS;
Expand Down Expand Up @@ -316,7 +316,7 @@ exports.setCrashReportingConfigFromReporter = (credentialsStr, bsConfigPath, cyp
}
}

const setCrashReportingConfig = (bsConfig, bsConfigPath) => {
exports.setCrashReportingConfig = (bsConfig, bsConfigPath) => {
try {
const browserstackConfigFile = utils.readBsConfigJSON(bsConfigPath);
const cypressConfigFile = getCypressConfigFileContent(bsConfig, null);
Expand All @@ -334,7 +334,7 @@ const setCrashReportingConfig = (bsConfig, bsConfigPath) => {
}

exports.launchTestSession = async (user_config, bsConfigPath) => {
setCrashReportingConfig(user_config, bsConfigPath);
exports.setCrashReportingConfig(user_config, bsConfigPath);

const obsUserName = user_config["auth"]["username"];
const obsAccessKey = user_config["auth"]["access_key"];
Expand Down Expand Up @@ -387,10 +387,10 @@ exports.launchTestSession = async (user_config, bsConfigPath) => {
}
};

const response = await nodeRequest('POST','api/v1/builds',data,config);
const response = await exports.nodeRequest('POST','api/v1/builds',data,config);
exports.debug('Build creation successfull!');
process.env.BS_TESTOPS_BUILD_COMPLETED = true;
setEnvironmentVariablesForRemoteReporter(response.data.jwt, response.data.build_hashed_id, response.data.allow_screenshots, data.observability_version.sdkVersion);
exports.setEnvironmentVariablesForRemoteReporter(response.data.jwt, response.data.build_hashed_id, response.data.allow_screenshots, data.observability_version.sdkVersion);
if(this.isBrowserstackInfra()) helper.setBrowserstackCypressCliDependency(user_config);
} catch(error) {
if(!error.errorType) {
Expand All @@ -417,7 +417,7 @@ exports.launchTestSession = async (user_config, bsConfigPath) => {
}

process.env.BS_TESTOPS_BUILD_COMPLETED = false;
setEnvironmentVariablesForRemoteReporter(null, null, null);
exports.setEnvironmentVariablesForRemoteReporter(null, null, null);
}
}
}
Expand Down Expand Up @@ -474,7 +474,7 @@ exports.batchAndPostEvents = async (eventUrl, kind, data) => {
};

try {
const response = await nodeRequest('POST',eventUrl,data,config);
const response = await exports.nodeRequest('POST',eventUrl,data,config);
if(response.data.error) {
throw({message: response.data.error});
} else {
Expand All @@ -491,6 +491,18 @@ exports.batchAndPostEvents = async (eventUrl, kind, data) => {
}
}

const shouldUploadEvent = (eventType) => {
isAccessibility = utils.isTrueString(process.env.BROWSERSTACK_TEST_ACCESSIBILITY) || !utils.isUndefined(process.env.ACCESSIBILITY_AUTH);
if(!this.isTestObservabilitySession() || isAccessibility) {
if (['HookRunStarted', 'HookRunFinished', 'LogCreated', 'BuildUpdate'].includes(eventType)) {
return false;
}
return true;
}

return this.isTestObservabilitySession() || isAccessibility;
}

const RequestQueueHandler = require('./requestQueueHandler');
exports.requestQueueHandler = new RequestQueueHandler();

Expand All @@ -506,9 +518,9 @@ exports.uploadEventData = async (eventData, run=0) => {
['BuildUpdate']: 'Build_Update'
}[eventData.event_type];

if (!shouldUploadEvent(eventData.event_type)) return;
if(run === 0 && process.env.BS_TESTOPS_JWT != "null") exports.pending_test_uploads.count += 1;

if (process.env.BS_TESTOPS_BUILD_COMPLETED === "true") {
if (process.env.BS_TESTOPS_BUILD_COMPLETED === "true" || process.env.ACCESSIBILITY_AUTH) {
if(process.env.BS_TESTOPS_JWT == "null") {
exports.debug(`EXCEPTION IN ${log_tag} REQUEST TO TEST OBSERVABILITY : missing authentication token`);
exports.pending_test_uploads.count = Math.max(0,exports.pending_test_uploads.count-1);
Expand Down Expand Up @@ -537,7 +549,7 @@ exports.uploadEventData = async (eventData, run=0) => {
};

try {
const response = await nodeRequest('POST',event_api_url,data,config);
const response = await exports.nodeRequest('POST',event_api_url,data,config);
if(response.data.error) {
throw({message: response.data.error});
} else {
Expand Down Expand Up @@ -626,7 +638,7 @@ exports.shouldReRunObservabilityTests = () => {
}

exports.stopBuildUpstream = async () => {
if (process.env.BS_TESTOPS_BUILD_COMPLETED === "true") {
if (process.env.BS_TESTOPS_BUILD_COMPLETED === "true" || process.env.ACCESSIBILITY_AUTH) {
if(process.env.BS_TESTOPS_JWT == "null" || process.env.BS_TESTOPS_BUILD_HASHED_ID == "null") {
exports.debug('EXCEPTION IN stopBuildUpstream REQUEST TO TEST OBSERVABILITY : Missing authentication token');
return {
Expand All @@ -646,7 +658,7 @@ exports.stopBuildUpstream = async () => {
};

try {
const response = await nodeRequest('PUT',`api/v1/builds/${process.env.BS_TESTOPS_BUILD_HASHED_ID}/stop`,data,config);
const response = await exports.nodeRequest('PUT',`api/v1/builds/${process.env.BS_TESTOPS_BUILD_HASHED_ID}/stop`,data,config);
if(response.data && response.data.error) {
throw({message: response.data.error});
} else {
Expand Down
Loading
Loading