Skip to content

Commit 97185f8

Browse files
committed
Merge branch 'master' of github.com:roshan04/browserstack-cypress-cli
2 parents be00df8 + 3a7425c commit 97185f8

File tree

7 files changed

+201
-6
lines changed

7 files changed

+201
-6
lines changed

bin/commands/runs.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ module.exports = function run(args, rawArgs) {
5353
// set cypress geo location
5454
utils.setGeolocation(bsConfig, args);
5555

56+
// set spec timeout
57+
utils.setSpecTimeout(bsConfig, args);
58+
5659
// accept the specs list from command line if provided
5760
utils.setUserSpecs(bsConfig, args);
5861

bin/helpers/capabilityHelper.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,20 @@ const validate = (bsConfig, args) => {
227227
addCypressZipStartLocation(bsConfig.run_settings);
228228
}
229229

230+
if(!Utils.isUndefined(bsConfig.run_settings.spec_timeout)) {
231+
if(Utils.isPositiveInteger(bsConfig.run_settings.spec_timeout.toString().trim())) {
232+
if(Number(bsConfig.run_settings.spec_timeout) > Constants.SPEC_TIMEOUT_LIMIT) {
233+
reject(Constants.validationMessages.SPEC_TIMEOUT_LIMIT_ERROR)
234+
} else {
235+
logger.info(Constants.userMessages.SPEC_LIMIT_SUCCESS_MESSAGE.replace("<x>", bsConfig.run_settings.spec_timeout));
236+
}
237+
} else {
238+
logger.warn(Constants.userMessages.SPEC_TIMEOUT_LIMIT_WARNING)
239+
}
240+
} else {
241+
logger.warn(Constants.validationMessages.SPEC_TIMEOUT_NOT_PASSED_ERROR);
242+
}
243+
230244
resolve(cypressJson);
231245
});
232246
}

bin/helpers/constants.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ const userMessages = {
6363
DOWNLOAD_BUILD_ARTIFACTS_SUCCESS: "Your build artifact(s) have been successfully downloaded in '<user-path>/build_artifacts/<build-id>' directory",
6464
LATEST_SYNTAX_TO_ACTUAL_VERSION_MESSAGE: "Your build will run using Cypress <actualVersion> as you had specified <latestSyntaxVersion>.<frameworkUpgradeMessage> Read more about supported versions here: http://browserstack.com/docs/automate/cypress/supported-versions",
6565
PROCESS_KILL_MESSAGE: "Stopping the CLI and the execution of the build on BrowserStack",
66-
BUILD_FAILED_ERROR: "The above stacktrace has been thrown by Cypress when we tried to run your build. If your test suite requires npm dependencies then please specify them on browserstack.json. Read more at " + chalk.blueBright("https://www.browserstack.com/docs/automate/cypress/npm-packages") + ". Also, we recommend you to try running the build locally using ‘cypress run’ and if it works fine then please reach out to support at " + chalk.blueBright("https://www.browserstack.com/contact#technical-support")
66+
BUILD_FAILED_ERROR: "The above stacktrace has been thrown by Cypress when we tried to run your build. If your test suite requires npm dependencies then please specify them on browserstack.json. Read more at " + chalk.blueBright("https://www.browserstack.com/docs/automate/cypress/npm-packages") + ". Also, we recommend you to try running the build locally using ‘cypress run’ and if it works fine then please reach out to support at " + chalk.blueBright("https://www.browserstack.com/contact#technical-support"),
67+
SPEC_TIMEOUT_LIMIT_WARNING: "Value for the 'spec_timeout' key not in the 1-120 range. Going ahead with 30 mins as the default spec timeout. Read more about how to specify the option in https://browserstack.com/docs/automate/cypress/spec-timeout",
68+
SPEC_LIMIT_SUCCESS_MESSAGE: "Spec timeout specified as <x> minutes. If any of your specs exceed the specified time limit, it would be forcibly killed by BrowserStack"
6769
};
6870

