codeceptjs 3.7.0-beta.7 → 3.7.0-beta.9

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 (52) 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/els.js +87 -106
  9. package/lib/event.js +18 -17
  10. package/lib/helper/Playwright.js +7 -1
  11. package/lib/listener/exit.js +5 -8
  12. package/lib/listener/globalTimeout.js +26 -9
  13. package/lib/listener/result.js +12 -0
  14. package/lib/listener/steps.js +0 -6
  15. package/lib/listener/store.js +9 -1
  16. package/lib/mocha/asyncWrapper.js +12 -2
  17. package/lib/mocha/cli.js +65 -31
  18. package/lib/mocha/hooks.js +32 -3
  19. package/lib/mocha/suite.js +27 -1
  20. package/lib/mocha/test.js +91 -7
  21. package/lib/mocha/types.d.ts +5 -0
  22. package/lib/output.js +2 -1
  23. package/lib/plugin/analyze.js +348 -0
  24. package/lib/plugin/commentStep.js +5 -0
  25. package/lib/plugin/customReporter.js +52 -0
  26. package/lib/plugin/heal.js +2 -2
  27. package/lib/plugin/pageInfo.js +140 -0
  28. package/lib/plugin/retryTo.js +10 -2
  29. package/lib/plugin/screenshotOnFail.js +11 -16
  30. package/lib/plugin/stepByStepReport.js +5 -4
  31. package/lib/plugin/stepTimeout.js +1 -1
  32. package/lib/plugin/tryTo.js +9 -1
  33. package/lib/recorder.js +4 -4
  34. package/lib/rerun.js +43 -42
  35. package/lib/result.js +161 -0
  36. package/lib/step/base.js +52 -4
  37. package/lib/step/func.js +46 -0
  38. package/lib/step/helper.js +3 -0
  39. package/lib/step/meta.js +9 -1
  40. package/lib/step/record.js +5 -5
  41. package/lib/step/section.js +55 -0
  42. package/lib/step.js +6 -0
  43. package/lib/steps.js +28 -1
  44. package/lib/store.js +2 -0
  45. package/lib/{step/timeout.js → timeout.js} +24 -0
  46. package/lib/utils.js +35 -0
  47. package/lib/workers.js +28 -38
  48. package/package.json +7 -6
  49. package/typings/promiseBasedTypes.d.ts +104 -0
  50. package/typings/types.d.ts +104 -0
  51. package/lib/listener/artifacts.js +0 -19
  52. package/lib/plugin/debugErrors.js +0 -67
@@ -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
  })
@@ -2,11 +2,19 @@ const event = require('../event')
2
2
  const store = require('../store')
3
3
 
