codeceptjs 3.7.0-beta.7 → 3.7.0-beta.8

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 (45) hide show
  1. package/lib/actor.js +1 -2
  2. package/lib/ai.js +130 -121
  3. package/lib/codecept.js +4 -4
  4. package/lib/command/check.js +4 -0
  5. package/lib/command/run-workers.js +1 -53
  6. package/lib/command/workers/runTests.js +25 -189
  7. package/lib/container.js +16 -0
  8. package/lib/event.js +18 -17
  9. package/lib/listener/exit.js +5 -8
  10. package/lib/listener/globalTimeout.js +26 -9
  11. package/lib/listener/result.js +12 -0
  12. package/lib/listener/steps.js +0 -6
  13. package/lib/mocha/asyncWrapper.js +12 -2
  14. package/lib/mocha/cli.js +50 -24
  15. package/lib/mocha/hooks.js +32 -3
  16. package/lib/mocha/suite.js +27 -1
  17. package/lib/mocha/test.js +91 -7
  18. package/lib/mocha/types.d.ts +5 -0
  19. package/lib/output.js +1 -0
  20. package/lib/plugin/analyze.js +351 -0
  21. package/lib/plugin/commentStep.js +5 -0
  22. package/lib/plugin/customReporter.js +52 -0
  23. package/lib/plugin/heal.js +2 -2
  24. package/lib/plugin/pageInfo.js +143 -0
  25. package/lib/plugin/retryTo.js +10 -2
  26. package/lib/plugin/screenshotOnFail.js +4 -6
  27. package/lib/plugin/stepTimeout.js +1 -1
  28. package/lib/plugin/tryTo.js +9 -1
  29. package/lib/recorder.js +4 -4
  30. package/lib/rerun.js +43 -42
  31. package/lib/result.js +161 -0
  32. package/lib/step/base.js +52 -4
  33. package/lib/step/helper.js +3 -0
  34. package/lib/step/meta.js +9 -1
  35. package/lib/step/record.js +5 -5
  36. package/lib/step/section.js +55 -0
  37. package/lib/steps.js +28 -1
  38. package/lib/{step/timeout.js → timeout.js} +24 -0
  39. package/lib/utils.js +35 -0
  40. package/lib/workers.js +28 -38
  41. package/package.json +2 -2
  42. package/typings/promiseBasedTypes.d.ts +12 -518
  43. package/typings/types.d.ts +75 -518
  44. package/lib/listener/artifacts.js +0 -19
  45. package/lib/plugin/debugErrors.js +0 -67
@@ -75,217 +75,53 @@ function filterTests() {
75
75
  }
76
76
 
