diff --git a/bin/commands/runs.js b/bin/commands/runs.js index 661e129f..e08154d0 100644 --- a/bin/commands/runs.js +++ b/bin/commands/runs.js @@ -26,6 +26,12 @@ module.exports = function run(args) { // accept the build name from command line if provided utils.setBuildName(bsConfig, args); + // accept the specs list from command line if provided + utils.setUserSpecs(bsConfig, args); + + // accept the env list from command line and set it + utils.setTestEnvs(bsConfig, args); + //accept the local from env variable if provided utils.setLocal(bsConfig); diff --git a/bin/helpers/capabilityHelper.js b/bin/helpers/capabilityHelper.js index 451a42be..d24cdc8d 100644 --- a/bin/helpers/capabilityHelper.js +++ b/bin/helpers/capabilityHelper.js @@ -67,6 +67,14 @@ const caps = (bsConfig, zip) => { obj.callbackURL = bsConfig.run_settings.callback_url; obj.projectNotifyURL = bsConfig.run_settings.project_notify_URL; obj.parallels = bsConfig.run_settings.parallels; + + if (!Utils.isUndefined(bsConfig.run_settings.specs)){ + obj.specs = bsConfig.run_settings.specs; + } + + if (!Utils.isUndefined(bsConfig.run_settings.env)){ + obj.env = bsConfig.run_settings.env; + } } if(obj.parallels === Constants.constants.DEFAULT_PARALLEL_MESSAGE) obj.parallels = undefined diff --git a/bin/helpers/constants.js b/bin/helpers/constants.js index b844cfc0..92ecd1b2 100644 --- a/bin/helpers/constants.js +++ b/bin/helpers/constants.js @@ -60,7 +60,9 @@ const cliMessages = { INFO: "Run your tests on BrowserStack.", DESC: "Path to BrowserStack config", CONFIG_DEMAND: "config file is required", - BUILD_NAME: "The build name you want to use to name your test runs" + BUILD_NAME: "The build name you want to use to name your test runs", + SPECS_DESCRIPTION: 'Specify the spec files to run', + ENV_DESCRIPTION: "Specify the environment variables for your spec files" }, COMMON: { DISABLE_USAGE_REPORTING: "Disable usage reporting", diff --git a/bin/helpers/utils.js b/bin/helpers/utils.js index 6a1d4680..945df8fb 100644 --- a/bin/helpers/utils.js +++ b/bin/helpers/utils.js @@ -117,6 +117,36 @@ exports.setBuildName = (bsConfig, args) => { } } +// specs can be passed from bstack configuration file +// specs can be passed via command line args as a string +// command line args takes precedence over config +exports.setUserSpecs = (bsConfig, args) => { + let bsConfigSpecs = bsConfig.run_settings.specs; + + if (!this.isUndefined(args.specs)) { + bsConfig.run_settings.specs = this.fixCommaSeparatedString(args.specs); + } else if (!this.isUndefined(bsConfigSpecs) && Array.isArray(bsConfigSpecs)) { + bsConfig.run_settings.specs = bsConfigSpecs.join(','); + } else if (!this.isUndefined(bsConfigSpecs) && typeof(bsConfigSpecs) == "string") { + bsConfig.run_settings.specs = this.fixCommaSeparatedString(bsConfigSpecs); + } else { + bsConfig.run_settings.specs = null; + } +} + +// env option must be set only from command line args as a string +exports.setTestEnvs = (bsConfig, args) => { + if (!this.isUndefined(args.env)) { + bsConfig.run_settings.env = this.fixCommaSeparatedString(args.env); + } else { + bsConfig.run_settings.env = null; + } +} + +exports.fixCommaSeparatedString = (string) => { + return string.split(/\s{0,},\s+/).join(','); +} + exports.isUndefined = value => (value === undefined || value === null); exports.isFloat = value => (Number(value) && Number(value) % 1 !== 0); diff --git a/bin/runner.js b/bin/runner.js index c84e716c..99f0bca6 100755 --- a/bin/runner.js +++ b/bin/runner.js @@ -165,6 +165,17 @@ var argv = yargs type: "string", default: undefined }, + 's': { + alias: ['specs', 'spec'], + describe: Constants.cliMessages.RUN.SPECS_DESCRIPTION, + type: "string", + default: undefined + }, + 'env': { + describe: Constants.cliMessages.RUN.ENV_DESCRIPTION, + type: "string", + default: undefined + }, 'disable-npm-warning': { default: false, description: Constants.cliMessages.COMMON.NO_NPM_WARNING, diff --git a/test/unit/bin/commands/runs.js b/test/unit/bin/commands/runs.js index 40db4205..227a80fe 100644 --- a/test/unit/bin/commands/runs.js +++ b/test/unit/bin/commands/runs.js @@ -85,6 +85,8 @@ describe("runs", () => { setUsernameStub = sandbox.stub(); setAccessKeyStub = sandbox.stub(); setBuildNameStub = sandbox.stub(); + setUserSpecsStub = sandbox.stub(); + setTestEnvsStub = sandbox.stub(); getConfigPathStub = sandbox.stub(); setUsageReportingFlagStub = sandbox.stub().returns(undefined); sendUsageReportStub = sandbox.stub().callsFake(function () { @@ -116,6 +118,8 @@ describe("runs", () => { setUsername: setUsernameStub, setAccessKey: setAccessKeyStub, setBuildName: setBuildNameStub, + setUserSpecs: setUserSpecsStub, + setTestEnvs: setTestEnvsStub, getConfigPath: getConfigPathStub, setLocal: setLocalStub, setLocalIdentifier: setLocalIdentifierStub, @@ -165,6 +169,8 @@ describe("runs", () => { setAccessKeyStub = sandbox.stub(); getConfigPathStub = sandbox.stub(); setBuildNameStub = sandbox.stub(); + setUserSpecsStub = sandbox.stub(); + setTestEnvsStub = sandbox.stub(); validateBstackJsonStub = sandbox.stub(); setUsageReportingFlagStub = sandbox.stub().returns(undefined); sendUsageReportStub = sandbox.stub().callsFake(function () { @@ -196,6 +202,8 @@ describe("runs", () => { setUsername: setUsernameStub, setAccessKey: setAccessKeyStub, setBuildName: setBuildNameStub, + setUserSpecs: setUserSpecsStub, + setTestEnvs: setTestEnvsStub, setUsageReportingFlag: setUsageReportingFlagStub, getConfigPath: getConfigPathStub, setLocal: setLocalStub, @@ -255,6 +263,8 @@ describe("runs", () => { setUsernameStub = sandbox.stub(); setAccessKeyStub = sandbox.stub(); setBuildNameStub = sandbox.stub(); + setUserSpecsStub = sandbox.stub(); + setTestEnvsStub = sandbox.stub(); getConfigPathStub = sandbox.stub(); setUsageReportingFlagStub = sandbox.stub().returns(undefined); sendUsageReportStub = sandbox.stub().callsFake(function () { @@ -287,6 +297,8 @@ describe("runs", () => { setUsername: setUsernameStub, setAccessKey: setAccessKeyStub, setBuildName: setBuildNameStub, + setUserSpecs: setUserSpecsStub, + setTestEnvs: setTestEnvsStub, setUsageReportingFlag: setUsageReportingFlagStub, getConfigPath: getConfigPathStub, setLocal: setLocalStub, @@ -353,6 +365,8 @@ describe("runs", () => { setUsernameStub = sandbox.stub(); setAccessKeyStub = sandbox.stub(); setBuildNameStub = sandbox.stub(); + setUserSpecsStub = sandbox.stub(); + setTestEnvsStub = sandbox.stub(); getConfigPathStub = sandbox.stub(); setUsageReportingFlagStub = sandbox.stub().returns(undefined); sendUsageReportStub = sandbox.stub().callsFake(function () { @@ -386,6 +400,8 @@ describe("runs", () => { setUsername: setUsernameStub, setAccessKey: setAccessKeyStub, setBuildName: setBuildNameStub, + setUserSpecs: setUserSpecsStub, + setTestEnvs: setTestEnvsStub, setUsageReportingFlag: setUsageReportingFlagStub, getConfigPath: getConfigPathStub, setLocal: setLocalStub, @@ -463,6 +479,8 @@ describe("runs", () => { setUsernameStub = sandbox.stub(); setAccessKeyStub = sandbox.stub(); setBuildNameStub = sandbox.stub(); + setUserSpecsStub = sandbox.stub(); + setTestEnvsStub = sandbox.stub(); getConfigPathStub = sandbox.stub(); setUsageReportingFlagStub = sandbox.stub().returns(undefined); sendUsageReportStub = sandbox.stub().callsFake(function () { @@ -499,6 +517,8 @@ describe("runs", () => { setUsername: setUsernameStub, setAccessKey: setAccessKeyStub, setBuildName: setBuildNameStub, + setUserSpecs: setUserSpecsStub, + setTestEnvs: setTestEnvsStub, setUsageReportingFlag: setUsageReportingFlagStub, setParallels: setParallelsStub, getConfigPath: getConfigPathStub, diff --git a/test/unit/bin/helpers/capabilityHelper.js b/test/unit/bin/helpers/capabilityHelper.js index e877f0b8..93675a3d 100644 --- a/test/unit/bin/helpers/capabilityHelper.js +++ b/test/unit/bin/helpers/capabilityHelper.js @@ -249,6 +249,136 @@ describe("capabilityHelper.js", () => { chai.assert.fail("Promise error"); }); }); + + context("specs and env from run_setting", () => { + it("sets specs list is present", () => { + let specsList = "spec1,spec2"; + let zip_url = "bs://"; + let bsConfig = { + auth: { + username: "random", + access_key: "random", + }, + browsers: [ + { + browser: "chrome", + os: "Windows 10", + versions: ["78", "77"], + }, + ], + run_settings: { + specs: specsList + }, + }; + + return capabilityHelper + .caps(bsConfig, { zip_url: zip_url }) + .then(function (data) { + let parsed_data = JSON.parse(data); + chai.assert.equal(parsed_data.specs, specsList); + chai.assert.equal(parsed_data.env, undefined); + }) + .catch((error) => { + chai.assert.fail("Promise error"); + }); + }); + + it("sets env list is present", () => { + let envList = "env1=value1,env2=value2"; + let zip_url = "bs://"; + let bsConfig = { + auth: { + username: "random", + access_key: "random", + }, + browsers: [ + { + browser: "chrome", + os: "Windows 10", + versions: ["78", "77"], + }, + ], + run_settings: { + env: envList + }, + }; + + return capabilityHelper + .caps(bsConfig, { zip_url: zip_url }) + .then(function (data) { + let parsed_data = JSON.parse(data); + chai.assert.equal(parsed_data.env, envList); + chai.assert.equal(parsed_data.specs, undefined); + }) + .catch((error) => { + chai.assert.fail("Promise error"); + }); + }); + + it("sets both specs and env list is present", () => { + let specsList = "spec1,spec2"; + let envList = "env1=value1,env2=value2"; + let zip_url = "bs://"; + let bsConfig = { + auth: { + username: "random", + access_key: "random", + }, + browsers: [ + { + browser: "chrome", + os: "Windows 10", + versions: ["78", "77"], + }, + ], + run_settings: { + specs: specsList, + env: envList + }, + }; + + return capabilityHelper + .caps(bsConfig, { zip_url: zip_url }) + .then(function (data) { + let parsed_data = JSON.parse(data); + chai.assert.equal(parsed_data.specs, specsList); + chai.assert.equal(parsed_data.env, envList); + }) + .catch((error) => { + chai.assert.fail("Promise error"); + }); + }); + + it("both specs and env list is not present", () => { + let zip_url = "bs://"; + let bsConfig = { + auth: { + username: "random", + access_key: "random", + }, + browsers: [ + { + browser: "chrome", + os: "Windows 10", + versions: ["78", "77"], + }, + ], + run_settings: { + }, + }; + + return capabilityHelper + .caps(bsConfig, { zip_url: zip_url }) + .then(function (data) { + let parsed_data = JSON.parse(data); + chai.assert.equal(parsed_data.specs, undefined); + chai.assert.equal(parsed_data.env, undefined); + }) + .catch((error) => { + chai.assert.fail("Promise error"); + }); + }); + }); }); describe("validate", () => { diff --git a/test/unit/bin/helpers/utils.js b/test/unit/bin/helpers/utils.js index 3f7331ca..96d696f4 100644 --- a/test/unit/bin/helpers/utils.js +++ b/test/unit/bin/helpers/utils.js @@ -243,6 +243,211 @@ describe("utils", () => { }); }); + describe("setBuildName", () => { + it("sets the build name from args list", () => { + let argBuildName = "argBuildName"; + let bsConfig = { + run_settings: { + build_name: "build_name" + } + }; + let args = { + 'build-name': argBuildName + }; + + utils.setBuildName(bsConfig, args); + expect(bsConfig.run_settings.build_name).to.be.eq(argBuildName); + }); + }); + + describe("setUsername", () => { + it("sets the username from args list", () => { + let argUserName = "argUserName"; + let bsConfig = { + auth: { + username: "username" + } + }; + let args = { + username: argUserName + }; + + utils.setUsername(bsConfig, args); + expect(bsConfig.auth.username).to.be.eq(argUserName); + }); + }); + + describe("setAccessKey", () => { + it("sets the access key from args list", () => { + let argAccessKey = "argAccessKey"; + let bsConfig = { + auth: { + access_key: "access_key" + } + }; + let args = { + key: argAccessKey + }; + + utils.setAccessKey(bsConfig, args); + expect(bsConfig.auth.access_key).to.be.eq(argAccessKey); + }); + }); + + describe("setUserSpecs", () => { + it("sets the specs from args list without space after comma with single space in given list", () => { + let argsSpecs = "spec3, spec4"; + let bsConfig = { + run_settings: { + specs: "spec1, spec2" + } + }; + let args = { + specs: argsSpecs + }; + + utils.setUserSpecs(bsConfig, args); + expect(bsConfig.run_settings.specs).to.be.eq('spec3,spec4'); + }); + + it("sets the specs from args list without space after comma with spaces in given list", () => { + let argsSpecs = "spec3 , spec4"; + let bsConfig = { + run_settings: { + specs: "spec1, spec2" + } + }; + let args = { + specs: argsSpecs + }; + + utils.setUserSpecs(bsConfig, args); + expect(bsConfig.run_settings.specs).to.be.eq('spec3,spec4'); + }); + + it("sets the specs list from specs key without space after comma with once space after comma in given list", () => { + let bsConfig = { + run_settings: { + specs: "spec1, spec2" + } + }; + let args = { + specs: null + }; + + utils.setUserSpecs(bsConfig, args); + expect(bsConfig.run_settings.specs).to.be.eq('spec1,spec2'); + }); + + it("sets the specs list from specs key without space after comma with extra space in given list", () => { + let bsConfig = { + run_settings: { + specs: "spec1 , spec2" + } + }; + let args = { + specs: null + }; + + utils.setUserSpecs(bsConfig, args); + expect(bsConfig.run_settings.specs).to.be.eq('spec1,spec2'); + }); + + it("sets the specs list from specs key array without space with comma", () => { + let bsConfig = { + run_settings: { + specs: ["spec1", "spec2"] + } + }; + let args = { + specs: null + }; + + utils.setUserSpecs(bsConfig, args); + expect(bsConfig.run_settings.specs).to.be.eq('spec1,spec2'); + }); + + it("does not set the specs list if no specs key specified", () => { + let bsConfig = { + run_settings: { + } + }; + let args = { + specs: null + }; + + utils.setUserSpecs(bsConfig, args); + expect(bsConfig.run_settings.specs).to.be.eq(null); + }); + }); + + describe("setTestEnvs", () => { + it("sets env only from args", () => { + let argsEnv = "env3=value3, env4=value4"; + let bsConfig = { + run_settings: { + env: "env1=value1, env2=value2" + } + }; + let args = { + env: argsEnv + }; + + utils.setTestEnvs(bsConfig, args); + expect(bsConfig.run_settings.env).to.be.eq('env3=value3,env4=value4'); + }); + + it("sets env from args without spaces in it", () => { + let argsEnv = "env3=value3 , env4=value4"; + let bsConfig = { + run_settings: { + env: "env1=value1 , env2=value2" + } + }; + let args = { + env: argsEnv + }; + + utils.setTestEnvs(bsConfig, args); + expect(bsConfig.run_settings.env).to.be.eq('env3=value3,env4=value4'); + }); + + it("does not set env if not specified in args", () => { + let argsEnv = "env3=value3 , env4=value4"; + let bsConfig = { + run_settings: { + env: "env1=value1 , env2=value2" + } + }; + let args = { + env: null + }; + + utils.setTestEnvs(bsConfig, args); + expect(bsConfig.run_settings.env).to.be.eq(null); + }); + }); + + describe("fixCommaSeparatedString", () => { + it("string with spaces after comma", () => { + let commaString = "string1, string2"; + let result = utils.fixCommaSeparatedString(commaString); + expect(result).to.be.eq('string1,string2'); + }); + + it("string with spaces around comma", () => { + let commaString = "string1 , string2"; + let result = utils.fixCommaSeparatedString(commaString); + expect(result).to.be.eq('string1,string2'); + }); + + it("string with 2 spaces around comma", () => { + let commaString = "string1 , string2"; + let result = utils.fixCommaSeparatedString(commaString); + expect(result).to.be.eq('string1,string2'); + }); + }); + describe("exportResults", () => { it("should export results to log/build_results.txt", () => {