dd-trace 2.0.0-appsec-beta.1 → 2.0.0-appsec-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/ci/cypress/plugin.js +3 -0
  2. package/ci/cypress/support.js +1 -0
  3. package/ci/init.js +13 -0
  4. package/ci/jest/env.js +14 -0
  5. package/index.d.ts +5 -5
  6. package/package.json +5 -4
  7. package/packages/datadog-instrumentations/index.js +5 -0
  8. package/packages/datadog-instrumentations/src/dns.js +94 -0
  9. package/packages/datadog-instrumentations/src/helpers/instrument.js +121 -0
  10. package/packages/datadog-instrumentations/src/memcached.js +55 -0
  11. package/packages/datadog-instrumentations/src/mysql.js +69 -0
  12. package/packages/datadog-plugin-cucumber/src/index.js +4 -4
  13. package/packages/datadog-plugin-cypress/src/plugin.js +6 -2
  14. package/packages/datadog-plugin-cypress/src/support.js +21 -6
  15. package/packages/datadog-plugin-dns/src/index.js +65 -178
  16. package/packages/datadog-plugin-http/src/server.js +5 -0
  17. package/packages/datadog-plugin-jest/src/jest-environment.js +4 -4
  18. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +2 -2
  19. package/packages/datadog-plugin-memcached/src/index.js +41 -63
  20. package/packages/datadog-plugin-mocha/src/index.js +2 -2
  21. package/packages/datadog-plugin-mysql/src/index.js +37 -89
  22. package/packages/datadog-plugin-redis/src/index.js +31 -1
  23. package/packages/dd-trace/lib/version.js +1 -1
  24. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +9 -20
  25. package/packages/dd-trace/src/{gateway → appsec/gateway}/als.js +0 -0
  26. package/packages/dd-trace/src/appsec/gateway/channels.js +8 -0
  27. package/packages/dd-trace/src/{gateway → appsec/gateway}/dc_block.js +0 -0
  28. package/packages/dd-trace/src/{gateway → appsec/gateway}/engine/engine.js +19 -30
  29. package/packages/dd-trace/src/{gateway → appsec/gateway}/engine/index.js +0 -0
  30. package/packages/dd-trace/src/{gateway → appsec/gateway}/engine/runner.js +2 -0
  31. package/packages/dd-trace/src/appsec/index.js +19 -28
  32. package/packages/dd-trace/src/appsec/recommended.json +1 -1
  33. package/packages/dd-trace/src/appsec/reporter.js +46 -180
  34. package/packages/dd-trace/src/config.js +11 -2
  35. package/packages/dd-trace/src/constants.js +6 -1
  36. package/packages/dd-trace/src/format.js +12 -0
  37. package/packages/dd-trace/src/instrumenter.js +6 -1
  38. package/packages/dd-trace/src/opentracing/propagation/text_map.js +34 -0
  39. package/packages/dd-trace/src/opentracing/span_context.js +2 -1
  40. package/packages/dd-trace/src/plugin_manager.js +65 -0
  41. package/packages/dd-trace/src/plugins/plugin.js +57 -0
  42. package/packages/dd-trace/src/plugins/util/ci.js +13 -4
  43. package/packages/dd-trace/src/plugins/util/redis.js +0 -2
  44. package/packages/dd-trace/src/plugins/util/test.js +8 -2
  45. package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -2
  46. package/packages/dd-trace/src/plugins/util/web.js +3 -13
  47. package/packages/dd-trace/src/priority_sampler.js +71 -19
  48. package/packages/dd-trace/src/profiling/exporters/agent.js +35 -34
  49. package/packages/dd-trace/src/proxy.js +5 -1
  50. package/packages/dd-trace/src/ritm.js +40 -16
  51. package/scripts/install_plugin_modules.js +23 -1
  52. package/packages/dd-trace/src/gateway/channels.js +0 -8
  53. package/packages/dd-trace/src/plugins/util/ci-app-spec.json +0 -36