77
77
  function initializeListeners() {
78
- function simplifyError(error) {
79
- if (error) {
80
- const { stack, uncaught, message, actual, expected } = error
81
-
82
- return {
83
- stack,
84
- uncaught,
85
- message,
86
- actual,
87
- expected,
88
- }
89
- }
90
-
91
- return null
92
- }
93
- function simplifyTest(test, err = null) {
94
- test = { ...test }
95
-
96
- if (test.start && !test.duration) {
97
- const end = new Date()
98
- test.duration = end - test.start
99
- }
100
-
101
- if (test.err) {
102
- err = simplifyError(test.err)
103
- test.status = 'failed'
104
- } else if (err) {
105
- err = simplifyError(err)
106
- test.status = 'failed'
107
- }
108
- const parent = {}
109
- if (test.parent) {
110
- parent.title = test.parent.title
111
- }
112
-
113
- if (test.opts) {
114
- Object.keys(test.opts).forEach(k => {
115
- if (typeof test.opts[k] === 'object') delete test.opts[k]
116
- if (typeof test.opts[k] === 'function') delete test.opts[k]
117
- })
118
- }
119
-
120
- return {
121
- opts: test.opts || {},
122
- tags: test.tags || [],
123
- uid: test.uid,
124
- workerIndex,
125
- retries: test._retries,
126
- title: test.title,
127
- status: test.status,
128
- notes: test.notes || [],
129
- meta: test.meta || {},
130
- artifacts: test.artifacts || [],
131
- duration: test.duration || 0,
132
- err,
133
- parent,
134
- steps: test.steps && test.steps.length > 0 ? simplifyStepsInTestObject(test.steps, err) : [],
135
- }
136
- }
137
-
138
- function simplifyStepsInTestObject(steps, err) {
139
- steps = [...steps]
140
- const _steps = []
141
-
142
- for (step of steps) {
143
- const _args = []
144
-
145
- if (step.args) {
146
- for (const arg of step.args) {
147
- // check if arg is a JOI object
148
- if (arg && arg.$_root) {
149
- _args.push(JSON.stringify(arg).slice(0, 300))
150
- // check if arg is a function
151
- } else if (arg && typeof arg === 'function') {
152
- _args.push(arg.name)
153
- } else {
154
- _args.push(arg)
155
- }
156
- }
157
- }
158
-
159
- _steps.push({
160
- actor: step.actor,
161
- name: step.name,
162
- status: step.status,
163
- args: JSON.stringify(_args),
164
- startedAt: step.startedAt,
165
- startTime: step.startTime,
166
- endTime: step.endTime,
167
- finishedAt: step.finishedAt,
168
- duration: step.duration,
169
- err,
170
- })
171
- }
172
-
173
- return _steps
174
- }
175
-
176
- function simplifyStep(step, err = null) {
177
- step = { ...step }
178
-
179
- if (step.startTime && !step.duration) {
180
- const end = new Date()
181
- step.duration = end - step.startTime
182
- }
183
-
184
- if (step.err) {
185
- err = simplifyError(step.err)
186
- step.status = 'failed'
187
- } else if (err) {
188
- err = simplifyError(err)
189
- step.status = 'failed'
190
- }
191
-
192
- const parent = {}
193
- if (step.metaStep) {
194
- parent.title = step.metaStep.actor
195
- }
196
-
197
- if (step.opts) {
198
- Object.keys(step.opts).forEach(k => {
199
- if (typeof step.opts[k] === 'object') delete step.opts[k]
200
- if (typeof step.opts[k] === 'function') delete step.opts[k]
201
- })
202
- }
203
-
204
- return {
205
- opts: step.opts || {},
206
- workerIndex,
207
- title: step.name,
208
- status: step.status,
209
- duration: step.duration || 0,
210
- err,
211
- parent,
212
- test: simplifyTest(step.test),
213
- }
214
- }
215
-
216
- collectStats()
217
78
  // suite
218
- event.dispatcher.on(event.suite.before, suite => sendToParentThread({ event: event.suite.before, workerIndex, data: simplifyTest(suite) }))
219
- event.dispatcher.on(event.suite.after, suite => sendToParentThread({ event: event.suite.after, workerIndex, data: simplifyTest(suite) }))
79
+ event.dispatcher.on(event.suite.before, suite => sendToParentThread({ event: event.suite.before, workerIndex, data: suite.simplify() }))
80
+ event.dispatcher.on(event.suite.after, suite => sendToParentThread({ event: event.suite.after, workerIndex, data: suite.simplify() }))
220
81
 
221
82
  // calculate duration
222
83
  event.dispatcher.on(event.test.started, test => (test.start = new Date()))
223
84
 
224
85
  // tests
225
- event.dispatcher.on(event.test.before, test => sendToParentThread({ event: event.test.before, workerIndex, data: simplifyTest(test) }))
226
- event.dispatcher.on(event.test.after, test => sendToParentThread({ event: event.test.after, workerIndex, data: simplifyTest(test) }))
86
+ event.dispatcher.on(event.test.before, test => sendToParentThread({ event: event.test.before, workerIndex, data: test.simplify() }))
87
+ event.dispatcher.on(event.test.after, test => sendToParentThread({ event: event.test.after, workerIndex, data: test.simplify() }))
227
88
  // we should force-send correct errors to prevent race condition
228
- event.dispatcher.on(event.test.finished, (test, err) => sendToParentThread({ event: event.test.finished, workerIndex, data: simplifyTest(test, err) }))
229
- event.dispatcher.on(event.test.failed, (test, err) => sendToParentThread({ event: event.test.failed, workerIndex, data: simplifyTest(test, err) }))
230
- event.dispatcher.on(event.test.passed, (test, err) => sendToParentThread({ event: event.test.passed, workerIndex, data: simplifyTest(test, err) }))
231
- event.dispatcher.on(event.test.started, test => sendToParentThread({ event: event.test.started, workerIndex, data: simplifyTest(test) }))
232
- event.dispatcher.on(event.test.skipped, test => sendToParentThread({ event: event.test.skipped, workerIndex, data: simplifyTest(test) }))
89
+ event.dispatcher.on(event.test.finished, (test, err) => sendToParentThread({ event: event.test.finished, workerIndex, data: { ...test.simplify(), err } }))
90
+ event.dispatcher.on(event.test.failed, (test, err) => sendToParentThread({ event: event.test.failed, workerIndex, data: { ...test.simplify(), err } }))
91
+ event.dispatcher.on(event.test.passed, (test, err) => sendToParentThread({ event: event.test.passed, workerIndex, data: { ...test.simplify(), err } }))
92
+ event.dispatcher.on(event.test.started, test => sendToParentThread({ event: event.test.started, workerIndex, data: test.simplify() }))
93
+ event.dispatcher.on(event.test.skipped, test => sendToParentThread({ event: event.test.skipped, workerIndex, data: test.simplify() }))
233
94
 
234
95
  // steps
235
- event.dispatcher.on(event.step.finished, step => sendToParentThread({ event: event.step.finished, workerIndex, data: simplifyStep(step) }))
236
- event.dispatcher.on(event.step.started, step => sendToParentThread({ event: event.step.started, workerIndex, data: simplifyStep(step) }))
237
- event.dispatcher.on(event.step.passed, step => sendToParentThread({ event: event.step.passed, workerIndex, data: simplifyStep(step) }))
238
- event.dispatcher.on(event.step.failed, step => sendToParentThread({ event: event.step.failed, workerIndex, data: simplifyStep(step) }))
96
+ event.dispatcher.on(event.step.finished, step => sendToParentThread({ event: event.step.finished, workerIndex, data: step.simplify() }))
97
+ event.dispatcher.on(event.step.started, step => sendToParentThread({ event: event.step.started, workerIndex, data: step.simplify() }))
98
+ event.dispatcher.on(event.step.passed, step => sendToParentThread({ event: event.step.passed, workerIndex, data: step.simplify() }))
99
+ event.dispatcher.on(event.step.failed, step => sendToParentThread({ event: event.step.failed, workerIndex, data: step.simplify() }))
239
100
 
240
- event.dispatcher.on(event.hook.failed, (test, err) => sendToParentThread({ event: event.hook.failed, workerIndex, data: simplifyTest(test, err) }))
241
- event.dispatcher.on(event.hook.passed, (test, err) => sendToParentThread({ event: event.hook.passed, workerIndex, data: simplifyTest(test, err) }))
242
- event.dispatcher.on(event.all.failures, data => sendToParentThread({ event: event.all.failures, workerIndex, data }))
101
+ event.dispatcher.on(event.hook.failed, (hook, err) => sendToParentThread({ event: event.hook.failed, workerIndex, data: { ...hook.simplify(), err } }))
102
+ event.dispatcher.on(event.hook.passed, hook => sendToParentThread({ event: event.hook.passed, workerIndex, data: hook.simplify() }))
103
+ event.dispatcher.on(event.hook.finished, hook => sendToParentThread({ event: event.hook.finished, workerIndex, data: hook.simplify() }))
243
104
 
105
+ event.dispatcher.once(event.all.after, () => {
106
+ sendToParentThread({ event: event.all.after, workerIndex, data: container.result().simplify() })
107
+ })
244
108
  // all
245
- event.dispatcher.once(event.all.result, () => parentPort.close())
109
+ event.dispatcher.once(event.all.result, () => {
110
+ sendToParentThread({ event: event.all.result, workerIndex, data: container.result().simplify() })
111
+ parentPort?.close()
112
+ })
246
113
  }
