Skip to content
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
198 changes: 114 additions & 84 deletions bin/helpers/buildArtifacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
const fs = require('fs'),
path = require('path');

const axios = require('axios'),
unzipper = require('unzipper');
const unzipper = require('unzipper');

const logger = require('./logger').winstonLogger,
utils = require("./utils"),
Constants = require("./constants"),
config = require("./config");

const request = require('request');


let BUILD_ARTIFACTS_TOTAL_COUNT = 0;
let BUILD_ARTIFACTS_FAIL_COUNT = 0;
Expand Down Expand Up @@ -95,32 +96,31 @@ const downloadAndUnzip = async (filePath, fileName, url) => {
let tmpFilePath = path.join(filePath, fileName);
const writer = fs.createWriteStream(tmpFilePath);

return axios({
method: 'get',
url: url,
responseType: 'stream',
}).then(response => {

//ensure that the user can call `then()` only when the file has
//been downloaded entirely.

return new Promise(async (resolve, reject) => {
response.data.pipe(writer);
let error = null;
writer.on('error', err => {
error = err;
writer.close();
reject(err);
});
writer.on('close', async () => {
if (!error) {
await unzipFile(filePath, fileName);
fs.unlinkSync(tmpFilePath);
resolve(true);
}
//no need to call the reject here, as it will have been called in the
//'error' stream;
});
return new Promise(async (resolve, reject) => {
request.get(url).on('response', function(response) {

if(response.statusCode != 200) {
reject();
} else {
//ensure that the user can call `then()` only when the file has
//been downloaded entirely.
response.pipe(writer);
let error = null;
writer.on('error', err => {
error = err;
writer.close();
reject(err);
});
writer.on('close', async () => {
if (!error) {
await unzipFile(filePath, fileName);
fs.unlinkSync(tmpFilePath);
resolve(true);
}
//no need to call the reject here, as it will have been called in the
//'error' stream;
});
}
});
});
}
Expand All @@ -135,7 +135,7 @@ const unzipFile = async (filePath, fileName) => {
}

const sendUpdatesToBstack = async (bsConfig, buildId, args, options, rawArgs) => {
let url = `${config.buildUrl}${buildId}/build_artifacts/status`;
options.url = `${config.buildUrl}${buildId}/build_artifacts/status`;

let cypressJSON = utils.getCypressJSON(bsConfig);

Expand All @@ -156,65 +156,95 @@ const sendUpdatesToBstack = async (bsConfig, buildId, args, options, rawArgs) =>
}
}

try {
await axios.post(url, data, options);
} catch (err) {
utils.sendUsageReport(bsConfig, args, err, Constants.messageTypes.ERROR, 'api_failed_build_artifacts_status_update', null, rawArgs);
}
options.formData = data.toString();
let responseData = null;
return new Promise (async (resolve, reject) => {
request.post(options, function (err, resp, data) {
if(err) {
utils.sendUsageReport(bsConfig, args, err, Constants.messageTypes.ERROR, 'api_failed_build_artifacts_status_update', null, rawArgs);
reject(err);
} else {
try {
responseData = JSON.parse(data);
} catch(e) {
responseData = {};
}
if (resp.statusCode != 200) {
if (responseData && responseData["error"]) {
utils.sendUsageReport(bsConfig, args, responseData["error"], Constants.messageTypes.ERROR, 'api_failed_build_artifacts_status_update', null, rawArgs);
reject(responseData["error"])
}
}
}
resolve()
});
});
}

exports.downloadBuildArtifacts = async (bsConfig, buildId, args, rawArgs) => {
BUILD_ARTIFACTS_FAIL_COUNT = 0;
BUILD_ARTIFACTS_TOTAL_COUNT = 0;

let url = `${config.buildUrl}${buildId}/build_artifacts`;
let options = {
auth: {
username: bsConfig.auth.username,
password: bsConfig.auth.access_key,
},
headers: {
'User-Agent': utils.getUserAgent(),
},
};

let message = null;
let messageType = null;
let errorCode = null;

try {
const res = await axios.get(url, options);
let buildDetails = res.data;

await createDirectories(buildId, buildDetails);
await parseAndDownloadArtifacts(buildId, buildDetails);

if (BUILD_ARTIFACTS_FAIL_COUNT > 0) {
messageType = Constants.messageTypes.ERROR;
message = Constants.userMessages.DOWNLOAD_BUILD_ARTIFACTS_FAILED.replace('<build-id>', buildId).replace('<machine-count>', BUILD_ARTIFACTS_FAIL_COUNT);
logger.error(message);
return new Promise ( async (resolve, reject) => {
BUILD_ARTIFACTS_FAIL_COUNT = 0;
BUILD_ARTIFACTS_TOTAL_COUNT = 0;

let options = {
url: `${config.buildUrl}${buildId}/build_artifacts`,
auth: {
username: bsConfig.auth.username,
password: bsConfig.auth.access_key,
},
headers: {
'User-Agent': utils.getUserAgent(),
},
};

let message = null;
let messageType = null;
let errorCode = null;
let buildDetails = null;
request.get(options, async function (err, resp, body) {
if(err) {
utils.sendUsageReport(bsConfig, args, err, Constants.messageTypes.ERROR, 'api_failed_build_artifacts', null, rawArgs);
process.exitCode = Constants.ERROR_EXIT_CODE;
} else {
messageType = Constants.messageTypes.SUCCESS;
message = Constants.userMessages.DOWNLOAD_BUILD_ARTIFACTS_SUCCESS.replace('<build-id>', buildId).replace('<user-path>', process.cwd());
logger.info(message);
try {
buildDetails = JSON.parse(body);
if(resp.statusCode != 200) {
logger.error('Downloading the build artifacts failed.');
logger.error(`Error: Request failed with status code ${resp.statusCode}`)
utils.sendUsageReport(bsConfig, args, buildDetails, Constants.messageTypes.ERROR, 'api_failed_build_artifacts', null, rawArgs);
process.exitCode = Constants.ERROR_EXIT_CODE;
} else {
await createDirectories(buildId, buildDetails);
await parseAndDownloadArtifacts(buildId, buildDetails);
if (BUILD_ARTIFACTS_FAIL_COUNT > 0) {
messageType = Constants.messageTypes.ERROR;
message = Constants.userMessages.DOWNLOAD_BUILD_ARTIFACTS_FAILED.replace('<build-id>', buildId).replace('<machine-count>', BUILD_ARTIFACTS_FAIL_COUNT);
logger.error(message);
process.exitCode = Constants.ERROR_EXIT_CODE;
} else {
messageType = Constants.messageTypes.SUCCESS;
message = Constants.userMessages.DOWNLOAD_BUILD_ARTIFACTS_SUCCESS.replace('<build-id>', buildId).replace('<user-path>', process.cwd());
logger.info(message);
}
await sendUpdatesToBstack(bsConfig, buildId, args, options, rawArgs)
utils.sendUsageReport(bsConfig, args, message, messageType, null, null, rawArgs);
}
} catch (err) {
messageType = Constants.messageTypes.ERROR;
errorCode = 'api_failed_build_artifacts';
if (BUILD_ARTIFACTS_FAIL_COUNT > 0) {
messageType = Constants.messageTypes.ERROR;
message = Constants.userMessages.DOWNLOAD_BUILD_ARTIFACTS_FAILED.replace('<build-id>', buildId).replace('<machine-count>', BUILD_ARTIFACTS_FAIL_COUNT);
logger.error(message);
} else {
logger.error('Downloading the build artifacts failed.');
}
utils.sendUsageReport(bsConfig, args, err, messageType, errorCode, null, rawArgs);
logger.error(`Error: Request failed with status code ${resp.statusCode}`)
process.exitCode = Constants.ERROR_EXIT_CODE;
}
}

await sendUpdatesToBstack(bsConfig, buildId, args, options, rawArgs);
utils.sendUsageReport(bsConfig, args, message, messageType, null, null, rawArgs);
} catch (err) {
messageType = Constants.messageTypes.ERROR;
errorCode = 'api_failed_build_artifacts';

if (BUILD_ARTIFACTS_FAIL_COUNT > 0) {
messageType = Constants.messageTypes.ERROR;
message = Constants.userMessages.DOWNLOAD_BUILD_ARTIFACTS_FAILED.replace('<build-id>', buildId).replace('<machine-count>', BUILD_ARTIFACTS_FAIL_COUNT);
logger.error(message);
} else {
logger.error('Downloading the build artifacts failed.');
}

utils.sendUsageReport(bsConfig, args, err, messageType, errorCode, null, rawArgs);
process.exitCode = Constants.ERROR_EXIT_CODE;
}
resolve();
});
});
};
2 changes: 2 additions & 0 deletions bin/helpers/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const userMessages = {
UPLOADING_TESTS_SUCCESS: "Uploaded tests successfully",
UPLOADING_NPM_PACKAGES: "Uploading required node_modules to BrowserStack",
UPLOADING_NPM_PACKAGES_SUCCESS: "Uploaded node_modules successfully",
SKIP_UPLOADING_TESTS: "Skipping zip upload since BrowserStack already has your test suite that has not changed since the last run.",
SKIP_UPLOADING_NPM_PACKAGES: "Skipping the upload of node_modules since BrowserStack has already cached your npm dependencies that have not changed since the last run.",
LOCAL_TRUE: "you will now be able to test localhost / private URLs",
LOCAL_FALSE: "you won't be able to test localhost / private URLs",
EXIT_SYNC_CLI_MESSAGE: "Exiting the CLI, but your build is still running. You can use the --sync option to keep getting test updates. You can also use the build-info <build-id> command now.",
Expand Down
84 changes: 67 additions & 17 deletions bin/helpers/reporterHTML.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ const fs = require('fs'),
logger = require('./logger').winstonLogger,
utils = require("./utils"),
Constants = require('./constants'),
config = require("./config"),
axios = require("axios");
config = require("./config");

let templatesDir = path.join(__dirname, '../', 'templates');

Expand Down Expand Up @@ -235,24 +234,74 @@ async function cypressReportData(report_data) {
return report_data;
}

function getConfigJsonResponse(combination) {
return new Promise(async (resolve, reject) => {
configJsonResponse = null;
configJsonError = false
request.get(combination.tests.config_json , function(err, resp, body) {
if(err) {
configJsonError = true;
reject([configJsonResponse, configJsonError]);
} else {
if(resp.statusCode != 200) {
configJsonError = true;
reject([configJsonResponse, configJsonError]);
} else {
try {
configJsonResponse = JSON.parse(body);
} catch (err) {
configJsonError = true
reject([configJsonResponse, configJsonError]);
}
}
}
resolve([configJsonResponse, configJsonError]);
});
});
}

function getResultsJsonResponse(combination) {
return new Promise(async (resolve, reject) => {
resultsJsonResponse = null
resultsJsonError = false;
request.get(combination.tests.result_json , function(err, resp, body) {
if(err) {
resultsJsonError = true;
reject([resultsJsonResponse, resultsJsonError]);
} else {
if(resp.statusCode != 200) {
resultsJsonError = true;
reject([resultsJsonResponse, resultsJsonError]);
} else {
try {
resultsJsonResponse = JSON.parse(body);
} catch (err) {
resultsJsonError = true
reject([resultsJsonResponse, resultsJsonError]);
}
}
}
resolve([resultsJsonResponse, resultsJsonError]);
});
});
}

function generateCypressCombinationSpecReportDataWithConfigJson(combination){
return new Promise(async (resolve, reject) => {
try {
let configJsonError, resultsJsonError;
let [configJsonResponse, resultsJsonResponse] = await axios.all([
axios.get(combination.tests.config_json).catch(function (error) {
configJsonError = true;
}),
axios.get(combination.tests.result_json).catch(function(error){
resultsJsonError = true;
})
]);
let configJson, resultsJson;

await Promise.all([getConfigJsonResponse(combination), getResultsJsonResponse(combination)]).then(function (successResult) {
[[configJson, configJsonError], [resultsJson, resultsJsonError]] = successResult;
}).catch(function (failureResult) {
[[configJson, configJsonError], [resultsJson, resultsJsonError]] = failureResult;
});

if(resultsJsonError || configJsonError){
resolve();
}
let tests = {};
let configJson = configJsonResponse.data;
let resultsJson = resultsJsonResponse.data;
if(utils.isUndefined(configJson.tests) || utils.isUndefined(resultsJson.tests)){
resolve();
}
Expand Down Expand Up @@ -287,14 +336,15 @@ function generateCypressCombinationSpecReportDataWithConfigJson(combination){
function generateCypressCombinationSpecReportDataWithoutConfigJson(combination){
return new Promise(async (resolve, reject) => {
try {
let resultsJsonError;
let resultsJsonResponse = await axios.get(combination.tests.result_json).catch(function(error){
resultsJsonError = true;
});
let resultsJson ,resultsJsonError;
await getResultsJsonResponse(combination).then(function (successResult) {
[resultsJson, resultsJsonError] = successResult
}).catch( function (failureResult) {
[resultsJson, resultsJsonError] = failureResult
})
if(resultsJsonError || utils.isUndefined(resultsJsonResponse)){
resolve();
}
let resultsJson = resultsJsonResponse.data;
let sessionTests = [];
if(utils.isUndefined(resultsJson.tests)){
resolve();
Expand Down
Loading