Skip to content

Commit fe41c93

Browse files
Merge pull request #78 from browserstack/sync-cli-part-2
Sync cli part 2
2 parents e616c67 + 37d7936 commit fe41c93

File tree

11 files changed

+592
-38
lines changed

11 files changed

+592
-38
lines changed

bin/commands/runs.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ module.exports = function run(args) {
6969
if (args.sync) {
7070
syncRunner.pollBuildStatus(bsConfig, data).then((exitCode) => {
7171
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null);
72+
logger.info(Constants.userMessages.BUILD_REPORT_MESSAGE);
73+
logger.info(data.dashboard_url)
7274
process.exit(exitCode);
7375
});
7476
}

bin/helpers/constants.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
const syncCLI = {
2-
FAILED_SPEC_DETAILS_COL_HEADER: ['Spec', 'Status', 'Browser', 'BrowserStack Session ID']
2+
FAILED_SPEC_DETAILS_COL_HEADER: ['Spec', 'Status', 'Browser', 'BrowserStack Session ID'],
3+
LOGS: {
4+
INIT_LOG: "Running Tests: ..."
5+
},
6+
INITIAL_DELAY_MULTIPLIER: 10
37
};
48

59
const userMessages = {

bin/helpers/sync/failedSpecsDetails.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ let failedSpecsDetails = (data) => {
2929
let specData = [specResultHeader]; // 2-D array
3030

3131
data.forEach((spec) => {
32+
if (spec.status.toLowerCase() === 'passed') {
33+
return;
34+
}
3235
if (spec.status && spec.status.toLowerCase() === 'failed' && !failedSpecs)
3336
failedSpecs = true;
3437

bin/helpers/sync/specsSummary.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ let printSpecsRunSummary = (data, time, machines) => {
3030
});
3131

3232
logger.info(`Total tests: ${summary.total}, passed: ${summary.passed}, failed: ${summary.failed}, skipped: ${summary.skipped}`);
33-
logger.info(`Done in ${time} using ${machines} machines\n`);
33+
logger.info(`Done in ${time/1000} seconds using ${machines} machines\n`);
3434

3535
resolve(data);
3636
})

bin/helpers/sync/syncSpecsLogs.js

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
"use strict";
2+
const request = require("request"),
3+
config = require("../config"),
4+
utils = require("../utils"),
5+
logger = require("../logger").syncCliLogger,
6+
async = require('async'),
7+
Constants = require("../constants"),
8+
tableStream = require('table').createStream,
9+
chalk = require('chalk');
10+
11+
let whileLoop = true, whileTries = 40, options, timeout = 3000, n = 10, tableConfig, stream, startTime, endTime;
12+
let specSummary = {
13+
"specs": [],
14+
"duration": null
15+
}
16+
17+
18+
let getOptions = (auth, build_id) => {
19+
return {
20+
url: `${config.buildUrl}${build_id}`,
21+
auth: {
22+
user: auth.username,
23+
password: auth.access_key
24+
},
25+
headers: {
26+
"Content-Type": "application/json",
27+
"User-Agent": utils.getUserAgent()
28+
}
29+
};
30+
}
31+
32+
let getTableConfig = () => {
33+
return {
34+
border: getBorderConfig(),
35+
singleLine: true,
36+
columns: {
37+
0: { alignment: 'right' }
38+
},
39+
columnDefault: {
40+
width: 50
41+
},
42+
columnCount: 2
43+
};
44+
}
45+
46+
let getBorderConfig = () => {
47+
return {
48+
topBody: `-`,
49+
topJoin: ``,
50+
topLeft: ``,
51+
topRight: ``,
52+
53+
bottomBody: `-`,
54+
bottomJoin: ``,
55+
bottomLeft: ``,
56+
bottomRight: ``,
57+
58+
bodyLeft: ``,
59+
bodyRight: ``,
60+
bodyJoin: ``,
61+
62+
joinBody: ``,
63+
joinLeft: ``,
64+
joinRight: ``,
65+
joinJoin: ``
66+
}
67+
}
68+
69+
let printSpecsStatus = (bsConfig, buildDetails) => {
70+
return new Promise((resolve, reject) => {
71+
options = getOptions(bsConfig.auth, buildDetails.build_id)
72+
tableConfig = getTableConfig();
73+
stream = tableStream(tableConfig);
74+
75+
async.whilst(
76+
function() { // condition for loop
77+
return whileLoop;
78+
},
79+
function(callback) { // actual loop
80+
whileProcess(callback)
81+
},
82+
function(err, result) { // when loop ends
83+
specSummary.duration = endTime - startTime
84+
logger.info();
85+
resolve(specSummary)
86+
}
87+
);
88+
});
89+
};
90+
91+
let whileProcess = (whilstCallback) => {
92+
request.post(options, function(error, response, body) {
93+
if (error) {
94+
return whilstCallback(error);
95+
}
96+
switch (response.statusCode) {
97+
case 202: // get data here and print it
98+
n = 2
99+
showSpecsStatus(body);
100+
return setTimeout(whilstCallback, timeout * n, null);
101+
case 204: // No data available, wait for some time and ask again
102+
n = 1
103+
return setTimeout(whilstCallback, timeout * n, null);
104+
case 200: // Build is completed.
105+
whileLoop = false;
106+
endTime = Date.now();
107+
showSpecsStatus(body);
108+
return whilstCallback(null, body);
109+
default:
110+
whileLoop = false;
111+
return whilstCallback({ status: response.statusCode, message: body });
112+
}
113+
});
114+
}
115+
116+
let showSpecsStatus = (data) => {
117+
let specData = JSON.parse(data);
118+
specData.forEach(specDetails => {
119+
if (specDetails == "created") {
120+
printInitialLog();
121+
} else {
122+
printSpecData(JSON.parse(specDetails))
123+
}
124+
});
125+
}
126+
127+
let printInitialLog = () => {
128+
startTime = Date.now();
129+
logger.info(Constants.syncCLI.LOGS.INIT_LOG)
130+
n = Constants.syncCLI.INITIAL_DELAY_MULTIPLIER
131+
}
132+
133+
let printSpecData = (data) => {
134+
let combination = getCombinationName(data["spec"]);
135+
let status = getStatus(data["spec"]["status"]);
136+
writeToTable(combination, data["path"], status)
137+
addSpecToSummary(data["path"], data["spec"]["status"], combination, data["session_id"])
138+
}
139+
140+
let writeToTable = (combination, specName, status) => {
141+
stream.write([combination + ":", `${specName} ${status}`]);
142+
}
143+
144+
let addSpecToSummary = (specName, status, combination, session_id) => {
145+
// Format for part 3: {specName: 'spec1.failed.js', status: 'Failed', combination: 'Win 10 / Chrome 78', sessionId: '3d3rdf3r...'},
146+
specSummary["specs"].push({
147+
"specName": specName,
148+
"status": status,
149+
"combination": combination,
150+
"sessionId": session_id
151+
})
152+
}
153+
154+
let getCombinationName = (spec) => {
155+
return `${spec["os"]} ${spec["osVersion"]} / ${spec["browser"]} ${spec["browserVersion"]}`
156+
}
157+
158+
let getStatus = (status) => {
159+
switch(status) {
160+
case "passed":
161+
return chalk.green("✔");
162+
case "failed":
163+
return chalk.red("✘");
164+
default:
165+
return chalk.blue(`[${status}]`);
166+
}
167+
}
168+
169+
exports.printSpecsStatus = printSpecsStatus;

bin/helpers/syncRunner.js

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@ const Config = require("./config"),
44
Constants = require("./constants"),
55
utils = require("./utils"),
66
request = require('request'),
7+
syncSpecsLogs = require('./sync/syncSpecsLogs'),
78
specDetails = require('./sync/failedSpecsDetails'),
89
specsSummary = require('./sync/specsSummary'),
910
{ table, getBorderCharacters } = require('table'),
1011
chalk = require('chalk');
1112

1213
exports.pollBuildStatus = (bsConfig, buildDetails) => {
13-
logBuildDetails(bsConfig, buildDetails);
14-
printSpecsStatus().then((data) => {
15-
return specsSummary.printSpecsRunSummary(data.specs, data.time, data.machines);
16-
}).then((data) => {
17-
return specDetails.failedSpecsDetails(data);
18-
}).then((successExitCode) => {
19-
return resolveExitCode(successExitCode); // exit code 0
20-
}).catch((nonZeroExitCode) => {
21-
return resolveExitCode(nonZeroExitCode); // exit code 1
22-
}).finally(() => {
23-
logger.info(Constants.userMessages.BUILD_REPORT_MESSAGE);
24-
logger.info(`${Config.dashboardUrl}${buildDetails.dashboard_url}`);
14+
return new Promise((resolve, reject) => {
15+
logBuildDetails(bsConfig, buildDetails);
16+
syncSpecsLogs.printSpecsStatus(bsConfig, buildDetails).then((data) => {
17+
return specsSummary.printSpecsRunSummary(data.specs, data.duration, buildDetails.machines);
18+
}).then((data) => {
19+
return specDetails.failedSpecsDetails(data);
20+
}).then((successExitCode) => {
21+
resolve(successExitCode); // exit code 0
22+
}).catch((nonZeroExitCode) => {
23+
resolve(nonZeroExitCode); // exit code 1
24+
})
2525
});
2626
};
2727

@@ -37,19 +37,3 @@ let logBuildDetails = (bsConfig, buildDetails) => {
3737
logger.info(parallelMessage);
3838
logger.info(`BrowserStack Dashboard: ${buildDetails.dashboard_url}`);
3939
};
40-
41-
let printSpecsStatus = () => {
42-
return new Promise(function (resolve, reject) {
43-
resolve();
44-
});
45-
};
46-
47-
let printSpecsRunSummary = () => {
48-
return new Promise(function (resolve, reject) {
49-
resolve();
50-
});
51-
};
52-
53-
let resolveExitCode = (exitCode) => {
54-
return new Promise((resolve, _reject) => { resolve(exitCode) });
55-
};

test/unit/bin/commands/runs.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ describe("runs", () => {
517517
let messageType = Constants.messageTypes.SUCCESS;
518518
let errorCode = null;
519519
let message = `Success! ${Constants.userMessages.BUILD_CREATED} with build id: random_build_id`;
520-
let dashboardLink = `${Constants.userMessages.VISIT_DASHBOARD} ${dashboardUrl}random_build_id`;
520+
let dashboardLink = `${Constants.userMessages.VISIT_DASHBOARD} ${dashboardUrl}`;
521521

522522
const runs = proxyquire("../../../../bin/commands/runs", {
523523
"../helpers/utils": {
@@ -564,7 +564,7 @@ describe("runs", () => {
564564
);
565565
archiverStub.returns(Promise.resolve("Zipping completed"));
566566
zipUploadStub.returns(Promise.resolve("zip uploaded"));
567-
createBuildStub.returns(Promise.resolve({ message: 'Success', build_id: 'random_build_id' }));
567+
createBuildStub.returns(Promise.resolve({ message: 'Success', build_id: 'random_build_id', dashboard_url: dashboardUrl }));
568568

569569
return runs(args)
570570
.then(function (_bsConfig) {

test/unit/bin/helpers/sync/failedSpecDetails.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,23 @@ const chai = require("chai"),
33
expect = chai.expect,
44
chaiAsPromised = require("chai-as-promised");
55

6+
const sinon = require("sinon");
67
chai.use(chaiAsPromised);
78
const specDetails = require('../../../../../bin/helpers/sync/failedSpecsDetails');
9+
var logger = require("../../../../../bin/helpers/logger").syncCliLogger;
810

911
describe("failedSpecsDetails", () => {
12+
var sandbox;
13+
14+
beforeEach(() => {
15+
sandbox = sinon.createSandbox();
16+
sandbox.stub(logger, 'info');
17+
});
18+
19+
afterEach(() => {
20+
sandbox.restore();
21+
});
22+
1023
context("data is empty", () => {
1124
let data = [];
1225
it('returns 0 exit code', () => {
@@ -30,7 +43,8 @@ describe("failedSpecsDetails", () => {
3043

3144
context("data has failed specs", () => {
3245
let data = [
33-
{specName: 'spec2.name.js', status: 'Failed', combination: 'Win 10 / Chrome 78', sessionId: '3d3rdf3r...'}
46+
{specName: 'spec2.name.js', status: 'Failed', combination: 'Win 10 / Chrome 78', sessionId: '3d3rdf3r...'},
47+
{specName: 'spec2.name.js', status: 'Passed', combination: 'Win 10 / Chrome 78', sessionId: '3d3rdf3r...'}
3448
];
3549

3650
it("returns 1 exit code", () => {

test/unit/bin/helpers/sync/specSummary.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ var specSummary = require('../../../../../bin/helpers/sync/specsSummary');
1010

1111
describe("printSpecsRunSummary", () => {
1212
context("data is empty", () => {
13-
let data = [], time = '2 minutes', machines = 2;
13+
let data = [], time = 6000, machines = 2;
1414
it('returns passed specs data', () => {
1515
return specSummary.printSpecsRunSummary(data, time, machines).then((specsData) => {
1616
expect(data).to.equal(specsData);
@@ -19,7 +19,7 @@ describe("printSpecsRunSummary", () => {
1919
});
2020

2121
context("with data", () => {
22-
let time = '2 minutes',
22+
let time = 6000,
2323
machines = 2,
2424
data = [
2525
{specName: 'spec2.name.js', status: 'Failed', combination: 'Win 10 / Chrome 78', sessionId: '3d3rdf3r...'},
@@ -33,7 +33,7 @@ describe("printSpecsRunSummary", () => {
3333

3434
specSummary.printSpecsRunSummary(data, time, machines);
3535
sinon.assert.calledWith(loggerInfoSpy, 'Total tests: 4, passed: 1, failed: 2, skipped: 1');
36-
sinon.assert.calledWith(loggerInfoSpy, `Done in ${time} using ${machines} machines\n`);
36+
sinon.assert.calledWith(loggerInfoSpy, `Done in ${time / 1000} seconds using ${machines} machines\n`);
3737

3838
loggerInfoSpy.restore();
3939
});

0 commit comments

Comments
 (0)