UNPKG

dd-trace

Version:

Datadog APM tracing client for JavaScript

170 lines (145 loc) 5.26 kB
'use strict' const { readFileSync } = require('fs') const { parse } = require('../../../vendor/dist/jest-docblock') const { getTestSuitePath } = require('../../dd-trace/src/plugins/util/test') const log = require('../../dd-trace/src/log') /** * There are two ways to call `test.each` in `jest`: * 1. With an array of arrays: https://jestjs.io/docs/api#1-testeachtablename-fn-timeout * 2. With a tagged template literal: https://jestjs.io/docs/api#2-testeachtablename-fn-timeout * This function distinguishes between the two and returns the test parameters in different formats: * 1. An array of arrays with the different parameters to the test, e.g. * [[1, 2, 3], [2, 3, 5]] * 2. An array of objects, e.g. * [{ a: 1, b: 2, expected: 3 }, { a: 2, b: 3, expected: 5}] */ function getFormattedJestTestParameters (testParameters) { if (!testParameters || !testParameters.length) { return } const [parameterArray, ...parameterValues] = testParameters if (parameterValues.length === 0) { // Way 1. return parameterArray } // Way 2. const parameterKeys = parameterArray[0].split('|').map(key => key.trim()) const formattedParameters = [] let lastFormattedParameter = {} for (let index = 0; index < parameterValues.length; index++) { const parameterIndex = index % parameterKeys.length if (parameterIndex === 0) { lastFormattedParameter = {} formattedParameters.push(lastFormattedParameter) } const key = parameterKeys[parameterIndex] lastFormattedParameter[key] = parameterValues[index] } return formattedParameters } // Support for `@fast-check/jest`: this library modifies the test name to include the seed // A test name that keeps changing breaks some Test Optimization's features. const SEED_SUFFIX_RE = /\s*\(with seed=-?\d+\)\s*$/i // https://github.com/facebook/jest/blob/3e38157ad5f23fb7d24669d24fae8ded06a7ab75/packages/jest-circus/src/utils.ts#L396 function getJestTestName (test, shouldStripSeed = false) { const titles = [] let parent = test do { titles.unshift(parent.name) } while ((parent = parent.parent)) titles.shift() // remove TOP_DESCRIBE_BLOCK_NAME const testName = titles.join(' ') if (shouldStripSeed) { return testName.replace(SEED_SUFFIX_RE, '') } return testName } const globalDocblockRegExp = /^\s*(\/\*\*?(.|\r?\n)*?\*\/)/ const MAX_COMMENTS_CHECKED = 10 function isMarkedAsUnskippable (test) { let testSource try { testSource = readFileSync(test.path, 'utf8') } catch { return false } const re = globalDocblockRegExp re.lastIndex = 0 let commentsChecked = 0 while (testSource.length) { const match = re.exec(testSource) if (!match) break const comment = match[1] let docblocks try { docblocks = parse(comment) } catch { // Skip unparsable comment and continue scanning if (commentsChecked++ >= MAX_COMMENTS_CHECKED) { return false } continue } if (docblocks?.datadog) { try { // @ts-expect-error The datadog type is defined by us and may only be a string. return JSON.parse(docblocks.datadog).unskippable } catch { // If the @datadog block comment is present but malformed, we'll run the suite log.warn('@datadog block comment is malformed.') return true } } if (commentsChecked++ >= MAX_COMMENTS_CHECKED) { return false } // To stop as soon as no doc blocks are found, slice the source. That way the // regexp works by using the `^` anchor. Without it, it would continue // scanning the rest of the file. testSource = testSource.slice(match[0].length) } return false } function getJestSuitesToRun (skippableSuites, originalTests, rootDir) { const unskippableSuites = {} const forcedToRunSuites = {} const skippedSuites = [] const suitesToRun = [] for (const test of originalTests) { const relativePath = getTestSuitePath(test.path, rootDir) const shouldBeSkipped = skippableSuites.includes(relativePath) if (isMarkedAsUnskippable(test)) { suitesToRun.push(test) unskippableSuites[relativePath] = true if (shouldBeSkipped) { forcedToRunSuites[relativePath] = true } continue } if (shouldBeSkipped) { skippedSuites.push(relativePath) } else { suitesToRun.push(test) } } const hasUnskippableSuites = Object.keys(unskippableSuites).length > 0 const hasForcedToRunSuites = Object.keys(forcedToRunSuites).length > 0 if (originalTests.length) { // The config object is shared by all tests, so we can just take the first one const [test] = originalTests if (test?.context?.config?.testEnvironmentOptions) { if (hasUnskippableSuites) { test.context.config.testEnvironmentOptions._ddUnskippable = JSON.stringify(unskippableSuites) } if (hasForcedToRunSuites) { test.context.config.testEnvironmentOptions._ddForcedToRun = JSON.stringify(forcedToRunSuites) } } } return { skippedSuites, suitesToRun, hasUnskippableSuites, hasForcedToRunSuites } } module.exports = { getFormattedJestTestParameters, getJestTestName, getJestSuitesToRun, isMarkedAsUnskippable }