Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[test-visibility] Add support for vitest #4415

Merged
merged 31 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0268820
add vitest support wip
juan-fernandez Jun 18, 2024
4b03ee1
better vitest support
juan-fernandez Jun 18, 2024
689d9aa
wip
juan-fernandez Jun 18, 2024
6512002
clean up
juan-fernandez Jun 19, 2024
d953763
clean up
juan-fernandez Jun 19, 2024
0d4f4ac
add e2e tests
juan-fernandez Jun 19, 2024
17de859
support 1.6.0 too
juan-fernandez Jun 19, 2024
b1ce709
clean up and add error handling
juan-fernandez Jun 20, 2024
81d8a1c
add session error message
juan-fernandez Jun 20, 2024
8c49dbd
add ci job
juan-fernandez Jun 20, 2024
b2a163e
remove volta
juan-fernandez Jun 20, 2024
6213755
Merge branch 'master' into juan-fernandez/add-vitest-support
juan-fernandez Jun 20, 2024
420e0dd
support for before after all hooks
juan-fernandez Jun 21, 2024
e5ae80f
support beforeeach and aftereach
juan-fernandez Jun 21, 2024
5fd20c7
add test source file
juan-fernandez Jun 21, 2024
7edbaf6
better tests
juan-fernandez Jun 21, 2024
482db61
ts definitions
juan-fernandez Jun 24, 2024
cb580df
Merge branch 'master' into juan-fernandez/add-vitest-support
juan-fernandez Jun 24, 2024
ba5088c
add support for skipped tests
juan-fernandez Jun 24, 2024
5c570a9
update comment
juan-fernandez Jun 24, 2024
1ddeee8
do not use iitm version and add wildcard logic
juan-fernandez Jun 25, 2024
95c5286
Merge branch 'master' into juan-fernandez/add-vitest-support
juan-fernandez Jun 25, 2024
aa20aae
static list of esm plugins
juan-fernandez Jun 25, 2024
5551e63
add safeguard hook
juan-fernandez Jun 25, 2024
c15e63c
better regex logic
juan-fernandez Jun 25, 2024
ee2b032
remove unused var
juan-fernandez Jun 25, 2024
0d60e30
Merge branch 'master' into juan-fernandez/add-vitest-support
juan-fernandez Jun 27, 2024
37f33e7
move esm first logic to hooks
juan-fernandez Jun 28, 2024
b29cfdb
remove dependency on getport
juan-fernandez Jun 28, 2024
6bee656
adding checks for hasSubscriber and asyncresource
juan-fernandez Jun 28, 2024
44ed749
Merge branch 'master' into juan-fernandez/add-vitest-support
juan-fernandez Jun 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,23 @@ jobs:
CYPRESS_VERSION: ${{ matrix.cypress-version }}
NODE_OPTIONS: '-r ./ci/init'

integration-vitest:
runs-on: ubuntu-latest
env:
DD_SERVICE: dd-trace-js-integration-tests
DD_CIVISIBILITY_AGENTLESS_ENABLED: 1
DD_API_KEY: ${{ secrets.DD_API_KEY_CI_APP }}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/node/setup
- run: yarn install
- uses: actions/setup-node@v3
with:
node-version: 20
- run: yarn test:integration:vitest
env:
NODE_OPTIONS: '-r ./ci/init'

lint:
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 2 additions & 0 deletions docs/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ tracer.use('sharedb');
tracer.use('sharedb', sharedbOptions);
tracer.use('tedious');
tracer.use('undici');
tracer.use('vitest');
tracer.use('vitest', { service: 'vitest-service' });
tracer.use('winston');

tracer.use('express', false)
Expand Down
2 changes: 1 addition & 1 deletion ext/exporters.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ declare const exporters: {
DATADOG: 'datadog',
AGENT_PROXY: 'agent_proxy',
JEST_WORKER: 'jest_worker',
CUCUMBER_WORKER: 'cucumber_worker'
CUCUMBER_WORKER: 'cucumber_worker',
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated fix

MOCHA_WORKER: 'mocha_worker'
}