@@ -0,0 +1,3 @@
1
+ require('../init')
2
+
3
+ module.exports = require('../../packages/datadog-plugin-cypress/src/plugin')
@@ -0,0 +1 @@
1
+ require('../../packages/datadog-plugin-cypress/src/support')
package/ci/init.js ADDED
@@ -0,0 +1,13 @@
1
+ const tracer = require('../packages/dd-trace')
2
+ const { ORIGIN_KEY } = require('../packages/dd-trace/src/constants')
3
+
4
+ tracer.init({
5
+ startupLogs: false,
6
+ tags: {
7
+ [ORIGIN_KEY]: 'ciapp-test'
8
+ }
9
+ })
10
+
11
+ tracer.use('fs', false)
12
+
13
+ module.exports = tracer
package/ci/jest/env.js ADDED
@@ -0,0 +1,14 @@
1
+ const tracer = require('../../packages/dd-trace')
2
+ const { ORIGIN_KEY } = require('../../packages/dd-trace/src/constants')
3
+
4
+ tracer.init({
5
+ startupLogs: false,
6
+ flushInterval: 400000,
7
+ tags: {
8
+ [ORIGIN_KEY]: 'ciapp-test'
9
+ }
10
+ })
11
+
12
+ tracer.use('fs', false)
13
+
14
+ module.exports = tracer
package/index.d.ts CHANGED
@@ -786,7 +786,7 @@ declare namespace plugins {
786
786
  * This plugin automatically instruments the
787
787
  * [cucumber](https://www.npmjs.com/package/@cucumber/cucumber) module.
788
788
  */
789
- interface cucumber extends Instrumentation {}
789
+ interface cucumber extends Integration {}
790
790
 
791
791
  /**
792
792
  * This plugin automatically instruments the
@@ -972,12 +972,12 @@ declare namespace plugins {
972
972
  /**
973
973
  * Configuration for HTTP clients.
974
974
  */
975
- client?: HttpClient,
975
+ client?: HttpClient | boolean,
976
976
 
977
977
  /**
978
978
  * Configuration for HTTP servers.
979
979
  */
980
- server?: HttpServer
980
+ server?: HttpServer | boolean
981
981
 
982
982
  /**
983
983
  * Hooks to run before spans are finished.
@@ -1006,12 +1006,12 @@ declare namespace plugins {
1006
1006
  /**
1007
1007
  * Configuration for HTTP clients.
1008
1008
  */
1009
- client?: Http2Client,
1009
+ client?: Http2Client | boolean,
1010
1010
 
1011
1011
  /**
1012
1012
  * Configuration for HTTP servers.
1013
1013
  */
1014
- server?: Http2Server
1014
+ server?: Http2Server | boolean
1015
1015
  }
1016
1016
 
1017
1017
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "2.0.0-appsec-beta.1",
3
+ "version": "2.0.0-appsec-beta.5",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -10,6 +10,7 @@
10
10
  "bench": "node benchmark",
11
11
  "bench:profiler": "node benchmark/profiler",
12
12
  "bench:e2e": "SERVICES=mongo yarn services && cd benchmark/e2e && node benchmark-run.js --duration=30",
13
+ "bench:e2e:ci-visibility": "node benchmark/e2e-ci/benchmark-run.js",
13
14
  "type:doc": "cd docs && yarn && yarn build",
14
15
  "type:test": "cd docs && yarn && yarn test",
15
16
  "lint": "node scripts/check_licenses.js && eslint . && yarn audit --groups dependencies",
@@ -58,11 +59,11 @@
58
59
  "node": ">=12"
59
60
  },
