Skip to content

Commit 8beb1be

Browse files
committed
rerun + cleanup
1 parent 7b6b299 commit 8beb1be

File tree

4 files changed

+79
-87
lines changed

4 files changed

+79
-87
lines changed

bin/helpers/utils.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const usageReporting = require("./usageReporting"),
2121
config = require("../helpers/config"),
2222
pkg = require('../../package.json'),
2323
transports = require('./logger').transports,
24-
{ findGitConfig, printBuildLink, isTestObservabilitySession, isBrowserstackInfra } = require('../testObservability/helper/helper');
24+
{ findGitConfig, printBuildLink, isTestObservabilitySession, isBrowserstackInfra, shouldReRunObservabilityTests } = require('../testObservability/helper/helper');
2525

2626
const request = require('request');
2727

@@ -460,6 +460,11 @@ exports.setNodeVersion = (bsConfig, args) => {
460460
// specs can be passed via command line args as a string
461461
// command line args takes precedence over config
462462
exports.setUserSpecs = (bsConfig, args) => {
463+
if(isBrowserstackInfra() && isTestObservabilitySession() && shouldReRunObservabilityTests()) {
464+
bsConfig.run_settings.specs = process.env.BROWSERSTACK_RERUN_TESTS;
465+
return;
466+
}
467+
463468
let bsConfigSpecs = bsConfig.run_settings.specs;
464469

465470
if (!this.isUndefined(args.specs)) {

bin/testObservability/helper/helper.js

Lines changed: 49 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ const nodeRequest = (type, url, data, config) => {
107107
try {
108108
if(typeof(body) !== 'object') body = JSON.parse(body);
109109
} catch(e) {
110-
reject('Not a JSON response from BrowserStack Server');
110+
if(!url.includes('/stop')) {
111+
reject('Not a JSON response from BrowserStack Server');
112+
}
111113
}
112114
resolve({
113115
data: body
@@ -157,29 +159,6 @@ exports.getTestEnv = () => {
157159
}
158160
}
159161

160-
// exports.extractValuesWithRegexKeyMatch = (obj) => {
161-
// let values = [];
162-
// let customTagRegex = new RegExp("^CUSTOM_TAG_\\d+$", 'i');
163-
// Object.keys(obj)
164-
// .filter(key => customTagRegex.test(key))
165-
// .forEach(key => values.push(obj[key]));
166-
// return values;
167-
// }
168-
169-
// exports.getCustomTags = (config) => {
170-
// let tags = [];
171-
172-
// let tag = config.customTag || process.env.CUSTOM_TAG;
173-
// if (tag) {
174-
// tags.push(tag);
175-
// }
176-
177-
// tags.push(...this.extractValuesWithRegexKeyMatch(process.env));
178-
// tags.push(...this.extractValuesWithRegexKeyMatch(config));
179-
180-
// return tags;
181-
// }
182-
183162
exports.getFileSeparatorData = () => {
184163
const fileSeparatorRegex = /^win/.test(process.platform) ? "\\\\" : "/";
185164
const fileSeparator = /^win/.test(process.platform) ? "\\" : "/";
@@ -414,7 +393,6 @@ const getBuildDetails = (bsConfig) => {
414393
let buildName = '',
415394
projectName = '',
416395
buildDescription = '',
417-
buildIdentifier = null,
418396
buildTags = [];
419397

420398
/* Pick from environment variables */
@@ -426,7 +404,6 @@ const getBuildDetails = (bsConfig) => {
426404
buildName = buildName || bsConfig["testObservabilityOptions"]["buildName"];
427405
projectName = projectName || bsConfig["testObservabilityOptions"]["projectName"];
428406
buildTags = [...buildTags, ...bsConfig["testObservabilityOptions"]["buildTag"]];
429-
buildIdentifier = buildIdentifier || bsConfig["testObservabilityOptions"]["buildIdentifier"];
430407
buildDescription = buildDescription || bsConfig["testObservabilityOptions"]["buildDescription"];
431408
}
432409

@@ -440,18 +417,28 @@ const getBuildDetails = (bsConfig) => {
440417
return {
441418
buildName,
442419
projectName,
443-
buildIdentifier,
444420
buildDescription,
445421
buildTags
446422
};
447423
}
448424

425+
const setBrowserstackCypressCliDependency = (bsConfig) => {
426+
const runSettings = bsConfig.run_settings;
427+
if (runSettings.npm_dependencies !== undefined &&
428+
Object.keys(runSettings.npm_dependencies).length !== 0 &&
429+
typeof runSettings.npm_dependencies === 'object') {
430+
if (!("browserstack-cypress-cli" in runSettings.npm_dependencies)) {
431+
logger.warn("Missing browserstack-cypress-cli not found in npm_dependencies");
432+
runSettings.npm_dependencies['browserstack-cypress-cli'] = "latest";
433+
logger.warn(`Adding browserstack-cypress-cli in npm_dependencies`);
434+
}
435+
}
436+
}
437+
449438
exports.launchTestSession = async (user_config) => {
450-
// const obsUserName = user_config["auth"]["username"];
451-
// const obsAccessKey = user_config["auth"]["access_key"];
439+
const obsUserName = user_config["auth"]["username"];
440+
const obsAccessKey = user_config["auth"]["access_key"];
452441

453-
const obsUserName = process.env.OBS_USERNAME || user_config["auth"]["username"];
454-
const obsAccessKey = process.env.OBS_ACCESS_KEY || user_config["auth"]["access_key"];
455442
const BSTestOpsToken = `${obsUserName || ''}:${obsAccessKey || ''}`;
456443
if(BSTestOpsToken === '') {
457444
exports.debug('EXCEPTION IN BUILD START EVENT : Missing authentication token');
@@ -462,15 +449,13 @@ exports.launchTestSession = async (user_config) => {
462449
const {
463450
buildName,
464451
projectName,
465-
buildIdentifier,
466452
buildDescription,
467453
buildTags
468454
} = getBuildDetails(user_config);
469455
const data = {
470456
'format': 'json',
471457
'project_name': projectName,
472458
'name': buildName,
473-
'build_identifier': buildIdentifier,
474459
'description': buildDescription,
475460
'start_time': (new Date()).toISOString(),
476461
'tags': buildTags,
@@ -482,6 +467,7 @@ exports.launchTestSession = async (user_config) => {
482467
arch: os.arch()
483468
},
484469
'ci_info': getCiInfo(),
470+
'build_run_identifier': process.env.BROWSERSTACK_BUILD_RUN_IDENTIFIER,
485471
'failed_tests_rerun': process.env.BROWSERSTACK_RERUN || false,
486472
'version_control': await getGitMetaData(),
487473
'observability_version': {
@@ -506,7 +492,7 @@ exports.launchTestSession = async (user_config) => {
506492
process.env.BS_TESTOPS_BUILD_COMPLETED = true;
507493
setEnvironmentVariablesForRemoteReporter(response.data.jwt, response.data.build_hashed_id, response.data.allow_screenshots, data.observability_version.sdkVersion);
508494
setEventListeners();
509-
// return [response.data.jwt, response.data.build_hashed_id, response.data.allow_screenshots];
495+
if(this.isBrowserstackInfra()) setBrowserstackCypressCliDependency(user_config);
510496
} catch(error) {
511497
if (error.response) {
512498
exports.debug(`EXCEPTION IN BUILD START EVENT : ${error.response.status} ${error.response.statusText} ${JSON.stringify(error.response.data)}`);
@@ -515,7 +501,6 @@ exports.launchTestSession = async (user_config) => {
515501
}
516502
process.env.BS_TESTOPS_BUILD_COMPLETED = false;
517503
setEnvironmentVariablesForRemoteReporter(null, null, null);
518-
// return [null, null, null];
519504
}
520505
}
521506
}
@@ -727,7 +712,7 @@ exports.stopBuildUpstream = async (buildStartWaitRun = 0, testUploadWaitRun = 0,
727712

728713
try {
729714
const response = await nodeRequest('PUT',`api/v1/builds/${process.env.BS_TESTOPS_BUILD_HASHED_ID}/stop`,data,config);
730-
if(response.data.error) {
715+
if(response.data && response.data.error) {
731716
throw({message: response.data.error});
732717
} else {
733718
exports.debug(`stopBuildUpstream buildStartWaitRun[${buildStartWaitRun}] testUploadWaitRun[${testUploadWaitRun}] event successfull!`)
@@ -755,10 +740,6 @@ exports.stopBuildUpstream = async (buildStartWaitRun = 0, testUploadWaitRun = 0,
755740
}
756741
}
757742

758-
// exports.getUpstreamConfig = () => {
759-
// return bsSetupHelper.readConfig(bsSetupHelper.getConfigPath());
760-
// }
761-
762743
exports.getPlatformVersion = (isBstack) => {
763744
if(isBstack) {
764745
try {
@@ -820,7 +801,7 @@ const getMacOSVersion = () => {
820801
return execSync("awk '/SOFTWARE LICENSE AGREEMENT FOR macOS/' '/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf' | awk -F 'macOS ' '{print $NF}' | awk '{print substr($0, 0, length($0)-1)}'").toString().trim()
821802
}
822803

823-
exports.getOSDetailsFromSystem = async () => {
804+
exports.getOSDetailsFromSystem = async (product) => {
824805
let platformName = getPlatformName();
825806
let platformVersion = os.release().toString();
826807

@@ -848,32 +829,18 @@ exports.getOSDetailsFromSystem = async () => {
848829
}
849830

850831
return {
851-
os: platformName,
832+
os: product == 'automate' && platformName == 'Linux' ? 'OS X' : platformName,
852833
os_version: platformVersion
853834
};
854835
}
855836

856-
// exports.getOSDetailsFromSystem = (product) => {
857-
// var opsys = os.platform();
858-
// if (opsys == "darwin") {
859-
// opsys = "MacOS";
860-
// } else if (opsys == "win32" || opsys == "win64") {
861-
// opsys = "Windows";
862-
// } else if (opsys == "linux") {
863-
// opsys = product === "automate" ? "MacOS" : "Linux";
864-
// }
865-
866-
// return {
867-
// os: opsys,
868-
// os_version: os.version()
869-
// }
870-
// }
871-
872-
exports.requireModule = (module) => {
837+
exports.requireModule = (module, internal = false) => {
873838
logger.debug(`Getting ${module} from ${process.cwd()}`);
874839
let local_path = "";
875840
if(process.env["browserStackCwd"]){
876841
local_path = path.join(process.env["browserStackCwd"], 'node_modules', module);
842+
} else if(internal) {
843+
local_path = path.join(process.cwd(), 'node_modules', 'browserstack-cypress-cli', 'node_modules', module);
877844
} else {
878845
local_path = path.join(process.cwd(), 'node_modules', module);
879846
}
@@ -894,24 +861,40 @@ exports.requireModule = (module) => {
894861
return require(local_path);
895862
}
896863

864+
const getReRunSpecs = (rawArgs) => {
865+
if (this.isTestObservabilitySession() && this.shouldReRunObservabilityTests()) {
866+
let startIdx = -1, numEle = 0;
867+
for(let idx=0; idx<rawArgs.length; idx++) {
868+
if(rawArgs[idx] == '--spec') {
869+
startIdx = idx;
870+
} else if(rawArgs[idx].includes('--') && startIdx != -1) {
871+
break;
872+
} else if(startIdx != -1) {
873+
numEle++;
874+
}
875+
}
876+
if(startIdx != -1) rawArgs.splice(startIdx, numEle + 1);
877+
return [...rawArgs, '--spec', process.env.BROWSERSTACK_RERUN_TESTS];
878+
} else {
879+
return rawArgs;
880+
}
881+
}
882+
897883
exports.runCypressTestsLocally = (bsConfig, args, rawArgs) => {
898-
logger.info(`\n >>> LOCAL CMD : npx cypress run ${rawArgs.slice(1)} --reporter 'browserstack-cypress-cli/bin/testObservability/reporter' \n`);
899-
// const index = rawArgs.findIndex((arg) => (arg === '--reporter' || arg === '-r'));
900-
884+
logger.info(`Running npx cypress run ${getReRunSpecs(rawArgs.slice(1)).join(' ')} --reporter 'browserstack-cypress-cli/bin/testObservability/reporter'`);
901885
const cypressProcess = spawn(
902886
'npx',
903-
['cypress', 'run', ...rawArgs.slice(1), '--reporter', 'browserstack-cypress-cli/bin/testObservability/reporter'],
887+
['cypress', 'run', ...getReRunSpecs(rawArgs.slice(1)), '--reporter', 'browserstack-cypress-cli/bin/testObservability/reporter'],
904888
{ stdio: 'inherit', cwd: process.cwd(), env: process.env }
905889
);
906-
907890
cypressProcess.on('close', async (code) => {
908891
logger.info(`Cypress process exited with code ${code}`);
909892
await this.printBuildLink();
910893
});
911894

912895
cypressProcess.on('error', (err) => {
913-
logger.info(`Cypress process error ${err}`);
914-
})
896+
logger.info(`Cypress process encountered an error ${err}`);
897+
});
915898
}
916899

917900
class PathHelper {

bin/testObservability/reporter/index.js

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ const util = require('util');
44
const fs = require('fs');
55
const path = require('path');
66
const { requireModule } = require('../helper/helper');
7-
const Base = requireModule('mocha/lib/reporters/base.js'),
8-
utils = requireModule('mocha/lib/utils.js');
7+
const Base = requireModule('mocha/lib/reporters/base.js', true),
8+
utils = requireModule('mocha/lib/utils.js', true);
99
const color = Base.color;
10-
const Mocha = requireModule('mocha');
10+
const Mocha = requireModule('mocha', true);
1111
// const Runnable = requireModule('mocha/lib/runnable');
12-
const Runnable = require('mocha/lib/runnable'); // need to handle as this isn't present in older mocha versions
12+
const Runnable = require('mocha/lib/runnable', true); // need to handle as this isn't present in older mocha versions
1313
const { v4: uuidv4 } = require('uuid');
14-
// const bsSetupHelper = require('../../../helpers/helper');
1514

1615
const { IPC_EVENTS } = require('../helper/constants');
1716
const { startIPCServer } = require('../plugin/ipcServer');
@@ -82,7 +81,6 @@ class MyReporter {
8281
this._testResults = [];
8382
this._testEnv = getTestEnv();
8483
this._paths = new PathHelper({ cwd: process.cwd() }, this._testEnv.location_prefix);
85-
// this._upstreamConfig = options.reporterOption.bsConfig;
8684
this.currentTestSteps = [];
8785
this.platformDetailsMap = {};
8886
this.runStatusMarkedHash = {};
@@ -119,6 +117,7 @@ class MyReporter {
119117
delete this.runStatusMarkedHash[hook.hookAnalyticsId];
120118
hook.hookAnalyticsId = uuidv4();
121119
}
120+
hook.hook_started_at = (new Date()).toISOString();
122121
hook.started_at = (new Date()).toISOString();
123122
this.current_hook = hook;
124123
await this.sendTestRunEvent(hook,undefined,false,"HookRunStarted");
@@ -348,14 +347,14 @@ class MyReporter {
348347
);
349348
}
350349

351-
// test observability methods
352350
testStarted = async (test) => {
353351
try {
354352
const lastTest = this.current_test;
355353
this.current_test = test;
356354
test.retryOf = null;
357355
test.testAnalyticsId = uuidv4();
358356
test.started_at = (new Date()).toISOString();
357+
test.test_started_at = test.started_at;
359358
if(test._currentRetry > 0 && lastTest && lastTest.title == test.title) {
360359
/* Sending async to current test start to avoid current test end call getting fired before its start call */
361360
test.retryOf = lastTest.testAnalyticsId
@@ -419,7 +418,7 @@ class MyReporter {
419418
'result': eventType === "TestRunSkipped" ? 'skipped' : ( eventType === "TestRunStarted" ? 'pending' : this.analyticsResult(test.state)),
420419
'failure_reason': failureReason,
421420
'duration_in_ms': test.duration || (eventType.match(/Finished/) || eventType.match(/Skipped/) ? Date.now() - (new Date(test.started_at)).getTime() : null),
422-
'started_at': test.started_at,
421+
'started_at': ( (eventType.match(/TestRun/) ? test.test_started_at : test.hook_started_at) || test.started_at ) || (new Date()).toISOString(),
423422
'finished_at': eventType.match(/Finished/) || eventType.match(/Skipped/) ? (new Date()).toISOString() : null,
424423
'failure': failureData(...failureArgs),
425424
'failure_type': !failureReason ? null : failureReason.match(/AssertionError/) ? 'AssertionError' : 'UnhandledError',
@@ -429,7 +428,7 @@ class MyReporter {
429428
}
430429
};
431430

432-
const { os, os_version } = await getOSDetailsFromSystem();
431+
const { os, os_version } = await getOSDetailsFromSystem(process.env.observability_product);
433432
if(process.env.observability_integration) {
434433
testData = {...testData, integrations: {
435434
[process.env.observability_integration || 'local_grid' ]: {
@@ -463,18 +462,22 @@ class MyReporter {
463462
testData['started_at'] = testData['finished_at'];
464463
}
465464

466-
if(eventType.match(/HookRun/)) {
467-
[testData.hook_type, testData.name] = getHookDetails(test.fullTitle() || test.originalTitle || test.title);
468-
if(eventType === "HookRunFinished") {
469-
if(testData.result === 'pending') testData.result = 'passed';
470-
if(testData.hook_type == 'before each' && testData.result === 'failed' && ( !this.runStatusMarkedHash[test.ctx.currentTest.testAnalyticsId] )) {
471-
if(test.ctx.currentTest.testAnalyticsId) this.runStatusMarkedHash[test.ctx.currentTest.testAnalyticsId] = true;
472-
test.ctx.currentTest.state = STATE_FAILED;
473-
await this.sendTestRunEvent(test.ctx.currentTest,undefined,true);
465+
try {
466+
if(eventType.match(/HookRun/)) {
467+
[testData.hook_type, testData.name] = getHookDetails(test.fullTitle() || test.originalTitle || test.title);
468+
if(eventType === "HookRunFinished") {
469+
if(testData.result === 'pending') testData.result = 'passed';
470+
if(testData.hook_type == 'before each' && testData.result === 'failed' && ( !this.runStatusMarkedHash[test.ctx.currentTest.testAnalyticsId] )) {
471+
if(test.ctx.currentTest.testAnalyticsId) this.runStatusMarkedHash[test.ctx.currentTest.testAnalyticsId] = true;
472+
test.ctx.currentTest.state = STATE_FAILED;
473+
await this.sendTestRunEvent(test.ctx.currentTest,undefined,true);
474+
}
474475
}
476+
} else if(eventType.match(/TestRun/)) {
477+
mapTestHooks(test);
475478
}
476-
} else if(eventType.match(/TestRun/)) {
477-
mapTestHooks(test);
479+
} catch(e) {
480+
debug(`Exception in processing hook data for event ${eventType} with error : ${e}`);
478481
}
479482

480483
const failure_data = testData['failure'][0];
@@ -505,7 +508,7 @@ class MyReporter {
505508
const buildUpdateData = {
506509
event_type: 'BuildUpdate',
507510
'misc': {
508-
observabilityVersion: {
511+
observability_version: {
509512
frameworkName: "Cypress",
510513
sdkVersion: process.env.OBSERVABILITY_LAUNCH_SDK_VERSION,
511514
frameworkVersion: ( process.env.observability_framework_version || this.currentCypressVersion )

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"git-repo-info": "^2.1.1",
2424
"gitconfiglocal": "^2.1.0",
2525
"glob": "^7.2.0",
26+
"mocha": "^10.2.0",
2627
"mkdirp": "1.0.4",
2728
"node-ipc": "9.1.1",
2829
"platform-detect": "^3.0.1",

0 commit comments

Comments
 (0)