Skip to content

Commit 1ec1716

Browse files
committed
Merge branch 'master' of github.com:roshan04/browserstack-cypress-cli into build_stacktace_for_cli
2 parents 23d0e6f + bc94c60 commit 1ec1716

File tree

13 files changed

+642
-47
lines changed

13 files changed

+642
-47
lines changed

bin/commands/runs.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ module.exports = function run(args, rawArgs) {
5050
// set cypress config filename
5151
utils.setCypressConfigFilename(bsConfig, args);
5252

53+
// set cypress geo location
54+
utils.setGeolocation(bsConfig, args);
55+
5356
// accept the specs list from command line if provided
5457
utils.setUserSpecs(bsConfig, args);
5558

@@ -255,6 +258,9 @@ module.exports = function run(args, rawArgs) {
255258
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null, buildReportData, rawArgs);
256259
return;
257260
}).catch(async function (err) {
261+
if (err && err.includes('browserstack.geoLocation')) {
262+
err = err.replace(/browserstack.geoLocation/g, 'geolocation');
263+
}
258264
// Build creation failed
259265
logger.error(err);
260266
// stop the Local instance

bin/helpers/archiver.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use strict';
2-
const fs = require("fs");
2+
const fs = require("fs"),
3+
path = require("path");
34

45
const archiver = require("archiver"),
56
Constants = require('../helpers/constants'),
67
logger = require("./logger").winstonLogger,
7-
utils = require('../helpers/utils'),
8-
path = require('path');
8+
utils = require('../helpers/utils');
99

1010
const archiveSpecs = (runSettings, filePath, excludeFiles, md5data) => {
1111
return new Promise(function (resolve, reject) {
@@ -14,7 +14,17 @@ const archiveSpecs = (runSettings, filePath, excludeFiles, md5data) => {
1414
}
1515
var output = fs.createWriteStream(filePath);
1616

17-
var cypressFolderPath = path.dirname(runSettings.cypressConfigFilePath);
17+
var cypressFolderPath = '';
18+
let cypressAppendFilesZipLocation = '';
19+
if (runSettings.home_directory) {
20+
cypressFolderPath = runSettings.home_directory;
21+
cypressAppendFilesZipLocation = runSettings.cypressZipStartLocation;
22+
if (cypressAppendFilesZipLocation !== '') {
23+
cypressAppendFilesZipLocation += '/';
24+
}
25+
} else {
26+
cypressFolderPath = path.dirname(runSettings.cypressConfigFilePath);
27+
}
1828

1929
logger.info(`Creating tests.zip with files in ${cypressFolderPath}`);
2030

@@ -61,7 +71,7 @@ const archiveSpecs = (runSettings, filePath, excludeFiles, md5data) => {
6171

6272
if (Object.keys(packageJSON).length > 0) {
6373
let packageJSONString = JSON.stringify(packageJSON, null, 4);
64-
archive.append(packageJSONString, {name: 'browserstack-package.json'});
74+
archive.append(packageJSONString, {name: `${cypressAppendFilesZipLocation}browserstack-package.json`});
6575
}
6676

6777
// do not add cypress.json if arg provided is false
@@ -73,7 +83,7 @@ const archiveSpecs = (runSettings, filePath, excludeFiles, md5data) => {
7383
fs.readFileSync(runSettings.cypressConfigFilePath)
7484
);
7585
let cypressJSONString = JSON.stringify(cypressJSON, null, 4);
76-
archive.append(cypressJSONString, {name: 'cypress.json'});
86+
archive.append(cypressJSONString, {name: `${cypressAppendFilesZipLocation}cypress.json`});
7787
}
7888

7989
archive.finalize();

bin/helpers/capabilityHelper.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
const fs = require('fs'),
2+
path = require('path');
3+
14
const logger = require("./logger").winstonLogger,
25
Constants = require("./constants"),
3-
Utils = require("./utils"),
4-
fs = require('fs');
6+
Utils = require("./utils");
57

68
const caps = (bsConfig, zip) => {
79
return new Promise(function (resolve, reject) {
@@ -129,6 +131,13 @@ const caps = (bsConfig, zip) => {
129131
})
130132
}
131133

134+
const addCypressZipStartLocation = (runSettings) => {
135+
let resolvedHomeDirectoryPath = path.resolve(runSettings.home_directory);
136+
let resolvedCypressConfigFilePath = path.resolve(runSettings.cypressConfigFilePath);
137+
runSettings.cypressZipStartLocation = path.dirname(resolvedCypressConfigFilePath.split(resolvedHomeDirectoryPath)[1]);
138+
runSettings.cypressZipStartLocation = runSettings.cypressZipStartLocation.substring(1);
139+
}
140+
132141
const validate = (bsConfig, args) => {
133142
return new Promise(function (resolve, reject) {
134143
logger.info(Constants.userMessages.VALIDATING_CONFIG);
@@ -164,6 +173,10 @@ const validate = (bsConfig, args) => {
164173

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

176+
if (bsConfig.run_settings.userProvidedGeolocation && !bsConfig.run_settings.geolocation.match(/^[A-Z]{2}$/g)) reject(Constants.validationMessages.INVALID_GEO_LOCATION);
177+
178+
if (bsConfig["connection_settings"]["local"] && bsConfig.run_settings.userProvidedGeolocation) reject(Constants.validationMessages.NOT_ALLOWED_GEO_LOCATION_AND_LOCAL_MODE);
179+
167180
// validate if config file provided exists or not when cypress_config_file provided
168181
// validate the cypressProjectDir key otherwise.
169182
let cypressConfigFilePath = bsConfig.run_settings.cypressConfigFilePath;
@@ -185,11 +198,33 @@ const validate = (bsConfig, args) => {
185198
} catch(error){
186199
reject(Constants.validationMessages.INVALID_CYPRESS_JSON)
187200
}
201+
202+
//check if home_directory is present or not in user run_settings
203+
if (!Utils.isUndefined(bsConfig.run_settings.home_directory)) {
204+
// check if home_directory exists or not
205+
if (!fs.existsSync(bsConfig.run_settings.home_directory)) {
206+
reject(Constants.validationMessages.HOME_DIRECTORY_NOT_FOUND);
207+
}
208+
209+
// check if home_directory is a directory or not
210+
if (!fs.statSync(bsConfig.run_settings.home_directory).isDirectory()) {
211+
reject(Constants.validationMessages.HOME_DIRECTORY_NOT_A_DIRECTORY);
212+
}
213+
214+
// check if cypress config file (cypress.json) is a part of home_directory or not
215+
if (!path.resolve(bsConfig.run_settings.cypressConfigFilePath).includes(path.resolve(bsConfig.run_settings.home_directory))) {
216+
reject(Constants.validationMessages.CYPRESS_CONFIG_FILE_NOT_PART_OF_HOME_DIRECTORY);
217+
}
218+
219+
addCypressZipStartLocation(bsConfig.run_settings);
220+
}
221+
188222
resolve(cypressJson);
189223
});
190224
}
191225

192226
module.exports = {
193227
caps,
228+
addCypressZipStartLocation,
194229
validate
195230
}

bin/helpers/checkUploaded.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ const checkSpecsMd5 = (runSettings, args, instrumentBlocks) => {
1515
if (args["force-upload"]) {
1616
return resolve("force-upload");
1717
}
18-
let cypressFolderPath = path.dirname(runSettings.cypressConfigFilePath);
18+
let cypressFolderPath = undefined;
19+
if (runSettings.home_directory) {
20+
cypressFolderPath = runSettings.home_directory;
21+
} else {
22+
cypressFolderPath = path.dirname(runSettings.cypressConfigFilePath);
23+
}
1924
let ignoreFiles = utils.getFilesToIgnore(runSettings, args.exclude, false);
2025
let options = {
2126
cwd: cypressFolderPath,

bin/helpers/constants.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ const validationMessages = {
8989
INVALID_LOCAL_IDENTIFIER: "Invalid value specified for local_identifier. For more info, check out https://www.browserstack.com/docs/automate/cypress/cli-reference",
9090
INVALID_BROWSER_ARGS: "Aborting as an unacceptable value was passed for --browser. Read more at https://www.browserstack.com/docs/automate/cypress/cli-reference",
9191
INVALID_LOCAL_ASYNC_ARGS: "Cannot run in --async mode when local is set to true. Please run the build after removing --async",
92+
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",
93+
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.",
94+
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.",
95+
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.",
96+
NOT_ALLOWED_GEO_LOCATION_AND_LOCAL_MODE: "IP Geolocation feature is not available in conjunction with BrowserStack Local.",
97+
HOME_DIRECTORY_NOT_FOUND: "Specified home directory could not be found. Please make sure the path of the home directory is correct.",
98+
HOME_DIRECTORY_NOT_A_DIRECTORY: "Specified home directory is not a directory. The home directory can only be a directory and not a file.",
99+
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."
92100
};
93101

94102
const cliMessages = {
@@ -131,6 +139,7 @@ const cliMessages = {
131139
CONFIG_DESCRIPTION: "Set configuration values. Separate multiple values with a comma. The values set here override any values set in your configuration file.",
132140
REPORTER: "Specify the custom reporter to use",
133141
REPORTER_OPTIONS: "Specify reporter options for custom reporter",
142+
CYPRESS_GEO_LOCATION: "Enterprise feature to simulate website and mobile behavior from different locations."
134143
},
135144
COMMON: {
136145
DISABLE_USAGE_REPORTING: "Disable usage reporting",

bin/helpers/packageInstaller.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const packageInstall = (packageDir) => {
6363
logger.error(`Some error occurred while installing packages: ${error}`);
6464
reject(`Packages were not installed successfully.`);
6565
};
66-
nodeProcess = spawn('npm', ['install'], {cwd: packageDir});
66+
nodeProcess = spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['install'], {cwd: packageDir});
6767
nodeProcess.on('close', nodeProcessCloseCallback);
6868
nodeProcess.on('error', nodeProcessErrorCallback);
6969
});

bin/helpers/utils.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,21 @@ exports.getErrorCodeFromMsg = (errMsg) => {
8585
case Constants.validationMessages.INVALID_LOCAL_ASYNC_ARGS:
8686
errorCode = 'invalid_local_async_args';
8787
break;
88+
case Constants.validationMessages.INVALID_GEO_LOCATION:
89+
errorCode = 'invalid_geo_location';
90+
break;
91+
case Constants.validationMessages.NOT_ALLOWED_GEO_LOCATION_AND_LOCAL_MODE:
92+
errorCode = 'not_allowed_geo_location_and_local_mode';
93+
break;
94+
case Constants.validationMessages.HOME_DIRECTORY_NOT_FOUND:
95+
errorCode = 'home_directory_not_found';
96+
break;
97+
case Constants.validationMessages.HOME_DIRECTORY_NOT_A_DIRECTORY:
98+
errorCode = 'home_directory_not_a_directory';
99+
break;
100+
case Constants.validationMessages.CYPRESS_CONFIG_FILE_NOT_PART_OF_HOME_DIRECTORY:
101+
errorCode = 'cypress_config_file_not_part_of_home_directory';
102+
break;
88103
}
89104
if (
90105
errMsg.includes("Please use --config-file <path to browserstack.json>.")
@@ -272,6 +287,28 @@ exports.setCypressConfigFilename = (bsConfig, args) => {
272287
}
273288
}
274289

290+
exports.verifyGeolocationOption = () => {
291+
let glOptionsSet = (this.searchForOption('-gl') || this.searchForOption('--gl'));
292+
let geoHyphenLocationOptionsSet = (this.searchForOption('-geo-location') || this.searchForOption('--geo-location'));
293+
let geoLocationOptionsSet = (this.searchForOption('-geolocation') || this.searchForOption('--geolocation'));
294+
return (glOptionsSet || geoHyphenLocationOptionsSet || geoLocationOptionsSet);
295+
}
296+
297+
exports.setGeolocation = (bsConfig, args) => {
298+
let userProvidedGeolocation = this.verifyGeolocationOption();
299+
bsConfig.run_settings.userProvidedGeolocation = (userProvidedGeolocation || (!this.isUndefined(bsConfig.run_settings.geolocation)));
300+
301+
if (userProvidedGeolocation && !this.isUndefined(args.geolocation)) {
302+
bsConfig.run_settings.geolocation = args.geolocation;
303+
}
304+
305+
if (this.isUndefined(bsConfig.run_settings.geolocation)){
306+
bsConfig.run_settings.geolocation = null;
307+
} else {
308+
bsConfig.run_settings.geolocation = bsConfig.run_settings.geolocation.toUpperCase();
309+
}
310+
}
311+
275312
// specs can be passed from bstack configuration file
276313
// specs can be passed via command line args as a string
277314
// command line args takes precedence over config

bin/helpers/zipUpload.js

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,18 @@ const config = require("./config"),
1010
Constants = require("./constants"),
1111
utils = require("./utils");
1212

13-
const uploadSuits = (bsConfig, filePath, opts) => {
13+
14+
const purgeUploadBar = (obj) => {
15+
obj.bar1.update(100, {
16+
speed: ((obj.size / (Date.now() - obj.startTime)) / 125).toFixed(2) //kbits per sec
17+
});
18+
obj.bar1.stop();
19+
clearInterval(obj.zipInterval);
20+
}
21+
22+
const uploadSuits = (bsConfig, filePath, opts, obj) => {
1423
return new Promise(function (resolve, reject) {
15-
let startTime = Date.now();
24+
obj.startTime = Date.now();
1625

1726
if (opts.urlPresent) {
1827
return resolve({ [opts.md5ReturnKey]: opts.url });
@@ -21,28 +30,26 @@ const uploadSuits = (bsConfig, filePath, opts) => {
2130
return resolve({});
2231
}
2332

24-
let size = fs.lstatSync(filePath).size;
33+
let size = obj.size;
2534

2635
// create new progress bar
27-
let bar1 = new cliProgress.SingleBar({
28-
format: `${filePath} [{bar}] {percentage}% | ETA: {eta}s | Speed: {speed} kbps | Duration: {duration}s [${(size / 1000000).toFixed(2)} MB]`,
29-
hideCursor: true,
36+
obj.bar1 = new cliProgress.SingleBar({
37+
format: `${filePath} [{bar}] {percentage}% | ETA: {eta}s | Speed: {speed} kbps | Duration: {duration}s [${(size / 1000000).toFixed(2)} MB]`
3038
});
3139

32-
bar1.start(100, 0, {
40+
obj.bar1.start(100, 0, {
3341
speed: "N/A"
3442
});
3543

36-
bar1.on('start', () => {
44+
obj.bar1.on('start', () => {
3745
});
3846

39-
bar1.on('stop', () => {
47+
obj.bar1.on('stop', () => {
4048
});
4149

4250
let options = utils.generateUploadParams(bsConfig, filePath, opts.md5Data, opts.fileDetails)
4351
let responseData = null;
4452
var r = request.post(options, function (err, resp, body) {
45-
clearInterval(q);
4653

4754
if (err) {
4855
reject(err);
@@ -55,7 +62,7 @@ const uploadSuits = (bsConfig, filePath, opts) => {
5562
if (resp.statusCode != 200) {
5663
if (resp.statusCode == 401) {
5764
if (responseData && responseData["error"]) {
58-
responseData["time"] = Date.now() - startTime;
65+
responseData["time"] = Date.now() - obj.startTime;
5966
return reject(responseData["error"]);
6067
} else {
6168
return reject(Constants.validationMessages.INVALID_DEFAULT_AUTH_PARAMS);
@@ -65,7 +72,7 @@ const uploadSuits = (bsConfig, filePath, opts) => {
6572
return resolve({});
6673
}
6774
if(responseData && responseData["error"]){
68-
responseData["time"] = Date.now() - startTime;
75+
responseData["time"] = Date.now() - obj.startTime;
6976
reject(responseData["error"]);
7077
} else {
7178
if (resp.statusCode == 413) {
@@ -75,30 +82,26 @@ const uploadSuits = (bsConfig, filePath, opts) => {
7582
}
7683
}
7784
} else {
78-
bar1.update(100, {
79-
speed: ((size / (Date.now() - startTime)) / 125).toFixed(2) //kbits per sec
80-
});
81-
bar1.stop();
85+
purgeUploadBar(obj);
8286
logger.info(`${opts.messages.uploadingSuccess} (${responseData[opts.md5ReturnKey]})`);
8387
opts.cleanupMethod();
84-
responseData["time"] = Date.now() - startTime;
88+
responseData["time"] = Date.now() - obj.startTime;
8589
resolve(responseData);
8690
}
8791
}
8892
});
8993

90-
var q = setInterval(function () {
94+
obj.zipInterval = setInterval(function () {
9195
let dispatched = r.req.connection._bytesDispatched;
9296
let percent = dispatched * 100.0 / size;
93-
bar1.update(percent, {
94-
speed: ((dispatched / (Date.now() - startTime)) / 125).toFixed(2) //kbits per sec
97+
obj.bar1.update(percent, {
98+
speed: ((dispatched / (Date.now() - obj.startTime)) / 125).toFixed(2) //kbits per sec
9599
});
96100
}, 150);
97101

98102
});
99103
}
100104

101-
102105
const uploadCypressZip = (bsConfig, md5data, packageData) => {
103106
return new Promise(function (resolve, reject) {
104107
let obj = {}
@@ -116,8 +119,22 @@ const uploadCypressZip = (bsConfig, md5data, packageData) => {
116119
logger.info(npmOptions.messages.uploading);
117120
}
118121

119-
let zipUpload = uploadSuits(bsConfig, config.fileName, zipOptions);
120-
let npmPackageUpload = uploadSuits(bsConfig, config.packageFileName, npmOptions);
122+
var testZipUploadObj = {
123+
bar1: null,
124+
zipInterval: null,
125+
size: fs.existsSync(config.fileName) ? fs.lstatSync(config.fileName).size : 0,
126+
startTime: null
127+
}
128+
129+
var npmPackageZipUploadObj = {
130+
bar1: null,
131+
zipInterval: null,
132+
size: fs.existsSync(config.packageFileName) ? fs.lstatSync(config.packageFileName).size : 0,
133+
startTime: null
134+
}
135+
136+
let zipUpload = uploadSuits(bsConfig, config.fileName, zipOptions, testZipUploadObj);
137+
let npmPackageUpload = uploadSuits(bsConfig, config.packageFileName, npmOptions, npmPackageZipUploadObj);
121138
Promise.all([zipUpload, npmPackageUpload]).then(function (uploads) {
122139
uploads.forEach(upload => {
123140
if(upload.zip_url && upload.time) {
@@ -130,6 +147,8 @@ const uploadCypressZip = (bsConfig, md5data, packageData) => {
130147
});
131148
return resolve(obj);
132149
}).catch((error) => {
150+
purgeUploadBar(testZipUploadObj);
151+
purgeUploadBar(npmPackageZipUploadObj);
133152
return reject(error);
134153
})
135154
})

bin/runner.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,12 @@ var argv = yargs
133133
demand: true,
134134
demand: Constants.cliMessages.RUN.CYPRESS_CONFIG_DEMAND
135135
},
136+
'gl': {
137+
alias: 'geolocation',
138+
describe: Constants.cliMessages.RUN.CYPRESS_GEO_LOCATION,
139+
default: undefined,
140+
type: 'string'
141+
},
136142
'p': {
137143
alias: ['parallels', 'parallel'],
138144
describe: Constants.cliMessages.RUN.PARALLEL_DESC,

0 commit comments

Comments
 (0)