@nightwatch/browserstack
Advanced tools
Comparing version 3.2.0 to 3.2.1
const LocalTunnel = require('../src/local-tunnel'); | ||
const TestObservability = require('../src/testObservability'); | ||
const {CUSTOM_REPORTER_CALLBACK_TIMEOUT} = require('../src/utils/constants'); | ||
const {CUSTOM_REPORTER_CALLBACK_TIMEOUT, EVENTS} = require('../src/utils/constants'); | ||
const CrashReporter = require('../src/utils/crashReporter'); | ||
const helper = require('../src/utils/helper'); | ||
const Logger = require('../src/utils/logger'); | ||
const {v4: uuidv4} = require('uuid'); | ||
const path = require('path'); | ||
const AccessibilityAutomation = require('../src/accessibilityAutomation'); | ||
const eventHelper = require('../src/utils/eventHelper'); | ||
const localTunnel = new LocalTunnel(); | ||
@@ -15,3 +17,28 @@ const testObservability = new TestObservability(); | ||
const nightwatchRerunFile = process.env.NIGHTWATCH_RERUN_REPORT_FILE; | ||
const _tests = {}; | ||
const _testCasesData = {}; | ||
let currentTestUUID = ''; | ||
let workerList = {}; | ||
eventHelper.eventEmitter.on(EVENTS.LOG_INIT, (loggingData) => { | ||
const testCaseStartedId = loggingData.message.replace('TEST-OBSERVABILITY-PID-TESTCASE-MAPPING-', '').slice(1, -1); | ||
const testCaseId = _testCasesData[testCaseStartedId]?.testCaseId; | ||
currentTestUUID = _tests[testCaseId]?.uuid; | ||
}); | ||
eventHelper.eventEmitter.on(EVENTS.LOG, (loggingData) => { | ||
if (currentTestUUID && currentTestUUID !== '') { | ||
testObservability.appendTestItemLog(loggingData, currentTestUUID); | ||
} | ||
}); | ||
const handleScreenshotUpload = async (data) => { | ||
try { | ||
const {args, uuid} = data; | ||
await testObservability.createScreenshotLogEvent(uuid, args.path, Date.now()); | ||
} catch (error) { | ||
CrashReporter.uploadCrashReport(error.message, error.stack); | ||
} | ||
}; | ||
module.exports = { | ||
@@ -41,3 +68,3 @@ | ||
} | ||
await Promise.all(promises); | ||
@@ -51,6 +78,182 @@ done(); | ||
}, | ||
registerEventHandlers(eventBroadcaster) { | ||
eventBroadcaster.on('TestCaseStarted', async (args) => { | ||
if (!helper.isTestObservabilitySession()) { | ||
return; | ||
} | ||
try { | ||
_testCasesData[args.envelope.id] = { | ||
...args.envelope | ||
}; | ||
const reportData = args.report; | ||
const testCaseId = reportData.testCaseStarted[args.envelope.id].testCaseId; | ||
const pickleId = reportData.testCases.find((testCase) => testCase.id === testCaseId).pickleId; | ||
const pickleData = reportData.pickle.find((pickle) => pickle.id === pickleId); | ||
const gherkinDocument = reportData?.gherkinDocument.find((document) => document.uri === pickleData.uri); | ||
const featureData = gherkinDocument.feature; | ||
const uniqueId = uuidv4(); | ||
process.env.TEST_OPS_TEST_UUID = uniqueId; | ||
Object.values(workerList).forEach((worker) => { | ||
worker.process.on('message', async (data) => { | ||
if (data.eventType === EVENTS.LOG_INIT) { | ||
const testCaseStartedId = data.loggingData.message.replace('TEST-OBSERVABILITY-PID-TESTCASE-MAPPING-', '').slice(1, -1); | ||
const testCaseId = _testCasesData[testCaseStartedId]?.testCaseId; | ||
const uuid = _tests[testCaseId]?.uuid; | ||
await worker.process.send({testCaseStartedId, uuid}); | ||
} | ||
}); | ||
}); | ||
const testMetaData = { | ||
uuid: uniqueId, | ||
startedAt: new Date().toISOString() | ||
}; | ||
if (pickleData) { | ||
testMetaData.scenario = { | ||
name: pickleData.name | ||
}; | ||
} | ||
if (gherkinDocument && featureData) { | ||
testMetaData.feature = { | ||
path: gherkinDocument.uri, | ||
name: featureData.name, | ||
description: featureData.description | ||
}; | ||
} | ||
_tests[testCaseId] = testMetaData; | ||
await testObservability.sendTestRunEventForCucumber(reportData, gherkinDocument, pickleData, 'TestRunStarted', testMetaData, args); | ||
} catch (error) { | ||
CrashReporter.uploadCrashReport(error.message, error.stack); | ||
Logger.error(`Something went wrong in processing report file for test observability - ${error.message} with stacktrace ${error.stack}`); | ||
} | ||
}); | ||
eventBroadcaster.on('TestCaseFinished', async (args) => { | ||
if (!helper.isTestObservabilitySession()) { | ||
return; | ||
} | ||
try { | ||
const reportData = args.report; | ||
const testCaseId = _testCasesData[args.envelope.testCaseStartedId].testCaseId; | ||
const pickleId = reportData.testCases.find((testCase) => testCase.id === testCaseId).pickleId; | ||
const pickleData = reportData.pickle.find((pickle) => pickle.id === pickleId); | ||
const gherkinDocument = reportData?.gherkinDocument.find((document) => document.uri === pickleData.uri); | ||
const testMetaData = _tests[testCaseId]; | ||
if (testMetaData) { | ||
delete _tests[testCaseId]; | ||
testMetaData.finishedAt = new Date().toISOString(); | ||
await testObservability.sendTestRunEventForCucumber(reportData, gherkinDocument, pickleData, 'TestRunFinished', testMetaData, args); | ||
} | ||
} catch (error) { | ||
CrashReporter.uploadCrashReport(error.message, error.stack); | ||
Logger.error(`Something went wrong in processing report file for test observability - ${error.message} with stacktrace ${error.stack}`); | ||
} | ||
}); | ||
eventBroadcaster.on('TestStepStarted', (args) => { | ||
if (!helper.isTestObservabilitySession()) { | ||
return; | ||
} | ||
try { | ||
const reportData = args.report; | ||
const testCaseId = _testCasesData[args.envelope.testCaseStartedId].testCaseId; | ||
const pickleId = reportData.testCases.find((testCase) => testCase.id === testCaseId).pickleId; | ||
const pickleData = reportData.pickle.find((pickle) => pickle.id === pickleId); | ||
const testSteps = reportData.testCases.find((testCase) => testCase.id === testCaseId).testSteps; | ||
const testStepId = reportData.testStepStarted[args.envelope.testCaseStartedId].testStepId; | ||
const pickleStepId = testSteps.find((testStep) => testStep.id === testStepId).pickleStepId; | ||
if (pickleStepId && _tests['testStepId'] !== testStepId) { | ||
_tests['testStepId'] = testStepId; | ||
const pickleStepData = pickleData.steps.find((pickle) => pickle.id === pickleStepId); | ||
const testMetaData = _tests[testCaseId] || {steps: []}; | ||
if (testMetaData && !testMetaData.steps) { | ||
testMetaData.steps = []; | ||
} | ||
testMetaData.steps?.push({ | ||
id: pickleStepData.id, | ||
text: pickleStepData.text, | ||
started_at: new Date().toISOString() | ||
}); | ||
_tests[testCaseId] = testMetaData; | ||
} | ||
} catch (error) { | ||
CrashReporter.uploadCrashReport(error.message, error.stack); | ||
Logger.error(`Something went wrong in processing report file for test observability - ${error.message} with stacktrace ${error.stack}`); | ||
} | ||
}); | ||
eventBroadcaster.on('TestStepFinished', async (args) => { | ||
if (!helper.isTestObservabilitySession()) { | ||
return; | ||
} | ||
try { | ||
const reportData = args.report; | ||
const testCaseId = _testCasesData[args.envelope.testCaseStartedId].testCaseId; | ||
const testStepFinished = reportData.testStepFinished[args.envelope.testCaseStartedId]; | ||
const pickleId = reportData.testCases.find((testCase) => testCase.id === testCaseId).pickleId; | ||
const pickleData = reportData.pickle.find((pickle) => pickle.id === pickleId); | ||
const testSteps = reportData.testCases.find((testCase) => testCase.id === testCaseId).testSteps; | ||
const testStepId = reportData.testStepFinished[args.envelope.testCaseStartedId].testStepId; | ||
const pickleStepId = testSteps.find((testStep) => testStep.id === testStepId).pickleStepId; | ||
let failure; | ||
let failureType; | ||
if (testStepFinished.testStepResult?.status.toString().toLowerCase() === 'failed') { | ||
failure = (testStepFinished.testStepResult?.exception === undefined) ? testStepFinished.testStepResult?.message : testStepFinished.testStepResult?.exception?.message; | ||
failureType = (testStepFinished.testStepResult?.exception === undefined) ? 'UnhandledError' : testStepFinished.testStepResult?.message; | ||
} | ||
if (pickleStepId && _tests['testStepId']) { | ||
const pickleStepData = pickleData.steps.find((pickle) => pickle.id === pickleStepId); | ||
const testMetaData = _tests[testCaseId] || {steps: []}; | ||
if (!testMetaData.steps) { | ||
testMetaData.steps = [{ | ||
id: pickleStepData.id, | ||
text: pickleStepData.text, | ||
finished_at: new Date().toISOString(), | ||
result: testStepFinished.testStepResult?.status, | ||
duration: testStepFinished.testStepResult?.duration?.seconds, | ||
failure: failure, | ||
failureType: failureType | ||
}]; | ||
} else { | ||
testMetaData.steps.forEach((step) => { | ||
if (step.id === pickleStepData.id) { | ||
step.finished_at = new Date().toISOString(); | ||
step.result = testStepFinished.testStepResult?.status; | ||
step.duration = testStepFinished.testStepResult?.duration?.seconds; | ||
step.failure = failure; | ||
step.failureType = failureType; | ||
} | ||
}); | ||
} | ||
_tests[testCaseId] = testMetaData; | ||
delete _tests['testStepId']; | ||
if (testStepFinished.httpOutput && testStepFinished.httpOutput.length > 0) { | ||
for (const [index, output] of testStepFinished.httpOutput.entries()) { | ||
if (index % 2 === 0) { | ||
await testObservability.createHttpLogEvent(output, testStepFinished.httpOutput[index + 1], testMetaData.uuid); | ||
} | ||
} | ||
} | ||
} | ||
} catch (error) { | ||
CrashReporter.uploadCrashReport(error.message, error.stack); | ||
Logger.error(`Something went wrong in processing report file for test observability - ${error.message} with stacktrace ${error.stack}`); | ||
} | ||
}); | ||
eventBroadcaster.on('ScreenshotCreated', async (args) => { | ||
if (!helper.isTestObservabilitySession()) {return} | ||
handleScreenshotUpload({args: args, uuid: process.env.TEST_OPS_TEST_UUID}); | ||
}); | ||
eventBroadcaster.on('TestRunStarted', async (test) => { | ||
await accessibilityAutomation.beforeEachExecution(test); | ||
}); | ||
eventBroadcaster.on('TestRunFinished', async (test) => { | ||
@@ -60,2 +263,3 @@ await accessibilityAutomation.afterEachExecution(test); | ||
}, | ||
onEvent({eventName, hook_type, ...args}) { | ||
@@ -92,2 +296,6 @@ if (typeof browser !== 'undefined' && eventName === 'TestRunStarted') { | ||
if (helper.isTestObservabilitySession()) { | ||
if (helper.isCucumberTestSuite(settings)) { | ||
cucumberPatcher(); | ||
settings.test_runner.options['require'] = path.resolve(__dirname, 'observabilityLogPatcherHook.js'); | ||
} | ||
settings.globals['customReporterCallbackTimeout'] = CUSTOM_REPORTER_CALLBACK_TIMEOUT; | ||
@@ -125,2 +333,7 @@ if (testObservability._user && testObservability._key) { | ||
if (helper.isTestObservabilitySession()) { | ||
process.env.NIGHTWATCH_RERUN_FAILED = nightwatchRerun; | ||
process.env.NIGHTWATCH_RERUN_REPORT_FILE = nightwatchRerunFile; | ||
if (process.env.BROWSERSTACK_RERUN === 'true' && process.env.BROWSERSTACK_RERUN_TESTS) { | ||
await helper.deleteRerunFile(); | ||
} | ||
try { | ||
@@ -134,7 +347,3 @@ await testObservability.stopBuildUpstream(); | ||
} | ||
process.env.NIGHTWATCH_RERUN_FAILED = nightwatchRerun; | ||
process.env.NIGHTWATCH_RERUN_REPORT_FILE = nightwatchRerunFile; | ||
if (process.env.BROWSERSTACK_RERUN === 'true' && process.env.BROWSERSTACK_RERUN_TESTS) { | ||
await helper.deleteRerunFile(); | ||
} | ||
process.exit(); | ||
} | ||
@@ -189,1 +398,28 @@ if (helper.isAccessibilitySession()){ | ||
}; | ||
const cucumberPatcher = () => { | ||
try { | ||
const Coordinator = helper.requireModule('@cucumber/cucumber/lib/runtime/parallel/coordinator.js'); | ||
class CoordinatorPatcher extends Coordinator.default { | ||
constructor(...args) { | ||
super(...args); | ||
} | ||
startWorker(...args) { | ||
const workerData = super.startWorker(...args); | ||
workerList = this.workers; | ||
return workerData; | ||
} | ||
parseWorkerMessage(...args) { | ||
if ([EVENTS.LOG, EVENTS.LOG_INIT].includes(args[1]?.eventType)) {return} | ||
return super.parseWorkerMessage(...args); | ||
} | ||
} | ||
Coordinator.default = CoordinatorPatcher; | ||
} catch (error) { | ||
Logger.debug(`Error while patching cucumber ${error}`); | ||
} | ||
}; |
{ | ||
"name": "@nightwatch/browserstack", | ||
"version": "3.2.0", | ||
"version": "3.2.1", | ||
"description": "Nightwatch plugin for integration with browserstack.", | ||
@@ -47,2 +47,3 @@ "main": "index.js", | ||
"strip-ansi": "^6.0.1", | ||
"winston-transport": "^4.5.0", | ||
"uuid": "^9.0.0" | ||
@@ -49,0 +50,0 @@ }, |
@@ -364,6 +364,6 @@ const path = require('path'); | ||
try { | ||
let session = await browser.session(); | ||
const session = await browser.session(); | ||
if (session) { | ||
let pageOpen = true; | ||
let currentURL = await browser.driver.getCurrentUrl(); | ||
const currentURL = await browser.driver.getCurrentUrl(); | ||
@@ -399,3 +399,3 @@ let url = {}; | ||
} else { | ||
await browser.executeScript(` | ||
await browser.executeAsyncScript(` | ||
const e = new CustomEvent('A11Y_FORCE_STOP'); | ||
@@ -439,3 +439,3 @@ window.dispatchEvent(e); | ||
}; | ||
let final_res = await browser.executeAsyncScript( | ||
const final_res = await browser.executeAsyncScript( | ||
` | ||
@@ -479,3 +479,3 @@ const callback = arguments[arguments.length - 1]; | ||
try { | ||
let results = await browser.executeScript(` | ||
const results = await browser.executeScript(` | ||
return new Promise(function (resolve, reject) { | ||
@@ -513,3 +513,3 @@ try { | ||
try { | ||
let summaryResults = await browser.executeScript(` | ||
const summaryResults = await browser.executeScript(` | ||
return new Promise(function (resolve, reject) { | ||
@@ -516,0 +516,0 @@ try{ |
@@ -127,3 +127,3 @@ const os = require('os'); | ||
try { | ||
const response = await makeRequest('PUT', `api/v1/builds/${process.env.BS_TESTOPS_BUILD_HASHED_ID}/stop`, data, config); | ||
const response = await makeRequest('PUT', `api/v1/builds/${process.env.BS_TESTOPS_BUILD_HASHED_ID}/stop`, data, config, API_URL, false); | ||
if (response.data?.error) { | ||
@@ -376,4 +376,113 @@ throw {message: response.data.error}; | ||
} | ||
async sendTestRunEventForCucumber(reportData, gherkinDocument, pickleData, eventType, testMetaData, args = {}) { | ||
const {feature, scenario, steps, uuid, startedAt, finishedAt} = testMetaData || {}; | ||
const examples = helper.getScenarioExamples(gherkinDocument, pickleData); | ||
const fullNameWithExamples = examples | ||
? pickleData.name + ' (' + examples.join(', ') + ')' | ||
: pickleData.name; | ||
const testData = { | ||
uuid: uuid, | ||
started_at: startedAt, | ||
finished_at: finishedAt, | ||
type: 'test', | ||
body: { | ||
lang: 'nightwatch', | ||
code: null | ||
}, | ||
name: fullNameWithExamples, | ||
scope: fullNameWithExamples, | ||
scopes: [feature?.name || ''], | ||
tags: pickleData.tags?.map(({name}) => (name)), | ||
identifier: scenario?.name, | ||
file_name: path.relative(process.cwd(), feature.path), | ||
location: path.relative(process.cwd(), feature.path), | ||
vc_filepath: (this._gitMetadata && this._gitMetadata.root) ? path.relative(this._gitMetadata.root, feature.path) : null, | ||
framework: 'nightwatch', | ||
result: 'pending', | ||
meta: { | ||
feature: feature, | ||
scenario: scenario, | ||
steps: steps, | ||
examples: examples | ||
} | ||
}; | ||
try { | ||
if (eventType === 'TestRunFinished') { | ||
const currentSessionCapabilities = reportData.session[args.envelope.testCaseStartedId]; | ||
if (currentSessionCapabilities.error) { | ||
throw new Error(`Error in driver capabilities: ${JSON.stringify(currentSessionCapabilities.error)}`); | ||
} | ||
const sessionCapabilities = currentSessionCapabilities.capabilities; | ||
if ((sessionCapabilities) && (args.envelope.testCaseStartedId === currentSessionCapabilities.testCaseStartedId)) { | ||
testData.integrations = {}; | ||
const provider = helper.getCloudProvider(currentSessionCapabilities.host); | ||
testData.integrations[provider] = helper.getIntegrationsObject(sessionCapabilities, currentSessionCapabilities.sessionId); | ||
} else { | ||
Logger.debug('Failed to upload integrations data'); | ||
} | ||
} | ||
} catch (error) { | ||
CrashReporter.uploadCrashReport(error.message, error.stack); | ||
} | ||
if (reportData.testCaseFinished && steps) { | ||
const testCaseResult = reportData.testCaseFinished[args.envelope.testCaseStartedId]; | ||
let result = 'passed'; | ||
steps.every((step) => { | ||
if (step.result === 'FAILED'){ | ||
result = 'failed'; | ||
testCaseResult.failure = step.failure; | ||
testCaseResult.failureType = step.failureType; | ||
return false; | ||
} else if (step.result === 'SKIPPED') { | ||
result = 'skipped'; | ||
return false; | ||
} | ||
return true; | ||
}); | ||
testData.finished_at = new Date().toISOString(); | ||
testData.result = result; | ||
testData.duration_in_ms = testCaseResult.timestamp.nanos / 1000000; | ||
if (result === 'failed') { | ||
testData.failure = [ | ||
{ | ||
'backtrace': [testCaseResult?.failure ? stripAnsi(testCaseResult?.failure) : 'unknown'] | ||
} | ||
], | ||
testData.failure_reason = testCaseResult?.failure ? stripAnsi(testCaseResult?.failure) : testCaseResult.message; | ||
if (testCaseResult?.failureType) { | ||
testData.failure_type = testCaseResult.failureType.match(/AssertError/) | ||
? 'AssertionError' | ||
: 'UnhandledError'; | ||
} | ||
} | ||
} | ||
const uploadData = { | ||
event_type: eventType, | ||
test_run: testData | ||
}; | ||
await helper.uploadEventData(uploadData); | ||
} | ||
async appendTestItemLog (log, testUuid) { | ||
try { | ||
if (testUuid) { | ||
log.test_run_uuid = testUuid; | ||
await helper.uploadEventData({event_type: 'LogCreated', logs: [log]}); | ||
} | ||
} catch (error) { | ||
Logger.error(`Exception in uploading log data to Observability with error : ${error}`); | ||
} | ||
} | ||
} | ||
module.exports = TestObservability; |
@@ -10,2 +10,12 @@ exports.BATCH_SIZE = 1000; | ||
exports.CUSTOM_REPORTER_CALLBACK_TIMEOUT = 3600000; | ||
exports.consoleHolder = Object.assign({}, console); | ||
// Regex = TEST-OBSERVABILITY-PID-TESTCASE-MAPPING-ea78bf4a-d02b-40bc-8f52-7b53a4350b2c | ||
exports.PID_MAPPING_REGEX = /^TEST-OBSERVABILITY-PID-TESTCASE-MAPPING-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/; | ||
exports.IPC_SERVER_NAME = 'browserstackTestObservability'; | ||
exports.EVENTS = { | ||
LOG: 'testObservability:log', | ||
LOG_INIT: 'testObservability:log:init', | ||
SCREENSHOT: 'testObservability:screenshot' | ||
}; | ||
exports.ACCESSIBILITY_URL= 'https://accessibility.browserstack.com/api'; |
const helper = require('./helper'); | ||
const {makeRequest} = require('./requestHelper'); | ||
const Logger = require('./logger'); | ||
const {API_URL} = require('./constants'); | ||
@@ -66,3 +67,3 @@ class CrashReporter { | ||
}; | ||
await makeRequest('POST', 'api/v1/analytics', data, config); | ||
await makeRequest('POST', 'api/v1/analytics', data, config, API_URL, false); | ||
} catch (error) { | ||
@@ -69,0 +70,0 @@ Logger.error(`[Crash_Report_Upload] Failed due to ${error}`); |
@@ -11,7 +11,21 @@ const os = require('os'); | ||
const {makeRequest} = require('./requestHelper'); | ||
const {RERUN_FILE, DEFAULT_WAIT_TIMEOUT_FOR_PENDING_UPLOADS, DEFAULT_WAIT_INTERVAL_FOR_PENDING_UPLOADS} = require('./constants'); | ||
const {RERUN_FILE, DEFAULT_WAIT_TIMEOUT_FOR_PENDING_UPLOADS, DEFAULT_WAIT_INTERVAL_FOR_PENDING_UPLOADS, consoleHolder} = require('./constants'); | ||
const requestQueueHandler = require('./requestQueueHandler'); | ||
const Logger = require('./logger'); | ||
const LogPatcher = require('./logPatcher'); | ||
const BSTestOpsPatcher = new LogPatcher({}); | ||
console = {}; | ||
Object.keys(consoleHolder).forEach(method => { | ||
console[method] = (...args) => { | ||
BSTestOpsPatcher[method](...args); | ||
}; | ||
}); | ||
exports.debug = (text) => { | ||
if (process.env.BROWSERSTACK_OBSERVABILITY_DEBUG === 'true' || process.env.BROWSERSTACK_OBSERVABILITY_DEBUG === '1') { | ||
consoleHolder.log(`\n[${(new Date()).toISOString()}][ OBSERVABILITY ] ${text}\n`); | ||
} | ||
}; | ||
exports.generateLocalIdentifier = () => { | ||
@@ -96,123 +110,237 @@ const formattedDate = new Intl.DateTimeFormat('en-GB', { | ||
exports.isTrue = (value) => (value+ '').toLowerCase() === 'true'; | ||
exports.getCIVendor = () => { | ||
var env = process.env; | ||
const ciInfo = this.getCiInfo(); | ||
if (ciInfo) { | ||
return ciInfo.name; | ||
} | ||
return null; | ||
}; | ||
exports.getCiInfo = () => { | ||
const env = process.env; | ||
// Jenkins | ||
if ((typeof env.JENKINS_URL === 'string' && env.JENKINS_URL.length > 0) || (typeof env.JENKINS_HOME === 'string' && env.JENKINS_HOME.length > 0)) { | ||
return 'Jenkins'; | ||
return { | ||
name: 'Jenkins', | ||
build_url: env.BUILD_URL, | ||
job_name: env.JOB_NAME, | ||
build_number: env.BUILD_NUMBER | ||
}; | ||
} | ||
// CircleCI | ||
if (env.CI === 'true' && env.CIRCLECI === 'true') { | ||
return 'CircleCI'; | ||
if (this.isTrue(env.CI) && this.isTrue(env.CIRCLECI)) { | ||
return { | ||
name: 'CircleCI', | ||
build_url: env.CIRCLE_BUILD_URL, | ||
job_name: env.CIRCLE_JOB, | ||
build_number: env.CIRCLE_BUILD_NUM | ||
}; | ||
} | ||
// Travis CI | ||
if (env.CI === 'true' && env.TRAVIS === 'true') { | ||
return 'TravisCI'; | ||
if (this.isTrue(env.CI) && this.isTrue(env.TRAVIS)) { | ||
return { | ||
name: 'Travis CI', | ||
build_url: env.TRAVIS_BUILD_WEB_URL, | ||
job_name: env.TRAVIS_JOB_NAME, | ||
build_number: env.TRAVIS_BUILD_NUMBER | ||
}; | ||
} | ||
// Codeship | ||
if (env.CI === 'true' && env.CI_NAME === 'codeship') { | ||
return 'Codeship'; | ||
if (this.isTrue(env.CI) && this.isTrue(env.CI_NAME)) { | ||
return { | ||
name: 'Codeship', | ||
build_url: null, | ||
job_name: null, | ||
build_number: null | ||
}; | ||
} | ||
// Bitbucket | ||
if (env.BITBUCKET_BRANCH && env.BITBUCKET_COMMIT) { | ||
return 'Bitbucket'; | ||
return { | ||
name: 'Bitbucket', | ||
build_url: env.BITBUCKET_GIT_HTTP_ORIGIN, | ||
job_name: null, | ||
build_number: env.BITBUCKET_BUILD_NUMBER | ||
}; | ||
} | ||
// Drone | ||
if (env.CI === 'true' && env.DRONE === 'true') { | ||
return 'Drone'; | ||
if (this.isTrue(env.CI) && this.isTrue(env.DRONE)) { | ||
return { | ||
name: 'Drone', | ||
build_url: env.DRONE_BUILD_LINK, | ||
job_name: null, | ||
build_number: env.DRONE_BUILD_NUMBER | ||
}; | ||
} | ||
// Semaphore | ||
if (env.CI === 'true' && env.SEMAPHORE === 'true') { | ||
return 'Semaphore'; | ||
if (this.isTrue(env.CI) && this.isTrue(env.SEMAPHORE)) { | ||
return { | ||
name: 'Semaphore', | ||
build_url: env.SEMAPHORE_ORGANIZATION_URL, | ||
job_name: env.SEMAPHORE_JOB_NAME, | ||
build_number: env.SEMAPHORE_JOB_ID | ||
}; | ||
} | ||
// GitLab | ||
if (env.CI === 'true' && env.GITLAB_CI === 'true') { | ||
return 'GitLab'; | ||
if (this.isTrue(env.CI) && this.isTrue(env.GITLAB_CI)) { | ||
return { | ||
name: 'GitLab', | ||
build_url: env.CI_JOB_URL, | ||
job_name: env.CI_JOB_NAME, | ||
build_number: env.CI_JOB_ID | ||
}; | ||
} | ||
// Buildkite | ||
if (env.CI === 'true' && env.BUILDKITE === 'true') { | ||
return 'Buildkite'; | ||
if (this.isTrue(env.CI) && this.isTrue(env.BUILDKITE)) { | ||
return { | ||
name: 'Buildkite', | ||
build_url: env.BUILDKITE_BUILD_URL, | ||
job_name: env.BUILDKITE_LABEL || env.BUILDKITE_PIPELINE_NAME, | ||
build_number: env.BUILDKITE_BUILD_NUMBER | ||
}; | ||
} | ||
// Visual Studio Team Services | ||
if (env.TF_BUILD === 'True') { | ||
return 'Visual Studio Team Services'; | ||
if (this.isTrue(env.TF_BUILD)) { | ||
return { | ||
name: 'Visual Studio Team Services', | ||
build_url: `${env.SYSTEM_TEAMFOUNDATIONSERVERURI}${env.SYSTEM_TEAMPROJECTID}`, | ||
job_name: env.SYSTEM_DEFINITIONID, | ||
build_number: env.BUILD_BUILDID | ||
}; | ||
} | ||
}; | ||
// Appveyor | ||
if (this.isTrue(env.APPVEYOR)) { | ||
return { | ||
name: 'Appveyor', | ||
build_url: `${env.APPVEYOR_URL}/project/${env.APPVEYOR_ACCOUNT_NAME}/${env.APPVEYOR_PROJECT_SLUG}/builds/${env.APPVEYOR_BUILD_ID}`, | ||
job_name: env.APPVEYOR_JOB_NAME, | ||
build_number: env.APPVEYOR_BUILD_NUMBER | ||
}; | ||
} | ||
// Azure CI | ||
if (env.AZURE_HTTP_USER_AGENT && env.TF_BUILD) { | ||
return { | ||
name: 'Azure CI', | ||
build_url: `${env.SYSTEM_TEAMFOUNDATIONSERVERURI}${env.SYSTEM_TEAMPROJECT}/_build/results?buildId=${env.BUILD_BUILDID}`, | ||
job_name: env.BUILD_BUILDID, | ||
build_number: env.BUILD_BUILDID | ||
}; | ||
} | ||
// AWS CodeBuild | ||
if (env.CODEBUILD_BUILD_ID || env.CODEBUILD_RESOLVED_SOURCE_VERSION || env.CODEBUILD_SOURCE_VERSION) { | ||
return { | ||
name: 'AWS CodeBuild', | ||
build_url: env.CODEBUILD_PUBLIC_BUILD_URL, | ||
job_name: env.CODEBUILD_BUILD_ID, | ||
build_number: env.CODEBUILD_BUILD_ID | ||
}; | ||
} | ||
// Bamboo | ||
if (env.bamboo_buildNumber) { | ||
return { | ||
name: 'Bamboo', | ||
build_url: env.bamboo_buildResultsUrl, | ||
job_name: env.bamboo_shortJobName, | ||
build_number: env.bamboo_buildNumber | ||
}; | ||
} | ||
// Wercker | ||
if (env.WERCKER || env.WERCKER_MAIN_PIPELINE_STARTED) { | ||
return { | ||
name: 'Wercker', | ||
build_url: env.WERCKER_BUILD_URL, | ||
job_name: env.WERCKER_MAIN_PIPELINE_STARTED ? 'Main Pipeline' : null, | ||
build_number: env.WERCKER_GIT_COMMIT | ||
}; | ||
} | ||
// Google Cloud | ||
if (env.GCP_PROJECT || env.GCLOUD_PROJECT || env.GOOGLE_CLOUD_PROJECT) { | ||
return { | ||
name: 'Google Cloud', | ||
build_url: null, | ||
job_name: env.PROJECT_ID, | ||
build_number: env.BUILD_ID | ||
}; | ||
} | ||
// Shippable | ||
if (env.SHIPPABLE) { | ||
return { | ||
name: 'Shippable', | ||
build_url: env.SHIPPABLE_BUILD_URL, | ||
job_name: env.SHIPPABLE_JOB_ID ? `Job #${env.SHIPPABLE_JOB_ID}` : null, | ||
build_number: env.SHIPPABLE_BUILD_NUMBER | ||
}; | ||
} | ||
// Netlify | ||
if (this.isTrue(env.NETLIFY)) { | ||
return { | ||
name: 'Netlify', | ||
build_url: env.DEPLOY_URL, | ||
job_name: env.SITE_NAME, | ||
build_number: env.BUILD_ID | ||
}; | ||
} | ||
// Github Actions | ||
if (this.isTrue(env.GITHUB_ACTIONS)) { | ||
return { | ||
name: 'GitHub Actions', | ||
build_url: `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`, | ||
job_name: env.GITHUB_WORKFLOW, | ||
build_number: env.GITHUB_RUN_ID | ||
}; | ||
} | ||
// Vercel | ||
if (this.isTrue(env.CI) && env.VERCEL === '1') { | ||
return { | ||
name: 'Vercel', | ||
build_url: `http://${env.VERCEL_URL}`, | ||
job_name: null, | ||
build_number: null | ||
}; | ||
} | ||
// Teamcity | ||
if (env.TEAMCITY_VERSION) { | ||
return { | ||
name: 'Teamcity', | ||
build_url: null, | ||
job_name: null, | ||
build_number: env.BUILD_NUMBER | ||
}; | ||
} | ||
// Concourse | ||
if (env.CONCOURSE || env.CONCOURSE_URL || env.CONCOURSE_USERNAME || env.CONCOURSE_TEAM) { | ||
return { | ||
name: 'Concourse', | ||
build_url: null, | ||
job_name: env.BUILD_JOB_NAME || null, | ||
build_number: env.BUILD_ID || null | ||
}; | ||
} | ||
// GoCD | ||
if (env.GO_JOB_NAME) { | ||
return { | ||
name: 'GoCD', | ||
build_url: null, | ||
job_name: env.GO_JOB_NAME, | ||
build_number: env.GO_PIPELINE_COUNTER | ||
}; | ||
} | ||
// CodeFresh | ||
if (env.CF_BUILD_ID) { | ||
return { | ||
name: 'CodeFresh', | ||
build_url: env.CF_BUILD_URL, | ||
job_name: env.CF_PIPELINE_NAME, | ||
build_number: env.CF_BUILD_ID | ||
}; | ||
} | ||
// if no matches, return null | ||
exports.getCiInfo = () => { | ||
var env = process.env; | ||
const ciVendor = this.getCIVendor(); | ||
switch (ciVendor) { | ||
case 'Jenkins': | ||
return { | ||
name: 'Jenkins', | ||
build_url: env.BUILD_URL, | ||
job_name: env.JOB_NAME, | ||
build_number: env.BUILD_NUMBER | ||
}; | ||
case 'CircleCI': | ||
return { | ||
name: 'CircleCI', | ||
build_url: env.CIRCLE_BUILD_URL, | ||
job_name: env.CIRCLE_JOB, | ||
build_number: env.CIRCLE_BUILD_NUM | ||
}; | ||
case 'TravisCI': | ||
return { | ||
name: 'Travis CI', | ||
build_url: env.TRAVIS_BUILD_WEB_URL, | ||
job_name: env.TRAVIS_JOB_NAME, | ||
build_number: env.TRAVIS_BUILD_NUMBER | ||
}; | ||
case 'Codeship': | ||
return { | ||
name: 'Codeship', | ||
build_url: null, | ||
job_name: null, | ||
build_number: null | ||
}; | ||
case 'Bitbucket': | ||
return { | ||
name: 'Bitbucket', | ||
build_url: env.BITBUCKET_GIT_HTTP_ORIGIN, | ||
job_name: null, | ||
build_number: env.BITBUCKET_BUILD_NUMBER | ||
}; | ||
case 'Drone': | ||
return { | ||
name: 'Drone', | ||
build_url: env.DRONE_BUILD_LINK, | ||
job_name: null, | ||
build_number: env.DRONE_BUILD_NUMBER | ||
}; | ||
case 'Semaphore': | ||
return { | ||
name: 'Semaphore', | ||
build_url: env.SEMAPHORE_ORGANIZATION_URL, | ||
job_name: env.SEMAPHORE_JOB_NAME, | ||
build_number: env.SEMAPHORE_JOB_ID | ||
}; | ||
case 'GitLab': | ||
return { | ||
name: 'GitLab', | ||
build_url: env.CI_JOB_URL, | ||
job_name: env.CI_JOB_NAME, | ||
build_number: env.CI_JOB_ID | ||
}; | ||
case 'Buildkite': | ||
return { | ||
name: 'Buildkite', | ||
build_url: env.BUILDKITE_BUILD_URL, | ||
job_name: env.BUILDKITE_LABEL || env.BUILDKITE_PIPELINE_NAME, | ||
build_number: env.BUILDKITE_BUILD_NUMBER | ||
}; | ||
case 'Visual Studio Team Services': | ||
return { | ||
name: 'Visual Studio Team Services', | ||
build_url: `${env.SYSTEM_TEAMFOUNDATIONSERVERURI}${env.SYSTEM_TEAMPROJECTID}`, | ||
job_name: env.SYSTEM_DEFINITIONID, | ||
build_number: env.BUILD_BUILDID | ||
}; | ||
default: | ||
return null; | ||
} | ||
return { | ||
name: null, | ||
build_number: null | ||
}; | ||
}; | ||
@@ -452,3 +580,3 @@ | ||
exports.getCloudProvider = (hostname) => { | ||
if (hostname.includes('browserstack')) { | ||
if (hostname && hostname.includes('browserstack')) { | ||
return 'browserstack'; | ||
@@ -517,6 +645,46 @@ } | ||
exports.getScenarioExamples = (gherkinDocument, scenario) => { | ||
if (!(scenario.astNodeIds?.length > 1)) { | ||
return; | ||
} | ||
const pickleId = scenario.astNodeIds[0]; | ||
const examplesId = scenario.astNodeIds[1]; | ||
const gherkinDocumentChildren = gherkinDocument.feature?.children; | ||
let examples = []; | ||
gherkinDocumentChildren?.forEach(child => { | ||
if (child.rule) { | ||
child.rule.children.forEach(childLevel2 => { | ||
if (childLevel2.scenario && childLevel2.scenario.id === pickleId && childLevel2.scenario.examples) { | ||
const passedExamples = childLevel2.scenario.examples.flatMap((val) => (val.tableBody)).find((item) => item.id === examplesId)?.cells.map((val) => (val.value)); | ||
if (passedExamples) { | ||
examples = passedExamples; | ||
} | ||
} | ||
}); | ||
} else if (child.scenario && child.scenario.id === pickleId && child.scenario.examples) { | ||
const passedExamples = child.scenario.examples.flatMap((val) => (val.tableBody)).find((item) => item.id === examplesId)?.cells.map((val) => (val.value)); | ||
if (passedExamples) { | ||
examples = passedExamples; | ||
} | ||
} | ||
}); | ||
if (examples.length) { | ||
return examples; | ||
} | ||
return; | ||
}; | ||
exports.isCucumberTestSuite = (settings) => { | ||
return settings?.test_runner?.type === 'cucumber'; | ||
}; | ||
exports.getPlatformVersion = (driver) => { | ||
let platformVersion = null; | ||
try { | ||
let caps = driver.desiredCapabilities || {}; | ||
const caps = driver.desiredCapabilities || {}; | ||
if (!this.isUndefined(caps['bstack:options']) && !this.isUndefined(caps['bstack:options']['osVersion'])){ | ||
@@ -523,0 +691,0 @@ platformVersion = caps['bstack:options']['osVersion']; |
@@ -9,3 +9,3 @@ const {API_URL, SCREENSHOT_EVENT_URL} = require('./constants'); | ||
keepAlive: true, | ||
timeout: 60000, | ||
timeout: 45000, | ||
maxSockets: 2, | ||
@@ -21,3 +21,3 @@ maxTotalSockets: 2 | ||
exports.makeRequest = (type, url, data, config, requestUrl=API_URL) => { | ||
exports.makeRequest = (type, url, data, config, requestUrl=API_URL, jsonResponse = true) => { | ||
const isHttps = requestUrl.includes('https'); | ||
@@ -47,6 +47,8 @@ let agent; | ||
} else { | ||
try { | ||
if (body && typeof(body) !== 'object') {body = JSON.parse(body)} | ||
} catch (e) { | ||
reject('Not a JSON response from BrowserStack Server'); | ||
if (jsonResponse) { | ||
try { | ||
if (body && typeof(body) !== 'object') {body = JSON.parse(body)} | ||
} catch (e) { | ||
reject('Not a JSON response from BrowserStack Server'); | ||
} | ||
} | ||
@@ -53,0 +55,0 @@ resolve({ |
@@ -13,3 +13,3 @@ const {BATCH_SIZE, BATCH_INTERVAL} = require('./constants'); | ||
this.pollEventBatchInterval = null; | ||
RequestQueueHandler.pending_test_uploads = 0; | ||
this.pending_test_uploads = 0; | ||
} | ||
@@ -35,3 +35,4 @@ | ||
this.queue.push(event); | ||
let data = null; const shouldProceed = this.shouldProceed(); | ||
let data = null; | ||
const shouldProceed = this.shouldProceed(); | ||
if (shouldProceed) { | ||
@@ -38,0 +39,0 @@ data = this.queue.slice(0, BATCH_SIZE); |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
95412
17
2336
8
84
+ Addedwinston-transport@^4.5.0
+ Added@colors/colors@1.6.0(transitive)
+ Added@types/triple-beam@1.3.5(transitive)
+ Addedfecha@4.2.3(transitive)
+ Addedlogform@2.7.0(transitive)
+ Addedreadable-stream@3.6.2(transitive)
+ Addedsafe-stable-stringify@2.5.0(transitive)
+ Addedstring_decoder@1.3.0(transitive)
+ Addedtriple-beam@1.4.1(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedwinston-transport@4.9.0(transitive)