diff --git a/.gitignore b/.gitignore index 6c5454b4..66e3ce98 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ tests.zip package-lock.json .nyc_output/ .env.* +log/*.log +results/* diff --git a/bin/commands/generateReport.js b/bin/commands/generateReport.js new file mode 100644 index 00000000..21c74436 --- /dev/null +++ b/bin/commands/generateReport.js @@ -0,0 +1,39 @@ +'use strict'; + +const logger = require("../helpers/logger").winstonLogger, + Constants = require("../helpers/constants"), + utils = require("../helpers/utils"), + reporterHTML = require('../helpers/reporterHTML'); + + +module.exports = function generateReport(args) { + let bsConfigPath = utils.getConfigPath(args.cf); + let reportGenerator = reporterHTML.reportGenerator; + + return utils.validateBstackJson(bsConfigPath).then(function (bsConfig) { + // setting setDefaults to {} if not present and set via env variables or via args. + utils.setDefaults(bsConfig, args); + + // accept the username from command line if provided + utils.setUsername(bsConfig, args); + + // accept the access key from command line if provided + utils.setAccessKey(bsConfig, args); + + utils.setUsageReportingFlag(bsConfig, args.disableUsageReporting); + + // set cypress config filename + utils.setCypressConfigFilename(bsConfig, args); + + let messageType = Constants.messageTypes.INFO; + let errorCode = null; + let buildId = args._[1]; + + reportGenerator(bsConfig, buildId, args); + utils.sendUsageReport(bsConfig, args, 'generate-report called', messageType, errorCode); + }).catch(function (err) { + logger.error(err); + utils.setUsageReportingFlag(null, args.disableUsageReporting); + utils.sendUsageReport(null, args, err.message, Constants.messageTypes.ERROR, utils.getErrorCodeFromErr(err)); + }); +}; diff --git a/bin/commands/runs.js b/bin/commands/runs.js index 9d69c3cb..6d7a0a04 100644 --- a/bin/commands/runs.js +++ b/bin/commands/runs.js @@ -8,7 +8,8 @@ const archiver = require("../helpers/archiver"), Constants = require("../helpers/constants"), utils = require("../helpers/utils"), fileHelpers = require("../helpers/fileHelpers"), - syncRunner = require("../helpers/syncRunner"); + syncRunner = require("../helpers/syncRunner"), + reportGenerator = require('../helpers/reporterHTML').reportGenerator; module.exports = function run(args) { let bsConfigPath = utils.getConfigPath(args.cf); @@ -73,8 +74,11 @@ module.exports = function run(args) { } if (args.sync) { syncRunner.pollBuildStatus(bsConfig, data).then((exitCode) => { - utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null); - utils.handleSyncExit(exitCode, data.dashboard_url) + // Generate custom report! + reportGenerator(bsConfig, data.build_id, args, function(){ + utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null); + utils.handleSyncExit(exitCode, data.dashboard_url); + }); }); } diff --git a/bin/helpers/constants.js b/bin/helpers/constants.js index e0a085e6..e26bac6f 100644 --- a/bin/helpers/constants.js +++ b/bin/helpers/constants.js @@ -10,6 +10,7 @@ const syncCLI = { const userMessages = { BUILD_FAILED: "Build creation failed.", + BUILD_GENERATE_REPORT_FAILED: "Generating report for the build failed.", BUILD_CREATED: "Build created", BUILD_INFO_FAILED: "Failed to get build info.", BUILD_STOP_FAILED: "Failed to stop build.", @@ -98,6 +99,9 @@ const cliMessages = { ACCESS_KEY: "Your BrowserStack access key", NO_NPM_WARNING: "No NPM warning if npm_dependencies is empty", }, + GENERATE_REPORT: { + INFO: "Generates the build report" + }, }; const messageTypes = { diff --git a/bin/helpers/reporterHTML.js b/bin/helpers/reporterHTML.js new file mode 100644 index 00000000..322be0f8 --- /dev/null +++ b/bin/helpers/reporterHTML.js @@ -0,0 +1,198 @@ +const fs = require('fs'), + path = require('path'), + request = require('request'), + logger = require('./logger').winstonLogger, + utils = require("./utils"), + Constants = require('./constants'), + config = require("./config"); + +let templatesDir = path.join(__dirname, '../', 'templates'); + +function loadInlineCss() { + return loadFile(path.join(templatesDir, 'assets', 'browserstack-cypress-report.css')); +} + +function loadFile(fileName) { + return fs.readFileSync(fileName, 'utf8'); +} + +function createBodyBuildHeader(report_data){ + let projectNameSpan = ` ${report_data.project_name} `; + let buildNameSpan = ` ${report_data.build_name} `; + let buildMeta = `
${buildNameSpan} ${projectNameSpan}
`; + let buildLink = ``; + let buildHeader = `
${buildMeta} ${buildLink}
`; + return buildHeader; +} + +function createBodyBuildTable(report_data) { + let specs = Object.keys(report_data.rows), + specRow = '', + specSessions = '', + sessionBlocks = '', + specData, + specNameSpan, + specPathSpan, + specStats, + specStatsSpan, + specMeta, + sessionStatus, + sessionClass, + sessionStatusIcon, + sessionLink; + + specs.forEach((specName) => { + specData = report_data.rows[specName]; + + specNameSpan = ` ${specName} `; + specPathSpan = ` ${specData.path} `; + + specStats = buildSpecStats(specData.meta); + specStatsSpan = ` ${specStats.label} `; + + specMeta = `
${specNameSpan} ${specPathSpan} ${specStatsSpan}
`; + sessionBlocks = ''; + specData.sessions.forEach((specSession) => { + + sessionStatus = specSession.status; + sessionClass = sessionStatus === 'passed' ? 'session-passed' : 'session-failed'; + sessionStatusIcon = sessionStatus === 'passed' ? "✔ " : "✗ "; + + sessionLink = ` ${sessionStatusIcon} ${specSession.name} `; + + sessionDetail = `
${sessionLink}
`; + sessionBlocks = `${sessionBlocks} ${sessionDetail}`; + }); + specSessions = `
${sessionBlocks}
`; + specRow = `${specRow}
${specMeta} ${specSessions}
`; + }); + + + return `
${specRow}
`; +} + +function buildSpecStats(specMeta) { + let failedSpecs = specMeta.failed, + passedSpecs = specMeta.passed, + totalSpecs = specMeta.total, + specStats = {}; + + if (failedSpecs) { + specStats.label = `${failedSpecs}/${totalSpecs} FAILED`; + specStats.cssClass = 'spec-stats-failed'; + } else { + specStats.label = `${passedSpecs}/${totalSpecs} PASSED`; + specStats.cssClass = 'spec-stats-passed'; + } + + return specStats; +} + +let reportGenerator = (bsConfig, buildId, args, cb) => { + let options = { + url: `${config.buildUrl}${buildId}/custom_report`, + auth: { + user: bsConfig.auth.username, + password: bsConfig.auth.access_key, + }, + headers: { + 'User-Agent': utils.getUserAgent(), + }, + }; + + return request.get(options, function (err, resp, body) { + let message = null; + let messageType = null; + let errorCode = null; + let build; + + if (err) { + message = Constants.userMessages.BUILD_INFO_FAILED; + messageType = Constants.messageTypes.ERROR; + errorCode = 'api_failed_build_info'; + + logger.info(message); + } else { + try { + build = JSON.parse(body); + } catch (error) { + build = null; + } + } + + if (resp.statusCode == 299) { + messageType = Constants.messageTypes.INFO; + errorCode = 'api_deprecated'; + + if (build) { + message = build.message; + logger.info(message); + } else { + message = Constants.userMessages.API_DEPRECATED; + logger.info(message); + } + } else if (resp.statusCode != 200) { + messageType = Constants.messageTypes.ERROR; + errorCode = 'api_failed_build_generate_report'; + + if (build) { + message = `${ + Constants.userMessages.BUILD_GENERATE_REPORT_FAILED.replace('', buildId) + } with error: \n${JSON.stringify(build, null, 2)}`; + logger.error(message); + if (build.message === 'Unauthorized') errorCode = 'api_auth_failed'; + } else { + message = Constants.userMessages.BUILD_GENERATE_REPORT_FAILED.replace('', buildId); + logger.error(message); + } + } else { + messageType = Constants.messageTypes.SUCCESS; + message = `Report for build: ${buildId} was successfully created.`; + renderReportHTML(build); + logger.info(message); + } + utils.sendUsageReport(bsConfig, args, message, messageType, errorCode); + if (cb){ + cb(); + } + }); +} + +function renderReportHTML(report_data) { + let resultsDir = 'results'; + let metaCharSet = ``; + let metaViewPort = ` `; + let pageTitle = ` Browserstack Cypress Report `; + let inlineCss = ``; + let head = ` ${metaCharSet} ${metaViewPort} ${pageTitle} ${inlineCss} `; + let htmlOpenTag = ``; + let htmlClosetag = ``; + let bodyBuildHeader = createBodyBuildHeader(report_data); + let bodyBuildTable = createBodyBuildTable(report_data); + let bodyReporterContainer = `
${bodyBuildHeader} ${bodyBuildTable}
`; + let body = ` ${bodyReporterContainer} `; + let html = `${htmlOpenTag} ${head} ${body} ${htmlClosetag}`; + + + if (!fs.existsSync(resultsDir)){ + fs.mkdirSync(resultsDir); + } + + // Writing the JSON used in creating the HTML file. + fs.writeFileSync(`${resultsDir}/browserstack-cypress-report.json`, JSON.stringify(report_data), () => { + if(err) { + return logger.error(err); + } + logger.info("The JSON file is saved"); + }); + + // Writing the HTML file generated from the JSON data. + fs.writeFileSync(`${resultsDir}/browserstack-cypress-report.html`, html, () => { + if(err) { + return logger.error(err); + } + logger.info("The HTML file was saved!"); + }); +} + +exports.reportGenerator = reportGenerator; diff --git a/bin/runner.js b/bin/runner.js index bdc7f5b8..33bf92b6 100755 --- a/bin/runner.js +++ b/bin/runner.js @@ -209,6 +209,46 @@ var argv = yargs return require('./commands/runs')(argv); } }) + .command('generate-report', Constants.cliMessages.GENERATE_REPORT.INFO, function(yargs) { + argv = yargs + .usage('usage: $0 generate-report ') + .demand(1, Constants.cliMessages.BUILD.DEMAND) + .options({ + 'cf': { + alias: 'config-file', + describe: Constants.cliMessages.BUILD.DESC, + default: 'browserstack.json', + type: 'string', + nargs: 1, + demand: true, + demand: Constants.cliMessages.BUILD.CONFIG_DEMAND + }, + 'disable-usage-reporting': { + default: undefined, + description: Constants.cliMessages.COMMON.DISABLE_USAGE_REPORTING, + type: "boolean" + }, + 'u': { + alias: 'username', + describe: Constants.cliMessages.COMMON.USERNAME, + type: "string", + default: undefined + }, + 'k': { + alias: 'key', + describe: Constants.cliMessages.COMMON.ACCESS_KEY, + type: "string", + default: undefined + }, + }) + .help('help') + .wrap(null) + .argv + if (checkCommands(yargs, argv, 1)) { + logger.info(Constants.cliMessages.BUILD.INFO_MESSAGE + argv._[1]); + return require('./commands/generateReport')(argv); + } + }) .help('help') .wrap(null) .argv diff --git a/bin/templates/assets/browserstack-cypress-report.css b/bin/templates/assets/browserstack-cypress-report.css new file mode 100644 index 00000000..aad3e1ef --- /dev/null +++ b/bin/templates/assets/browserstack-cypress-report.css @@ -0,0 +1,104 @@ +.report-container { + font-family: HelveticaNeue; + width: 80%; + margin: 0 auto; + margin-top: 8px; + border: solid 1px #dddddd; + background-color: #f7f7f7; +} +.build-header { + padding: 10px 16px; + display: flex; + background-color: #f7f7f7; + border: solid 1px #ddd; +} +.build-meta { + flex-grow: 1; + flex-direction: column; + display: flex; +} +.spec-meta { + flex-direction: column; + display: flex; + padding: 16px; + justify-content: center; + margin-right: 40px; +} +.build-name { + font-size: 18px; + font-weight: bold; + color: #333; +} +.project-name { + font-size: 12px; + color: #666; +} +.build-link { + display: flex; + align-items: center; +} +.build-link a { + cursor: pointer; + font-size: 12px; + color: #009cfc; + text-decoration: none; +} +.spec-row { + display: flex; + border: solid 1px #ddd; +} +.spec-row:nth-child(odd) { + background-color: #fff; +} +.spec-row:nth-child(even) { + background-color: #fbfbfb; +} +.spec-name { + font-size: 16px; + color: #333 +} +.spec-path { + font-family: SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace; + color: #666; + padding-top: 8px; + font-size: 12px; +} +.spec-stats { + font-size: 12px; + font-weight: 500; + line-height: 1.5; + padding-top: 16px; +} +.spec-stats-failed { + color: #b22617; +} +.spec-stats-passed { + color: #68b300; +} +.spec-sessions { + display: flex; + flex-wrap: wrap; + align-items: center; + padding: 12px 0; + height: min-content; +} +.session-detail { + padding: 8px 16px; + min-width: 160px; + max-width: 160px; + height: min-content; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 12px; + font-weight: 500; + line-height: 1.17; + letter-spacing: normal; + text-decoration: underline; +} +.session-passed a { + color: #68b300; +} +.session-failed a { + color: #d04536; +} diff --git a/test/unit/bin/commands/generateReport.js b/test/unit/bin/commands/generateReport.js new file mode 100644 index 00000000..2f1ed848 --- /dev/null +++ b/test/unit/bin/commands/generateReport.js @@ -0,0 +1,112 @@ +const chai = require("chai"), + chaiAsPromised = require("chai-as-promised"), + sinon = require('sinon'); + +const Constants = require("../../../../bin/helpers/constants"), + logger = require("../../../../bin/helpers/logger").winstonLogger, + testObjects = require("../../support/fixtures/testObjects"); + +const proxyquire = require("proxyquire").noCallThru(); + +chai.use(chaiAsPromised); +logger.transports["console.info"].silent = true; + +describe("generateReport", () => { + let args = testObjects.generateReportInputArgs; + let body = testObjects.buildInfoSampleBody; + let bsConfig = testObjects.sampleBsConfig; + + describe("Calling the report generator", () => { + var sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + + getConfigPathStub = sandbox.stub(); + validateBstackJsonStub = sandbox.stub(); + setDefaultAuthHashStub = sandbox.stub(); + setUsernameStub = sandbox.stub(); + setAccessKeyStub = sandbox.stub(); + setUsageReportingFlagStub = sandbox.stub().returns(undefined); + setCypressConfigFilenameStub = sandbox.stub(); + sendUsageReportStub = sandbox.stub().callsFake(function () { + return "end"; + }); + + reportGeneratorSpy = sandbox.spy(); + getErrorCodeFromErrStub = sandbox.stub().returns("random-error"); + setDefaultsStub = sandbox.stub(); + }); + + afterEach(() => { + sandbox.restore(); + sinon.restore(); + }); + + it("calls the reportGenerator", () => { + const generateReport = proxyquire('../../../../bin/commands/generateReport', { + '../helpers/utils': { + getConfigPath: getConfigPathStub, + validateBstackJson: validateBstackJsonStub, + setDefaultAuthHash: setDefaultAuthHashStub, + setUsername: setUsernameStub, + setAccessKey: setAccessKeyStub, + setUsageReportingFlag: setUsageReportingFlagStub, + setCypressConfigFilename: setCypressConfigFilenameStub, + sendUsageReport: sendUsageReportStub, + setDefaults: setDefaultsStub, + getErrorCodeFromErr: getErrorCodeFromErrStub + }, + '../helpers/reporterHTML': { + reportGenerator: reportGeneratorSpy + } + }); + + validateBstackJsonStub.returns(Promise.resolve(bsConfig)); + + generateReport(args) + .then(function (_bsConfig) { + sinon.assert.calledWith(reportGeneratorSpy, bsConfig, args._[1], args); + sinon.assert.calledOnce(getConfigPathStub); + sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, 'generate-report called', Constants.messageTypes.INFO, null); + }) + .catch((error) => { + chai.assert.isNotOk(error, 'Promise error'); + }); + }); + + it("logs and send usage report on rejection", () => { + const generateReport = proxyquire('../../../../bin/commands/generateReport', { + '../helpers/utils': { + getConfigPath: getConfigPathStub, + validateBstackJson: validateBstackJsonStub, + setDefaultAuthHash: setDefaultAuthHashStub, + setUsername: setUsernameStub, + setAccessKey: setAccessKeyStub, + setUsageReportingFlag: setUsageReportingFlagStub, + setCypressConfigFilename: setCypressConfigFilenameStub, + sendUsageReport: sendUsageReportStub, + setDefaults: setDefaultsStub, + getErrorCodeFromErr: getErrorCodeFromErrStub + }, + '../helpers/reporterHTML': { + reportGenerator: reportGeneratorSpy + } + }); + + let err = { message: "Promise error" }; + + validateBstackJsonStub.returns(Promise.reject(err)); + + generateReport(args) + .then(function (_bsConfig) { + sinon.assert.notCalled(reportGeneratorSpy); + sinon.assert.notCalled(getConfigPathStub); + }) + .catch((_error) => { + sinon.assert.calledWith(setUsageReportingFlagStub, null, args.disableUsageReporting); + sinon.assert.calledWith(sendUsageReportStub, null, args, err.message, Constants.messageTypes.ERROR, "random-error"); + }); + }); + }); +}); diff --git a/test/unit/bin/helpers/reporterHTML.js b/test/unit/bin/helpers/reporterHTML.js new file mode 100644 index 00000000..48153f14 --- /dev/null +++ b/test/unit/bin/helpers/reporterHTML.js @@ -0,0 +1,241 @@ +const chai = require("chai"), + chaiAsPromised = require("chai-as-promised"), + sinon = require('sinon'); + +const fs = require('fs'), + path = require('path'), + request = require('request'), + Constants = require("../../../../bin/helpers/constants"), + logger = require("../../../../bin/helpers/logger").winstonLogger, + testObjects = require("../../support/fixtures/testObjects"); + +const proxyquire = require("proxyquire").noCallThru(); + +chai.use(chaiAsPromised); +logger.transports["console.info"].silent = true; + +describe("reportHTML", () => { + var sandbox; + let templateDir = 'templateDir', + args = testObjects.generateReportInputArgs, + buildId = 'buildId', + bsConfig = testObjects.sampleBsConfig; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + + validateBstackJsonStub = sandbox.stub(); + setDefaultAuthHashStub = sandbox.stub(); + setUsernameStub = sandbox.stub(); + setAccessKeyStub = sandbox.stub(); + setUsageReportingFlagStub = sandbox.stub().returns(undefined); + setCypressConfigFilenameStub = sandbox.stub(); + sendUsageReportStub = sandbox.stub().callsFake(function () { + return "end"; + }); + + reportGeneratorSpy = sandbox.spy(); + getErrorCodeFromErrStub = sandbox.stub().returns("random-error"); + setDefaultsStub = sandbox.stub(); + + getUserAgentStub = sandbox.stub().returns("random user-agent"); + // pathStub = sinon.stub(path, 'join').returns(templateDir); + }); + + afterEach(() => { + sandbox.restore(); + sinon.restore(); + // pathStub.restore(); + }); + + describe("calls API to generate report", () => { + + it("is deprecated, i.e. 299, does not have build message", () => { + let requestStub = sandbox.stub(request, "get").yields(null, { statusCode: 299 }, null); + let message = Constants.userMessages.API_DEPRECATED; + let messageType = Constants.messageTypes.INFO; + let errorCode = "api_deprecated"; + + const reporterHTML = proxyquire('../../../../bin/helpers/reporterHTML', { + './utils': { + validateBstackJson: validateBstackJsonStub, + setDefaultAuthHash: setDefaultAuthHashStub, + setUsername: setUsernameStub, + setAccessKey: setAccessKeyStub, + getUserAgent: getUserAgentStub, + setUsageReportingFlag: setUsageReportingFlagStub, + setCypressConfigFilename: setCypressConfigFilenameStub, + sendUsageReport: sendUsageReportStub, + setDefaults: setDefaultsStub, + getErrorCodeFromErr: getErrorCodeFromErrStub + }, + request: {get: requestStub} + }); + + reporterHTML.reportGenerator(bsConfig, buildId, args); + + sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode); + }); + + it("is deprecated, i.e. 299", () => { + let build = { buildId: buildId, message: 'API has been deprecated', rows: [] }; + let body = JSON.stringify(build); + let requestStub = sandbox.stub(request, "get").yields(null, { statusCode: 299 }, body); + let message = build.message; + let messageType = Constants.messageTypes.INFO; + let errorCode = "api_deprecated"; + + const reporterHTML = proxyquire('../../../../bin/helpers/reporterHTML', { + './utils': { + validateBstackJson: validateBstackJsonStub, + setDefaultAuthHash: setDefaultAuthHashStub, + setUsername: setUsernameStub, + setAccessKey: setAccessKeyStub, + getUserAgent: getUserAgentStub, + setUsageReportingFlag: setUsageReportingFlagStub, + setCypressConfigFilename: setCypressConfigFilenameStub, + sendUsageReport: sendUsageReportStub, + setDefaults: setDefaultsStub, + getErrorCodeFromErr: getErrorCodeFromErrStub + }, + request: {get: requestStub} + }); + + reporterHTML.reportGenerator(bsConfig, buildId, args); + + sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode); + }); + + context("non 200 response", () => { + it("400 status, build available, cannot generate report", () => { + let build = { buildId: buildId, message: 'success', rows: [] }; + let body = JSON.stringify(build); + let requestStub = sandbox.stub(request, "get").yields(null, { statusCode: 400 }, body); + let message = `${ + Constants.userMessages.BUILD_GENERATE_REPORT_FAILED.replace('', buildId) + } with error: \n${JSON.stringify(build, null, 2)}`; + let messageType = Constants.messageTypes.ERROR; + let errorCode = 'api_failed_build_generate_report'; + + const reporterHTML = proxyquire('../../../../bin/helpers/reporterHTML', { + './utils': { + validateBstackJson: validateBstackJsonStub, + setDefaultAuthHash: setDefaultAuthHashStub, + setUsername: setUsernameStub, + setAccessKey: setAccessKeyStub, + getUserAgent: getUserAgentStub, + setUsageReportingFlag: setUsageReportingFlagStub, + setCypressConfigFilename: setCypressConfigFilenameStub, + sendUsageReport: sendUsageReportStub, + setDefaults: setDefaultsStub, + getErrorCodeFromErr: getErrorCodeFromErrStub + }, + request: {get: requestStub} + }); + + reporterHTML.reportGenerator(bsConfig, buildId, args); + + sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode); + }); + + it("user is unauthorized", () => { + let build = { buildId: buildId, message: 'Unauthorized', rows: [] }; + let body = JSON.stringify(build); + let requestStub = sandbox.stub(request, "get").yields(null, { statusCode: 401 }, body); + let message = `${ + Constants.userMessages.BUILD_GENERATE_REPORT_FAILED.replace('', buildId) + } with error: \n${JSON.stringify(build, null, 2)}`; + let messageType = Constants.messageTypes.ERROR; + let errorCode = 'api_auth_failed'; + + const reporterHTML = proxyquire('../../../../bin/helpers/reporterHTML', { + './utils': { + validateBstackJson: validateBstackJsonStub, + setDefaultAuthHash: setDefaultAuthHashStub, + setUsername: setUsernameStub, + setAccessKey: setAccessKeyStub, + getUserAgent: getUserAgentStub, + setUsageReportingFlag: setUsageReportingFlagStub, + setCypressConfigFilename: setCypressConfigFilenameStub, + sendUsageReport: sendUsageReportStub, + setDefaults: setDefaultsStub, + getErrorCodeFromErr: getErrorCodeFromErrStub + }, + request: {get: requestStub} + }); + + reporterHTML.reportGenerator(bsConfig, buildId, args); + + sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode); + }); + + it("400 status, build not available, cannot generate report", () => { + let requestStub = sandbox.stub(request, "get").yields(null, { statusCode: 400 }, null); + let message = Constants.userMessages.BUILD_GENERATE_REPORT_FAILED.replace('', buildId); + let messageType = Constants.messageTypes.ERROR; + let errorCode = 'api_failed_build_generate_report'; + + const reporterHTML = proxyquire('../../../../bin/helpers/reporterHTML', { + './utils': { + validateBstackJson: validateBstackJsonStub, + setDefaultAuthHash: setDefaultAuthHashStub, + setUsername: setUsernameStub, + setAccessKey: setAccessKeyStub, + getUserAgent: getUserAgentStub, + setUsageReportingFlag: setUsageReportingFlagStub, + setCypressConfigFilename: setCypressConfigFilenameStub, + sendUsageReport: sendUsageReportStub, + setDefaults: setDefaultsStub, + getErrorCodeFromErr: getErrorCodeFromErrStub + }, + request: {get: requestStub} + }); + + reporterHTML.reportGenerator(bsConfig, buildId, args); + + sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode); + }); + }); + + it("200 response code", () => { + let build = { buildId: buildId, message: 'success', rows: [] }; + let body = JSON.stringify(build); + let requestStub = sandbox.stub(request, "get").yields(null, { statusCode: 200 }, body); + let message = `Report for build: ${buildId} was successfully created.`; + let messageType = Constants.messageTypes.SUCCESS; + let errorCode = null; + + const reporterHTML = proxyquire('../../../../bin/helpers/reporterHTML', { + './utils': { + validateBstackJson: validateBstackJsonStub, + setDefaultAuthHash: setDefaultAuthHashStub, + setUsername: setUsernameStub, + setAccessKey: setAccessKeyStub, + getUserAgent: getUserAgentStub, + setUsageReportingFlag: setUsageReportingFlagStub, + setCypressConfigFilename: setCypressConfigFilenameStub, + sendUsageReport: sendUsageReportStub, + setDefaults: setDefaultsStub, + getErrorCodeFromErr: getErrorCodeFromErrStub + }, + request: {get: requestStub} + }); + + reporterHTML.reportGenerator(bsConfig, buildId, args); + + sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode); + }); + }); +}); diff --git a/test/unit/support/fixtures/testObjects.js b/test/unit/support/fixtures/testObjects.js index 5aa00ee5..9c188649 100644 --- a/test/unit/support/fixtures/testObjects.js +++ b/test/unit/support/fixtures/testObjects.js @@ -42,6 +42,16 @@ const buildInfoSampleArgs = { $0: "browserstack-cypress", }; +const generateReportInputArgs = { + _: ["generate-report", "f3c94f7203792d03a75be3912d19354fe0961e53"], + cf: "browserstack.json", + "config-file": "browserstack.json", + configFile: "browserstack.json", + "disable-usage-reporting": undefined, + disableUsageReporting: undefined, + $0: "browserstack-cypress", +}; + const buildInfoSampleBody = { build_id: "random_hashed_id", framework: "cypress", @@ -135,4 +145,5 @@ module.exports = Object.freeze({ buildStopSampleBody, sampleCapsData, runSampleArgs, + generateReportInputArgs, });