Skip to content

Commit e04d4c3

Browse files
committed
Merge branch 'master' of github.com:roshan04/browserstack-cypress-cli
2 parents 5d2bc85 + 18c8281 commit e04d4c3

File tree

14 files changed

+355
-35
lines changed

14 files changed

+355
-35
lines changed

bin/commands/runs.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ const archiver = require("../helpers/archiver"),
1919
downloadBuildArtifacts = require('../helpers/buildArtifacts').downloadBuildArtifacts,
2020
downloadBuildStacktrace = require('../helpers/downloadBuildStacktrace').downloadBuildStacktrace,
2121
updateNotifier = require('update-notifier'),
22-
pkg = require('../../package.json');
22+
pkg = require('../../package.json'),
23+
packageDiff = require('../helpers/package-diff');
2324
const { getStackTraceUrl } = require('../helpers/sync/syncSpecsLogs');
2425

2526
module.exports = function run(args, rawArgs) {
2627

28+
markBlockStart('preBuild');
2729
// set debug mode (--cli-debug)
2830
utils.setDebugMode(args);
2931

@@ -158,13 +160,21 @@ module.exports = function run(args, rawArgs) {
158160
// Archive the spec files
159161
logger.debug("Started archiving test suite");
160162
markBlockStart('zip.archive');
161-
return archiver.archive(bsConfig.run_settings, config.fileName, args.exclude, md5data).then(function (data) {
163+
return archiver.archive(bsConfig.run_settings, config.fileName, args.exclude, md5data).then(async function (data) {
162164
logger.debug("Completed archiving test suite");
163165
markBlockEnd('zip.archive');
164166

165167
let test_zip_size = utils.fetchZipSize(path.join(process.cwd(), config.fileName));
166168
let npm_zip_size = utils.fetchZipSize(path.join(process.cwd(), config.packageFileName));
167-
169+
let node_modules_size = await utils.fetchFolderSize(path.join(process.cwd(), "node_modules"))
170+
171+
//Package diff
172+
let isPackageDiff = false;
173+
if(!md5data.zipUrlPresent){
174+
isPackageDiff = packageDiff.run(`package.json`, `${config.packageDirName}/package.json`);
175+
logger.debug(`Package difference was ${isPackageDiff ? `found` : `not found`}`);
176+
}
177+
168178
// Uploaded zip file
169179
logger.debug("Started uploading the test suite zip");
170180
logger.debug("Started uploading the node_module zip");
@@ -185,6 +195,8 @@ module.exports = function run(args, rawArgs) {
185195
logger.debug("Started build creation");
186196
markBlockStart('createBuild');
187197
return build.createBuild(bsConfig, zip).then(function (data) {
198+
markBlockEnd('preBuild');
199+
markBlockStart('buildProcessing');
188200
logger.debug("Completed build creation");
189201
markBlockEnd('createBuild');
190202
markBlockEnd('total');
@@ -216,6 +228,8 @@ module.exports = function run(args, rawArgs) {
216228
if (args.sync) {
217229
logger.debug("Started polling build status from BrowserStack");
218230
syncRunner.pollBuildStatus(bsConfig, data, rawArgs, buildReportData).then(async (exitCode) => {
231+
markBlockEnd('buildProcessing');
232+
markBlockStart('postBuild');
219233
logger.debug("Completed polling of build status");
220234

221235
// stop the Local instance
@@ -234,6 +248,7 @@ module.exports = function run(args, rawArgs) {
234248
// Generate custom report!
235249
reportGenerator(bsConfig, data.build_id, args, rawArgs, buildReportData, function(){
236250
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null, buildReportData, rawArgs);
251+
markBlockEnd('postBuild');
237252
utils.handleSyncExit(exitCode, data.dashboard_url);
238253
});
239254
} else {
@@ -267,9 +282,13 @@ module.exports = function run(args, rawArgs) {
267282
build_id: data.build_id,
268283
test_zip_size: test_zip_size,
269284
npm_zip_size: npm_zip_size,
285+
node_modules_size: node_modules_size,
270286
test_suite_zip_upload: md5data.zipUrlPresent ? 0 : 1,
271287
package_zip_upload: md5data.packageUrlPresent ? 0 : 1
272288
};
289+
if(dataToSend.test_suite_zip_upload === 1 ){
290+
dataToSend['is_package_diff'] = isPackageDiff;
291+
}
273292

274293
if (!md5data.zipUrlPresent && zip.tests_upload_time) {
275294
dataToSend.test_suite_zip_size = parseFloat((test_zip_size / 1024).toFixed(2));

bin/helpers/archiver.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
'use strict';
2+
const config = require('./config.js');
23
const fs = require("fs"),
34
path = require("path");
45

@@ -76,6 +77,14 @@ const archiveSpecs = (runSettings, filePath, excludeFiles, md5data) => {
7677
archive.append(packageJSONString, {name: `${cypressAppendFilesZipLocation}browserstack-package.json`});
7778
}
7879

80+
//Create copy of package.json
81+
if(fs.existsSync('package.json')){
82+
let originalPackageJson = JSON.parse(fs.readFileSync('package.json'));
83+
let originalPackageJsonString = JSON.stringify(originalPackageJson, null, 4);
84+
archive.append(originalPackageJsonString, {name: `${cypressAppendFilesZipLocation}userPackage.json`});
85+
logger.debug(`Created copy of package.json in ${config.packageDirName} folder`)
86+
}
87+
7988
// do not add cypress.json if arg provided is false
8089
if (
8190
runSettings.cypress_config_file &&

bin/helpers/checkUploaded.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ const checkUploadedMd5 = (bsConfig, args, instrumentBlocks) => {
8686
zipUrlPresent: false,
8787
packageUrlPresent: false,
8888
};
89+
utils.setCypressNpmDependency(bsConfig);
8990
if (args["force-upload"]) {
9091
logger.debug("force-upload set to true. Uploading tests and npm packages.");
9192
return resolve(obj);

bin/helpers/config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ if(config.env !== "production") {
1313
}
1414

1515
config.cypress_v1 = `${config.rails_host}/automate/cypress/v1`;
16+
config.cypress_v2 = `${config.rails_host}/automate/cypress/v2`;
1617
config.buildUrl = `${config.cypress_v1}/builds/`;
18+
config.buildUrlV2 = `${config.cypress_v2}/builds/`;
1719
config.buildStopUrl = `${config.cypress_v1}/builds/stop/`;
1820
config.checkMd5sum = `${config.cypress_v1}/md5sumcheck/`;
1921
config.getInitialDetails = `${config.cypress_v1}/get_initial_details/`;

bin/helpers/constants.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ const userMessages = {
110110
"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",
111111
SPEC_LIMIT_SUCCESS_MESSAGE:
112112
"Spec timeout specified as <x> minutes. If any of your specs exceed the specified time limit, it would be forcibly killed by BrowserStack",
113+
NO_CONNECTION_WHILE_UPDATING_UPLOAD_PROGRESS_BAR:
114+
"Unable to determine zip upload progress due to undefined/null connection request"
113115
};
114116

115117
const validationMessages = {

bin/helpers/package-diff.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"use strict";
2+
const fs = require("fs");
3+
const path = require("path");
4+
const logger = require("./logger").winstonLogger;
5+
6+
exports.run = (basePath, comparePath) => {
7+
if (!basePath || !comparePath) {
8+
logger.debug("Skipping package difference check.");
9+
}
10+
11+
let base;
12+
let compare;
13+
let isDiff = false;
14+
try {
15+
base = readModules(basePath);
16+
compare = readModules(comparePath);
17+
} catch (error) {
18+
logger.debug('Unable to process package difference');
19+
return isDiff;
20+
}
21+
22+
Object.keys(base.deps).forEach((baseKey) => {
23+
if (baseKey in compare.deps) {
24+
if (base.deps[baseKey] !== compare.deps[baseKey]) {
25+
isDiff = true;
26+
return;
27+
}
28+
} else {
29+
isDiff = true;
30+
return;
31+
}
32+
});
33+
return isDiff;
34+
};
35+
const readModules = (location) => {
36+
const table = {};
37+
38+
// Resolve package dependencies
39+
if (location.indexOf("package.json") !== -1) {
40+
const data = fs.readFileSync(location.replace(":dev", ""), "utf-8");
41+
let parsed;
42+
try {
43+
parsed = JSON.parse(data);
44+
} catch (e) {
45+
parsed = false;
46+
}
47+
if (!parsed) {
48+
return;
49+
}
50+
51+
const depsKey =
52+
location.indexOf(":dev") !== -1 ? "devDependencies" : "dependencies";
53+
const deps = parsed[depsKey]
54+
? parsed[depsKey]
55+
: parsed.dependencies || parsed.devDependencies;
56+
57+
Object.keys(deps).forEach((key) => {
58+
deps[key] = deps[key].replace(/\^|~/g, "");
59+
});
60+
return {
61+
name: `${location} {${depsKey}}`,
62+
deps,
63+
};
64+
}
65+
66+
fs.readdirSync(location)
67+
.filter((name) => name !== ".bin")
68+
.map((name) => {
69+
const pkg = path.join(location, name, "package.json");
70+
const exists = fs.existsSync(pkg);
71+
if (!exists) {
72+
return;
73+
}
74+
75+
const data = fs.readFileSync(pkg, "utf-8");
76+
let parsed;
77+
78+
try {
79+
parsed = JSON.parse(data);
80+
} catch (e) {
81+
parsed = false;
82+
}
83+
if (!parsed) {
84+
return;
85+
}
86+
87+
table[name] = parsed.version;
88+
});
89+
return {
90+
name: location,
91+
deps: table,
92+
};
93+
};

bin/helpers/sync/specsSummary.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ let printSpecsRunSummary = (data, machines, customErrorsToPrint) => {
3333
});
3434

3535
logger.info(`Total tests: ${summary.total}, passed: ${summary.passed}, failed: ${summary.failed}, skipped: ${summary.skipped}, passed_with_skipped: ${summary.passed_with_skipped}, pending: ${summary.pending}`);
36-
logger.info(`Done in ${data.duration/1000} seconds using ${machines} machines\n`);
36+
logger.info(`Done in ${data.duration} seconds using ${data.parallels} machines\n`);
37+
winstonlogger.debug(`CLI calculated duration is ${data.cliDuration/1000}`);
3738

3839
if (customErrorsToPrint && customErrorsToPrint.length > 0) {
3940
for (const error of customErrorsToPrint) {

bin/helpers/sync/syncSpecsLogs.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ let specSummary = {
1414
"buildError": null,
1515
"specs": [],
1616
"duration": null,
17+
"parallels": null,
18+
"cliDuration": null,
1719
"customErrorsToPrint": []
1820
}
1921

@@ -24,7 +26,7 @@ if (!isNaN(terminalWidth)) lineSeparator = "\n" + "-".repeat(terminalWidth);
2426

2527
let getOptions = (auth, build_id) => {
2628
return {
27-
url: `${config.buildUrl}${build_id}`,
29+
url: `${config.buildUrlV2}${build_id}`,
2830
auth: {
2931
user: auth.username,
3032
password: auth.access_key
@@ -120,7 +122,7 @@ let printSpecsStatus = (bsConfig, buildDetails, rawArgs, buildReportData) => {
120122
}
121123
}
122124
logger.info(lineSeparator);
123-
specSummary.duration = endTime - startTime
125+
specSummary.cliDuration = endTime - startTime
124126
resolve(specSummary);
125127
}
126128
);
@@ -147,15 +149,15 @@ let whileProcess = (whilstCallback) => {
147149
switch (response.statusCode) {
148150
case 202: // get data here and print it
149151
n = 2
150-
showSpecsStatus(body);
152+
showSpecsStatus(body, 202);
151153
return setTimeout(whilstCallback, timeout * n, null);
152154
case 204: // No data available, wait for some time and ask again
153155
n = 1
154156
return setTimeout(whilstCallback, timeout * n, null);
155157
case 200: // Build is completed.
156158
whileLoop = false;
157159
endTime = Date.now();
158-
showSpecsStatus(body);
160+
showSpecsStatus(body, 200);
159161
return specSummary.exitCode == Constants.BUILD_FAILED_EXIT_CODE ?
160162
whilstCallback({ status: 204, message: "No specs ran in the build"} ) : whilstCallback(null, body);
161163
default:
@@ -169,9 +171,9 @@ let getStackTraceUrl = () => {
169171
return specSummary.buildError
170172
}
171173

172-
let showSpecsStatus = (data) => {
174+
let showSpecsStatus = (data, statusCode) => {
173175
let specData = JSON.parse(data);
174-
specData.forEach(specDetails => {
176+
specData["specData"].forEach(specDetails => {
175177
if (specDetails.type === Constants.CYPRESS_CUSTOM_ERRORS_TO_PRINT_KEY) {
176178
addCustomErrorToPrint(specDetails);
177179
} else {
@@ -190,6 +192,17 @@ let showSpecsStatus = (data) => {
190192
}
191193
}
192194
});
195+
if ( statusCode != 200 ) return;
196+
// Below block is for printing build details, return if non 200 status code
197+
if ("buildData" in specData) {
198+
const buildDetails = specData.buildData;
199+
const totalDuration = (utils.isUndefined(buildDetails.duration)) ? "-" : buildDetails.duration.total_duration
200+
const parallels = (utils.isUndefined(buildDetails.parallels)) ? "-" : buildDetails.parallels
201+
specSummary.duration = totalDuration;
202+
specSummary.parallels = parallels;
203+
} else {
204+
logger.debug(`Build details not sent`)
205+
}
193206
}
194207

195208
let printInitialLog = () => {

bin/helpers/utils.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ const { v4: uuidv4 } = require('uuid');
88
const browserstack = require('browserstack-local');
99
const crypto = require('crypto');
1010
const util = require('util');
11+
const { promisify } = require('util');
12+
const readdir = promisify(fs.readdir);
13+
const stat = promisify(fs.stat);
1114

1215
const usageReporting = require("./usageReporting"),
1316
logger = require("./logger").winstonLogger,
@@ -328,6 +331,23 @@ exports.setCypressTestSuiteType = (bsConfig) => {
328331
logger.debug(`Setting cypress test suite type as ${bsConfig.run_settings.cypressTestSuiteType}`);
329332
}
330333

334+
exports.setCypressNpmDependency = (bsConfig) => {
335+
const runSettings = bsConfig.run_settings;
336+
if (runSettings.npm_dependencies !== undefined &&
337+
Object.keys(runSettings.npm_dependencies).length !== 0 &&
338+
typeof runSettings.npm_dependencies === 'object') {
339+
if (!("cypress" in runSettings.npm_dependencies) && runSettings.cypressTestSuiteType === Constants.CYPRESS_V10_AND_ABOVE_TYPE) {
340+
logger.warn("Missing cypress not found in npm_dependencies");
341+
if("cypress_version" in runSettings){
342+
runSettings.npm_dependencies.cypress = `^${runSettings.cypress_version.toString().split(".")[0]}`;
343+
} else if (runSettings.cypressTestSuiteType === Constants.CYPRESS_V10_AND_ABOVE_TYPE) {
344+
runSettings.npm_dependencies.cypress = "latest";
345+
}
346+
logger.warn(`Adding cypress version ${runSettings.npm_dependencies.cypress} in npm_dependencies`);
347+
}
348+
}
349+
}
350+
331351
exports.verifyGeolocationOption = () => {
332352
let glOptionsSet = (this.searchForOption('-gl') || this.searchForOption('--gl'));
333353
let geoHyphenLocationOptionsSet = (this.searchForOption('-geo-location') || this.searchForOption('--geo-location'));
@@ -1278,6 +1298,34 @@ exports.fetchZipSize = (fileName) => {
12781298
}
12791299
}
12801300

1301+
const getDirectorySize = async function(dir) {
1302+
try{
1303+
const subdirs = (await readdir(dir));
1304+
const files = await Promise.all(subdirs.map(async (subdir) => {
1305+
const res = path.resolve(dir, subdir);
1306+
const s = (await stat(res));
1307+
return s.isDirectory() ? getDirectorySize(res) : (s.size);
1308+
}));
1309+
return files.reduce((a, f) => a+f, 0);
1310+
}catch(e){
1311+
console.log(`Error ${e}`)
1312+
logger.debug('Failed to get file or directory.');
1313+
return 0;
1314+
}
1315+
};
1316+
1317+
exports.fetchFolderSize = async (dir) => {
1318+
try {
1319+
if(fs.existsSync(dir)){
1320+
return (await getDirectorySize(dir) / 1024 / 1024);
1321+
}
1322+
return 0;
1323+
} catch (error) {
1324+
logger.debug(`Failed to get directory size.`);
1325+
return 0;
1326+
}
1327+
}
1328+
12811329
exports.getVideoConfig = (cypressConfig) => {
12821330
let conf = {
12831331
video: true,

0 commit comments

Comments
 (0)