247
114
 
248
115
  function disablePause() {
249
116
  global.pause = () => {}
250
117
  }
251
118
 
252
- function collectStats() {
253
- const stats = {
254
- passes: 0,
255
- failures: 0,
256
- skipped: 0,
257
- tests: 0,
258
- pending: 0,
259
- }
260
- event.dispatcher.on(event.test.skipped, () => {
261
- stats.skipped++
262
- })
263
- event.dispatcher.on(event.test.passed, () => {
264
- stats.passes++
265
- })
266
- event.dispatcher.on(event.test.failed, test => {
267
- if (test.ctx._runnable.title.includes('hook: AfterSuite')) {
268
- stats.failedHooks += 1
269
- }
270
- stats.failures++
271
- })
272
- event.dispatcher.on(event.test.skipped, () => {
273
- stats.pending++
274
- })
275
- event.dispatcher.on(event.test.finished, () => {
276
- stats.tests++
277
- })
278
- event.dispatcher.once(event.all.after, () => {
279
- sendToParentThread({ event: event.all.after, data: stats })
280
- })
281
- }
282
-
283
119
  function sendToParentThread(data) {
284
- parentPort.postMessage(data)
120
+ parentPort?.postMessage(data)
285
121
  }
286
122
 
287
123
  function listenToParentThread() {
288
- parentPort.on('message', eventData => {
124
+ parentPort?.on('message', eventData => {
289
125
  container.append({ support: eventData.data })
290
126
  })
291
127
  }
package/lib/container.js CHANGED
@@ -9,6 +9,7 @@ const recorder = require('./recorder')
9
9
  const event = require('./event')
10
10
  const WorkerStorage = require('./workerStorage')
11
11
  const store = require('./store')
12
+ const Result = require('./result')
12
13
  const ai = require('./ai')
13
14
 
14
15
  let asyncHelperPromise
@@ -25,6 +26,8 @@ let container = {
25
26
  */
26
27
  mocha: {},
27
28
  translation: {},
29
+ /** @type {Result | null} */
30
+ result: null,
28
31
  }
29
32
 
30
33
  /**
@@ -54,6 +57,7 @@ class Container {
54
57
  container.translation = loadTranslation(config.translation || null, config.vocabularies || [])
55
58
  container.proxySupport = createSupportObjects(config.include || {})
56
59
  container.plugins = createPlugins(config.plugins || {}, opts)
60
+ container.result = new Result()
57
61
 
58
62
  createActor(config.include?.I)
59
63
 
@@ -127,6 +131,18 @@ class Container {
127
131
  return container.mocha
128
132
  }
129
133
 
134
+ /**
135
+ * Get result
136
+ *
137
+ * @returns {Result}
138
+ */
139
+ static result() {
140
+ if (!container.result) {
141
+ container.result = new Result()
142
+ }
143
+ return container.result
144
+ }
145
+
130
146
  /**
131
147
  * Append new services to container
132
148
  *
package/lib/event.js CHANGED
@@ -1,10 +1,10 @@
1
- const debug = require('debug')('codeceptjs:event');
2
- const events = require('events');
3
- const { error } = require('./output');
1
+ const debug = require('debug')('codeceptjs:event')
2
+ const events = require('events')
3
+ const { error } = require('./output')
4
4
 
5
- const dispatcher = new events.EventEmitter();
5
+ const dispatcher = new events.EventEmitter()
6
6
 
7
- dispatcher.setMaxListeners(50);
7
+ dispatcher.setMaxListeners(50)
8
8
  /**
9
9
  * @namespace
10
10
  * @alias event
@@ -59,6 +59,7 @@ module.exports = {
59
59
  started: 'hook.start',
60
60
  passed: 'hook.passed',
61
61
  failed: 'hook.failed',
62
+ finished: 'hook.finished',
62
63
  },
63
64
 
64
65
  /**
@@ -141,33 +142,33 @@ module.exports = {
141
142
  * @param {*} [param]
142
143
  */
143
144
  emit(event, param) {
144
- let msg = `Emitted | ${event}`;
145
+ let msg = `Emitted | ${event}`
145
146
  if (param && param.toString()) {
146
- msg += ` (${param.toString()})`;
147
+ msg += ` (${param.toString()})`
147
148
  }
148
- debug(msg);
149
+ debug(msg)
149
150
  try {
150
- this.dispatcher.emit.apply(this.dispatcher, arguments);
151
+ this.dispatcher.emit.apply(this.dispatcher, arguments)
151
152
  } catch (err) {
152
- error(`Error processing ${event} event:`);
153
- error(err.stack);
153
+ error(`Error processing ${event} event:`)
154
+ error(err.stack)
154
155
  }
155
156
  },
156
157
 
157
158
  /** for testing only! */
158
159
  cleanDispatcher: () => {
159
- let event;
160
+ let event
160
161
  for (event in this.test) {
161
- this.dispatcher.removeAllListeners(this.test[event]);
162
+ this.dispatcher.removeAllListeners(this.test[event])
162
163
  }
163
164
  for (event in this.suite) {
164
- this.dispatcher.removeAllListeners(this.test[event]);
165
+ this.dispatcher.removeAllListeners(this.test[event])
165
166
  }
166
167
  for (event in this.step) {
167
- this.dispatcher.removeAllListeners(this.test[event]);
168
+ this.dispatcher.removeAllListeners(this.test[event])
168
169
  }
169
170
  for (event in this.all) {
170
- this.dispatcher.removeAllListeners(this.test[event]);
171
+ this.dispatcher.removeAllListeners(this.test[event])
171
172
  }
172
173
  },
173
- };
174
+ }
@@ -1,20 +1,17 @@
1
1
  const event = require('../event')
