Comparing version 5.18.0 to 5.19.0
{ | ||
"name": "dd-trace", | ||
"version": "5.18.0", | ||
"version": "5.19.0", | ||
"description": "Datadog APM tracing client for JavaScript", | ||
@@ -48,5 +48,3 @@ "main": "index.js", | ||
"test:shimmer": "mocha 'packages/datadog-shimmer/test/**/*.spec.js'", | ||
"test:shimmer:ci": "nyc --no-clean --include 'packages/datadog-shimmer/src/**/*.js' -- npm run test:shimmer", | ||
"leak:core": "node ./scripts/install_plugin_modules && (cd packages/memwatch && yarn) && NODE_PATH=./packages/memwatch/node_modules node --no-warnings ./node_modules/.bin/tape 'packages/dd-trace/test/leak/**/*.js'", | ||
"leak:plugins": "yarn services && (cd packages/memwatch && yarn) && NODE_PATH=./packages/memwatch/node_modules node --no-warnings ./node_modules/.bin/tape \"packages/datadog-plugin-@($(echo $PLUGINS))/test/leak.js\"" | ||
"test:shimmer:ci": "nyc --no-clean --include 'packages/datadog-shimmer/src/**/*.js' -- npm run test:shimmer" | ||
}, | ||
@@ -120,9 +118,8 @@ "repository": { | ||
"esbuild": "0.16.12", | ||
"eslint": "^8.23.0", | ||
"eslint": "^8.57.0", | ||
"eslint-config-standard": "^17.1.0", | ||
"eslint-plugin-import": "^2.8.0", | ||
"eslint-plugin-mocha": "^10.1.0", | ||
"eslint-plugin-n": "^15.7.0", | ||
"eslint-plugin-promise": "^3.6.0", | ||
"eslint-plugin-standard": "^3.0.1", | ||
"eslint-plugin-import": "^2.29.1", | ||
"eslint-plugin-mocha": "^10.4.3", | ||
"eslint-plugin-n": "^16.6.2", | ||
"eslint-plugin-promise": "^6.4.0", | ||
"express": "^4.18.2", | ||
@@ -144,5 +141,4 @@ "get-port": "^3.2.0", | ||
"tap": "^16.3.7", | ||
"tape": "^5.6.5", | ||
"tiktoken": "^1.0.15" | ||
} | ||
} |
@@ -42,6 +42,6 @@ 'use strict' | ||
childProcessInfo.command = childProcessInfo.command + ' ' + args[1].join(' ') | ||
if (args[2] != null && typeof args[2] === 'object') { | ||
if (args[2] !== null && typeof args[2] === 'object') { | ||
childProcessInfo.options = args[2] | ||
} | ||
} else if (args[1] != null && typeof args[1] === 'object') { | ||
} else if (args[1] !== null && typeof args[1] === 'object') { | ||
childProcessInfo.options = args[1] | ||
@@ -48,0 +48,0 @@ } |
@@ -275,3 +275,3 @@ 'use strict' | ||
arguments[lastIndex] = innerResource.bind(function (e) { | ||
if (typeof e === 'object') { // fs.exists receives a boolean | ||
if (e !== null && typeof e === 'object') { // fs.exists receives a boolean | ||
errorChannel.publish(e) | ||
@@ -278,0 +278,0 @@ } |
@@ -41,3 +41,3 @@ 'use strict' | ||
return function (events, method, options) { | ||
if (typeof events === 'object') { | ||
if (events !== null && typeof events === 'object') { | ||
arguments[0] = wrapEvents(events) | ||
@@ -44,0 +44,0 @@ } else { |
@@ -135,3 +135,3 @@ 'use strict' | ||
function combineOptions (inputURL, inputOptions) { | ||
if (typeof inputOptions === 'object') { | ||
if (inputOptions !== null && typeof inputOptions === 'object') { | ||
return Object.assign(inputURL || {}, inputOptions) | ||
@@ -138,0 +138,0 @@ } else { |
@@ -15,3 +15,4 @@ 'use strict' | ||
removeEfdStringFromTestName, | ||
getIsFaultyEarlyFlakeDetection | ||
getIsFaultyEarlyFlakeDetection, | ||
NUM_FAILED_TEST_RETRIES | ||
} = require('../../dd-trace/src/plugins/util/test') | ||
@@ -53,2 +54,5 @@ const { | ||
const FLUSH_TIMEOUT = 10000 | ||
// eslint-disable-next-line | ||
// https://github.com/jestjs/jest/blob/41f842a46bb2691f828c3a5f27fc1d6290495b82/packages/jest-circus/src/types.ts#L9C8-L9C54 | ||
const RETRY_TIMES = Symbol.for('RETRY_TIMES') | ||
@@ -132,2 +136,3 @@ let skippableSuites = [] | ||
this.isEarlyFlakeDetectionEnabled = this.testEnvironmentOptions._ddIsEarlyFlakeDetectionEnabled | ||
this.isFlakyTestRetriesEnabled = this.testEnvironmentOptions._ddIsFlakyTestRetriesEnabled | ||
@@ -146,2 +151,9 @@ if (this.isEarlyFlakeDetectionEnabled) { | ||
} | ||
if (this.isFlakyTestRetriesEnabled) { | ||
const currentNumRetries = this.global[RETRY_TIMES] | ||
if (!currentNumRetries) { | ||
this.global[RETRY_TIMES] = NUM_FAILED_TEST_RETRIES | ||
} | ||
} | ||
} | ||
@@ -225,2 +237,3 @@ | ||
} | ||
const isJestRetry = event.test?.invocations > 1 | ||
asyncResource.runInAsyncScope(() => { | ||
@@ -236,3 +249,4 @@ testStartCh.publish({ | ||
isNew: isNewTest, | ||
isEfdRetry: numEfdRetry > 0 | ||
isEfdRetry: numEfdRetry > 0, | ||
isJestRetry | ||
}) | ||
@@ -767,2 +781,3 @@ originalTestFns.set(event.test, event.test.fn) | ||
_ddRepositoryRoot, | ||
_ddIsFlakyTestRetriesEnabled, | ||
...restOfTestEnvironmentOptions | ||
@@ -769,0 +784,0 @@ } = testEnvironmentOptions |
@@ -62,3 +62,3 @@ 'use strict' | ||
for (const message of messages) { | ||
if (typeof message === 'object') { | ||
if (message !== null && typeof message === 'object') { | ||
message.headers = message.headers || {} | ||
@@ -65,0 +65,0 @@ } |
@@ -64,3 +64,3 @@ 'use strict' | ||
filter = options | ||
} else if (typeof options === 'object' && options.filter) { | ||
} else if (options !== null && typeof options === 'object' && options.filter) { | ||
if (isString(options.filter)) { | ||
@@ -82,3 +82,3 @@ filter = options.filter | ||
arguments[callbackIndex] = shimmer.wrap(callback, function (err, corkedEmitter) { | ||
if (typeof corkedEmitter === 'object' && typeof corkedEmitter.on === 'function') { | ||
if (corkedEmitter !== null && typeof corkedEmitter === 'object' && typeof corkedEmitter.on === 'function') { | ||
wrapEmitter(corkedEmitter) | ||
@@ -85,0 +85,0 @@ } |
@@ -28,5 +28,5 @@ 'use strict' | ||
const filters = arg0 && typeof arg0 === 'object' ? [arg0] : [] | ||
const filters = arg0 !== null && typeof arg0 === 'object' ? [arg0] : [] | ||
if (arg1 && typeof arg1 === 'object' && methodsOptionalArgs.includes(methodName)) { | ||
if (arg1 !== null && typeof arg1 === 'object' && methodsOptionalArgs.includes(methodName)) { | ||
filters.push(arg1) | ||
@@ -33,0 +33,0 @@ } |
@@ -49,3 +49,3 @@ 'use strict' | ||
return instrument(req, res, () => { | ||
const page = (typeof match === 'object' && typeof match.definition === 'object') | ||
const page = (match !== null && typeof match === 'object' && typeof match.definition === 'object') | ||
? match.definition.pathname | ||
@@ -52,0 +52,0 @@ : undefined |
@@ -38,3 +38,3 @@ 'use strict' | ||
const pgQuery = arguments[0] && typeof arguments[0] === 'object' | ||
const pgQuery = arguments[0] !== null && typeof arguments[0] === 'object' | ||
? arguments[0] | ||
@@ -113,3 +113,3 @@ : { text: arguments[0] } | ||
const pgQuery = arguments[0] && typeof arguments[0] === 'object' ? arguments[0] : { text: arguments[0] } | ||
const pgQuery = arguments[0] !== null && typeof arguments[0] === 'object' ? arguments[0] : { text: arguments[0] } | ||
@@ -116,0 +116,0 @@ return asyncResource.runInAsyncScope(() => { |
@@ -5,3 +5,3 @@ const semver = require('semver') | ||
const shimmer = require('../../datadog-shimmer') | ||
const { parseAnnotations, getTestSuitePath } = require('../../dd-trace/src/plugins/util/test') | ||
const { parseAnnotations, getTestSuitePath, NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test') | ||
const log = require('../../dd-trace/src/log') | ||
@@ -25,2 +25,3 @@ | ||
const testSuiteToErrors = new Map() | ||
const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn') | ||
@@ -40,2 +41,3 @@ let applyRepeatEachIndex = null | ||
let isEarlyFlakeDetectionEnabled = false | ||
let isFlakyTestRetriesEnabled = false | ||
let earlyFlakeDetectionNumRetries = 0 | ||
@@ -202,2 +204,27 @@ let knownTests = {} | ||
function getTestByTestId (dispatcher, testId) { | ||
if (dispatcher._testById) { | ||
return dispatcher._testById.get(testId)?.test | ||
} | ||
const allTests = dispatcher._allTests || dispatcher._ddAllTests | ||
if (allTests) { | ||
return allTests.find(({ id }) => id === testId) | ||
} | ||
} | ||
function getChannelPromise (channelToPublishTo) { | ||
return new Promise(resolve => { | ||
testSessionAsyncResource.runInAsyncScope(() => { | ||
channelToPublishTo.publish({ onDone: resolve }) | ||
}) | ||
}) | ||
} | ||
// eslint-disable-next-line | ||
// Inspired by https://github.com/microsoft/playwright/blob/2b77ed4d7aafa85a600caa0b0d101b72c8437eeb/packages/playwright/src/reporters/base.ts#L293 | ||
// We can't use test.outcome() directly because it's set on follow up handlers: | ||
// our `testEndHandler` is called before the outcome is set. | ||
function testWillRetry (test, testStatus) { | ||
return testStatus === 'fail' && test.results.length <= test.retries | ||
} | ||
function testBeginHandler (test, browserName) { | ||
@@ -257,2 +284,3 @@ const { | ||
steps: testResult?.steps || [], | ||
isRetry: testResult?.retry > 0, | ||
error, | ||
@@ -275,4 +303,6 @@ extraTags: annotationTags, | ||
remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath] | ||
.filter(currentTest => currentTest !== test) | ||
if (!testWillRetry(test, testStatus)) { | ||
remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath] | ||
.filter(currentTest => currentTest !== test) | ||
} | ||
@@ -344,12 +374,2 @@ // Last test, we finish the suite | ||
function getTestByTestId (dispatcher, testId) { | ||
if (dispatcher._testById) { | ||
return dispatcher._testById.get(testId)?.test | ||
} | ||
const allTests = dispatcher._allTests || dispatcher._ddAllTests | ||
if (allTests) { | ||
return allTests.find(({ id }) => id === testId) | ||
} | ||
} | ||
function dispatcherHookNew (dispatcherExport, runWrapper) { | ||
@@ -383,4 +403,2 @@ shimmer.wrap(dispatcherExport.Dispatcher.prototype, 'run', runWrapper) | ||
const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn') | ||
rootDir = getRootDir(this) | ||
@@ -394,17 +412,11 @@ | ||
const configurationPromise = new Promise((resolve) => { | ||
onDone = resolve | ||
}) | ||
testSessionAsyncResource.runInAsyncScope(() => { | ||
libraryConfigurationCh.publish({ onDone }) | ||
}) | ||
try { | ||
const { err, libraryConfig } = await configurationPromise | ||
const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh) | ||
if (!err) { | ||
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled | ||
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries | ||
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled | ||
} | ||
} catch (e) { | ||
isEarlyFlakeDetectionEnabled = false | ||
log.error(e) | ||
@@ -414,11 +426,4 @@ } | ||
if (isEarlyFlakeDetectionEnabled && semver.gte(playwrightVersion, MINIMUM_SUPPORTED_VERSION_EFD)) { | ||
const knownTestsPromise = new Promise((resolve) => { | ||
onDone = resolve | ||
}) | ||
testSessionAsyncResource.runInAsyncScope(() => { | ||
knownTestsCh.publish({ onDone }) | ||
}) | ||
try { | ||
const { err, knownTests: receivedKnownTests } = await knownTestsPromise | ||
const { err, knownTests: receivedKnownTests } = await getChannelPromise(knownTestsCh) | ||
if (!err) { | ||
@@ -430,2 +435,3 @@ knownTests = receivedKnownTests | ||
} catch (err) { | ||
isEarlyFlakeDetectionEnabled = false | ||
log.error(err) | ||
@@ -437,2 +443,10 @@ } | ||
if (isFlakyTestRetriesEnabled) { | ||
projects.forEach(project => { | ||
if (project.retries === 0) { // Only if it hasn't been set by the user | ||
project.retries = NUM_FAILED_TEST_RETRIES | ||
} | ||
}) | ||
} | ||
const runAllTestsReturn = await runAllTests.apply(this, arguments) | ||
@@ -439,0 +453,0 @@ |
@@ -54,3 +54,3 @@ 'use strict' | ||
const result = fn.apply(this, arguments) | ||
if (result && typeof result === 'object' && typeof result.then === 'function') { | ||
if (result !== null && typeof result === 'object' && typeof result.then === 'function') { | ||
return result.then(function () { | ||
@@ -57,0 +57,0 @@ nextChannel.publish({ req }) |
const { addHook, channel, AsyncResource } = require('./helpers/instrument') | ||
const shimmer = require('../../datadog-shimmer') | ||
const { NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test') | ||
@@ -19,2 +20,3 @@ // test hooks | ||
const testSessionFinishCh = channel('ci:vitest:session:finish') | ||
const libraryConfigurationCh = channel('ci:vitest:library-configuration') | ||
@@ -34,2 +36,10 @@ const taskToAsync = new WeakMap() | ||
function getChannelPromise (channelToPublishTo) { | ||
return new Promise(resolve => { | ||
sessionAsyncResource.runInAsyncScope(() => { | ||
channelToPublishTo.publish({ onDone: resolve }) | ||
}) | ||
}) | ||
} | ||
function getSessionStatus (state) { | ||
@@ -95,2 +105,19 @@ if (state.getCountOfFailedTests() > 0) { | ||
} | ||
// There isn't any other async function that we seem to be able to hook into | ||
// So we will use the sort from BaseSequencer. This means that a custom sequencer | ||
// will not work. This will be a known limitation. | ||
let isFlakyTestRetriesEnabled = false | ||
try { | ||
const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh) | ||
if (!err) { | ||
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled | ||
} | ||
} catch (e) { | ||
isFlakyTestRetriesEnabled = false | ||
} | ||
if (isFlakyTestRetriesEnabled && !this.ctx.config.retry) { | ||
this.ctx.config.retry = NUM_FAILED_TEST_RETRIES | ||
} | ||
shimmer.wrap(this.ctx, 'exit', exit => async function () { | ||
@@ -132,6 +159,18 @@ let onFinish | ||
// test start (only tests that are not marked as skip or todo) | ||
shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task) { | ||
shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task, retryInfo) { | ||
if (!testStartCh.hasSubscribers) { | ||
return onBeforeTryTask.apply(this, arguments) | ||
} | ||
const { retry: numAttempt } = retryInfo | ||
// We finish the previous test here because we know it has failed already | ||
if (numAttempt > 0) { | ||
const asyncResource = taskToAsync.get(task) | ||
const testError = task.result?.errors?.[0] | ||
if (asyncResource) { | ||
asyncResource.runInAsyncScope(() => { | ||
testErrorCh.publish({ error: testError }) | ||
}) | ||
} | ||
} | ||
const asyncResource = new AsyncResource('bound-anonymous-fn') | ||
@@ -141,3 +180,7 @@ taskToAsync.set(task, asyncResource) | ||
asyncResource.runInAsyncScope(() => { | ||
testStartCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.suite.file.filepath }) | ||
testStartCh.publish({ | ||
testName: getTestName(task), | ||
testSuiteAbsolutePath: task.file.filepath, | ||
isRetry: numAttempt > 0 | ||
}) | ||
}) | ||
@@ -243,2 +286,3 @@ return onBeforeTryTask.apply(this, arguments) | ||
// Only one test task per test, even if there are retries | ||
testTasks.forEach(task => { | ||
@@ -251,3 +295,3 @@ const testAsyncResource = taskToAsync.get(task) | ||
if (state === 'skip') { // programmatic skip | ||
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.suite.file.filepath }) | ||
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.file.filepath }) | ||
} else if (state === 'pass') { | ||
@@ -268,4 +312,6 @@ if (testAsyncResource) { | ||
if (testAsyncResource) { | ||
const isRetry = task.result?.retryCount > 0 | ||
// `duration` is the duration of all the retries, so it can't be used if there are retries | ||
testAsyncResource.runInAsyncScope(() => { | ||
testErrorCh.publish({ duration, error: testError }) | ||
testErrorCh.publish({ duration: !isRetry ? duration : undefined, error: testError }) | ||
}) | ||
@@ -278,3 +324,3 @@ } | ||
} else { // test.skip or test.todo | ||
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.suite.file.filepath }) | ||
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.file.filepath }) | ||
} | ||
@@ -281,0 +327,0 @@ }) |
@@ -76,3 +76,3 @@ 'use strict' | ||
const operation = response.request.operation | ||
this.responseExtractDSMContext(operation, params, response.data, span) | ||
this.responseExtractDSMContext(operation, params, response.data ?? response, span) | ||
} | ||
@@ -79,0 +79,0 @@ this.addResponseTags(span, response) |
@@ -24,3 +24,3 @@ 'use strict' | ||
// dynamoDB batch TableName | ||
if (params.RequestItems) { | ||
if (params.RequestItems !== null) { | ||
if (typeof params.RequestItems === 'object') { | ||
@@ -27,0 +27,0 @@ if (Object.keys(params.RequestItems).length === 1) { |
@@ -50,3 +50,3 @@ 'use strict' | ||
const inputObj = JSON.parse(input) | ||
if (inputObj && typeof inputObj === 'object') { | ||
if (inputObj !== null && typeof inputObj === 'object') { | ||
// We've parsed the input JSON string | ||
@@ -53,0 +53,0 @@ inputObj._datadog = {} |
@@ -10,6 +10,6 @@ 'use strict' | ||
// eslint-disable-next-line max-len | ||
const PARAM_PATTERN = '^-{0,2}(?:p(?:ass(?:w(?:or)?d)?)?|api_?key|secret|a(?:ccess|uth)_token|mysql_pwd|credentials|(?:stripe)?token)$' | ||
const PARAM_PATTERN = '^-{0,2}(?:p(?:ass(?:w(?:or)?d)?)?|address|api[-_]?key|e?mail|secret(?:[-_]?key)?|a(?:ccess|uth)[-_]?token|mysql_pwd|credentials|(?:stripe)?token)$' | ||
const regexParam = new RegExp(PARAM_PATTERN, 'i') | ||
const ENV_PATTERN = '^(\\w+=\\w+;)*\\w+=\\w+;?$' | ||
const envvarRegex = new RegExp(ENV_PATTERN) | ||
const envVarRegex = new RegExp(ENV_PATTERN) | ||
const REDACTED = '?' | ||
@@ -65,3 +65,5 @@ | ||
if (typeof token === 'object') { | ||
if (token === null) { | ||
continue | ||
} else if (typeof token === 'object') { | ||
if (token.pattern) { | ||
@@ -75,3 +77,3 @@ result.push(token.pattern) | ||
} else if (!foundBinary) { | ||
if (envvarRegex.test(token)) { | ||
if (envVarRegex.test(token)) { | ||
const envSplit = token.split('=') | ||
@@ -78,0 +80,0 @@ |
@@ -31,3 +31,4 @@ const { | ||
TEST_IS_RETRY, | ||
TEST_EARLY_FLAKE_ENABLED | ||
TEST_EARLY_FLAKE_ENABLED, | ||
NUM_FAILED_TEST_RETRIES | ||
} = require('../../dd-trace/src/plugins/util/test') | ||
@@ -211,2 +212,5 @@ const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util') | ||
// Init function returns a promise that resolves with the Cypress configuration | ||
// Depending on the received configuration, the Cypress configuration can be modified: | ||
// for example, to enable retries for failed tests. | ||
init (tracer, cypressConfig) { | ||
@@ -216,2 +220,29 @@ this._isInit = true | ||
this.cypressConfig = cypressConfig | ||
this.libraryConfigurationPromise = getLibraryConfiguration(this.tracer, this.testConfiguration) | ||
.then((libraryConfigurationResponse) => { | ||
if (libraryConfigurationResponse.err) { | ||
log.error(libraryConfigurationResponse.err) | ||
} else { | ||
const { | ||
libraryConfig: { | ||
isSuitesSkippingEnabled, | ||
isCodeCoverageEnabled, | ||
isEarlyFlakeDetectionEnabled, | ||
earlyFlakeDetectionNumRetries, | ||
isFlakyTestRetriesEnabled | ||
} | ||
} = libraryConfigurationResponse | ||
this.isSuitesSkippingEnabled = isSuitesSkippingEnabled | ||
this.isCodeCoverageEnabled = isCodeCoverageEnabled | ||
this.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled | ||
this.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries | ||
this.isFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled | ||
if (this.isFlakyTestRetriesEnabled) { | ||
this.cypressConfig.retries.runMode = NUM_FAILED_TEST_RETRIES | ||
} | ||
} | ||
return this.cypressConfig | ||
}) | ||
return this.libraryConfigurationPromise | ||
} | ||
@@ -303,2 +334,5 @@ | ||
async beforeRun (details) { | ||
// We need to make sure that the plugin is initialized before running the tests | ||
// This is for the case where the user has not returned the promise from the init function | ||
await this.libraryConfigurationPromise | ||
this.command = getCypressCommand(details) | ||
@@ -308,21 +342,2 @@ this.frameworkVersion = getCypressVersion(details) | ||
const libraryConfigurationResponse = await getLibraryConfiguration(this.tracer, this.testConfiguration) | ||
if (libraryConfigurationResponse.err) { | ||
log.error(libraryConfigurationResponse.err) | ||
} else { | ||
const { | ||
libraryConfig: { | ||
isSuitesSkippingEnabled, | ||
isCodeCoverageEnabled, | ||
isEarlyFlakeDetectionEnabled, | ||
earlyFlakeDetectionNumRetries | ||
} | ||
} = libraryConfigurationResponse | ||
this.isSuitesSkippingEnabled = isSuitesSkippingEnabled | ||
this.isCodeCoverageEnabled = isCodeCoverageEnabled | ||
this.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled | ||
this.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries | ||
} | ||
if (this.isEarlyFlakeDetectionEnabled) { | ||
@@ -493,25 +508,47 @@ const knownTestsResponse = await getKnownTests( | ||
let latestError | ||
finishedTests.forEach((finishedTest) => { | ||
const cypressTest = cypressTests.find(test => test.title.join(' ') === finishedTest.testName) | ||
if (!cypressTest) { | ||
return | ||
const finishedTestsByTestName = finishedTests.reduce((acc, finishedTest) => { | ||
if (!acc[finishedTest.testName]) { | ||
acc[finishedTest.testName] = [] | ||
} | ||
if (cypressTest.displayError) { | ||
latestError = new Error(cypressTest.displayError) | ||
} | ||
const cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state] | ||
// update test status | ||
if (cypressTestStatus !== finishedTest.testStatus) { | ||
finishedTest.testSpan.setTag(TEST_STATUS, cypressTestStatus) | ||
finishedTest.testSpan.setTag('error', latestError) | ||
} | ||
if (this.itrCorrelationId) { | ||
finishedTest.testSpan.setTag(ITR_CORRELATION_ID, this.itrCorrelationId) | ||
} | ||
if (spec.absolute && this.repositoryRoot) { | ||
finishedTest.testSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, this.repositoryRoot)) | ||
} else { | ||
finishedTest.testSpan.setTag(TEST_SOURCE_FILE, spec.relative) | ||
} | ||
finishedTest.testSpan.finish(finishedTest.finishTime) | ||
acc[finishedTest.testName].push(finishedTest) | ||
return acc | ||
}, {}) | ||
Object.entries(finishedTestsByTestName).forEach(([testName, finishedTestAttempts]) => { | ||
finishedTestAttempts.forEach((finishedTest, attemptIndex) => { | ||
// TODO: there could be multiple if there have been retries! | ||
// potentially we need to match the test status! | ||
const cypressTest = cypressTests.find(test => test.title.join(' ') === testName) | ||
if (!cypressTest) { | ||
return | ||
} | ||
// finishedTests can include multiple tests with the same name if they have been retried | ||
// by early flake detection. Cypress is unaware of this so .attempts does not necessarily have | ||
// the same length as `finishedTestAttempts` | ||
let cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state] | ||
if (cypressTest.attempts && cypressTest.attempts[attemptIndex]) { | ||
cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.attempts[attemptIndex].state] | ||
if (attemptIndex > 0) { | ||
finishedTest.testSpan.setTag(TEST_IS_RETRY, 'true') | ||
} | ||
} | ||
if (cypressTest.displayError) { | ||
latestError = new Error(cypressTest.displayError) | ||
} | ||
// Update test status | ||
if (cypressTestStatus !== finishedTest.testStatus) { | ||
finishedTest.testSpan.setTag(TEST_STATUS, cypressTestStatus) | ||
finishedTest.testSpan.setTag('error', latestError) | ||
} | ||
if (this.itrCorrelationId) { | ||
finishedTest.testSpan.setTag(ITR_CORRELATION_ID, this.itrCorrelationId) | ||
} | ||
if (spec.absolute && this.repositoryRoot) { | ||
finishedTest.testSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, this.repositoryRoot)) | ||
} else { | ||
finishedTest.testSpan.setTag(TEST_SOURCE_FILE, spec.relative) | ||
} | ||
finishedTest.testSpan.finish(finishedTest.finishTime) | ||
}) | ||
}) | ||
@@ -518,0 +555,0 @@ |
@@ -25,7 +25,6 @@ const NoopTracer = require('../../dd-trace/src/noop/tracer') | ||
// We still need to register these tasks or the support file will fail | ||
return on('task', noopTask) | ||
on('task', noopTask) | ||
return config | ||
} | ||
cypressPlugin.init(tracer, config) | ||
on('before:run', cypressPlugin.beforeRun.bind(cypressPlugin)) | ||
@@ -35,2 +34,4 @@ on('after:spec', cypressPlugin.afterSpec.bind(cypressPlugin)) | ||
on('task', cypressPlugin.getTasks()) | ||
return cypressPlugin.init(tracer, config) | ||
} |
@@ -32,3 +32,3 @@ 'use strict' | ||
meta: { | ||
'file.descriptor': (typeof fd === 'object' || typeof fd === 'number') ? fd.toString() : '', | ||
'file.descriptor': ((fd !== null && typeof fd === 'object') || typeof fd === 'number') ? fd.toString() : '', | ||
'file.dest': params.dest || params.newPath || (params.target && params.path), | ||
@@ -35,0 +35,0 @@ 'file.flag': String(flag || defaultFlag || ''), |
@@ -151,2 +151,3 @@ const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin') | ||
config._ddRepositoryRoot = this.repositoryRoot | ||
config._ddIsFlakyTestRetriesEnabled = this.libraryConfig?.isFlakyTestRetriesEnabled ?? false | ||
}) | ||
@@ -328,3 +329,4 @@ }) | ||
isNew, | ||
isEfdRetry | ||
isEfdRetry, | ||
isJestRetry | ||
} = test | ||
@@ -354,2 +356,6 @@ | ||
if (isJestRetry) { | ||
extraTags[TEST_IS_RETRY] = 'true' | ||
} | ||
return super.startTestSpan(name, suite, this.testSuiteSpan, extraTags) | ||
@@ -356,0 +362,0 @@ } |
@@ -84,3 +84,3 @@ 'use strict' | ||
for (const message of messages) { | ||
if (typeof message === 'object') { | ||
if (message !== null && typeof message === 'object') { | ||
this.tracer.inject(span, 'text_map', message.headers) | ||
@@ -87,0 +87,0 @@ if (this.config.dsmEnabled) { |
@@ -118,3 +118,3 @@ 'use strict' | ||
function isObject (val) { | ||
return typeof val === 'object' && val !== null && !(val instanceof Array) | ||
return val !== null && typeof val === 'object' && !Array.isArray(val) | ||
} | ||
@@ -121,0 +121,0 @@ |
@@ -121,3 +121,3 @@ 'use strict' | ||
// createChatCompletion, createCompletion | ||
if (typeof payload.logit_bias === 'object' && payload.logit_bias) { | ||
if (payload.logit_bias !== null && typeof payload.logit_bias === 'object') { | ||
for (const [tokenId, bias] of Object.entries(payload.logit_bias)) { | ||
@@ -431,3 +431,3 @@ tags[`openai.request.logit_bias.${tokenId}`] = bias | ||
const img = payload.file || payload.image | ||
if (img && typeof img === 'object' && img.path) { | ||
if (img !== null && typeof img === 'object' && img.path) { | ||
const file = path.basename(img.path) | ||
@@ -439,3 +439,3 @@ tags['openai.request.image'] = file | ||
// createImageEdit | ||
if (payload.mask && typeof payload.mask === 'object' && payload.mask.path) { | ||
if (payload.mask !== null && typeof payload.mask === 'object' && payload.mask.path) { | ||
const mask = path.basename(payload.mask.path) | ||
@@ -639,3 +639,3 @@ tags['openai.request.mask'] = mask | ||
if (body.file && typeof body.file === 'object' && body.file.path) { | ||
if (body.file !== null && typeof body.file === 'object' && body.file.path) { | ||
const filename = path.basename(body.file.path) | ||
@@ -653,3 +653,3 @@ tags['openai.request.filename'] = filename | ||
// This is a best effort attempt to extract the filename during the request | ||
if (body.file && typeof body.file === 'object' && body.file.path) { | ||
if (body.file !== null && typeof body.file === 'object' && body.file.path) { | ||
tags['openai.request.filename'] = path.basename(body.file.path) | ||
@@ -656,0 +656,0 @@ } |
@@ -119,3 +119,3 @@ 'use strict' | ||
}) | ||
this.addSub('ci:playwright:test:finish', ({ testStatus, steps, error, extraTags, isNew, isEfdRetry }) => { | ||
this.addSub('ci:playwright:test:finish', ({ testStatus, steps, error, extraTags, isNew, isEfdRetry, isRetry }) => { | ||
const store = storage.getStore() | ||
@@ -139,2 +139,5 @@ const span = store && store.span | ||
} | ||
if (isRetry) { | ||
span.setTag(TEST_IS_RETRY, 'true') | ||
} | ||
@@ -141,0 +144,0 @@ steps.forEach(step => { |
@@ -57,5 +57,5 @@ 'use strict' | ||
function isObject (val) { | ||
return typeof val === 'object' && val !== null && !(val instanceof Array) | ||
return val !== null && typeof val === 'object' && !Array.isArray(val) | ||
} | ||
module.exports = SharedbPlugin |
@@ -9,3 +9,4 @@ const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin') | ||
getTestSuiteCommonTags, | ||
TEST_SOURCE_FILE | ||
TEST_SOURCE_FILE, | ||
TEST_IS_RETRY | ||
} = require('../../dd-trace/src/plugins/util/test') | ||
@@ -29,5 +30,13 @@ const { COMPONENT } = require('../../dd-trace/src/constants') | ||
this.addSub('ci:vitest:test:start', ({ testName, testSuiteAbsolutePath }) => { | ||
this.addSub('ci:vitest:test:start', ({ testName, testSuiteAbsolutePath, isRetry }) => { | ||
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot) | ||
const store = storage.getStore() | ||
const extraTags = { | ||
[TEST_SOURCE_FILE]: testSuite | ||
} | ||
if (isRetry) { | ||
extraTags[TEST_IS_RETRY] = 'true' | ||
} | ||
const span = this.startTestSpan( | ||
@@ -37,5 +46,3 @@ testName, | ||
this.testSuiteSpan, | ||
{ | ||
[TEST_SOURCE_FILE]: testSuite | ||
} | ||
extraTags | ||
) | ||
@@ -79,3 +86,7 @@ | ||
} | ||
span.finish(span._startTime + duration - MILLISECONDS_TO_SUBTRACT_FROM_FAILED_TEST_DURATION) // milliseconds | ||
if (duration) { | ||
span.finish(span._startTime + duration - MILLISECONDS_TO_SUBTRACT_FROM_FAILED_TEST_DURATION) // milliseconds | ||
} else { | ||
span.finish() // retries will not have a duration | ||
} | ||
finishAllTraceSpans(span) | ||
@@ -82,0 +93,0 @@ } |
@@ -7,3 +7,3 @@ 'use strict' | ||
sample (span, measured, measuredByDefault) { | ||
if (typeof measured === 'object') { | ||
if (measured !== null && typeof measured === 'object') { | ||
this.sample(span, measured[span.context()._name], measuredByDefault) | ||
@@ -10,0 +10,0 @@ } else if (measured !== undefined) { |
@@ -16,3 +16,3 @@ 'use strict' | ||
function iterateObjectStrings (target, fn, levelKeys = [], depth = 20, visited = new Set()) { | ||
if (target && typeof target === 'object') { | ||
if (target !== null && typeof target === 'object') { | ||
Object.keys(target).forEach((key) => { | ||
@@ -19,0 +19,0 @@ const nextLevelKeys = [...levelKeys, key] |
@@ -48,3 +48,3 @@ 'use strict' | ||
({ req }) => { | ||
if (req && req.body && typeof req.body === 'object') { | ||
if (req && req.body !== null && typeof req.body === 'object') { | ||
const iastContext = getIastContext(storage.getStore()) | ||
@@ -67,3 +67,3 @@ if (iastContext && iastContext.body !== req.body) { | ||
({ req }) => { | ||
if (req && req.params && typeof req.params === 'object') { | ||
if (req && req.params !== null && typeof req.params === 'object') { | ||
this._taintTrackingHandler(HTTP_REQUEST_PATH_PARAM, req, 'params') | ||
@@ -70,0 +70,0 @@ } |
@@ -30,3 +30,3 @@ 'use strict' | ||
if (key && typeof key === 'object') { | ||
if (key !== null && typeof key === 'object') { | ||
shimmer.wrap(key, 'toString', | ||
@@ -38,3 +38,3 @@ toString => this.getToStringWrap(toString, iastContext, KAFKA_MESSAGE_KEY)) | ||
if (value && typeof value === 'object') { | ||
if (value !== null && typeof value === 'object') { | ||
shimmer.wrap(value, 'toString', | ||
@@ -41,0 +41,0 @@ toString => this.getToStringWrap(toString, iastContext, KAFKA_MESSAGE_VALUE)) |
@@ -46,2 +46,4 @@ 'use strict' | ||
if (evidence.value == null) return { valueParts } | ||
if (typeof evidence.value === 'object' && evidence.rangesToApply) { | ||
@@ -73,3 +75,3 @@ const { value, ranges } = stringifyWithRanges(evidence.value, evidence.rangesToApply) | ||
formatEvidence (type, evidence, sourcesIndexes, sources) { | ||
if (typeof evidence.value === 'undefined') { | ||
if (evidence.value === undefined) { | ||
return undefined | ||
@@ -76,0 +78,0 @@ } |
@@ -124,3 +124,3 @@ 'use strict' | ||
// TODO: temporary express instrumentation, will use express plugin later | ||
if (req.params && typeof req.params === 'object') { | ||
if (req.params !== null && typeof req.params === 'object') { | ||
persistent[addresses.HTTP_INCOMING_PARAMS] = req.params | ||
@@ -130,7 +130,7 @@ } | ||
// we need to keep this to support other cookie parsers | ||
if (req.cookies && typeof req.cookies === 'object') { | ||
if (req.cookies !== null && typeof req.cookies === 'object') { | ||
persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies | ||
} | ||
if (req.query && typeof req.query === 'object') { | ||
if (req.query !== null && typeof req.query === 'object') { | ||
persistent[addresses.HTTP_INCOMING_QUERY] = req.query | ||
@@ -137,0 +137,0 @@ } |
@@ -16,3 +16,3 @@ 'use strict' | ||
if (tags && typeof tags === 'object') { | ||
if (tags !== null && typeof tags === 'object') { | ||
called = Object.entries(tags).some(([key, value]) => regexSdkEvent.test(key) && value === 'true') | ||
@@ -19,0 +19,0 @@ } |
@@ -210,6 +210,2 @@ 'use strict' | ||
const mandatoryTags = filterHeaders(req.headers, REQUEST_HEADERS_MAP) | ||
const ua = mandatoryTags['http.request.headers.user-agent'] | ||
if (ua) { | ||
mandatoryTags['http.useragent'] = ua | ||
} | ||
rootSpan.addTags(mandatoryTags) | ||
@@ -216,0 +212,0 @@ |
@@ -28,3 +28,3 @@ 'use strict' | ||
if (persistent && typeof persistent === 'object') { | ||
if (persistent !== null && typeof persistent === 'object') { | ||
// TODO: possible optimization: only send params that haven't already been sent with same value to this wafContext | ||
@@ -31,0 +31,0 @@ for (const key of Object.keys(persistent)) { |
@@ -137,3 +137,3 @@ 'use strict' | ||
// eslint-disable-next-line max-len | ||
const defaultWafObfuscatorKeyRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key)|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization|jsessionid|phpsessid|asp\\.net[_-]sessionid|sid|jwt' | ||
const defaultWafObfuscatorKeyRegex = '(?i)pass|pw(?:or)?d|secret|(?:api|private|public|access)[_-]?key|token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization|jsessionid|phpsessid|asp\\.net[_-]sessionid|sid|jwt' | ||
// eslint-disable-next-line max-len | ||
@@ -189,3 +189,3 @@ const defaultWafObfuscatorValueRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key(?:[_-]?id)?|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|jsessionid|phpsessid|asp\\.net(?:[_-]|-)sessionid|sid|jwt)(?:\\s*=[^;]|"\\s*:\\s*"[^"]+")|bearer\\s+[a-z0-9\\._\\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=-]+\\.ey[I-L][\\w=-]+(?:\\.[\\w.+\\/=-]+)?|[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY|ssh-rsa\\s*[a-z0-9\\/\\.+]{100,}' | ||
// Extract by key if in object-form value | ||
if (typeof option === 'object' && !Array.isArray(option)) { | ||
if (option !== null && typeof option === 'object' && !Array.isArray(option)) { | ||
option = option[key] | ||
@@ -198,3 +198,3 @@ } | ||
// If it's not an array but not undefined there's something wrong with the input | ||
if (typeof option !== 'undefined') { | ||
if (option !== undefined) { | ||
log.warn('Unexpected input for config.tracePropagationStyle') | ||
@@ -207,3 +207,3 @@ } | ||
const envVar = coalesce(process.env[envKey], process.env.DD_TRACE_PROPAGATION_STYLE, process.env.OTEL_PROPAGATORS) | ||
if (typeof envVar !== 'undefined') { | ||
if (envVar !== undefined) { | ||
return envVar.split(',') | ||
@@ -223,3 +223,3 @@ .filter(v => v !== '') | ||
appsec: options.appsec != null ? options.appsec : options.experimental?.appsec, | ||
iastOptions: options.experimental?.iast | ||
iast: options.iast != null ? options.iast : options.experimental?.iast | ||
} | ||
@@ -866,19 +866,19 @@ | ||
this._setString(opts, 'hostname', options.hostname) | ||
this._setBoolean(opts, 'iast.deduplicationEnabled', options.iastOptions && options.iastOptions.deduplicationEnabled) | ||
this._setBoolean(opts, 'iast.deduplicationEnabled', options.iast && options.iast.deduplicationEnabled) | ||
this._setBoolean(opts, 'iast.enabled', | ||
options.iastOptions && (options.iastOptions === true || options.iastOptions.enabled === true)) | ||
options.iast && (options.iast === true || options.iast.enabled === true)) | ||
this._setValue(opts, 'iast.maxConcurrentRequests', | ||
maybeInt(options.iastOptions?.maxConcurrentRequests)) | ||
this._optsUnprocessed['iast.maxConcurrentRequests'] = options.iastOptions?.maxConcurrentRequests | ||
this._setValue(opts, 'iast.maxContextOperations', maybeInt(options.iastOptions?.maxContextOperations)) | ||
this._optsUnprocessed['iast.maxContextOperations'] = options.iastOptions?.maxContextOperations | ||
this._setBoolean(opts, 'iast.redactionEnabled', options.iastOptions?.redactionEnabled) | ||
this._setString(opts, 'iast.redactionNamePattern', options.iastOptions?.redactionNamePattern) | ||
this._setString(opts, 'iast.redactionValuePattern', options.iastOptions?.redactionValuePattern) | ||
const iastRequestSampling = maybeInt(options.iastOptions?.requestSampling) | ||
maybeInt(options.iast?.maxConcurrentRequests)) | ||
this._optsUnprocessed['iast.maxConcurrentRequests'] = options.iast?.maxConcurrentRequests | ||
this._setValue(opts, 'iast.maxContextOperations', maybeInt(options.iast?.maxContextOperations)) | ||
this._optsUnprocessed['iast.maxContextOperations'] = options.iast?.maxContextOperations | ||
this._setBoolean(opts, 'iast.redactionEnabled', options.iast?.redactionEnabled) | ||
this._setString(opts, 'iast.redactionNamePattern', options.iast?.redactionNamePattern) | ||
this._setString(opts, 'iast.redactionValuePattern', options.iast?.redactionValuePattern) | ||
const iastRequestSampling = maybeInt(options.iast?.requestSampling) | ||
if (iastRequestSampling > -1 && iastRequestSampling < 101) { | ||
this._setValue(opts, 'iast.requestSampling', iastRequestSampling) | ||
this._optsUnprocessed['iast.requestSampling'] = options.iastOptions?.requestSampling | ||
this._optsUnprocessed['iast.requestSampling'] = options.iast?.requestSampling | ||
} | ||
this._setString(opts, 'iast.telemetryVerbosity', options.iastOptions && options.iastOptions.telemetryVerbosity) | ||
this._setString(opts, 'iast.telemetryVerbosity', options.iast && options.iast.telemetryVerbosity) | ||
this._setBoolean(opts, 'isCiVisibility', options.isCiVisibility) | ||
@@ -913,3 +913,3 @@ this._setBoolean(opts, 'logInjection', options.logInjection) | ||
const hasTelemetryLogsUsingFeatures = | ||
(options.iastOptions && (options.iastOptions === true || options.iastOptions?.enabled === true)) || | ||
(options.iast && (options.iast === true || options.iast?.enabled === true)) || | ||
(options.profiling && options.profiling === true) | ||
@@ -1053,11 +1053,14 @@ this._setBoolean(opts, 'telemetry.logCollection', hasTelemetryLogsUsingFeatures) | ||
this._setBoolean(opts, 'tracing', options.tracing_enabled) | ||
// ignore tags for now since rc sampling rule tags format is not supported | ||
this._setSamplingRule(opts, 'sampler.rules', this._ignoreTags(options.tracing_sampling_rules)) | ||
this._remoteUnprocessed['sampler.rules'] = options.tracing_sampling_rules | ||
this._setSamplingRule(opts, 'sampler.rules', this._reformatTags(options.tracing_sampling_rules)) | ||
} | ||
_ignoreTags (samplingRules) { | ||
if (samplingRules) { | ||
for (const rule of samplingRules) { | ||
delete rule.tags | ||
_reformatTags (samplingRules) { | ||
for (const rule of (samplingRules || [])) { | ||
const reformattedTags = {} | ||
if (rule.tags) { | ||
for (const tag of (rule.tags || {})) { | ||
reformattedTags[tag.key] = tag.value_glob | ||
} | ||
rule.tags = reformattedTags | ||
} | ||
@@ -1064,0 +1067,0 @@ } |
@@ -135,3 +135,3 @@ const os = require('os') | ||
} | ||
if (typeof obj === 'object') { | ||
if (obj !== null && typeof obj === 'object') { | ||
try { | ||
@@ -138,0 +138,0 @@ return getHeadersSize(obj) |
@@ -286,3 +286,3 @@ 'use strict' | ||
get ended () { | ||
return typeof this.duration !== 'undefined' | ||
return this.duration !== undefined | ||
} | ||
@@ -289,0 +289,0 @@ } |
@@ -18,2 +18,3 @@ 'use strict' | ||
this.instrumentationLibrary = library | ||
this._spanLimits = {} | ||
} | ||
@@ -122,4 +123,9 @@ | ||
} | ||
// not used in our codebase but needed for compatibility. See issue #1244 | ||
getSpanLimits () { | ||
return this._spanLimits | ||
} | ||
} | ||
module.exports = Tracer |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1878438
35
54220