dd-trace
Version:
Datadog APM tracing client for JavaScript
130 lines (115 loc) • 4.05 kB
JavaScript
const ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}${String.raw`\[[0-?]*[ -/]*[@-~]`}`, 'g')
/**
* Extracts the final test count from common JavaScript test-runner summaries.
*
* @param {string} framework test framework name
* @param {string} stdout captured stdout
* @param {string} stderr captured stderr
* @returns {number|null} observed count when a supported summary was found
*/
function getObservedTestCount (framework, stdout = '', stderr = '') {
const output = `${stdout}\n${stderr}`.replaceAll(ANSI_PATTERN, '')
if (framework === 'jest') return getJestObservedTestCount(output)
if (framework === 'vitest') return getVitestObservedTestCount(output)
if (framework === 'playwright') return getPlaywrightObservedTestCount(output)
const totalPatterns = framework === 'node:test'
? [/^# tests\s+(\d+)\s*$/gim]
: framework === 'cucumber'
? [/\b(\d+)\s+scenarios?\b/gi]
: []
for (const pattern of totalPatterns) {
const count = getLastMatchCount(output, pattern)
if (count !== null) return count
}
if (framework === 'mocha') {
return sumLastMatchCounts(output, [
/\b(\d+)\s+passing\b/gi,
/\b(\d+)\s+failing\b/gi,
/\b(\d+)\s+pending\b/gi,
])
}
return getLastMatchCount(output, /\b(\d+)\s+tests?\s+(?:passed|failed)\b/gi)
}
/**
* Counts Playwright tests that completed instead of treating skipped tests as executions.
*
* @param {string} output test output without ANSI codes
* @returns {number|null} executed test count
*/
function getPlaywrightObservedTestCount (output) {
const observed = sumLastMatchCounts(output, [
/^\s*(\d+)\s+passed\b/gim,
/^\s*(\d+)\s+failed\b/gim,
/^\s*(\d+)\s+flaky\b/gim,
])
if (observed !== null) return observed
return /^\s*\d+\s+skipped\b/im.test(output) ? 0 : null
}
/**
* Counts Jest tests that actually ran, excluding tests skipped by a name filter.
*
* @param {string} output test output without ANSI codes
* @returns {number|null} executed test count
*/
function getJestObservedTestCount (output) {
return getExecutedTestSummaryCount(output, /^\s*Tests:\s+/)
}
/**
* Counts Vitest tests that actually ran, excluding tests skipped by a name filter.
*
* @param {string} output test output without ANSI codes
* @returns {number|null} executed test count
*/
function getVitestObservedTestCount (output) {
return getExecutedTestSummaryCount(output, /^\s*Tests\s+/)
}
/**
* Extracts passed and failed counts from the final matching runner summary.
*
* @param {string} output test output without ANSI codes
* @param {RegExp} summaryPattern test-summary line pattern
* @returns {number|null} executed test count
*/
function getExecutedTestSummaryCount (output, summaryPattern) {
const summaryLines = output.split(/\r?\n/).filter(line => summaryPattern.test(line))
const summary = summaryLines.at(-1)
if (!summary) return null
const observed = sumLastMatchCounts(summary, [
/\b(\d+)\s+passed\b/gi,
/\b(\d+)\s+failed\b/gi,
])
if (observed !== null) return observed
return /\b\d+\s+skipped\b/i.test(summary) ? 0 : null
}
/**
* Returns the count captured by the last match of one summary pattern.
*
* @param {string} output test output
* @param {RegExp} pattern global summary pattern
* @returns {number|null} final captured count
*/
function getLastMatchCount (output, pattern) {
let count = null
for (const match of output.matchAll(pattern)) count = Number(match[1])
return count
}
/**
* Sums the final counts from multiple summary categories.
*
* @param {string} output test output
* @param {RegExp[]} patterns global summary patterns
* @returns {number|null} summed count when any category was found
*/
function sumLastMatchCounts (output, patterns) {
let found = false
let count = 0
for (const pattern of patterns) {
const value = getLastMatchCount(output, pattern)
if (value === null) continue
found = true
count += value
}
return found ? count : null
}
module.exports = { getObservedTestCount }