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.
- package/lib/actor.js +1 -2
- package/lib/ai.js +130 -121
- package/lib/codecept.js +4 -4
- package/lib/command/check.js +4 -0
- package/lib/command/run-workers.js +1 -53
- package/lib/command/workers/runTests.js +25 -189
- package/lib/container.js +16 -0
- package/lib/els.js +87 -106
- package/lib/event.js +18 -17
- package/lib/helper/Playwright.js +7 -1
- package/lib/listener/exit.js +5 -8
- package/lib/listener/globalTimeout.js +26 -9
- package/lib/listener/result.js +12 -0
- package/lib/listener/steps.js +0 -6
- package/lib/listener/store.js +9 -1
- package/lib/mocha/asyncWrapper.js +12 -2
- package/lib/mocha/cli.js +65 -31
- package/lib/mocha/hooks.js +32 -3
- package/lib/mocha/suite.js +27 -1
- package/lib/mocha/test.js +91 -7
- package/lib/mocha/types.d.ts +5 -0
- package/lib/output.js +2 -1
- package/lib/plugin/analyze.js +348 -0
- package/lib/plugin/commentStep.js +5 -0
- package/lib/plugin/customReporter.js +52 -0
- package/lib/plugin/heal.js +2 -2
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/retryTo.js +10 -2
- package/lib/plugin/screenshotOnFail.js +11 -16
- package/lib/plugin/stepByStepReport.js +5 -4
- package/lib/plugin/stepTimeout.js +1 -1
- package/lib/plugin/tryTo.js +9 -1
- package/lib/recorder.js +4 -4
- package/lib/rerun.js +43 -42
- package/lib/result.js +161 -0
- package/lib/step/base.js +52 -4
- package/lib/step/func.js +46 -0
- package/lib/step/helper.js +3 -0
- package/lib/step/meta.js +9 -1
- package/lib/step/record.js +5 -5
- package/lib/step/section.js +55 -0
- package/lib/step.js +6 -0
- package/lib/steps.js +28 -1
- package/lib/store.js +2 -0
- package/lib/{step/timeout.js → timeout.js} +24 -0
- package/lib/utils.js +35 -0
- package/lib/workers.js +28 -38
- package/package.json +7 -6
- package/typings/promiseBasedTypes.d.ts +104 -0
- package/typings/types.d.ts +104 -0
- package/lib/listener/artifacts.js +0 -19
- 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('../
|
|
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
|
-
|
|
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
|
+
}
|
package/lib/listener/steps.js
CHANGED
|
@@ -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
|
})
|
package/lib/listener/store.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
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
|
|
150
|
-
stats
|
|
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
|
|
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
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
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
|
-
|
|
239
|
-
|
|
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) {
|
package/lib/mocha/hooks.js
CHANGED
|
@@ -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.
|
|
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))
|
package/lib/mocha/suite.js
CHANGED
|
@@ -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
|
|
66
|
-
test = Object.assign(
|
|
67
|
-
|
|
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
|
-
|
|
156
|
+
serializeTest,
|
|
157
|
+
deserializeTest,
|
|
158
|
+
cloneTest,
|
|
75
159
|
}
|
package/lib/mocha/types.d.ts
CHANGED
|
@@ -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
|
},
|