codeceptjs 3.7.0-beta.1 → 3.7.0-beta.10
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/README.md +9 -10
- package/bin/codecept.js +7 -0
- package/lib/actor.js +46 -92
- package/lib/ai.js +130 -121
- package/lib/codecept.js +2 -2
- package/lib/command/check.js +186 -0
- package/lib/command/definitions.js +3 -1
- package/lib/command/interactive.js +1 -1
- package/lib/command/run-workers.js +2 -54
- package/lib/command/workers/runTests.js +64 -225
- package/lib/container.js +27 -0
- package/lib/effects.js +218 -0
- package/lib/els.js +87 -106
- package/lib/event.js +18 -17
- package/lib/heal.js +10 -0
- package/lib/helper/AI.js +2 -1
- package/lib/helper/Appium.js +31 -22
- package/lib/helper/Playwright.js +22 -1
- package/lib/helper/Puppeteer.js +5 -0
- package/lib/helper/WebDriver.js +29 -8
- package/lib/listener/emptyRun.js +2 -5
- package/lib/listener/exit.js +5 -8
- package/lib/listener/globalTimeout.js +66 -10
- package/lib/listener/result.js +12 -0
- package/lib/listener/steps.js +3 -6
- package/lib/listener/store.js +9 -1
- package/lib/mocha/asyncWrapper.js +15 -3
- package/lib/mocha/cli.js +79 -28
- package/lib/mocha/featureConfig.js +13 -0
- package/lib/mocha/hooks.js +32 -3
- package/lib/mocha/inject.js +5 -0
- package/lib/mocha/scenarioConfig.js +11 -0
- package/lib/mocha/suite.js +27 -1
- package/lib/mocha/test.js +102 -3
- package/lib/mocha/types.d.ts +11 -0
- package/lib/output.js +75 -73
- package/lib/pause.js +3 -10
- package/lib/plugin/analyze.js +349 -0
- package/lib/plugin/autoDelay.js +2 -2
- package/lib/plugin/commentStep.js +5 -0
- package/lib/plugin/customReporter.js +52 -0
- package/lib/plugin/heal.js +30 -0
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/retryTo.js +18 -118
- package/lib/plugin/screenshotOnFail.js +12 -17
- package/lib/plugin/standardActingHelpers.js +4 -1
- package/lib/plugin/stepByStepReport.js +6 -5
- package/lib/plugin/stepTimeout.js +1 -1
- package/lib/plugin/tryTo.js +17 -107
- package/lib/recorder.js +5 -5
- package/lib/rerun.js +43 -42
- package/lib/result.js +161 -0
- package/lib/step/base.js +228 -0
- package/lib/step/config.js +50 -0
- package/lib/step/func.js +46 -0
- package/lib/step/helper.js +50 -0
- package/lib/step/meta.js +99 -0
- package/lib/step/record.js +74 -0
- package/lib/step/retry.js +11 -0
- package/lib/step/section.js +55 -0
- package/lib/step.js +20 -347
- package/lib/steps.js +50 -0
- package/lib/store.js +4 -0
- package/lib/timeout.js +66 -0
- package/lib/utils.js +93 -0
- package/lib/within.js +2 -2
- package/lib/workers.js +29 -49
- package/package.json +23 -20
- package/typings/index.d.ts +5 -4
- package/typings/promiseBasedTypes.d.ts +617 -7
- package/typings/types.d.ts +663 -34
- package/lib/listener/artifacts.js +0 -19
- package/lib/plugin/debugErrors.js +0 -67
package/lib/mocha/cli.js
CHANGED
|
@@ -2,10 +2,11 @@ const {
|
|
|
2
2
|
reporters: { Base },
|
|
3
3
|
} = require('mocha')
|
|
4
4
|
const ms = require('ms')
|
|
5
|
+
const figures = require('figures')
|
|
5
6
|
const event = require('../event')
|
|
6
7
|
const AssertionFailedError = require('../assert/error')
|
|
7
8
|
const output = require('../output')
|
|
8
|
-
|
|
9
|
+
const { cloneTest } = require('./test')
|
|
9
10
|
const cursor = Base.cursor
|
|
10
11
|
let currentMetaStep = []
|
|
11
12
|
let codeceptjsEventDispatchersRegistered = false
|
|
@@ -31,6 +32,16 @@ class Cli extends Base {
|
|
|
31
32
|
output.print(output.styles.debug(`Plugins: ${Object.keys(Containter.plugins()).join(', ')}`))
|
|
32
33
|
}
|
|
33
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
|
+
|
|
34
45
|
runner.on('start', () => {
|
|
35
46
|
console.log()
|
|
36
47
|
})
|
|
@@ -90,9 +101,11 @@ class Cli extends Base {
|
|
|
90
101
|
event.dispatcher.on(event.step.started, step => {
|
|
91
102
|
let processingStep = step
|
|
92
103
|
const metaSteps = []
|
|
104
|
+
let isHidden = false
|
|
93
105
|
while (processingStep.metaStep) {
|
|
94
106
|
metaSteps.unshift(processingStep.metaStep)
|
|
95
107
|
processingStep = processingStep.metaStep
|
|
108
|
+
if (processingStep.collapsed) isHidden = true
|
|
96
109
|
}
|
|
97
110
|
const shift = metaSteps.length
|
|
98
111
|
|
|
@@ -106,10 +119,9 @@ class Cli extends Base {
|
|
|
106
119
|
}
|
|
107
120
|
}
|
|
108
121
|
currentMetaStep = metaSteps
|
|
122
|
+
if (isHidden) return
|
|
109
123
|
output.stepShift = 3 + 2 * shift
|
|
110
|
-
|
|
111
|
-
output.step(step)
|
|
112
|
-
}
|
|
124
|
+
output.step(step)
|
|
113
125
|
})
|
|
114
126
|
|
|
115
127
|
event.dispatcher.on(event.step.finished, () => {
|
|
@@ -137,16 +149,19 @@ class Cli extends Base {
|
|
|
137
149
|
}
|
|
138
150
|
}
|
|
139
151
|
|
|
140
|
-
|
|
141
|
-
|
|
152
|
+
const container = require('../container')
|
|
153
|
+
container.result().addStats({ pending: skippedCount, tests: skippedCount })
|
|
142
154
|
})
|
|
143
155
|
|
|
144
156
|
runner.on('end', this.result.bind(this))
|
|
145
157
|
}
|
|
146
158
|
|
|
147
159
|
result() {
|
|
148
|
-
const
|
|
149
|
-
stats
|
|
160
|
+
const container = require('../container')
|
|
161
|
+
container.result().addStats(this.stats)
|
|
162
|
+
container.result().finish()
|
|
163
|
+
|
|
164
|
+
const stats = container.result().stats
|
|
150
165
|
console.log()
|
|
151
166
|
|
|
152
167
|
// passes
|
|
@@ -159,53 +174,93 @@ class Cli extends Base {
|
|
|
159
174
|
// failures
|
|
160
175
|
if (stats.failures) {
|
|
161
176
|
// append step traces
|
|
162
|
-
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
|
|
163
179
|
const err = test.err
|
|
164
180
|
|
|
165
181
|
let log = ''
|
|
182
|
+
let originalMessage = err.message
|
|
166
183
|
|
|
167
184
|
if (err instanceof AssertionFailedError) {
|
|
168
185
|
err.message = err.inspect()
|
|
169
186
|
}
|
|
170
187
|
|
|
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
|
+
}
|
|
205
|
+
|
|
171
206
|
const steps = test.steps || (test.ctx && test.ctx.test.steps)
|
|
172
207
|
|
|
173
208
|
if (steps && steps.length) {
|
|
174
209
|
let scenarioTrace = ''
|
|
175
|
-
steps
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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`
|
|
181
220
|
}
|
|
182
221
|
|
|
183
222
|
// display artifacts in debug mode
|
|
184
223
|
if (test?.artifacts && Object.keys(test.artifacts).length) {
|
|
185
|
-
log += `\n${output.styles.
|
|
224
|
+
log += `\n${output.styles.basic(figures.circle)} ${output.styles.section('Artifacts:')}`
|
|
186
225
|
for (const artifact of Object.keys(test.artifacts)) {
|
|
187
226
|
log += `\n- ${artifact}: ${test.artifacts[artifact]}`
|
|
188
227
|
}
|
|
189
228
|
}
|
|
190
229
|
|
|
230
|
+
// display metadata
|
|
231
|
+
if (test.meta && Object.keys(test.meta).length) {
|
|
232
|
+
log += `\n\n${output.styles.basic(figures.circle)} ${output.styles.section('Metadata:')}`
|
|
233
|
+
for (const [key, value] of Object.entries(test.meta)) {
|
|
234
|
+
log += `\n- ${key}: ${value}`
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
191
238
|
try {
|
|
192
|
-
let stack = err.stack
|
|
239
|
+
let stack = err.stack
|
|
240
|
+
stack = (stack || '').replace(originalMessage, '')
|
|
241
|
+
stack = stack ? stack.split('\n') : []
|
|
242
|
+
|
|
193
243
|
if (stack[0] && stack[0].includes(err.message)) {
|
|
194
244
|
stack.shift()
|
|
195
245
|
}
|
|
196
246
|
|
|
247
|
+
if (stack[0] && stack[0].trim() == 'Error:') {
|
|
248
|
+
stack.shift()
|
|
249
|
+
}
|
|
250
|
+
|
|
197
251
|
if (output.level() < 3) {
|
|
198
252
|
stack = stack.slice(0, 3)
|
|
199
253
|
}
|
|
200
254
|
|
|
201
255
|
err.stack = `${stack.join('\n')}\n\n${output.colors.blue(log)}`
|
|
202
|
-
|
|
203
|
-
// clone err object so stack trace adjustments won't affect test other reports
|
|
204
|
-
test.err = err
|
|
205
|
-
return test
|
|
206
256
|
} catch (e) {
|
|
207
|
-
|
|
257
|
+
console.error(e)
|
|
208
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
|
|
209
264
|
})
|
|
210
265
|
|
|
211
266
|
const originalLog = Base.consoleLog
|
|
@@ -218,12 +273,8 @@ class Cli extends Base {
|
|
|
218
273
|
console.log()
|
|
219
274
|
}
|
|
220
275
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
stats.failedHooks += 1
|
|
224
|
-
}
|
|
225
|
-
})
|
|
226
|
-
event.emit(event.all.failures, { failuresLog, stats })
|
|
276
|
+
container.result().addFailures(failuresLog)
|
|
277
|
+
|
|
227
278
|
output.result(stats.passes, stats.failures, stats.pending, ms(stats.duration), stats.failedHooks)
|
|
228
279
|
|
|
229
280
|
if (stats.failures && output.level() < 3) {
|
|
@@ -7,6 +7,19 @@ class FeatureConfig {
|
|
|
7
7
|
this.suite = suite
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Set metadata for this suite
|
|
12
|
+
* @param {string} key
|
|
13
|
+
* @param {string} value
|
|
14
|
+
* @returns {this}
|
|
15
|
+
*/
|
|
16
|
+
meta(key, value) {
|
|
17
|
+
this.suite.tests.forEach(test => {
|
|
18
|
+
test.meta[key] = value
|
|
19
|
+
})
|
|
20
|
+
return this
|
|
21
|
+
}
|
|
22
|
+
|
|
10
23
|
/**
|
|
11
24
|
* Retry this test for number of times
|
|
12
25
|
*
|
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/inject.js
CHANGED
|
@@ -5,6 +5,7 @@ const getInjectedArguments = (fn, test) => {
|
|
|
5
5
|
const testArgs = {}
|
|
6
6
|
const params = parser.getParams(fn) || []
|
|
7
7
|
const objects = container.support()
|
|
8
|
+
|
|
8
9
|
for (const key of params) {
|
|
9
10
|
testArgs[key] = {}
|
|
10
11
|
if (test && test.inject && test.inject[key]) {
|
|
@@ -18,6 +19,10 @@ const getInjectedArguments = (fn, test) => {
|
|
|
18
19
|
testArgs[key] = container.support(key)
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
if (test) {
|
|
23
|
+
testArgs.suite = test?.parent
|
|
24
|
+
testArgs.test = test
|
|
25
|
+
}
|
|
21
26
|
return testArgs
|
|
22
27
|
}
|
|
23
28
|
|
|
@@ -42,6 +42,17 @@ class ScenarioConfig {
|
|
|
42
42
|
return this
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Set metadata for this test
|
|
47
|
+
* @param {string} key
|
|
48
|
+
* @param {string} value
|
|
49
|
+
* @returns {this}
|
|
50
|
+
*/
|
|
51
|
+
meta(key, value) {
|
|
52
|
+
this.test.meta[key] = value
|
|
53
|
+
return this
|
|
54
|
+
}
|
|
55
|
+
|
|
45
56
|
/**
|
|
46
57
|
* Set timeout for this test
|
|
47
58
|
* @param {number} timeout
|
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,8 +1,9 @@
|
|
|
1
1
|
const Test = require('mocha/lib/test')
|
|
2
|
+
const Suite = require('mocha/lib/suite')
|
|
2
3
|
const { test: testWrapper } = require('./asyncWrapper')
|
|
3
|
-
const { enhanceMochaSuite } = require('./suite')
|
|
4
|
-
const { genTestId } = require('../utils')
|
|
5
|
-
|
|
4
|
+
const { enhanceMochaSuite, createSuite } = require('./suite')
|
|
5
|
+
const { genTestId, serializeError, clearString, relativeDir } = require('../utils')
|
|
6
|
+
const Step = require('../step/base')
|
|
6
7
|
/**
|
|
7
8
|
* Factory function to create enhanced tests
|
|
8
9
|
* @param {string} title - Test title
|
|
@@ -31,6 +32,12 @@ function enhanceMochaTest(test) {
|
|
|
31
32
|
test.artifacts = []
|
|
32
33
|
test.inject = {}
|
|
33
34
|
test.opts = {}
|
|
35
|
+
test.meta = {}
|
|
36
|
+
|
|
37
|
+
test.notes = []
|
|
38
|
+
test.addNote = (type, note) => {
|
|
39
|
+
test.notes.push({ type, text: note })
|
|
40
|
+
}
|
|
34
41
|
|
|
35
42
|
// Add new methods
|
|
36
43
|
/**
|
|
@@ -39,6 +46,7 @@ function enhanceMochaTest(test) {
|
|
|
39
46
|
test.addToSuite = function (suite) {
|
|
40
47
|
enhanceMochaSuite(suite)
|
|
41
48
|
suite.addTest(testWrapper(this))
|
|
49
|
+
if (test.file && !suite.file) suite.file = test.file
|
|
42
50
|
test.tags = [...(test.tags || []), ...(suite.tags || [])]
|
|
43
51
|
test.fullTitle = () => `${suite.title}: ${test.title}`
|
|
44
52
|
test.uid = genTestId(test)
|
|
@@ -47,14 +55,105 @@ function enhanceMochaTest(test) {
|
|
|
47
55
|
test.applyOptions = function (opts) {
|
|
48
56
|
if (!opts) opts = {}
|
|
49
57
|
test.opts = opts
|
|
58
|
+
test.meta = opts.meta || {}
|
|
50
59
|
test.totalTimeout = opts.timeout
|
|
51
60
|
if (opts.retries) this.retries(opts.retries)
|
|
52
61
|
}
|
|
53
62
|
|
|
63
|
+
test.simplify = function () {
|
|
64
|
+
return serializeTest(this)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return test
|
|
68
|
+
}
|
|
69
|
+
|
|
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))
|
|
54
78
|
return test
|
|
55
79
|
}
|
|
56
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
|
+
|
|
57
152
|
module.exports = {
|
|
58
153
|
createTest,
|
|
154
|
+
testToFileName,
|
|
59
155
|
enhanceMochaTest,
|
|
156
|
+
serializeTest,
|
|
157
|
+
deserializeTest,
|
|
158
|
+
cloneTest,
|
|
60
159
|
}
|
package/lib/mocha/types.d.ts
CHANGED
|
@@ -7,14 +7,25 @@ declare global {
|
|
|
7
7
|
title: string
|
|
8
8
|
tags: string[]
|
|
9
9
|
steps: string[]
|
|
10
|
+
meta: Record<string, any>
|
|
11
|
+
notes: Array<{
|
|
12
|
+
type: string
|
|
13
|
+
text: string
|
|
14
|
+
}>
|
|
15
|
+
state: string
|
|
16
|
+
err?: Error
|
|
10
17
|
config: Record<string, any>
|
|
11
18
|
artifacts: string[]
|
|
12
19
|
inject: Record<string, any>
|
|
13
20
|
opts: Record<string, any>
|
|
14
21
|
throws?: Error | string | RegExp | Function
|
|
15
22
|
totalTimeout?: number
|
|
23
|
+
relativeFile?: string
|
|
16
24
|
addToSuite(suite: Mocha.Suite): void
|
|
17
25
|
applyOptions(opts: Record<string, any>): void
|
|
26
|
+
simplify(): Record<string, any>
|
|
27
|
+
toFileName(): string
|
|
28
|
+
addNote(type: string, note: string): void
|
|
18
29
|
codeceptjs: boolean
|
|
19
30
|
}
|
|
20
31
|
|