60
61
  "dependencies": {
61
- "@datadog/native-appsec": "^0.6.0",
62
- "@datadog/native-metrics": "^1.0.1",
62
+ "@datadog/native-appsec": "^0.7.0",
63
+ "@datadog/native-metrics": "^1.1.0",
63
64
  "@datadog/pprof": "^0.3.0",
64
65
  "@datadog/sketches-js": "^1.0.4",
65
- "@types/node": "^10.12.18",
66
+ "@types/node": ">=12",
66
67
  "crypto-randomuuid": "^1.0.0",
67
68
  "diagnostics_channel": "^1.1.0",
68
69
  "form-data": "^3.0.0",
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ require('./src/dns')
4
+ require('./src/memcached')
5
+ require('./src/mysql')
@@ -0,0 +1,94 @@
1
+ 'use strict'
2
+
3
+ const { channel, addHook, bind } = require('./helpers/instrument')
4
+ const shimmer = require('../../datadog-shimmer')
5
+
6
+ const rrtypes = {
7
+ resolveAny: 'ANY',
8
+ resolve4: 'A',
9
+ resolve6: 'AAAA',
10
+ resolveCname: 'CNAME',
11
+ resolveMx: 'MX',
12
+ resolveNs: 'NS',
13
+ resolveTxt: 'TXT',
14
+ resolveSrv: 'SRV',
15
+ resolvePtr: 'PTR',
16
+ resolveNaptr: 'NAPTR',
17
+ resolveSoa: 'SOA'
18
+ }
19
+
20
+ const rrtypeMap = new WeakMap()
21
+
22
+ addHook({ name: 'dns' }, dns => {
23
+ dns.lookup = wrap('apm:dns:lookup', dns.lookup, 2)
24
+ dns.lookupService = wrap('apm:dns:lookup_service', dns.lookupService, 3)
25
+ dns.resolve = wrap('apm:dns:resolve', dns.resolve, 2)
26
+ dns.reverse = wrap('apm:dns:reverse', dns.reverse, 2)
27
+
28
+ patchResolveShorthands(dns)
29
+
30
+ if (dns.Resolver) {
31
+ dns.Resolver.prototype.resolve = wrap('apm:dns:resolve', dns.Resolver.prototype.resolve, 2)
32
+ dns.Resolver.prototype.reverse = wrap('apm:dns:reverse', dns.Resolver.prototype.reverse, 2)
33
+
34
+ patchResolveShorthands(dns.Resolver.prototype)
35
+ }
36
+
37
+ return dns
38
+ })
39
+
40
+ function patchResolveShorthands (prototype) {
41
+ Object.keys(rrtypes)
42
+ .filter(method => !!prototype[method])
43
+ .forEach(method => {
44
+ rrtypeMap.set(prototype[method], rrtypes[method])
45
+ prototype[method] = wrap('apm:dns:resolve', prototype[method], 2, rrtypes[method])
46
+ })
47
+ }
48
+
49
+ function wrap (prefix, fn, expectedArgs, rrtype) {
50
+ const startCh = channel(prefix + ':start')
51
+ const endCh = channel(prefix + ':end')
52
+ const asyncEndCh = channel(prefix + ':async-end')
53
+ const errorCh = channel(prefix + ':error')
54
+
55
+ const wrapped = function () {
56
+ const cb = bind(arguments[arguments.length - 1])
57
+ if (
58
+ !startCh.hasSubscribers ||
59
+ arguments.length < expectedArgs ||
60
+ typeof cb !== 'function'
61
+ ) {
62
+ return fn.apply(this, arguments)
63
+ }
64
+
65
+ const startArgs = Array.from(arguments)
66
+ startArgs.pop() // gets rid of the callback
67
+ if (rrtype) {
68
+ startArgs.push(rrtype)
69
+ }
70
+ startCh.publish(startArgs)
71
+
72
+ arguments[arguments.length - 1] = function (error, result) {
73
+ if (error) {
74
+ errorCh.publish(error)
75
+ }
76
+ asyncEndCh.publish(result)
77
+ cb.apply(this, arguments)
78
+ }
79
+
80
+ try {
81
+ return fn.apply(this, arguments)
82
+ // TODO deal with promise versions when we support `dns/promises`
83
+ } catch (error) {
84
+ error.stack // trigger getting the stack at the original throwing point
85
+ errorCh.publish(error)
86
+
87
+ throw error
88
+ } finally {
89
+ endCh.publish(undefined)
90
+ }
91
+ }
92
+
93
+ return shimmer.wrap(fn, wrapped)
94
+ }
@@ -0,0 +1,121 @@
1
+ 'use strict'
2
+
3
+ const dc = require('diagnostics_channel')
4
+ const path = require('path')
5
+ const semver = require('semver')
6
+ const iitm = require('../../../dd-trace/src/iitm')
7
+ const ritm = require('../../../dd-trace/src/ritm')
8
+ const parse = require('module-details-from-path')
9
+ const requirePackageJson = require('../../../dd-trace/src/require-package-json')
10
+ const { AsyncResource } = require('async_hooks')
11
+
12
+ const pathSepExpr = new RegExp(`\\${path.sep}`, 'g')
13
+ const channelMap = {}
14
+ exports.channel = function channel (name) {
15
+ const maybe = channelMap[name]
16
+ if (maybe) return maybe
17
+ const ch = dc.channel(name)
18
+ channelMap[name] = ch
19
+ return ch
20
+ }
21
+
22
+ exports.addHook = function addHook ({ name, versions, file }, hook) {
23
+ file = filename(name, file)
24
+ const loaderHook = (moduleExports, moduleName, moduleBaseDir) => {
25
+ moduleName = moduleName.replace(pathSepExpr, '/')
26
+ const moduleVersion = getVersion(moduleBaseDir)
27
+ if (moduleName !== file || !matchVersion(moduleVersion, versions)) {
28
+ return moduleExports
29
+ }
30
+ return hook(moduleExports)
31
+ }
32
+ ritm([name], loaderHook)
33
+ cjsPostLoad({ name, versions, file }, hook)
34
+ iitm([name], loaderHook)
35
+ }
36
+
37
+ function matchVersion (version, ranges) {
38
+ return !version || (ranges && ranges.some(range => semver.satisfies(semver.coerce(version), range)))
39
+ }
40
+
41
+ function getVersion (moduleBaseDir) {
42
+ if (moduleBaseDir) {
43
+ return requirePackageJson(moduleBaseDir, module).version
44
+ }
45
+ }
46
+
47
+ function filename (name, file) {
48
+ return [name, file].filter(val => val).join('/')
49
+ }
50
+
51
+ // TODO this is basically Loader#_getModules + running the hook. DRY up.
52
+ function cjsPostLoad (instrumentation, hook) {
53
+ const ids = Object.keys(require.cache)
54
+
55
+ let pkg
56
+
57
+ for (let i = 0, l = ids.length; i < l; i++) {
58
+ if (ids[i] === instrumentation.name) {
59
+ hook(require.cache[ids[i]].exports)
60
+ continue
61
+ }
62
+
63
+ const id = ids[i].replace(pathSepExpr, '/')
64
+
65
+ if (!id.includes(`/node_modules/${instrumentation.name}/`)) continue
66
+
67
+ if (instrumentation.file) {
68
+ if (!id.endsWith(`/node_modules/${filename(instrumentation)}`)) continue
69
+
70
+ const basedir = getBasedir(ids[i])
71
+
72
+ pkg = requirePackageJson(basedir, module)
73
+ } else {
74
+ const basedir = getBasedir(ids[i])
75
+
76
+ pkg = requirePackageJson(basedir, module)
77
+
78
+ const mainFile = path.posix.normalize(pkg.main || 'index.js')
79
+ if (!id.endsWith(`/node_modules/${instrumentation.name}/${mainFile}`)) continue
80
+ }
81
+
82
+ if (!matchVersion(pkg.version, instrumentation.versions)) continue
83
+
84
+ hook(require.cache[ids[i]].exports)
85
+ }
86
+ }
87
+
88
+ function getBasedir (id) {
89
+ return parse(id).basedir.replace(pathSepExpr, '/')
90
+ }
91
+
92
+ if (semver.satisfies(process.versions.node, '>=16.0.0')) {
93
+ exports.bind = AsyncResource.bind
94
+ exports.bindAsyncResource = AsyncResource.prototype.bind
95
+ } else {
96
+ exports.bindAsyncResource = function bindAsyncResource (fn, thisArg) {
97
+ thisArg = thisArg || this
98
+ const ret = this.runInAsyncScope.bind(this, fn, thisArg)
99
+ Object.defineProperties(ret, {
100
+ 'length': {
101
+ configurable: true,
102
+ enumerable: false,
103
+ value: fn.length,
104
+ writable: false
105
+ },
106
+ 'asyncResource': {
107
+ configurable: true,
108
+ enumerable: true,
109
+ value: this,
110
+ writable: true
111
+ }
112
+ })
113
+ return ret
114
+ }
115
+
116
+ exports.bind = function bind (fn, type, thisArg) {
117
+ type = type || fn.name
118
+ const ar = new AsyncResource(type || 'bound-anonymous-fn')
119
+ return exports.bindAsyncResource.call(ar, fn, thisArg)
120
+ }
121
+ }
@@ -0,0 +1,55 @@
1
+ 'use strict'
2
+
3
+ const { AsyncResource } = require('async_hooks')
4
+ const {
5
+ channel,
6
+ addHook,
7
+ bind,
8
+ bindAsyncResource
9
+ } = require('./helpers/instrument')
10
+ const shimmer = require('../../datadog-shimmer')
11
+
12
+ addHook({ name: 'memcached', versions: ['>=2.2'] }, Memcached => {
13
+ const startCh = channel('apm:memcached:command:start')
14
+ const startWithArgsCh = channel('apm:memcached:command:start:with-args')
15
+ const asyncEndCh = channel('apm:memcached:command:async-end')
16
+ const endCh = channel('apm:memcached:command:end')
17
+ const errorCh = channel('apm:memcached:command:error')
18
+
19
+ shimmer.wrap(Memcached.prototype, 'command', command => function (queryCompiler, server) {
20
+ if (!startCh.hasSubscribers) {
21
+ return command.apply(this, arguments)
22
+ }
23
+
24
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
25
+
26
+ const client = this
27
+
28
+ const wrappedQueryCompiler = function () {
29
+ const query = queryCompiler.apply(this, arguments)
30
+ const callback = bindAsyncResource.call(asyncResource, query.callback)
31
+
32
+ query.callback = bind(function (err) {
33
+ if (err) {
34
+ errorCh.publish(err)
35
+ }
36
+ asyncEndCh.publish(undefined)
37
+
38
+ return callback.apply(this, arguments)
39
+ })
40
+ startWithArgsCh.publish({ client, server, query })
41
+
42
+ return query
43
+ }
44
+
45
+ startCh.publish(undefined)
46
+
47
+ arguments[0] = wrappedQueryCompiler
48
+
49
+ const result = command.apply(this, arguments)
50
+ endCh.publish(undefined)
51
+ return result
52
+ })
53
+
54
+ return Memcached
55
+ })
@@ -0,0 +1,69 @@
1
+ 'use strict'
2
+
3
+ const { AsyncResource } = require('async_hooks')
4
+ const {
5
+ channel,
6
+ addHook,
7
+ bind,
8
+ bindAsyncResource
9
+ } = require('./helpers/instrument')
10
+ const shimmer = require('../../datadog-shimmer')
11
+
12
+ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connection => {
13
+ const startCh = channel('apm:mysql:query:start')
14
+ const asyncEndCh = channel('apm:mysql:query:async-end')
15
+ const endCh = channel('apm:mysql:query:end')
16
+ const errorCh = channel('apm:mysql:query:error')
17
+
18
+ shimmer.wrap(Connection.prototype, 'query', query => function () {
19
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
20
+ if (!startCh.hasSubscribers) {
21
+ return query.apply(this, arguments)
22
+ }
23
+
24
+ const sql = arguments[0].sql ? arguments[0].sql : arguments[0]
25
+ const startArgs = [sql, this.config]
26
+
27
+ startCh.publish(startArgs)
28
+
29
+ try {
30
+ const res = query.apply(this, arguments)
31
+
32
+ if (res._callback) {
33
+ const cb = bindAsyncResource.call(asyncResource, res._callback)
34
+ res._callback = bind(function (error, result) {
35
+ if (error) {
36
+ errorCh.publish(error)
37
+ }
38
+ asyncEndCh.publish(result)
39
+
40
+ return cb.apply(this, arguments)
41
+ })
42
+ } else {
43
+ const cb = bind(function () {
44
+ asyncEndCh.publish(undefined)
45
+ })
46
+ res.on('end', cb)
47
+ }
48
+
49
+ return res
50
+ } catch (err) {
51
+ err.stack // trigger getting the stack at the original throwing point
52
+ errorCh.publish(err)
53
+
54
+ throw err
55
+ } finally {
56
+ endCh.publish(undefined)
57
+ }
58
+ })
59
+
60
+ return Connection
61
+ })
62
+
63
+ addHook({ name: 'mysql', file: 'lib/Pool.js', versions: ['>=2'] }, Pool => {
64
+ shimmer.wrap(Pool.prototype, 'getConnection', getConnection => function (cb) {
65
+ arguments[0] = bind(cb)
66
+ return getConnection.apply(this, arguments)
67
+ })
68
+ return Pool
69
+ })
@@ -103,8 +103,8 @@ module.exports = [
103
103
  name: '@cucumber/cucumber',
104
104
  versions: ['7.0.0 - 7.2.1'],
105
105
  file: 'lib/runtime/pickle_runner.js',
106
- patch (PickleRunner, tracer) {
107
- const testEnvironmentMetadata = getTestEnvironmentMetadata('cucumber')
106
+ patch (PickleRunner, tracer, config) {
107
+ const testEnvironmentMetadata = getTestEnvironmentMetadata('cucumber', config)
108
108
  const sourceRoot = process.cwd()
109
109
  const pl = PickleRunner.default
110
110
  this.wrap(
@@ -127,8 +127,8 @@ module.exports = [
127
127
  name: '@cucumber/cucumber',
128
128
  versions: ['>=7.3.0'],
129
129
  file: 'lib/runtime/test_case_runner.js',
130
- patch (TestCaseRunner, tracer) {
131
- const testEnvironmentMetadata = getTestEnvironmentMetadata('cucumber')
130
+ patch (TestCaseRunner, tracer, config) {
131
+ const testEnvironmentMetadata = getTestEnvironmentMetadata('cucumber', config)
132
132
  const sourceRoot = process.cwd()
133
133
  const pl = TestCaseRunner.default
134
134
  this.wrap(
@@ -4,6 +4,7 @@ const {
4
4
  TEST_SUITE,
5
5
  TEST_STATUS,
6
6
  TEST_FRAMEWORK_VERSION,
7
+ TEST_IS_RUM_ACTIVE,
7
8
  getTestEnvironmentMetadata,
8
9
  CI_APP_ORIGIN,
9
10
  getTestParentSpan
@@ -66,15 +67,18 @@ module.exports = (on, config) => {
66
67
  }
67
68
  })
68
69
  }
69
- return null
70
+ return activeSpan ? activeSpan._spanContext._traceId.toString(10) : null
70
71
  },
71
72
  'dd:afterEach': (test) => {
72
- const { state, error } = test
73
+ const { state, error, isRUMActive } = test
73
74
  if (activeSpan) {
74
75
  activeSpan.setTag(TEST_STATUS, CYPRESS_STATUS_TO_TEST_STATUS[state])
75
76
  if (error) {
76
77
  activeSpan.setTag('error', error)
77
78
  }
79
+ if (isRUMActive) {
80
+ activeSpan.setTag(TEST_IS_RUM_ACTIVE, true)
81
+ }
78
82
  activeSpan.finish()
79
83
  }
80
84
  activeSpan = null
@@ -3,15 +3,30 @@ beforeEach(() => {
3
3
  cy.task('dd:beforeEach', {
4
4
  testName: Cypress.mocha.getRunner().suite.ctx.currentTest.fullTitle(),
5
5
  testSuite: Cypress.mocha.getRootSuite().file
6
+ }).then(traceId => {
7
+ Cypress.env('traceId', traceId)
6
8
  })
7
9
  })
8
10
 
11
+ after(() => {
12
+ cy.window().then(win => {
13
+ win.dispatchEvent(new Event('beforeunload'))
14
+ })
15
+ })
16
+
17
+
9
18
  afterEach(() => {
10
- const currentTest = Cypress.mocha.getRunner().suite.ctx.currentTest
11
- cy.task('dd:afterEach', {
12
- testName: currentTest.fullTitle(),
13
- testSuite: Cypress.mocha.getRootSuite().file,
14
- state: currentTest.state,
15
- error: currentTest.err
19
+ cy.window().then(win => {
20
+ const currentTest = Cypress.mocha.getRunner().suite.ctx.currentTest
21
+ const testInfo = {
22
+ testName: currentTest.fullTitle(),
23
+ testSuite: Cypress.mocha.getRootSuite().file,
24
+ state: currentTest.state,
25
+ error: currentTest.err,
26
+ }
27
+ if (win.DD_RUM) {
28
+ testInfo.isRUMActive = true
29
+ }
30
+ cy.task('dd:afterEach', testInfo)
16
31
  })
17
32
  })