4
4
  module.exports = function () {
5
+ event.dispatcher.on(event.suite.before, suite => {
6
+ store.currentSuite = suite
7
+ })
8
+
9
+ event.dispatcher.on(event.suite.after, () => {
10
+ store.currentSuite = null
11
+ })
12
+
5
13
  event.dispatcher.on(event.test.before, test => {
6
14
  store.currentTest = test
7
15
  })
8
16
 
9
- event.dispatcher.on(event.test.finished, test => {
17
+ event.dispatcher.on(event.test.finished, () => {
10
18
  store.currentTest = null
11
19
  })
12
20
  }
@@ -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, hookName) {
23
+ suite.eachTest(test => {
24
+ test.err = err
25
+ event.emit(event.test.failed, test, err, ucfirst(hookName))
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, hookName)
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
  }
package/lib/mocha/cli.js CHANGED
@@ -1,12 +1,12 @@
1
1
  const {
2
2
  reporters: { Base },
3
3
  } = require('mocha')
4
- const figures = require('figures')
5
4
  const ms = require('ms')
5
+ const figures = require('figures')
6
6
  const event = require('../event')
7
7
  const AssertionFailedError = require('../assert/error')
8
8
  const output = require('../output')
9
-
9
+ const { cloneTest } = require('./test')
10
10
  const cursor = Base.cursor
11
11
  let currentMetaStep = []
12
12
  let codeceptjsEventDispatchersRegistered = false
@@ -32,6 +32,16 @@ class Cli extends Base {
32
32
  output.print(output.styles.debug(`Plugins: ${Object.keys(Containter.plugins()).join(', ')}`))
33
33
  }
34
34
 
35
+ if (level >= 3) {
36
+ process.on('warning', warning => {
37
+ console.log('\nWarning Details:')
38
+ console.log('Name:', warning.name)
39
+ console.log('Message:', warning.message)
40
+ console.log('Stack:', warning.stack)
41
+ console.log('-------------------')
42
+ })
43
+ }
44
+
35
45
  runner.on('start', () => {
36
46
  console.log()
37
47
  })
@@ -91,9 +101,11 @@ class Cli extends Base {
91
101
  event.dispatcher.on(event.step.started, step => {
92
102
  let processingStep = step
93
103
  const metaSteps = []
104
+ let isHidden = false
94
105
  while (processingStep.metaStep) {
95
106
  metaSteps.unshift(processingStep.metaStep)
96
107
  processingStep = processingStep.metaStep
108
+ if (processingStep.collapsed) isHidden = true
97
109
  }
98
110
  const shift = metaSteps.length
99
111
 
@@ -107,10 +119,9 @@ class Cli extends Base {
107
119
  }
108
120
  }
109
121
  currentMetaStep = metaSteps
122
+ if (isHidden) return
110
123
  output.stepShift = 3 + 2 * shift
111
- if (step.helper.constructor.name !== 'ExpectHelper') {
112
- output.step(step)
113
- }
124
+ output.step(step)
114
125
  })
115
126
 
116
127
  event.dispatcher.on(event.step.finished, () => {
@@ -138,16 +149,19 @@ class Cli extends Base {
138
149
  }
139
150
  }
140
151
 
141
- this.stats.pending += skippedCount
142
- this.stats.tests += skippedCount
152
+ const container = require('../container')
153
+ container.result().addStats({ pending: skippedCount, tests: skippedCount })
143
154
  })
144
155
 
145
156
  runner.on('end', this.result.bind(this))
146
157
  }
147
158
 
