dd-trace 3.12.1 → 3.15.0

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 (101) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/README.md +5 -5
  3. package/ci/init.js +3 -1
  4. package/index.d.ts +100 -1
  5. package/package.json +5 -4
  6. package/packages/datadog-instrumentations/src/aws-sdk.js +86 -0
  7. package/packages/datadog-instrumentations/src/cucumber.js +74 -15
  8. package/packages/datadog-instrumentations/src/cypress.js +1 -1
  9. package/packages/datadog-instrumentations/src/fs.js +358 -0
  10. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  11. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  12. package/packages/datadog-instrumentations/src/jest.js +24 -23
  13. package/packages/datadog-instrumentations/src/ldapjs.js +12 -2
  14. package/packages/datadog-instrumentations/src/mocha.js +10 -7
  15. package/packages/datadog-instrumentations/src/mongoose.js +1 -1
  16. package/packages/datadog-instrumentations/src/mysql.js +7 -1
  17. package/packages/datadog-instrumentations/src/mysql2.js +7 -1
  18. package/packages/datadog-instrumentations/src/next.js +2 -1
  19. package/packages/datadog-instrumentations/src/playwright.js +263 -0
  20. package/packages/datadog-plugin-aws-sdk/src/base.js +12 -5
  21. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -2
  22. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +29 -24
  23. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +31 -16
  24. package/packages/datadog-plugin-cucumber/src/index.js +42 -11
  25. package/packages/datadog-plugin-cypress/src/plugin.js +129 -4
  26. package/packages/datadog-plugin-cypress/src/support.js +5 -0
  27. package/packages/datadog-plugin-fs/src/index.js +45 -0
  28. package/packages/datadog-plugin-hapi/src/index.js +5 -1
  29. package/packages/datadog-plugin-http/src/server.js +1 -1
  30. package/packages/datadog-plugin-http2/src/server.js +1 -1
  31. package/packages/datadog-plugin-jest/src/index.js +40 -70
  32. package/packages/datadog-plugin-mocha/src/index.js +44 -64
  33. package/packages/datadog-plugin-mysql/src/index.js +8 -7
  34. package/packages/datadog-plugin-playwright/src/index.js +112 -0
  35. package/packages/datadog-shimmer/src/shimmer.js +28 -11
  36. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  37. package/packages/dd-trace/src/appsec/blocking.js +35 -9
  38. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +1 -1
  39. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  40. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +60 -0
  41. package/packages/dd-trace/src/appsec/iast/iast-context.js +6 -2
  42. package/packages/dd-trace/src/appsec/iast/index.js +3 -2
  43. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +5 -2
  44. package/packages/dd-trace/src/appsec/index.js +5 -5
  45. package/packages/dd-trace/src/appsec/recommended.json +320 -184
  46. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  47. package/packages/dd-trace/src/appsec/remote_config/index.js +3 -0
  48. package/packages/dd-trace/src/appsec/reporter.js +14 -14
  49. package/packages/dd-trace/src/appsec/sdk/index.js +41 -0
  50. package/packages/dd-trace/src/appsec/sdk/noop.js +17 -0
  51. package/packages/dd-trace/src/appsec/sdk/set_user.js +30 -0
  52. package/packages/dd-trace/src/appsec/sdk/track_event.js +74 -0
  53. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +73 -0
  54. package/packages/dd-trace/src/appsec/sdk/utils.js +10 -0
  55. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +1 -5
  56. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -5
  57. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +48 -11
  58. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +7 -1
  59. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +4 -2
  60. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +5 -3
  61. package/packages/dd-trace/src/config.js +63 -7
  62. package/packages/dd-trace/src/encode/0.4.js +1 -1
  63. package/packages/dd-trace/src/encode/0.5.js +1 -1
  64. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +44 -4
  65. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +52 -37
  66. package/packages/dd-trace/src/encode/tags-processors.js +3 -2
  67. package/packages/dd-trace/src/exporters/common/request.js +10 -3
  68. package/packages/dd-trace/src/lambda/handler.js +5 -6
  69. package/packages/dd-trace/src/log/channels.js +47 -0
  70. package/packages/dd-trace/src/log/index.js +79 -0
  71. package/packages/dd-trace/src/log/writer.js +124 -0
  72. package/packages/dd-trace/src/metrics.js +18 -0
  73. package/packages/dd-trace/src/noop/proxy.js +5 -2
  74. package/packages/dd-trace/src/opentracing/propagation/text_map.js +188 -36
  75. package/packages/dd-trace/src/opentracing/propagation/tracestate.js +99 -0
  76. package/packages/dd-trace/src/opentracing/span.js +2 -1
  77. package/packages/dd-trace/src/opentracing/span_context.js +6 -3
  78. package/packages/dd-trace/src/plugins/ci_plugin.js +72 -12
  79. package/packages/dd-trace/src/plugins/index.js +2 -0
  80. package/packages/dd-trace/src/plugins/util/ci.js +13 -21
  81. package/packages/dd-trace/src/plugins/util/exec.js +2 -2
  82. package/packages/dd-trace/src/plugins/util/git.js +16 -1
  83. package/packages/dd-trace/src/{appsec → plugins/util}/ip_extractor.js +1 -1
  84. package/packages/dd-trace/src/plugins/util/test.js +53 -10
  85. package/packages/dd-trace/src/plugins/util/user-provided-git.js +2 -7
  86. package/packages/dd-trace/src/plugins/util/web.js +11 -0
  87. package/packages/dd-trace/src/profiler.js +3 -0
  88. package/packages/dd-trace/src/profiling/config.js +8 -3
  89. package/packages/dd-trace/src/profiling/exporters/file.js +13 -2
  90. package/packages/dd-trace/src/profiling/profiler.js +23 -6
  91. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
  92. package/packages/dd-trace/src/proxy.js +2 -0
  93. package/packages/dd-trace/src/span_processor.js +1 -1
  94. package/packages/dd-trace/src/span_sampler.js +68 -52
  95. package/packages/dd-trace/src/startup-log.js +3 -6
  96. package/packages/dd-trace/src/telemetry/index.js +23 -2
  97. package/packages/dd-trace/src/telemetry/send-data.js +4 -1
  98. package/packages/dd-trace/src/tracer.js +0 -16
  99. package/scripts/check-proposal-labels.js +71 -0
  100. package/packages/dd-trace/src/log.js +0 -143
  101. /package/packages/dd-trace/src/{appsec → plugins/util}/ip_blocklist.js +0 -0
