Skip to content

Commit 131533b

Browse files
fix: populating projectName from test_observability object (#42)
* feat: Implement Nightwatch Test Orchestration Module - Added helpers.js for utility functions related to test orchestration. - Created index.js to serve as the main entry point for the orchestration module. - Introduced orchestrationUtils.js for managing orchestration settings and operations. - Developed requestUtils.js for handling API requests to the BrowserStack orchestration API. - Implemented testOrchestrationHandler.js to manage test orchestration operations. - Added testOrchestrationIntegration.js for integrating orchestration with Nightwatch. - Created testOrderingServer.js to handle communication with the BrowserStack server for test ordering. - Updated requestHelper.js to improve error handling for API requests. * feat: Enhance test file collection and orchestration logic with improved glob pattern matching * feat: Refactor test orchestration logic to apply specific feature path for cucumber tests * feat: Update cucumber feature path for API tests in Nightwatch globals * feat: Update cucumber feature path handling to use ordered files * feat: Add framework name to build data payload in OrchestrationUtils * feat: Remove unused helper functions and debug logs from orchestration files * feat: Update framework name comment in build data payload and enhance error logging in RequestUtils * feat: Remove unused orchestration files and refactor helper functions for improved Git metadata handling * style: eslint update * feat: Remove framework name from build data payload in OrchestrationUtils * feat: Enhance build details extraction by adding observability options * feat: Add test observability options to project and build name retrieval * feat: Enhance test orchestration by adding observability session check and improve git metadata handling * feat: Add test observability session check to build data collection in orchestration * feat: Refactor test orchestration logic to improve readability and maintainability * feat: Validate smart selection mode in run smart selection settings * fix: Corrected base branch retrieval by updating the string replacement for origin reference * refactor: Change log level from info to debug for test orchestration messages
1 parent f9d02b8 commit 131533b

File tree

5 files changed

+98
-85
lines changed

5 files changed

+98
-85
lines changed

nightwatch/globals.js

Lines changed: 74 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const path = require('path');
99
const AccessibilityAutomation = require('../src/accessibilityAutomation');
1010
const eventHelper = require('../src/utils/eventHelper');
1111
const OrchestrationUtils = require('../src/testorchestration/orchestrationUtils');
12-
const {type} = require('os');
1312
const localTunnel = new LocalTunnel();
1413
const testObservability = new TestObservability();
1514
const accessibilityAutomation = new AccessibilityAutomation();
@@ -327,83 +326,82 @@ module.exports = {
327326

328327
// Initialize and configure test orchestration
329328
try {
330-
const orchestrationUtils = OrchestrationUtils.getInstance(settings);
331-
if (orchestrationUtils && orchestrationUtils.testOrderingEnabled()) {
332-
// Apply test orchestration to reorder test files before execution
333-
const TestOrchestrationIntegration = require('../src/testorchestration/testOrchestrationIntegration');
334-
const orchestrationIntegration = TestOrchestrationIntegration.getInstance();
335-
orchestrationIntegration.configure(settings);
336-
337-
// Check if we have test files to reorder from various sources
338-
let allTestFiles = [];
339-
340-
// Checking either for Feature Path or src_folders, feature path take priority
341-
if (helper.isCucumberTestSuite(settings) && settings?.test_runner?.options?.feature_path){
342-
Logger.debug('Getting test files from feature_path configuration...');
343-
if (Array.isArray(settings.test_runner.options.feature_path)){
344-
settings.test_runner.options.feature_path.forEach(featurePath => {
345-
const files = helper.collectTestFiles(featurePath, 'feature_path config');
329+
if (helper.isTestObservabilitySession()) {
330+
const orchestrationUtils = OrchestrationUtils.getInstance(settings);
331+
if (orchestrationUtils && orchestrationUtils.testOrderingEnabled()){
332+
// Apply test orchestration to reorder test files before execution
333+
const TestOrchestrationIntegration = require('../src/testorchestration/testOrchestrationIntegration');
334+
const orchestrationIntegration = TestOrchestrationIntegration.getInstance();
335+
orchestrationIntegration.configure(settings);
336+
337+
// Check if we have test files to reorder from various sources
338+
let allTestFiles = [];
339+
340+
// Checking either for Feature Path or src_folders, feature path take priority
341+
if (helper.isCucumberTestSuite(settings) && settings?.test_runner?.options?.feature_path){
342+
Logger.debug('Getting test files from feature_path configuration...');
343+
if (Array.isArray(settings.test_runner.options.feature_path)){
344+
settings.test_runner.options.feature_path.forEach(featurePath => {
345+
const files = helper.collectTestFiles(featurePath, 'feature_path config');
346+
allTestFiles = allTestFiles.concat(files);
347+
});
348+
} else if (typeof settings.test_runner.options.feature_path === 'string'){
349+
const files = helper.collectTestFiles(settings.test_runner.options.feature_path, 'feature_path config');
350+
allTestFiles = allTestFiles.concat(files);
351+
}
352+
} else if (settings.src_folders && Array.isArray(settings.src_folders) && settings.src_folders.length > 0) {
353+
Logger.debug('Getting test files from src_folders configuration...');
354+
settings.src_folders.forEach(folder => {
355+
const files = helper.collectTestFiles(folder, 'src_folders config');
346356
allTestFiles = allTestFiles.concat(files);
347357
});
348-
} else if (typeof settings.test_runner.options.feature_path === 'string'){
349-
const files = helper.collectTestFiles(settings.test_runner.options.feature_path, 'feature_path config');
350-
allTestFiles = allTestFiles.concat(files);
351358
}
352-
} else if (settings.src_folders && Array.isArray(settings.src_folders) && settings.src_folders.length > 0) {
353-
Logger.debug('Getting test files from src_folders configuration...');
354-
settings.src_folders.forEach(folder => {
355-
const files = helper.collectTestFiles(folder, 'src_folders config');
356-
allTestFiles = allTestFiles.concat(files);
357-
});
358-
}
359359

360-
// Remove duplicates and ensure all paths are relative to cwd
361-
allTestFiles = [...new Set(allTestFiles)].map(file => {
362-
return path.isAbsolute(file) ? path.relative(process.cwd(), file) : file;
363-
});
360+
// Remove duplicates and ensure all paths are relative to cwd
361+
allTestFiles = [...new Set(allTestFiles)].map(file => {
362+
return path.isAbsolute(file) ? path.relative(process.cwd(), file) : file;
363+
});
364364

365365

366-
if (allTestFiles.length > 0) {
367-
Logger.info(`Applying test orchestration to reorder test files... Found ${allTestFiles.length} test files`);
368-
Logger.debug(`Test files: ${JSON.stringify(allTestFiles)}`);
369-
370-
// Apply orchestration to get ordered test files (synchronously)
371-
try {
372-
const orderedFiles = await orchestrationIntegration.applyOrchestration(allTestFiles, settings);
373-
if (orderedFiles && orderedFiles.length > 0) {
374-
Logger.info(`Test files reordered by orchestration: ${orderedFiles.length} files`);
375-
376-
Logger.info('Test orchestration recommended order change:');
377-
Logger.info(`Original: ${allTestFiles.join(', ')}`);
378-
Logger.info(`Optimized: ${orderedFiles.join(', ')}`);
379-
380-
try {
381-
if (helper.isCucumberTestSuite(settings) && settings?.test_runner?.options?.feature_path){
382-
// For cucumber, we override the feature_path option with ordered files
383-
settings.test_runner.options['feature_path'] = orderedFiles;
384-
} else {
385-
settings.src_folders = orderedFiles;
386-
for (const envName in testEnvSettings) {
387-
testEnvSettings[envName].src_folders = orderedFiles;
388-
testEnvSettings[envName].test_runner.src_folders = orderedFiles;
366+
if (allTestFiles.length > 0) {
367+
Logger.debug(`Applying test orchestration to reorder test files... Found ${allTestFiles.length} test files`);
368+
Logger.debug(`Test files: ${JSON.stringify(allTestFiles)}`);
369+
370+
// Apply orchestration to get ordered test files (synchronously)
371+
try {
372+
const orderedFiles = await orchestrationIntegration.applyOrchestration(allTestFiles, settings);
373+
if (orderedFiles && orderedFiles.length > 0) {
374+
Logger.info(`Test files reordered by orchestration: ${orderedFiles.length} files`);
375+
376+
try {
377+
if (helper.isCucumberTestSuite(settings) && settings?.test_runner?.options?.feature_path){
378+
// For cucumber, we override the feature_path option with ordered files
379+
settings.test_runner.options['feature_path'] = orderedFiles;
380+
} else {
381+
settings.src_folders = orderedFiles;
382+
for (const envName in testEnvSettings) {
383+
testEnvSettings[envName].src_folders = orderedFiles;
384+
testEnvSettings[envName].test_runner.src_folders = orderedFiles;
385+
}
386+
if (settings.test_runner && typeof settings.test_runner === 'object' && !Array.isArray(settings.test_runner)) {
387+
settings.test_runner.src_folders = orderedFiles;
388+
}
389389
}
390-
if (settings.test_runner && typeof settings.test_runner === 'object' && !Array.isArray(settings.test_runner)) {
391-
settings.test_runner.src_folders = orderedFiles;
392-
}
393-
}
394-
395-
} catch (reorderError) {
396-
Logger.error(`Runtime reordering failed: ${reorderError.message}`);
397-
Logger.info('Falling back to original order for current execution.');
398-
}
399-
} else {
400-
Logger.info('Split test API called - no reordering available');
390+
391+
} catch (reorderError) {
392+
Logger.error(`Runtime reordering failed: ${reorderError.message}`);
393+
Logger.info('Falling back to original order for current execution.');
394+
}
395+
} else {
396+
Logger.info('Split test API called - no reordering available');
397+
}
398+
} catch (error) {
399+
Logger.error(`Error applying test orchestration: ${error}`);
401400
}
402-
} catch (error) {
403-
Logger.error(`Error applying test orchestration: ${error}`);
401+
402+
} else {
403+
Logger.debug('No test files found for orchestration - skipping split test API call');
404404
}
405-
} else {
406-
Logger.debug('No test files found for orchestration - skipping split test API call');
407405
}
408406
}
409407
} catch (error) {
@@ -434,10 +432,13 @@ module.exports = {
434432

435433
// Collect build data for test orchestration if enabled
436434
try {
437-
const orchestrationUtils = OrchestrationUtils.getInstance();
438-
if (orchestrationUtils && orchestrationUtils.testOrderingEnabled()) {
439-
Logger.info('Collecting build data for test orchestration...');
440-
await orchestrationUtils.collectBuildData(this.settings || {});
435+
if (helper.isTestObservabilitySession()) {
436+
const orchestrationUtils = OrchestrationUtils.getInstance();
437+
if (orchestrationUtils && orchestrationUtils.testOrderingEnabled()){
438+
439+
Logger.info('Collecting build data for test orchestration...');
440+
await orchestrationUtils.collectBuildData(this.settings || {});
441+
}
441442
}
442443
} catch (error) {
443444
Logger.error(`Error collecting build data for test orchestration: ${error}`);

src/testorchestration/applyOrchestration.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const TestOrchestrationHandler = require('./testOrchestrationHandler');
99
async function applyOrchestrationIfEnabled(specs, config) {
1010
// Initialize orchestration handler
1111
const orchestrationHandler = TestOrchestrationHandler.getInstance(config);
12-
Logger.info('Orchestration handler is initialized');
1312

1413
if (!orchestrationHandler) {
1514
Logger.warn('Orchestration handler is not initialized. Skipping orchestration.');
@@ -32,13 +31,10 @@ async function applyOrchestrationIfEnabled(specs, config) {
3231
orchestrationHandler.addToOrderingInstrumentationData('enabled', orchestrationHandler.testOrderingEnabled());
3332

3433
const startTime = performance.now();
35-
36-
Logger.info('Test orchestration is enabled. Attempting to reorder test files.');
37-
34+
3835
// Get the test files from the specs
3936
const testFiles = specs;
4037
testOrderingApplied = true;
41-
Logger.info(`Test files to be reordered: ${testFiles.join(', ')}`);
4238

4339
// Reorder the test files
4440
const orderedFiles = await orchestrationHandler.reorderTestFiles(testFiles);

src/testorchestration/orchestrationUtils.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class OrchestrationUtils {
6767
this._setRunSmartSelection(
6868
runSmartSelectionOpts.enabled || false,
6969
runSmartSelectionOpts.mode || 'relevantFirst',
70-
runSmartSelectionOpts.source || null
70+
runSmartSelectionOpts.source
7171
);
7272

7373
// Extract build details
@@ -98,9 +98,12 @@ class OrchestrationUtils {
9898
*/
9999
_extractBuildDetails() {
100100
try {
101-
this.buildName = helper.getBuildName(this._settings, this._bstackOptions) || '';
101+
const fromProduct = {
102+
test_observability: true
103+
};
104+
this.buildName = helper.getBuildName(this._settings, this._bstackOptions, fromProduct) || '';
102105

103-
this.projectName = helper.getProjectName(this._settings, this._bstackOptions) || '';
106+
this.projectName = helper.getProjectName(this._settings, this._bstackOptions, fromProduct) || '';
104107

105108
this.buildIdentifier = process.env.BROWSERSTACK_BUILD_RUN_IDENTIFIER || '';
106109

@@ -217,8 +220,13 @@ class OrchestrationUtils {
217220
_setRunSmartSelection(enabled, mode, source = null) {
218221
try {
219222
this.runSmartSelection = Boolean(enabled);
220-
this.smartSelectionMode = mode;
223+
if (['relevantFirst', 'relevantOnly'].includes(mode)) {
224+
this.smartSelectionMode = mode;
225+
} else {
226+
this.smartSelectionMode = 'relevantFirst';
227+
}
221228

229+
this.smartSelectionSource = [];
222230
// Log the configuration for debugging
223231
this.logger.debug(`Setting runSmartSelection: enabled=${this.runSmartSelection}, mode=${this.smartSelectionMode}`);
224232

src/testorchestration/testOrderingServer.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ class TestOrderingServer {
2525
if (config && config.desiredCapabilities && config.desiredCapabilities['bstack:options']) {
2626
this._bstackOptions = config.desiredCapabilities['bstack:options'];
2727
}
28+
this.fromProduct = {
29+
test_observability: true
30+
};
2831
}
32+
2933

3034
/**
3135
* Initiates the split tests request and stores the response data for polling.
@@ -46,8 +50,8 @@ class TestOrderingServer {
4650
orchestrationMetadata,
4751
nodeIndex: parseInt(process.env.BROWSERSTACK_NODE_INDEX || '0'),
4852
totalNodes: parseInt(process.env.BROWSERSTACK_TOTAL_NODE_COUNT || '1'),
49-
projectName: getProjectName(this._settings, this._bstackOptions),
50-
buildName: getBuildName(this._settings, this._bstackOptions),
53+
projectName: getProjectName(this._settings, this._bstackOptions, this.fromProduct),
54+
buildName: getBuildName(this._settings, this._bstackOptions, this.fromProduct),
5155
buildRunIdentifier: process.env.BROWSERSTACK_BUILD_RUN_IDENTIFIER || '',
5256
hostInfo: getHostInfo(),
5357
prDetails

src/utils/helper.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1057,7 +1057,7 @@ function getBaseBranch() {
10571057
try {
10581058
const originHeadOutput = execSync('git symbolic-ref refs/remotes/origin/HEAD').toString().trim();
10591059
if (originHeadOutput.startsWith('refs/remotes/origin/')) {
1060-
return originHeadOutput.replace('refs/remotes/', '');
1060+
return originHeadOutput.replace('refs/remotes/origin/', '');
10611061
}
10621062
} catch (e) {
10631063
// Symbolic ref might not exist
@@ -1134,7 +1134,11 @@ function getChangedFilesFromCommits(commitHashes) {
11341134
* @param multiRepoSource Array of repository paths for multi-repo setup
11351135
*/
11361136
exports.getGitMetadataForAiSelection = (folders = []) => {
1137+
11371138
if (folders && folders.length === 0) {
1139+
return [];
1140+
}
1141+
if (folders === null){
11381142
folders = [process.cwd()];
11391143
}
11401144

0 commit comments

Comments
 (0)