148
159
  result() {
149
- const stats = this.stats
150
- stats.failedHooks = 0
160
+ const container = require('../container')
161
+ container.result().addStats(this.stats)
162
+ container.result().finish()
163
+
164
+ const stats = container.result().stats
151
165
  console.log()
152
166
 
153
167
  // passes
@@ -160,7 +174,8 @@ class Cli extends Base {
160
174
  // failures
161
175
  if (stats.failures) {
162
176
  // append step traces
163
- this.failures.map(test => {
177
+ this.failures = this.failures.map(test => {
178
+ // we will change the stack trace, so we need to clone the test
164
179
  const err = test.err
165
180
 
166
181
  let log = ''
@@ -170,20 +185,38 @@ class Cli extends Base {
170
185
  err.message = err.inspect()
171
186
  }
172
187
 
173
- // multi-line error messages
174
- err.message = '\n ' + (err.message || '').replace(/^/gm, ' ').trim()
188
+ // multi-line error messages (for Playwright)
189
+ if (err.message && err.message.includes('\n')) {
190
+ const lines = err.message.split('\n')
191
+ const truncatedLines = lines.slice(0, 5)
192
+ if (lines.length > 5) {
193
+ truncatedLines.push('...')
194
+ }
195
+ err.message = truncatedLines.join('\n').replace(/^/gm, ' ').trim()
196
+ }
197
+
198
+ // add new line before the message
199
+ err.message = '\n ' + err.message
200
+
201
+ // explicitly show file with error
202
+ if (test.file) {
203
+ log += `\n${output.styles.basic(figures.circle)} ${output.styles.section('File:')} ${output.styles.basic(test.file)}\n`
204
+ }
175
205
 
176
206
  const steps = test.steps || (test.ctx && test.ctx.test.steps)
177
207
 
178
208
  if (steps && steps.length) {
179
209
  let scenarioTrace = ''
180
- steps.reverse().forEach(step => {
181
- const hasFailed = step.status === 'failed'
182
- let line = `${hasFailed ? output.styles.bold(figures.cross) : figures.tick} ${step.toCode()} ${step.line()}`
183
- if (hasFailed) line = output.styles.bold(line)
184
- scenarioTrace += `\n${line}`
185
- })
186
- log += `${output.styles.basic(figures.circle)} ${output.styles.section('Scenario Steps')}:${scenarioTrace}\n`
210
+ steps
211
+ .reverse()
212
+ .slice(0, 10)
213
+ .forEach(step => {
214
+ const hasFailed = step.status === 'failed'
215
+ let line = `${hasFailed ? output.styles.bold(figures.cross) : figures.tick} ${step.toCode()} ${step.line()}`
216
+ if (hasFailed) line = output.styles.bold(line)
217
+ scenarioTrace += `\n${line}`
218
+ })
219
+ log += `\n${output.styles.basic(figures.circle)} ${output.styles.section('Scenario Steps')}:${scenarioTrace}\n`
187
220
  }
188
221
 
189
222
  // display artifacts in debug mode
@@ -204,25 +237,30 @@ class Cli extends Base {
204
237
 
205
238
  try {
206
239
  let stack = err.stack
207
- stack = stack.replace(originalMessage, '')
240
+ stack = (stack || '').replace(originalMessage, '')
208
241
  stack = stack ? stack.split('\n') : []
209
242
 
210
243
  if (stack[0] && stack[0].includes(err.message)) {
211
244
  stack.shift()
212
245
  }
213
246
 
247
+ if (stack[0] && stack[0].trim() == 'Error:') {
248
+ stack.shift()
249
+ }
250
+
214
251
  if (output.level() < 3) {
215
252
  stack = stack.slice(0, 3)
216
253
  }
217
254
 
218
255
  err.stack = `${stack.join('\n')}\n\n${output.colors.blue(log)}`
219
-
220
- // clone err object so stack trace adjustments won't affect test other reports
221
- test.err = err
222
- return test
223
256
  } catch (e) {
224
- throw Error(e)
257
+ console.error(e)
225
258
  }
259
+
260
+ // we will change the stack trace, so we need to clone the test
261
+ test = cloneTest(test)
262
+ test.err = err
263
+ return test
226
264
  })
227
265
 
228
266
  const originalLog = Base.consoleLog
@@ -235,12 +273,8 @@ class Cli extends Base {
235
273
  console.log()
236
274
  }
237
275
 
238
- this.failures.forEach(failure => {
239
- if (failure.constructor.name === 'Hook') {
240
- stats.failedHooks += 1
241
- }
242
- })
243
- event.emit(event.all.failures, { failuresLog, stats })
276
+ container.result().addFailures(failuresLog)
277
+
244
278
  output.result(stats.passes, stats.failures, stats.pending, ms(stats.duration), stats.failedHooks)
245
279
 
246
280
  if (stats.failures && output.level() < 3) {
@@ -1,18 +1,47 @@
1
1
  const event = require('../event')
2
+ const { serializeError } = require('../utils')
3
+ // const { serializeTest } = require('./test')
2
4
 
5
+ /**
6
+ * Represents a test hook in the testing framework
7
+ * @class
8
+ * @property {Object} suite - The test suite this hook belongs to
9
+ * @property {Object} test - The test object associated with this hook
10
+ * @property {Object} runnable - The current test being executed
11
+ * @property {Object} ctx - The context object
12
+ * @property {Error|null} err - The error that occurred during hook execution, if any
13
+ */
3
14
  class Hook {
15
+ /**
16
+ * Creates a new Hook instance
17
+ * @param {Object} context - The context object containing suite and test information
18
+ * @param {Object} context.suite - The test suite
19
+ * @param {Object} context.test - The test object
20
+ * @param {Object} context.ctx - The context object
21
+ * @param {Error} error - The error object if hook execution failed
22
+ */
4
23
  constructor(context, error) {
5
24
  this.suite = context.suite
6
25
  this.test = context.test
7
26
  this.runnable = context?.ctx?.test
8
27
  this.ctx = context.ctx
9
- this.error = error
28
+ this.err = error
10
29
  }
11
30
 
12
31
  get hookName() {
13
32
  return this.constructor.name.replace('Hook', '')
14
33
  }
15
34
 
35
+ simplify() {
36
+ return {
37
+ hookName: this.hookName,
38
+ title: this.title,
39
+ // test: this.test ? serializeTest(this.test) : null,
40
+ // suite: this.suite ? serializeSuite(this.suite) : null,
41
+ error: this.err ? serializeError(this.err) : null,
42
+ }
43
+ }
44
+
16
45
  toString() {
17
46
  return this.hookName
18
47
  }
@@ -46,13 +75,13 @@ function fireHook(eventType, suite, error) {
46
75
  const hook = suite.ctx?.test?.title?.match(/"([^"]*)"/)[1]
47
76
  switch (hook) {
48
77
  case 'before each':
49
- event.emit(eventType, new BeforeHook(suite))
78
+ event.emit(eventType, new BeforeHook(suite, error))
50
79
  break
51
80
  case 'after each':
52
81
  event.emit(eventType, new AfterHook(suite, error))
53
82
  break
54
83
  case 'before all':
55
- event.emit(eventType, new BeforeSuiteHook(suite))
84
+ event.emit(eventType, new BeforeSuiteHook(suite, error))
56
85
  break
57
86
  case 'after all':
58
87
  event.emit(eventType, new AfterSuiteHook(suite, error))
@@ -1,5 +1,4 @@
1
1
  const MochaSuite = require('mocha/lib/suite')
2
-
3
2
  /**
4
3
  * @typedef {import('mocha')} Mocha
5
4
  */
@@ -34,6 +33,10 @@ function enhanceMochaSuite(suite) {
34
33
  }
35
34
  }
36
35
 
36
+ suite.simplify = function () {
37
+ return serializeSuite(this)
38
+ }
39
+
37
40
  return suite
38
41
  }
39
42
 
@@ -49,7 +52,30 @@ function createSuite(parent, title) {
49
52
  return enhanceMochaSuite(suite)
50
53
  }
51
54
 
55
+ function serializeSuite(suite) {
56
+ suite = { ...suite }
57
+
58
+ return {
59
+ opts: suite.opts || {},
60
+ tags: suite.tags || [],
61
+ retries: suite._retries,
62
+ title: suite.title,
63
+ status: suite.status,
64
+ notes: suite.notes || [],
65
+ meta: suite.meta || {},
66
+ duration: suite.duration || 0,
67
+ }
68
+ }
69
+
70
+ function deserializeSuite(suite) {
71
+ suite = Object.assign(new MochaSuite(suite.title), suite)
72
+ enhanceMochaSuite(suite)
73
+ return suite
74
+ }
75
+
52
76
  module.exports = {
53
77
  createSuite,
54
78
  enhanceMochaSuite,
79
+ serializeSuite,
80
+ deserializeSuite,
55
81
  }
package/lib/mocha/test.js CHANGED
@@ -1,9 +1,9 @@
1
1
  const Test = require('mocha/lib/test')
2
2
  const Suite = require('mocha/lib/suite')
3
3
  const { test: testWrapper } = require('./asyncWrapper')
4
- const { enhanceMochaSuite } = require('./suite')
5
- const { genTestId } = require('../utils')
6
-
4
+ const { enhanceMochaSuite, createSuite } = require('./suite')
5
+ const { genTestId, serializeError, clearString, relativeDir } = require('../utils')
6
+ const Step = require('../step/base')
7
7
  /**
8
8
  * Factory function to create enhanced tests
9
9
  * @param {string} title - Test title
@@ -46,6 +46,7 @@ function enhanceMochaTest(test) {
46
46
  test.addToSuite = function (suite) {
47
47
  enhanceMochaSuite(suite)
48
48
  suite.addTest(testWrapper(this))
49
+ if (test.file && !suite.file) suite.file = test.file
49
50
  test.tags = [...(test.tags || []), ...(suite.tags || [])]
50
51
  test.fullTitle = () => `${suite.title}: ${test.title}`
51
52
  test.uid = genTestId(test)
@@ -59,17 +60,100 @@ function enhanceMochaTest(test) {
59
60
  if (opts.retries) this.retries(opts.retries)
60
61
  }
61
62
 
63
+ test.simplify = function () {
64
+ return serializeTest(this)
65
+ }
66
+
62
67
  return test
63
68
  }
64
69
 
65
- function repackTestForWorkersTransport(test) {
66
- test = Object.assign(new Test(test.title || '', () => {}), test)
67
- test.parent = Object.assign(new Suite(test.parent.title), test.parent)
70
+ function deserializeTest(test) {
71
+ test = Object.assign(
72
+ createTest(test.title || '', () => {}),
73
+ test,
74
+ )
75
+ test.parent = Object.assign(new Suite(test.parent?.title || 'Suite'), test.parent)
76
+ enhanceMochaSuite(test.parent)
77
+ if (test.steps) test.steps = test.steps.map(step => Object.assign(new Step(step.title), step))
68
78
  return test
69
79
  }
70
80
 
81
+ function serializeTest(test, error = null) {
82
+ // test = { ...test }
83
+
84
+ if (test.start && !test.duration) {
85
+ const end = +new Date()
86
+ test.duration = end - test.start
87
+ }
88
+
89
+ let err
90
+
91
+ if (test.err) {
92
+ err = serializeError(test.err)
93
+ test.state = 'failed'
94
+ } else if (error) {
95
+ err = serializeError(error)
96
+ test.state = 'failed'
97
+ }
98
+ const parent = {}
99
+ if (test.parent) {
100
+ parent.title = test.parent.title
101
+ }
102
+
103
+ if (test.opts) {
104
+ Object.keys(test.opts).forEach(k => {
105
+ if (typeof test.opts[k] === 'object') delete test.opts[k]
106
+ if (typeof test.opts[k] === 'function') delete test.opts[k]
107
+ })
108
+ }
109
+
110
+ let steps = undefined
111
+ if (Array.isArray(test.steps)) {
112
+ steps = test.steps.map(step => (step.simplify ? step.simplify() : step))
113
+ }
114
+
115
+ return {
116
+ opts: test.opts || {},
117
+ tags: test.tags || [],
118
+ uid: test.uid,
119
+ retries: test._retries,
120
+ title: test.title,
121
+ state: test.state,
122
+ notes: test.notes || [],
123
+ meta: test.meta || {},
124
+ artifacts: test.artifacts || {},
125
+ duration: test.duration || 0,
126
+ err,
127
+ parent,
128
+ steps,
129
+ }
130
+ }
131
+
132
+ function cloneTest(test) {
133
+ return deserializeTest(serializeTest(test))
134
+ }
135
+
136
+ function testToFileName(test) {
137
+ let fileName = clearString(test.title)
138
+ // remove tags with empty string (disable for now)
139
+ // fileName = fileName.replace(/\@\w+/g, '')
140
+ fileName = fileName.slice(0, 100)
141
+ if (fileName.indexOf('{') !== -1) {
142
+ fileName = fileName.substr(0, fileName.indexOf('{') - 3).trim()
143
+ }
144
+ if (test.ctx && test.ctx.test && test.ctx.test.type === 'hook') fileName = clearString(`${test.title}_${test.ctx.test.title}`)
145
+ // TODO: add suite title to file name
146
+ // if (test.parent && test.parent.title) {
147
+ // fileName = `${clearString(test.parent.title)}_${fileName}`
148
+ // }
149
+ return fileName
150
+ }
151
+
71
152
  module.exports = {
72
153
  createTest,
154
+ testToFileName,
73
155
  enhanceMochaTest,
74
- repackTestForWorkersTransport,
156
+ serializeTest,
157
+ deserializeTest,
158
+ cloneTest,
75
159
  }
@@ -12,14 +12,19 @@ declare global {
12
12
  type: string
13
13
  text: string
14
14
  }>
15
+ state: string
16
+ err?: Error
15
17
  config: Record<string, any>
16
18
  artifacts: string[]
17
19
  inject: Record<string, any>
18
20
  opts: Record<string, any>
19
21
  throws?: Error | string | RegExp | Function
20
22
  totalTimeout?: number
23
+ relativeFile?: string
21
24
  addToSuite(suite: Mocha.Suite): void
22
25
  applyOptions(opts: Record<string, any>): void
26
+ simplify(): Record<string, any>
27
+ toFileName(): string
23
28
  addNote(type: string, note: string): void
24
29
  codeceptjs: boolean
25
30
  }
package/lib/output.js CHANGED
@@ -115,7 +115,7 @@ module.exports = {
115
115
  }
116
116
  }
117
117
 
118
- let stepLine = step.toCliStyled()
118
+ let stepLine = step.toCliStyled ? step.toCliStyled() : step.toString()
119
119
  if (step.metaStep && outputLevel >= 1) {
120
120
  // this.stepShift += 2;
121
121
  stepLine = colors.dim(truncate(stepLine, this.spaceShift))
@@ -136,6 +136,7 @@ module.exports = {
136
136
  started: suite => {
137
137
  if (!suite.title) return
138
138
  print(`${colors.bold(suite.title)} --`)
139
+ if (suite.file && outputLevel >= 1) print(colors.underline.grey(suite.file))
139
140
  if (suite.comment) print(suite.comment)
140
141
  },
141
142
  },