Skip to content
35 changes: 16 additions & 19 deletions bin/accessibility-automation/cypress/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,26 +336,23 @@ afterEach(() => {
} else if (attributes.prevAttempts && attributes.prevAttempts.length > 0) {
filePath = (attributes.prevAttempts[0].invocationDetails && attributes.prevAttempts[0].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

let testRunUuid = null;
cy.task('get_test_run_uuid', { testIdentifier: attributes.title })
.then((response) => {
if (response && response.testRunUuid) {
testRunUuid = response.testRunUuid;
}
};
browserStackLog(`Saving accessibility test results`);
cy.wrap(saveTestResults(win, payloadToSend), {timeout: 30000}).then(() => {

const payloadToSend = {
"thTestRunUuid": testRunUuid,
"thBuildUuid": Cypress.env("BROWSERSTACK_TESTHUB_UUID"),
"thJwtToken": Cypress.env("BROWSERSTACK_TESTHUB_JWT")
};
browserStackLog(`Payload to send: ${JSON.stringify(payloadToSend)}`);

return cy.wrap(saveTestResults(win, payloadToSend), {timeout: 30000});
}).then(() => {
browserStackLog(`Saved accessibility test results`);
})

Expand Down
49 changes: 49 additions & 0 deletions bin/accessibility-automation/plugin/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const path = require("node:path");
const { decodeJWTToken } = require("../../helpers/utils");
const utils = require('../../helpers/utils');
const http = require('http');

const browserstackAccessibility = (on, config) => {
let browser_validation = true;
Expand All @@ -14,6 +15,50 @@

return null
},
get_test_run_uuid({ testIdentifier, retries = 15, interval = 300 } = {}) {
return new Promise((resolve) => {
if(!testIdentifier) return resolve(null);
const port = process.env.REPORTER_API_PORT_NO;
let attempt = 0;
const fetchUuid = () => {
const options = {
hostname: '127.0.0.1',
port,
path: `/test-uuid?testIdentifier=${encodeURIComponent(testIdentifier)}`,
method: 'GET',
timeout: 2000
};
const req = http.request(options, (res) => {

Check warning

Code scanning / Semgrep OSS

Semgrep Finding: problem-based-packs.insecure-transport.js-node.using-http-server.using-http-server Warning

Checks for any usage of http servers instead of https servers. Encourages the usage of https protocol instead of http, which does not have TLS and is therefore unencrypted. Using http can lead to man-in-the-middle attacks in which the attacker is able to read sensitive information.
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => {
if(res.statusCode === 200) {
try {
const json = JSON.parse(data || '{}');
return resolve({ testRunUuid: json.testRunUuid || null });
} catch(e) {
return resolve(null);
}
} else if (res.statusCode === 404) {
// Server up but endpoint not responding as expected – stop retrying.
return resolve(null);
} else {
retryOrResolve();
}
});
});
Comment on lines +24 to +49

Check warning

Code scanning / Semgrep OSS

Semgrep Finding: problem-based-packs.insecure-transport.js-node.http-request.http-request Warning

Checks for requests sent to http:// URLs. This is dangerous as the server is attempting to connect to a website that does not encrypt traffic with TLS. Instead, only send requests to https:// URLs.
req.on('error', () => retryOrResolve());
req.on('timeout', () => { req.destroy(); retryOrResolve(); });
req.end();
};
const retryOrResolve = () => {
attempt += 1;
if(attempt >= retries) return resolve(null);
setTimeout(fetchUuid, interval);
};
fetchUuid();
});
}
})
on('before:browser:launch', (browser = {}, launchOptions) => {
try {
Expand Down Expand Up @@ -51,6 +96,10 @@
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.BROWSERSTACK_TESTHUB_API_PORT = process.env.BROWSERSTACK_TESTHUB_API_PORT
config.env.REPORTER_API_PORT_NO = process.env.REPORTER_API_PORT_NO

config.env.IS_ACCESSIBILITY_EXTENSION_LOADED = browser_validation.toString()

Expand Down
17 changes: 10 additions & 7 deletions bin/commands/runs.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ const {
supportFileCleanup
} = require('../accessibility-automation/helper');
const { isTurboScaleSession, getTurboScaleGridDetails, patchCypressConfigFileContent, atsFileCleanup } = require('../helpers/atsHelper');

const { shouldProcessEventForTesthub, checkAndSetAccessibility, findAvailablePort } = require('../testhub/utils');
const TestHubHandler = require('../testhub/testhubHandler');

module.exports = function run(args, rawArgs) {
utils.normalizeTestReportingEnvVars();
Expand Down Expand Up @@ -112,9 +113,15 @@ module.exports = function run(args, rawArgs) {
// set build tag caps
utils.setBuildTags(bsConfig, args);

checkAndSetAccessibility(bsConfig, isAccessibilitySession);

const preferredPort = 5348;
const port = await findAvailablePort(preferredPort);
process.env.REPORTER_API_PORT_NO = port

// Send build start to TEST REPORTING AND ANALYTICS
if(isTestObservabilitySession) {
await launchTestSession(bsConfig, bsConfigPath);
if(shouldProcessEventForTesthub()) {
await TestHubHandler.launchBuild(bsConfig, bsConfigPath);
utils.setO11yProcessHooks(null, bsConfig, args, null, buildReportData);
}

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

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

if (turboScaleSession) {
const gridDetails = await getTurboScaleGridDetails(bsConfig, args, rawArgs);

Expand Down
11 changes: 11 additions & 0 deletions bin/helpers/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,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
3 changes: 2 additions & 1 deletion bin/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const usageReporting = require("./usageReporting"),
{ OBSERVABILITY_ENV_VARS, TEST_OBSERVABILITY_REPORTER } = require('../testObservability/helper/constants');

const { default: axios } = require("axios");
const { shouldProcessEventForTesthub } = require("../testhub/utils");

exports.validateBstackJson = (bsConfigPath) => {
return new Promise(function (resolve, reject) {
Expand Down Expand Up @@ -1499,7 +1500,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
18 changes: 9 additions & 9 deletions bin/testObservability/helper/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ exports.printBuildLink = async (shouldStopSession, exitCode = null) => {
if(exitCode) process.exit(exitCode);
}

const nodeRequest = (type, url, data, config) => {
exports.nodeRequest = (type, url, data, config) => {
const requestQueueHandler = require('./requestQueueHandler');
return new Promise(async (resolve, reject) => {
const options = {
Expand Down Expand Up @@ -269,7 +269,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 @@ -343,7 +343,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 Down Expand Up @@ -414,10 +414,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() && (user_config.run_settings.auto_import_dev_dependencies != true)) helper.setBrowserstackCypressCliDependency(user_config);
} catch(error) {
if(!error.errorType) {
Expand All @@ -444,7 +444,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 @@ -503,7 +503,7 @@ exports.batchAndPostEvents = async (eventUrl, kind, data) => {
try {
const eventsUuids = data.map(eventData => `${eventData.event_type}:${eventData.test_run ? eventData.test_run.uuid : (eventData.hook_run ? eventData.hook_run.uuid : null)}`).join(', ');
exports.debugOnConsole(`[Request Batch Send] for events:uuids ${eventsUuids}`);
const response = await nodeRequest('POST',eventUrl,data,config);
const response = await exports.nodeRequest('POST',eventUrl,data,config);
exports.debugOnConsole(`[Request Batch Response] for events:uuids ${eventsUuids}`);
if(response.data.error) {
throw({message: response.data.error});
Expand Down Expand Up @@ -570,7 +570,7 @@ exports.uploadEventData = async (eventData, run=0) => {
try {
const eventsUuids = data.map(eventData => `${eventData.event_type}:${eventData.test_run ? eventData.test_run.uuid : (eventData.hook_run ? eventData.hook_run.uuid : null)}`).join(', ');
exports.debugOnConsole(`[Request Send] for events:uuids ${eventsUuids}`);
const response = await nodeRequest('POST',event_api_url,data,config);
const response = await exports.nodeRequest('POST',event_api_url,data,config);
exports.debugOnConsole(`[Request Repsonse] ${util.format(response.data)} for events:uuids ${eventsUuids}`)
if(response.data.error) {
throw({message: response.data.error});
Expand Down Expand Up @@ -681,7 +681,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