@@ -57,6 +57,7 @@ dev,multer,MIT,Copyright 2014 Hage Yaapa
57
57
  dev,msgpack-lite,MIT,Copyright 2015 Yusuke Kawasaki
58
58
  dev,nock,MIT,Copyright 2017 Pedro Teixeira and other contributors
59
59
  dev,nyc,ISC,Copyright 2015 Contributors
60
+ dev,pprof-format,MIT,Copyright 2022 Stephen Belanger
60
61
  dev,proxyquire,MIT,Copyright 2013 Thorsten Lorenz
61
62
  dev,rimraf,ISC,Copyright Isaac Z. Schlueter and Contributors
62
63
  dev,sinon,BSD-3-Clause,Copyright 2010-2017 Christian Johansen
package/README.md CHANGED
@@ -24,11 +24,11 @@ Most of the documentation for `dd-trace` is available on these webpages:
24
24
 
25
25
  ## Version Release Lines and Maintenance
26
26
 
27
- | Release Line | Latest Version | Status |Initial Release | End of Life |
28
- | :--: | :--: | :---: | :---: | :---: |
29
- | [`v1`](https://github.com/DataDog/dd-trace-js/tree/v1.x) | ![npm v1](https://img.shields.io/npm/v/dd-trace/legacy-v1?color=white&label=%20&style=flat-square) | **End of Life** | 2021-07-13 | 2022-02-25 |
30
- | [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) | ![npm v2](https://img.shields.io/npm/v/dd-trace/latest-node12?color=white&label=%20&style=flat-square) | **Maintenance** | 2022-01-28 | 2023-08-15 |
31
- | [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) | ![npm v3](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | **Current** | 2022-08-15 | Unknown |
27
+ | Release Line | Latest Version | Node.js | Status |Initial Release | End of Life |
28
+ | :---: | :---: | :---: | :---: | :---: | :---: |
29
+ | [`v1`](https://github.com/DataDog/dd-trace-js/tree/v1.x) | ![npm v1](https://img.shields.io/npm/v/dd-trace/legacy-v1?color=white&label=%20&style=flat-square) | `>= v12` | **End of Life** | 2021-07-13 | 2022-02-25 |
30
+ | [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) | ![npm v2](https://img.shields.io/npm/v/dd-trace/latest-node12?color=white&label=%20&style=flat-square) | `>= v12` | **Maintenance** | 2022-01-28 | 2023-08-15 |
31
+ | [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) | ![npm v3](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v14` | **Current** | 2022-08-15 | Unknown |
32
32
 
33
33
  We currently maintain two release lines, namely `v2` and `v3`.
34
34
  Features and bug fixes that are merged are released to the `v3` line and, if appropriate, also the `v2` line.
package/ci/init.js CHANGED
@@ -8,7 +8,8 @@ const options = {
8
8
  tags: {
9
9
  [ORIGIN_KEY]: 'ciapp-test'
10
10
  },
11
- isCiVisibility: true
11
+ isCiVisibility: true,
12
+ flushInterval: 5000
12
13
  }
13
14
 
14
15
  let shouldInit = true
@@ -34,6 +35,7 @@ so dd-trace will not be initialized.`)
34
35
 
35
36
  if (shouldInit) {
36
37
  tracer.init(options)
38
+ tracer.use('fs', false)
37
39
  }
38
40
 
39
41
  module.exports = tracer
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ClientRequest, IncomingMessage, ServerResponse } from "http";
1
+ import { ClientRequest, IncomingMessage, OutgoingMessage, ServerResponse } from "http";
2
2
  import { LookupFunction } from 'net';
3
3
  import * as opentracing from "opentracing";
4
4
  import { SpanOptions } from "opentracing/lib/tracer";
@@ -115,6 +115,8 @@ export declare interface Tracer extends opentracing.Tracer {
115
115
  * @returns {Tracer} The Tracer instance for chaining.
116
116
  */
117
117
  setUser (user: User): Tracer;
118
+
119
+ appsec: Appsec;
118
120
  }
119
121
 
120
122
  export declare interface TraceOptions extends Analyzable {
@@ -219,6 +221,21 @@ export declare interface SpanSamplingRule {
219
221
  name?: string
220
222
  }
221
223
 
224
+ /**
225
+ * Selection and priority order of context propagation injection and extraction mechanisms.
226
+ */
227
+ export declare interface PropagationStyle {
228
+ /**
229
+ * Selection of context propagation injection mechanisms.
230
+ */
231
+ inject: string[],
232
+
233
+ /**
234
+ * Selection and priority order of context propagation extraction mechanisms.
235
+ */
236
+ extract: string[]
237
+ }
238
+
222
239
  /**
223
240
  * List of options available to the tracer.
224
241
  */
@@ -537,6 +554,22 @@ export declare interface TracerOptions {
537
554
  */
538
555
  pollInterval?: number,
539
556
  }
557
+
558
+ /**
559
+ * Whether to enable client IP collection from relevant IP headers
560
+ * @default false
561
+ */
562
+ clientIpEnabled?: boolean
563
+
564
+ /**
565
+ * Custom header name to source the http.client_ip tag from.
566
+ */
567
+ clientIpHeader?: string,
568
+
569
+ /**
570
+ * The selection and priority order of context propagation injection and extraction mechanisms.
571
+ */
572
+ propagationStyle?: string[] | PropagationStyle
540
573
  }
541
574
 
542
575
  /**
@@ -582,6 +615,65 @@ export declare interface User {
582
615
  [key: string]: string | undefined
583
616
  }
584
617
 
618
+ export declare interface Appsec {
619
+ /**
620
+ * Links a successful login event to the current trace. Will link the passed user to the current trace with Appsec.setUser() internally.
621
+ * @param {User} user Properties of the authenticated user. Accepts custom fields.
622
+ * @param {[key: string]: string} metadata Custom fields to link to the login success event.
623
+ *
624
+ * @beta This method is in beta and could change in future versions.
625
+ */
626
+ trackUserLoginSuccessEvent(user: User, metadata?: { [key: string]: string }): void
627
+
628
+ /**
629
+ * Links a failed login event to the current trace.
630
+ * @param {string} userId The user id of the attemped login.
631
+ * @param {boolean} exists If the user id exists.
632
+ * @param {[key: string]: string} metadata Custom fields to link to the login failure event.
633
+ *
634
+ * @beta This method is in beta and could change in future versions.
635
+ */
636
+ trackUserLoginFailureEvent(userId: string, exists: boolean, metadata?: { [key: string]: string }): void
637
+
638
+ /**
639
+ * Links a custom event to the current trace.
640
+ * @param {string} eventName The name of the event.
641
+ * @param {[key: string]: string} metadata Custom fields to link to the event.
642
+ *
643
+ * @beta This method is in beta and could change in future versions.
644
+ */
645
+ trackCustomEvent(eventName: string, metadata?: { [key: string]: string }): void
646
+
647
+ /**
648
+ * Checks if the passed user should be blocked according to AppSec rules.
649
+ * If no user is linked to the current trace, will link the passed user to it.
650
+ * @param {User} user Properties of the authenticated user. Accepts custom fields.
651
+ * @return {boolean} Indicates whether the user should be blocked.
652
+ *
653
+ * @beta This method is in beta and could change in the future
654
+ */
655
+ isUserBlocked(user: User): boolean
656
+
657
+ /**
658
+ * Sends a "blocked" template response based on the request accept header and ends the response.
659
+ * **You should stop processing the request after calling this function!**
660
+ * @param {IncomingMessage} req Can be passed to force which request to act on. Optional.
661
+ * @param {OutgoingMessage} res Can be passed to force which response to act on. Optional.
662
+ * @return {boolean} Indicates if the action was successful.
663
+ *
664
+ * @beta This method is in beta and could change in the future
665
+ */
666
+ blockRequest(req?: IncomingMessage, res?: OutgoingMessage): boolean
667
+
668
+ /**
669
+ * Links an authenticated user to the current trace.
670
+ * @param {User} user Properties of the authenticated user. Accepts custom fields.
671
+ *
672
+ * @beta This method is in beta and could change in the future
673
+ */
674
+ setUser(user: User): void
675
+ }
676
+
585
677
  /** @hidden */
586
678
  declare type anyObject = {
587
679
  [key: string]: any;
@@ -669,6 +761,7 @@ interface Plugins {
669
761
  "opensearch": plugins.opensearch;
670
762
  "oracledb": plugins.oracledb;
671
763
  "paperplane": plugins.paperplane;
764
+ "playwright": plugins.playwright;
672
765
  "pg": plugins.pg;
673
766
  "pino": plugins.pino;
674
767
  "redis": plugins.redis;
@@ -1358,6 +1451,12 @@ declare namespace plugins {
1358
1451
  */
1359
1452
  interface paperplane extends HttpServer {}
1360
1453
 
1454
+ /**
1455
+ * This plugin automatically instruments the
1456
+ * [playwright](https://github.com/microsoft/playwright) module.
1457
+ */
1458
+ interface playwright extends Integration {}
1459
+
1361
1460
  /**
1362
1461
  * This plugin automatically instruments the
1363
1462
  * [pg](https://node-postgres.com/) module.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "3.12.1",
3
+ "version": "3.15.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -61,10 +61,10 @@
61
61
  },
62
62
  "dependencies": {
63
63
  "@datadog/native-appsec": "2.0.0",
64
- "@datadog/native-iast-rewriter": "1.1.2",
65
- "@datadog/native-iast-taint-tracking": "1.1.0",
64
+ "@datadog/native-iast-rewriter": "2.0.1",
65
+ "@datadog/native-iast-taint-tracking": "1.1.1",
66
66
  "@datadog/native-metrics": "^1.5.0",
67
- "@datadog/pprof": "^1.1.1",
67
+ "@datadog/pprof": "^2.0.0",
68
68
  "@datadog/sketches-js": "^2.1.0",
69
69
  "crypto-randomuuid": "^1.0.0",
70
70
  "diagnostics_channel": "^1.1.0",
@@ -120,6 +120,7 @@
120
120
  "multer": "^1.4.5-lts.1",
121
121
  "nock": "^11.3.3",
122
122
  "nyc": "^15.1.0",
123
+ "pprof-format": "^2.0.7",
123
124
  "proxyquire": "^1.8.0",
124
125
  "rimraf": "^3.0.0",
125
126
  "sinon": "^11.1.2",
@@ -39,6 +39,77 @@ function wrapRequest (send) {
39
39
  }
40
40
  }
41
41
 
42
+ function wrapSmithySend (send) {
43
+ return function (command, ...args) {
44
+ const cb = args[args.length - 1]
45
+ const innerAr = new AsyncResource('apm:aws:request:inner')
46
+ const outerAr = new AsyncResource('apm:aws:request:outer')
47
+ const serviceIdentifier = this.config.serviceId.toLowerCase()
48
+ const channelSuffix = getChannelSuffix(serviceIdentifier)
49
+ const commandName = command.constructor.name
50
+ const clientName = this.constructor.name.replace(/Client$/, '')
51
+ const operation = `${commandName[0].toLowerCase()}${commandName.slice(1).replace(/Command$/, '')}`
52
+ const request = {
53
+ operation,
54
+ params: command.input
55
+ }
56
+
57
+ const startCh = channel(`apm:aws:request:start:${channelSuffix}`)
58
+ const regionCh = channel(`apm:aws:request:region:${channelSuffix}`)
59
+ const completeChannel = channel(`apm:aws:request:complete:${channelSuffix}`)
60
+ const responseStartChannel = channel(`apm:aws:response:start:${channelSuffix}`)
61
+ const responseFinishChannel = channel(`apm:aws:response:finish:${channelSuffix}`)
62
+
63
+ return innerAr.runInAsyncScope(() => {
64
+ startCh.publish({
65
+ serviceIdentifier,
66
+ operation,
67
+ awsService: clientName,
68
+ request
69
+ })
70
+
71
+ // When the region is not set this never resolves so we can't await.
72
+ this.config.region().then(region => {
73
+ regionCh.publish(region)
74
+ })
75
+
76
+ if (typeof cb === 'function') {
77
+ args[args.length - 1] = function (err, result) {
78
+ const message = getMessage(request, err, result)
79
+
80
+ completeChannel.publish(message)
81
+
82
+ outerAr.runInAsyncScope(() => {
83
+ responseStartChannel.publish(message)
84
+
85
+ cb.apply(this, arguments)
86
+
87
+ if (message.needsFinish) {
88
+ responseFinishChannel.publish(message.response.error)
89
+ }
90
+ })
91
+ }
92
+ } else { // always a promise
93
+ return send.call(this, command, ...args)
94
+ .then(
95
+ result => {
96
+ const message = getMessage(request, null, result)
97
+ completeChannel.publish(message)
98
+ return result
99
+ },
100
+ error => {
101
+ const message = getMessage(request, error)
102
+ completeChannel.publish(message)
103
+ throw error
104
+ }
105
+ )
106
+ }
107
+
108
+ return send.call(this, command, ...args)
109
+ })
110
+ }
111
+ }
112
+
42
113
  function wrapCb (cb, serviceName, request, ar) {
43
114
  return function wrappedCb (err, response) {
44
115
  const obj = { request, response }
@@ -71,6 +142,16 @@ function wrapCb (cb, serviceName, request, ar) {
71
142
  }
72
143
  }
73
144
 
145
+ function getMessage (request, error, result) {
146
+ const response = { request, error, ...result }
147
+
148
+ if (result && result.$metadata) {
149
+ response.requestId = result.$metadata.requestId
150
+ }
151
+
152
+ return { request, response }
153
+ }
154
+
74
155
  function getChannelSuffix (name) {
75
156
  return [
76
157
  'cloudwatchlogs',
@@ -85,6 +166,11 @@ function getChannelSuffix (name) {
85
166
  ].includes(name) ? name : 'default'
86
167
  }
87
168
 
169
+ addHook({ name: '@aws-sdk/smithy-client', versions: ['>=3'] }, smithy => {
170
+ shimmer.wrap(smithy.Client.prototype, 'send', wrapSmithySend)
171
+ return smithy
172
+ })
173
+
88
174
  addHook({ name: 'aws-sdk', versions: ['>=2.3.0'] }, AWS => {
89
175
  shimmer.wrap(AWS.Request.prototype, 'promise', wrapRequest)
90
176
  shimmer.wrap(AWS.config, 'setPromisesDependency', setPromisesDependency => {
@@ -3,15 +3,35 @@
3
3
  const { addHook, channel, AsyncResource } = require('./helpers/instrument')
4
4
  const shimmer = require('../../datadog-shimmer')
5
5
 
6
- const runStartCh = channel('ci:cucumber:run:start')
7
- const runFinishCh = channel('ci:cucumber:run:finish')
8
- const runStepStartCh = channel('ci:cucumber:run-step:start')
6
+ const testStartCh = channel('ci:cucumber:test:start')
7
+ const testFinishCh = channel('ci:cucumber:test:finish') // used for test steps too
8
+
9
+ const testStepStartCh = channel('ci:cucumber:test-step:start')
10
+
9
11
  const errorCh = channel('ci:cucumber:error')
12
+
13
+ const testSuiteStartCh = channel('ci:cucumber:test-suite:start')
14
+ const testSuiteFinishCh = channel('ci:cucumber:test-suite:finish')
15
+
16
+ const sessionStartCh = channel('ci:cucumber:session:start')
10
17
  const sessionFinishCh = channel('ci:cucumber:session:finish')
11
18
 
12
19
  // TODO: remove in a later major version
13
20
  const patched = new WeakSet()
14
21
 
22
+ let pickleByFile = {}
23
+ const pickleResultByFile = {}
24
+
25
+ function getSuiteStatusFromTestStatuses (testStatuses) {
26
+ if (testStatuses.some(status => status === 'fail')) {
27
+ return 'fail'
28
+ }
29
+ if (testStatuses.every(status => status === 'skip')) {
30
+ return 'skip'
31
+ }
32
+ return 'pass'
33
+ }
34
+
15
35
  function getStatusFromResult (result) {
16
36
  if (result.status === 1) {
17
37
  return { status: 'pass' }
@@ -44,13 +64,18 @@ function wrapRun (pl, isLatestVersion) {
44
64
  patched.add(pl)
45
65
 
46
66
  shimmer.wrap(pl.prototype, 'run', run => function () {
47
- if (!runStartCh.hasSubscribers) {
67
+ if (!testStartCh.hasSubscribers) {
48
68
  return run.apply(this, arguments)
49
69
  }
50
70
 
51
71
  const asyncResource = new AsyncResource('bound-anonymous-fn')
52
72
  return asyncResource.runInAsyncScope(() => {
53
- runStartCh.publish({ testName: this.pickle.name, fullTestSuite: this.pickle.uri })
73
+ const testSuiteFullPath = this.pickle.uri
74
+
75
+ if (!pickleResultByFile[testSuiteFullPath]) { // first test in suite
76
+ testSuiteStartCh.publish(testSuiteFullPath)
77
+ }
78
+ testStartCh.publish({ testName: this.pickle.name, fullTestSuite: testSuiteFullPath })
54
79
  try {
55
80
  const promise = run.apply(this, arguments)
56
81
  promise.finally(() => {
@@ -58,7 +83,17 @@ function wrapRun (pl, isLatestVersion) {
58
83
  const { status, skipReason, errorMessage } = isLatestVersion
59
84
  ? getStatusFromResultLatest(result) : getStatusFromResult(result)
60
85
 
61
- runFinishCh.publish({ status, skipReason, errorMessage })
86
+ if (!pickleResultByFile[testSuiteFullPath]) {
87
+ pickleResultByFile[testSuiteFullPath] = [status]
88
+ } else {
89
+ pickleResultByFile[testSuiteFullPath].push(status)
90
+ }
91
+ // last test in suite
92
+ if (pickleResultByFile[testSuiteFullPath].length === pickleByFile[testSuiteFullPath].length) {
93
+ const testSuiteStatus = getSuiteStatusFromTestStatuses(pickleResultByFile[testSuiteFullPath])
94
+ testSuiteFinishCh.publish(testSuiteStatus)
95
+ }
96
+ testFinishCh.publish({ status, skipReason, errorMessage })
62
97
  })
63
98
  return promise
64
99
  } catch (err) {
@@ -68,7 +103,7 @@ function wrapRun (pl, isLatestVersion) {
68
103
  })
69
104
  })
70
105
  shimmer.wrap(pl.prototype, 'runStep', runStep => function () {
71
- if (!runStepStartCh.hasSubscribers) {
106
+ if (!testStepStartCh.hasSubscribers) {
72
107
  return runStep.apply(this, arguments)
73
108
  }
74
109
  const testStep = arguments[0]
@@ -82,7 +117,7 @@ function wrapRun (pl, isLatestVersion) {
82
117
 
83
118
  const asyncResource = new AsyncResource('bound-anonymous-fn')
84
119
  return asyncResource.runInAsyncScope(() => {
85
- runStepStartCh.publish({ resource })
120
+ testStepStartCh.publish({ resource })
86
121
  try {
87
122
  const promise = runStep.apply(this, arguments)
88
123
 
@@ -90,7 +125,7 @@ function wrapRun (pl, isLatestVersion) {
90
125
  const { status, skipReason, errorMessage } = isLatestVersion
91
126
  ? getStatusFromResultLatest(result) : getStatusFromResult(result)
92
127
 
93
- runFinishCh.publish({ isStep: true, status, skipReason, errorMessage })
128
+ testFinishCh.publish({ isStep: true, status, skipReason, errorMessage })
94
129
  })
95
130
  return promise
96
131
  } catch (err) {
@@ -129,16 +164,40 @@ addHook({
129
164
  file: 'lib/runtime/test_case_runner.js'
130
165
  }, testCaseHook)
131
166
 
167
+ function getPickleByFile (runtime) {
168
+ return runtime.pickleIds.reduce((acc, pickleId) => {
169
+ const test = runtime.eventDataCollector.getPickle(pickleId)
170
+ if (acc[test.uri]) {
171
+ acc[test.uri].push(test)
172
+ } else {
173
+ acc[test.uri] = [test]
174
+ }
175
+ return acc
176
+ }, {})
177
+ }
178
+
132
179
  addHook({
133
180
  name: '@cucumber/cucumber',
134
181
  versions: ['>=7.0.0'],
135
182
  file: 'lib/runtime/index.js'
136
- }, (Runtime) => {
137
- shimmer.wrap(Runtime.default.prototype, 'start', start => async function () {
138
- const result = await start.apply(this, arguments)
139
- sessionFinishCh.publish(undefined)
140
- return result
183
+ }, (runtimePackage, cucumberVersion) => {
184
+ shimmer.wrap(runtimePackage.default.prototype, 'start', start => async function () {
185
+ pickleByFile = getPickleByFile(this)
186
+
187
+ const processArgv = process.argv.slice(2).join(' ')
188
+ const command = process.env.npm_lifecycle_script || `cucumber-js ${processArgv}`
189
+
190
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
191
+ asyncResource.runInAsyncScope(() => {
192
+ sessionStartCh.publish({ command, frameworkVersion: cucumberVersion })
193
+ })
194
+ const success = await start.apply(this, arguments)
195
+
196
+ asyncResource.runInAsyncScope(() => {
197
+ sessionFinishCh.publish(success ? 'pass' : 'fail')
198
+ })
199
+ return success
141
200
  })
142
201
 
143
- return Runtime
202
+ return runtimePackage
144
203
  })
@@ -5,4 +5,4 @@ const { addHook } = require('./helpers/instrument')
5
5
  addHook({
6
6
  name: 'cypress',
7
7
  versions: ['>=6.7.0']
8
- })
8
+ }, lib => lib)