6971
const validationMessages = {
@@ -97,7 +99,9 @@ const validationMessages = {
9799
NOT_ALLOWED_GEO_LOCATION_AND_LOCAL_MODE: "IP Geolocation feature is not available in conjunction with BrowserStack Local.",
98100
HOME_DIRECTORY_NOT_FOUND: "Specified home directory could not be found. Please make sure the path of the home directory is correct.",
99101
HOME_DIRECTORY_NOT_A_DIRECTORY: "Specified home directory is not a directory. The home directory can only be a directory and not a file.",
100-
CYPRESS_CONFIG_FILE_NOT_PART_OF_HOME_DIRECTORY: "Could not find cypress.json within the specified home directory. Please make sure cypress.json resides within the home directory."
102+
CYPRESS_CONFIG_FILE_NOT_PART_OF_HOME_DIRECTORY: "Could not find cypress.json within the specified home directory. Please make sure cypress.json resides within the home directory.",
103+
SPEC_TIMEOUT_LIMIT_ERROR: "The maximum allowed value of 'spec_timeout' is 120. Read more on https://browserstack.com/docs/automate/cypress/spec-timeout ",
104+
SPEC_TIMEOUT_NOT_PASSED_ERROR: "'spec_timeout' key not specified. Going ahead with 30 mins as the default spec timeout. Read more about how to specify the option in https://browserstack.com/docs/automate/cypress/spec-timeout "
101105
};
102106

103107
const cliMessages = {
@@ -140,7 +144,8 @@ const cliMessages = {
140144
CONFIG_DESCRIPTION: "Set configuration values. Separate multiple values with a comma. The values set here override any values set in your configuration file.",
141145
REPORTER: "Specify the custom reporter to use",
142146
REPORTER_OPTIONS: "Specify reporter options for custom reporter",
143-
CYPRESS_GEO_LOCATION: "Enterprise feature to simulate website and mobile behavior from different locations."
147+
CYPRESS_GEO_LOCATION: "Enterprise feature to simulate website and mobile behavior from different locations.",
148+
SPEC_TIMEOUT: "Specify a value for a hard timeout for each spec execution in the 1-120 mins range. Read https://browserstack.com/docs/automate/cypress/spec-timeout for more details."
144149
},
145150
COMMON: {
146151
DISABLE_USAGE_REPORTING: "Disable usage reporting",
@@ -168,7 +173,7 @@ const messageTypes = {
168173
NULL: null
169174
}
170175

171-
const allowedFileTypes = ['js', 'json', 'txt', 'ts', 'feature', 'features', 'pdf', 'jpg', 'jpeg', 'png', 'zip', 'npmrc', 'xml', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'jsx', 'coffee', 'cjsx', 'csv', 'tsv', 'yml', 'yaml', 'env', 'mov', 'mp4', 'mp3', 'wav', 'gz'];
176+
const allowedFileTypes = ['js', 'json', 'txt', 'ts', 'feature', 'features', 'pdf', 'jpg', 'jpeg', 'png', 'zip', 'npmrc', 'xml', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'jsx', 'coffee', 'cjsx', 'csv', 'tsv', 'yml', 'yaml', 'env', 'mov', 'mp4', 'mp3', 'wav', 'gz', 'tgz', 'tiff', 'bmp', 'msg', 'gif', 'sql'];
172177

173178
const filesToIgnoreWhileUploading = [
174179
'**/node_modules/**',
@@ -234,6 +239,8 @@ const REDACTED = "[REDACTED]";
234239

235240
const REDACTED_AUTH =`auth: { "username": ${REDACTED}, "access_key": ${REDACTED} }`;
236241

242+
const SPEC_TIMEOUT_LIMIT = 120 // IN MINS
243+
237244
module.exports = Object.freeze({
238245
syncCLI,
239246
userMessages,
@@ -254,5 +261,6 @@ module.exports = Object.freeze({
254261
ERROR_EXIT_CODE,
255262
AUTH_REGEX,
256263
REDACTED_AUTH,
257-
BUILD_FAILED_EXIT_CODE
264+
BUILD_FAILED_EXIT_CODE,
265+
SPEC_TIMEOUT_LIMIT
258266
});

bin/helpers/utils.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,23 @@ exports.setGeolocation = (bsConfig, args) => {
310310
}
311311
}
312312

313+
exports.isSpecTimeoutArgPassed = () => {
314+
return this.searchForOption('--spec-timeout') || this.searchForOption('-t');
315+
}
316+
exports.setSpecTimeout = (bsConfig, args) => {
317+
let specTimeout = null;
318+
if(this.isSpecTimeoutArgPassed()) {
319+
if(!this.isUndefined(args.specTimeout)) {
320+
specTimeout = args.specTimeout;
321+
} else {
322+
specTimeout = 'undefined'
323+
}
324+
} else if (!this.isUndefined(bsConfig.run_settings.spec_timeout)) {
325+
specTimeout = bsConfig.run_settings.spec_timeout;
326+
}
327+
bsConfig.run_settings.spec_timeout = specTimeout;
328+
}
329+
313330
// specs can be passed from bstack configuration file
314331
// specs can be passed via command line args as a string
315332
// command line args takes precedence over config
@@ -390,10 +407,26 @@ exports.fixCommaSeparatedString = (string) => {
390407

391408
exports.isUndefined = value => (value === undefined || value === null);
392409

410+
exports.isPositiveInteger = (str) => {
411+
if (typeof str !== 'string') {
412+
return false;
413+
}
414+
415+
const num = Number(str);
416+
417+
if (this.isInteger(num) && num > 0) {
418+
return true;
419+
}
420+
421+
return false;
422+
}
423+
393424
exports.isTrueString = value => (!this.isUndefined(value) && value.toString().toLowerCase() === 'true');
394425

395426
exports.isFloat = (value) => Number(value) && Number(value) % 1 !== 0;
396427

428+
exports.isInteger = (value) => Number.isInteger(value);
429+
397430
exports.nonEmptyArray = (value) => {
398431
if(!this.isUndefined(value) && value && value.length) {
399432
return true;

bin/runner.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,12 @@ var argv = yargs
168168
type: "string",
169169
default: undefined
170170
},
171+
't': {
172+
alias: ['specTimeout'],
173+
default: undefined,
174+
describe: Constants.cliMessages.RUN.SPEC_TIMEOUT,
175+
type: "string"
176+
},
171177
'disable-npm-warning': {
172178
default: false,
173179
description: Constants.cliMessages.COMMON.NO_NPM_WARNING,

test/unit/bin/commands/runs.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ describe("runs", () => {
116116
setConfigStub = sandbox.stub();
117117
setCLIModeStub = sandbox.stub();
118118
setGeolocationStub = sandbox.stub();
119+
setSpecTimeoutStub = sandbox.stub().returns(undefined);
119120
});
120121

121122
afterEach(() => {
@@ -156,7 +157,8 @@ describe("runs", () => {
156157
setBrowsers: setBrowsersStub,
157158
setConfig: setConfigStub,
158159
setCLIMode: setCLIModeStub,
159-
setGeolocation: setGeolocationStub
160+
setGeolocation: setGeolocationStub,
161+
setSpecTimeout: setSpecTimeoutStub
160162
},
161163
'../helpers/capabilityHelper': {
162164
validate: capabilityValidatorStub
@@ -197,6 +199,7 @@ describe("runs", () => {
197199
sinon.assert.calledOnce(setLocalIdentifierStub);
198200
sinon.assert.calledOnce(setUsageReportingFlagStub);
199201
sinon.assert.calledOnce(setGeolocationStub);
202+
sinon.assert.calledOnce(setSpecTimeoutStub);
200203
sinon.assert.calledOnceWithExactly(
201204
sendUsageReportStub,
202205
bsConfig,
@@ -254,6 +257,7 @@ describe("runs", () => {
254257
setCLIModeStub = sandbox.stub();
255258
setGeolocationStub = sandbox.stub();
256259
getVideoConfigStub = sandbox.stub();
260+
setSpecTimeoutStub = sandbox.stub().returns(undefined);
257261
});
258262

259263
afterEach(() => {
@@ -298,6 +302,7 @@ describe("runs", () => {
298302
setCLIMode: setCLIModeStub,
299303
setGeolocation: setGeolocationStub,
300304
getVideoConfig: getVideoConfigStub,
305+
setSpecTimeout: setSpecTimeoutStub
301306
},
302307
'../helpers/capabilityHelper': {
303308
validate: capabilityValidatorStub,
@@ -357,6 +362,7 @@ describe("runs", () => {
357362
sinon.assert.calledOnce(setDefaultsStub);
358363
sinon.assert.calledOnce(setSystemEnvsStub);
359364
sinon.assert.calledOnce(setGeolocationStub);
365+
sinon.assert.calledOnce(setSpecTimeoutStub);
360366
sinon.assert.calledOnceWithExactly(
361367
sendUsageReportStub,
362368
bsConfig,
@@ -416,6 +422,7 @@ describe("runs", () => {
416422
fetchZipSizeStub = sandbox.stub();
417423
setGeolocationStub = sandbox.stub();
418424
getVideoConfigStub = sandbox.stub();
425+
setSpecTimeoutStub = sandbox.stub().returns(undefined);
419426
});
420427

421428
afterEach(() => {
@@ -461,6 +468,7 @@ describe("runs", () => {
461468
fetchZipSize: fetchZipSizeStub,
462469
setGeolocation: setGeolocationStub,
463470
getVideoConfig: getVideoConfigStub,
471+
setSpecTimeout: setSpecTimeoutStub
464472
},
465473
'../helpers/capabilityHelper': {
466474
validate: capabilityValidatorStub,
@@ -522,6 +530,7 @@ describe("runs", () => {
522530
sinon.assert.calledOnce(setDefaultsStub);
523531
sinon.assert.calledOnce(setSystemEnvsStub);
524532
sinon.assert.calledOnce(setGeolocationStub);
533+
sinon.assert.calledOnce(setSpecTimeoutStub);
525534
sinon.assert.calledOnceWithExactly(
526535
sendUsageReportStub,
527536
bsConfig,
@@ -586,6 +595,7 @@ describe("runs", () => {
586595
fetchZipSizeStub = sandbox.stub();
587596
setGeolocationStub = sandbox.stub();
588597
getVideoConfigStub = sandbox.stub();
598+
setSpecTimeoutStub = sandbox.stub().returns(undefined);
589599
});
590600

591601
afterEach(() => {
@@ -632,6 +642,7 @@ describe("runs", () => {
632642
fetchZipSize: fetchZipSizeStub,
633643
setGeolocation: setGeolocationStub,
634644
getVideoConfig: getVideoConfigStub,
645+
setSpecTimeout: setSpecTimeoutStub
635646
},
636647
'../helpers/capabilityHelper': {
637648
validate: capabilityValidatorStub,
@@ -704,6 +715,7 @@ describe("runs", () => {
704715
sinon.assert.calledOnce(setDefaultsStub);
705716
sinon.assert.calledOnce(setSystemEnvsStub);
706717
sinon.assert.calledOnce(setGeolocationStub);
718+
sinon.assert.calledOnce(setSpecTimeoutStub);
707719

708720
sinon.assert.calledOnceWithExactly(
709721
sendUsageReportStub,
@@ -782,6 +794,7 @@ describe("runs", () => {
782794
fetchZipSizeStub = sandbox.stub();
783795
setGeolocationStub = sandbox.stub();
784796
getVideoConfigStub = sandbox.stub();
797+
setSpecTimeoutStub = sandbox.stub().returns(undefined);
785798
});
786799

787800
afterEach(() => {
@@ -836,6 +849,7 @@ describe("runs", () => {
836849
fetchZipSize: fetchZipSizeStub,
837850
setGeolocation: setGeolocationStub,
838851
getVideoConfig: getVideoConfigStub,
852+
setSpecTimeout: setSpecTimeoutStub
839853
},
840854
'../helpers/capabilityHelper': {
841855
validate: capabilityValidatorStub,

test/unit/bin/helpers/utils.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2920,6 +2920,123 @@ describe('utils', () => {
29202920
});
29212921
});
29222922

2923+
describe('isSpecTimeoutArgPassed', () => {
2924+
let searchForOptionStub;
2925+
beforeEach(() => {
2926+
searchForOptionStub = sinon.stub(utils, 'searchForOption').withArgs('--spec-timeout');
2927+
})
2928+
afterEach(() => {
2929+
sinon.restore();
2930+
})
2931+
it('returns true if --spec-timeout flag is passed', () => {
2932+
searchForOptionStub.withArgs('--spec-timeout').returns(true);
2933+
expect(utils.isSpecTimeoutArgPassed()).to.eq(true);
2934+
});
2935+
2936+
it('returns true if -t flag is passed', () => {
2937+
searchForOptionStub.withArgs('--spec-timeout').returns(true);
2938+
searchForOptionStub.withArgs('-t').returns(true);
2939+
// stub2.returns(true);
2940+
expect(utils.isSpecTimeoutArgPassed()).to.eq(true);
2941+
});
2942+
2943+
it('returns false if no flag is passed', () => {
2944+
searchForOptionStub.withArgs('--spec-timeout').returns(false);
2945+
searchForOptionStub.withArgs('-t').returns(false);
2946+
expect(utils.isSpecTimeoutArgPassed()).to.eq(false);
2947+
});
2948+
});
2949+
2950+
describe("setSpecTimeout", () => {
2951+
let isSpecTimeoutArgPassedStub;
2952+
beforeEach(() => {
2953+
isSpecTimeoutArgPassedStub = sinon.stub(utils, 'isSpecTimeoutArgPassed');
2954+
});
2955+
2956+
afterEach(() => {
2957+
isSpecTimeoutArgPassedStub.restore();
2958+
});
2959+
it('sets spec_timeout defined value passed in args', () => {
2960+
let bsConfig = {
2961+
run_settings: {
2962+
spec_timeout: "abc"
2963+
}
2964+
}
2965+
let args = {
2966+
specTimeout: 20
2967+
};
2968+
isSpecTimeoutArgPassedStub.returns(true);
2969+
utils.setSpecTimeout(bsConfig, args);
2970+
expect(bsConfig.run_settings.spec_timeout).to.eq(20);
2971+
});
2972+
2973+
it('sets spec_timeout undefined if no value passed in args', () => {
2974+
let bsConfig = {
2975+
run_settings: {
2976+
spec_timeout: "abc"
2977+
}
2978+
}
2979+
let args = {};
2980+
isSpecTimeoutArgPassedStub.returns(true);
2981+
utils.setSpecTimeout(bsConfig, args);
2982+
expect(bsConfig.run_settings.spec_timeout).to.eq('undefined');
2983+
});
2984+
2985+
it('sets spec_timeout to value passed in bsConfig is not in args', () => {
2986+
let bsConfig = {
2987+
run_settings: {
2988+
spec_timeout: 20
2989+
}
2990+
}
2991+
let args = {};
2992+
isSpecTimeoutArgPassedStub.returns(false);
2993+
utils.setSpecTimeout(bsConfig, args);
2994+
expect(bsConfig.run_settings.spec_timeout).to.eq(20);
2995+
});
2996+
2997+
it('sets spec_timeout to null if no value passed in args or bsConfig', () => {
2998+
let bsConfig = {
2999+
run_settings: {}
3000+
}
3001+
let args = {};
3002+
isSpecTimeoutArgPassedStub.returns(false);
3003+
utils.setSpecTimeout(bsConfig, args);
3004+
expect(bsConfig.run_settings.spec_timeout).to.eq(null);
3005+
});
3006+
});
3007+
3008+
describe('#isInteger', () => {
3009+
it('returns true if positive integer', () => {
3010+
expect(utils.isInteger(123)).to.eq(true);
3011+
});
3012+
3013+
it('returns true if negative integer', () => {
3014+
expect(utils.isInteger(-123)).to.eq(true);
3015+
});
3016+
3017+
it('returns false if string', () => {
3018+
expect(utils.isInteger("123")).to.eq(false);
3019+
});
3020+
});
3021+
3022+
describe('#isPositiveInteger', () => {
3023+
it('returns true if string positive integer', () => {
3024+
expect(utils.isPositiveInteger("123")).to.eq(true);
3025+
});
3026+
3027+
it('returns false if string negative integer', () => {
3028+
expect(utils.isPositiveInteger("-123")).to.eq(false);
3029+
});
3030+
3031+
it('returns false if complex string without integer', () => {
3032+
expect(utils.isPositiveInteger("abc qskbd wie")).to.eq(false);
3033+
});
3034+
3035+
it('returns false if complex string with integer', () => {
3036+
expect(utils.isPositiveInteger("123 2138 a1bc qs3kbd wie")).to.eq(false);
3037+
});
3038+
});
3039+
29233040
describe('formatRequest', () => {
29243041
it('should return correct JSON', () => {
29253042
expect(utils.formatRequest('Something went wrong.', undefined, undefined)).to.be.eql({err: 'Something went wrong.', status: null, body: null});

0 commit comments

Comments
 (0)