2
+ const debug = require('debug')('codeceptjs:exit')
2
3
 
3
4
  module.exports = function () {
4
5
  let failedTests = []
5
6
 
6
- event.dispatcher.on(event.test.failed, testOrSuite => {
7
- // NOTE When an error happens in one of the hooks (BeforeAll/BeforeEach...) the event object
8
- // is a suite and not a test
9
- const id = testOrSuite.uid || (testOrSuite.ctx && testOrSuite.ctx.test.uid) || 'empty'
7
+ event.dispatcher.on(event.test.failed, test => {
8
+ const id = test.uid || (test.ctx && test.ctx.test.uid) || 'empty'
10
9
  failedTests.push(id)
11
10
  })
12
11
 
13
12
  // if test was successful after retries
14
- event.dispatcher.on(event.test.passed, testOrSuite => {
15
- // NOTE When an error happens in one of the hooks (BeforeAll/BeforeEach...) the event object
16
- // is a suite and not a test
17
- const id = testOrSuite.uid || (testOrSuite.ctx && testOrSuite.ctx.test.uid) || 'empty'
13
+ event.dispatcher.on(event.test.passed, test => {
14
+ const id = test.uid || (test.ctx && test.ctx.test.uid) || 'empty'
18
15
  failedTests = failedTests.filter(failed => id !== failed)
19
16
  })
20
17
 
@@ -4,7 +4,7 @@ const recorder = require('../recorder')
4
4
  const Config = require('../config')
5
5
  const store = require('../store')
6
6
  const debug = require('debug')('codeceptjs:timeout')
7
- const { TIMEOUT_ORDER } = require('../step/timeout')
7
+ const { TIMEOUT_ORDER, TimeoutError, TestTimeoutError, StepTimeoutError } = require('../timeout')
8
8
  const { BeforeSuiteHook, AfterSuiteHook } = require('../mocha/hooks')
9
9
 
10
10
  module.exports = function () {
@@ -119,24 +119,41 @@ module.exports = function () {
119
119
  }
120
120
  })
121
121
 
122
+ event.dispatcher.on(event.step.after, step => {
123
+ if (typeof timeout !== 'number') return
124
+ if (!store.timeouts) return
125
+
126
+ recorder.catchWithoutStop(err => {
127
+ // we wrap timeout errors in a StepTimeoutError
128
+ // but only if global timeout is set
129
+ // should we wrap all timeout errors?
130
+ if (err instanceof TimeoutError) {
131
+ const testTimeoutExceeded = timeout && +Date.now() - step.startTime >= timeout
132
+ debug('Step failed due to global test or suite timeout')
133
+ if (testTimeoutExceeded) {
134
+ debug('Test failed due to global test or suite timeout')
135
+ throw new TestTimeoutError(currentTimeout)
136
+ }
137
+ throw new StepTimeoutError(currentTimeout, step)
138
+ }
139
+ throw err
140
+ })
141
+ })
142
+
122
143
  event.dispatcher.on(event.step.finished, step => {
123
144
  if (!store.timeouts) {
124
145
  debug('step', step.toCode().trim(), 'timeout disabled')
125
146
  return
126
147
  }
127
148
 
149
+ if (typeof timeout === 'number') debug('Timeout', timeout)
150
+
151
+ debug(`step ${step.toCode().trim()}:${step.status} duration`, step.duration)
128
152
  if (typeof timeout === 'number' && !Number.isNaN(timeout)) timeout -= step.duration
129
153
 
130
154
  if (typeof timeout === 'number' && timeout <= 0 && recorder.isRunning()) {
131
155
  debug(`step ${step.toCode().trim()} timed out`)
132
- if (currentTest && currentTest.callback) {
133
- debug(`Failing test ${currentTest.title} with timeout ${currentTimeout}s`)
134
- recorder.reset()
135
- // replace mocha timeout with custom timeout
136
- currentTest.timeout(0)
137
- currentTest.callback(new Error(`Timeout ${currentTimeout}s exceeded (with Before hook)`))
138
- currentTest.timedOut = true
139
- }
156
+ recorder.throw(new TestTimeoutError(currentTimeout))
140
157
  }
141
158
  })
142
159
  }
@@ -0,0 +1,12 @@
1
+ const event = require('../event')
2
+ const container = require('../container')
3
+
4
+ module.exports = function () {
5
+ event.dispatcher.on(event.hook.failed, err => {
6
+ container.result().addStats({ failedHooks: 1 })
7
+ })
8
+
9
+ event.dispatcher.on(event.test.before, test => {
10
+ container.result().addTest(test)
11
+ })
12
+ }
@@ -13,7 +13,6 @@ let currentHook
13
13
  module.exports = function () {
14
14
  event.dispatcher.on(event.test.before, test => {
15
15
  test.startedAt = +new Date()
16
- test.artifacts = {}
17
16
  })
18
17
 
19
18
  event.dispatcher.on(event.test.started, test => {
@@ -71,8 +70,6 @@ module.exports = function () {
71
70
  })
72
71
 
73
72
  event.dispatcher.on(event.step.started, step => {
74
- step.startedAt = +new Date()
75
- step.test = currentTest
76
73
  store.currentStep = step
77
74
  if (currentHook && Array.isArray(currentHook.steps)) {
78
75
  return currentHook.steps.push(step)
@@ -82,9 +79,6 @@ module.exports = function () {
82
79
  })
83
80
 
84
81
  event.dispatcher.on(event.step.finished, step => {
85
- step.finishedAt = +new Date()
86
- if (step.startedAt) step.duration = step.finishedAt - step.startedAt
87
- debug(`Step '${step}' finished; Duration: ${step.duration || 0}ms`)
88
82
  store.currentStep = null
89
83
  store.stepOptions = null
90
84
  })
@@ -13,12 +13,19 @@ const injectHook = function (inject, suite) {
13
13
  recorder.throw(err)
14
14
  }
15
15
  recorder.catch(err => {
16
- event.emit(event.test.failed, suite, err)
16
+ suiteTestFailedHookError(suite, err)
17
17
  throw err
18
18
  })
19
19
  return recorder.promise()
20
20
  }
21
21
 
22
+ function suiteTestFailedHookError(suite, err) {
23
+ suite.eachTest(test => {
24
+ test.err = err
25
+ event.emit(event.test.failed, test, err)
26
+ })
27
+ }
28
+
22
29
  function makeDoneCallableOnce(done) {
23
30
  let called = false
24
31
  return function (err) {
@@ -61,6 +68,7 @@ module.exports.test = test => {
61
68
  err = newErr
62
69
  }
63
70
  }
71
+ test.err = err
64
72
  event.emit(event.test.failed, test, err)
65
73
  event.emit(event.test.finished, test)
66
74
  recorder.add(() => doneFn(err))
@@ -112,7 +120,7 @@ module.exports.injected = function (fn, suite, hookName) {
112
120
  const errHandler = err => {
113
121
  recorder.session.start('teardown')
114
122
  recorder.cleanAsyncErr()
115
- event.emit(event.test.failed, suite, err)
123
+ if (hookName == 'before' || hookName == 'beforeSuite') suiteTestFailedHookError(suite, err)
116
124
  if (hookName === 'after') event.emit(event.test.after, suite)
117
125
  if (hookName === 'afterSuite') event.emit(event.suite.after, suite)
118
126
  recorder.add(() => doneFn(err))
@@ -156,6 +164,7 @@ module.exports.injected = function (fn, suite, hookName) {
156
164
  )
157
165
  .then(() => {
158
166
  recorder.add('fire hook.passed', () => fireHook(event.hook.passed, suite))
167
+ recorder.add('fire hook.finished', () => fireHook(event.hook.finished, suite))
159
168
  recorder.add(`finish ${hookName} hook`, doneFn)
160
169
  recorder.catch()
161
170
  })
@@ -166,6 +175,7 @@ module.exports.injected = function (fn, suite, hookName) {
166
175
  errHandler(err)
167
176
  })
168
177
  recorder.add('fire hook.failed', () => fireHook(event.hook.failed, suite, e))
178
+ recorder.add('fire hook.finished', () => fireHook(event.hook.finished, suite))
169
179
  })
170
180
  }
171
181
  }