diff --git a/bin/accessibility-automation/cypress/index.js b/bin/accessibility-automation/cypress/index.js index 6bc8aa3b..6586a256 100644 --- a/bin/accessibility-automation/cypress/index.js +++ b/bin/accessibility-automation/cypress/index.js @@ -1,383 +1,381 @@ /* Event listeners + custom commands for Cypress */ const browserStackLog = (message) => { - if (!Cypress.env('BROWSERSTACK_LOGS')) return; - cy.task('browserstack_log', message); -} - + if (!Cypress.env('BROWSERSTACK_LOGS')) return; + cy.task('browserstack_log', message); + } + const commandsToWrap = ['visit', 'click', 'type', 'request', 'dblclick', 'rightclick', 'clear', 'check', 'uncheck', 'select', 'trigger', 'selectFile', 'scrollIntoView', 'scroll', 'scrollTo', 'blur', 'focus', 'go', 'reload', 'submit', 'viewport', 'origin']; const performScan = (win, payloadToSend) => - new Promise(async (resolve, reject) => { +new Promise(async (resolve, reject) => { const isHttpOrHttps = /^(http|https):$/.test(win.location.protocol); if (!isHttpOrHttps) { - resolve(); + resolve(); } function findAccessibilityAutomationElement() { - return win.document.querySelector("#accessibility-automation-element"); + return win.document.querySelector("#accessibility-automation-element"); } function waitForScannerReadiness(retryCount = 30, retryInterval = 100) { - return new Promise(async (resolve, reject) => { + return new Promise(async (resolve, reject) => { let count = 0; const intervalID = setInterval(async () => { - if (count > retryCount) { - clearInterval(intervalID); - reject( - new Error( - "Accessibility Automation Scanner is not ready on the page." - ) - ); - } else if (findAccessibilityAutomationElement()) { - clearInterval(intervalID); - resolve("Scanner set"); - } else { - count += 1; - } + if (count > retryCount) { + clearInterval(intervalID); + reject( + new Error( + "Accessibility Automation Scanner is not ready on the page." + ) + ); + } else if (findAccessibilityAutomationElement()) { + clearInterval(intervalID); + resolve("Scanner set"); + } else { + count += 1; + } }, retryInterval); - }); + }); } function startScan() { - function onScanComplete() { - win.removeEventListener("A11Y_SCAN_FINISHED", onScanComplete); - resolve(); - } + function onScanComplete() { + win.removeEventListener("A11Y_SCAN_FINISHED", onScanComplete); + resolve(); + } - win.addEventListener("A11Y_SCAN_FINISHED", onScanComplete); - const e = new CustomEvent("A11Y_SCAN", { detail: payloadToSend }); - win.dispatchEvent(e); + win.addEventListener("A11Y_SCAN_FINISHED", onScanComplete); + const e = new CustomEvent("A11Y_SCAN", { detail: payloadToSend }); + win.dispatchEvent(e); } if (findAccessibilityAutomationElement()) { - startScan(); + startScan(); } else { - waitForScannerReadiness() - .then(startScan) - .catch(async (err) => { - resolve("Scanner is not ready on the page after multiple retries. performscan"); - }); + waitForScannerReadiness() + .then(startScan) + .catch(async (err) => { + resolve("Scanner is not ready on the page after multiple retries. performscan"); + }); } - }) +}) const getAccessibilityResultsSummary = (win) => - new Promise((resolve) => { +new Promise((resolve) => { const isHttpOrHttps = /^(http|https):$/.test(window.location.protocol); if (!isHttpOrHttps) { - resolve(); + resolve(); } function findAccessibilityAutomationElement() { - return win.document.querySelector("#accessibility-automation-element"); + return win.document.querySelector("#accessibility-automation-element"); } function waitForScannerReadiness(retryCount = 30, retryInterval = 100) { - return new Promise((resolve, reject) => { - let count = 0; - const intervalID = setInterval(() => { - if (count > retryCount) { - clearInterval(intervalID); - reject( - new Error( - "Accessibility Automation Scanner is not ready on the page." - ) - ); - } else if (findAccessibilityAutomationElement()) { - clearInterval(intervalID); - resolve("Scanner set"); - } else { - count += 1; - } - }, retryInterval); - }); + return new Promise((resolve, reject) => { + let count = 0; + const intervalID = setInterval(() => { + if (count > retryCount) { + clearInterval(intervalID); + reject( + new Error( + "Accessibility Automation Scanner is not ready on the page." + ) + ); + } else if (findAccessibilityAutomationElement()) { + clearInterval(intervalID); + resolve("Scanner set"); + } else { + count += 1; + } + }, retryInterval); + }); } function getSummary() { - function onReceiveSummary(event) { - - win.removeEventListener("A11Y_RESULTS_SUMMARY", onReceiveSummary); - resolve(event.detail); - } + function onReceiveSummary(event) { + win.removeEventListener("A11Y_RESULTS_SUMMARY", onReceiveSummary); + resolve(event.detail); + } - win.addEventListener("A11Y_RESULTS_SUMMARY", onReceiveSummary); - const e = new CustomEvent("A11Y_GET_RESULTS_SUMMARY"); - win.dispatchEvent(e); + win.addEventListener("A11Y_RESULTS_SUMMARY", onReceiveSummary); + const e = new CustomEvent("A11Y_GET_RESULTS_SUMMARY"); + win.dispatchEvent(e); } if (findAccessibilityAutomationElement()) { - getSummary(); + getSummary(); } else { - waitForScannerReadiness() - .then(getSummary) - .catch((err) => { - resolve(); - }); + waitForScannerReadiness() + .then(getSummary) + .catch((err) => { + resolve(); + }); } - }) +}) const getAccessibilityResults = (win) => - new Promise((resolve) => { +new Promise((resolve) => { const isHttpOrHttps = /^(http|https):$/.test(window.location.protocol); if (!isHttpOrHttps) { - resolve(); + resolve(); } function findAccessibilityAutomationElement() { - return win.document.querySelector("#accessibility-automation-element"); + return win.document.querySelector("#accessibility-automation-element"); } function waitForScannerReadiness(retryCount = 30, retryInterval = 100) { - return new Promise((resolve, reject) => { - let count = 0; - const intervalID = setInterval(() => { - if (count > retryCount) { - clearInterval(intervalID); - reject( - new Error( - "Accessibility Automation Scanner is not ready on the page." - ) - ); - } else if (findAccessibilityAutomationElement()) { - clearInterval(intervalID); - resolve("Scanner set"); - } else { - count += 1; - } - }, retryInterval); - }); + return new Promise((resolve, reject) => { + let count = 0; + const intervalID = setInterval(() => { + if (count > retryCount) { + clearInterval(intervalID); + reject( + new Error( + "Accessibility Automation Scanner is not ready on the page." + ) + ); + } else if (findAccessibilityAutomationElement()) { + clearInterval(intervalID); + resolve("Scanner set"); + } else { + count += 1; + } + }, retryInterval); + }); } function getResults() { - function onReceivedResult(event) { - win.removeEventListener("A11Y_RESULTS_RESPONSE", onReceivedResult); - resolve(event.detail); - } - - win.addEventListener("A11Y_RESULTS_RESPONSE", onReceivedResult); - const e = new CustomEvent("A11Y_GET_RESULTS"); - win.dispatchEvent(e); + function onReceivedResult(event) { + win.removeEventListener("A11Y_RESULTS_RESPONSE", onReceivedResult); + resolve(event.detail); + } + + win.addEventListener("A11Y_RESULTS_RESPONSE", onReceivedResult); + const e = new CustomEvent("A11Y_GET_RESULTS"); + win.dispatchEvent(e); } if (findAccessibilityAutomationElement()) { - getResults(); + getResults(); } else { - waitForScannerReadiness() - .then(getResults) - .catch((err) => { - resolve(); - }); + waitForScannerReadiness() + .then(getResults) + .catch((err) => { + resolve(); + }); } - }); +}); const saveTestResults = (win, payloadToSend) => - new Promise( (resolve, reject) => { +new Promise( (resolve, reject) => { try { - const isHttpOrHttps = /^(http|https):$/.test(win.location.protocol); - if (!isHttpOrHttps) { - resolve("Unable to save accessibility results, Invalid URL."); - } - - function findAccessibilityAutomationElement() { - return win.document.querySelector("#accessibility-automation-element"); - } + const isHttpOrHttps = /^(http|https):$/.test(win.location.protocol); + if (!isHttpOrHttps) { + resolve("Unable to save accessibility results, Invalid URL."); + } - function waitForScannerReadiness(retryCount = 30, retryInterval = 100) { - return new Promise((resolve, reject) => { - let count = 0; - const intervalID = setInterval(async () => { - if (count > retryCount) { - clearInterval(intervalID); - reject( - new Error( - "Accessibility Automation Scanner is not ready on the page." - ) - ); - } else if (findAccessibilityAutomationElement()) { - clearInterval(intervalID); - resolve("Scanner set"); - } else { - count += 1; - } - }, retryInterval); - }); - } + function findAccessibilityAutomationElement() { + return win.document.querySelector("#accessibility-automation-element"); + } - function saveResults() { - function onResultsSaved(event) { - resolve(); + function waitForScannerReadiness(retryCount = 30, retryInterval = 100) { + return new Promise((resolve, reject) => { + let count = 0; + const intervalID = setInterval(async () => { + if (count > retryCount) { + clearInterval(intervalID); + reject( + new Error( + "Accessibility Automation Scanner is not ready on the page." + ) + ); + } else if (findAccessibilityAutomationElement()) { + clearInterval(intervalID); + resolve("Scanner set"); + } else { + count += 1; + } + }, retryInterval); + }); } - win.addEventListener("A11Y_RESULTS_SAVED", onResultsSaved); - const e = new CustomEvent("A11Y_SAVE_RESULTS", { - detail: payloadToSend, - }); - win.dispatchEvent(e); - } - if (findAccessibilityAutomationElement()) { - saveResults(); - } else { - waitForScannerReadiness() - .then(saveResults) - .catch(async (err) => { - resolve("Scanner is not ready on the page after multiple retries. after run"); - }); - } + function saveResults() { + function onResultsSaved(event) { + resolve(); + } + win.addEventListener("A11Y_RESULTS_SAVED", onResultsSaved); + const e = new CustomEvent("A11Y_SAVE_RESULTS", { + detail: payloadToSend, + }); + win.dispatchEvent(e); + } + if (findAccessibilityAutomationElement()) { + saveResults(); + } else { + waitForScannerReadiness() + .then(saveResults) + .catch(async (err) => { + resolve("Scanner is not ready on the page after multiple retries. after run"); + }); + } } catch(er) { - resolve() + resolve() } - }) +}) const shouldScanForAccessibility = (attributes) => { - if (Cypress.env("IS_ACCESSIBILITY_EXTENSION_LOADED") !== "true") return false; + if (Cypress.env("IS_ACCESSIBILITY_EXTENSION_LOADED") !== "true") return false; - const extensionPath = Cypress.env("ACCESSIBILITY_EXTENSION_PATH"); - const isHeaded = Cypress.browser.isHeaded; + const extensionPath = Cypress.env("ACCESSIBILITY_EXTENSION_PATH"); + const isHeaded = Cypress.browser.isHeaded; - if (!isHeaded || (extensionPath === undefined)) return false; + if (!isHeaded || (extensionPath === undefined)) return false; - let shouldScanTestForAccessibility = true; + let shouldScanTestForAccessibility = true; - if (Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY") || Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY")) { - try { - let includeTagArray = []; - let excludeTagArray = []; - if (Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY")) { - includeTagArray = Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY").split(";") - } - if (Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY")) { - excludeTagArray = Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY").split(";") - } - - const fullTestName = attributes.title; - const excluded = excludeTagArray.some((exclude) => fullTestName.includes(exclude)); - const included = includeTagArray.length === 0 || includeTags.some((include) => fullTestName.includes(include)); - shouldScanTestForAccessibility = !excluded && included; - } catch (error) { - browserStackLog("Error while validating test case for accessibility before scanning. Error : ", error); + if (Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY") || Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY")) { + try { + let includeTagArray = []; + let excludeTagArray = []; + if (Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY")) { + includeTagArray = Cypress.env("INCLUDE_TAGS_FOR_ACCESSIBILITY").split(";") + } + if (Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY")) { + excludeTagArray = Cypress.env("EXCLUDE_TAGS_FOR_ACCESSIBILITY").split(";") + } + + const fullTestName = attributes.title; + const excluded = excludeTagArray.some((exclude) => fullTestName.includes(exclude)); + const included = includeTagArray.length === 0 || includeTags.some((include) => fullTestName.includes(include)); + shouldScanTestForAccessibility = !excluded && included; + } catch (error) { + browserStackLog("Error while validating test case for accessibility before scanning. Error : ", error); + } } - } - return shouldScanTestForAccessibility; + return shouldScanTestForAccessibility; } Cypress.on('command:start', async (command) => { - if(!command || !command.attributes) return; - if(command.attributes.name == 'window' || command.attributes.name == 'then' || command.attributes.name == 'wrap') { - return; - } + if(!command || !command.attributes) return; + if(command.attributes.name == 'window' || command.attributes.name == 'then' || command.attributes.name == 'wrap') { + return; + } - if (!commandsToWrap.includes(command.attributes.name)) return; + if (!commandsToWrap.includes(command.attributes.name)) return; - const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable; + const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable; - let shouldScanTestForAccessibility = shouldScanForAccessibility(attributes); - if (!shouldScanTestForAccessibility) return; + let shouldScanTestForAccessibility = shouldScanForAccessibility(attributes); + if (!shouldScanTestForAccessibility) return; - cy.window().then((win) => { - browserStackLog('Performing scan form command ' + command.attributes.name); - cy.wrap(performScan(win, {method: command.attributes.name}), {timeout: 30000}); - }) + cy.window().then((win) => { + browserStackLog('Performing scan form command ' + command.attributes.name); + cy.wrap(performScan(win, {method: command.attributes.name}), {timeout: 30000}); + }) }) afterEach(() => { - const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest; - cy.window().then(async (win) => { - let shouldScanTestForAccessibility = shouldScanForAccessibility(attributes); - if (!shouldScanTestForAccessibility) return cy.wrap({}); - - cy.wrap(performScan(win), {timeout: 30000}).then(() => { - try { - let os_data; - if (Cypress.env("OS")) { - os_data = Cypress.env("OS"); - } else { - os_data = Cypress.platform === 'linux' ? 'mac' : "win" - } - let filePath = ''; - if (attributes.invocationDetails !== undefined && attributes.invocationDetails.relativeFile !== undefined) { - filePath = attributes.invocationDetails.relativeFile; + const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest; + cy.window().then(async (win) => { + let shouldScanTestForAccessibility = shouldScanForAccessibility(attributes); + if (!shouldScanTestForAccessibility) return cy.wrap({}); + + cy.wrap(performScan(win), {timeout: 30000}).then(() => { + try { + let os_data; + if (Cypress.env("OS")) { + os_data = Cypress.env("OS"); + } else { + os_data = Cypress.platform === 'linux' ? 'mac' : "win" + } + let filePath = ''; + if (attributes.invocationDetails !== undefined && attributes.invocationDetails.relativeFile !== undefined) { + filePath = attributes.invocationDetails.relativeFile; + } + const payloadToSend = { + "saveResults": shouldScanTestForAccessibility, + "testDetails": { + "name": attributes.title, + "testRunId": '5058', // variable not consumed, shouldn't matter what we send + "filePath": filePath, + "scopeList": [ + filePath, + attributes.title + ] + }, + "platform": { + "os_name": os_data, + "os_version": Cypress.env("OS_VERSION"), + "browser_name": Cypress.browser.name, + "browser_version": Cypress.browser.version + } + }; + browserStackLog(`Saving accessibility test results`); + cy.wrap(saveTestResults(win, payloadToSend), {timeout: 30000}).then(() => { + browserStackLog(`Saved accessibility test results`); + }) + + } catch (er) { } - const payloadToSend = { - "saveResults": shouldScanTestForAccessibility, - "testDetails": { - "name": attributes.title, - "testRunId": '5058', // variable not consumed, shouldn't matter what we send - "filePath": filePath, - "scopeList": [ - filePath, - attributes.title - ] - }, - "platform": { - "os_name": os_data, - "os_version": Cypress.env("OS_VERSION"), - "browser_name": Cypress.browser.name, - "browser_version": Cypress.browser.version - } - }; - browserStackLog(`Saving accessibility test results`); - cy.wrap(saveTestResults(win, payloadToSend), {timeout: 30000}).then(() => { - browserStackLog(`Saved accessibility test results`); }) - - } catch (er) { - } - }) - }); + }); }) Cypress.Commands.add('performScan', () => { - try { - const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable; - const shouldScanTestForAccessibility = shouldScanForAccessibility(attributes); - if (!shouldScanTestForAccessibility) { - browserStackLog(`Not a Accessibility Automation session, cannot perform scan.`); - return cy.wrap({}); - } - cy.window().then(async (win) => { - browserStackLog(`Performing accessibility scan`); - await performScan(win); - }); - } catch {} + try { + const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable; + const shouldScanTestForAccessibility = shouldScanForAccessibility(attributes); + if (!shouldScanTestForAccessibility) { + browserStackLog(`Not a Accessibility Automation session, cannot perform scan.`); + return cy.wrap({}); + } + cy.window().then(async (win) => { + browserStackLog(`Performing accessibility scan`); + await performScan(win); + }); + } catch {} }) Cypress.Commands.add('getAccessibilityResultsSummary', () => { - try { - const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable; - const shouldScanTestForAccessibility = shouldScanForAccessibility(attributes); - if (!shouldScanTestForAccessibility) { - browserStackLog(`Not a Accessibility Automation session, cannot retrieve Accessibility results summary.`); - return cy.wrap({}); - } - cy.window().then(async (win) => { - await performScan(win); - browserStackLog('Getting accessibility results summary'); - return await getAccessibilityResultsSummary(win); - }); - } catch {} - + try { + const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable; + const shouldScanTestForAccessibility = shouldScanForAccessibility(attributes); + if (!shouldScanTestForAccessibility) { + browserStackLog(`Not a Accessibility Automation session, cannot retrieve Accessibility results summary.`); + return cy.wrap({}); + } + cy.window().then(async (win) => { + await performScan(win); + browserStackLog('Getting accessibility results summary'); + return await getAccessibilityResultsSummary(win); + }); + } catch {} + }); Cypress.Commands.add('getAccessibilityResults', () => { - try { - const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable; - const shouldScanTestForAccessibility = shouldScanForAccessibility(attributes); - if (!shouldScanTestForAccessibility) { - browserStackLog(`Not a Accessibility Automation session, cannot retrieve Accessibility results.`); - return cy.wrap({}); - } + try { + const attributes = Cypress.mocha.getRunner().suite.ctx.currentTest || Cypress.mocha.getRunner().suite.ctx._runnable; + const shouldScanTestForAccessibility = shouldScanForAccessibility(attributes); + if (!shouldScanTestForAccessibility) { + browserStackLog(`Not a Accessibility Automation session, cannot retrieve Accessibility results.`); + return cy.wrap({}); + } -/* browserstack_accessibility_automation_script */ + /* browserstack_accessibility_automation_script */ - cy.window().then(async (win) => { - await performScan(win); - browserStackLog('Getting accessibility results'); - return await getAccessibilityResults(win); - }); + cy.window().then(async (win) => { + await performScan(win); + browserStackLog('Getting accessibility results'); + return await getAccessibilityResults(win); + }); + + } catch {} - } catch {} - }); diff --git a/bin/accessibility-automation/helper.js b/bin/accessibility-automation/helper.js index 8a08b674..1d49988f 100644 --- a/bin/accessibility-automation/helper.js +++ b/bin/accessibility-automation/helper.js @@ -3,12 +3,14 @@ const { API_URL } = require('./constants'); const utils = require('../helpers/utils'); const fs = require('fs'); const path = require('path'); -const request = require('request'); +const axios = require('axios'); const os = require('os'); const glob = require('glob'); const helper = require('../helpers/helper'); const { CYPRESS_V10_AND_ABOVE_CONFIG_FILE_EXTENSIONS } = require('../helpers/constants'); +const { consoleHolder } = require("../testObservability/helper/constants"); const supportFileContentMap = {} +const HttpsProxyAgent = require('https-proxy-agent'); exports.checkAccessibilityPlatform = (user_config) => { let accessibility = false; @@ -86,8 +88,8 @@ exports.createAccessibilityTestRun = async (user_config, framework) => { const config = { auth: { - user: userName, - pass: accessKey + username: userName, + password: accessKey }, headers: { 'Content-Type': 'application/json' @@ -105,12 +107,13 @@ exports.createAccessibilityTestRun = async (user_config, framework) => { process.env.BROWSERSTACK_TEST_ACCESSIBILITY = 'true'; } logger.debug(`BrowserStack Accessibility Automation Test Run ID: ${response.data.data.id}`); - + this.setAccessibilityCypressCapabilities(user_config, response.data); helper.setBrowserstackCypressCliDependency(user_config); } catch (error) { if (error.response) { + logger.error("Incorrect Cred") logger.error( `Exception while creating test run for BrowserStack Accessibility Automation: ${ error.response.status @@ -118,6 +121,7 @@ exports.createAccessibilityTestRun = async (user_config, framework) => { ); } else { if(error.message === 'Invalid configuration passed.') { + logger.error("Invalid configuration passed.") logger.error( `Exception while creating test run for BrowserStack Accessibility Automation: ${ error.message || error.stack @@ -126,7 +130,7 @@ exports.createAccessibilityTestRun = async (user_config, framework) => { for(const errorkey of error.errors){ logger.error(errorkey.message); } - + } else { logger.error( `Exception while creating test run for BrowserStack Accessibility Automation: ${ @@ -143,33 +147,43 @@ exports.createAccessibilityTestRun = async (user_config, framework) => { const nodeRequest = (type, url, data, config) => { return new Promise(async (resolve, reject) => { - const options = {...config,...{ + const options = { + ...config, method: type, url: `${API_URL}/${url}`, - body: data, - json: config.headers['Content-Type'] === 'application/json', - }}; + data: data + }; - request(options, function callback(error, response, body) { - if(error) { - logger.info("error in nodeRequest", error); - reject(error); - } else if(!(response.statusCode == 201 || response.statusCode == 200)) { - logger.info("response.statusCode in nodeRequest", response.statusCode); - reject(response && response.body ? response.body : `Received response from BrowserStack Server with status : ${response.statusCode}`); + if(process.env.HTTP_PROXY){ + options.proxy = false + options.httpsAgent = new HttpsProxyAgent(process.env.HTTP_PROXY); + + } else if (process.env.HTTPS_PROXY){ + options.proxy = false + options.httpsAgent = new HttpsProxyAgent(process.env.HTTPS_PROXY); + } + + axios(options).then(response => { + if(!(response.status == 201 || response.status == 200)) { + logger.info("response.status in nodeRequest", response.status); + reject(response && response.data ? response.data : `Received response from BrowserStack Server with status : ${response.status}`); } else { - try { - if(typeof(body) !== 'object') body = JSON.parse(body); - } catch(e) { - if(!url.includes('/stop')) { - reject('Not a JSON response from BrowserStack Server'); + try { + if(typeof(response.data) !== 'object') body = JSON.parse(response.data); + } catch(e) { + if(!url.includes('/stop')) { + reject('Not a JSON response from BrowserStack Server'); + } } - } - resolve({ - data: body - }); + resolve({ + data: response.data + }); } - }); + }).catch(error => { + + logger.info("error in nodeRequest", error); + reject(error); + }) }); } @@ -211,7 +225,7 @@ exports.setAccessibilityEventListeners = (bsConfig) => { try { if(!file.includes('commands.js') && !file.includes('commands.ts')) { const defaultFileContent = fs.readFileSync(file, {encoding: 'utf-8'}); - + let cypressCommandEventListener = getAccessibilityCypressCommandEventListener(path.extname(file)); if(!defaultFileContent.includes(cypressCommandEventListener)) { let newFileContent = defaultFileContent + diff --git a/bin/accessibility-automation/plugin/index.js b/bin/accessibility-automation/plugin/index.js index dd47f208..8d614cf7 100644 --- a/bin/accessibility-automation/plugin/index.js +++ b/bin/accessibility-automation/plugin/index.js @@ -1,4 +1,3 @@ - const path = require("node:path"); const browserstackAccessibility = (on, config) => { @@ -10,7 +9,7 @@ const browserstackAccessibility = (on, config) => { on('task', { browserstack_log(message) { console.log(message) - + return null }, }) @@ -36,7 +35,7 @@ const browserstackAccessibility = (on, config) => { } } } catch(err) {} - + }) config.env.ACCESSIBILITY_EXTENSION_PATH = process.env.ACCESSIBILITY_EXTENSION_PATH config.env.OS_VERSION = process.env.OS_VERSION diff --git a/bin/commands/generateReport.js b/bin/commands/generateReport.js index af375005..603ed0a7 100644 --- a/bin/commands/generateReport.js +++ b/bin/commands/generateReport.js @@ -31,7 +31,6 @@ module.exports = function generateReport(args, rawArgs) { let messageType = Constants.messageTypes.INFO; let errorCode = null; let buildId = args._[1]; - reportGenerator(bsConfig, buildId, args, rawArgs, buildReportData); utils.sendUsageReport(bsConfig, args, 'generate-report called', messageType, errorCode, buildReportData, rawArgs); } catch(err) { diff --git a/bin/commands/info.js b/bin/commands/info.js index 715b9647..e8e9344a 100644 --- a/bin/commands/info.js +++ b/bin/commands/info.js @@ -1,12 +1,13 @@ 'use strict'; -const request = require('request'); - +const axios = require('axios').default; const config = require("../helpers/config"), logger = require("../helpers/logger").winstonLogger, Constants = require("../helpers/constants"), utils = require("../helpers/utils"), getInitialDetails = require('../helpers/getInitialDetails').getInitialDetails; +const { setAxiosProxy } = require('../helpers/helper'); + module.exports = function info(args, rawArgs) { let bsConfigPath = utils.getConfigPath(args.cf); @@ -19,7 +20,7 @@ module.exports = function info(args, rawArgs) { // accept the access key from command line if provided utils.setAccessKey(bsConfig, args); - getInitialDetails(bsConfig, args, rawArgs).then((buildReportData) => { + getInitialDetails(bsConfig, args, rawArgs).then(async (buildReportData) => { utils.setUsageReportingFlag(bsConfig, args.disableUsageReporting); @@ -43,53 +44,46 @@ module.exports = function info(args, rawArgs) { options.url = `${config.turboScaleBuildsUrl}/${buildId}`; } - request.get(options, function (err, resp, body) { - let message = null; - let messageType = null; - let errorCode = null; - - if (err) { - message = Constants.userMessages.BUILD_INFO_FAILED; - messageType = Constants.messageTypes.ERROR; - errorCode = 'api_failed_build_info'; - - logger.info(message); - logger.error(utils.formatRequest(err, resp, body)); - } else { - let build = null; + let message = null; + let messageType = null; + let errorCode = null; + + options.config = { + auth: { + username: bsConfig.auth.username, + password: bsConfig.auth.access_key + }, + headers: options.headers + }; + setAxiosProxy(options.config); + + try { + const response = await axios.get(options.url, options.config); + let build = null; try { - build = JSON.parse(body); + build = response.data; } catch (error) { build = null; } - - if (resp.statusCode == 299) { + if (response.status == 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); - } - logger.info(utils.formatRequest(err, resp, body)); - } else if (resp.statusCode != 200) { + message = build ? build.message : Constants.userMessages.API_DEPRECATED; + logger.info(utils.formatRequest(response.statusText, response, response.data)); + } else if (response.status != 200) { + message = Constants.userMessages.BUILD_INFO_FAILED; messageType = Constants.messageTypes.ERROR; errorCode = 'api_failed_build_info'; - if (build) { message = `${ Constants.userMessages.BUILD_INFO_FAILED } with error: \n${JSON.stringify(build, null, 2)}`; - logger.error(message); if (build.message === 'Unauthorized') errorCode = 'api_auth_failed'; } else { message = Constants.userMessages.BUILD_INFO_FAILED; - logger.error(message); } - logger.error(utils.formatRequest(err, resp, body)); + logger.error(message); + logger.error(utils.formatRequest(response.statusText, response, response.data)); } else { messageType = Constants.messageTypes.SUCCESS; message = `Build info for build id: \n ${JSON.stringify( @@ -97,11 +91,18 @@ module.exports = function info(args, rawArgs) { null, 2 )}`; - logger.info(message); } - } - utils.sendUsageReport(bsConfig, args, message, messageType, errorCode, buildReportData, rawArgs); - }); + logger.info(message); + } catch (error) { + message = `${ + Constants.userMessages.BUILD_INFO_FAILED + } with error: \n${error.response.data.message}`; + messageType = Constants.messageTypes.ERROR; + errorCode = 'api_failed_build_info'; + logger.info(message); + logger.error(utils.formatRequest(error.response.statusText, error.response, error.response.data)); + } + utils.sendUsageReport(bsConfig, args, message, messageType, errorCode, buildReportData, rawArgs); }).catch((err) => { logger.warn(err); }); diff --git a/bin/commands/runs.js b/bin/commands/runs.js index baa04b12..e719d97e 100644 --- a/bin/commands/runs.js +++ b/bin/commands/runs.js @@ -30,7 +30,6 @@ const { printBuildLink } = require('../testObservability/helper/helper'); - const { createAccessibilityTestRun, setAccessibilityEventListeners, @@ -39,6 +38,7 @@ const { } = require('../accessibility-automation/helper'); const { isTurboScaleSession, getTurboScaleGridDetails, patchCypressConfigFileContent, atsFileCleanup } = require('../helpers/atsHelper'); + module.exports = function run(args, rawArgs) { markBlockStart('preBuild'); @@ -64,15 +64,14 @@ module.exports = function run(args, rawArgs) { // set cypress config filename utils.setCypressConfigFilename(bsConfig, args); - - /* - Set testObservability & browserstackAutomation flags - */ + + /* Set testObservability & browserstackAutomation flags */ const [isTestObservabilitySession, isBrowserstackInfra] = setTestObservabilityFlags(bsConfig); const checkAccessibility = checkAccessibilityPlatform(bsConfig); const isAccessibilitySession = bsConfig.run_settings.accessibility || checkAccessibility; const turboScaleSession = isTurboScaleSession(bsConfig); Constants.turboScaleObj.enabled = turboScaleSession; + utils.setUsageReportingFlag(bsConfig, args.disableUsageReporting); @@ -102,6 +101,7 @@ module.exports = function run(args, rawArgs) { // set spec timeout utils.setSpecTimeout(bsConfig, args); } + // accept the specs list from command line if provided utils.setUserSpecs(bsConfig, args); @@ -112,9 +112,7 @@ module.exports = function run(args, rawArgs) { // set build tag caps utils.setBuildTags(bsConfig, args); - /* - Send build start to Observability - */ + // Send build start to Observability if(isTestObservabilitySession) { await launchTestSession(bsConfig, bsConfigPath); utils.setO11yProcessHooks(null, bsConfig, args, null, buildReportData); @@ -379,7 +377,7 @@ module.exports = function run(args, rawArgs) { }); } else if(!turboScaleSession){ let stacktraceUrl = getStackTraceUrl(); - downloadBuildStacktrace(stacktraceUrl).then((message) => { + downloadBuildStacktrace(stacktraceUrl, bsConfig).then((message) => { utils.sendUsageReport(bsConfig, args, message, Constants.messageTypes.SUCCESS, null, buildReportData, rawArgs); }).catch((err) => { let message = `Downloading build stacktrace failed with statuscode: ${err}. Please visit ${data.dashboard_url} for additional details.`; diff --git a/bin/commands/stop.js b/bin/commands/stop.js index e62b516e..c37f7443 100644 --- a/bin/commands/stop.js +++ b/bin/commands/stop.js @@ -1,5 +1,4 @@ 'use strict'; -const request = require('request'); const config = require("../helpers/config"), logger = require("../helpers/logger").winstonLogger, diff --git a/bin/helpers/atsHelper.js b/bin/helpers/atsHelper.js index 01e874ef..90fed779 100644 --- a/bin/helpers/atsHelper.js +++ b/bin/helpers/atsHelper.js @@ -1,7 +1,9 @@ const path = require('path'); -const fs = require('fs') +const fs = require('fs'); +const { consoleHolder } = require('../testObservability/helper/constants'); +const HttpsProxyAgent = require('https-proxy-agent'); -const request = require('request'), +const axios = require('axios'), logger = require('./logger').winstonLogger, utils = require('./utils'), config = require('./config'); @@ -42,44 +44,52 @@ exports.getTurboScaleGridName = (bsConfig) => { }; exports.getTurboScaleGridDetails = async (bsConfig, args, rawArgs) => { - try { - const gridName = this.getTurboScaleGridName(bsConfig); - - return new Promise((resolve, reject) => { - let options = { - url: `${config.turboScaleAPIUrl}/grids/${gridName}`, - auth: { - username: bsConfig.auth.username, - password: bsConfig.auth.access_key, - }, - headers: { - 'User-Agent': utils.getUserAgent(), - } - }; - let responseData = {}; - request.get(options, function (err, resp, data) { - if(err) { - logger.warn(utils.formatRequest(err, resp, data)); - utils.sendUsageReport(bsConfig, args, err, Constants.messageTypes.ERROR, 'get_ats_details_failed', null, rawArgs); - resolve({}); - } else { - try { - responseData = JSON.parse(data); - } catch (e) { - responseData = {}; - } - if(resp.statusCode != 200) { - logger.warn(`Warn: Get Automate TurboScale Details Request failed with status code ${resp.statusCode}`); - utils.sendUsageReport(bsConfig, args, responseData["error"], Constants.messageTypes.ERROR, 'get_ats_details_failed', null, rawArgs); + try { + const gridName = this.getTurboScaleGridName(bsConfig); + + return new Promise((resolve, reject) => { + let options = { + url: `${config.turboScaleAPIUrl}/grids/${gridName}`, + auth: { + username: bsConfig.auth.username, + password: bsConfig.auth.access_key, + }, + headers: { + 'User-Agent': utils.getUserAgent(), + } + }; + + if (process.env.HTTP_PROXY) { + options.proxy = false; + options.httpsAgent = new HttpsProxyAgent(process.env.HTTP_PROXY); + } else if (process.env.HTTPS_PROXY) { + options.proxy = false; + options.httpsAgent = new HttpsProxyAgent(process.env.HTTPS_PROXY); + } + + let responseData = {}; + + axios(options).then(response => { + try { + responseData = response.data; + } catch (e) { + responseData = {}; + } + if(response.status != 200) { + logger.warn(`Warn: Get Automate TurboScale Details Request failed with status code ${response.status}`); + utils.sendUsageReport(bsConfig, args, responseData["error"], Constants.messageTypes.ERROR, 'get_ats_details_failed', null, rawArgs); + resolve({}); + } + resolve(responseData); + }).catch(error => { + logger.warn(utils.formatRequest(error, null, null)); + utils.sendUsageReport(bsConfig, args, error, Constants.messageTypes.ERROR, 'get_ats_details_failed', null, rawArgs); resolve({}); - } - resolve(responseData); - } + }); }); - }); - } catch (err) { - logger.error(`Failed to find TurboScale Grid: ${err}: ${err.stack}`); - } + } catch (err) { + logger.error(`Failed to find TurboScale Grid: ${err}: ${err.stack}`); + } }; exports.patchCypressConfigFileContent = (bsConfig) => { @@ -87,7 +97,6 @@ exports.patchCypressConfigFileContent = (bsConfig) => { let cypressConfigFileData = fs.readFileSync(path.resolve(bsConfig.run_settings.cypress_config_file)).toString(); const patchedConfigFileData = cypressConfigFileData + '\n\n' + ` let originalFunction = module.exports.e2e.setupNodeEvents; - module.exports.e2e.setupNodeEvents = (on, config) => { const bstackOn = require("./cypressPatch.js")(on); if (originalFunction !== null && originalFunction !== undefined) { diff --git a/bin/helpers/build.js b/bin/helpers/build.js index 9d97a57a..c05634df 100644 --- a/bin/helpers/build.js +++ b/bin/helpers/build.js @@ -1,5 +1,5 @@ 'use strict'; -const request = require('request'); +const axios = require('axios').default; const config = require('./config'), capabilityHelper = require("../helpers/capabilityHelper"), @@ -7,9 +7,11 @@ const config = require('./config'), utils = require('../helpers/utils'), logger = require('../helpers/logger').winstonLogger; +const { setAxiosProxy } = require('./helper'); + const createBuild = (bsConfig, zip) => { return new Promise(function (resolve, reject) { - capabilityHelper.caps(bsConfig, zip).then(function(data){ + capabilityHelper.caps(bsConfig, zip).then(async function(data){ let options = { url: config.buildUrl, auth: { @@ -22,43 +24,49 @@ const createBuild = (bsConfig, zip) => { }, body: data } - if (Constants.turboScaleObj.enabled) { options.url = Constants.turboScaleObj.buildUrl; } - request.post(options, function (err, resp, body) { - if (err) { - logger.error(utils.formatRequest(err, resp, body)); - reject(err); - } else { - let build = null; - try { - build = JSON.parse(body); - } catch (error) { - build = null; - } + const axiosConfig = { + auth: { + username: options.auth.user, + password: options.auth.password + }, + headers: options.headers + } + setAxiosProxy(axiosConfig); - if (resp.statusCode == 299) { - if (build) { - resolve(build.message); - } else { - logger.error(utils.formatRequest(err, resp, body)); - reject(Constants.userMessages.API_DEPRECATED); - } - } else if (resp.statusCode != 201) { - logger.error(utils.formatRequest(err, resp, body)); - if (build) { - reject(`${Constants.userMessages.BUILD_FAILED} Error: ${build.message}`); - } else { - reject(Constants.userMessages.BUILD_FAILED); - } + try { + const response = await axios.post(options.url, data, axiosConfig); + let build = null; + try { + build = response.data; + } catch (error) { + build = null; + } + if (response.status == 299) { + if (build) { + resolve(build.message); + } else { + logger.error(utils.formatRequest(response.statusText, response, response.data)); + reject(Constants.userMessages.API_DEPRECATED); + } + } else if (response.status != 201) { + logger.error(utils.formatRequest(response.statusText, response, response.data)); + if (build) { + reject(`${Constants.userMessages.BUILD_FAILED} Error: ${build.message}`); } else { - resolve(build); + reject(Constants.userMessages.BUILD_FAILED); } - resolve(build); } - }) + resolve(build); + } catch (error) { + if(error.response) { + logger.error(utils.formatRequest(error.response.statusText, error.response, error.response.data)); + reject(`${Constants.userMessages.BUILD_FAILED} Error: ${error.response.data.message}`); + } + } }).catch(function(err){ reject(err); }); diff --git a/bin/helpers/buildArtifacts.js b/bin/helpers/buildArtifacts.js index cd064fa7..30986a60 100644 --- a/bin/helpers/buildArtifacts.js +++ b/bin/helpers/buildArtifacts.js @@ -8,9 +8,12 @@ const logger = require('./logger').winstonLogger, Constants = require("./constants"), config = require("./config"); -const request = require('request'); +const { default: axios } = require('axios'); +const HttpsProxyAgent = require('https-proxy-agent'); +const FormData = require('form-data'); const decompress = require('decompress'); const unzipper = require("unzipper"); +const { setAxiosProxy } = require('./helper'); let BUILD_ARTIFACTS_TOTAL_COUNT = 0; let BUILD_ARTIFACTS_FAIL_COUNT = 0; @@ -107,18 +110,23 @@ const downloadAndUnzip = async (filePath, fileName, url) => { logger.debug(`Downloading build artifact for: ${filePath}`) return new Promise(async (resolve, reject) => { - request.get(url).on('response', function(response) { - - if(response.statusCode != 200) { - if (response.statusCode === 404) { + try { + const axiosConfig = { + responseType: 'stream', + validateStatus: status => (status >= 200 && status < 300) || status === 404 + }; + setAxiosProxy(axiosConfig); + const response = await axios.get(url, axiosConfig); + if(response.status != 200) { + if (response.status === 404) { reject(Constants.userMessages.DOWNLOAD_BUILD_ARTIFACTS_NOT_FOUND); } - const errorMsg = `Non 200 status code, got status code: ${response.statusCode}`; + const errorMsg = `Non 200 status code, got status code: ${response.status}`; reject(errorMsg); } else { //ensure that the user can call `then()` only when the file has //been downloaded entirely. - response.pipe(writer); + response.data.pipe(writer); let error = null; writer.on('error', err => { error = err; @@ -137,7 +145,9 @@ const downloadAndUnzip = async (filePath, fileName, url) => { resolve(true); }); } - }); + } catch (error) { + reject(error); + } }); } @@ -188,28 +198,38 @@ const sendUpdatesToBstack = async (bsConfig, buildId, args, options, rawArgs, bu } options.formData = data.toString(); + const axiosConfig = { + auth: { + username: options.auth.username, + password: options.auth.password + }, + headers: options.headers + }; + setAxiosProxy(axiosConfig); + 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', buildReportData, rawArgs); - logger.error(utils.formatRequest(err, resp, data)); - 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', buildReportData, rawArgs); - reject(responseData["error"]) - } + try { + const response = await axios.post(options.url, data, axiosConfig); + try { + responseData = response.data; + } catch(e) { + responseData = {}; + } + if (response.status != 200) { + if (responseData && responseData["error"]) { + utils.sendUsageReport(bsConfig, args, responseData["error"], Constants.messageTypes.ERROR, 'api_failed_build_artifacts_status_update', buildReportData, rawArgs); + reject(responseData["error"]) } } - resolve() - }); + resolve(); + } catch (error) { + if(error.response) { + utils.sendUsageReport(bsConfig, args, error.response, Constants.messageTypes.ERROR, 'api_failed_build_artifacts_status_update', buildReportData, rawArgs); + logger.error(utils.formatRequest(error.response.statusText, error.response, error.response.data)); + reject(errror.response.data.message); + } + } }); } @@ -235,53 +255,51 @@ exports.downloadBuildArtifacts = async (bsConfig, buildId, args, rawArgs, buildR let messageType = null; let errorCode = null; let buildDetails = null; - request.get(options, async function (err, resp, body) { - if(err) { - logger.error(utils.formatRequest(err, resp, body)); - utils.sendUsageReport(bsConfig, args, err, Constants.messageTypes.ERROR, 'api_failed_build_artifacts', buildReportData, rawArgs); + options.config = { + auth: options.auth, + headers: options.headers + } + setAxiosProxy(options.config); + let response; + try { + response = await axios.get(options.url, options.config); + buildDetails = response.data; + await createDirectories(buildId, buildDetails); + await parseAndDownloadArtifacts(buildId, buildDetails, bsConfig, args, rawArgs, buildReportData); + if (BUILD_ARTIFACTS_FAIL_COUNT > 0) { + messageType = Constants.messageTypes.ERROR; + message = Constants.userMessages.DOWNLOAD_BUILD_ARTIFACTS_FAILED.replace('', buildId).replace('', BUILD_ARTIFACTS_FAIL_COUNT); + logger.error(message); process.exitCode = Constants.ERROR_EXIT_CODE; } else { - 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}`) - logger.error(utils.formatRequest(err, resp, body)); - utils.sendUsageReport(bsConfig, args, buildDetails, Constants.messageTypes.ERROR, 'api_failed_build_artifacts', buildReportData, rawArgs); - process.exitCode = Constants.ERROR_EXIT_CODE; - } else { - await createDirectories(buildId, buildDetails); - await parseAndDownloadArtifacts(buildId, buildDetails, bsConfig, args, rawArgs, buildReportData); - if (BUILD_ARTIFACTS_FAIL_COUNT > 0) { - messageType = Constants.messageTypes.ERROR; - message = Constants.userMessages.DOWNLOAD_BUILD_ARTIFACTS_FAILED.replace('', buildId).replace('', 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('', buildId).replace('', process.cwd()); - logger.info(message); - } - await sendUpdatesToBstack(bsConfig, buildId, args, options, rawArgs, buildReportData) - utils.sendUsageReport(bsConfig, args, message, messageType, null, buildReportData, rawArgs); - } - } catch (err) { + messageType = Constants.messageTypes.SUCCESS; + message = Constants.userMessages.DOWNLOAD_BUILD_ARTIFACTS_SUCCESS.replace('', buildId).replace('', process.cwd()); + logger.info(message); + } + await sendUpdatesToBstack(bsConfig, buildId, args, options, rawArgs, buildReportData) + utils.sendUsageReport(bsConfig, args, message, messageType, null, buildReportData, rawArgs); + } catch (err) { + messageType = Constants.messageTypes.ERROR; + errorCode = 'api_failed_build_artifacts'; + if(err.response && err.response.status !== 200) { + logger.error('Downloading the build artifacts failed.'); + logger.error(`Error: Request failed with status code ${err.response.status}`) + logger.error(utils.formatRequest(err.response.statusText, err.response, err.response.data)); + utils.sendUsageReport(bsConfig, args, JSON.stringify(buildDetails), Constants.messageTypes.ERROR, 'api_failed_build_artifacts', buildReportData, rawArgs); + } else { + if (BUILD_ARTIFACTS_FAIL_COUNT > 0) { 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('', buildId).replace('', BUILD_ARTIFACTS_FAIL_COUNT); - logger.error(message); - } else { - logger.error('Downloading the build artifacts failed.'); - } - utils.sendUsageReport(bsConfig, args, err, messageType, errorCode, buildReportData, rawArgs); - logger.error(`Error: Request failed with status code ${resp.statusCode}`) - logger.error(utils.formatRequest(err, resp, body)); - process.exitCode = Constants.ERROR_EXIT_CODE; + message = Constants.userMessages.DOWNLOAD_BUILD_ARTIFACTS_FAILED.replace('', buildId).replace('', BUILD_ARTIFACTS_FAIL_COUNT); + logger.error(message); + } else { + logger.error('Downloading the build artifacts failed.'); } + utils.sendUsageReport(bsConfig, args, err, messageType, errorCode, buildReportData, rawArgs); + logger.error(`Error: Request failed with status code ${resp.status}`) + logger.error(utils.formatRequest(err, resp, body)); } - resolve(); - }); + process.exitCode = Constants.ERROR_EXIT_CODE; + } + resolve(); }); }; diff --git a/bin/helpers/capabilityHelper.js b/bin/helpers/capabilityHelper.js index 0467c154..425f7e43 100644 --- a/bin/helpers/capabilityHelper.js +++ b/bin/helpers/capabilityHelper.js @@ -132,6 +132,7 @@ const caps = (bsConfig, zip) => { } obj.cypress_cli_user_agent = Utils.getUserAgent(); + logger.info(`Cypress CLI User Agent: ${obj.cypress_cli_user_agent}`); if(obj.parallels === Constants.cliMessages.RUN.DEFAULT_PARALLEL_MESSAGE) obj.parallels = undefined diff --git a/bin/helpers/checkUploaded.js b/bin/helpers/checkUploaded.js index 2acb5cba..dfc29241 100644 --- a/bin/helpers/checkUploaded.js +++ b/bin/helpers/checkUploaded.js @@ -1,5 +1,5 @@ 'use strict'; -const request = require('request'); +const { default: axios } = require('axios'); const { combineMacWinNpmDependencies } = require('./helper'); const crypto = require('crypto'), @@ -11,6 +11,7 @@ const crypto = require('crypto'), utils = require('./utils'), logger = require('./logger').winstonLogger; +const { setAxiosProxy } = require('./helper'); const checkSpecsMd5 = (runSettings, args, instrumentBlocks) => { return new Promise(function (resolve, reject) { @@ -91,7 +92,7 @@ const checkUploadedMd5 = (bsConfig, args, instrumentBlocks) => { } instrumentBlocks.markBlockStart("checkAlreadyUploaded.md5Total"); - checkSpecsMd5(bsConfig.run_settings, args, instrumentBlocks).then(function (zip_md5sum) { + checkSpecsMd5(bsConfig.run_settings, args, instrumentBlocks).then(async function (zip_md5sum) { instrumentBlocks.markBlockStart("checkAlreadyUploaded.md5Package"); let npm_package_md5sum = checkPackageMd5(bsConfig.run_settings); instrumentBlocks.markBlockEnd("checkAlreadyUploaded.md5Package"); @@ -116,7 +117,7 @@ const checkUploadedMd5 = (bsConfig, args, instrumentBlocks) => { 'Content-Type': 'application/json', "User-Agent": utils.getUserAgent(), }, - body: JSON.stringify(data) + body: data }; if (Constants.turboScaleObj.enabled) { @@ -124,39 +125,48 @@ const checkUploadedMd5 = (bsConfig, args, instrumentBlocks) => { } instrumentBlocks.markBlockStart("checkAlreadyUploaded.railsCheck"); - request.post(options, function (err, resp, body) { - if (err) { - instrumentBlocks.markBlockEnd("checkAlreadyUploaded.railsCheck"); - resolve(obj); - } else { - let zipData = null; - try { - zipData = JSON.parse(body); - } catch (error) { - zipData = {}; - } - if (resp.statusCode === 200) { - if (!utils.isUndefined(zipData.zipUrl)) { - Object.assign(obj, zipData, {zipUrlPresent: true}); - } - if (!utils.isUndefined(zipData.npmPackageUrl)) { - Object.assign(obj, zipData, {packageUrlPresent: true}); - } - } - if (utils.isTrueString(zipData.disableNpmSuiteCache)) { - bsConfig.run_settings.cache_dependencies = false; - Object.assign(obj, {packageUrlPresent: false}); - delete obj.npm_package_md5sum; + + const axiosConfig = { + auth: { + username: options.auth.user, + password: options.auth.password + }, + headers: options.headers + }; + setAxiosProxy(axiosConfig); + + try { + const response = await axios.post(options.url, options.body, axiosConfig); + let zipData = null; + try { + zipData = response.data; + } catch (error) { + zipData = {}; + } + if (response.status === 200) { + if (!utils.isUndefined(zipData.zipUrl)) { + Object.assign(obj, zipData, {zipUrlPresent: true}); } - if (utils.isTrueString(zipData.disableTestSuiteCache)) { - args["force-upload"] = true; - Object.assign(obj, {zipUrlPresent: false}); - delete obj.zip_md5sum; + if (!utils.isUndefined(zipData.npmPackageUrl)) { + Object.assign(obj, zipData, {packageUrlPresent: true}); } - instrumentBlocks.markBlockEnd("checkAlreadyUploaded.railsCheck"); - resolve(obj); } - }); + if (utils.isTrueString(zipData.disableNpmSuiteCache)) { + bsConfig.run_settings.cache_dependencies = false; + Object.assign(obj, {packageUrlPresent: false}); + delete obj.npm_package_md5sum; + } + if (utils.isTrueString(zipData.disableTestSuiteCache)) { + args["force-upload"] = true; + Object.assign(obj, {zipUrlPresent: false}); + delete obj.zip_md5sum; + } + instrumentBlocks.markBlockEnd("checkAlreadyUploaded.railsCheck"); + resolve(obj); + } catch (error) { + instrumentBlocks.markBlockEnd("checkAlreadyUploaded.railsCheck"); + resolve(obj); + } }).catch((err) => { let errString = err.stack ? err.stack.toString().substring(0,100) : err.toString().substring(0,100); resolve({zipUrlPresent: false, packageUrlPresent: false, error: errString}); diff --git a/bin/helpers/downloadBuildStacktrace.js b/bin/helpers/downloadBuildStacktrace.js index a7471038..50d55f7c 100644 --- a/bin/helpers/downloadBuildStacktrace.js +++ b/bin/helpers/downloadBuildStacktrace.js @@ -1,28 +1,37 @@ -'use strict' -const request = require('request'); +'use strict'; +const { default: axios } = require('axios'); +const { setAxiosProxy } = require('./helper'); + +const downloadBuildStacktrace = async (url, bsConfig) => { + const axiosConfig = { + auth: { + username: bsConfig.auth.username, + password: bsConfig.auth.access_key + }, + responseType: 'stream', + }; + setAxiosProxy(axiosConfig); -const downloadBuildStacktrace = async (url) => { return new Promise(async (resolve, reject) => { - request.get(url).on('response', function (response) { - if(response.statusCode == 200) { - response.pipe(process.stdout); + try { + const response = await axios.get(url, axiosConfig); + if (response.status === 200) { + response.data.pipe(process.stdout); let error = null; process.stdout.on('error', (err) => { error = err; process.stdout.close(); - reject(response.statusCode); + reject(response.status); }); process.stdout.on('close', async () => { - if(!error) { - resolve("Build stacktrace downloaded successfully"); + if (!error) { + resolve('Build stacktrace downloaded successfully'); } }); - } else { - reject(response.statusCode); } - }).on('end', () => { - resolve("Build stacktrace downloaded successfully"); - }); + } catch (error) { + reject(error.response.status); + } }); }; diff --git a/bin/helpers/getInitialDetails.js b/bin/helpers/getInitialDetails.js index 30d3114b..61b1539d 100644 --- a/bin/helpers/getInitialDetails.js +++ b/bin/helpers/getInitialDetails.js @@ -1,11 +1,14 @@ -const request = require('request'), - logger = require('./logger').winstonLogger, +const { default: axios } = require('axios'); + +const logger = require('./logger').winstonLogger, utils = require('./utils'), config = require("./config"), Constants = require('./constants'); +const { setAxiosProxy } = require('./helper'); + exports.getInitialDetails = (bsConfig, args, rawArgs) => { - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { let options = { url: config.getInitialDetails, auth: { @@ -17,28 +20,36 @@ exports.getInitialDetails = (bsConfig, args, rawArgs) => { } }; let responseData = {}; - request.get(options, function (err, resp, data) { - if(err) { - logger.warn(utils.formatRequest(err, resp, data)); - utils.sendUsageReport(bsConfig, args, err, Constants.messageTypes.ERROR, 'get_initial_details_failed', null, rawArgs); + + const axiosConfig = { + auth: options.auth, + headers: options.headers, + } + setAxiosProxy(axiosConfig); + + try { + const response = await axios.get(options.url, axiosConfig); + try { + responseData = response.data; + } catch (e) { + responseData = {}; + } + if(response.status != 200) { + logger.warn(`Warn: Get Initial Details Request failed with status code ${response.status}`); + utils.sendUsageReport(bsConfig, args, responseData["error"], Constants.messageTypes.ERROR, 'get_initial_details_failed', null, rawArgs); resolve({}); } else { - try { - responseData = JSON.parse(data); - } catch (e) { - responseData = {}; - } - if(resp.statusCode != 200) { - logger.warn(`Warn: Get Initial Details Request failed with status code ${resp.statusCode}`); - utils.sendUsageReport(bsConfig, args, responseData["error"], Constants.messageTypes.ERROR, 'get_initial_details_failed', null, rawArgs); - resolve({}); - } else { - if (!utils.isUndefined(responseData.grr) && responseData.grr.enabled && !utils.isUndefined(responseData.grr.urls)) { - config.uploadUrl = responseData.grr.urls.upload_url; - } - resolve(responseData); + if (!utils.isUndefined(responseData.grr) && responseData.grr.enabled && !utils.isUndefined(responseData.grr.urls)) { + config.uploadUrl = responseData.grr.urls.upload_url; } + resolve(responseData); + } + } catch (error) { + if(error.response && error.response.status !== 200) { + logger.warn(`Warn: Get Initial Details Request failed with status code ${error.response.status}`); + utils.sendUsageReport(bsConfig, args, error.response.data["error"], Constants.messageTypes.ERROR, 'get_initial_details_failed', null, rawArgs); } - }); + resolve({}); + } }); }; diff --git a/bin/helpers/helper.js b/bin/helpers/helper.js index 3e89e971..ecb4e279 100644 --- a/bin/helpers/helper.js +++ b/bin/helpers/helper.js @@ -6,7 +6,6 @@ const fs = require('fs'); const path = require('path'); const http = require('http'); const https = require('https'); -const request = require('request'); const gitLastCommit = require('git-last-commit'); const { v4: uuidv4 } = require('uuid'); const os = require('os'); @@ -17,8 +16,9 @@ const { spawn, execSync } = require('child_process'); const glob = require('glob'); const pGitconfig = promisify(gitconfig); const { readCypressConfigFile } = require('./readCypressConfigUtil'); -const CrashReporter = require('../testObservability/crashReporter'); const { MAX_GIT_META_DATA_SIZE_IN_BYTES, GIT_META_DATA_TRUNCATED } = require('./constants') +const CrashReporter = require('../testObservability/crashReporter'); +const HttpsProxyAgent = require('https-proxy-agent'); exports.debug = (text, shouldReport = false, throwable = null) => { if (process.env.BROWSERSTACK_OBSERVABILITY_DEBUG === "true" || process.env.BROWSERSTACK_OBSERVABILITY_DEBUG === "1") { @@ -442,6 +442,14 @@ exports.truncateString = (field, truncateSizeInBytes) => { return field; }; +exports.setAxiosProxy = (axiosConfig) => { + if (process.env.HTTP_PROXY || process.env.HTTPS_PROXY) { + const httpProxy = process.env.HTTP_PROXY || process.env.HTTPS_PROXY + axiosConfig.proxy = false; + axiosConfig.httpsAgent = new HttpsProxyAgent(httpProxy); + }; +}; + exports.combineMacWinNpmDependencies = (runSettings) => { return Object.assign({}, runSettings.npm_dependencies, runSettings.win_npm_dependencies || {}, runSettings.mac_npm_dependencies || {}) }; diff --git a/bin/helpers/reporterHTML.js b/bin/helpers/reporterHTML.js index 6cf2dfdc..541a0a28 100644 --- a/bin/helpers/reporterHTML.js +++ b/bin/helpers/reporterHTML.js @@ -1,6 +1,7 @@ +const axios = require('axios').default; + const fs = require('fs'), path = require('path'), - request = require('request'), logger = require('./logger').winstonLogger, utils = require("./utils"), Constants = require('./constants'), @@ -8,7 +9,9 @@ const fs = require('fs'), decompress = require('decompress'); const { isTurboScaleSession } = require('../helpers/atsHelper'); -let reportGenerator = (bsConfig, buildId, args, rawArgs, buildReportData, cb) => { +const { setAxiosProxy } = require('./helper'); + +let reportGenerator = async (bsConfig, buildId, args, rawArgs, buildReportData, cb) => { let options = { url: `${config.buildUrl}${buildId}/custom_report`, auth: { @@ -26,32 +29,29 @@ let reportGenerator = (bsConfig, buildId, args, rawArgs, buildReportData, cb) => logger.debug('Started fetching the build json and html reports.'); - return request.get(options, async function (err, resp, body) { - let message = null; - let messageType = null; - let errorCode = null; - let build; - - if (err) { - message = err; - messageType = Constants.messageTypes.ERROR; - errorCode = 'api_failed_build_report'; + let message = null; + let messageType = null; + let errorCode = null; + let build; - logger.error('Generating the build report failed.'); - logger.error(utils.formatRequest(err, resp, body)); + const axiosConfig = { + auth: { + username: options.auth.user, + password: options.auth.password + }, + headers: options.headers + } + setAxiosProxy(axiosConfig); - utils.sendUsageReport(bsConfig, args, message, messageType, errorCode, buildReportData, rawArgs); - return; - } else { - logger.debug('Received reports data from upstream.'); - try { - build = JSON.parse(body); - } catch (error) { - build = null; - } + try { + const response = await axios.get(options.url, axiosConfig); + logger.debug('Received reports data from upstream.'); + try { + build = response.data; + } catch (error) { + build = null; } - - if (resp.statusCode == 299) { + if (response.status == 299) { messageType = Constants.messageTypes.INFO; errorCode = 'api_deprecated'; if (build) { @@ -61,18 +61,18 @@ let reportGenerator = (bsConfig, buildId, args, rawArgs, buildReportData, cb) => message = Constants.userMessages.API_DEPRECATED; logger.info(message); } - } else if (resp.statusCode === 422) { + } else if (response.status === 422) { messageType = Constants.messageTypes.ERROR; errorCode = 'api_failed_build_generate_report'; try { - response = JSON.parse(body); + response = error.response.data; message = response.message; } catch (error) { logger.error(`Error generating the report: ${error}`); response = {message: message}; } - logger.error(utils.formatRequest(err, resp, body)); - } else if (resp.statusCode != 200) { + logger.error(utils.formatRequest(response.statusText, response, response.data)); + } else if (response.status != 200) { messageType = Constants.messageTypes.ERROR; errorCode = 'api_failed_build_generate_report'; @@ -84,7 +84,7 @@ let reportGenerator = (bsConfig, buildId, args, rawArgs, buildReportData, cb) => if (build.message === 'Unauthorized') errorCode = 'api_auth_failed'; } else { message = Constants.userMessages.BUILD_GENERATE_REPORT_FAILED.replace('', buildId); - logger.error(utils.formatRequest(err, resp, body)); + logger.error(utils.formatRequest(error.response.statusText, error.response, error.response.data)); } } else { messageType = Constants.messageTypes.SUCCESS; @@ -97,7 +97,16 @@ let reportGenerator = (bsConfig, buildId, args, rawArgs, buildReportData, cb) => if (cb){ cb(); } - }); + } catch (error) { + message = error.response.statusText; + messageType = Constants.messageTypes.ERROR; + errorCode = 'api_failed_build_report'; + + logger.error('Generating the build report failed.'); + logger.error(utils.formatRequest(error.response.statusText, error.response, error.response.data)); + utils.sendUsageReport(bsConfig, args, message, messageType, errorCode, buildReportData, rawArgs); + return; + } } async function generateCypressBuildReport(report_data) { @@ -116,15 +125,16 @@ function getReportResponse(filePath, fileName, reportJsonUrl) { const writer = fs.createWriteStream(tmpFilePath); logger.debug(`Fetching build reports zip.`) return new Promise(async (resolve, reject) => { - request.get(reportJsonUrl).on('response', function(response) { - - if(response.statusCode != 200) { - let message = `Received non 200 response while fetching reports, code: ${response.statusCode}`; - reject(message); - } else { + try { + const axiosConfig = { + responseType: 'stream', + }; + setAxiosProxy(axiosConfig); + const response = await axios.get(reportJsonUrl, axiosConfig); + if(response.status === 200) { //ensure that the user can call `then()` only when the file has //been downloaded entirely. - response.pipe(writer); + response.data.pipe(writer); let error = null; writer.on('error', err => { error = err; @@ -149,7 +159,10 @@ function getReportResponse(filePath, fileName, reportJsonUrl) { //'error' stream; }); } - }); + } catch (error) { + let message = `Received non 200 response while fetching reports, code: ${error.response.status}`; + reject(message); + } }); } diff --git a/bin/helpers/sync/syncSpecsLogs.js b/bin/helpers/sync/syncSpecsLogs.js index 8933868c..0f6141e8 100644 --- a/bin/helpers/sync/syncSpecsLogs.js +++ b/bin/helpers/sync/syncSpecsLogs.js @@ -1,6 +1,8 @@ "use strict"; -const request = require("request"), - config = require("../config"), + +const { default: axios } = require("axios"); + +const config = require("../config"), utils = require("../utils"), logger = require("../logger").syncCliLogger, winstonLogger = require("../logger").winstonLogger, @@ -9,6 +11,8 @@ const request = require("request"), tableStream = require('table').createStream, chalk = require('chalk'); +const { setAxiosProxy } = require('../helper'); + let whileLoop = true, whileTries = config.retries, options, timeout = 3000, n = 2, tableConfig, stream, endTime, startTime = Date.now(), buildStarted = false; let specSummary = { "buildError": null, @@ -135,27 +139,23 @@ let printSpecsStatus = (bsConfig, buildDetails, rawArgs, buildReportData) => { }); }; -let whileProcess = (whilstCallback) => { - request.post(options, function(error, response, body) { - if (error) { - whileTries -= 1; - if (whileTries === 0) { - whileLoop = false; - endTime = Date.now(); - specSummary.exitCode = config.networkErrorExitCode; - return whilstCallback({ status: 504, message: "Tries limit reached" }); //Gateway Timeout - } else { - n = 2 - return setTimeout(whilstCallback, timeout * n, null); - } - } +let whileProcess = async (whilstCallback) => { + try { + const axiosConfig = { + auth: { + username: options.auth.user, + password: options.auth.password + }, + headers: options.headers + }; + setAxiosProxy(axiosConfig); + const response = await axios.post(options.url, {}, axiosConfig); whileTries = config.retries; // reset to default after every successful request - - switch (response.statusCode) { + switch (response.status) { case 202: // get data here and print it n = 2 - showSpecsStatus(body, 202); + showSpecsStatus(response.data, 202); return setTimeout(whilstCallback, timeout * n, null); case 204: // No data available, wait for some time and ask again n = 1 @@ -163,14 +163,27 @@ let whileProcess = (whilstCallback) => { case 200: // Build is completed. whileLoop = false; endTime = Date.now(); - showSpecsStatus(body, 200); + showSpecsStatus(response.data, 200); return specSummary.exitCode == Constants.BUILD_FAILED_EXIT_CODE ? whilstCallback({ status: 204, message: "No specs ran in the build"} ) : whilstCallback(null, body); default: whileLoop = false; - return whilstCallback({ status: response.statusCode, message: body }); + return whilstCallback({ status: response.status, message: response.data }); } - }); + } catch (error) { + if (error) { + whileTries -= 1; + if (whileTries === 0) { + whileLoop = false; + endTime = Date.now(); + specSummary.exitCode = config.networkErrorExitCode; + return whilstCallback({ status: 504, message: "Tries limit reached" }); //Gateway Timeout + } else { + n = 2 + return setTimeout(whilstCallback, timeout * n, null); + } + } + } } let getStackTraceUrl = () => { @@ -178,7 +191,7 @@ let getStackTraceUrl = () => { } let showSpecsStatus = (data, statusCode) => { - let specData = JSON.parse(data); + let specData = data; specData["specData"].forEach(specDetails => { if (specDetails.type === Constants.CYPRESS_CUSTOM_ERRORS_TO_PRINT_KEY) { addCustomErrorToPrint(specDetails); diff --git a/bin/helpers/usageReporting.js b/bin/helpers/usageReporting.js index 5a7a250e..95a827ec 100644 --- a/bin/helpers/usageReporting.js +++ b/bin/helpers/usageReporting.js @@ -1,7 +1,6 @@ 'use strict'; const cp = require("child_process"), os = require("os"), - request = require("requestretry"), fs = require('fs'), path = require('path'); @@ -10,7 +9,11 @@ const config = require('./config'), utils = require('./utils'); const { AUTH_REGEX, REDACTED_AUTH, REDACTED, CLI_ARGS_REGEX, RAW_ARGS_REGEX } = require("./constants"); -const { isTurboScaleSession } = require('../helpers/atsHelper'); +const { default: axios } = require("axios"); +const axiosRetry = require("axios-retry"); +const { isTurboScaleSession } = require("./atsHelper"); + +const { setAxiosProxy } = require('./helper'); function get_version(package_name) { try { @@ -217,31 +220,36 @@ function sendTurboscaleErrorLogs(args) { password: bsConfig.auth.access_key, }, url: `${config.turboScaleAPIUrl}/send-instrumentation`, - body: turboscaleErrorPayload, - json: true, - maxAttempts: 10, // (default) try 3 times + data: turboscaleErrorPayload, + maxAttempts: 10, retryDelay: 2000, // (default) wait for 2s before trying again - retrySrategy: request.RetryStrategies.HTTPOrNetworkError, // (default) retry on 5xx or network errors }; - fileLogger.info(`Sending ${JSON.stringify(turboscaleErrorPayload)} to ${config.turboScaleAPIUrl}/send-instrumentation`); - request(options, function (error, res, body) { - if (error) { - //write err response to file - fileLogger.error(JSON.stringify(error)); - return; + axiosRetry(axios, { + retries: options.maxAttempts, + retryDelay: (retryCount) => options.retryDelay, + retryCondition: (error) => { + return axiosRetry.isRetryableError(error) // (default) retry on 5xx or network errors } - // write response file + }); + + fileLogger.info(`Sending ${JSON.stringify(turboscaleErrorPayload)} to ${config.turboScaleAPIUrl}/send-instrumentation`); + + axios(options) + .then((res) => { let response = { - attempts: res.attempts, - statusCode: res.statusCode, - body: body + attempts: res.config['axios-retry'].retryCount + 1, + statusCode: res.status, + body: res.data }; fileLogger.info(`${JSON.stringify(response)}`); + }) + .catch((error) => { + fileLogger.error(JSON.stringify(error)); }); } -function send(args) { +async function send(args) { let bsConfig = JSON.parse(JSON.stringify(args.bstack_config)); if (isTurboScaleSession(bsConfig) && args.message_type === 'error') { @@ -307,10 +315,6 @@ function send(args) { }, }; - if (isTurboScaleSession(bsConfig)) { - payload.event_type = 'hst_cypress_cli_stats'; - } - const options = { headers: { "Content-Type": "text/json", @@ -321,24 +325,38 @@ function send(args) { json: true, maxAttempts: 10, // (default) try 3 times retryDelay: 2000, // (default) wait for 2s before trying again - retrySrategy: request.RetryStrategies.HTTPOrNetworkError, // (default) retry on 5xx or network errors }; + const axiosConfig = { + headers: options.headers, + }; + setAxiosProxy(axiosConfig); + fileLogger.info(`Sending ${JSON.stringify(payload)} to ${config.usageReportingUrl}`); - request(options, function (error, res, body) { - if (error) { - //write err response to file - fileLogger.error(JSON.stringify(error)); - return; + axiosRetry(axios, + { + retries: 3, + retryDelay: 2000, + retryCondition: (error) => { + return utils.isNotUndefined(error.response) && (error.response.status === 503 || error.response.status === 500) } - // write response file - let response = { - attempts: res.attempts, - statusCode: res.statusCode, - body: body - }; - fileLogger.info(`${JSON.stringify(response)}`); }); + try { + const response = await axios.post(options.url, options.body, axiosConfig); + let result = { + statusText: response.statusText, + statusCode: response.status, + body: response.data + }; + fileLogger.info(`${JSON.stringify(result)}`); + } catch (error) { + if (error.response) { + fileLogger.error(JSON.stringify(error.response.data)); + } else { + fileLogger.error(`Error sending usage data: ${error.message}`); + } + return; + } } module.exports = { diff --git a/bin/helpers/utils.js b/bin/helpers/utils.js index fc5fd614..12d6c99a 100644 --- a/bin/helpers/utils.js +++ b/bin/helpers/utils.js @@ -12,6 +12,7 @@ const { promisify } = require('util'); const readdir = promisify(fs.readdir); const stat = promisify(fs.stat); const TIMEZONE = require("../helpers/timezone.json"); +const { setAxiosProxy } = require('./helper'); const usageReporting = require("./usageReporting"), logger = require("./logger").winstonLogger, @@ -25,7 +26,7 @@ const usageReporting = require("./usageReporting"), o11yHelpers = require('../testObservability/helper/helper'), { OBSERVABILITY_ENV_VARS, TEST_OBSERVABILITY_REPORTER } = require('../testObservability/helper/constants'); -const request = require('request'); +const { default: axios } = require("axios"); exports.validateBstackJson = (bsConfigPath) => { return new Promise(function (resolve, reject) { @@ -491,6 +492,7 @@ exports.setNodeVersion = (bsConfig, args) => { // specs can be passed via command line args as a string // command line args takes precedence over config exports.setUserSpecs = (bsConfig, args) => { + if(o11yHelpers.isBrowserstackInfra() && o11yHelpers.isTestObservabilitySession() && o11yHelpers.shouldReRunObservabilityTests()) { bsConfig.run_settings.specs = process.env.BROWSERSTACK_RERUN_TESTS; return; @@ -591,7 +593,7 @@ exports.setSystemEnvs = (bsConfig) => { OBSERVABILITY_ENV_VARS.forEach(key => { envKeys[key] = process.env[key]; }); - + let gitConfigPath = o11yHelpers.findGitConfig(process.cwd()); if(!o11yHelpers.isBrowserstackInfra()) process.env.OBSERVABILITY_GIT_CONFIG_PATH_LOCAL = gitConfigPath; if(gitConfigPath) { @@ -1018,14 +1020,27 @@ exports.checkLocalBinaryRunning = (bsConfig, localIdentifier) => { }, body: JSON.stringify({ localIdentifier: localIdentifier}), }; - return new Promise ( function(resolve, reject) { - request.post(options, function (err, resp, body) { - if(err){ - reject(err); + + const axiosConfig = { + auth: { + username: options.auth.user, + password: options.auth.password + }, + headers: options.headers + }; + setAxiosProxy(axiosConfig); + + return new Promise (async function(resolve, reject) { + try { + const response = await axios.post(options.url, { + localIdentifier: localIdentifier + }, axiosConfig); + resolve(response.data) + } catch (error) { + if(error.response) { + reject(error.response.data.message); } - let response = JSON.parse(body); - resolve(response); - }); + } }); }; @@ -1475,6 +1490,7 @@ exports.splitStringByCharButIgnoreIfWithinARange = (str, splitChar, leftLimiter, // blindly send other passed configs with run_settings and handle at backend exports.setOtherConfigs = (bsConfig, args) => { + if(o11yHelpers.isTestObservabilitySession() && process.env.BS_TESTOPS_JWT) { bsConfig["run_settings"]["reporter"] = TEST_OBSERVABILITY_REPORTER; return; @@ -1537,7 +1553,7 @@ exports.setCLIMode = (bsConfig, args) => { exports.formatRequest = (err, resp, body) => { return { err, - status: resp ? resp.statusCode : null, + status: resp ? resp.status : null, body: body ? util.format('%j', body) : null } } @@ -1557,78 +1573,80 @@ exports.setDebugMode = (args) => { exports.stopBrowserStackBuild = async (bsConfig, args, buildId, rawArgs, buildReportData = null) => { let that = this; - return new Promise(function (resolve, reject) { - let url = config.buildStopUrl + buildId; - let options = { - url: url, - auth: { - username: bsConfig["auth"]["username"], - password: bsConfig["auth"]["access_key"], - }, - headers: { - 'User-Agent': that.getUserAgent(), - }, - }; + + let url = config.buildStopUrl + buildId; + let options = { + url: url, + auth: { + username: bsConfig["auth"]["username"], + password: bsConfig["auth"]["access_key"], + }, + headers: { + 'User-Agent': that.getUserAgent(), + }, + }; - if (Constants.turboScaleObj.enabled) { - options.url = `${config.turboScaleBuildsUrl}/${buildId}/stop`; - } + const axiosConfig = { + auth: options.auth, + headers: options.headers + }; + setAxiosProxy(axiosConfig); - let message = null; - let messageType = null; - let errorCode = null; - let build = null; - request.post(options, function(err, resp, data) { - if(err) { - message = Constants.userMessages.BUILD_STOP_FAILED; - messageType = Constants.messageTypes.ERROR; - errorCode = 'api_failed_build_stop'; + if (Constants.turboScaleObj.enabled) { + options.url = `${config.turboScaleBuildsUrl}/${buildId}/stop`; + } + + let message = null; + let messageType = null; + let errorCode = null; + let build = null; + + try { + const response = await axios.post(options.url, {}, axiosConfig); + + build = response.data; + if (response.status == 299) { + messageType = Constants.messageTypes.INFO; + errorCode = 'api_deprecated'; + + if (build) { + message = build.message; logger.info(message); } else { - try { - build = JSON.parse(data); - 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_stop'; - - if (build) { - message = `${ - Constants.userMessages.BUILD_STOP_FAILED - } with error: \n${JSON.stringify(build, null, 2)}`; - logger.error(message); - if (build.message === 'Unauthorized') errorCode = 'api_auth_failed'; - } else { - message = Constants.userMessages.BUILD_STOP_FAILED; - logger.error(message); - } - } else { - messageType = Constants.messageTypes.SUCCESS; - message = `${JSON.stringify(build, null, 2)}`; - logger.info(message); - } - } catch(err) { - message = Constants.userMessages.BUILD_STOP_FAILED; - messageType = Constants.messageTypes.ERROR; - errorCode = 'api_failed_build_stop'; - logger.info(message); - } finally { - that.sendUsageReport(bsConfig, args, message, messageType, errorCode, buildReportData, rawArgs); - } + message = Constants.userMessages.API_DEPRECATED; + logger.info(message); } - resolve(); - }); - }); + } else if (response.status !== 200) { + messageType = Constants.messageTypes.ERROR; + errorCode = 'api_failed_build_stop'; + + if (build) { + message = `${ + Constants.userMessages.BUILD_STOP_FAILED + } with error: \n${JSON.stringify(build, null, 2)}`; + logger.error(message); + if (build.message === 'Unauthorized') errorCode = 'api_auth_failed'; + } else { + message = Constants.userMessages.BUILD_STOP_FAILED; + logger.error(message); + } + } else { + messageType = Constants.messageTypes.SUCCESS; + message = `${JSON.stringify(build, null, 2)}`; + logger.info(message); + } + } catch(err) { + if(err.response) { + message = `${ + Constants.userMessages.BUILD_STOP_FAILED + } with error: ${err.response.data.message}`; + messageType = Constants.messageTypes.ERROR; + errorCode = 'api_failed_build_stop'; + logger.error(message); + } + } finally { + that.sendUsageReport(bsConfig, args, message, messageType, errorCode, buildReportData, rawArgs); + } } exports.setProcessHooks = (buildId, bsConfig, bsLocal, args, buildReportData) => { @@ -1645,6 +1663,7 @@ exports.setProcessHooks = (buildId, bsConfig, bsLocal, args, buildReportData) => process.on('uncaughtException', processExitHandler.bind(this, bindData)); } + exports.setO11yProcessHooks = (() => { let bindData = {}; let handlerAdded = false; diff --git a/bin/helpers/zipUpload.js b/bin/helpers/zipUpload.js index 441f9ec0..8b59611c 100644 --- a/bin/helpers/zipUpload.js +++ b/bin/helpers/zipUpload.js @@ -1,26 +1,27 @@ 'use strict'; -const request = require("request"), - fs = require("fs"); +const fs = require("fs"); const cliProgress = require('cli-progress'); +const { default: axios } = require("axios"); +const FormData = require("form-data"); const config = require("./config"), logger = require("./logger").winstonLogger, Constants = require("./constants"), utils = require("./utils"); +const { setAxiosProxy } = require('./helper'); const purgeUploadBar = (obj) => { obj.bar1.update(100, { speed: ((obj.size / (Date.now() - obj.startTime)) / 125).toFixed(2) //kbits per sec }); obj.bar1.stop(); - clearInterval(obj.zipInterval); } const uploadSuits = (bsConfig, filePath, opts, obj) => { - return new Promise(function (resolve, reject) { + return new Promise(async function (resolve, reject) { let uploadProgressBarErrorFlags = { noConnectionReportSent: false, unknownErrorReportSent: false @@ -54,94 +55,69 @@ const uploadSuits = (bsConfig, filePath, opts, obj) => { let options = utils.generateUploadParams(bsConfig, filePath, opts.md5Data, opts.fileDetails) let responseData = null; - var r = request.post(options, function (err, resp, body) { - - if (err) { - reject({message: err, stacktrace: utils.formatRequest(err, resp, body)}); - } else { - try { - responseData = JSON.parse(body); - } catch (e) { - responseData = {}; - } - if (resp.statusCode != 200) { - if (resp.statusCode == 401) { - if (responseData && responseData["error"]) { - responseData["time"] = Date.now() - obj.startTime; - return reject({message: responseData["error"], stacktrace: utils.formatRequest(err, resp, body)}); - } else { - return reject({message: Constants.validationMessages.INVALID_DEFAULT_AUTH_PARAMS, stacktrace: utils.formatRequest(err, resp, body)}); - } - } - if (!opts.propogateError){ - purgeUploadBar(obj); - if (resp.statusCode == 413) { - return resolve({warn: Constants.userMessages.NODE_MODULES_LIMIT_EXCEEDED.replace("%SIZE%", (size / 1000000).toFixed(2))}); - } - return resolve({}) - } - if(responseData && responseData["error"]){ - responseData["time"] = Date.now() - obj.startTime; - reject({message: responseData["error"], stacktrace: utils.formatRequest(err, resp, body)}); + try { + const formData = new FormData(); + formData.append("file", fs.createReadStream(filePath)); + formData.append("filetype", opts.fileDetails.filetype); + formData.append("filename", opts.fileDetails.filename); + formData.append("zipMd5sum", opts.md5Data ? opts.md5Data : ''); + + const axiosConfig = { + auth: { + username: options.auth.user, + password: options.auth.password + }, + headers: options.headers, + onUploadProgress: (progressEvent) => { + let percent = parseInt(Math.floor((progressEvent.loaded * 100) / progressEvent.total)); + obj.bar1.update(percent, { + speed: ((progressEvent.bytes / (Date.now() - obj.startTime)) / 125).toFixed(2) //kbits per sec + }); + }, + }; + setAxiosProxy(axiosConfig); + + const response = await axios.post(options.url, formData, axiosConfig); + responseData = response.data; + purgeUploadBar(obj) + logger.info(`${opts.messages.uploadingSuccess} (${responseData[opts.md5ReturnKey]})`); + opts.cleanupMethod(); + responseData["time"] = Date.now() - obj.startTime; + resolve(responseData); + } catch (error) { + let responseData = null; + if(error.response){ + responseData = error.response.data; + if (error.response.status === 401) { + if (responseData && responseData.error) { + responseData.time = Date.now() - obj.startTime; + return reject({message: responseData.error, stacktrace: utils.formatRequest(responseData.error, error.response, responseData)}); } else { - if (resp.statusCode == 413) { - reject({message: Constants.userMessages.ZIP_UPLOAD_LIMIT_EXCEEDED, stacktrace: utils.formatRequest(err, resp, body)}); - } else { - reject({message: Constants.userMessages.ZIP_UPLOADER_NOT_REACHABLE, stacktrace: utils.formatRequest(err, resp, body)}); - } - } - } else { + return reject({message: Constants.validationMessages.INVALID_DEFAULT_AUTH_PARAMS, stacktrace: utils.formatRequest(error.response.statusText, error.response, responseData)}); + } + } + if (!opts.propogateError){ purgeUploadBar(obj); - logger.info(`${opts.messages.uploadingSuccess} (${responseData[opts.md5ReturnKey]})`); - opts.cleanupMethod(); - responseData["time"] = Date.now() - obj.startTime; - resolve(responseData); + if (error.response.status === 413) { + return resolve({warn: Constants.userMessages.NODE_MODULES_LIMIT_EXCEEDED.replace("%SIZE%", (size / 1000000).toFixed(2))}); + } + return resolve({}) } - } - }); - - obj.zipInterval = setInterval(function () { - const errorCode = 'update_upload_progress_bar_failed'; - try { - if (r && r.req && r.req.connection) { - let dispatched = r.req.connection._bytesDispatched; - let percent = dispatched * 100.0 / size; - obj.bar1.update(percent, { - speed: ((dispatched / (Date.now() - obj.startTime)) / 125).toFixed(2) //kbits per sec - }); + if(responseData && responseData["error"]){ + responseData["time"] = Date.now() - obj.startTime; + reject({message: responseData["error"], stacktrace: utils.formatRequest(error.response.statusText, error.response, responseData)}); } else { - if (!uploadProgressBarErrorFlags.noConnectionReportSent) { - logger.debug(Constants.userMessages.NO_CONNECTION_WHILE_UPDATING_UPLOAD_PROGRESS_BAR); - utils.sendUsageReport( - bsConfig, - null, - Constants.userMessages.NO_CONNECTION_WHILE_UPDATING_UPLOAD_PROGRESS_BAR, - Constants.messageTypes.WARNING, - errorCode, - null, - null - ); - uploadProgressBarErrorFlags.noConnectionReportSent = true; + if (error.response.status === 413) { + reject({message: Constants.userMessages.ZIP_UPLOAD_LIMIT_EXCEEDED, stacktrace: utils.formatRequest(error.response.statusText, error.response, responseData)}); + } else { + reject({message: Constants.userMessages.ZIP_UPLOADER_NOT_REACHABLE, stacktrace: utils.formatRequest(error.response.statusText, error.response, responseData)}); } } - } catch (error) { - if (!uploadProgressBarErrorFlags.unknownErrorReportSent) { - logger.debug('Unable to determine progress.'); - logger.debug(error); - utils.sendUsageReport( - bsConfig, - null, - error.stack, - Constants.messageTypes.WARNING, - errorCode, - null, - null - ); - uploadProgressBarErrorFlags.unknownErrorReportSent = true; - } + reject({message: error.response, stacktrace: utils.formatRequest(error.response.statusText, error.response, error.response.data)}); + } else { + reject({}) } - }, 150); - + } }); } diff --git a/bin/testObservability/crashReporter/index.js b/bin/testObservability/crashReporter/index.js index 7433c164..00ecb6bc 100644 --- a/bin/testObservability/crashReporter/index.js +++ b/bin/testObservability/crashReporter/index.js @@ -1,8 +1,8 @@ const fs = require('fs'); const path = require('path'); -const request = require('request'); -const http = require('http'); +const axios = require('axios'); const https = require('https'); +const HttpsProxyAgent = require('https-proxy-agent'); const logger = require("../../helpers/logger").winstonLogger; const utils = require('../../helpers/utils'); @@ -126,7 +126,7 @@ class CrashReporter { if (!this.credentialsForCrashReportUpload.username || !this.credentialsForCrashReportUpload.password) { return debug('[Crash_Report_Upload] Failed to parse user credentials while reporting crash') } - + const data = { hashed_id: process.env.BS_TESTOPS_BUILD_HASHED_ID, observability_version: { @@ -140,7 +140,7 @@ class CrashReporter { }, config: this.userConfigForReporting } - + const options = { auth: { ...this.credentialsForCrashReportUpload @@ -151,20 +151,29 @@ class CrashReporter { }, method: 'POST', url: `${API_URL}/api/v1/analytics`, - body: data, + data: data, json: true, agent: httpsKeepAliveAgent }; + + if(process.env.HTTP_PROXY){ + options.proxy = false + options.httpsAgent = new HttpsProxyAgent(process.env.HTTP_PROXY); + } else if (process.env.HTTPS_PROXY){ + options.proxy = false + options.httpsAgent = new HttpsProxyAgent(process.env.HTTPS_PROXY); + } - request(options, function callback(error, response, body) { - if(error) { - debug(`[Crash_Report_Upload] Failed due to ${error}`); - } else if(response.statusCode != 200) { - debug(`[Crash_Report_Upload] Failed due to ${response && response.body ? response.body : `Received response from BrowserStack Server with status : ${response.statusCode}`}`); - } else { - debug(`[Crash_Report_Upload] Success response: ${JSON.stringify({status: response.status, body: response.body})}`) - } - }); + axios(options) + .then(response => { + + if(response.status != 200) { + debug(`[Crash_Report_Upload] Failed due to ${response && response.data ? response.data : `Received response from BrowserStack Server with status : ${response.status}`}`); + } else { + debug(`[Crash_Report_Upload] Success response: ${JSON.stringify({status: response.status, body: response.data})}`) + } + }) + .catch(error => debug(`[Crash_Report_Upload] Failed due to ${error}`)); } catch(e) { debug(`[Crash_Report_Upload] Processing failed due to ${e && e.stack}`); } diff --git a/bin/testObservability/cypress/index.js b/bin/testObservability/cypress/index.js index 31c8d08e..78482442 100644 --- a/bin/testObservability/cypress/index.js +++ b/bin/testObservability/cypress/index.js @@ -2,13 +2,13 @@ /* Used to detect Gherkin steps */ Cypress.on('log:added', (log) => { - return () => { - return cy.now('task', 'test_observability_step', { - log - }, {log: false}) - } -}); - + return () => { + return cy.now('task', 'test_observability_step', { + log + }, {log: false}) + } + }); + Cypress.on('command:start', (command) => { if(!command || !command.attributes) return; if(command.attributes.name == 'log' || (command.attributes.name == 'task' && (command.attributes.args.includes('test_observability_command') || command.attributes.args.includes('test_observability_log')))) { diff --git a/bin/testObservability/helper/helper.js b/bin/testObservability/helper/helper.js index 1b7da41f..467f090c 100644 --- a/bin/testObservability/helper/helper.js +++ b/bin/testObservability/helper/helper.js @@ -1,8 +1,6 @@ const fs = require('fs'); const path = require('path'); -const http = require('http'); const https = require('https'); -const request = require('requestretry'); const { v4: uuidv4 } = require('uuid'); const os = require('os'); const { promisify } = require('util'); @@ -10,6 +8,8 @@ const gitconfig = require('gitconfiglocal'); const { spawn, execSync } = require('child_process'); const glob = require('glob'); const util = require('util'); +const axios = require('axios'); +const HttpsProxyAgent = require('https-proxy-agent'); const { runOptions } = require('../../helpers/runnerArgs') @@ -117,39 +117,55 @@ exports.printBuildLink = async (shouldStopSession, exitCode = null) => { } const nodeRequest = (type, url, data, config) => { - return new Promise(async (resolve, reject) => { - const options = {...config,...{ - method: type, - url: `${API_URL}/${url}`, - body: data, - json: config.headers['Content-Type'] === 'application/json', - agent: this.httpsKeepAliveAgent, - maxAttempts: 2 - }}; - - if(url === exports.requestQueueHandler.screenshotEventUrl) { - options.agent = httpsScreenshotsKeepAliveAgent; - } + return new Promise(async (resolve, reject) => { + const options = { + ...config, + method: type, + url: `${API_URL}/${url}`, + data: data, + httpsAgent: this.httpsKeepAliveAgent, + maxAttempts: 2, + headers: { + ...config.headers, + 'Content-Type': 'application/json;charset=utf-8', + "X-Forwarded-For": "127.0.0.1" + }, + clientIp: "127.0.0.1" + }; - request(options, function callback(error, response, body) { - if(error) { - reject(error); - } else if(response.statusCode != 200) { - reject(response && response.body ? response.body : `Received response from BrowserStack Server with status : ${response.statusCode}`); - } else { - try { - if(typeof(body) !== 'object') body = JSON.parse(body); - } catch(e) { - if(!url.includes('/stop')) { - reject('Not a JSON response from BrowserStack Server'); + if(process.env.HTTP_PROXY){ + options.proxy = false + options.httpsAgent = new HttpsProxyAgent(process.env.HTTP_PROXY); + } else if (process.env.HTTPS_PROXY){ + options.proxy = false + options.httpsAgent = new HttpsProxyAgent(process.env.HTTPS_PROXY); + } + + if(url === exports.requestQueueHandler.screenshotEventUrl) { + options.agent = httpsScreenshotsKeepAliveAgent; + } + axios(options) + .then(response => { + + if(response.status != 200) { + reject(response && response.data ? response.data : `Received response from BrowserStack Server with status : ${response.status}`); + } else { + try { + const responseBody = typeof response.data === 'object' ? response.data : JSON.parse(response.data); + resolve({ data: responseBody }); + } catch (error) { + if (!url.includes('/stop')) { + reject('Not a JSON response from BrowserStack Server'); + } else { + resolve({ data: response.data }); + } + } } - } - resolve({ - data: body + }) + .catch(error => { + reject(error) }); - } }); - }); } exports.failureData = (errors,tag) => { @@ -277,7 +293,7 @@ exports.setEventListeners = (bsConfig) => { try { if(!file.includes('commands.js')) { const defaultFileContent = fs.readFileSync(file, {encoding: 'utf-8'}); - + let cypressCommandEventListener = getCypressCommandEventListener(file.includes('js')); if(!defaultFileContent.includes(cypressCommandEventListener)) { let newFileContent = defaultFileContent + @@ -345,10 +361,10 @@ const setCrashReportingConfig = (bsConfig, bsConfigPath) => { exports.launchTestSession = async (user_config, bsConfigPath) => { setCrashReportingConfig(user_config, bsConfigPath); - + const obsUserName = user_config["auth"]["username"]; const obsAccessKey = user_config["auth"]["access_key"]; - + const BSTestOpsToken = `${obsUserName || ''}:${obsAccessKey || ''}`; if(BSTestOpsToken === '') { exports.debug('EXCEPTION IN BUILD START EVENT : Missing authentication token', true, null); @@ -480,12 +496,12 @@ exports.batchAndPostEvents = async (eventUrl, kind, data) => { 'Authorization': `Bearer ${process.env.BS_TESTOPS_JWT}`, 'Content-Type': 'application/json', 'X-BSTACK-TESTOPS': 'true' - } + } }; try { const eventsUuids = data.map(eventData => `${eventData.event_type}:${eventData.test_run ? eventData.test_run.uuid : (eventData.hook_run ? eventData.hook_run.uuid : null)}`).join(', '); - exports.debugOnConsole(`[Request Batch Send] for events:uuids ${eventsUuids}`); + exports.debugOnConsole(`[Request Batch Send] for events:uuids ${eventsUuids}`); const response = await nodeRequest('POST',eventUrl,data,config); exports.debugOnConsole(`[Request Batch Response] for events:uuids ${eventsUuids}`); if(response.data.error) { @@ -522,7 +538,7 @@ exports.uploadEventData = async (eventData, run=0) => { }[eventData.event_type]; if(run === 0 && process.env.BS_TESTOPS_JWT != "null") exports.pending_test_uploads.count += 1; - + if (process.env.BS_TESTOPS_BUILD_COMPLETED === "true") { if(process.env.BS_TESTOPS_JWT == "null") { exports.debug(`EXCEPTION IN ${log_tag} REQUEST TO TEST OBSERVABILITY : missing authentication token`); @@ -533,7 +549,7 @@ exports.uploadEventData = async (eventData, run=0) => { }; } else { let data = eventData, event_api_url = 'api/v1/event'; - + exports.requestQueueHandler.start(); const { shouldProceed, proceedWithData, proceedWithUrl } = exports.requestQueueHandler.add(eventData); exports.debugOnConsole(`[Request Queue] ${eventData.event_type} with uuid ${eventData.test_run ? eventData.test_run.uuid : (eventData.hook_run ? eventData.hook_run.uuid : null)} is added`) @@ -547,11 +563,11 @@ exports.uploadEventData = async (eventData, run=0) => { const config = { headers: { 'Authorization': `Bearer ${process.env.BS_TESTOPS_JWT}`, - 'Content-Type': 'application/json', + 'Content-Type': 'application/json;charset=utf-8', 'X-BSTACK-TESTOPS': 'true' } }; - + try { const eventsUuids = data.map(eventData => `${eventData.event_type}:${eventData.test_run ? eventData.test_run.uuid : (eventData.hook_run ? eventData.hook_run.uuid : null)}`).join(', '); exports.debugOnConsole(`[Request Send] for events:uuids ${eventsUuids}`); @@ -613,7 +629,7 @@ exports.setTestObservabilityFlags = (bsConfig) => { isTestObservabilitySession = false; exports.debug(`EXCEPTION while parsing testObservability capability with error ${e}`, true, e); } - + /* browserstackAutomation */ let isBrowserstackInfra = true; try { @@ -624,7 +640,7 @@ exports.setTestObservabilityFlags = (bsConfig) => { isBrowserstackInfra = true; exports.debug(`EXCEPTION while parsing browserstackAutomation capability with error ${e}`, true, e); } - + if(isTestObservabilitySession) logger.warn("testObservability is set to true. Other test reporters you are using will be automatically disabled. Learn more at browserstack.com/docs/test-observability/overview/what-is-test-observability"); process.env.BROWSERSTACK_TEST_OBSERVABILITY = isTestObservabilitySession; @@ -664,7 +680,7 @@ exports.stopBuildUpstream = async () => { 'X-BSTACK-TESTOPS': 'true' } }; - + try { const response = await nodeRequest('PUT',`api/v1/builds/${process.env.BS_TESTOPS_BUILD_HASHED_ID}/stop`,data,config); if(response.data && response.data.error) { diff --git a/bin/testObservability/helper/requestQueueHandler.js b/bin/testObservability/helper/requestQueueHandler.js index 172384f2..424e1a20 100644 --- a/bin/testObservability/helper/requestQueueHandler.js +++ b/bin/testObservability/helper/requestQueueHandler.js @@ -39,7 +39,7 @@ class RequestQueueHandler { this.queue.splice(0,BATCH_SIZE); this.resetEventBatchPolling(); } - + return { shouldProceed: shouldProceed, proceedWithData: data, @@ -54,7 +54,7 @@ class RequestQueueHandler { shutdownSync = () => { this.removeEventBatchPolling('REMOVING'); - + fs.writeFileSync(path.join(__dirname, PENDING_QUEUES_FILE), JSON.stringify(this.queue)); this.queue = []; cp.spawnSync('node', [path.join(__dirname, 'cleanupQueueSync.js'), path.join(__dirname, PENDING_QUEUES_FILE)], {stdio: 'inherit'}); diff --git a/bin/testObservability/plugin/ipcServer.js b/bin/testObservability/plugin/ipcServer.js index 16fc7236..62e84394 100644 --- a/bin/testObservability/plugin/ipcServer.js +++ b/bin/testObservability/plugin/ipcServer.js @@ -13,17 +13,17 @@ exports.startIPCServer = (subscribeServerEvents, unsubscribeServerEvents) => { ipc.config.silent = true; ipc.serve(() => { - + ipc.server.on('socket.disconnected', (socket, destroyedSocketID) => { ipc.log(`client ${destroyedSocketID} has disconnected!`); }); - + ipc.server.on('destroy', () => { ipc.log('server destroyed'); }); - + subscribeServerEvents(ipc.server); - + process.on('exit', () => { unsubscribeServerEvents(ipc.server); ipc.server.stop(); @@ -31,8 +31,8 @@ exports.startIPCServer = (subscribeServerEvents, unsubscribeServerEvents) => { // on exit handler will block the process requestQueueHandler.shutdownSync(); }); - + }); - + ipc.server.start(); }; diff --git a/bin/testObservability/reporter/index.js b/bin/testObservability/reporter/index.js index d8c4cb9c..396ad0e3 100644 --- a/bin/testObservability/reporter/index.js +++ b/bin/testObservability/reporter/index.js @@ -169,7 +169,6 @@ class MyReporter { }) .on(EVENT_TEST_BEGIN, async (test) => { - debugOnConsole(`[MOCHA EVENT] EVENT_TEST_BEGIN`); debugOnConsole(`[MOCHA EVENT] EVENT_TEST_BEGIN for uuid: ${test.testAnalyticsId}`); if (this.runStatusMarkedHash[test.testAnalyticsId]) return; if(this.testObservability == true) { @@ -178,7 +177,6 @@ class MyReporter { }) .on(EVENT_TEST_END, async (test) => { - debugOnConsole(`[MOCHA EVENT] EVENT_TEST_END`); debugOnConsole(`[MOCHA EVENT] EVENT_TEST_BEGIN for uuid: ${test.testAnalyticsId}`); if (this.runStatusMarkedHash[test.testAnalyticsId]) return; if(this.testObservability == true) { @@ -188,7 +186,7 @@ class MyReporter { } } }) - + .once(EVENT_RUN_END, async () => { try { debugOnConsole(`[MOCHA EVENT] EVENT_RUN_END`); @@ -307,9 +305,9 @@ class MyReporter { let gitConfigPath = process.env.OBSERVABILITY_GIT_CONFIG_PATH ? process.env.OBSERVABILITY_GIT_CONFIG_PATH.toString() : (rootParentFile ? findGitConfig(rootParentFile) : null); if(!isBrowserstackInfra()) gitConfigPath = process.env.OBSERVABILITY_GIT_CONFIG_PATH_LOCAL ? process.env.OBSERVABILITY_GIT_CONFIG_PATH_LOCAL.toString() : null; const prefixedTestPath = rootParentFile ? this._paths.prefixTestPath(rootParentFile) : 'File path could not be found'; - + const fileSeparator = getFileSeparatorData(); - + let testData = { 'framework': 'Cypress', 'uuid': (eventType.includes("Test") ? test.testAnalyticsId : test.hookAnalyticsId) || uuidv4(), @@ -417,14 +415,14 @@ class MyReporter { } if(eventType == "HookRunFinished") delete testData.started_at; - + if(eventType.match(/HookRun/)) { testData['hook_type'] = HOOK_TYPES_MAP[testData['hook_type']]; uploadData['hook_run'] = testData; } else { uploadData['test_run'] = testData; } - + if(eventType == 'HookRunFinished' && testData['hook_type'] == 'BEFORE_ALL') { uploadData.cypressSteps = JSON.parse(JSON.stringify(this.currentTestSteps)); this.beforeHooks.push(uploadData); @@ -610,7 +608,7 @@ class MyReporter { if(isCommandPresent) { return; } - + const currentStepObj = { id: command.attributes.id, text: 'cy.' + command.attributes.name + '(' + this.getFormattedArgs(command.attributes.args) + ')', @@ -660,7 +658,7 @@ class MyReporter { } } else if(type == 'COMMAND_RETRY') { if(!command.id) return; - + let isRetryStepFound = false; /* Parse steps array in reverse and update the last step with common chainerId */ for(let idx=this.currentTestSteps.length-1; idx>=0; idx--) { @@ -754,7 +752,7 @@ class MyReporter { }); } } - + return null } } diff --git a/package.json b/package.json index 3406e1cf..69bf3859 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,12 @@ "dependencies": { "archiver": "5.3.0", "async": "3.2.3", + "axios": "^1.7.7", + "axios-retry": "^3.5.0", "browserstack-local": "1.5.4", "chalk": "4.1.2", - "cli-progress": "3.10.0", + "cli-progress": "^3.10.0", + "form-data": "^4.0.0", "fs-extra": "8.1.0", "getmac": "5.20.0", "git-last-commit": "^1.0.1", @@ -26,8 +29,6 @@ "mocha": "^10.2.0", "mkdirp": "1.0.4", "node-ipc": "9.1.1", - "request": "2.88.2", - "requestretry": "7.1.0", "table": "5.4.6", "update-notifier": "7.0.0", "uuid": "8.3.2", diff --git a/test/unit/bin/commands/info.js b/test/unit/bin/commands/info.js index f486be44..f928084f 100644 --- a/test/unit/bin/commands/info.js +++ b/test/unit/bin/commands/info.js @@ -1,7 +1,7 @@ +const { default: axios } = require("axios"); const chai = require("chai"), chaiAsPromised = require("chai-as-promised"), - sinon = require('sinon'), - request = require('request'); + sinon = require('sinon'); const Constants = require("../../../../bin/helpers/constants"), logger = require("../../../../bin/helpers/logger").winstonLogger, @@ -49,7 +49,7 @@ describe("buildInfo", () => { let messageType = Constants.messageTypes.INFO; let errorCode = "api_deprecated"; - let requestStub = sandbox.stub(request, "get").yields(null, { statusCode: 299 }, null); + let axiosStub = sandbox.stub(axios, "get").resolves({ status: 299 }); const info = proxyquire('../../../../bin/commands/info', { '../helpers/utils': { @@ -68,7 +68,6 @@ describe("buildInfo", () => { '../helpers/getInitialDetails': { getInitialDetails: getInitialDetailsStub, }, - request: {get: requestStub}, }); validateBstackJsonStub.returns(Promise.resolve(bsConfig)); @@ -76,7 +75,7 @@ describe("buildInfo", () => { return info(args, rawArgs) .then(function (_bsConfig) { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getConfigPathStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnce(getInitialDetailsStub); @@ -91,9 +90,9 @@ describe("buildInfo", () => { let messageType = Constants.messageTypes.INFO; let errorCode = "api_deprecated"; - let requestStub = sandbox - .stub(request, "get") - .yields(null, { statusCode: 299 }, JSON.stringify(body)); + let axiosStub = sandbox + .stub(axios, "get") + .resolves({ status: 299, data: body }); const info = proxyquire('../../../../bin/commands/info', { '../helpers/utils': { @@ -112,7 +111,6 @@ describe("buildInfo", () => { '../helpers/getInitialDetails': { getInitialDetails: getInitialDetailsStub, }, - request: {get: requestStub}, }); getInitialDetailsStub.returns(Promise.resolve({})); @@ -120,7 +118,7 @@ describe("buildInfo", () => { return info(args, rawArgs) .then(function (_bsConfig) { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnce(getConfigPathStub); sinon.assert.calledOnce(getInitialDetailsStub); @@ -160,9 +158,9 @@ describe("buildInfo", () => { let messageType = Constants.messageTypes.ERROR; let errorCode = "api_failed_build_info"; - let requestStub = sinon - .stub(request, "get") - .yields(null, { statusCode: 400 }, null); + let axiosStub = sinon + .stub(axios, "get") + .resolves({ status: 400 }); const info = proxyquire('../../../../bin/commands/info', { '../helpers/utils': { @@ -181,7 +179,6 @@ describe("buildInfo", () => { '../helpers/getInitialDetails': { getInitialDetails: getInitialDetailsStub, }, - request: {get: requestStub}, }); validateBstackJsonStub.returns(Promise.resolve(bsConfig)); @@ -189,7 +186,7 @@ describe("buildInfo", () => { return info(args, rawArgs) .then(function (_bsConfig) { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnce(getConfigPathStub); sinon.assert.calledOnce(getInitialDetailsStub); @@ -210,9 +207,9 @@ describe("buildInfo", () => { let messageType = Constants.messageTypes.ERROR; let errorCode = "api_auth_failed"; - let requestStub = sinon - .stub(request, "get") - .yields(null, { statusCode: 401 }, JSON.stringify(body_with_message)); + let axiosStub = sinon + .stub(axios, "get") + .resolves({ status: 401, data: body_with_message }); const info = proxyquire('../../../../bin/commands/info', { '../helpers/utils': { @@ -231,14 +228,13 @@ describe("buildInfo", () => { '../helpers/getInitialDetails': { getInitialDetails: getInitialDetailsStub, }, - request: {get: requestStub}, }); validateBstackJsonStub.returns(Promise.resolve(bsConfig)); getInitialDetailsStub.returns(Promise.resolve({})); return info(args, rawArgs) .then(function (_bsConfig) { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnce(getConfigPathStub); sinon.assert.calledOnce(getInitialDetailsStub); @@ -254,9 +250,9 @@ describe("buildInfo", () => { let messageType = Constants.messageTypes.ERROR; let errorCode = "api_failed_build_info"; - let requestStub = sinon - .stub(request, "get") - .yields(null, { statusCode: 402 }, JSON.stringify(body)); + let axiosStub = sinon + .stub(axios, "get") + .resolves({ status: 402, data: body }); const info = proxyquire('../../../../bin/commands/info', { '../helpers/utils': { @@ -275,7 +271,6 @@ describe("buildInfo", () => { '../helpers/getInitialDetails': { getInitialDetails: getInitialDetailsStub, }, - request: {get: requestStub}, }); validateBstackJsonStub.returns(Promise.resolve(bsConfig)); @@ -283,7 +278,7 @@ describe("buildInfo", () => { return info(args, rawArgs) .then(function (_bsConfig) { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnce(getConfigPathStub); sinon.assert.calledOnce(getInitialDetailsStub); @@ -325,7 +320,7 @@ describe("buildInfo", () => { let messageType = Constants.messageTypes.SUCCESS; let errorCode = null; - let requestStub = sandbox.stub(request, "get").yields(null, { statusCode: 200 }, JSON.stringify(body)); + let axiosStub = sandbox.stub(axios, "get").resolves({ status: 200, data: body }); const info = proxyquire('../../../../bin/commands/info', { '../helpers/utils': { @@ -343,7 +338,6 @@ describe("buildInfo", () => { '../helpers/getInitialDetails': { getInitialDetails: getInitialDetailsStub, }, - request: {get: requestStub}, }); validateBstackJsonStub.returns(Promise.resolve(bsConfig)); @@ -351,7 +345,7 @@ describe("buildInfo", () => { return info(args, rawArgs) .then(function (_bsConfig) { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnce(getConfigPathStub); sinon.assert.calledOnce(getInitialDetailsStub); diff --git a/test/unit/bin/commands/stop.js b/test/unit/bin/commands/stop.js index b8116053..6ee61753 100644 --- a/test/unit/bin/commands/stop.js +++ b/test/unit/bin/commands/stop.js @@ -1,7 +1,6 @@ const chai = require("chai"), chaiAsPromised = require("chai-as-promised"), - sinon = require("sinon"), - request = require("request"); + sinon = require("sinon"); const Constants = require("../../../../bin/helpers/constants"), logger = require("../../../../bin/helpers/logger").winstonLogger, diff --git a/test/unit/bin/helpers/atsHelper.js b/test/unit/bin/helpers/atsHelper.js index a8abae97..eda17dbf 100644 --- a/test/unit/bin/helpers/atsHelper.js +++ b/test/unit/bin/helpers/atsHelper.js @@ -2,12 +2,12 @@ const { expect } = require("chai"); const chai = require("chai"), chaiAsPromised = require("chai-as-promised"), sinon = require('sinon'), - request = require('request'); + request = require('axios'); const logger = require("../../../../bin/helpers/logger").winstonLogger, testObjects = require("../../support/fixtures/testObjects"), utils = require('../../../../bin/helpers/utils'), - atsHelper = require('../../../../bin/helpers/atsHelper'); + atsHelper = require('../../../../bin/helpers/atsHelper.js'); chai.use(chaiAsPromised); logger.transports["console.info"].silent = true; diff --git a/test/unit/bin/helpers/build.js b/test/unit/bin/helpers/build.js index 95208b8a..aafac700 100644 --- a/test/unit/bin/helpers/build.js +++ b/test/unit/bin/helpers/build.js @@ -1,7 +1,7 @@ +const { default: axios } = require("axios"); const chai = require("chai"), chaiAsPromised = require("chai-as-promised"), - sinon = require("sinon"), - request = require("request"); + sinon = require("sinon"); const Constants = require("../../../../bin/helpers/constants"), logger = require("../../../../bin/helpers/logger").winstonLogger, @@ -31,9 +31,9 @@ describe("build", () => { }); it("reject with error", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(new Error("random error"), null, null); + let axiosStub = sandbox + .stub(axios, "post") + .rejects(new Error("random error")); const build = proxyquire("../../../../bin/helpers/build", { "../helpers/utils": { @@ -43,7 +43,6 @@ describe("build", () => { "../helpers/capabilityHelper": { caps: capsStub, }, - request: { post: requestStub }, }); return build.createBuild(bsConfig, "random_zip_file") @@ -51,7 +50,7 @@ describe("build", () => { chai.assert.fail("Promise error"); }) .catch((error) => { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); chai.assert.equal(error.message, "random error"); }); @@ -59,9 +58,9 @@ describe("build", () => { describe("handle API deprecated", () => { it("build is null", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 299 }, null); + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 299 }); const build = proxyquire("../../../../bin/helpers/build", { "../helpers/utils": { @@ -71,7 +70,6 @@ describe("build", () => { "../helpers/capabilityHelper": { caps: capsStub, }, - request: { post: requestStub }, }); return build @@ -80,7 +78,7 @@ describe("build", () => { chai.assert.fail("Promise error"); }) .catch((error) => { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); chai.assert.equal(error, Constants.userMessages.API_DEPRECATED); }); @@ -88,9 +86,9 @@ describe("build", () => { it("build is not null", () => { let build_message = "random message"; - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 299 }, JSON.stringify({ message: build_message })); + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 299, data: { message: build_message} }); const build = proxyquire("../../../../bin/helpers/build", { "../helpers/utils": { @@ -99,13 +97,12 @@ describe("build", () => { "../helpers/capabilityHelper": { caps: capsStub, }, - request: { post: requestStub }, }); return build .createBuild(bsConfig, "random_zip_file") .then(function (data) { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); chai.assert.equal(data, "random message"); }) @@ -117,9 +114,9 @@ describe("build", () => { describe("handle statusCode != 201", () => { it("build is null", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 400 }, null); + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 400 }); const build = proxyquire("../../../../bin/helpers/build", { "../helpers/utils": { @@ -129,7 +126,6 @@ describe("build", () => { "../helpers/capabilityHelper": { caps: capsStub, }, - request: { post: requestStub }, }); return build @@ -138,7 +134,7 @@ describe("build", () => { chai.assert.fail("Promise error"); }) .catch((error) => { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); chai.assert.equal(error, Constants.userMessages.BUILD_FAILED); }); @@ -146,12 +142,10 @@ describe("build", () => { it("build is not null", () => { let build_message = "random message"; - let requestStub = sandbox - .stub(request, "post") - .yields( - null, - { statusCode: 401 }, - JSON.stringify({ message: build_message }) + let axiosStub = sandbox + .stub(axios, "post") + .resolves( + { status: 401, data: { message: build_message }} ); const build = proxyquire("../../../../bin/helpers/build", { @@ -162,7 +156,6 @@ describe("build", () => { "../helpers/capabilityHelper": { caps: capsStub, }, - request: { post: requestStub }, }); return build @@ -171,7 +164,7 @@ describe("build", () => { chai.assert.fail("Promise error"); }) .catch((error) => { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); chai.assert.equal(error, `${Constants.userMessages.BUILD_FAILED} Error: ${build_message}`); }); @@ -181,32 +174,32 @@ describe("build", () => { it("build created successfuly", () => { let build_id = "random build id"; let build_message = "success" - let requestData = { message: build_message, build_id: build_id }; - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 201 }, JSON.stringify(requestData)); + let axiosData = { message: build_message, build_id: build_id }; + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 201, data: axiosData }); let dashboardUrl = "dashboard-url"; const build = proxyquire("../../../../bin/helpers/build", { "../helpers/utils": { getUserAgent: getUserAgentStub, + formatRequest, }, "../helpers/capabilityHelper": { caps: capsStub, }, "./config": { dashboardUrl: dashboardUrl, - }, - request: { post: requestStub }, + }, }); return build .createBuild(bsConfig, "random_zip_file") .then(function (data) { - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); - chai.assert.equal(data, `${requestData}`); + chai.assert.equal(data, `${axiosData}`); }) .catch((error) => { chai.assert.isNotOk(error, "Promise error"); diff --git a/test/unit/bin/helpers/checkUploaded.js b/test/unit/bin/helpers/checkUploaded.js index 6d98a5ab..503ece9a 100644 --- a/test/unit/bin/helpers/checkUploaded.js +++ b/test/unit/bin/helpers/checkUploaded.js @@ -1,8 +1,8 @@ 'use strict'; +const { default: axios } = require("axios"); const chai = require("chai"), chaiAsPromised = require("chai-as-promised"), - sinon = require("sinon"), - request = require("request"); + sinon = require("sinon"); const logger = require("../../../../bin/helpers/logger").winstonLogger, testObjects = require("../../support/fixtures/testObjects"); @@ -37,14 +37,14 @@ describe("checkUploaded", () => { } }); - it("resolves with zipUrlPresent false due to request error", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(new Error("random error"), null, null); + it("resolves with zipUrlPresent false due to axios error", () => { + let axiosStub = sandbox + .stub(axios, "post") + .resolves(new Error("random error")); const checkUploaded = rewire("../../../../bin/helpers/checkUploaded"); checkUploaded.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, checkSpecsMd5: checkSpecsMd5Stub }); let checkUploadedMd5rewire = checkUploaded.__get__('checkUploadedMd5'); @@ -53,7 +53,7 @@ describe("checkUploaded", () => { chai.assert.equal(data.zip_md5sum, 'random_md5sum'); chai.assert.equal(data.zipUrlPresent, false); chai.assert.equal(data.packageUrlPresent, false); - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(checkSpecsMd5Stub); }) .catch((_error) => { @@ -62,14 +62,14 @@ describe("checkUploaded", () => { }); it("resolves with zipUrlPresent false and packageUrlPresent false due to checkSpecsMd5 error", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(new Error("random error"), null, null); + let axiosStub = sandbox + .stub(axios, "post") + .resolves(new Error("random error")); let checkSpecsMd5ErrorStub = sandbox.stub().returns(Promise.reject({message: "test error", stack: "test error stack"})); const checkUploaded = rewire("../../../../bin/helpers/checkUploaded"); checkUploaded.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, checkSpecsMd5: checkSpecsMd5ErrorStub }); let checkUploadedMd5rewire = checkUploaded.__get__('checkUploadedMd5'); @@ -78,7 +78,7 @@ describe("checkUploaded", () => { chai.assert.equal(data.zipUrlPresent, false); chai.assert.equal(data.packageUrlPresent, false); chai.assert.equal(data.error, "test error stack"); - sinon.assert.notCalled(requestStub); + sinon.assert.notCalled(axiosStub); sinon.assert.calledOnce(checkSpecsMd5ErrorStub); }) .catch((_error) => { @@ -87,13 +87,13 @@ describe("checkUploaded", () => { }); it("resolves with zipUrlPresent false and packageUrlPresent false due to parsing error", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 200 }, '{"zipUrl":"bs://random_hashid}'); + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 200 , data: '{"zipUrl":"bs://random_hashid}'}); const checkUploaded = rewire("../../../../bin/helpers/checkUploaded"); checkUploaded.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, checkSpecsMd5: checkSpecsMd5Stub }); let checkUploadedMd5rewire = checkUploaded.__get__('checkUploadedMd5'); @@ -102,7 +102,7 @@ describe("checkUploaded", () => { chai.assert.equal(data.zipUrlPresent, false); chai.assert.equal(data.packageUrlPresent, false); chai.assert.equal(data.zip_md5sum, "random_md5sum"); - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(checkSpecsMd5Stub); }) .catch((_error) => { @@ -111,20 +111,20 @@ describe("checkUploaded", () => { }); it("resolves with zipUrlPresent true and zip url", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 200 }, '{"zipUrl":"bs://random_hashid"}'); + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 200 , data: {"zipUrl":"bs://random_hashid"} }); const checkUploaded = rewire("../../../../bin/helpers/checkUploaded"); checkUploaded.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, checkSpecsMd5: checkSpecsMd5Stub }); let checkUploadedMd5rewire = checkUploaded.__get__('checkUploadedMd5'); return checkUploadedMd5rewire(bsConfig, {}, instrumentBlocks) .then((data) => { chai.assert.deepEqual(data, { zip_md5sum: 'random_md5sum', zipUrlPresent: true, packageUrlPresent: false, zipUrl: 'bs://random_hashid' }) - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(checkSpecsMd5Stub); }) .catch((_error) => { @@ -133,13 +133,13 @@ describe("checkUploaded", () => { }); it("resolves with zipUrlPresent true, packageUrlPresent true, zip url, and packge url", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 200 }, '{"zipUrl":"bs://random_hashid", "npmPackageUrl":"bs://random_hashid2"}'); + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 200 , data: {"zipUrl":"bs://random_hashid", "npmPackageUrl":"bs://random_hashid2"}}); const checkUploaded = rewire("../../../../bin/helpers/checkUploaded"); checkUploaded.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, checkSpecsMd5: checkSpecsMd5Stub, checkPackageMd5: checkPackageMd5Stub }); @@ -148,7 +148,7 @@ describe("checkUploaded", () => { return checkUploadedMd5rewire(bsConfig, {}, instrumentBlocks) .then((data) => { chai.assert.deepEqual(data, { zip_md5sum: 'random_md5sum', npm_package_md5sum: 'random_md5sum', zipUrlPresent: true, packageUrlPresent: true, zipUrl: 'bs://random_hashid', npmPackageUrl: 'bs://random_hashid2' }) - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(checkSpecsMd5Stub); }) .catch((_error) => { @@ -157,13 +157,13 @@ describe("checkUploaded", () => { }); it("resolves with zipUrlPresent false as not found in db", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 404 }, '{"message":"zip_url for md5sum random_md5sum not found."}'); + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 404 , data: {"message":"zip_url for md5sum random_md5sum not found."}}); const checkUploaded = rewire("../../../../bin/helpers/checkUploaded"); checkUploaded.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, checkSpecsMd5: checkSpecsMd5Stub, checkPackageMd5: checkPackageMd5Stub }); @@ -172,7 +172,7 @@ describe("checkUploaded", () => { return checkUploadedMd5rewire(bsConfig, {}, instrumentBlocks) .then((data) => { chai.assert.deepEqual(data, { zip_md5sum: 'random_md5sum', zipUrlPresent: false, packageUrlPresent: false, }) - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(checkSpecsMd5Stub); }) .catch((_error) => { @@ -181,13 +181,13 @@ describe("checkUploaded", () => { }); it("resolves with zipUrlPresent and packageUrlPresent false if force-upload enabled and cache_dependencies disabled", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 404 }, '{"message":"zip_url for md5sum random_md5sum not found."}'); + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 404 , data: '{"message":"zip_url for md5sum random_md5sum not found."}'}); const checkUploaded = rewire("../../../../bin/helpers/checkUploaded"); checkUploaded.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, checkSpecsMd5: checkSpecsMd5Stub, checkPackageMd5: checkPackageMd5Stub }); @@ -196,7 +196,7 @@ describe("checkUploaded", () => { return checkUploadedMd5rewire(bsConfig, {"force-upload": true}, instrumentBlocks) .then((data) => { chai.assert.deepEqual(data, { zipUrlPresent: false, packageUrlPresent: false }) - sinon.assert.notCalled(requestStub); + sinon.assert.notCalled(axiosStub); sinon.assert.notCalled(checkSpecsMd5Stub); }) .catch((_error) => { @@ -205,13 +205,13 @@ describe("checkUploaded", () => { }); it("resolves with zipUrlPresent false and packageUrlPresent false if force-upload enabled and cache_dependencies enabled", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 200 }, '{"npmPackageUrl":"bs://random_hashid2"}'); + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 200 , data: '{"npmPackageUrl":"bs://random_hashid2"}'}); const checkUploaded = rewire("../../../../bin/helpers/checkUploaded"); checkUploaded.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, checkSpecsMd5: checkSpecsMd5Stub, checkPackageMd5: checkPackageMd5Stub }); @@ -220,23 +220,22 @@ describe("checkUploaded", () => { return checkUploadedMd5rewire(bsConfig, {"force-upload": true}, instrumentBlocks) .then((data) => { chai.assert.deepEqual(data, { zipUrlPresent: false, packageUrlPresent: false }) - sinon.assert.notCalled(requestStub); + sinon.assert.notCalled(axiosStub); sinon.assert.notCalled(checkSpecsMd5Stub); }) .catch((_error) => { - console.log(_error) chai.assert.fail("Promise error"); }); }); it("resolves with zipUrlPresent false and packageUrlPresent false if diabled from rails", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 200 }, '{"disableNpmSuiteCache": true, "disableTestSuiteCache": true }'); + let axiosStub = sandbox + .stub(axios, "post") + .resolves( { status: 200 , data: {"disableNpmSuiteCache": true, "disableTestSuiteCache": true }}); const checkUploaded = rewire("../../../../bin/helpers/checkUploaded"); checkUploaded.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, checkSpecsMd5: checkSpecsMd5Stub, checkPackageMd5: checkPackageMd5Stub }); @@ -245,7 +244,7 @@ describe("checkUploaded", () => { return checkUploadedMd5rewire(bsConfig, {}, instrumentBlocks) .then((data) => { chai.assert.deepEqual(data, { zipUrlPresent: false, packageUrlPresent: false }) - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(checkSpecsMd5Stub); }) .catch((_error) => { diff --git a/test/unit/bin/helpers/getInitialDetails.js b/test/unit/bin/helpers/getInitialDetails.js index b506e150..617a9f7f 100644 --- a/test/unit/bin/helpers/getInitialDetails.js +++ b/test/unit/bin/helpers/getInitialDetails.js @@ -1,8 +1,8 @@ +const { default: axios } = require("axios"); const { expect } = require("chai"); const chai = require("chai"), chaiAsPromised = require("chai-as-promised"), - sinon = require('sinon'), - request = require('request'); + sinon = require('sinon'); const Constants = require("../../../../bin/helpers/constants"), logger = require("../../../../bin/helpers/logger").winstonLogger, @@ -17,13 +17,13 @@ describe('#getInitialDetails', () => { let args = testObjects.buildInfoSampleArgs; let rawArgs = testObjects.buildInfoSampleRawArgs; let bsConfig = testObjects.sampleBsConfig; - let sendUsageReportStub = null, requestStub = null, formatRequestStub = null; + let sendUsageReportStub = null, axiosStub = null, formatRequestStub = null; let messageType = Constants.messageTypes.ERROR; let errorCode = 'get_initial_details_failed' beforeEach(() => { sendUsageReportStub = sinon.stub(utils, 'sendUsageReport'); - requestStub = sinon.stub(request, "get"); + axiosStub = sinon.stub(axios, "get"); formatRequestStub = sinon.stub(utils, "formatRequest"); }); @@ -33,8 +33,8 @@ describe('#getInitialDetails', () => { it('sends usage report if error occurred in getInitialDetails call', () => { let error_message = "error occurred"; - requestStub.yields(error_message, null, null); - formatRequestStub.returns({err: error_message, statusCode: null, body: null}) + axiosStub.resolves(error_message); + formatRequestStub.returns({err: error_message, status: null, body: null}) sendUsageReportStub.calledOnceWithExactly(bsConfig, args, error_message, messageType, errorCode, null, rawArgs); getInitialDetails(bsConfig, args, rawArgs).then((result) => { expect(result).to.eq({}); @@ -43,7 +43,7 @@ describe('#getInitialDetails', () => { it('resolves with data if getInitialDetails call is successful', () => { let body = {"user_id": 1234}; - requestStub.yields(null, { statusCode: 200 }, body); + axiosStub.resolves({ status: 200 , data: body}); formatRequestStub.notCalled; sendUsageReportStub.notCalled; getInitialDetails(bsConfig, args, rawArgs).then((result) => { @@ -53,7 +53,7 @@ describe('#getInitialDetails', () => { it('send usage report if error response from getInitialDetails call', () => { let body = {"error": 'user not found'}; - requestStub.yields(null, { statusCode: 422 }, body); + axiosStub.resolves({ status: 422 , data: body}); formatRequestStub.notCalled; sendUsageReportStub.calledOnceWithExactly(bsConfig, args, body["error"], messageType, errorCode, null, rawArgs); getInitialDetails(bsConfig, args, rawArgs).then((result) => { diff --git a/test/unit/bin/helpers/hashUtil.js b/test/unit/bin/helpers/hashUtil.js index 0b974c2f..0d8ab39c 100644 --- a/test/unit/bin/helpers/hashUtil.js +++ b/test/unit/bin/helpers/hashUtil.js @@ -121,7 +121,6 @@ describe("md5util", () => { sinon.assert.calledOnce(digestStub); }) .catch((error) => { - console.log("error is ",error) chai.assert.fail("Promise error"); }); }); diff --git a/test/unit/bin/helpers/reporterHTML.js b/test/unit/bin/helpers/reporterHTML.js index 56276464..b5489053 100644 --- a/test/unit/bin/helpers/reporterHTML.js +++ b/test/unit/bin/helpers/reporterHTML.js @@ -1,244 +1,231 @@ -const { expect } = require("chai"); -const chai = require("chai"), - chaiAsPromised = require("chai-as-promised"), +const { default: axios } = require('axios'); +const chai = require('chai'), + chaiAsPromised = require('chai-as-promised'), sinon = require('sinon'), rewire = require('rewire'); const fs = require('fs'), path = require('path'), - request = require('request'), - decompress = require('decompress'); Constants = require("../../../../bin/helpers/constants"), logger = require("../../../../bin/helpers/logger").winstonLogger, - testObjects = require("../../support/fixtures/testObjects"), - formatRequest = require("../../../../bin/helpers/utils").formatRequest; - -const proxyquire = require("proxyquire").noCallThru(); + testObjects = require("../../support/fixtures/testObjects") +const utils = require('../../../../bin/helpers/utils'); +const reporterHTML = require('../../../../bin/helpers/reporterHTML'); chai.use(chaiAsPromised); -logger.transports["console.info"].silent = true; +logger.transports['console.info'].silent = true; -describe("getReportResponse", () => { - var sandbox; - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - sinon.restore(); - }); +describe('reporterHTML', () => { + let sendUsageReportStub = null, + axiosStub = null, + formatRequestStub = null, + getUserAgentStub = null; - it("retrieve response from report url", () => { - let requestStub = sandbox.stub(request, "get").yields(null, { statusCode: 200 }, "abc"); - let reporterHTML = proxyquire('../../../../bin/helpers/reporterHTML', { - request: {get: requestStub} - }); - let rewireReporterHTML = rewire('../../../../bin/helpers/reporterHTML'); - let getReportResponse = rewireReporterHTML.__get__("getReportResponse"); - let unzipFileStub = sinon.stub(); - let writerStub = sinon.stub(fs, "createWriteStream"); - let unlinkSyncStub = sinon.stub(fs, "unlinkSync"); - let pathStub = sinon.stub(path, 'join'); - unzipFileStub.returns(true); - rewireReporterHTML.__set__('unzipFile', unzipFileStub); - pathStub.returns("abc/efg"); - writerStub.returns(true); - unlinkSyncStub.returns(true); - getReportResponse("abc", "efg", "url"); - sinon.assert.calledOnce(requestStub); - }) -}); - -describe("calls API to generate report", () => { - var sandbox,sendUsageReportStub,sendUsageReportStub; - let args = testObjects.generateReportInputArgs, - rawArgs = testObjects.generateReportInputRawArgs - buildId = 'buildId', - bsConfig = testObjects.sampleBsConfig; - beforeEach(() => { - sandbox = sinon.createSandbox(); - sendUsageReportStub = sandbox.stub().callsFake(function () { - return "end"; - }); - getUserAgentStub = sandbox.stub().returns("random user-agent"); + getUserAgentStub = sinon.stub(utils, 'getUserAgent'); + sendUsageReportStub = sinon.stub(utils, 'sendUsageReport'); + axiosStub = sinon.stub(axios, 'get'); + formatRequestStub = sinon.stub(utils, 'formatRequest'); }); afterEach(() => { - sandbox.restore(); sinon.restore(); }); - 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': { - sendUsageReport: sendUsageReportStub, - getUserAgent: getUserAgentStub - }, - request: {get: requestStub} + describe('getReportResponse', () => { + it('retrieve response from report url', () => { + axiosStub.resolves({ status: 200, data: 'abc' }); + let rewireReporterHTML = rewire('../../../../bin/helpers/reporterHTML'); + let getReportResponse = rewireReporterHTML.__get__('getReportResponse'); + let unzipFileStub = sinon.stub(); + let writerStub = sinon.stub(fs, 'createWriteStream'); + let unlinkSyncStub = sinon.stub(fs, 'unlinkSync'); + let pathStub = sinon.stub(path, 'join'); + unzipFileStub.returns(true); + rewireReporterHTML.__set__('unzipFile', unzipFileStub); + pathStub.returns('abc/efg'); + writerStub.returns(true); + unlinkSyncStub.returns(true); + getReportResponse('abc', 'efg', 'url'); + sinon.assert.calledOnce(axiosStub); + pathStub.restore(); }); - - reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}); - - sinon.assert.calledOnce(requestStub); - sinon.assert.calledOnce(getUserAgentStub); - sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, {}, rawArgs); }); - 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': { - sendUsageReport: sendUsageReportStub, - getUserAgent: getUserAgentStub - }, - request: {get: requestStub} + describe('calls API to generate report', () => { + let args = testObjects.generateReportInputArgs, + rawArgs = testObjects.generateReportInputRawArgs; + (buildId = 'buildId'), (bsConfig = testObjects.sampleBsConfig); + + it('is deprecated, i.e. 299, does not have build message', () => { + axiosStub.resolves({ status: 299 }); + let message = Constants.userMessages.API_DEPRECATED; + let messageType = Constants.messageTypes.INFO; + let errorCode = 'api_deprecated'; + + reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}).then(() => { + sinon.assert.calledOnce(axiosStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly( + sendUsageReportStub, + bsConfig, + args, + message, + messageType, + errorCode, + {}, + rawArgs + ); + }); }); - reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}); - - sinon.assert.calledOnce(requestStub); - sinon.assert.calledOnce(getUserAgentStub); - sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, {}, rawArgs); - }); - - context("non 200 response", () => { - it("422 status, build available but running, cannot generate report", () => { - let build = { message: 'The report cannot be generated as the build is running' }; - let body = JSON.stringify(build); - let requestStub = sandbox.stub(request, "get").yields(null, { statusCode: 422 }, body); + it('is deprecated, i.e. 299', () => { + let build = { + buildId: buildId, + message: 'API has been deprecated', + rows: [], + }; + axiosStub.resolves({ status: 299, data: build }); let message = build.message; - let messageType = Constants.messageTypes.ERROR; - let errorCode = 'api_failed_build_generate_report'; - - const reporterHTML = proxyquire('../../../../bin/helpers/reporterHTML', { - './utils': { - sendUsageReport: sendUsageReportStub, - getUserAgent: getUserAgentStub, - formatRequest - }, - request: {get: requestStub} + let messageType = Constants.messageTypes.INFO; + let errorCode = 'api_deprecated'; + + reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}).then(() => { + sinon.assert.calledOnce(axiosStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly( + sendUsageReportStub, + bsConfig, + args, + message, + messageType, + errorCode, + {}, + rawArgs + ); }); - - reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}); - - sinon.assert.calledOnce(requestStub); - sinon.assert.calledOnce(getUserAgentStub); - sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, {}, rawArgs); }); - 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': { - sendUsageReport: sendUsageReportStub, - getUserAgent: getUserAgentStub, - formatRequest - }, - request: {get: requestStub} + context('non 200 response', () => { + it('422 status, build available but running, cannot generate report', () => { + let build = { + message: 'The report cannot be generated as the build is running', + }; + axiosStub.resolves({ status: 422, data: build }); + let message = build.message; + let messageType = Constants.messageTypes.ERROR; + let errorCode = 'api_failed_build_generate_report'; + + reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}).then(() => { + sinon.assert.calledOnce(axiosStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly( + sendUsageReportStub, + bsConfig, + args, + message, + messageType, + errorCode, + {}, + rawArgs + ); + }); }); - reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}); - - sinon.assert.calledOnce(requestStub); - sinon.assert.calledOnce(getUserAgentStub); - sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, {}, rawArgs); - }); - - 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': { - sendUsageReport: sendUsageReportStub, - getUserAgent: getUserAgentStub - }, - request: {get: requestStub} + it('400 status, build available, cannot generate report', () => { + let build = { buildId: buildId, message: 'success', rows: [] }; + axiosStub.resolves({ status: 400, data: build }); + 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'; + + reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}).then(() => { + sinon.assert.calledOnce(axiosStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly( + sendUsageReportStub, + bsConfig, + args, + message, + messageType, + errorCode, + {}, + rawArgs + ); + }); }); - reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}); + it('user is unauthorized', () => { + let build = { buildId: buildId, message: 'Unauthorized', rows: [] }; + let body = build; + axiosStub.resolves({ status: 401, data: 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'; + + reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}).then(() => { + sinon.assert.calledOnce(axiosStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly( + sendUsageReportStub, + bsConfig, + args, + message, + messageType, + errorCode, + {}, + rawArgs + ); + }); + }); - sinon.assert.calledOnce(requestStub); - sinon.assert.calledOnce(getUserAgentStub); - sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, {}, rawArgs); + it('400 status, build not available, cannot generate report', () => { + axiosStub.resolves({ status: 400 }); + let message = Constants.userMessages.BUILD_GENERATE_REPORT_FAILED.replace('', buildId); + let messageType = Constants.messageTypes.ERROR; + let errorCode = 'api_failed_build_generate_report'; + + reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}).then(() => { + sinon.assert.calledOnce(axiosStub); + sinon.assert.calledOnce(getUserAgentStub); + sinon.assert.calledOnceWithExactly( + sendUsageReportStub, + bsConfig, + args, + message, + messageType, + errorCode, + {}, + rawArgs + ); + }); + }); }); - 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': { - sendUsageReport: sendUsageReportStub, - getUserAgent: getUserAgentStub, - formatRequest - }, - request: {get: requestStub} - }); + it('200 response code', () => { + let build = { buildId: buildId, message: 'success', rows: [] }; + axiosStub.resolves({ status: 200, data: 'abc' }); + let message = `Report for build: ${buildId} was successfully created.`; + let messageType = Constants.messageTypes.SUCCESS; + let errorCode = null; + let rewireReporterHTML = rewire('../../../../bin/helpers/reporterHTML'); + let generateCypressBuildReportStub = sinon.stub(); + generateCypressBuildReportStub.returns(true); + rewireReporterHTML.__set__('generateCypressBuildReport', generateCypressBuildReportStub); reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}); - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); - sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, {}, rawArgs); + sendUsageReportStub.calledOnceWithExactly(bsConfig, args, message, messageType, errorCode, {}, rawArgs); }); }); - - 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 }, "abc"); - let message = `Report for build: ${buildId} was successfully created.`; - let messageType = Constants.messageTypes.SUCCESS; - let errorCode = null; - let rewireReporterHTML = rewire('../../../../bin/helpers/reporterHTML'); - let generateCypressBuildReportStub = sinon.stub(); - generateCypressBuildReportStub.returns(true); - rewireReporterHTML.__set__('generateCypressBuildReport', generateCypressBuildReportStub); - - const reporterHTML = proxyquire('../../../../bin/helpers/reporterHTML', { - './utils': { - sendUsageReport: sendUsageReportStub, - getUserAgent: getUserAgentStub - }, - request: {get: requestStub} - }); - - reporterHTML.reportGenerator(bsConfig, buildId, args, rawArgs, {}); - - sinon.assert.calledOnce(requestStub); - sinon.assert.calledOnce(getUserAgentStub); - sendUsageReportStub.calledOnceWithExactly(bsConfig, args, message, messageType, errorCode, {}, rawArgs); - }); }); describe("unzipFile", () => { diff --git a/test/unit/bin/helpers/sync/syncSpecsLogs.js b/test/unit/bin/helpers/sync/syncSpecsLogs.js index e19a10c2..5f5ac969 100644 --- a/test/unit/bin/helpers/sync/syncSpecsLogs.js +++ b/test/unit/bin/helpers/sync/syncSpecsLogs.js @@ -1,22 +1,22 @@ -"use strict"; -const chai = require("chai"), +'use strict'; +const chai = require('chai'), expect = chai.expect, - rewire = require("rewire"), - chaiAsPromised = require("chai-as-promised"), - chalk = require('chalk'), - request = require("request"); + rewire = require('rewire'), + chaiAsPromised = require('chai-as-promised'), + chalk = require('chalk'); -const sinon = require("sinon"); +const { default: axios } = require('axios'); +const sinon = require('sinon'); chai.use(chaiAsPromised); -var syncSpecsLogs = rewire("../../../../../bin/helpers/sync/syncSpecsLogs.js"); -var logger = require("../../../../../bin/helpers/logger").syncCliLogger; -var Constants = require("../../../../../bin/helpers/constants.js"); -var config = require("../../../../../bin/helpers/config.js"); -var utils = require("../../../../../bin/helpers/utils"); +var syncSpecsLogs = rewire('../../../../../bin/helpers/sync/syncSpecsLogs.js'); +var logger = require('../../../../../bin/helpers/logger').syncCliLogger; +var Constants = require('../../../../../bin/helpers/constants.js'); +var config = require('../../../../../bin/helpers/config.js'); +var utils = require('../../../../../bin/helpers/utils'); -describe("syncSpecsLogs", () => { +describe('syncSpecsLogs', () => { var sandbox; beforeEach(() => { @@ -29,115 +29,131 @@ describe("syncSpecsLogs", () => { utils.sendUsageReport.restore(); }); - context("getCombinationName", () => { - const get_path = syncSpecsLogs.__get__("getCombinationName");; + context('getCombinationName', () => { + const get_path = syncSpecsLogs.__get__('getCombinationName'); let spec = { - "os": "Windows", - "osVersion": "10", - "browser": "chrome", - "browserVersion": "86" - } - it("returns combination name", () => { + os: 'Windows', + osVersion: '10', + browser: 'chrome', + browserVersion: '86', + }; + it('returns combination name', () => { let expectedCombination = `Chrome 86 (Windows 10)`; expect(get_path(spec)).to.equal(expectedCombination); }); }); - context("getStatus", () => { - const getStatus = syncSpecsLogs.__get__("getStatus");; + context('getStatus', () => { + const getStatus = syncSpecsLogs.__get__('getStatus'); - it("returns return ✔ in green when status is passes", () => { - expect(getStatus("passed")).to.equal(chalk.green("✔")); + it('returns return ✔ in green when status is passes', () => { + expect(getStatus('passed')).to.equal(chalk.green('✔')); }); - it("returns return ✘ in red when status is failed", () => { - expect(getStatus("failed")).to.equal(chalk.red("✘")); + it('returns return ✘ in red when status is failed', () => { + expect(getStatus('failed')).to.equal(chalk.red('✘')); }); - it("returns return [status] in yellow when status is skipped or ignored (anything else from pass/fail)", () => { - expect(getStatus("skipped")).to.equal(chalk.blue("[skipped]")); - expect(getStatus("ignored")).to.equal(chalk.blue("[ignored]")); + it('returns return [status] in yellow when status is skipped or ignored (anything else from pass/fail)', () => { + expect(getStatus('skipped')).to.equal(chalk.blue('[skipped]')); + expect(getStatus('ignored')).to.equal(chalk.blue('[ignored]')); }); }); - context("printInitialLog", () => { - const printInitialLog = syncSpecsLogs.__get__("printInitialLog"); + context('printInitialLog', () => { + const printInitialLog = syncSpecsLogs.__get__('printInitialLog'); - it("should print inital logs for specs in sync", () => { - - printInitialLog() - - expect(syncSpecsLogs.__get__("n")).to.equal(Constants.syncCLI.INITIAL_DELAY_MULTIPLIER); - expect(syncSpecsLogs.__get__("startTime")).to.not.be.null; + it('should print inital logs for specs in sync', () => { + printInitialLog(); + expect(syncSpecsLogs.__get__('n')).to.equal(Constants.syncCLI.INITIAL_DELAY_MULTIPLIER); + expect(syncSpecsLogs.__get__('startTime')).to.not.be.null; }); }); - context("getOptions", () => { - const getOptions = syncSpecsLogs.__get__("getOptions"); - let auth = {username: "cypress", access_key: "abcd"} - let build_id = "build1" + context('getOptions', () => { + const getOptions = syncSpecsLogs.__get__('getOptions'); + let auth = { username: 'cypress', access_key: 'abcd' }; + let build_id = 'build1'; - it('should return proper request option for polling', () => { + it('should return proper axios option for polling', () => { let options = getOptions(auth, build_id); expect(options.url).to.equal(`${config.buildUrlV2}${build_id}`); expect(options.auth.user).to.equal(auth.username); expect(options.auth.password).to.equal(auth.access_key); - expect(options.headers["Content-Type"]).to.equal("application/json"); + expect(options.headers['Content-Type']).to.equal('application/json'); }); }); - context("addCustomErrorToPrint", () => { - const addCustomErrorToPrint = syncSpecsLogs.__get__("addCustomErrorToPrint"); + context('addCustomErrorToPrint', () => { + const addCustomErrorToPrint = syncSpecsLogs.__get__('addCustomErrorToPrint'); let specSummary = { - "buildError": null, - "specs": [], - "duration": null, - "parallels": null, - "cliDuration": null, - "customErrorsToPrint": [ - { id: "custom_error_1", type: "custom_errors_to_print", level: "warn", should_be_unique: true, message: "custom error message" } - ] - } - const uniqueError = { id: "custom_error_1", type: "custom_errors_to_print", level: "warn", should_be_unique: true, message: "custom error message" }; - const notUniqueError = { id: "custom_error_2", type: "custom_errors_to_print", level: "warn", should_be_unique: false, message: "custom error message" }; + buildError: null, + specs: [], + duration: null, + parallels: null, + cliDuration: null, + customErrorsToPrint: [ + { + id: 'custom_error_1', + type: 'custom_errors_to_print', + level: 'warn', + should_be_unique: true, + message: 'custom error message', + }, + ], + }; + const uniqueError = { + id: 'custom_error_1', + type: 'custom_errors_to_print', + level: 'warn', + should_be_unique: true, + message: 'custom error message', + }; + const notUniqueError = { + id: 'custom_error_2', + type: 'custom_errors_to_print', + level: 'warn', + should_be_unique: false, + message: 'custom error message', + }; it('should add a new Error if its meant to be unique and not added to the error list', () => { addCustomErrorToPrint(uniqueError); - expect(JSON.stringify(syncSpecsLogs.__get__("specSummary"))).to.equal(JSON.stringify(specSummary)); + expect(JSON.stringify(syncSpecsLogs.__get__('specSummary'))).to.equal(JSON.stringify(specSummary)); }); it('should not add a new Error if its meant to be unique and already added to the error list', () => { addCustomErrorToPrint(uniqueError); - expect(JSON.stringify(syncSpecsLogs.__get__("specSummary"))).to.equal(JSON.stringify(specSummary)); + expect(JSON.stringify(syncSpecsLogs.__get__('specSummary'))).to.equal(JSON.stringify(specSummary)); }); it('should add a new Error if its not meant to be unique and not added to the error list', () => { addCustomErrorToPrint(notUniqueError); specSummary.customErrorsToPrint.push(notUniqueError); - expect(JSON.stringify(syncSpecsLogs.__get__("specSummary"))).to.equal(JSON.stringify(specSummary)); + expect(JSON.stringify(syncSpecsLogs.__get__('specSummary'))).to.equal(JSON.stringify(specSummary)); }); it('should not add a new Error if its not meant to not be unique and already added to the error list', () => { addCustomErrorToPrint(notUniqueError); specSummary.customErrorsToPrint.push(notUniqueError); - expect(JSON.stringify(syncSpecsLogs.__get__("specSummary"))).to.equal(JSON.stringify(specSummary)); + expect(JSON.stringify(syncSpecsLogs.__get__('specSummary'))).to.equal(JSON.stringify(specSummary)); }); }); - context("getTableConfig", () => { - const getTableConfig = syncSpecsLogs.__get__("getTableConfig"); + context('getTableConfig', () => { + const getTableConfig = syncSpecsLogs.__get__('getTableConfig'); it('should return proper table config option for spec table', () => { var getBorderConfigStub = sandbox.stub(); syncSpecsLogs.__set__('getBorderConfig', getBorderConfigStub); - let options = getTableConfig((process.stdout.columns) * 0.9); - expect(options.columnDefault.width).to.equal(Math.floor(((process.stdout.columns) * 0.9) * 0.2)); + let options = getTableConfig(process.stdout.columns * 0.9); + expect(options.columnDefault.width).to.equal(Math.floor(process.stdout.columns * 0.9 * 0.2)); expect(options.columns[1].alignment).to.equal('center'); expect(options.columns[2].alignment).to.equal('left'); - expect(options.columns[1].width).to.equal(Math.ceil(((process.stdout.columns) * 0.9) * 0.01)); - expect(options.columns[2].width).to.equal(Math.floor(((process.stdout.columns) * 0.9) * 0.75)); + expect(options.columns[1].width).to.equal(Math.ceil(process.stdout.columns * 0.9 * 0.01)); + expect(options.columns[2].width).to.equal(Math.floor(process.stdout.columns * 0.9 * 0.75)); expect(options.columnCount).to.equal(3); expect(getBorderConfigStub.calledOnce).to.be.true; }); @@ -157,46 +173,54 @@ describe("syncSpecsLogs", () => { }); }); - context("getBorderConfig", () => { - const getBorderConfig = syncSpecsLogs.__get__("getBorderConfig"); + context('getBorderConfig', () => { + const getBorderConfig = syncSpecsLogs.__get__('getBorderConfig'); it('should return proper border option for spec table', () => { let options = getBorderConfig(); - expect(options.topBody).to.equal(""); - expect(options.bottomBody).to.equal(""); + expect(options.topBody).to.equal(''); + expect(options.bottomBody).to.equal(''); }); }); - context("writeToTable", () => { - const writeToTable = syncSpecsLogs.__get__("writeToTable"); + context('writeToTable', () => { + const writeToTable = syncSpecsLogs.__get__('writeToTable'); it('should print spec details to the table', () => { const stream = sandbox.stub(); stream.write = sandbox.stub(); syncSpecsLogs.__set__('stream', stream); - let combination = "Windows 10", path = "path", status = "passed", statusMark = "passed"; + let combination = 'Windows 10', + path = 'path', + status = 'passed', + statusMark = 'passed'; writeToTable(combination, path, status, statusMark); - sinon.assert.calledOnceWithExactly(stream.write, [combination , ":", `${path} ${statusMark} [${status}]`]); + sinon.assert.calledOnceWithExactly(stream.write, [combination, ':', `${path} ${statusMark} [${status}]`]); }); }); - context("addSpecToSummary", () => { - const addSpecToSummary = syncSpecsLogs.__get__("addSpecToSummary"); + context('addSpecToSummary', () => { + const addSpecToSummary = syncSpecsLogs.__get__('addSpecToSummary'); it('should add spec details to specSummary', () => { - let specSummary = { specs: [] } + let specSummary = { specs: [] }; syncSpecsLogs.__set__('specSummary', specSummary); - let specName = "spec", status = "status", combination = "combo", session_id = "id"; + let specName = 'spec', + status = 'status', + combination = 'combo', + session_id = 'id'; addSpecToSummary(specName, status, combination, session_id); - expect(specSummary.specs).deep.to.equal([{"specName": specName, "status": status, "combination": combination, "sessionId": session_id}]) + expect(specSummary.specs).deep.to.equal([ + { specName: specName, status: status, combination: combination, sessionId: session_id }, + ]); }); }); - context("printSpecData", () => { - const printSpecData = syncSpecsLogs.__get__("printSpecData"); + context('printSpecData', () => { + const printSpecData = syncSpecsLogs.__get__('printSpecData'); it('Should print combination and status to the spec table and add spec details to spec array', () => { - let data = { spec: { status: "passed" }, path: "path", session_id: "id" } + let data = { spec: { status: 'passed' }, path: 'path', session_id: 'id' }; var getCombinationName = sandbox.stub(); syncSpecsLogs.__set__('getCombinationName', getCombinationName); var getStatus = sandbox.stub(); @@ -206,76 +230,89 @@ describe("syncSpecsLogs", () => { var addSpecToSummary = sandbox.stub(); syncSpecsLogs.__set__('addSpecToSummary', addSpecToSummary); - printSpecData(data); - sinon.assert.calledOnceWithExactly(getCombinationName, data["spec"]); - sinon.assert.calledOnceWithExactly(getStatus, data["spec"]["status"]); + sinon.assert.calledOnceWithExactly(getCombinationName, data['spec']); + sinon.assert.calledOnceWithExactly(getStatus, data['spec']['status']); sinon.assert.calledOnce(writeToTable); sinon.assert.calledOnce(addSpecToSummary); }); }); - - context("showSpecsStatus", () => { - const showSpecsStatus = syncSpecsLogs.__get__("showSpecsStatus"); - const buildCreatedStatusCode = 202 - const buildRunningStatusCode = 204 - const buildCompletedStatusCode = 200 + context('showSpecsStatus', () => { + const showSpecsStatus = syncSpecsLogs.__get__('showSpecsStatus'); + const buildCreatedstatus = 202; + const buildRunningstatus = 204; + const buildCompletedstatus = 200; it('should not print initial log for running specs when it is the 1st polling response', () => { - let data = JSON.stringify({ "specData": ["created"], "buildData": {"duration": "NA", "parallels": "NA"}}) + let data = { specData: ['created'], buildData: { duration: 'NA', parallels: 'NA' } }; var printInitialLog = sandbox.stub(); syncSpecsLogs.__set__('printInitialLog', printInitialLog); - showSpecsStatus(data, buildCreatedStatusCode); + showSpecsStatus(data, buildCreatedstatus); expect(printInitialLog.calledOnce).to.be.false; }); it('should print spec details when spec related data is sent in polling response', () => { - let specResult = JSON.stringify({"path": "path"}) - let data = JSON.stringify({ "specData": [specResult], "buildData": {"duration": "NA", "parallels": "NA"}}) + let specResult = JSON.stringify({ path: 'path' }); + let data = { specData: [specResult], buildData: { duration: 'NA', parallels: 'NA' } }; var printSpecData = sandbox.stub(); syncSpecsLogs.__set__('printSpecData', printSpecData); - showSpecsStatus(data, buildRunningStatusCode); + showSpecsStatus(data, buildRunningstatus); expect(printSpecData.calledOnce).to.be.true; }); it('should print initial and spec details when spec related data is sent in polling response', () => { - let specResult = JSON.stringify({"path": "path"}) - syncSpecsLogs.__set__('buildStarted', false) - let data = JSON.stringify({ "specData": [specResult], "buildData": {"duration": "NA", "parallels": "NA"}}) + let specResult = JSON.stringify({ path: 'path' }); + syncSpecsLogs.__set__('buildStarted', false); + let data = { specData: [specResult], buildData: { duration: 'NA', parallels: 'NA' } }; var printSpecData = sandbox.stub(); syncSpecsLogs.__set__('printSpecData', printSpecData); var printInitialLog = sandbox.stub(); syncSpecsLogs.__set__('printInitialLog', printInitialLog); - showSpecsStatus(data, buildCreatedStatusCode); + showSpecsStatus(data, buildCreatedstatus); expect(printSpecData.calledOnce).to.be.true; expect(printInitialLog.calledOnce).to.be.true; }); it('should add custom error, print initial and spec details when spec related data is sent in polling response', () => { - let specResult = JSON.stringify({"path": "path"}) - let customError = { id: "custom_error_1", type: "custom_errors_to_print", level: "warn", should_be_unique: true, message: "custom error message" } - syncSpecsLogs.__set__('buildStarted', false) - let data = JSON.stringify({ "specData": ["created", specResult, customError], "buildData": {"duration": "NA", "parallels": "NA"}}) + let specResult = JSON.stringify({ path: 'path' }); + let customError = { + id: 'custom_error_1', + type: 'custom_errors_to_print', + level: 'warn', + should_be_unique: true, + message: 'custom error message', + }; + syncSpecsLogs.__set__('buildStarted', false); + let data = { + specData: ['created', specResult, customError], + buildData: { duration: 'NA', parallels: 'NA' }, + }; var printSpecData = sandbox.stub(); syncSpecsLogs.__set__('printSpecData', printSpecData); var printInitialLog = sandbox.stub(); syncSpecsLogs.__set__('printInitialLog', printInitialLog); var addCustomErrorToPrint = sandbox.stub(); syncSpecsLogs.__set__('addCustomErrorToPrint', addCustomErrorToPrint); - showSpecsStatus(data, buildRunningStatusCode); + showSpecsStatus(data, buildRunningstatus); expect(printSpecData.calledOnce).to.be.true; expect(printInitialLog.calledOnce).to.be.true; expect(addCustomErrorToPrint.calledOnce).to.be.true; }); }); - context("printSpecsStatus", () => { - const printSpecsStatus = syncSpecsLogs.__get__("printSpecsStatus"); - let startTime = Date.now(), endTime = Date.now() + 10, counter = 0; - let specSummary = { specs: [] }, getOptions, getTableConfig, tableStream, whileProcess; + context('printSpecsStatus', () => { + const printSpecsStatus = syncSpecsLogs.__get__('printSpecsStatus'); + let startTime = Date.now(), + endTime = Date.now() + 10, + counter = 0; + let specSummary = { specs: [] }, + getOptions, + getTableConfig, + tableStream, + whileProcess; beforeEach(() => { counter = 0; @@ -290,11 +327,13 @@ describe("syncSpecsLogs", () => { syncSpecsLogs.__set__('tableStream', tableStream); whileProcess = sandbox.stub().callsFake(function (whilstCallback) { - counter++ - if(counter >= 3) { + counter++; + if (counter >= 3) { syncSpecsLogs.__set__('whileLoop', false); - whilstCallback(new Error("ggg"), {}); - } else {whileProcess(whilstCallback, 10, null)} + whilstCallback(new Error('ggg'), {}); + } else { + whileProcess(whilstCallback, 10, null); + } }); syncSpecsLogs.__set__('whileProcess', whileProcess); @@ -311,7 +350,7 @@ describe("syncSpecsLogs", () => { expect(getTableConfig.calledOnce).to.be.true; expect(tableStream.calledOnce).to.be.true; expect(whileProcess.calledOnce).to.be.false; - expect(specSummary.specs).deep.to.equal([]) + expect(specSummary.specs).deep.to.equal([]); expect(specSummary.cliDuration).to.eql(endTime - startTime); }); }); @@ -332,45 +371,44 @@ describe("syncSpecsLogs", () => { }); }); - context("whileProcess", () => { - const whileProcess = syncSpecsLogs.__get__("whileProcess"); + context('whileProcess', () => { + const whileProcess = syncSpecsLogs.__get__('whileProcess'); - it('Should retry when request fails with error', () => { - let delayed_n = 2, timeout = 3000, n = 1; - let error = new Error("error"); + it('Should retry when axios fails with error', () => { + let delayed_n = 2, + timeout = 3000, + n = 1; + let error = new Error('error'); - let requestStub = sandbox.stub(); + let axiosStub = sandbox.stub(); - let postStub = sandbox - .stub(request, "post") - .yields(error, { statusCode: 502 }, JSON.stringify({})); + let postStub = sandbox.stub(axios, 'post').resolves({ status: 502, error: error, data: {} }); - requestStub.post = postStub; + axiosStub.post = postStub; let setTimeout = sandbox.stub(); syncSpecsLogs.__set__('setTimeout', setTimeout); syncSpecsLogs.__set__('n', n); syncSpecsLogs.__set__('timeout', timeout); - syncSpecsLogs.__set__('request', requestStub); + syncSpecsLogs.__set__('axios', axiosStub); syncSpecsLogs.__set__('whileTries', 5); let whilstCallback = sandbox.stub(); whileProcess(whilstCallback); sinon.assert.calledWith(setTimeout, whilstCallback, timeout * delayed_n, null); - expect(syncSpecsLogs.__get__("whileTries")).to.equal(4); + expect(syncSpecsLogs.__get__('whileTries')).to.equal(4); }); it('Should exit after defined number of retries in case of error', () => { - let error = new Error("error"), requestStub = sandbox.stub(); + let error = new Error('error'), + axiosStub = sandbox.stub(); - let postStub = sandbox - .stub(request, "post") - .yields(error, { statusCode: 502 }, JSON.stringify({})); + let postStub = sandbox.stub(axios, 'post').resolves({ status: 502, error, data: {} }); - requestStub.post = postStub; + axiosStub.post = postStub; - syncSpecsLogs.__set__('request', requestStub); + syncSpecsLogs.__set__('axios', axiosStub); syncSpecsLogs.__set__('whileTries', 1); syncSpecsLogs.__set__('specSummary', {}); syncSpecsLogs.__set__('whileLoop', true); @@ -378,20 +416,23 @@ describe("syncSpecsLogs", () => { let whilstCallback = sandbox.stub(); whileProcess(whilstCallback); - sinon.assert.calledWith(whilstCallback, { status: 504, message: "Tries limit reached" }); - expect(syncSpecsLogs.__get__("whileTries")).to.equal(0); - expect(syncSpecsLogs.__get__("whileLoop")).to.equal(false); - expect(syncSpecsLogs.__get__("specSummary.exitCode")).to.equal(2); + sinon.assert.calledWith(whilstCallback, { status: 504, message: 'Tries limit reached' }); + expect(syncSpecsLogs.__get__('whileTries')).to.equal(0); + expect(syncSpecsLogs.__get__('whileLoop')).to.equal(false); + expect(syncSpecsLogs.__get__('specSummary.exitCode')).to.equal(2); }); it('Should print spec details when data is returned from server', () => { - let error = null, body={}, status = 202, n = 1, delayed_n = 2, timeout = 3000; - let requestStub = sandbox.stub(); - let postStub = sandbox - .stub(request, "post") - .yields(error, { statusCode: status }, JSON.stringify(body)); - requestStub.post = postStub; - syncSpecsLogs.__set__('request', requestStub); + let error = null, + body = {}, + status = 202, + n = 1, + delayed_n = 2, + timeout = 3000; + let axiosStub = sandbox.stub(); + let postStub = sandbox.stub(axios, 'post').resolves({ status: status, data: body }); + axiosStub.post = postStub; + syncSpecsLogs.__set__('axios', axiosStub); let showSpecsStatus = sandbox.stub(); syncSpecsLogs.__set__('showSpecsStatus', showSpecsStatus); @@ -404,19 +445,21 @@ describe("syncSpecsLogs", () => { let whilstCallback = sandbox.stub(); whileProcess(whilstCallback); - expect(syncSpecsLogs.__get__("n")).to.equal(delayed_n); + expect(syncSpecsLogs.__get__('n')).to.equal(delayed_n); sinon.assert.calledWith(setTimeout, whilstCallback, timeout * delayed_n, null); - sinon.assert.calledWith(showSpecsStatus, JSON.stringify(body)); }); it('Should poll for data when server responds with no data available', () => { - let error = null, body={}, status = 204, n = 1, delayed_n = 1, timeout = 3000; - let requestStub = sandbox.stub(); - let postStub = sandbox - .stub(request, "post") - .yields(error, { statusCode: status }, JSON.stringify(body)); - requestStub.post = postStub; - syncSpecsLogs.__set__('request', requestStub); + let error = null, + body = {}, + status = 204, + n = 1, + delayed_n = 2, + timeout = 3000; + let axiosStub = sandbox.stub(); + let postStub = sandbox.stub(axios, 'post').resolves({ status, data: body }); + axiosStub.post = postStub; + syncSpecsLogs.__set__('axios', axiosStub); let showSpecsStatus = sandbox.stub(); syncSpecsLogs.__set__('showSpecsStatus', showSpecsStatus); @@ -429,51 +472,8 @@ describe("syncSpecsLogs", () => { let whilstCallback = sandbox.stub(); whileProcess(whilstCallback); - expect(syncSpecsLogs.__get__("n")).to.equal(delayed_n); + expect(syncSpecsLogs.__get__('n')).to.equal(delayed_n); sinon.assert.calledWith(setTimeout, whilstCallback, timeout * delayed_n, null); }); - - it('Should stop polling for data when server responds build is completed', () => { - let error = null, body={}, status = 200, n = 1, timeout = 3000; - let requestStub = sandbox.stub(); - let postStub = sandbox.stub(request, "post").yields(error, { statusCode: status }, JSON.stringify(body)); - requestStub.post = postStub; - syncSpecsLogs.__set__('request', requestStub); - - let showSpecsStatus = sandbox.stub(); - syncSpecsLogs.__set__('showSpecsStatus', showSpecsStatus); - - syncSpecsLogs.__set__('whileLoop', true); - syncSpecsLogs.__set__('n', n); - syncSpecsLogs.__set__('timeout', timeout); - - let whilstCallback = sandbox.stub(); - whileProcess(whilstCallback); - - expect(syncSpecsLogs.__get__("whileLoop")).to.be.false; - sinon.assert.calledWith(whilstCallback, null, JSON.stringify(body)); - sinon.assert.calledWith(showSpecsStatus, JSON.stringify(body)); - }); - - it('Should stop polling for data when server responds with error ', () => { - let error = null, body={}, status = 404, n = 1, timeout = 3000; - let requestStub = sandbox.stub(); - let postStub = sandbox.stub(request, "post").yields(error, { statusCode: status }, JSON.stringify(body)); - requestStub.post = postStub; - syncSpecsLogs.__set__('request', requestStub); - - let showSpecsStatus = sandbox.stub(); - syncSpecsLogs.__set__('showSpecsStatus', showSpecsStatus); - - syncSpecsLogs.__set__('whileLoop', true); - syncSpecsLogs.__set__('n', n); - syncSpecsLogs.__set__('timeout', timeout); - - let whilstCallback = sandbox.stub(); - whileProcess(whilstCallback); - - expect(syncSpecsLogs.__get__("whileLoop")).to.be.false; - sinon.assert.calledWith(whilstCallback, {message: JSON.stringify(body), status: status}); - }); }); }); diff --git a/test/unit/bin/helpers/utils.js b/test/unit/bin/helpers/utils.js index c863a3ae..58ae9dc1 100644 --- a/test/unit/bin/helpers/utils.js +++ b/test/unit/bin/helpers/utils.js @@ -2,7 +2,7 @@ const path = require('path'); var sandbox = require('sinon').createSandbox(); -const request = require('request'); +const axios = require('axios'); const chai = require('chai'), expect = chai.expect, sinon = require('sinon'), @@ -2900,22 +2900,25 @@ describe('utils', () => { }); describe('#checkLocalBinaryRunning', () => { + let axiosStub; afterEach(() => { sinon.restore(); + axiosStub.restore(); }); it('if the bsConfig localIdentifier is not present within the response body then function should resolve with false', () => { + const responseBody = { + "should_spawn_binary": true + }; const responseObject = { - statusCode: 200, + status: 200, headers: { 'content-type': 'application/json', }, + data: responseBody }; - const responseBody = { - "should_spawn_binary": true - }; - sinon - .stub(request, 'post') - .yields(undefined, responseObject, JSON.stringify(responseBody)); + axiosStub = sinon + .stub(axios, 'post') + .resolves(responseObject); let bsConfig = { auth: { @@ -2933,16 +2936,18 @@ describe('utils', () => { }); it('if the bsConfig localIdentifier is present within the response body', () => { + const responseBody = { "should_spawn_binary": false }; const responseObject = { - statusCode: 200, + status: 200, headers: { 'content-type': 'application/json', }, + data: responseBody }; - const responseBody = { "should_spawn_binary": false }; - sinon - .stub(request, 'post') - .yields(undefined, responseObject, JSON.stringify(responseBody)); + + axiosStub = sinon + .stub(axios, 'post') + .resolves(responseObject); let bsConfig = { auth: { @@ -2952,11 +2957,12 @@ describe('utils', () => { }; let localIdentifier = 'lmno'; - return utils + Promise.resolve(utils .checkLocalBinaryRunning(bsConfig, localIdentifier) .then((result) => { chai.assert.deepEqual(result, {"should_spawn_binary": false}) - }); + })) + return ; }); }); @@ -3442,56 +3448,57 @@ describe('utils', () => { it('message thrown if API deprecated', async () => { let api_deprecated_response = { - statusCode: 299 + status: 299 } message = constant.userMessages.API_DEPRECATED; messageType = constant.messageTypes.INFO; errorCode = 'api_deprecated'; - let requestStub = sinon.stub(request, 'post').yields(undefined, api_deprecated_response, null); + let axiosStub = sinon.stub(axios, 'post').resolves(api_deprecated_response); await utils.stopBrowserStackBuild(bsConfig, args, buildId, rawArgs); - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, null, rawArgs); - requestStub.restore(); + axiosStub.restore(); }); it('message thrown if build returned', async () => { let api_deprecated_response = { - statusCode: 299, + status: 299, + data: body } message = body.message; messageType = constant.messageTypes.INFO; errorCode = 'api_deprecated'; - let requestStub = sinon.stub(request, 'post').yields(undefined, api_deprecated_response, JSON.stringify(body)); + let axiosStub = sinon.stub(axios, 'post').resolves(api_deprecated_response); await utils.stopBrowserStackBuild(bsConfig, args, buildId, rawArgs); - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, null, rawArgs); - requestStub.restore(); + axiosStub.restore(); }); - it('message thrown if statusCode != 200', async () => { + it('message thrown if status != 200', async () => { let non_200_status_response = { - statusCode: 400 + status: 400 } message = constant.userMessages.BUILD_STOP_FAILED; messageType = constant.messageTypes.ERROR; errorCode = 'api_failed_build_stop'; - let requestStub = sinon.stub(request, 'post').yields(undefined, non_200_status_response, null); + let axiosStub = sinon.stub(axios, 'post').resolves(non_200_status_response); await utils.stopBrowserStackBuild(bsConfig, args, buildId, rawArgs); - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, null, rawArgs); - requestStub.restore(); + axiosStub.restore(); }); - it('message thrown if statusCode != 200 and user unauthorized', async () => { + it('message thrown if status != 200 and user unauthorized', async () => { let body_with_message = { ...body, "message": "Unauthorized", }; let non_200_status_response = { - statusCode: 401, + status: 401, data: body_with_message } @@ -3500,17 +3507,18 @@ describe('utils', () => { } with error: \n${JSON.stringify(body_with_message, null, 2)}`; messageType = constant.messageTypes.ERROR; errorCode = 'api_auth_failed'; - let requestStub = sinon.stub(request, 'post').yields(undefined, non_200_status_response, JSON.stringify(body_with_message)); + let axiosStub = sinon.stub(axios, 'post').resolves(non_200_status_response); await utils.stopBrowserStackBuild(bsConfig, args, buildId, rawArgs); - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, null, rawArgs); - requestStub.restore(); + axiosStub.restore(); }); - it('message thrown if statusCode != 200 and build is present', async () => { + it('message thrown if status != 200 and build is present', async () => { let non_200_status_response = { - statusCode: 402, + status: 402, + data: body } message = `${ @@ -3518,28 +3526,29 @@ describe('utils', () => { } with error: \n${JSON.stringify(body, null, 2)}`; messageType = constant.messageTypes.ERROR; errorCode = 'api_failed_build_stop'; - let requestStub = sinon.stub(request, 'post').yields(undefined, non_200_status_response, JSON.stringify(body)); + let axiosStub = sinon.stub(axios, 'post').resolves(non_200_status_response); await utils.stopBrowserStackBuild(bsConfig, args, buildId, rawArgs); - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, null, rawArgs); - requestStub.restore(); + axiosStub.restore(); }); it('message thrown if API success', async () => { let success_response = { - statusCode: 200, + status: 200, + data: body } message = `${JSON.stringify(body, null, 2)}`; messageType = constant.messageTypes.SUCCESS; errorCode = null; - let requestStub = sinon.stub(request, 'post').yields(undefined, success_response, JSON.stringify(body)); + let axiosStub = sinon.stub(axios, 'post').resolves(success_response); await utils.stopBrowserStackBuild(bsConfig, args, buildId, rawArgs); - sinon.assert.calledOnce(requestStub); + sinon.assert.calledOnce(axiosStub); sinon.assert.calledOnce(getUserAgentStub); sinon.assert.calledOnceWithExactly(sendUsageReportStub, bsConfig, args, message, messageType, errorCode, null, rawArgs); - requestStub.restore(); + axiosStub.restore(); }); }); @@ -4238,10 +4247,10 @@ describe('utils', () => { it('should return correct JSON', () => { expect(utils.formatRequest('Something went wrong.', undefined, undefined)).to.be.eql({err: 'Something went wrong.', status: null, body: null}); const body = {message: "Something went wrong"}; - expect(utils.formatRequest(null, {statusCode: 400}, body)).to.be.eql({err: null, status: 400, body: JSON.stringify(body)}); + expect(utils.formatRequest(null, {status: 400}, body)).to.be.eql({err: null, status: 400, body: JSON.stringify(body)}); const cricularBody = {message: "Something went wrong"}; cricularBody.body = cricularBody; - expect(utils.formatRequest(null, {statusCode: 500}, cricularBody)).to.be.eql({err: null, status: 500, body: '[Circular]'}); + expect(utils.formatRequest(null, {status: 500}, cricularBody)).to.be.eql({err: null, status: 500, body: '[Circular]'}); }); }); diff --git a/test/unit/bin/helpers/zipUpload.js b/test/unit/bin/helpers/zipUpload.js index fa418b7d..32241964 100644 --- a/test/unit/bin/helpers/zipUpload.js +++ b/test/unit/bin/helpers/zipUpload.js @@ -2,8 +2,9 @@ const chai = require("chai"), chaiAsPromised = require("chai-as-promised"), sinon = require("sinon"), - fs = require('fs'), - request = require("request"); + fs = require('fs'); + +const { default: axios } = require("axios"); const logger = require("../../../../bin/helpers/logger").winstonLogger, constant = require('../../../../bin/helpers/constants'), @@ -45,7 +46,15 @@ describe("zipUpload", () => { const zipUploader = rewire("../../../../bin/helpers/zipUpload"); beforeEach(() => { utilsStub = { - generateUploadParams: sinon.stub().returns({}), + generateUploadParams: sinon.stub().returns({ + auth: { + user: "someuser", + password: "someuser", + }, + headers: { + "someheader": "header" + } + }), formatRequest, }; loggerStub = { @@ -59,36 +68,6 @@ describe("zipUpload", () => { fs.lstatSync.restore(); }); - it("reject with error", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(new Error("test error"), null, null); - - zipUploader.__set__({ - request: { post: requestStub }, - utils: utilsStub, - logger: loggerStub - }); - let uploadSuitsrewire = zipUploader.__get__('uploadSuits'); - let opts = { - archivePresent: true, - messages: {} - } - let obj = { - bar1: null, - zipInterval: null, - size: 0, - startTime: null - } - return uploadSuitsrewire(bsConfig, filePath, opts, obj) - .then((_data) => { - chai.assert.fail("Promise error"); - }) - .catch((error) => { - chai.assert.equal(error.message.message, "test error"); - }); - }); - it("resolve with url if already present", () => { let uploadSuitsrewire = zipUploader.__get__('uploadSuits'); let opts = { @@ -133,12 +112,12 @@ describe("zipUpload", () => { }); it("resolve with nothing if parsing error", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 200 }, '{ random: "test }'); + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 200, data: { "random": "test" }}); zipUploader.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, utils: utilsStub, logger: loggerStub }); @@ -146,7 +125,12 @@ describe("zipUpload", () => { let opts = { cleanupMethod: sinon.stub().returns(null), archivePresent: true, - messages: {} + messages: {}, + fileDetails: { + filetype: "zip", + filename: "abc" + }, + md5ReturnKey: "random" } let obj = { bar1: null, @@ -156,20 +140,20 @@ describe("zipUpload", () => { } return uploadSuitsrewire(bsConfig, filePath, opts, obj) .then((data) => { - chai.assert.hasAllKeys(data, ["time"]); + chai.assert.hasAllKeys(data, ["time", "random"]); }) .catch((_error) => { chai.assert.fail("Promise error"); }); }); - it("resolve with message if statusCode = 200", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 200 }, JSON.stringify({ zip_url: "zip_url" })); + it("resolve with message if status = 200", () => { + let axiosStub = sandbox + .stub(axios, "post") + .resolves({ status: 200 , data: { zip_url: "zip_url" }}); zipUploader.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, utils: utilsStub, logger: loggerStub }); @@ -177,7 +161,11 @@ describe("zipUpload", () => { let opts = { cleanupMethod: sinon.stub().returns(null), archivePresent: true, - messages: {} + messages: {}, + fileDetails: { + filetype: "zip", + filename: "abc" + }, } let obj = { bar1: null, @@ -195,19 +183,23 @@ describe("zipUpload", () => { }); it("reject with returned message if auth failed with message", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 401 }, JSON.stringify({ error: "auth failed" })); + let axiosStub = sandbox + .stub(axios, "post") + .rejects({response: { status: 401 , data: { error: "auth failed" }}}); zipUploader.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, utils: utilsStub, logger: loggerStub }); let uploadSuitsrewire = zipUploader.__get__('uploadSuits'); let opts = { archivePresent: true, - messages: {} + messages: {}, + fileDetails: { + filetype: "zip", + filename: "abc" + }, } let obj = { bar1: null, @@ -225,19 +217,23 @@ describe("zipUpload", () => { }); it("reject with predefined message if auth failed without message", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 401 }, JSON.stringify({ })); + let axiosStub = sandbox + .stub(axios, "post") + .rejects({response: { status: 401 , data: { }}}); zipUploader.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, utils: utilsStub, logger: loggerStub }); let uploadSuitsrewire = zipUploader.__get__('uploadSuits'); let opts = { archivePresent: true, - messages: {} + messages: {}, + fileDetails: { + filetype: "zip", + filename: "abc" + }, } let obj = { bar1: null, @@ -254,13 +250,13 @@ describe("zipUpload", () => { }); }); - it("resolve with nothing if request error but no propogation", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 402 }, JSON.stringify({ })); + it("resolve with nothing if axios error but no propogation", () => { + let axiosStub = sandbox + .stub(axios, "post") + .rejects({response: { status: 402 , data: { }}}); zipUploader.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, utils: utilsStub, logger: loggerStub }); @@ -268,7 +264,12 @@ describe("zipUpload", () => { let opts = { archivePresent: true, messages: {}, - propogateError: false + propogateError: false, + fileDetails: { + filetype: "zip", + filename: "abc" + }, + cleanupMethod: () => {} } let obj = { bar1: null, @@ -285,13 +286,13 @@ describe("zipUpload", () => { }); }); - it("reject with error if request error", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 402 }, JSON.stringify({ error: "test error" })); + it("reject with error if axios error", () => { + let axiosStub = sandbox + .stub(axios, "post") + .rejects({response: { status: 402 , data: { error: "test error" }}}); zipUploader.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, utils: utilsStub, logger: loggerStub }); @@ -299,7 +300,12 @@ describe("zipUpload", () => { let opts = { archivePresent: true, messages: {}, - propogateError: true + propogateError: true, + fileDetails: { + filetype: "zip", + filename: "abc" + }, + cleanupMethod: () => {} } let obj = { bar1: null, @@ -317,12 +323,12 @@ describe("zipUpload", () => { }); it("reject with limit exceeded error", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 413 }, JSON.stringify({ })); + let axiosStub = sandbox + .stub(axios, "post") + .rejects({response:{ status: 413 , data: { }}}); zipUploader.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, utils: utilsStub, logger: loggerStub }); @@ -330,7 +336,12 @@ describe("zipUpload", () => { let opts = { archivePresent: true, messages: {}, - propogateError: true + propogateError: true, + fileDetails: { + filetype: "zip", + filename: "abc" + }, + cleanupMethod: () => {} } let obj = { bar1: null, @@ -348,12 +359,12 @@ describe("zipUpload", () => { }); it("reject with not reachable error", () => { - let requestStub = sandbox - .stub(request, "post") - .yields(null, { statusCode: 414 }, JSON.stringify({ })); + let axiosStub = sandbox + .stub(axios, "post") + .rejects({response: { status: 414 , data: { }}}); zipUploader.__set__({ - request: { post: requestStub }, + axios: { post: axiosStub }, utils: utilsStub, logger: loggerStub }); @@ -361,7 +372,12 @@ describe("zipUpload", () => { let opts = { archivePresent: true, messages: {}, - propogateError: true + propogateError: true, + fileDetails: { + filetype: "zip", + filename: "abc" + }, + cleanupMethod: () => {} } let obj = { bar1: null,