Skip to content

Commit 9d21523

Browse files
Karan NagpalKaran Nagpal
authored andcommitted
send reporter and reporter options
1 parent d2cabb6 commit 9d21523

File tree

6 files changed

+219
-4
lines changed

6 files changed

+219
-4
lines changed

bin/commands/runs.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const archiver = require("../helpers/archiver"),
1010
fileHelpers = require("../helpers/fileHelpers"),
1111
syncRunner = require("../helpers/syncRunner"),
1212
reportGenerator = require('../helpers/reporterHTML').reportGenerator,
13-
{initTimeComponents, markBlockStart, markBlockEnd, getTimeComponents} = require('../helpers/timeComponents');
13+
{initTimeComponents, markBlockStart, markBlockEnd, getTimeComponents} = require('../helpers/timeComponents'),
14+
downloadBuildArtifacts = require('../helpers/buildArtifacts').downloadBuildArtifacts;
1415

1516
module.exports = function run(args) {
1617
let bsConfigPath = utils.getConfigPath(args.cf);
@@ -68,6 +69,9 @@ module.exports = function run(args) {
6869
utils.setNoWrap(bsConfig, args);
6970
markBlockEnd('setConfig');
7071

72+
// set other cypress configs e.g. reporter and reporter-options
73+
utils.setCypressConfigs(bsConfig, args);
74+
7175
// Validate browserstack.json values and parallels specified via arguments
7276
markBlockStart('validateConfig');
7377
return capabilityHelper.validate(bsConfig, args).then(function (cypressJson) {
@@ -128,6 +132,9 @@ module.exports = function run(args) {
128132
// stop the Local instance
129133
await utils.stopLocalBinary(bsConfig, bs_local, args);
130134

135+
// download build artifacts
136+
await downloadBuildArtifacts(bsConfig, data.build_id, args);
137+
131138
// Generate custom report!
132139
reportGenerator(bsConfig, data.build_id, args, function(){
133140
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null);

bin/helpers/buildArtifacts.js

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
'use strict';
2+
3+
const fs = require('fs'),
4+
path = require('path'),
5+
https = require('https');
6+
7+
const axios = require('axios'),
8+
unzipper = require('unzipper');
9+
10+
const logger = require('./logger').winstonLogger,
11+
utils = require("./utils"),
12+
Constants = require("./constants"),
13+
config = require("./config");
14+
15+
16+
const parseAndDownloadArtifacts = async (buildId, data) => {
17+
return new Promise((resolve, reject) => {
18+
let all_promises = [];
19+
let combs = Object.keys(data);
20+
for(let i = 0; i < combs.length; i++) {
21+
let comb = combs[i];
22+
let sessions = Object.keys(data[comb]);
23+
for(let j = 0; j < sessions.length; j++) {
24+
let sessionId = sessions[j];
25+
let filePath = path.join('./', 'buildArtifacts', buildId, comb, sessionId);
26+
let fileName = 'buildArtifacts.zip';
27+
console.log(`adding promise for sessionID=${sessionId}`);
28+
process.env.BUILD_ARTIFACTS_TOTAL_COUNT = Number(process.env.BUILD_ARTIFACTS_TOTAL_COUNT) + 1
29+
all_promises.push(downloadAndUnzip(filePath, fileName, data[comb][sessionId]).catch((error) => {
30+
process.env.BUILD_ARTIFACTS_FAIL_COUNT = Number(process.env.BUILD_ARTIFACTS_FAIL_COUNT) + 1;
31+
reject;
32+
}));
33+
}
34+
}
35+
Promise.all(all_promises)
36+
.then(() => {
37+
resolve;
38+
})
39+
});
40+
}
41+
42+
const createDirectories = async (buildId, data) => {
43+
// create dir for buildArtifacts if not already present
44+
let artifactsDir = path.join('./', 'buildArtifacts');
45+
if (!fs.existsSync(artifactsDir)){
46+
fs.mkdirSync(artifactsDir);
47+
}
48+
49+
// create dir for buildId if not already present
50+
let buildDir = path.join('./', 'buildArtifacts', buildId);
51+
if (fs.existsSync(buildDir)){
52+
// remove dir in case already exists
53+
fs.rmdirSync(buildDir, { recursive: true, force: true });
54+
}
55+
fs.mkdirSync(buildDir);
56+
57+
// create subdirs for combinations inside build
58+
Object.keys(data).forEach(comb => {
59+
let combDir = path.join('./', 'buildArtifacts', buildId, comb);
60+
if (!fs.existsSync(combDir)){
61+
fs.mkdirSync(combDir);
62+
}
63+
64+
// create subdirs for each parellel consumed for each combination
65+
Object.keys(data[comb]).forEach(sessionId => {
66+
let sessionDir = path.join('./', 'buildArtifacts', buildId, comb, sessionId);
67+
if (!fs.existsSync(sessionDir)){
68+
fs.mkdirSync(sessionDir);
69+
}
70+
})
71+
});
72+
}
73+
74+
const downloadAndUnzip = async (filePath, fileName, url) => {
75+
return new Promise((resolve, reject) => {
76+
let tmpFilePath = path.join(filePath, fileName);
77+
https.get(url, function(response) {
78+
response.on('data', function (data) {
79+
fs.appendFileSync(tmpFilePath, data);
80+
});
81+
response.on('end', function() {
82+
fs.createReadStream(tmpFilePath).pipe(unzipper.Extract({ path: filePath })
83+
.on('close', function () {
84+
fs.unlinkSync(tmpFilePath);
85+
resolve;
86+
})
87+
.on('error', function(err) {
88+
reject;
89+
})
90+
);
91+
});
92+
response.on('error', function () {
93+
reject;
94+
})
95+
});
96+
});
97+
}
98+
99+
const sendUpdatesToBstack = async (bsConfig, buildId, args, options) => {
100+
let url = `${config.buildUrl}${buildId}/build_artifacts/status`;
101+
102+
let cypressJSON = utils.getCypressJSON(bsConfig);
103+
104+
let reporter = null;
105+
if(!utils.isUndefined(args.reporter)) {
106+
reporter = args.reporter;
107+
} else if(cypressJSON !== undefined){
108+
reporter = cypressJSON.reporter;
109+
}
110+
111+
let data = {
112+
feature_usage: {
113+
downloads: {
114+
eligible_download_folders: Number(process.env.BUILD_ARTIFACTS_TOTAL_COUNT),
115+
successfully_downloaded_folders: Number(process.env.BUILD_ARTIFACTS_TOTAL_COUNT) - Number(process.env.BUILD_ARTIFACTS_FAIL_COUNT)
116+
},
117+
reporter: reporter
118+
}
119+
}
120+
121+
try {
122+
await axios.put(url, data, options);
123+
} catch (err) {
124+
utils.sendUsageReport(bsConfig, args, err, Constants.messageTypes.ERROR, 'api_failed_build_artifacts_status_update');
125+
}
126+
}
127+
128+
exports.downloadBuildArtifacts = async (bsConfig, buildId, args) => {
129+
process.env.BUILD_ARTIFACTS_FAIL_COUNT = 0;
130+
process.env.BUILD_ARTIFACTS_TOTAL_COUNT = 0;
131+
132+
let url = `${config.buildUrl}${buildId}/build_artifacts`;
133+
let options = {
134+
auth: {
135+
username: bsConfig.auth.username,
136+
password: bsConfig.auth.access_key,
137+
},
138+
headers: {
139+
'User-Agent': utils.getUserAgent(),
140+
},
141+
};
142+
143+
let message = null;
144+
let messageType = null;
145+
let errorCode = null;
146+
let build;
147+
148+
try {
149+
const res = await axios.get(url, options);
150+
let buildDetails = res.data;
151+
152+
await createDirectories(buildId, buildDetails);
153+
await parseAndDownloadArtifacts(buildId, buildDetails);
154+
await sendUpdatesToBstack(bsConfig, buildId, args, options);
155+
156+
messageType = Constants.messageTypes.SUCCESS;
157+
message = `Build artifacts for build: ${buildId} were successfully downloaded.`;
158+
logger.info(message);
159+
} catch (err) {
160+
//TODO: handle error codes - 422 etc
161+
162+
message = err;
163+
messageType = Constants.messageTypes.ERROR;
164+
errorCode = 'api_failed_build_artifacts';
165+
166+
logger.error('Downloading the build artifacts failed.');
167+
logger.error(message);
168+
utils.sendUsageReport(bsConfig, args, message, messageType, errorCode);
169+
}
170+
};

bin/helpers/constants.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ const userMessages = {
4242
LOCAL_START_FAILED: "Local Testing setup failed.",
4343
LOCAL_STOP_FAILED: "Local Binary stop failed.",
4444
INVALID_LOCAL_MODE_WARNING: "Invalid value specified for local_mode. local_mode: (\"always-on\" | \"on-demand\"). For more info, check out https://www.browserstack.com/docs/automate/cypress/cli-reference",
45-
SPEC_LIMIT_WARNING: "You might not see all your results on the dashboard because of high spec count, please consider reducing the number of spec files in this folder."
45+
SPEC_LIMIT_WARNING: "You might not see all your results on the dashboard because of high spec count, please consider reducing the number of spec files in this folder.",
46+
DOWNLOAD_BUILD_ARTIFACTS: "Downloading build artifacts for the build <build-id> failed."
4647
};
4748

4849
const validationMessages = {
@@ -107,7 +108,9 @@ const cliMessages = {
107108
LOCAL_MODE: 'Accepted values: ("always-on" | "on-demand") - if you choose to keep the binary "always-on", it will speed up your tests by keeping the Local connection warmed up in the background; otherwise, you can choose to have it spawn and killed for every build',
108109
LOCAL_IDENTIFIER: "Accepted values: String - assign an identifier to your Local process instance",
109110
LOCAL_CONFIG_FILE: "Accepted values: String - path to local config-file to your Local process instance. Learn more at https://www.browserstack.com/local-testing/binary-params",
110-
SYNC_NO_WRAP: "Wrap the spec names in --sync mode in case of smaller terminal window size pass --no-wrap"
111+
SYNC_NO_WRAP: "Wrap the spec names in --sync mode in case of smaller terminal window size pass --no-wrap",
112+
REPORTER: "REPORTER",
113+
REPORTER_OPTIONS: "REPORTER_OPTIONS",
111114
},
112115
COMMON: {
113116
DISABLE_USAGE_REPORTING: "Disable usage reporting",

bin/helpers/utils.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,15 @@ exports.setParallels = (bsConfig, args, numOfSpecs) => {
140140
let maxParallels = browserCombinations.length * numOfSpecs;
141141
if (numOfSpecs <= 0) {
142142
bsConfig['run_settings']['parallels'] = browserCombinations.length;
143+
bsConfig['run_settings']['specs_count'] = numOfSpecs;
143144
return;
144145
}
145146
if (bsConfig['run_settings']['parallels'] > maxParallels && bsConfig['run_settings']['parallels'] != -1 ) {
146147
logger.warn(
147148
`Using ${maxParallels} machines instead of ${bsConfig['run_settings']['parallels']} that you configured as there are ${numOfSpecs} specs to be run on ${browserCombinations.length} browser combinations.`
148149
);
149150
bsConfig['run_settings']['parallels'] = maxParallels;
151+
bsConfig['run_settings']['specs_count'] = numOfSpecs;
150152
}
151153
};
152154

@@ -722,3 +724,25 @@ exports.deleteBaseUrlFromError = (err) => {
722724
return err.replace(/To test ([\s\S]*)on BrowserStack/g, 'To test on BrowserStack');
723725
}
724726

727+
// blindly send other passed configs with run_settings and handle at backend
728+
exports.setCypressConfigs = (bsConfig, args) => {
729+
if (!this.isUndefined(args.reporter)) {
730+
bsConfig["run_settings"]["reporter"] = args.reporter;
731+
}
732+
if (!this.isUndefined(args.reporterOptions)) {
733+
bsConfig["run_settings"]["reporter_options"] = args.reporterOptions;
734+
}
735+
}
736+
737+
exports.getCypressJSON = (bsConfig) => {
738+
if (
739+
bsConfig.runSettings.cypress_config_file &&
740+
bsConfig.runSettings.cypress_config_filename !== 'false'
741+
) {
742+
let cypressJSON = JSON.parse(
743+
fs.readFileSync(bsConfig.runSettings.cypressConfigFilePath)
744+
);
745+
return cypressJSON;
746+
}
747+
return undefined;
748+
}

bin/runner.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,16 @@ var argv = yargs
226226
default: false,
227227
describe: Constants.cliMessages.RUN.SYNC_NO_WRAP,
228228
type: "boolean"
229+
},
230+
'reporter': {
231+
default: undefined,
232+
describe: Constants.cliMessages.RUN.REPORTER,
233+
type: "string"
234+
},
235+
'reporter-options': {
236+
default: undefined,
237+
describe: Constants.cliMessages.RUN.REPORTER_OPTIONS,
238+
type: "string"
229239
}
230240
})
231241
.help('help')

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"nyc": "^15.0.1",
4444
"proxyquire": "^2.1.3",
4545
"rewire": "^5.0.0",
46-
"sinon": "^9.0.2"
46+
"sinon": "^9.0.2",
47+
"unzipper": "^0.10.11"
4748
}
4849
}

0 commit comments

Comments
 (0)