dd-trace
Version:
Datadog APM tracing client for JavaScript
184 lines (165 loc) • 6.26 kB
JavaScript
const { promisify } = require('util')
const { RESOURCE_NAME } = require('../../../ext/tags')
const {
TEST_NAME,
TEST_SUITE,
TEST_STATUS,
TEST_FRAMEWORK_VERSION,
JEST_TEST_RUNNER,
CI_APP_ORIGIN,
getTestEnvironmentMetadata,
finishAllTraceSpans,
getTestSuitePath
} = require('../../dd-trace/src/plugins/util/test')
const { getTestSpanTags, setSuppressedErrors } = require('./util')
function createWrapIt (tracer, globalConfig, globalInput, testEnvironmentMetadata) {
return function wrapIt (it) {
return function itWithTrace (description, specFunction, timeout) {
let oldSpecFunction = specFunction
if (specFunction.length) {
oldSpecFunction = promisify(oldSpecFunction)
}
const { childOf, commonSpanTags } = getTestSpanTags(tracer, testEnvironmentMetadata)
const testSuite = getTestSuitePath(globalInput.jasmine.testPath, globalConfig.rootDir)
const newSpecFunction = tracer.wrap(
'jest.test',
{
type: 'test',
childOf,
tags: {
...commonSpanTags,
[TEST_SUITE]: testSuite,
[TEST_FRAMEWORK_VERSION]: tracer._version,
[JEST_TEST_RUNNER]: 'jest-jasmine2'
}
},
async (done) => {
const testSpan = tracer.scope().active()
const { currentTestName } = globalInput.expect.getState()
const resource = `${testSuite}.${currentTestName}`
testSpan.setTag(TEST_NAME, currentTestName)
testSpan.setTag(RESOURCE_NAME, resource)
testSpan.context()._trace.origin = CI_APP_ORIGIN
let result
globalInput.jasmine.testSpanByTestName[currentTestName] = testSpan
try {
result = await oldSpecFunction()
const suppressedErrors = globalInput.expect.getState().suppressedErrors
setSuppressedErrors(suppressedErrors, testSpan)
if (!testSpan._spanContext._tags[TEST_STATUS]) {
testSpan.setTag(TEST_STATUS, 'pass')
}
} catch (error) {
testSpan.setTag(TEST_STATUS, 'fail')
testSpan.setTag('error', error)
if (done) {
done(error)
}
throw error
} finally {
finishAllTraceSpans(testSpan)
}
if (done) {
done(result)
}
}
)
return it(description, newSpecFunction, timeout)
}
}
}
function createWrapOnException (tracer, globalInput) {
return function wrapOnException (onException) {
return function onExceptionWithTrace (err) {
let activeTestSpan = tracer.scope().active()
if (!activeTestSpan) {
activeTestSpan = globalInput.jasmine.testSpanByTestName[this.getFullName()]
}
if (!activeTestSpan) {
return onException.apply(this, arguments)
}
const {
[TEST_NAME]: testName,
[TEST_SUITE]: testSuite,
[TEST_STATUS]: testStatus
} = activeTestSpan._spanContext._tags
const isActiveSpanFailing = this.getFullName() === testName &&
this.result.testPath.endsWith(testSuite)
if (isActiveSpanFailing && !testStatus) {
activeTestSpan.setTag(TEST_STATUS, 'fail')
// If we don't do this, jest will show this file on its error message
const stackFrames = err.stack.split('\n')
const filteredStackFrames = stackFrames.filter(frame => !frame.includes(__dirname)).join('\n')
err.stack = filteredStackFrames
activeTestSpan.setTag('error', err)
// need to manually finish, as it will not be caught in `itWithTrace`
activeTestSpan.finish()
}
return onException.apply(this, arguments)
}
}
}
function createWrapItSkip (tracer, globalConfig, globalInput, testEnvironmentMetadata) {
return function wrapItSkip (it) {
return function itSkipWithTrace () {
const { childOf, commonSpanTags } = getTestSpanTags(tracer, testEnvironmentMetadata)
const testSuite = getTestSuitePath(globalInput.jasmine.testPath, globalConfig.rootDir)
const spec = it.apply(this, arguments)
const testName = spec.getFullName()
const resource = `${testSuite}.${testName}`
const testSpan = tracer.startSpan(
'jest.test',
{
childOf,
tags: {
...commonSpanTags,
[RESOURCE_NAME]: resource,
[TEST_NAME]: testName,
[TEST_SUITE]: testSuite,
[TEST_STATUS]: 'skip',
[TEST_FRAMEWORK_VERSION]: tracer._version,
[JEST_TEST_RUNNER]: 'jest-jasmine2'
}
}
)
testSpan.context()._trace.origin = CI_APP_ORIGIN
testSpan.finish()
return spec
}
}
}
function createWrapJasmineAsyncInstall (tracer, instrumenter, testEnvironmentMetadata) {
return function jasmineAsyncInstallWithTrace (jasmineAsyncInstall) {
return function (globalConfig, globalInput) {
globalInput.jasmine.testSpanByTestName = {}
instrumenter.wrap(globalInput.jasmine.Spec.prototype, 'onException', createWrapOnException(tracer, globalInput))
instrumenter.wrap(globalInput, 'it', createWrapIt(tracer, globalConfig, globalInput, testEnvironmentMetadata))
// instruments 'it.only'
instrumenter.wrap(globalInput, 'fit', createWrapIt(tracer, globalConfig, globalInput, testEnvironmentMetadata))
// instruments 'it.skip'
instrumenter.wrap(
globalInput,
'xit',
createWrapItSkip(tracer, globalConfig, globalInput, testEnvironmentMetadata)
)
return jasmineAsyncInstall(globalConfig, globalInput)
}
}
}
module.exports = [
{
name: 'jest-jasmine2',
versions: ['>=24.8.0'],
file: 'build/jasmineAsyncInstall.js',
patch: function (jasmineAsyncInstallExport, tracer, config) {
const testEnvironmentMetadata = getTestEnvironmentMetadata('jest', config)
return this.wrapExport(
jasmineAsyncInstallExport.default,
createWrapJasmineAsyncInstall(tracer, this, testEnvironmentMetadata)(jasmineAsyncInstallExport.default)
)
},
unpatch: function (jasmineAsyncInstallExport) {
this.unwrapExport(jasmineAsyncInstallExport.default)
}
}
]