dd-trace
Version:
Datadog APM tracing client for JavaScript
97 lines (85 loc) • 3.36 kB
JavaScript
const shimmer = require('../../datadog-shimmer')
const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
const {
readCoverageBackfillFromCache,
readCoverageBackfillRootDirFromCache,
setupSettingsCachePath,
} = require('../../dd-trace/src/ci-visibility/test-optimization-cache')
const { applySkippedCoverageToCoverage } = require('../../dd-trace/src/plugins/util/test')
const { addHook, channel } = require('./helpers/instrument')
const codeCoverageWrapCh = channel('ci:nyc:wrap')
const codeCoverageReportCh = channel('ci:nyc:report')
addHook({
name: 'nyc',
versions: ['>=17'],
}, (nycPackage) => {
// Set up the cache file path early (when nyc is required) so it's available
// when dd-trace fetches library configuration
setupSettingsCachePath()
if (nycPackage.prototype.getCoverageMapFromAllCoverageFiles) {
// Some test frameworks receive skipped-suite coverage in the test process, but nyc merges reports later in the nyc
// process. Reuse the settings cache path as the process handoff so nyc can backfill skipped files before reporting.
shimmer.wrap(
nycPackage.prototype,
'getCoverageMapFromAllCoverageFiles',
getCoverageMapFromAllCoverageFiles => function (...args) {
const coverageMap = getCoverageMapFromAllCoverageFiles.apply(this, args)
const applyCoverageBackfill = (resolvedCoverageMap) => {
try {
if (!resolvedCoverageMap) {
return resolvedCoverageMap
}
applySkippedCoverageToCoverage(
resolvedCoverageMap,
readCoverageBackfillFromCache(),
readCoverageBackfillRootDirFromCache() || this.cwd
)
} catch {
// Do not break nyc's report generation if the cached backfill is stale or malformed.
}
return resolvedCoverageMap
}
if (coverageMap && typeof coverageMap.then === 'function') {
return coverageMap.then(applyCoverageBackfill)
}
return applyCoverageBackfill(coverageMap)
}
)
}
// `wrap` is an async function
shimmer.wrap(nycPackage.prototype, 'wrap', wrap => function (...args) {
// Only relevant if the config `all` is set to true (for untested code coverage)
try {
if (JSON.parse(getEnvironmentVariable('NYC_CONFIG')).all) {
codeCoverageWrapCh.publish(this)
}
} catch {
// ignore errors
}
return wrap.apply(this, args)
})
// `report` is an async function, so we wait for it to complete before publishing
shimmer.wrap(nycPackage.prototype, 'report', report => function (...args) {
if (!codeCoverageReportCh.hasSubscribers) {
return report.apply(this, args)
}
const nycInstance = this
const reportPromise = report.apply(this, args)
if (reportPromise && typeof reportPromise.then === 'function') {
// Return a new promise that waits for both the report AND the coverage upload
return reportPromise.then(() => {
return new Promise((resolve) => {
codeCoverageReportCh.publish({
rootDir: nycInstance.cwd,
onDone: resolve,
})
})
}).catch(() => {
// Ignore errors - report generation failed
})
}
return reportPromise
})
return nycPackage
})