Expand Down
9 changes: 8 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ interface Plugins {
"sharedb": tracer.plugins.sharedb;
"tedious": tracer.plugins.tedious;
"undici": tracer.plugins.undici;
"vitest": tracer.plugins.vitest;
"winston": tracer.plugins.winston;
}

Expand Down Expand Up @@ -1556,7 +1557,7 @@ declare namespace tracer {

/**
* This plugin automatically instruments the
* [jest](https://github.com/facebook/jest) module.
* [jest](https://github.com/jestjs/jest) module.
*/
interface jest extends Integration {}

Expand Down Expand Up @@ -1839,6 +1840,12 @@ declare namespace tracer {
*/
interface undici extends HttpClient {}

/**
* This plugin automatically instruments the
* [vitest](https://github.com/vitest-dev/vitest) module.
*/
interface vitest extends Integration {}

/**
* This plugin patches the [winston](https://github.com/winstonjs/winston)
* to automatically inject trace identifiers in log records when the
Expand Down
3 changes: 3 additions & 0 deletions integration-tests/ci-visibility/vitest-tests/sum.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function sum (a, b) {
return a + b
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { describe, test, expect, beforeEach, afterEach } from 'vitest'
import { sum } from './sum'

describe('context', () => {
beforeEach(() => {
throw new Error('failed before each')
})
test('can report failed test', () => {
expect(sum(1, 2)).to.equal(4)
})
test('can report more', () => {
expect(sum(1, 2)).to.equal(3)
})
})

describe('other context', () => {
afterEach(() => {
throw new Error('failed after each')
})
test('can report passed test', () => {
expect(sum(1, 2)).to.equal(3)
})
test('can report more', () => {
expect(sum(1, 2)).to.equal(3)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, test, expect, beforeEach, afterEach } from 'vitest'
import { sum } from './sum'

let preparedValue = 1

describe('test-visibility-failed-suite-first-describe', () => {
beforeEach(() => {
preparedValue = 2
})
test('can report failed test', () => {
expect(sum(1, 2)).to.equal(4)
})
test('can report more', () => {
expect(sum(1, 2)).to.equal(3)
expect(preparedValue).to.equal(2)
})
})

describe('test-visibility-failed-suite-second-describe', () => {
afterEach(() => {
preparedValue = 1
})
test('can report passed test', () => {
expect(sum(1, 2)).to.equal(3)
})
test('can report more', () => {
expect(sum(1, 2)).to.equal(3)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, test, expect } from 'vitest'
import { sum } from './sum'

describe('context', () => {
test('can report passed test', () => {
expect(sum(1, 2)).to.equal(3)
})
test('can report more', () => {
expect(sum(1, 2)).to.equal(3)
})
})

describe('other context', () => {
test('can report passed test', () => {
expect(sum(1, 2)).to.equal(3)
})
test('can report more', () => {
expect(sum(1, 2)).to.equal(3)
})
test.skip('can skip', () => {
expect(sum(1, 2)).to.equal(3)
})
test.todo('can todo', () => {
expect(sum(1, 2)).to.equal(3)
})
// eslint-disable-next-line
test('can programmatic skip', (context) => {
// eslint-disable-next-line
context.skip()
expect(sum(1, 2)).to.equal(3)
})
})
9 changes: 9 additions & 0 deletions integration-tests/vitest.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'vite'

export default defineConfig({
test: {
include: [
'ci-visibility/vitest-tests/test-visibility*'
]
}
})
137 changes: 137 additions & 0 deletions integration-tests/vitest/vitest.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
'use strict'

const { exec } = require('child_process')

const { assert } = require('chai')

const {
createSandbox,
getCiVisAgentlessConfig
} = require('../helpers')
const { FakeCiVisIntake } = require('../ci-visibility-intake')
const {
TEST_STATUS,
TEST_TYPE
} = require('../../packages/dd-trace/src/plugins/util/test')

// tested with 1.6.0
const versions = ['latest']

versions.forEach((version) => {
describe(`vitest@${version}`, () => {
let sandbox, cwd, receiver, childProcess

before(async function () {
sandbox = await createSandbox([`vitest@${version}`], true)
cwd = sandbox.folder
})

after(async () => {
await sandbox.remove()
})

beforeEach(async function () {
receiver = await new FakeCiVisIntake().start()
})

afterEach(async () => {
childProcess.kill()
await receiver.stop()
})

it('can run and report tests', (done) => {
receiver.gatherPayloadsMaxTimeout(({ url }) => url === '/api/v2/citestcycle', payloads => {
const events = payloads.flatMap(({ payload }) => payload.events)

const testSessionEvent = events.find(event => event.type === 'test_session_end')
const testModuleEvent = events.find(event => event.type === 'test_module_end')
const testSuiteEvents = events.filter(event => event.type === 'test_suite_end')
const testEvents = events.filter(event => event.type === 'test')

assert.include(testSessionEvent.content.resource, 'test_session.vitest run')
assert.equal(testSessionEvent.content.meta[TEST_STATUS], 'fail')
assert.include(testModuleEvent.content.resource, 'test_module.vitest run')
assert.equal(testModuleEvent.content.meta[TEST_STATUS], 'fail')
assert.equal(testSessionEvent.content.meta[TEST_TYPE], 'test')
assert.equal(testModuleEvent.content.meta[TEST_TYPE], 'test')

const passedSuite = testSuiteEvents.find(
suite => suite.content.resource === 'test_suite.ci-visibility/vitest-tests/test-visibility-passed-suite.mjs'
)
assert.equal(passedSuite.content.meta[TEST_STATUS], 'pass')

const failedSuite = testSuiteEvents.find(
suite => suite.content.resource === 'test_suite.ci-visibility/vitest-tests/test-visibility-failed-suite.mjs'
)
assert.equal(failedSuite.content.meta[TEST_STATUS], 'fail')

const failedSuiteHooks = testSuiteEvents.find(
suite => suite.content.resource === 'test_suite.ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs'
)
assert.equal(failedSuiteHooks.content.meta[TEST_STATUS], 'fail')

assert.includeMembers(testEvents.map(test => test.content.resource),
[
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
'.test-visibility-failed-suite-first-describe can report failed test',
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
'.test-visibility-failed-suite-first-describe can report more',
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
'.test-visibility-failed-suite-second-describe can report passed test',
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
'.test-visibility-failed-suite-second-describe can report more',
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.context can report passed test',
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.context can report more',
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can report passed test',
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can report more',
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can skip',
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can todo',
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report failed test',
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report more',
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report passed test',
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report more'
]
)

const failedTests = testEvents.filter(test => test.content.meta[TEST_STATUS] === 'fail')

assert.includeMembers(
failedTests.map(test => test.content.resource),
[
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
'.test-visibility-failed-suite-first-describe can report failed test',
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report failed test',
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report more',
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report passed test',
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report more'
]
)

const skippedTests = testEvents.filter(test => test.content.meta[TEST_STATUS] === 'skip')

assert.includeMembers(
skippedTests.map(test => test.content.resource),
[
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can skip',
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can todo',
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can programmatic skip'
]
)
// TODO: check error messages
}).then(() => done()).catch(done)

childProcess = exec(
'./node_modules/.bin/vitest run',
{
cwd,
env: {
...getCiVisAgentlessConfig(receiver.port),
// maybe only in node@20
NODE_OPTIONS: '--import dd-trace/register.js -r dd-trace/ci/init' // ESM requires more flags
},
stdio: 'pipe'
}
)
})
})
})
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"test:integration:cypress": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/cypress/*.spec.js\"",
"test:integration:playwright": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/playwright/*.spec.js\"",
"test:integration:selenium": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/selenium/*.spec.js\"",
"test:integration:vitest": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/vitest/*.spec.js\"",
"test:integration:profiler": "mocha --colors --timeout 180000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/profiler/*.spec.js\"",
"test:integration:serverless": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/serverless/*.spec.js\"",
"test:integration:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
Expand Down
6 changes: 5 additions & 1 deletion packages/datadog-esbuild/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ const hooks = require('../datadog-instrumentations/src/helpers/hooks.js')
const extractPackageAndModulePath = require('../datadog-instrumentations/src/utils/src/extract-package-and-module-path')

for (const hook of Object.values(hooks)) {
hook()
if (typeof hook === 'object') {
hook.fn()
} else {
hook()
}
}

const modulesOfInterest = new Set()
Expand Down
11 changes: 8 additions & 3 deletions packages/datadog-instrumentations/src/helpers/hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ const ritm = require('../../../dd-trace/src/ritm')
* @param {string[]} modules list of modules to hook into
* @param {Function} onrequire callback to be executed upon encountering module
*/
function Hook (modules, onrequire) {
if (!(this instanceof Hook)) return new Hook(modules, onrequire)
function Hook (modules, hookOptions, onrequire) {
if (!(this instanceof Hook)) return new Hook(modules, hookOptions, onrequire)

if (typeof hookOptions === 'function') {
onrequire = hookOptions
hookOptions = {}
}

this._patched = Object.create(null)

Expand All @@ -28,7 +33,7 @@ function Hook (modules, onrequire) {
}

this._ritmHook = ritm(modules, {}, safeHook)
this._iitmHook = iitm(modules, {}, (moduleExports, moduleName, moduleBaseDir) => {
this._iitmHook = iitm(modules, hookOptions, (moduleExports, moduleName, moduleBaseDir) => {
// TODO: Move this logic to import-in-the-middle and only do it for CommonJS
// modules and not ESM. In the meantime, all the modules we instrument are
// CommonJS modules for which the default export is always moved to
Expand Down
2 changes: 2 additions & 0 deletions packages/datadog-instrumentations/src/helpers/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module.exports = {
'@opentelemetry/sdk-trace-node': () => require('../otel-sdk-trace'),
'@redis/client': () => require('../redis'),
'@smithy/smithy-client': () => require('../aws-sdk'),
'@vitest/runner': { esmFirst: true, fn: () => require('../vitest') },
aerospike: () => require('../aerospike'),
amqp10: () => require('../amqp10'),
amqplib: () => require('../amqplib'),
Expand Down Expand Up @@ -110,6 +111,7 @@ module.exports = {
sharedb: () => require('../sharedb'),
tedious: () => require('../tedious'),
undici: () => require('../undici'),
vitest: { esmFirst: true, fn: () => require('../vitest') },
when: () => require('../when'),
winston: () => require('../winston')
}
7 changes: 4 additions & 3 deletions packages/datadog-instrumentations/src/helpers/instrument.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ exports.channel = function (name) {
/**
* @param {string} args.name module name
* @param {string[]} args.versions array of semver range strings
* @param {string} args.file path to file within package to instrument?
* @param {string} args.file path to file within package to instrument
* @param {string} args.filePattern pattern to match files within package to instrument
* @param Function hook
*/
exports.addHook = function addHook ({ name, versions, file }, hook) {
exports.addHook = function addHook ({ name, versions, file, filePattern }, hook) {
if (typeof name === 'string') {
name = [name]
}
Expand All @@ -29,7 +30,7 @@ exports.addHook = function addHook ({ name, versions, file }, hook) {
if (!instrumentations[val]) {
instrumentations[val] = []
}
instrumentations[val].push({ name: val, versions, file, hook })
instrumentations[val].push({ name: val, versions, file, filePattern, hook })
}
}

Expand Down
Loading
Loading