Skip to content

IP Geolocation #207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bin/commands/runs.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ module.exports = function run(args, rawArgs) {
// set cypress config filename
utils.setCypressConfigFilename(bsConfig, args);

// set cypress geo location
utils.setGeolocation(bsConfig, args);

// accept the specs list from command line if provided
utils.setUserSpecs(bsConfig, args);

Expand Down Expand Up @@ -236,6 +239,9 @@ module.exports = function run(args, rawArgs) {
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null, buildReportData, rawArgs);
return;
}).catch(async function (err) {
if (err && err.includes('browserstack.geoLocation')) {
err = err.replace(/browserstack.geoLocation/g, 'geolocation');
}
// Build creation failed
logger.error(err);
// stop the Local instance
Expand Down
4 changes: 4 additions & 0 deletions bin/helpers/capabilityHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ const validate = (bsConfig, args) => {

if( Utils.searchForOption('--async') && ( !Utils.isUndefined(args.async) && bsConfig["connection_settings"]["local"])) reject(Constants.validationMessages.INVALID_LOCAL_ASYNC_ARGS);

if (bsConfig.run_settings.userProvidedGeolocation && !bsConfig.run_settings.geolocation.match(/^[A-Z]{2}$/g)) reject(Constants.validationMessages.INVALID_GEO_LOCATION);

if (bsConfig["connection_settings"]["local"] && bsConfig.run_settings.userProvidedGeolocation) reject(Constants.validationMessages.NOT_ALLOWED_GEO_LOCATION_AND_LOCAL_MODE);

// validate if config file provided exists or not when cypress_config_file provided
// validate the cypressProjectDir key otherwise.
let cypressConfigFilePath = bsConfig.run_settings.cypressConfigFilePath;
Expand Down
6 changes: 6 additions & 0 deletions bin/helpers/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ const validationMessages = {
INVALID_LOCAL_IDENTIFIER: "Invalid value specified for local_identifier. For more info, check out https://www.browserstack.com/docs/automate/cypress/cli-reference",
INVALID_BROWSER_ARGS: "Aborting as an unacceptable value was passed for --browser. Read more at https://www.browserstack.com/docs/automate/cypress/cli-reference",
INVALID_LOCAL_ASYNC_ARGS: "Cannot run in --async mode when local is set to true. Please run the build after removing --async",
INVALID_GEO_LOCATION: "[BROWSERSTACK_INVALID_COUNTRY_CODE] The country code specified for 'geolocation' is invalid. For list of supported countries, refer to - https://www.browserstack.com/ip-geolocation",
NOT_SUPPORTED_GEO_LOCATION: "The country code you have passed for IP Geolocation is currently not supported. Please refer the link https://www.browserstack.com/ip-geolocation for a list of supported countries.",
NOT_AVAILABLE_GEO_LOCATION: "The country code you have passed for IP Geolocation is not available at the moment. Please try again in a few hours.",
ACCESS_DENIED_GEO_LOCATION: "'geolocation' (IP Geolocation feature) capability is not supported in your account. It is only available under Enterprise plans, refer https://www.browserstack.com/ip-geolocation for more details.",
NOT_ALLOWED_GEO_LOCATION_AND_LOCAL_MODE: "IP Geolocation feature is not available in conjunction with BrowserStack Local.",
HOME_DIRECTORY_NOT_FOUND: "Specified home directory could not be found. Please make sure the path of the home directory is correct.",
HOME_DIRECTORY_NOT_A_DIRECTORY: "Specified home directory is not a directory. The home directory can only be a directory and not a file.",
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."
Expand Down Expand Up @@ -132,6 +137,7 @@ const cliMessages = {
CONFIG_DESCRIPTION: "Set configuration values. Separate multiple values with a comma. The values set here override any values set in your configuration file.",
REPORTER: "Specify the custom reporter to use",
REPORTER_OPTIONS: "Specify reporter options for custom reporter",
CYPRESS_GEO_LOCATION: "Enterprise feature to simulate website and mobile behavior from different locations."
},
COMMON: {
DISABLE_USAGE_REPORTING: "Disable usage reporting",
Expand Down
28 changes: 28 additions & 0 deletions bin/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ exports.getErrorCodeFromMsg = (errMsg) => {
case Constants.validationMessages.INVALID_LOCAL_ASYNC_ARGS:
errorCode = 'invalid_local_async_args';
break;
case Constants.validationMessages.INVALID_GEO_LOCATION:
errorCode = 'invalid_geo_location';
break;
case Constants.validationMessages.NOT_ALLOWED_GEO_LOCATION_AND_LOCAL_MODE:
errorCode = 'not_allowed_geo_location_and_local_mode';
break;
case Constants.validationMessages.HOME_DIRECTORY_NOT_FOUND:
errorCode = 'home_directory_not_found';
break;
Expand Down Expand Up @@ -281,6 +287,28 @@ exports.setCypressConfigFilename = (bsConfig, args) => {
}
}

exports.verifyGeolocationOption = () => {
let glOptionsSet = (this.searchForOption('-gl') || this.searchForOption('--gl'));
let geoHyphenLocationOptionsSet = (this.searchForOption('-geo-location') || this.searchForOption('--geo-location'));
let geoLocationOptionsSet = (this.searchForOption('-geolocation') || this.searchForOption('--geolocation'));
return (glOptionsSet || geoHyphenLocationOptionsSet || geoLocationOptionsSet);
}

exports.setGeolocation = (bsConfig, args) => {
let userProvidedGeolocation = this.verifyGeolocationOption();
bsConfig.run_settings.userProvidedGeolocation = (userProvidedGeolocation || (!this.isUndefined(bsConfig.run_settings.geolocation)));

if (userProvidedGeolocation && !this.isUndefined(args.geolocation)) {
bsConfig.run_settings.geolocation = args.geolocation;
}

if (this.isUndefined(bsConfig.run_settings.geolocation)){
bsConfig.run_settings.geolocation = null;
} else {
bsConfig.run_settings.geolocation = bsConfig.run_settings.geolocation.toUpperCase();
}
}

// 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
Expand Down
6 changes: 6 additions & 0 deletions bin/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ var argv = yargs
demand: true,
demand: Constants.cliMessages.RUN.CYPRESS_CONFIG_DEMAND
},
'gl': {
alias: 'geolocation',
describe: Constants.cliMessages.RUN.CYPRESS_GEO_LOCATION,
default: undefined,
type: 'string'
},
'p': {
alias: ['parallels', 'parallel'],
describe: Constants.cliMessages.RUN.PARALLEL_DESC,
Expand Down
17 changes: 16 additions & 1 deletion test/unit/bin/commands/runs.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ describe("runs", () => {
setBrowsersStub = sandbox.stub();
setConfigStub = sandbox.stub();
setCLIModeStub = sandbox.stub();
setGeolocationStub = sandbox.stub();
});

afterEach(() => {
Expand Down Expand Up @@ -154,7 +155,8 @@ describe("runs", () => {
setSystemEnvs: setSystemEnvsStub,
setBrowsers: setBrowsersStub,
setConfig: setConfigStub,
setCLIMode: setCLIModeStub
setCLIMode: setCLIModeStub,
setGeolocation: setGeolocationStub
},
'../helpers/capabilityHelper': {
validate: capabilityValidatorStub
Expand Down Expand Up @@ -194,6 +196,7 @@ describe("runs", () => {
sinon.assert.calledOnce(getErrorCodeFromMsgStub);
sinon.assert.calledOnce(setLocalIdentifierStub);
sinon.assert.calledOnce(setUsageReportingFlagStub);
sinon.assert.calledOnce(setGeolocationStub);
sinon.assert.calledOnceWithExactly(
sendUsageReportStub,
bsConfig,
Expand Down Expand Up @@ -249,6 +252,7 @@ describe("runs", () => {
setBrowsersStub = sandbox.stub();
setConfigStub = sandbox.stub();
setCLIModeStub = sandbox.stub();
setGeolocationStub = sandbox.stub();
getVideoConfigStub = sandbox.stub();
});

Expand Down Expand Up @@ -292,6 +296,7 @@ describe("runs", () => {
setBrowsers: setBrowsersStub,
setConfig: setConfigStub,
setCLIMode: setCLIModeStub,
setGeolocation: setGeolocationStub,
getVideoConfig: getVideoConfigStub,
},
'../helpers/capabilityHelper': {
Expand Down Expand Up @@ -351,6 +356,7 @@ describe("runs", () => {
sinon.assert.calledOnce(deleteResultsStub);
sinon.assert.calledOnce(setDefaultsStub);
sinon.assert.calledOnce(setSystemEnvsStub);
sinon.assert.calledOnce(setGeolocationStub);
sinon.assert.calledOnceWithExactly(
sendUsageReportStub,
bsConfig,
Expand Down Expand Up @@ -408,6 +414,7 @@ describe("runs", () => {
setBrowsersStub = sandbox.stub();
setCLIModeStub = sandbox.stub();
fetchZipSizeStub = sandbox.stub();
setGeolocationStub = sandbox.stub();
getVideoConfigStub = sandbox.stub();
});

Expand Down Expand Up @@ -452,6 +459,7 @@ describe("runs", () => {
setConfig: setConfigStub,
setCLIMode: setCLIModeStub,
fetchZipSize: fetchZipSizeStub,
setGeolocation: setGeolocationStub,
getVideoConfig: getVideoConfigStub,
},
'../helpers/capabilityHelper': {
Expand Down Expand Up @@ -513,6 +521,7 @@ describe("runs", () => {
sinon.assert.calledOnce(deleteResultsStub);
sinon.assert.calledOnce(setDefaultsStub);
sinon.assert.calledOnce(setSystemEnvsStub);
sinon.assert.calledOnce(setGeolocationStub);
sinon.assert.calledOnceWithExactly(
sendUsageReportStub,
bsConfig,
Expand Down Expand Up @@ -575,6 +584,7 @@ describe("runs", () => {
setBrowsersStub = sandbox.stub();
setCLIModeStub = sandbox.stub();
fetchZipSizeStub = sandbox.stub();
setGeolocationStub = sandbox.stub();
getVideoConfigStub = sandbox.stub();
});

Expand Down Expand Up @@ -620,6 +630,7 @@ describe("runs", () => {
setConfig: setConfigStub,
setCLIMode: setCLIModeStub,
fetchZipSize: fetchZipSizeStub,
setGeolocation: setGeolocationStub,
getVideoConfig: getVideoConfigStub,
},
'../helpers/capabilityHelper': {
Expand Down Expand Up @@ -692,6 +703,7 @@ describe("runs", () => {
sinon.assert.calledOnce(deleteResultsStub);
sinon.assert.calledOnce(setDefaultsStub);
sinon.assert.calledOnce(setSystemEnvsStub);
sinon.assert.calledOnce(setGeolocationStub);

sinon.assert.calledOnceWithExactly(
sendUsageReportStub,
Expand Down Expand Up @@ -768,6 +780,7 @@ describe("runs", () => {
setCLIModeStub = sandbox.stub();
setProcessHooksStub = sandbox.stub();
fetchZipSizeStub = sandbox.stub();
setGeolocationStub = sandbox.stub();
getVideoConfigStub = sandbox.stub();
});

Expand Down Expand Up @@ -821,6 +834,7 @@ describe("runs", () => {
setCLIMode: setCLIModeStub,
setProcessHooks: setProcessHooksStub,
fetchZipSize: fetchZipSizeStub,
setGeolocation: setGeolocationStub,
getVideoConfig: getVideoConfigStub,
},
'../helpers/capabilityHelper': {
Expand Down Expand Up @@ -910,6 +924,7 @@ describe("runs", () => {
sinon.assert.calledOnce(deleteResultsStub);
sinon.assert.calledOnce(setDefaultsStub);
sinon.assert.calledOnce(setSystemEnvsStub);
sinon.assert.calledOnce(setGeolocationStub);
sinon.assert.match(
sendUsageReportStub.getCall(0).args,
[
Expand Down
96 changes: 96 additions & 0 deletions test/unit/bin/helpers/capabilityHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ describe("capabilityHelper.js", () => {
cypress_proj_dir: "random path",
cypressConfigFilePath: "random path"
},
connection_settings: {local: false}
};
});

Expand Down Expand Up @@ -952,6 +953,7 @@ describe("capabilityHelper.js", () => {
cypressConfigFilePath: "random path",
cypressProjectDir: "random path"
},
connection_settings: {local: false}
};
});
it("validate cypress json is present", () => {
Expand Down Expand Up @@ -1064,6 +1066,99 @@ describe("capabilityHelper.js", () => {
})
});
});

describe("validate ip geolocation", () => {
beforeEach(() => {
bsConfig = {
auth: {},
browsers: [
{
browser: "chrome",
os: "Windows 10",
versions: ["78", "77"],
},
],
run_settings: {
cypress_proj_dir: "random path",
cypressConfigFilePath: "random path",
cypressProjectDir: "random path"
},
connection_settings: {}
};
});

it("should throw an error if both local and geolocation are used", () => {
bsConfig.run_settings.geolocation = "US";
bsConfig.run_settings.userProvidedGeolocation = true;
bsConfig.connection_settings.local = true;
bsConfig.connection_settings.local_identifier = "some text";

return capabilityHelper
.validate(bsConfig, {})
.then(function (data) {
chai.assert.fail("Promise error");
})
.catch((error) => {
chai.assert.equal(error, Constants.validationMessages.NOT_ALLOWED_GEO_LOCATION_AND_LOCAL_MODE);
});
});

it("should throw an error if incorrect format for geolocation code is used (valid country name but incorrect code)", () => {
bsConfig.run_settings.geolocation = "USA";
bsConfig.run_settings.userProvidedGeolocation = true;

return capabilityHelper
.validate(bsConfig, {})
.then(function (data) {
chai.assert.fail("Promise error");
})
.catch((error) => {
chai.assert.equal(error, Constants.validationMessages.INVALID_GEO_LOCATION);
});
});

it("should throw an error if incorrect format for geolocation code is used (random value)", () => {
bsConfig.run_settings.geolocation = "RANDOM";
bsConfig.run_settings.userProvidedGeolocation = true;

return capabilityHelper
.validate(bsConfig, {})
.then(function (data) {
chai.assert.fail("Promise error");
})
.catch((error) => {
chai.assert.equal(error, Constants.validationMessages.INVALID_GEO_LOCATION);
});
});

it("should throw an error if incorrect format for geolocation code is used (special chars)", () => {
bsConfig.run_settings.geolocation = "$USA$!&@*)()";
bsConfig.run_settings.userProvidedGeolocation = true;

return capabilityHelper
.validate(bsConfig, {})
.then(function (data) {
chai.assert.fail("Promise error");
})
.catch((error) => {
chai.assert.equal(error, Constants.validationMessages.INVALID_GEO_LOCATION);
});
});

it("should throw an error if incorrect format for geolocation code is used (small caps)", () => {
bsConfig.run_settings.geolocation = "us";
bsConfig.run_settings.userProvidedGeolocation = true;

return capabilityHelper
.validate(bsConfig, {})
.then(function (data) {
chai.assert.fail("Promise error");
})
.catch((error) => {
chai.assert.equal(error, Constants.validationMessages.INVALID_GEO_LOCATION);
});
});
});

describe("validate home directory", () => {
beforeEach(() => {
Expand All @@ -1081,6 +1176,7 @@ describe("capabilityHelper.js", () => {
cypressConfigFilePath: "random path",
cypressProjectDir: "random path"
},
connection_settings: {local: false}
};
});

Expand Down
Loading