diff --git a/bin/accessibility-automation/cypress/index.js b/bin/accessibility-automation/cypress/index.js index 78e9c388..d5e6a0bd 100644 --- a/bin/accessibility-automation/cypress/index.js +++ b/bin/accessibility-automation/cypress/index.js @@ -336,23 +336,26 @@ afterEach(() => { } else if (attributes.prevAttempts && attributes.prevAttempts.length > 0) { filePath = (attributes.prevAttempts[0].invocationDetails && attributes.prevAttempts[0].invocationDetails.relativeFile) || ''; } - - let testRunUuid = null; - cy.task('get_test_run_uuid', { testIdentifier: attributes.title }) - .then((response) => { - if (response && response.testRunUuid) { - testRunUuid = response.testRunUuid; + 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 } - - 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(`Saving accessibility test results`); + cy.wrap(saveTestResults(win, payloadToSend), {timeout: 30000}).then(() => { browserStackLog(`Saved accessibility test results`); }) diff --git a/bin/accessibility-automation/plugin/index.js b/bin/accessibility-automation/plugin/index.js index 5ea42676..4c35ef99 100644 --- a/bin/accessibility-automation/plugin/index.js +++ b/bin/accessibility-automation/plugin/index.js @@ -1,7 +1,6 @@ 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; @@ -15,51 +14,6 @@ const browserstackAccessibility = (on, config) => { 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 httpModule = http; - const req = httpModule.request(options, (res) => { - 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(); - } - }); - }); - 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 { @@ -97,10 +51,6 @@ 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.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() diff --git a/bin/commands/runs.js b/bin/commands/runs.js index 8e7fba91..103edfba 100644 --- a/bin/commands/runs.js +++ b/bin/commands/runs.js @@ -37,8 +37,7 @@ 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(); @@ -113,15 +112,9 @@ 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(shouldProcessEventForTesthub()) { - await TestHubHandler.launchBuild(bsConfig, bsConfigPath); + if(isTestObservabilitySession) { + await launchTestSession(bsConfig, bsConfigPath); utils.setO11yProcessHooks(null, bsConfig, args, null, buildReportData); } @@ -156,6 +149,10 @@ 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); diff --git a/bin/helpers/helper.js b/bin/helpers/helper.js index e7c32f49..b839c76b 100644 --- a/bin/helpers/helper.js +++ b/bin/helpers/helper.js @@ -182,17 +182,6 @@ 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 diff --git a/bin/helpers/utils.js b/bin/helpers/utils.js index 4be9125f..c451378b 100644 --- a/bin/helpers/utils.js +++ b/bin/helpers/utils.js @@ -27,7 +27,6 @@ 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) { @@ -1500,7 +1499,7 @@ exports.splitStringByCharButIgnoreIfWithinARange = (str, splitChar, leftLimiter, // blindly send other passed configs with run_settings and handle at backend exports.setOtherConfigs = (bsConfig, args) => { - if(shouldProcessEventForTesthub()) { + if(o11yHelpers.isTestObservabilitySession() && process.env.BS_TESTOPS_JWT) { bsConfig["run_settings"]["reporter"] = TEST_OBSERVABILITY_REPORTER; return; } diff --git a/bin/testObservability/helper/helper.js b/bin/testObservability/helper/helper.js index 8d7ad133..69a1a2b9 100644 --- a/bin/testObservability/helper/helper.js +++ b/bin/testObservability/helper/helper.js @@ -116,7 +116,7 @@ exports.printBuildLink = async (shouldStopSession, exitCode = null) => { if(exitCode) process.exit(exitCode); } -exports.nodeRequest = (type, url, data, config) => { +const nodeRequest = (type, url, data, config) => { const requestQueueHandler = require('./requestQueueHandler'); return new Promise(async (resolve, reject) => { const options = { @@ -269,7 +269,7 @@ exports.getPackageVersion = (package_, bsConfig = null) => { return packageVersion; } -exports.setEnvironmentVariablesForRemoteReporter = (BS_TESTOPS_JWT, BS_TESTOPS_BUILD_HASHED_ID, BS_TESTOPS_ALLOW_SCREENSHOTS, OBSERVABILITY_LAUNCH_SDK_VERSION) => { +const 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; @@ -343,7 +343,7 @@ exports.setCrashReportingConfigFromReporter = (credentialsStr, bsConfigPath, cyp } } -exports.setCrashReportingConfig = (bsConfig, bsConfigPath) => { +const setCrashReportingConfig = (bsConfig, bsConfigPath) => { try { const browserstackConfigFile = utils.readBsConfigJSON(bsConfigPath); const cypressConfigFile = getCypressConfigFileContent(bsConfig, null); @@ -414,10 +414,10 @@ exports.launchTestSession = async (user_config, bsConfigPath) => { } }; - const response = await exports.nodeRequest('POST','api/v1/builds',data,config); + const response = await nodeRequest('POST','api/v1/builds',data,config); exports.debug('Build creation successfull!'); process.env.BS_TESTOPS_BUILD_COMPLETED = true; - exports.setEnvironmentVariablesForRemoteReporter(response.data.jwt, response.data.build_hashed_id, response.data.allow_screenshots, data.observability_version.sdkVersion); + 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) { @@ -444,7 +444,7 @@ exports.launchTestSession = async (user_config, bsConfigPath) => { } process.env.BS_TESTOPS_BUILD_COMPLETED = false; - exports.setEnvironmentVariablesForRemoteReporter(null, null, null); + setEnvironmentVariablesForRemoteReporter(null, null, null); } } } @@ -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 exports.nodeRequest('POST',eventUrl,data,config); + const response = await nodeRequest('POST',eventUrl,data,config); exports.debugOnConsole(`[Request Batch Response] for events:uuids ${eventsUuids}`); if(response.data.error) { throw({message: response.data.error}); @@ -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 exports.nodeRequest('POST',event_api_url,data,config); + const response = await 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}); @@ -681,7 +681,7 @@ exports.stopBuildUpstream = async () => { }; try { - const response = await exports.nodeRequest('PUT',`api/v1/builds/${process.env.BS_TESTOPS_BUILD_HASHED_ID}/stop`,data,config); + const response = await 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 { diff --git a/bin/testObservability/reporter/index.js b/bin/testObservability/reporter/index.js index 242a2d9b..5885e020 100644 --- a/bin/testObservability/reporter/index.js +++ b/bin/testObservability/reporter/index.js @@ -11,7 +11,6 @@ const Mocha = requireModule('mocha'); // const Runnable = requireModule('mocha/lib/runnable'); const Runnable = require('mocha/lib/runnable'); // need to handle as this isn't present in older mocha versions const { v4: uuidv4 } = require('uuid'); -const http = require('http'); const { IPC_EVENTS, TEST_REPORTING_ANALYTICS } = require('../helper/constants'); const { startIPCServer } = require('../plugin/ipcServer'); @@ -62,7 +61,6 @@ const { } = require('../helper/helper'); const { consoleHolder } = require('../helper/constants'); -const { shouldProcessEventForTesthub } = require('../../testhub/utils'); // this reporter outputs test results, indenting two spaces per suite class MyReporter { @@ -72,15 +70,12 @@ class MyReporter { this._testEnv = getTestEnv(); this._paths = new PathHelper({ cwd: process.cwd() }, this._testEnv.location_prefix); this.currentTestSteps = []; - this.httpServer = null; this.currentTestCucumberSteps = []; this.hooksStarted = {}; this.beforeHooks = []; - this.testIdMap = {}; this.platformDetailsMap = {}; this.runStatusMarkedHash = {}; this.haveSentBuildUpdate = false; - this.startHttpServer(); this.registerListeners(); setCrashReportingConfigFromReporter(null, process.env.OBS_CRASH_REPORTING_BS_CONFIG_PATH, process.env.OBS_CRASH_REPORTING_CYPRESS_CONFIG_PATH); @@ -94,7 +89,7 @@ class MyReporter { .on(EVENT_HOOK_BEGIN, async (hook) => { if (this.isInternalHook(hook)) return; debugOnConsole(`[MOCHA EVENT] EVENT_HOOK_BEGIN`); - if(shouldProcessEventForTesthub()) { + if(this.testObservability == true) { if(!hook.hookAnalyticsId) { hook.hookAnalyticsId = uuidv4(); } else if(this.runStatusMarkedHash[hook.hookAnalyticsId]) { @@ -112,7 +107,7 @@ class MyReporter { .on(EVENT_HOOK_END, async (hook) => { if (this.isInternalHook(hook)) return; debugOnConsole(`[MOCHA EVENT] EVENT_HOOK_END`); - if(shouldProcessEventForTesthub()) { + if(this.testObservability == true) { if(!this.runStatusMarkedHash[hook.hookAnalyticsId]) { if(!hook.hookAnalyticsId) { /* Hook objects don't maintain uuids in Cypress-Mocha */ @@ -137,7 +132,7 @@ class MyReporter { .on(EVENT_TEST_PASS, async (test) => { debugOnConsole(`[MOCHA EVENT] EVENT_TEST_PASS`); - if(shouldProcessEventForTesthub()) { + if(this.testObservability == true) { debugOnConsole(`[MOCHA EVENT] EVENT_TEST_PASS for uuid: ${test.testAnalyticsId}`); if(!this.runStatusMarkedHash[test.testAnalyticsId]) { if(test.testAnalyticsId) this.runStatusMarkedHash[test.testAnalyticsId] = true; @@ -148,7 +143,7 @@ class MyReporter { .on(EVENT_TEST_FAIL, async (test, err) => { debugOnConsole(`[MOCHA EVENT] EVENT_TEST_FAIL`); - if(shouldProcessEventForTesthub()) { + if(this.testObservability == true) { debugOnConsole(`[MOCHA EVENT] EVENT_TEST_FAIL for uuid: ${test.testAnalyticsId}`); if((test.testAnalyticsId && !this.runStatusMarkedHash[test.testAnalyticsId]) || (test.hookAnalyticsId && !this.runStatusMarkedHash[test.hookAnalyticsId])) { if(test.testAnalyticsId) { @@ -164,7 +159,7 @@ class MyReporter { .on(EVENT_TEST_PENDING, async (test) => { debugOnConsole(`[MOCHA EVENT] EVENT_TEST_PENDING`); - if(shouldProcessEventForTesthub()) { + if(this.testObservability == true) { if(!test.testAnalyticsId) test.testAnalyticsId = uuidv4(); debugOnConsole(`[MOCHA EVENT] EVENT_TEST_PENDING for uuid: ${test.testAnalyticsId}`); if(!this.runStatusMarkedHash[test.testAnalyticsId]) { @@ -177,7 +172,7 @@ class MyReporter { .on(EVENT_TEST_BEGIN, async (test) => { debugOnConsole(`[MOCHA EVENT] EVENT_TEST_BEGIN for uuid: ${test.testAnalyticsId}`); if (this.runStatusMarkedHash[test.testAnalyticsId]) return; - if(shouldProcessEventForTesthub()) { + if(this.testObservability == true) { await this.testStarted(test); } }) @@ -185,7 +180,7 @@ class MyReporter { .on(EVENT_TEST_END, async (test) => { debugOnConsole(`[MOCHA EVENT] EVENT_TEST_BEGIN for uuid: ${test.testAnalyticsId}`); if (this.runStatusMarkedHash[test.testAnalyticsId]) return; - if(shouldProcessEventForTesthub()) { + if(this.testObservability == true) { if(!this.runStatusMarkedHash[test.testAnalyticsId]) { if(test.testAnalyticsId) this.runStatusMarkedHash[test.testAnalyticsId] = true; await this.sendTestRunEvent(test); @@ -196,7 +191,7 @@ class MyReporter { .once(EVENT_RUN_END, async () => { try { debugOnConsole(`[MOCHA EVENT] EVENT_RUN_END`); - if(shouldProcessEventForTesthub()) { + if(this.testObservability == true) { const hookSkippedTests = getHookSkippedTests(this.runner.suite); for(const test of hookSkippedTests) { if(!test.testAnalyticsId) test.testAnalyticsId = uuidv4(); @@ -219,66 +214,6 @@ class MyReporter { return false; } - async startHttpServer() { - if(this.httpServer !== null) return; - - try { - const httpModule = http; - this.httpServer = httpModule.createServer(async(req, res) => { - try { - // Set CORS headers - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - - if (req.method === 'OPTIONS') { - res.writeHead(200); - res.end(); - return; - } - const parsedUrl = new URL(req.url, `http://${req.headers.host}`); - const pathname = parsedUrl.pathname; - const query = parsedUrl.searchParams; - - if (pathname === '/test-uuid' && req.method === 'GET') { - const testIdentifier = query.get('testIdentifier'); - - if (!testIdentifier) { - res.writeHead(400, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify({ - error: 'testIdentifier parameter is required', - testRunUuid: null - })); - return; - } - const testRunUuid = this.getTestId(testIdentifier); - - - res.writeHead(200, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify({ testRunUuid: testRunUuid })); - } else { - res.writeHead(404, { 'Content-Type': 'text/plain' }); - res.end('Not Found'); - } - } catch (error) { - debugOnConsole(`Exception in handling HTTP request : ${error}`); - debug(`Exception in handling HTTP request : ${error}`, true, error); - res.writeHead(500, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify({ testRunUuid: null })); - } - }); - - const port = process.env.REPORTER_API_PORT_NO; - - this.httpServer.listen(port, '127.0.0.1', async () => { - console.log(`Reporter HTTP server listening on port ${port}`); - }); - } catch (error) { - debugOnConsole(`Exception in starting reporter server : ${error}`); - debug(`Exception in starting reporter server : ${error}`, true, error); - } - } - registerListeners() { startIPCServer( (server) => { @@ -410,8 +345,6 @@ class MyReporter { debugOnConsole(`${eventType} for uuid: ${testData.uuid}`); - this.mapTestId(testData, eventType); - if(eventType.match(/TestRunFinished/) || eventType.match(/TestRunSkipped/)) { testData['meta'].steps = JSON.parse(JSON.stringify(this.currentTestCucumberSteps)); this.currentTestCucumberSteps = []; @@ -573,16 +506,6 @@ class MyReporter { } } - mapTestId = (testData, eventType) => { - if (!eventType.match(/TestRun/)) {return} - - this.testIdMap[testData.name] = testData.uuid; - } - - getTestId = (testIdentifier) => { - return this.testIdMap[testIdentifier] || null; - } - appendTestItemLog = async (log) => { try { if(this.current_hook && ( this.current_hook.hookAnalyticsId && !this.runStatusMarkedHash[this.current_hook.hookAnalyticsId] )) { diff --git a/bin/testhub/constants.js b/bin/testhub/constants.js deleted file mode 100644 index 6be9945f..00000000 --- a/bin/testhub/constants.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - 'TESTHUB_BUILD_API': 'api/v2/builds', - 'ACCESSIBILITY': 'accessibility', - 'OBSERVABILITY': 'observability', - 'ERROR': { - 'INVALID_CREDENTIALS': 'ERROR_INVALID_CREDENTIALS', - 'DEPRECATED': 'ERROR_SDK_DEPRECATED', - 'ACCESS_DENIED': 'ERROR_ACCESS_DENIED' - }, -}; diff --git a/bin/testhub/testhubHandler.js b/bin/testhub/testhubHandler.js deleted file mode 100644 index a6b4724a..00000000 --- a/bin/testhub/testhubHandler.js +++ /dev/null @@ -1,135 +0,0 @@ -const { - setCrashReportingConfig, - isTestObservabilitySession, - nodeRequest, -} = require("../testObservability/helper/helper"); -const helper = require("../helpers/helper"); -const testhubUtils = require("../testhub/utils"); -const TESTHUB_CONSTANTS = require("./constants"); -const logger = require('../../bin/helpers/logger').winstonLogger; - -class TestHubHandler { - static async launchBuild(user_config, bsConfigPath) { - setCrashReportingConfig(user_config, bsConfigPath); - - const obsUserName = user_config["auth"]["username"]; - const obsAccessKey = user_config["auth"]["access_key"]; - const BSTestOpsToken = `${obsUserName || ""}:${obsAccessKey || ""}`; - - if (BSTestOpsToken === "") { - // if olly true - if (isTestObservabilitySession()) { - logger.debug( - "EXCEPTION IN BUILD START EVENT : Missing authentication token" - ); - process.env.BS_TESTOPS_BUILD_COMPLETED = false; - } - - if (testhubUtils.isAccessibilityEnabled()) { - logger.debug( - "Exception while creating test run for BrowserStack Accessibility Automation: Missing authentication token" - ); - process.env.BROWSERSTACK_TEST_ACCESSIBILITY = "false"; - } - - return [null, null]; - } - - try { - const data = await this.generateBuildUpstreamData(user_config); - const config = this.getConfig(obsUserName, obsAccessKey); - const response = await nodeRequest( "POST", TESTHUB_CONSTANTS.TESTHUB_BUILD_API, data, config); - const launchData = this.extractDataFromResponse(user_config, data, response, config); - } catch (error) { - console.log(error); - if (error.success === false) { // non 200 response - testhubUtils.logBuildError(error); - return; - } - - } - } - - static async generateBuildUpstreamData(user_config) { - const { buildName, projectName, buildDescription, buildTags } = helper.getBuildDetails(user_config, true); - const productMap = testhubUtils.getProductMap(user_config); - const data = { - project_name: projectName, - name: buildName, - build_identifier: "", // no build identifier in cypress - description: buildDescription || "", - started_at: new Date().toISOString(), - tags: buildTags, - host_info: helper.getHostInfo(), - ci_info: helper.getCiInfo(), - build_run_identifier: process.env.BROWSERSTACK_BUILD_RUN_IDENTIFIER, - failed_tests_rerun: process.env.BROWSERSTACK_RERUN || false, - version_control: await helper.getGitMetaData(), - accessibility: testhubUtils.getAccessibilityOptions(user_config), - framework_details: testhubUtils.getFrameworkDetails(), - product_map: productMap, - browserstackAutomation: productMap["automate"], - }; - - return data; - } - - static async extractDataFromResponse( - user_config, - requestData, - response, - config - ) { - const launchData = {}; - - if (isTestObservabilitySession()) { - const [jwt, buildHashedId, allowScreenshot] = - testhubUtils.setTestObservabilityVariables( - user_config, - requestData, - response.data - ); - if (jwt && buildHashedId) { - launchData[TESTHUB_CONSTANTS.OBSERVABILITY] = { - jwt, - buildHashedId, - allowScreenshot, - }; - process.env.BROWSERSTACK_TEST_OBSERVABILITY = "true"; - } else { - launchData[TESTHUB_CONSTANTS.OBSERVABILITY] = {}; - process.env.BROWSERSTACK_TEST_OBSERVABILITY = "false"; - } - } else { - process.env.BROWSERSTACK_TEST_OBSERVABILITY = "false"; - } - - if(testhubUtils.isAccessibilityEnabled()) { - testhubUtils.setAccessibilityVariables(user_config, response.data); - } else { - process.env.BROWSERSTACK_ACCESSIBILITY = 'false'; - testhubUtils.checkAndSetAccessibility(user_config, false) - } - - if (testhubUtils.shouldProcessEventForTesthub()) { - testhubUtils.setTestHubCommonMetaInfo(user_config, response.data); - } - - return launchData; - } - - static getConfig(obsUserName, obsAccessKey) { - return { - auth: { - username: obsUserName, - password: obsAccessKey, - }, - headers: { - "Content-Type": "application/json", - "X-BSTACK-TESTOPS": "true", - }, - }; - } -} - -module.exports = TestHubHandler; diff --git a/bin/testhub/utils.js b/bin/testhub/utils.js deleted file mode 100644 index 642ecb62..00000000 --- a/bin/testhub/utils.js +++ /dev/null @@ -1,286 +0,0 @@ -const os = require("os"); - -const logger = require("../../bin/helpers/logger").winstonLogger; -const TESTHUB_CONSTANTS = require("./constants"); -const testObservabilityHelper = require("../../bin/testObservability/helper/helper"); -const helper = require("../helpers/helper"); -const accessibilityHelper = require("../accessibility-automation/helper"); -const { detect } = require('detect-port'); - - -const isUndefined = (value) => value === undefined || value === null; - -exports.getFrameworkDetails = (user_config) => { - return { - frameworkName: "Cypress", - frameworkVersion: testObservabilityHelper.getPackageVersion( - "cypress", - user_config - ), - sdkVersion: helper.getAgentVersion(), - language: "javascript", - testFramework: { - name: "cypress", - version: helper.getPackageVersion("cypress", user_config), - }, - }; -}; - -exports.isAccessibilityEnabled = () => { - if (process.env.BROWSERSTACK_TEST_ACCESSIBILITY !== undefined) { - return process.env.BROWSERSTACK_TEST_ACCESSIBILITY === "true"; - } - logger.debug('Accessibility is disabled'); - return false; -}; - -// app-automate and percy support is not present for cypress -exports.getProductMap = (user_config) => { - return { - observability: testObservabilityHelper.isTestObservabilitySession(), - accessibility: exports.isAccessibilityEnabled(user_config), - percy: false, - automate: testObservabilityHelper.isBrowserstackInfra(), - app_automate: false, - }; -}; - -exports.shouldProcessEventForTesthub = () => { - return ( - testObservabilityHelper.isTestObservabilitySession() || - exports.isAccessibilityEnabled() - ); -}; - -exports.setTestObservabilityVariables = ( - user_config, - requestData, - responseData -) => { - if (!responseData.observability) { - exports.handleErrorForObservability(); - - return [null, null, null]; - } - - if (!responseData.observability.success) { - exports.handleErrorForObservability(responseData.observability); - - return [null, null, null]; - } - - if (responseData.observability && responseData.observability.success) { - process.env.BS_TESTOPS_BUILD_COMPLETED = true; - testObservabilityHelper.setEnvironmentVariablesForRemoteReporter( - responseData.jwt, - responseData.build_hashed_id, - responseData.observability.options.allow_screenshots.toString(), - requestData.framework_details.sdkVersion - ); - helper.setBrowserstackCypressCliDependency(user_config); - return [ - responseData.jwt, - responseData.build_hashed_id, - process.env.BS_TESTOPS_ALLOW_SCREENSHOTS, - ]; - } - return [null, null, null]; -}; - -exports.handleErrorForObservability = (error = null) => { - process.env.BROWSERSTACK_TESTHUB_UUID = "null"; - process.env.BROWSERSTACK_TESTHUB_JWT = "null"; - process.env.BS_TESTOPS_BUILD_COMPLETED = "false"; - process.env.BS_TESTOPS_JWT = "null"; - process.env.BS_TESTOPS_BUILD_HASHED_ID = "null"; - process.env.BS_TESTOPS_ALLOW_SCREENSHOTS = "null"; - exports.logBuildError(error, TESTHUB_CONSTANTS.OBSERVABILITY); -}; - -exports.setAccessibilityVariables = (user_config, responseData) => { - if (!responseData.accessibility) { - exports.handleErrorForAccessibility(user_config); - - return [null, null]; - } - - if (!responseData.accessibility.success) { - exports.handleErrorForAccessibility( - user_config, - responseData.accessibility - ); - - return [null, null]; - } - - if (responseData.accessibility.options) { - logger.debug( - `BrowserStack Accessibility Automation Build Hashed ID: ${responseData.build_hashed_id}` - ); - setAccessibilityCypressCapabilities(user_config, responseData); - helper.setBrowserstackCypressCliDependency(user_config); - } -}; - -const setAccessibilityCypressCapabilities = (user_config, responseData) => { - if (isUndefined(user_config.run_settings.accessibilityOptions)) { - user_config.run_settings.accessibilityOptions = {}; - } - const { accessibilityToken, scannerVersion } = jsonifyAccessibilityArray( - responseData.accessibility.options.capabilities, - "name", - "value" - ); - process.env.ACCESSIBILITY_AUTH = accessibilityToken; - process.env.BS_A11Y_JWT = accessibilityToken; - process.env.ACCESSIBILITY_SCANNERVERSION = scannerVersion; - - if (accessibilityToken && responseData.build_hashed_id) { - this.checkAndSetAccessibility(user_config, true); - } - - user_config.run_settings.accessibilityOptions["authToken"] = accessibilityToken; - user_config.run_settings.accessibilityOptions["auth"] = accessibilityToken; - user_config.run_settings.accessibilityOptions["scannerVersion"] = scannerVersion; - user_config.run_settings.system_env_vars.push(`ACCESSIBILITY_AUTH=${accessibilityToken}`) - user_config.run_settings.system_env_vars.push(`ACCESSIBILITY_SCANNERVERSION=${scannerVersion}`) -}; - -// To handle array of json, eg: [{keyName : '', valueName : ''}] -const jsonifyAccessibilityArray = (dataArray, keyName, valueName) => { - const result = {}; - dataArray.forEach((element) => { - result[element[keyName]] = element[valueName]; - }); - - return result; -}; - -exports.handleErrorForAccessibility = (user_config, error = null) => { - exports.checkAndSetAccessibility(user_config, false); - process.env.BROWSERSTACK_TESTHUB_UUID = "null"; - process.env.BROWSERSTACK_TESTHUB_JWT = "null"; - exports.logBuildError(error, TESTHUB_CONSTANTS.ACCESSIBILITY); -}; - -exports.logBuildError = (error, product = "") => { - if (error === undefined) { - logger.error(`${product.toUpperCase()} Build creation failed`); - - return; - } - - try { - for (const errorJson of error.errors) { - const errorType = errorJson.key; - const errorMessage = errorJson.message; - if (errorMessage) { - switch (errorType) { - case TESTHUB_CONSTANTS.ERROR.INVALID_CREDENTIALS: - logger.error(errorMessage); - break; - case TESTHUB_CONSTANTS.ERROR.ACCESS_DENIED: - logger.info(errorMessage); - break; - case TESTHUB_CONSTANTS.ERROR.DEPRECATED: - logger.error(errorMessage); - break; - default: - logger.error(errorMessage); - } - } - } - } catch (e) { - logger.error(error); - } -}; - -exports.findAvailablePort = async (preferredPort, maxAttempts = 10) => { - let port = preferredPort; - for (let attempts = 0; attempts < maxAttempts; attempts++) { - try { - const availablePort = await detect(port); - - if (availablePort === port) { - return port; - } else { - // Double-check suggested port - const verify = await detect(availablePort); - if (verify === availablePort) { - return availablePort; - } - } - - // Try next port - port++; - } catch (err) { - logger.warn(`Error checking port ${port}:`, err.message); - - // If permission denied, jump to dynamic range - if (err.code === "EACCES") { - port = 49152; - } else { - port++; - } - } - } - - const fallbackPort = Math.floor(Math.random() * (65535 - 49152)) + 49152; - logger.warn(`Could not find available port. Using fallback port: ${fallbackPort}`); - return fallbackPort; -} - -exports.setTestHubCommonMetaInfo = (user_config, responseData) => { - process.env.BROWSERSTACK_TESTHUB_JWT = responseData.jwt; - process.env.BROWSERSTACK_TESTHUB_UUID = responseData.build_hashed_id; - user_config.run_settings.system_env_vars.push(`BROWSERSTACK_TESTHUB_JWT`); - user_config.run_settings.system_env_vars.push(`BROWSERSTACK_TESTHUB_UUID`); - user_config.run_settings.system_env_vars.push(`REPORTER_API_PORT_NO`); -}; - -exports.checkAndSetAccessibility = (user_config, accessibilityFlag) => { - if (!accessibilityHelper.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; - } - - if (!user_config.run_settings.system_env_vars) { - user_config.run_settings.system_env_vars = []; - } - - if (!isUndefined(accessibilityFlag)) { - process.env.BROWSERSTACK_TEST_ACCESSIBILITY = accessibilityFlag.toString(); - user_config.run_settings.accessibility = accessibilityFlag; - if ( - !user_config.run_settings.system_env_vars.includes("BROWSERSTACK_TEST_ACCESSIBILITY") - ) { - user_config.run_settings.system_env_vars.push(`BROWSERSTACK_TEST_ACCESSIBILITY=${accessibilityFlag}`); - } - return; - } - return; -}; - -exports.getAccessibilityOptions = (user_config) => { - const settings = isUndefined(user_config.run_settings.accessibilityOptions) - ? {} - : user_config.run_settings.accessibilityOptions; - return { settings: settings }; -}; - -exports.appendTestHubParams = (testData, eventType, accessibilityScanInfo) => { - if ( - exports.isAccessibilityEnabled() && - !["HookRunStarted", "HookRunFinished", "TestRunStarted"].includes( - eventType - ) && - !isUndefined(accessibilityScanInfo[testData.name]) - ) { - testData["product_map"] = { - accessibility: accessibilityScanInfo[testData.name], - }; - } -}; diff --git a/package.json b/package.json index 7be0bf80..0ac41b32 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "chalk": "4.1.2", "cli-progress": "^3.10.0", "decompress": "4.2.1", - "detect-port": "^2.1.0", "form-data": "^4.0.0", "fs-extra": "8.1.0", "getmac": "5.20.0",