codeceptjs 3.7.0-beta.6 → 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/README.md +7 -7
- 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/helper/Appium.js +4 -6
- package/lib/helper/WebDriver.js +20 -7
- 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/pause.js +0 -3
- 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 +12 -12
- package/typings/promiseBasedTypes.d.ts +6 -520
- package/typings/types.d.ts +115 -562
- 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/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/Appium.js
CHANGED
|
@@ -44,7 +44,7 @@ const vendorPrefix = {
|
|
|
44
44
|
*
|
|
45
45
|
* This helper should be configured in codecept.conf.ts or codecept.conf.js
|
|
46
46
|
*
|
|
47
|
-
* * `appiumV2`: set this to
|
|
47
|
+
* * `appiumV2`: by default is true, set this to false if you want to run tests with AppiumV1. See more how to setup [here](https://codecept.io/mobile/#setting-up)
|
|
48
48
|
* * `app`: Application path. Local path or remote URL to an .ipa or .apk file, or a .zip containing one of these. Alias to desiredCapabilities.appPackage
|
|
49
49
|
* * `host`: (default: 'localhost') Appium host
|
|
50
50
|
* * `port`: (default: '4723') Appium port
|
|
@@ -124,7 +124,7 @@ const vendorPrefix = {
|
|
|
124
124
|
* {
|
|
125
125
|
* helpers: {
|
|
126
126
|
* Appium: {
|
|
127
|
-
* appiumV2: true,
|
|
127
|
+
* appiumV2: true, // By default is true, set to false if you want to run against Appium v1
|
|
128
128
|
* host: "hub-cloud.browserstack.com",
|
|
129
129
|
* port: 4444,
|
|
130
130
|
* user: process.env.BROWSERSTACK_USER,
|
|
@@ -178,14 +178,12 @@ class Appium extends Webdriver {
|
|
|
178
178
|
super(config)
|
|
179
179
|
|
|
180
180
|
this.isRunning = false
|
|
181
|
-
|
|
182
|
-
this.appiumV2 = true
|
|
183
|
-
}
|
|
181
|
+
this.appiumV2 = config.appiumV2 || true
|
|
184
182
|
this.axios = axios.create()
|
|
185
183
|
|
|
186
184
|
webdriverio = require('webdriverio')
|
|
187
185
|
if (!config.appiumV2) {
|
|
188
|
-
console.log('The Appium core team does not maintain Appium 1.x anymore since the 1st of January 2022.
|
|
186
|
+
console.log('The Appium core team does not maintain Appium 1.x anymore since the 1st of January 2022. Appium 2.x is used by default.')
|
|
189
187
|
console.log('More info: https://bit.ly/appium-v2-migration')
|
|
190
188
|
console.log('This Appium 1.x support will be removed in next major release.')
|
|
191
189
|
}
|
package/lib/helper/WebDriver.js
CHANGED
|
@@ -24,6 +24,7 @@ const { dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTr
|
|
|
24
24
|
|
|
25
25
|
const SHADOW = 'shadow'
|
|
26
26
|
const webRoot = 'body'
|
|
27
|
+
let browserLogs = []
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* ## Configuration
|
|
@@ -621,6 +622,10 @@ class WebDriver extends Helper {
|
|
|
621
622
|
}
|
|
622
623
|
|
|
623
624
|
this.browser.on('dialog', () => {})
|
|
625
|
+
|
|
626
|
+
await this.browser.sessionSubscribe({ events: ['log.entryAdded'] })
|
|
627
|
+
this.browser.on('log.entryAdded', logEvents)
|
|
628
|
+
|
|
624
629
|
return this.browser
|
|
625
630
|
}
|
|
626
631
|
|
|
@@ -658,6 +663,7 @@ class WebDriver extends Helper {
|
|
|
658
663
|
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
659
664
|
})
|
|
660
665
|
await this.closeOtherTabs()
|
|
666
|
+
browserLogs = []
|
|
661
667
|
return this.browser
|
|
662
668
|
}
|
|
663
669
|
|
|
@@ -1522,11 +1528,7 @@ class WebDriver extends Helper {
|
|
|
1522
1528
|
* {{> grabBrowserLogs }}
|
|
1523
1529
|
*/
|
|
1524
1530
|
async grabBrowserLogs() {
|
|
1525
|
-
|
|
1526
|
-
this.debug('Logs not available in W3C specification')
|
|
1527
|
-
return
|
|
1528
|
-
}
|
|
1529
|
-
return this.browser.getLogs('browser')
|
|
1531
|
+
return browserLogs
|
|
1530
1532
|
}
|
|
1531
1533
|
|
|
1532
1534
|
/**
|
|
@@ -1788,18 +1790,25 @@ class WebDriver extends Helper {
|
|
|
1788
1790
|
|
|
1789
1791
|
if (browser) {
|
|
1790
1792
|
this.debug(`Screenshot of ${sessionName} session has been saved to ${outputFile}`)
|
|
1791
|
-
|
|
1793
|
+
await browser.saveScreenshot(outputFile)
|
|
1792
1794
|
}
|
|
1793
1795
|
}
|
|
1794
1796
|
}
|
|
1795
1797
|
|
|
1796
1798
|
if (!fullPage) {
|
|
1797
1799
|
this.debug(`Screenshot has been saved to ${outputFile}`)
|
|
1798
|
-
|
|
1800
|
+
await this.browser.saveScreenshot(outputFile)
|
|
1799
1801
|
}
|
|
1800
1802
|
|
|
1801
1803
|
const originalWindowSize = await this.browser.getWindowSize()
|
|
1802
1804
|
|
|
1805
|
+
// this case running on device, so we could not set the windowSize
|
|
1806
|
+
if (this.browser.isMobile) {
|
|
1807
|
+
this.debug(`Screenshot has been saved to ${outputFile}, size: ${originalWindowSize.width}x${originalWindowSize.height}`)
|
|
1808
|
+
const buffer = await this.browser.saveScreenshot(outputFile)
|
|
1809
|
+
return buffer
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1803
1812
|
let { width, height } = await this.browser
|
|
1804
1813
|
.execute(function () {
|
|
1805
1814
|
return {
|
|
@@ -3134,4 +3143,8 @@ function prepareLocateFn(context) {
|
|
|
3134
3143
|
}
|
|
3135
3144
|
}
|
|
3136
3145
|
|
|
3146
|
+
function logEvents(event) {
|
|
3147
|
+
browserLogs.push(event.text) // add log message to the array
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3137
3150
|
module.exports = WebDriver
|
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
|
|
|
@@ -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
|
})
|
|
@@ -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) {
|
|
23
|
+
suite.eachTest(test => {
|
|
24
|
+
test.err = err
|
|
25
|
+
event.emit(event.test.failed, test, err)
|
|
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)
|
|
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
|
}
|