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
|
@@ -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:
|
|
219
|
-
event.dispatcher.on(event.suite.after, suite => sendToParentThread({ event: event.suite.after, workerIndex, data:
|
|
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:
|
|
226
|
-
event.dispatcher.on(event.test.after, test => sendToParentThread({ event: event.test.after, workerIndex, data:
|
|
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:
|
|
229
|
-
event.dispatcher.on(event.test.failed, (test, err) => sendToParentThread({ event: event.test.failed, workerIndex, data:
|
|
230
|
-
event.dispatcher.on(event.test.passed, (test, err) => sendToParentThread({ event: event.test.passed, workerIndex, data:
|
|
231
|
-
event.dispatcher.on(event.test.started, test => sendToParentThread({ event: event.test.started, workerIndex, data:
|
|
232
|
-
event.dispatcher.on(event.test.skipped, test => sendToParentThread({ event: event.test.skipped, workerIndex, data:
|
|
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:
|
|
236
|
-
event.dispatcher.on(event.step.started, step => sendToParentThread({ event: event.step.started, workerIndex, data:
|
|
237
|
-
event.dispatcher.on(event.step.passed, step => sendToParentThread({ event: event.step.passed, workerIndex, data:
|
|
238
|
-
event.dispatcher.on(event.step.failed, step => sendToParentThread({ event: event.step.failed, workerIndex, data:
|
|
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, (
|
|
241
|
-
event.dispatcher.on(event.hook.passed,
|
|
242
|
-
event.dispatcher.on(event.
|
|
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, () =>
|
|
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
|
|
120
|
+
parentPort?.postMessage(data)
|
|
285
121
|
}
|
|
286
122
|
|
|
287
123
|
function listenToParentThread() {
|
|
288
|
-
parentPort
|
|
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/els.js
CHANGED
|
@@ -1,115 +1,124 @@
|
|
|
1
|
-
const output = require('./output')
|
|
2
|
-
const store = require('./store')
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const { truth } = require('./assert/truth')
|
|
8
|
-
const { isAsyncFunction, humanizeFunction } = require('./utils')
|
|
1
|
+
const output = require('./output')
|
|
2
|
+
const store = require('./store')
|
|
3
|
+
const container = require('./container')
|
|
4
|
+
const StepConfig = require('./step/config')
|
|
5
|
+
const recordStep = require('./step/record')
|
|
6
|
+
const FuncStep = require('./step/func')
|
|
7
|
+
const { truth } = require('./assert/truth')
|
|
8
|
+
const { isAsyncFunction, humanizeFunction } = require('./utils')
|
|
9
9
|
|
|
10
10
|
function element(purpose, locator, fn) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
purpose = 'first element';
|
|
11
|
+
let stepConfig
|
|
12
|
+
if (arguments[arguments.length - 1] instanceof StepConfig) {
|
|
13
|
+
stepConfig = arguments[arguments.length - 1]
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
if (!fn || fn === stepConfig) {
|
|
17
|
+
fn = locator
|
|
18
|
+
locator = purpose
|
|
19
|
+
purpose = 'first element'
|
|
20
|
+
}
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
output.debug(`Found ${els.length} elements, using first element`);
|
|
22
|
+
const step = prepareStep(purpose, locator, fn)
|
|
23
|
+
if (!step) return
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
return executeStep(
|
|
26
|
+
step,
|
|
27
|
+
async () => {
|
|
28
|
+
const els = await step.helper._locate(locator)
|
|
29
|
+
output.debug(`Found ${els.length} elements, using first element`)
|
|
30
|
+
|
|
31
|
+
return fn(els[0])
|
|
32
|
+
},
|
|
33
|
+
stepConfig,
|
|
34
|
+
)
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
function eachElement(purpose, locator, fn) {
|
|
29
38
|
if (!fn) {
|
|
30
|
-
fn = locator
|
|
31
|
-
locator = purpose
|
|
32
|
-
purpose = 'for each element'
|
|
39
|
+
fn = locator
|
|
40
|
+
locator = purpose
|
|
41
|
+
purpose = 'for each element'
|
|
33
42
|
}
|
|
34
43
|
|
|
35
|
-
const step = prepareStep(purpose, locator, fn)
|
|
36
|
-
if (!step) return
|
|
44
|
+
const step = prepareStep(purpose, locator, fn)
|
|
45
|
+
if (!step) return
|
|
37
46
|
|
|
38
47
|
return executeStep(step, async () => {
|
|
39
|
-
const els = await step.helper._locate(locator)
|
|
40
|
-
output.debug(`Found ${els.length} elements for each elements to iterate`)
|
|
48
|
+
const els = await step.helper._locate(locator)
|
|
49
|
+
output.debug(`Found ${els.length} elements for each elements to iterate`)
|
|
41
50
|
|
|
42
|
-
const errs = []
|
|
43
|
-
let i = 0
|
|
51
|
+
const errs = []
|
|
52
|
+
let i = 0
|
|
44
53
|
for (const el of els) {
|
|
45
54
|
try {
|
|
46
|
-
await fn(el, i)
|
|
55
|
+
await fn(el, i)
|
|
47
56
|
} catch (err) {
|
|
48
|
-
output.error(`eachElement: failed operation on element #${i} ${el}`)
|
|
49
|
-
errs.push(err)
|
|
57
|
+
output.error(`eachElement: failed operation on element #${i} ${el}`)
|
|
58
|
+
errs.push(err)
|
|
50
59
|
}
|
|
51
|
-
i
|
|
60
|
+
i++
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
if (errs.length) {
|
|
55
|
-
throw errs[0]
|
|
64
|
+
throw errs[0]
|
|
56
65
|
}
|
|
57
|
-
})
|
|
66
|
+
})
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
function expectElement(locator, fn) {
|
|
61
|
-
const step = prepareStep('expect element to be', locator, fn)
|
|
62
|
-
if (!step) return
|
|
70
|
+
const step = prepareStep('expect element to be', locator, fn)
|
|
71
|
+
if (!step) return
|
|
63
72
|
|
|
64
73
|
return executeStep(step, async () => {
|
|
65
|
-
const els = await step.helper._locate(locator)
|
|
66
|
-
output.debug(`Found ${els.length} elements, first will be used for assertion`)
|
|
74
|
+
const els = await step.helper._locate(locator)
|
|
75
|
+
output.debug(`Found ${els.length} elements, first will be used for assertion`)
|
|
67
76
|
|
|
68
|
-
const result = await fn(els[0])
|
|
69
|
-
const assertion = truth(`element (${locator})`, fn.toString())
|
|
70
|
-
assertion.assert(result)
|
|
71
|
-
})
|
|
77
|
+
const result = await fn(els[0])
|
|
78
|
+
const assertion = truth(`element (${locator})`, fn.toString())
|
|
79
|
+
assertion.assert(result)
|
|
80
|
+
})
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
function expectAnyElement(locator, fn) {
|
|
75
|
-
const step = prepareStep('expect any element to be', locator, fn)
|
|
76
|
-
if (!step) return
|
|
84
|
+
const step = prepareStep('expect any element to be', locator, fn)
|
|
85
|
+
if (!step) return
|
|
77
86
|
|
|
78
87
|
return executeStep(step, async () => {
|
|
79
|
-
const els = await step.helper._locate(locator)
|
|
80
|
-
output.debug(`Found ${els.length} elements, at least one should pass the assertion`)
|
|
88
|
+
const els = await step.helper._locate(locator)
|
|
89
|
+
output.debug(`Found ${els.length} elements, at least one should pass the assertion`)
|
|
81
90
|
|
|
82
|
-
const assertion = truth(`any element of (${locator})`, fn.toString())
|
|
91
|
+
const assertion = truth(`any element of (${locator})`, fn.toString())
|
|
83
92
|
|
|
84
|
-
let found = false
|
|
93
|
+
let found = false
|
|
85
94
|
for (const el of els) {
|
|
86
|
-
const result = await fn(el)
|
|
95
|
+
const result = await fn(el)
|
|
87
96
|
if (result) {
|
|
88
|
-
found = true
|
|
89
|
-
break
|
|
97
|
+
found = true
|
|
98
|
+
break
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
|
-
if (!found) throw assertion.getException()
|
|
93
|
-
})
|
|
101
|
+
if (!found) throw assertion.getException()
|
|
102
|
+
})
|
|
94
103
|
}
|
|
95
104
|
|
|
96
105
|
function expectAllElements(locator, fn) {
|
|
97
|
-
const step = prepareStep('expect all elements', locator, fn)
|
|
98
|
-
if (!step) return
|
|
106
|
+
const step = prepareStep('expect all elements', locator, fn)
|
|
107
|
+
if (!step) return
|
|
99
108
|
|
|
100
109
|
return executeStep(step, async () => {
|
|
101
|
-
const els = await step.helper._locate(locator)
|
|
102
|
-
output.debug(`Found ${els.length} elements, all should pass the assertion`)
|
|
110
|
+
const els = await step.helper._locate(locator)
|
|
111
|
+
output.debug(`Found ${els.length} elements, all should pass the assertion`)
|
|
103
112
|
|
|
104
|
-
let i = 1
|
|
113
|
+
let i = 1
|
|
105
114
|
for (const el of els) {
|
|
106
|
-
output.debug(`checking element #${i}: ${el}`)
|
|
107
|
-
const result = await fn(el)
|
|
108
|
-
const assertion = truth(`element #${i} of (${locator})`, humanizeFunction(fn))
|
|
109
|
-
assertion.assert(result)
|
|
110
|
-
i
|
|
115
|
+
output.debug(`checking element #${i}: ${el}`)
|
|
116
|
+
const result = await fn(el)
|
|
117
|
+
const assertion = truth(`element #${i} of (${locator})`, humanizeFunction(fn))
|
|
118
|
+
assertion.assert(result)
|
|
119
|
+
i++
|
|
111
120
|
}
|
|
112
|
-
})
|
|
121
|
+
})
|
|
113
122
|
}
|
|
114
123
|
|
|
115
124
|
module.exports = {
|
|
@@ -118,60 +127,32 @@ module.exports = {
|
|
|
118
127
|
expectElement,
|
|
119
128
|
expectAnyElement,
|
|
120
129
|
expectAllElements,
|
|
121
|
-
}
|
|
130
|
+
}
|
|
122
131
|
|
|
123
132
|
function prepareStep(purpose, locator, fn) {
|
|
124
|
-
if (store.dryRun) return
|
|
125
|
-
const helpers = Object.values(container.helpers())
|
|
133
|
+
if (store.dryRun) return
|
|
134
|
+
const helpers = Object.values(container.helpers())
|
|
126
135
|
|
|
127
|
-
const helper = helpers.filter(h => !!h._locate)[0]
|
|
136
|
+
const helper = helpers.filter(h => !!h._locate)[0]
|
|
128
137
|
|
|
129
138
|
if (!helper) {
|
|
130
|
-
throw new Error('No helper enabled with _locate method with returns a list of elements.')
|
|
139
|
+
throw new Error('No helper enabled with _locate method with returns a list of elements.')
|
|
131
140
|
}
|
|
132
141
|
|
|
133
142
|
if (!isAsyncFunction(fn)) {
|
|
134
|
-
throw new Error('Async function should be passed into each element')
|
|
143
|
+
throw new Error('Async function should be passed into each element')
|
|
135
144
|
}
|
|
136
145
|
|
|
137
|
-
const isAssertion = purpose.startsWith('expect')
|
|
146
|
+
const isAssertion = purpose.startsWith('expect')
|
|
138
147
|
|
|
139
|
-
const step = new
|
|
140
|
-
step.
|
|
141
|
-
step.setArguments([humanizeFunction(fn)])
|
|
142
|
-
step.helperMethod = '_locate';
|
|
148
|
+
const step = new FuncStep(`${purpose} within "${locator}" ${isAssertion ? 'to be' : 'to'}`)
|
|
149
|
+
step.setHelper(helper)
|
|
150
|
+
step.setArguments([humanizeFunction(fn)]) // user defined function is a passed argument
|
|
143
151
|
|
|
144
|
-
return step
|
|
152
|
+
return step
|
|
145
153
|
}
|
|
146
154
|
|
|
147
|
-
async function executeStep(step, action) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
event.emit(event.step.started, step);
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
await action();
|
|
154
|
-
} catch (err) {
|
|
155
|
-
recorder.throw(err);
|
|
156
|
-
event.emit(event.step.failed, step, err);
|
|
157
|
-
event.emit(event.step.finished, step);
|
|
158
|
-
// event.emit(event.step.after, step)
|
|
159
|
-
error = err;
|
|
160
|
-
// await recorder.promise();
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
event.emit(event.step.after, step);
|
|
165
|
-
event.emit(event.step.passed, step);
|
|
166
|
-
event.emit(event.step.finished, step);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// await recorder.promise();
|
|
170
|
-
|
|
171
|
-
// if (error) {
|
|
172
|
-
// console.log('error', error.inspect())
|
|
173
|
-
// return recorder.throw(error);
|
|
174
|
-
// }
|
|
175
|
-
|
|
176
|
-
return promise;
|
|
155
|
+
async function executeStep(step, action, stepConfig = {}) {
|
|
156
|
+
step.setCallable(action)
|
|
157
|
+
return recordStep(step, [stepConfig])
|
|
177
158
|
}
|
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
|
+
}
|
package/lib/helper/Playwright.js
CHANGED
|
@@ -523,12 +523,17 @@ class Playwright extends Helper {
|
|
|
523
523
|
this.currentRunningTest.artifacts.har = fileName
|
|
524
524
|
contextOptions.recordHar = this.options.recordHar
|
|
525
525
|
}
|
|
526
|
+
|
|
527
|
+
// load pre-saved cookies
|
|
528
|
+
if (test?.opts?.cookies) contextOptions.storageState = { cookies: test.opts.cookies }
|
|
529
|
+
|
|
526
530
|
if (this.storageState) contextOptions.storageState = this.storageState
|
|
527
531
|
if (this.options.userAgent) contextOptions.userAgent = this.options.userAgent
|
|
528
532
|
if (this.options.locale) contextOptions.locale = this.options.locale
|
|
529
533
|
if (this.options.colorScheme) contextOptions.colorScheme = this.options.colorScheme
|
|
530
534
|
this.contextOptions = contextOptions
|
|
531
535
|
if (!this.browserContext || !restartsSession()) {
|
|
536
|
+
this.debugSection('New Session', JSON.stringify(this.contextOptions))
|
|
532
537
|
this.browserContext = await this.browser.newContext(this.contextOptions) // Adding the HTTPSError ignore in the context so that we can ignore those errors
|
|
533
538
|
}
|
|
534
539
|
}
|
|
@@ -938,7 +943,8 @@ class Playwright extends Helper {
|
|
|
938
943
|
throw new Error('Cannot open pages inside an Electron container')
|
|
939
944
|
}
|
|
940
945
|
if (!/^\w+\:(\/\/|.+)/.test(url)) {
|
|
941
|
-
url = this.options.url + (url.startsWith('/') ? url : `/${url}`)
|
|
946
|
+
url = this.options.url + (!this.options.url.endsWith('/') && url.startsWith('/') ? url : `/${url}`)
|
|
947
|
+
this.debug(`Changed URL to base url + relative path: ${url}`)
|
|
942
948
|
}
|
|
943
949
|
|
|
944
950
|
if (this.options.basicAuth && this.isAuthenticated !== true) {
|
package/lib/listener/exit.js
CHANGED
|
@@ -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,
|
|
7
|
-
|
|
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,
|
|
15
|
-
|
|
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
|
|