Skip to content

Commit e4e30bc

Browse files
authored
Merge pull request #788 from browserstack/sanitize-glob-pattern-update
update sanitizeSpecsPattern logic
2 parents ba41d26 + deac31b commit e4e30bc

File tree

3 files changed

+169
-18
lines changed

3 files changed

+169
-18
lines changed

bin/commands/runs.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ module.exports = function run(args, rawArgs) {
302302
if ( !utils.isUndefinedOrFalse(bsConfig.run_settings.enforce_settings) ) {
303303
markBlockStart('setEnforceSettingsConfig');
304304
logger.debug('Started setting the configs');
305-
utils.setEnforceSettingsConfig(bsConfig);
305+
utils.setEnforceSettingsConfig(bsConfig, args);
306306
logger.debug('Completed setting the configs');
307307
markBlockEnd('setEnforceSettingsConfig');
308308
}

bin/helpers/utils.js

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,7 @@ exports.getNumberOfSpecFiles = (bsConfig, args, cypressConfig, turboScaleSession
11631163
};
11641164

11651165
exports.sanitizeSpecsPattern = (pattern) => {
1166-
return pattern && pattern.split(",").length > 1 ? "{" + pattern + "}" : pattern;
1166+
return pattern && !(pattern.includes("{") && pattern.includes("}")) && pattern.split(",").length > 1 ? "{" + pattern + "}" : pattern;
11671167
}
11681168

11691169
exports.generateUniqueHash = () => {
@@ -1319,7 +1319,7 @@ exports.setVideoCliConfig = (bsConfig, videoConfig) => {
13191319
}
13201320

13211321
// set configs if enforce_settings is passed
1322-
exports.setEnforceSettingsConfig = (bsConfig) => {
1322+
exports.setEnforceSettingsConfig = (bsConfig, args) => {
13231323
if ( this.isUndefined(bsConfig) || this.isUndefined(bsConfig.run_settings) ) return;
13241324
let config_args = (bsConfig && bsConfig.run_settings && bsConfig.run_settings.config) ? bsConfig.run_settings.config : undefined;
13251325
if ( this.isUndefined(config_args) || !config_args.includes("video") ) {
@@ -1337,17 +1337,87 @@ exports.setEnforceSettingsConfig = (bsConfig) => {
13371337
if( this.isNotUndefined(bsConfig.run_settings.specs) && bsConfig.run_settings.cypressTestSuiteType === Constants.CYPRESS_V10_AND_ABOVE_TYPE && (this.isUndefined(config_args) || !config_args.includes("specPattern")) ) {
13381338
// doing this only for cypress 10 and above as --spec is given precedence for cypress 9.
13391339
let specConfigs = bsConfig.run_settings.specs;
1340-
// if multiple specs are passed, convert it into an array.
1341-
if(specConfigs && specConfigs.includes(',')) {
1342-
specConfigs = JSON.stringify(specConfigs.split(','));
1340+
let spec_pattern_args = "";
1341+
1342+
if (specConfigs && specConfigs.includes('{') && specConfigs.includes('}')) {
1343+
if (specConfigs && !Array.isArray(specConfigs)) {
1344+
if (specConfigs.includes(',')) {
1345+
specConfigs = this.splitStringByCharButIgnoreIfWithinARange(specConfigs, ',', '{', '}');
1346+
} else {
1347+
specConfigs = [specConfigs];
1348+
}
1349+
}
1350+
let ignoreFiles = args.exclude || bsConfig.run_settings.exclude
1351+
let specFilesMatched = [];
1352+
specConfigs.forEach(specPattern => {
1353+
specFilesMatched.push(
1354+
...glob.sync(specPattern, {
1355+
cwd: bsConfig.run_settings.cypressProjectDir, matchBase: true, ignore: ignoreFiles
1356+
})
1357+
);
1358+
});
1359+
logger.debug(`${specFilesMatched && specFilesMatched.length > 0 ? specFilesMatched.length : 0} spec files found with the provided specPattern for enforce_settings`);
1360+
// If spec files were found then lets we'll load the matched spec files
1361+
// If spec files were not found then we'll let cypress decide the loading of spec files
1362+
spec_pattern_args = `specPattern=${JSON.stringify(specFilesMatched && specFilesMatched.length > 0 ? specFilesMatched : specConfigs)}`;
1363+
} else {
1364+
// if multiple specs are passed, convert it into an array.
1365+
if(specConfigs && specConfigs.includes(',')) {
1366+
specConfigs = JSON.stringify(specConfigs.split(','));
1367+
}
1368+
spec_pattern_args = `specPattern=${specConfigs}`;
13431369
}
1344-
let spec_pattern_args = `specPattern=${specConfigs}`;
13451370
config_args = this.isUndefined(config_args) ? spec_pattern_args : config_args + ',' + spec_pattern_args;
13461371
}
13471372
if ( this.isNotUndefined(config_args) ) bsConfig["run_settings"]["config"] = config_args;
13481373
logger.debug(`Setting conifg_args for enforce_settings to ${config_args}`);
13491374
}
13501375

1376+
/**
1377+
* Splits a string by a specified splitChar.
1378+
* If leftLimiter and rightLimiter are specified then string won't be splitted if the splitChar is within the range
1379+
*
1380+
* @param {String} str - the string that needs to be splitted
1381+
* @param {String} splitChar - the split string/char from which the string will be splited
1382+
* @param {String} [leftLimiter] - the starting string/char of the range
1383+
* @param {String} [rightLimiter] - the ending string/char of the range
1384+
*
1385+
* @example Example usage of splitStringByCharButIgnoreIfWithinARange.
1386+
* // returns ["folder/A/B", "folder/{C,D}/E"]
1387+
* utils.splitStringByCharButIgnoreIfWithinARange("folder/A/B,folder/{C,D}/E", ",", "{", "}");
1388+
* @returns String[] | null
1389+
*/
1390+
exports.splitStringByCharButIgnoreIfWithinARange = (str, splitChar, leftLimiter, rightLimiter) => {
1391+
if (typeof(str) !== 'string' || this.isUndefined(splitChar)) return null;
1392+
1393+
if (this.isUndefined(leftLimiter) || this.isUndefined(rightLimiter)) return str.split(splitChar);
1394+
1395+
let result = [];
1396+
let buffer = '';
1397+
let openBraceCount = 0;
1398+
1399+
for (let i = 0; i < str.length; i++) {
1400+
if (str[i] === leftLimiter) {
1401+
openBraceCount++;
1402+
} else if (str[i] === rightLimiter) {
1403+
openBraceCount--;
1404+
}
1405+
1406+
if (str[i] === splitChar && openBraceCount === 0) {
1407+
result.push(buffer);
1408+
buffer = '';
1409+
} else {
1410+
buffer += str[i];
1411+
}
1412+
}
1413+
1414+
if (buffer !== '') {
1415+
result.push(buffer);
1416+
}
1417+
1418+
return result;
1419+
}
1420+
13511421
// blindly send other passed configs with run_settings and handle at backend
13521422
exports.setOtherConfigs = (bsConfig, args) => {
13531423
if(o11yHelpers.isTestObservabilitySession() && process.env.BS_TESTOPS_JWT) {

test/unit/bin/helpers/utils.js

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2721,6 +2721,10 @@ describe('utils', () => {
27212721
expect(utils.sanitizeSpecsPattern('pattern3')).to.eq('pattern3');
27222722
});
27232723

2724+
it('should not wrap pattern around {} when input already has {}', () => {
2725+
expect(utils.sanitizeSpecsPattern('pattern/{folderA,folderB}/*.spec.ts')).to.eq('pattern/{folderA,folderB}/*.spec.ts');
2726+
});
2727+
27242728
it('should return undefined when --spec is undefined', () => {
27252729
expect(utils.sanitizeSpecsPattern(undefined)).to.eq(undefined);
27262730
});
@@ -3094,56 +3098,133 @@ describe('utils', () => {
30943098
describe('setEnforceSettingsConfig', () => {
30953099
it('the video config should be assigned to bsconfig run_settings config', () => {
30963100
let bsConfig = {
3097-
run_settings: { video_config: { video:true, videoUploadOnPasses:true} },
3101+
run_settings: {
3102+
video_config: { video:true, videoUploadOnPasses:true },
3103+
cypressProjectDir: 'cypressProjectDir',
3104+
},
30983105
};
30993106
let args = {
31003107
config: 'video=true,videoUploadOnPasses=true'
31013108
}
3102-
utils.setEnforceSettingsConfig(bsConfig);
3109+
utils.setEnforceSettingsConfig(bsConfig, args);
31033110
expect(args.config).to.be.eql(bsConfig.run_settings.config);
31043111
});
3105-
it('the specPattern config should be assigned as strings for single string to bsconfig run_settings config', () => {
3112+
it('the specPattern config should be assigned as array for single spec string to bsconfig run_settings config', () => {
31063113
let bsConfig = {
3107-
run_settings: { specs: 'somerandomspecs', cypressTestSuiteType: 'CYPRESS_V10_AND_ABOVE_TYPE' },
3114+
run_settings: {
3115+
specs: 'somerandomspecs',
3116+
cypressTestSuiteType: 'CYPRESS_V10_AND_ABOVE_TYPE',
3117+
cypressProjectDir: 'cypressProjectDir',
3118+
},
31083119
};
31093120
let args = {
3121+
exclude: "",
31103122
config: 'video=false,videoUploadOnPasses=false,specPattern=somerandomspecs'
31113123
}
3112-
utils.setEnforceSettingsConfig(bsConfig);
3124+
utils.setEnforceSettingsConfig(bsConfig, args);
31133125
expect(args.config).to.be.eql(bsConfig.run_settings.config);
31143126
});
31153127
it('the specPattern config should be assigned as array for multiple spec strings to bsconfig run_settings config', () => {
31163128
let bsConfig = {
3117-
run_settings: { specs: 'somerandomspecs1,somerandomspecs2', cypressTestSuiteType: 'CYPRESS_V10_AND_ABOVE_TYPE' },
3129+
run_settings: {
3130+
specs: 'somerandomspecs1,somerandomspecs2',
3131+
cypressTestSuiteType: 'CYPRESS_V10_AND_ABOVE_TYPE',
3132+
cypressProjectDir: 'cypressProjectDir',
3133+
},
31183134
};
31193135
let args = {
3136+
exclude: "",
31203137
config: 'video=false,videoUploadOnPasses=false,specPattern=["somerandomspecs1","somerandomspecs2"]'
31213138
}
3122-
utils.setEnforceSettingsConfig(bsConfig);
3139+
utils.setEnforceSettingsConfig(bsConfig, args);
3140+
expect(args.config).to.be.eql(bsConfig.run_settings.config);
3141+
});
3142+
it('the specPattern config should not be assigned just on the basis of "," as array for single spec string to bsconfig run_settings config', () => {
3143+
let bsConfig = {
3144+
run_settings: {
3145+
specs: 'folders/{sample1,sample2}/somerandomspecs',
3146+
cypressTestSuiteType: 'CYPRESS_V10_AND_ABOVE_TYPE',
3147+
cypressProjectDir: 'cypressProjectDir',
3148+
},
3149+
};
3150+
let args = {
3151+
exclude: "",
3152+
config: 'video=false,videoUploadOnPasses=false,specPattern=["folders/{sample1,sample2}/somerandomspecs"]'
3153+
}
3154+
utils.setEnforceSettingsConfig(bsConfig, args);
3155+
expect(args.config).to.be.eql(bsConfig.run_settings.config);
3156+
});
3157+
it('the specPattern config should not be assigned just on the basis of "," as array for multiple spec strings to bsconfig run_settings config', () => {
3158+
let bsConfig = {
3159+
run_settings: {
3160+
specs: 'folders/{sample1,sample2}/somerandomspecs,folders2/sample3/somerandomspecs2',
3161+
cypressTestSuiteType: 'CYPRESS_V10_AND_ABOVE_TYPE',
3162+
cypressProjectDir: 'cypressProjectDir',
3163+
},
3164+
};
3165+
let args = {
3166+
exclude: "",
3167+
config: 'video=false,videoUploadOnPasses=false,specPattern=["folders/{sample1,sample2}/somerandomspecs","folders2/sample3/somerandomspecs2"]'
3168+
}
3169+
utils.setEnforceSettingsConfig(bsConfig, args);
31233170
expect(args.config).to.be.eql(bsConfig.run_settings.config);
31243171
});
31253172
it('the testFiles config should be assigned to bsconfig run_settings config', () => {
31263173
let bsConfig = {
31273174
run_settings: { specs: 'somerandomspecs', cypressTestSuiteType: 'CYPRESS_V9_AND_OLDER_TYPE' },
31283175
};
31293176
let args = {
3130-
config: 'video=false,videoUploadOnPasses=false'
3177+
config: 'video=false,videoUploadOnPasses=false',
3178+
cypressProjectDir: 'cypressProjectDir',
31313179
}
3132-
utils.setEnforceSettingsConfig(bsConfig);
3180+
utils.setEnforceSettingsConfig(bsConfig, args);
31333181
expect(args.config).to.be.eql(bsConfig.run_settings.config);
31343182
});
31353183
it('the baseUrl config should be assigned to bsconfig run_settings config', () => {
31363184
let bsConfig = {
3137-
run_settings: { baseUrl: 'http://localhost:8080' },
3185+
run_settings: {
3186+
baseUrl: 'http://localhost:8080',
3187+
cypressProjectDir: 'cypressProjectDir',
3188+
},
31383189
};
31393190
let args = {
31403191
config: 'video=false,videoUploadOnPasses=false,baseUrl=http://localhost:8080'
31413192
}
3142-
utils.setEnforceSettingsConfig(bsConfig);
3193+
utils.setEnforceSettingsConfig(bsConfig, args);
31433194
expect(args.config).to.be.eql(bsConfig.run_settings.config);
31443195
});
31453196
});
31463197

3198+
describe('splitStringByCharButIgnoreIfWithinARange', () => {
3199+
it('should return null if string is not provided', () => {
3200+
expect(utils.splitStringByCharButIgnoreIfWithinARange()).to.be.eql(null);
3201+
});
3202+
3203+
it('should return null if splitChar is not provided', () => {
3204+
expect(utils.splitStringByCharButIgnoreIfWithinARange("some")).to.be.eql(null);
3205+
});
3206+
3207+
it('should return splitted string even if leftLimiter and rightLimiter is not provided', () => {
3208+
expect(utils.splitStringByCharButIgnoreIfWithinARange("some,random,text", ",")).to.be.eql(["some", "random", "text"]);
3209+
});
3210+
3211+
it('should return splitted string even if leftLimiter is provided but rightLimiter is not provided', () => {
3212+
expect(utils.splitStringByCharButIgnoreIfWithinARange("some,random,{text,here},and,here", ",", "{")).to.be.eql(["some", "random", "{text", "here}", "and", "here"]);
3213+
});
3214+
3215+
it('should return splitted string even if leftLimiter is not provided but rightLimiter is provided', () => {
3216+
expect(utils.splitStringByCharButIgnoreIfWithinARange("some,random,{text,here},and,here", ",", null, "}")).to.be.eql(["some", "random", "{text", "here}", "and", "here"]);
3217+
});
3218+
3219+
it('should return splitted string and ignore splitting if splitChar is withing the leftLimiter and rightLimiter', () => {
3220+
expect(utils.splitStringByCharButIgnoreIfWithinARange("some,random,{text,here},and,here", ",", "{", "}")).to.be.eql(["some", "random", "{text,here}", "and", "here"]);
3221+
});
3222+
3223+
it('should return splitted string and ignore splitting if splitChar is withing the leftLimiter and rightLimiter', () => {
3224+
expect(utils.splitStringByCharButIgnoreIfWithinARange("some,random,{text,here}", ",", "{", "}")).to.be.eql(["some", "random", "{text,here}"]);
3225+
});
3226+
});
3227+
31473228
describe('generateUniqueHash', () => {
31483229
beforeEach(() => {
31493230
let interfaceList = {

0 commit comments

Comments
 (0)