Skip to content

Commit 88497fb

Browse files
xxshubhamxxosho-20aniketxbrowserstack
authored
testPlanID support (#50)
* SDK-5540: feat: Add test plan ID handling in helper functions and update TestObservability class * test plan id format update * Added log for test plan id * Added log for test plan id * fix: read testPlanId from plugin settings so nightwatch config path works getTestPlanId() was being called with this._bstackOptions, which is populated from settings.desiredCapabilities['bstack:options']. But users configure testManagementOptions under the @nightwatch/browserstack block in nightwatch.conf.js, which lands on this._settings — so the config-based path was always returning undefined. CLI arg and env var paths were unaffected because they don't depend on the first arg. Pass this._settings instead so testManagementOptions.testPlanId in nightwatch.conf.js is picked up correctly. * fix: update readTestPlanIdFromConfig to use plugin settings instead of bstackOptions * lint fix --------- Co-authored-by: YASH JAIN <yashjainiiita@gmail.com> Co-authored-by: aniketxbrowserstack <aniket.p@browserstack.com> Co-authored-by: YASH JAIN <76126720+osho-20@users.noreply.github.com>
1 parent bbabaa6 commit 88497fb

2 files changed

Lines changed: 100 additions & 20 deletions

File tree

src/testObservability.js

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@ class TestObservability {
3636
process.env.BROWSERSTACK_TEST_REPORTING = 'false';
3737
}
3838

39-
// Check for test_observability or test_reporting configuration
39+
// Check for test_observability or test_reporting configuration
4040
const observabilityConfig = this._settings.test_observability || this._settings.test_reporting;
4141
const testReportingOptions = this._settings.testReportingOptions || this._settings.testObservabilityOptions;
42-
42+
4343
if (!helper.isUndefined(observabilityConfig) && !helper.isUndefined(observabilityConfig.enabled)) {
4444
process.env.BROWSERSTACK_TEST_OBSERVABILITY = observabilityConfig.enabled;
4545
process.env.BROWSERSTACK_TEST_REPORTING = observabilityConfig.enabled;
4646
}
47-
47+
4848
if (process.argv.includes('--disable-test-observability') || process.argv.includes('--disable-test-reporting')) {
4949
process.env.BROWSERSTACK_TEST_OBSERVABILITY = 'false';
5050
process.env.BROWSERSTACK_TEST_REPORTING = 'false';
@@ -71,7 +71,7 @@ class TestObservability {
7171
CrashReporter.setCredentialsForCrashReportUpload(this._user, this._key);
7272
CrashReporter.setConfigDetails(settings);
7373
}
74-
74+
7575
// Also check for top-level testReportingOptions or testObservabilityOptions
7676
if (settings.testReportingOptions || settings.testObservabilityOptions) {
7777
const topLevelOptions = settings.testReportingOptions || settings.testObservabilityOptions;
@@ -88,14 +88,15 @@ class TestObservability {
8888

8989
async launchTestSession() {
9090
// Support both old and new configuration options at different levels
91-
const testReportingOptions = this._settings.test_observability ||
92-
this._settings.test_reporting ||
93-
this._settings.testReportingOptions ||
94-
this._settings.testObservabilityOptions ||
91+
const testReportingOptions = this._settings.test_observability ||
92+
this._settings.test_reporting ||
93+
this._settings.testReportingOptions ||
94+
this._settings.testObservabilityOptions ||
9595
this._parentSettings?.testReportingOptions ||
9696
this._parentSettings?.testObservabilityOptions ||
9797
{};
98-
const accessibility = helper.isAccessibilityEnabled(this._parentSettings);
98+
const testPlanId = helper.getTestPlanId(this._settings);
99+
const accessibility = helper.isAccessibilityEnabled(this._parentSettings);
99100
const accessibilityOptions = accessibility ? this._settings.accessibilityOptions || {} : {};
100101
this._gitMetadata = await helper.getGitMetaData();
101102
const fromProduct = {
@@ -134,6 +135,12 @@ class TestObservability {
134135
test_orchestration: this.getTestOrchestrationBuildStartData(this._parentSettings)
135136
};
136137

138+
if (testPlanId) {
139+
data['test_management'] = {
140+
'test_plan_id': testPlanId
141+
};
142+
}
143+
137144
const config = {
138145
auth: {
139146
username: this._user,
@@ -148,6 +155,7 @@ class TestObservability {
148155
try {
149156
const response = await makeRequest('POST', 'api/v2/builds', data, config, API_URL);
150157
Logger.info('Build creation successful!');
158+
Logger.info(`Test plan id sent to build start: ${testPlanId}`);
151159
process.env.BS_TESTOPS_BUILD_COMPLETED = true;
152160

153161
const responseData = response.data || {};
@@ -171,11 +179,11 @@ class TestObservability {
171179
}
172180
getTestOrchestrationBuildStartData(settings) {
173181
const orchestrationUtils = OrchestrationUtils.getInstance(settings);
174-
182+
175183
return orchestrationUtils.getBuildStartData();
176184
}
177185

178-
processLaunchBuildResponse(responseData, settings) {
186+
processLaunchBuildResponse(responseData, settings) {
179187
if (helper.isTestObservabilitySession()) {
180188
this.processTestObservabilityResponse(responseData);
181189
}
@@ -243,12 +251,12 @@ class TestObservability {
243251
handleErrorForObservability(error) {
244252
process.env.BROWSERSTACK_TEST_OBSERVABILITY = 'false';
245253
process.env.BROWSERSTACK_TEST_REPORTING = 'false';
246-
helper.logBuildError(error, 'Test Reporting and Analytics');
254+
helper.logBuildError(error, 'Test Reporting and Analytics');
247255
}
248256

249257
handleErrorForAccessibility(error) {
250258
process.env.BROWSERSTACK_ACCESSIBILITY = 'false';
251-
helper.logBuildError(error, 'Accessibility');
259+
helper.logBuildError(error, 'Accessibility');
252260
}
253261

254262
async stopBuildUpstream () {
@@ -396,7 +404,7 @@ class TestObservability {
396404
}
397405
}
398406
}
399-
407+
400408
async sendSkippedTestEvent(skippedTest, testFileReport) {
401409
const testData = {
402410
uuid: uuidv4(),
@@ -480,7 +488,7 @@ class TestObservability {
480488
type: 'test',
481489
name: testName,
482490
body: {
483-
lang: 'javascript',
491+
lang: 'javascript',
484492
code: testBody ? testBody.toString() : null
485493
},
486494
scope: `${testMetaData.name} - ${testName}`,
@@ -524,7 +532,7 @@ class TestObservability {
524532
}
525533
}
526534
}
527-
}
535+
}
528536
await this.processTestRunData (eventData, uuid);
529537
}
530538

@@ -726,7 +734,7 @@ class TestObservability {
726734
testData.failure = hook.failure_data;
727735
testData.failure_reason = (hook.failure_data instanceof Array) ? hook.failure_data[0]?.backtrace.join('\n') : '';
728736
testData.failure_type = hook.failure_type;
729-
737+
730738
return testData;
731739
}
732740
}
@@ -773,7 +781,7 @@ class TestObservability {
773781
const startedAt = new Date().toISOString();
774782
const result = 'pending';
775783
const hookTagsList = hookDetails.tagExpression ? hookDetails.tagExpression.split(' ').filter(val => val.includes('@')) : null;
776-
784+
777785
const hookEventData = {
778786
uuid: hookData.id,
779787
type: 'hook',
@@ -865,7 +873,7 @@ class TestObservability {
865873

866874
getProductMapForBuildStartCall(settings) {
867875
const product = helper.getObservabilityLinkedProductName(settings.desiredCapabilities, settings?.selenium?.host);
868-
876+
869877
const buildProductMap = {
870878
automate: product === 'automate',
871879
app_automate: product === 'app-automate',
@@ -881,6 +889,6 @@ class TestObservability {
881889
getTestBody(testCaseData) {
882890
return testCaseData?.context.__module[testCaseData.testName] || null;
883891
}
884-
}
892+
}
885893

886894
module.exports = TestObservability;

src/utils/helper.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {RERUN_FILE, DEFAULT_WAIT_TIMEOUT_FOR_PENDING_UPLOADS, DEFAULT_WAIT_INTERV
1414
const requestQueueHandler = require('./requestQueueHandler');
1515
const Logger = require('./logger');
1616
const LogPatcher = require('./logPatcher');
17+
const TEST_MANAGEMENT_TEST_PLAN_ID_ARG = 'browserstack.testManagementOptions.testPlanId';
1718
const BSTestOpsPatcher = new LogPatcher({});
1819
const sessions = {};
1920
const {execSync} = require('child_process');
@@ -74,6 +75,77 @@ exports.getObservabilityKey = (config, bstackOptions={}) => {
7475
return process.env.BROWSERSTACK_ACCESS_KEY || config?.key || bstackOptions?.accessKey;
7576
};
7677

78+
const normalizeTestPlanId = (testPlanId) => {
79+
if (typeof testPlanId !== 'string') {
80+
return undefined;
81+
}
82+
83+
const normalizedTestPlanId = testPlanId.trim();
84+
85+
return normalizedTestPlanId.length > 0 ? normalizedTestPlanId : undefined;
86+
};
87+
88+
const readTestPlanIdFromCliArgs = (argv = process.argv) => {
89+
const cliFlag = `--${TEST_MANAGEMENT_TEST_PLAN_ID_ARG}`;
90+
91+
for (let index = 0; index < argv.length; index += 1) {
92+
const currentArg = argv[index];
93+
94+
if (currentArg === cliFlag) {
95+
const nextArg = argv[index + 1];
96+
97+
if (!nextArg || nextArg.startsWith('--')) {
98+
return undefined;
99+
}
100+
101+
return nextArg;
102+
}
103+
104+
if (currentArg.startsWith(`${cliFlag}=`)) {
105+
return currentArg.slice(cliFlag.length + 1) || undefined;
106+
}
107+
}
108+
109+
return undefined;
110+
};
111+
112+
const readTestPlanIdFromConfig = (settings = {}) => {
113+
const nestedConfigTestPlanId = normalizeTestPlanId(
114+
settings?.testManagementOptions?.testPlanId
115+
);
116+
117+
if (nestedConfigTestPlanId) {
118+
return nestedConfigTestPlanId;
119+
}
120+
121+
return normalizeTestPlanId(settings?.testPlanId);
122+
};
123+
124+
/**
125+
* Resolve the test plan id from supported Nightwatch client-side inputs.
126+
*
127+
* Priority order is CLI arguments, then environment variables, and finally
128+
* the @nightwatch/browserstack plugin settings from nightwatch.conf.js.
129+
*
130+
* @param {Record<string, unknown>} [settings={}] Plugin settings block.
131+
* @param {string[]} [argv=process.argv] CLI arguments to inspect.
132+
* @param {NodeJS.ProcessEnv} [env=process.env] Environment variables to inspect.
133+
* @returns {string|undefined} The resolved test plan id, if present.
134+
*/
135+
exports.getTestPlanId = (settings = {}, argv = process.argv, env = process.env) => {
136+
const cliTestPlanId = normalizeTestPlanId(readTestPlanIdFromCliArgs(argv));
137+
if (cliTestPlanId) {
138+
return cliTestPlanId;
139+
}
140+
141+
const envTestPlanId = normalizeTestPlanId(env.BROWSERSTACK_TEST_PLAN_ID);
142+
if (envTestPlanId) {
143+
return envTestPlanId;
144+
}
145+
146+
return readTestPlanIdFromConfig(settings);
147+
};
148+
77149
exports.isAppAutomate = () => {
78150
return process.env.BROWSERSTACK_APP_AUTOMATE === 'true';
79151
};

0 commit comments

Comments
 (0)