Skip to content

Commit e64fd6d

Browse files
Karan NagpalKaran Nagpal
authored andcommitted
Merge branch 'master' of github.com:browserstack/browserstack-cypress-cli into custom_reporting
2 parents cab3e32 + 0040ad8 commit e64fd6d

File tree

15 files changed

+751
-177
lines changed

15 files changed

+751
-177
lines changed

bin/commands/runs.js

Lines changed: 100 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const archiver = require("../helpers/archiver"),
99
utils = require("../helpers/utils"),
1010
fileHelpers = require("../helpers/fileHelpers"),
1111
syncRunner = require("../helpers/syncRunner"),
12+
checkUploaded = require("../helpers/checkUploaded"),
1213
reportGenerator = require('../helpers/reporterHTML').reportGenerator,
1314
{initTimeComponents, markBlockStart, markBlockEnd, getTimeComponents} = require('../helpers/timeComponents'),
1415
downloadBuildArtifacts = require('../helpers/buildArtifacts').downloadBuildArtifacts;
@@ -85,117 +86,125 @@ module.exports = function run(args) {
8586

8687
// warn if specFiles cross our limit
8788
utils.warnSpecLimit(bsConfig, args, specFiles);
88-
8989
markBlockEnd('preArchiveSteps');
90-
// Archive the spec files
91-
markBlockStart('zip');
92-
markBlockStart('zip.archive');
93-
return archiver.archive(bsConfig.run_settings, config.fileName, args.exclude).then(function (data) {
94-
95-
markBlockEnd('zip.archive');
96-
// Uploaded zip file
97-
markBlockStart('zip.zipUpload');
98-
return zipUploader.zipUpload(bsConfig, config.fileName).then(async function (zip) {
99-
100-
markBlockEnd('zip.zipUpload');
101-
markBlockEnd('zip');
102-
// Create build
103-
104-
//setup Local Testing
105-
markBlockStart('localSetup');
106-
let bs_local = await utils.setupLocalTesting(bsConfig, args);
107-
markBlockEnd('localSetup');
108-
markBlockStart('createBuild');
109-
return build.createBuild(bsConfig, zip).then(function (data) {
110-
markBlockEnd('createBuild');
111-
markBlockEnd('total');
112-
let message = `${data.message}! ${Constants.userMessages.BUILD_CREATED} with build id: ${data.build_id}`;
113-
let dashboardLink = `${Constants.userMessages.VISIT_DASHBOARD} ${data.dashboard_url}`;
114-
utils.exportResults(data.build_id, `${config.dashboardUrl}${data.build_id}`);
115-
if ((utils.isUndefined(bsConfig.run_settings.parallels) && utils.isUndefined(args.parallels)) || (!utils.isUndefined(bsConfig.run_settings.parallels) && bsConfig.run_settings.parallels == Constants.cliMessages.RUN.DEFAULT_PARALLEL_MESSAGE)) {
116-
logger.warn(Constants.userMessages.NO_PARALLELS);
117-
}
90+
markBlockStart('checkAlreadyUploaded');
91+
return checkUploaded.checkUploadedMd5(bsConfig, args).then(function (md5data) {
92+
markBlockEnd('checkAlreadyUploaded');
93+
94+
// Archive the spec files
95+
markBlockStart('zip');
96+
markBlockStart('zip.archive');
97+
return archiver.archive(bsConfig.run_settings, config.fileName, args.exclude, md5data).then(function (data) {
98+
markBlockEnd('zip.archive');
99+
100+
// Uploaded zip file
101+
markBlockStart('zip.zipUpload');
102+
return zipUploader.zipUpload(bsConfig, config.fileName, md5data).then(async function (zip) {
103+
markBlockEnd('zip.zipUpload');
104+
markBlockEnd('zip');
105+
// Create build
106+
107+
//setup Local Testing
108+
markBlockStart('localSetup');
109+
let bs_local = await utils.setupLocalTesting(bsConfig, args);
110+
markBlockEnd('localSetup');
111+
markBlockStart('createBuild');
112+
return build.createBuild(bsConfig, zip).then(function (data) {
113+
markBlockEnd('createBuild');
114+
markBlockEnd('total');
115+
let message = `${data.message}! ${Constants.userMessages.BUILD_CREATED} with build id: ${data.build_id}`;
116+
let dashboardLink = `${Constants.userMessages.VISIT_DASHBOARD} ${data.dashboard_url}`;
117+
utils.exportResults(data.build_id, `${config.dashboardUrl}${data.build_id}`);
118+
if ((utils.isUndefined(bsConfig.run_settings.parallels) && utils.isUndefined(args.parallels)) || (!utils.isUndefined(bsConfig.run_settings.parallels) && bsConfig.run_settings.parallels == Constants.cliMessages.RUN.DEFAULT_PARALLEL_MESSAGE)) {
119+
logger.warn(Constants.userMessages.NO_PARALLELS);
120+
}
118121

119-
if (bsConfig.run_settings.cypress_version && bsConfig.run_settings.cypress_version !== data.cypress_version) {
120-
let versionMessage = utils.versionChangedMessage(bsConfig.run_settings.cypress_version, data.cypress_version)
121-
logger.warn(versionMessage);
122-
}
122+
if (bsConfig.run_settings.cypress_version && bsConfig.run_settings.cypress_version !== data.cypress_version) {
123+
let versionMessage = utils.versionChangedMessage(bsConfig.run_settings.cypress_version, data.cypress_version)
124+
logger.warn(versionMessage);
125+
}
123126

124-
if (!args.disableNpmWarning && bsConfig.run_settings.npm_dependencies && Object.keys(bsConfig.run_settings.npm_dependencies).length <= 0) {
125-
logger.warn(Constants.userMessages.NO_NPM_DEPENDENCIES);
126-
logger.warn(Constants.userMessages.NO_NPM_DEPENDENCIES_READ_MORE);
127-
}
127+
if (!args.disableNpmWarning && bsConfig.run_settings.npm_dependencies && Object.keys(bsConfig.run_settings.npm_dependencies).length <= 0) {
128+
logger.warn(Constants.userMessages.NO_NPM_DEPENDENCIES);
129+
logger.warn(Constants.userMessages.NO_NPM_DEPENDENCIES_READ_MORE);
130+
}
128131

129-
if (args.sync) {
130-
syncRunner.pollBuildStatus(bsConfig, data).then(async (exitCode) => {
132+
if (args.sync) {
133+
syncRunner.pollBuildStatus(bsConfig, data).then(async (exitCode) => {
131134

132-
// stop the Local instance
133-
await utils.stopLocalBinary(bsConfig, bs_local, args);
135+
// stop the Local instance
136+
await utils.stopLocalBinary(bsConfig, bs_local, args);
134137

135-
// waiting for 5 secs for upload to complete (as a safety measure)
136-
await new Promise(resolve => setTimeout(resolve, 5000));
138+
// waiting for 5 secs for upload to complete (as a safety measure)
139+
await new Promise(resolve => setTimeout(resolve, 5000));
137140

138-
// download build artifacts
139-
if (!utils.isUndefined(bsConfig.run_settings.downloads) && bsConfig.run_settings.downloads.length) {
140-
await downloadBuildArtifacts(bsConfig, data.build_id, args);
141-
}
141+
// download build artifacts
142+
if (!utils.isUndefined(bsConfig.run_settings.downloads) && bsConfig.run_settings.downloads.length) {
143+
await downloadBuildArtifacts(bsConfig, data.build_id, args);
144+
}
142145

143-
// Generate custom report!
144-
reportGenerator(bsConfig, data.build_id, args, function(){
145-
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null);
146-
utils.handleSyncExit(exitCode, data.dashboard_url);
146+
// Generate custom report!
147+
reportGenerator(bsConfig, data.build_id, args, function(){
148+
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null);
149+
utils.handleSyncExit(exitCode, data.dashboard_url);
150+
});
147151
});
148-
});
149-
} else {
150-
logger.info(Constants.userMessages.ASYNC_DOWNLOADS.replace('<build-id>', data.build_id));
151-
}
152-
153-
logger.info(message);
154-
logger.info(dashboardLink);
155-
if(!args.sync) logger.info(Constants.userMessages.EXIT_SYNC_CLI_MESSAGE.replace("<build-id>", data.build_id));
156-
let dataToSend = {
157-
time_components: getTimeComponents(),
158-
build_id: data.build_id,
159-
};
160-
if (bsConfig && bsConfig.connection_settings) {
161-
if (bsConfig.connection_settings.local_mode) {
162-
dataToSend.local_mode = bsConfig.connection_settings.local_mode;
152+
} else {
153+
logger.info(Constants.userMessages.ASYNC_DOWNLOADS.replace('<build-id>', data.build_id));
163154
}
164-
if (bsConfig.connection_settings.usedAutoLocal) {
165-
dataToSend.used_auto_local = bsConfig.connection_settings.usedAutoLocal;
155+
156+
logger.info(message);
157+
logger.info(dashboardLink);
158+
if(!args.sync) logger.info(Constants.userMessages.EXIT_SYNC_CLI_MESSAGE.replace("<build-id>", data.build_id));
159+
let dataToSend = {
160+
time_components: getTimeComponents(),
161+
build_id: data.build_id,
162+
};
163+
if (bsConfig && bsConfig.connection_settings) {
164+
if (bsConfig.connection_settings.local_mode) {
165+
dataToSend.local_mode = bsConfig.connection_settings.local_mode;
166+
}
167+
if (bsConfig.connection_settings.usedAutoLocal) {
168+
dataToSend.used_auto_local = bsConfig.connection_settings.usedAutoLocal;
169+
}
166170
}
167-
}
168-
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null, dataToSend);
169-
return;
170-
}).catch(async function (err) {
171-
// Build creation failed
171+
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null, dataToSend);
172+
return;
173+
}).catch(async function (err) {
174+
// Build creation failed
175+
logger.error(err);
176+
// stop the Local instance
177+
await utils.stopLocalBinary(bsConfig, bs_local, args);
178+
179+
utils.sendUsageReport(bsConfig, args, err, Constants.messageTypes.ERROR, 'build_failed');
180+
});
181+
}).catch(function (err) {
182+
// Zip Upload failed | Local Start failed
172183
logger.error(err);
173-
// stop the Local instance
174-
await utils.stopLocalBinary(bsConfig, bs_local, args);
175-
176-
utils.sendUsageReport(bsConfig, args, err, Constants.messageTypes.ERROR, 'build_failed');
184+
if(err === Constants.userMessages.LOCAL_START_FAILED){
185+
utils.sendUsageReport(bsConfig, args, `${err}\n${Constants.userMessages.LOCAL_START_FAILED}`, Constants.messageTypes.ERROR, 'local_start_failed');
186+
} else {
187+
logger.error(Constants.userMessages.ZIP_UPLOAD_FAILED);
188+
fileHelpers.deleteZip();
189+
utils.sendUsageReport(bsConfig, args, `${err}\n${Constants.userMessages.ZIP_UPLOAD_FAILED}`, Constants.messageTypes.ERROR, 'zip_upload_failed');
190+
}
177191
});
178192
}).catch(function (err) {
179-
// Zip Upload failed | Local Start failed
193+
// Zipping failed
180194
logger.error(err);
181-
if(err === Constants.userMessages.LOCAL_START_FAILED){
182-
utils.sendUsageReport(bsConfig, args, `${err}\n${Constants.userMessages.LOCAL_START_FAILED}`, Constants.messageTypes.ERROR, 'local_start_failed');
183-
} else {
184-
logger.error(Constants.userMessages.ZIP_UPLOAD_FAILED);
195+
logger.error(Constants.userMessages.FAILED_TO_ZIP);
196+
utils.sendUsageReport(bsConfig, args, `${err}\n${Constants.userMessages.FAILED_TO_ZIP}`, Constants.messageTypes.ERROR, 'zip_creation_failed');
197+
try {
185198
fileHelpers.deleteZip();
186-
utils.sendUsageReport(bsConfig, args, `${err}\n${Constants.userMessages.ZIP_UPLOAD_FAILED}`, Constants.messageTypes.ERROR, 'zip_upload_failed');
199+
} catch (err) {
200+
utils.sendUsageReport(bsConfig, args, Constants.userMessages.ZIP_DELETE_FAILED, Constants.messageTypes.ERROR, 'zip_deletion_failed');
187201
}
188202
});
189203
}).catch(function (err) {
190-
// Zipping failed
204+
// md5 check failed
191205
logger.error(err);
192-
logger.error(Constants.userMessages.FAILED_TO_ZIP);
193-
utils.sendUsageReport(bsConfig, args, `${err}\n${Constants.userMessages.FAILED_TO_ZIP}`, Constants.messageTypes.ERROR, 'zip_creation_failed');
194-
try {
195-
fileHelpers.deleteZip();
196-
} catch (err) {
197-
utils.sendUsageReport(bsConfig, args, Constants.userMessages.ZIP_DELETE_FAILED, Constants.messageTypes.ERROR, 'zip_deletion_failed');
198-
}
206+
logger.error(Constants.userMessages.FAILED_MD5_CHECK);
207+
utils.sendUsageReport(bsConfig, args, Constants.userMessages.MD5_CHECK_FAILED, Constants.messageTypes.ERROR, 'zip_already_uploaded_failed');
199208
});
200209
}).catch(function (err) {
201210
// browerstack.json is not valid

bin/helpers/archiver.js

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ const archiver = require("archiver"),
77
utils = require('../helpers/utils'),
88
path = require('path');
99

10-
const archiveSpecs = (runSettings, filePath, excludeFiles) => {
10+
const archiveSpecs = (runSettings, filePath, excludeFiles, md5data) => {
1111
return new Promise(function (resolve, reject) {
12+
if (md5data.zipUrlPresent) {
13+
return resolve('Zipping not required');
14+
}
1215
var output = fs.createWriteStream(filePath);
1316

1417
var cypressFolderPath = path.dirname(runSettings.cypressConfigFilePath);
@@ -41,8 +44,7 @@ const archiveSpecs = (runSettings, filePath, excludeFiles) => {
4144

4245
archive.pipe(output);
4346

44-
let ignoreFiles = getFilesToIgnore(runSettings, excludeFiles);
45-
47+
let ignoreFiles = utils.getFilesToIgnore(runSettings, excludeFiles);
4648
archive.glob(`**/*.+(${Constants.allowedFileTypes.join("|")})`, { cwd: cypressFolderPath, matchBase: true, ignore: ignoreFiles, dot:true });
4749

4850
let packageJSON = {};
@@ -78,21 +80,4 @@ const archiveSpecs = (runSettings, filePath, excludeFiles) => {
7880
});
7981
}
8082

81-
const getFilesToIgnore = (runSettings, excludeFiles) => {
82-
let ignoreFiles = Constants.filesToIgnoreWhileUploading;
83-
84-
// exclude files asked by the user
85-
// args will take precedence over config file
86-
if (!utils.isUndefined(excludeFiles)) {
87-
let excludePatterns = utils.fixCommaSeparatedString(excludeFiles).split(',');
88-
ignoreFiles = ignoreFiles.concat(excludePatterns);
89-
logger.info(`Excluding files matching: ${JSON.stringify(excludePatterns)}`);
90-
} else if (!utils.isUndefined(runSettings.exclude) && runSettings.exclude.length) {
91-
ignoreFiles = ignoreFiles.concat(runSettings.exclude);
92-
logger.info(`Excluding files matching: ${JSON.stringify(runSettings.exclude)}`);
93-
}
94-
95-
return ignoreFiles;
96-
}
97-
9883
exports.archive = archiveSpecs

bin/helpers/checkUploaded.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
'use strict';
2+
const request = require('request');
3+
4+
const crypto = require('crypto'),
5+
Constants = require('./constants'),
6+
hashHelper = require('./hashUtil'),
7+
config = require('./config'),
8+
path = require('path'),
9+
fs = require("fs"),
10+
utils = require('./utils');
11+
12+
13+
const checkSpecsMd5 = (runSettings, excludeFiles) => {
14+
return new Promise(function (resolve, reject) {
15+
let cypressFolderPath = path.dirname(runSettings.cypressConfigFilePath);
16+
let ignoreFiles = utils.getFilesToIgnore(runSettings, excludeFiles, false);
17+
let options = {
18+
cwd: cypressFolderPath,
19+
ignore: ignoreFiles,
20+
pattern: `**/*.+(${Constants.allowedFileTypes.join("|")})`
21+
};
22+
hashHelper.hashWrapper(options).then(function (data) {
23+
const outputHash = crypto.createHash(Constants.hashingOptions.algo);
24+
outputHash.update(data);
25+
let packageJSON = {};
26+
27+
if (typeof runSettings.package_config_options === 'object') {
28+
Object.assign(packageJSON, runSettings.package_config_options);
29+
}
30+
31+
if (typeof runSettings.npm_dependencies === 'object') {
32+
Object.assign(packageJSON, {
33+
devDependencies: runSettings.npm_dependencies,
34+
});
35+
}
36+
37+
if (Object.keys(packageJSON).length > 0) {
38+
let packageJSONString = JSON.stringify(packageJSON);
39+
outputHash.update(packageJSONString);
40+
}
41+
42+
if (
43+
runSettings.cypress_config_file &&
44+
runSettings.cypress_config_filename !== 'false'
45+
) {
46+
let cypressJSON = JSON.parse(
47+
fs.readFileSync(runSettings.cypressConfigFilePath)
48+
);
49+
let cypressJSONString = JSON.stringify(cypressJSON);
50+
outputHash.update(cypressJSONString);
51+
}
52+
resolve(outputHash.digest(Constants.hashingOptions.encoding));
53+
}).catch(function (error) {
54+
reject(error);
55+
});
56+
});
57+
};
58+
59+
const checkUploadedMd5 = (bsConfig, args) => {
60+
return new Promise(function (resolve) {
61+
let obj = {
62+
zipUrlPresent: false,
63+
};
64+
if (args["force-upload"]) {
65+
return resolve(obj);
66+
}
67+
checkSpecsMd5(bsConfig.run_settings, args.exclude).then(function (md5data) {
68+
Object.assign(obj, {md5sum: md5data});
69+
let data = JSON.stringify({ zip_md5sum: md5data });
70+
71+
let options = {
72+
url: config.checkMd5sum,
73+
auth: {
74+
user: bsConfig.auth.username,
75+
password: bsConfig.auth.access_key
76+
},
77+
headers: {
78+
'Content-Type': 'application/json',
79+
"User-Agent": utils.getUserAgent(),
80+
},
81+
body: data
82+
};
83+
84+
request.post(options, function (err, resp, body) {
85+
if (err) {
86+
resolve(obj);
87+
} else {
88+
let zipData = null;
89+
try {
90+
zipData = JSON.parse(body);
91+
} catch (error) {
92+
zipData = {};
93+
}
94+
if (resp.statusCode === 200 && !utils.isUndefined(zipData.zipUrl)) {
95+
Object.assign(obj, zipData, {zipUrlPresent: true});
96+
}
97+
resolve(obj);
98+
}
99+
});
100+
}).catch((error) => {
101+
resolve({zipUrlPresent: false});
102+
});
103+
});
104+
};
105+
106+
exports.checkUploadedMd5 = checkUploadedMd5;

bin/helpers/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ if(config.env !== "production") {
1515
config.cypress_v1 = `${config.rails_host}/automate/cypress/v1`;
1616
config.buildUrl = `${config.cypress_v1}/builds/`;
1717
config.buildStopUrl = `${config.cypress_v1}/builds/stop/`;
18+
config.checkMd5sum = `${config.cypress_v1}/md5sumcheck/`;
1819
config.fileName = "tests.zip";
1920
config.retries = 5;
2021
config.networkErrorExitCode = 2;

0 commit comments

Comments
 (0)