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.
- 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/event.js +18 -17
- 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/mocha/asyncWrapper.js +12 -2
- package/lib/mocha/cli.js +50 -24
- 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 +1 -0
- package/lib/plugin/analyze.js +351 -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 +143 -0
- package/lib/plugin/retryTo.js +10 -2
- package/lib/plugin/screenshotOnFail.js +4 -6
- 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/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/steps.js +28 -1
- package/lib/{step/timeout.js → timeout.js} +24 -0
- package/lib/utils.js +35 -0
- package/lib/workers.js +28 -38
- package/package.json +2 -2
- package/typings/promiseBasedTypes.d.ts +12 -518
- package/typings/types.d.ts +75 -518
- package/lib/listener/artifacts.js +0 -19
- package/lib/plugin/debugErrors.js +0 -67
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,8 +185,18 @@ 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
|
|
175
200
|
|
|
176
201
|
const steps = test.steps || (test.ctx && test.ctx.test.steps)
|
|
177
202
|
|
|
@@ -204,25 +229,30 @@ class Cli extends Base {
|
|
|
204
229
|
|
|
205
230
|
try {
|
|
206
231
|
let stack = err.stack
|
|
207
|
-
stack = stack.replace(originalMessage, '')
|
|
232
|
+
stack = (stack || '').replace(originalMessage, '')
|
|
208
233
|
stack = stack ? stack.split('\n') : []
|
|
209
234
|
|
|
210
235
|
if (stack[0] && stack[0].includes(err.message)) {
|
|
211
236
|
stack.shift()
|
|
212
237
|
}
|
|
213
238
|
|
|
239
|
+
if (stack[0] && stack[0].trim() == 'Error:') {
|
|
240
|
+
stack.shift()
|
|
241
|
+
}
|
|
242
|
+
|
|
214
243
|
if (output.level() < 3) {
|
|
215
244
|
stack = stack.slice(0, 3)
|
|
216
245
|
}
|
|
217
246
|
|
|
218
247
|
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
248
|
} catch (e) {
|
|
224
|
-
|
|
249
|
+
console.error(e)
|
|
225
250
|
}
|
|
251
|
+
|
|
252
|
+
// we will change the stack trace, so we need to clone the test
|
|
253
|
+
test = cloneTest(test)
|
|
254
|
+
test.err = err
|
|
255
|
+
return test
|
|
226
256
|
})
|
|
227
257
|
|
|
228
258
|
const originalLog = Base.consoleLog
|
|
@@ -235,12 +265,8 @@ class Cli extends Base {
|
|
|
235
265
|
console.log()
|
|
236
266
|
}
|
|
237
267
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
stats.failedHooks += 1
|
|
241
|
-
}
|
|
242
|
-
})
|
|
243
|
-
event.emit(event.all.failures, { failuresLog, stats })
|
|
268
|
+
container.result().addFailures(failuresLog)
|
|
269
|
+
|
|
244
270
|
output.result(stats.passes, stats.failures, stats.pending, ms(stats.duration), stats.failedHooks)
|
|
245
271
|
|
|
246
272
|
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
|
@@ -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
|
},
|