codeceptjs 4.0.0-beta.4 → 4.0.0-beta.5
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 +134 -119
- package/bin/codecept.js +12 -2
- package/bin/test-server.js +53 -0
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +66 -102
- package/lib/ai.js +130 -121
- package/lib/assert/empty.js +3 -5
- package/lib/assert/equal.js +4 -7
- package/lib/assert/include.js +4 -6
- package/lib/assert/throws.js +2 -4
- package/lib/assert/truth.js +2 -2
- package/lib/codecept.js +139 -87
- package/lib/command/check.js +201 -0
- package/lib/command/configMigrate.js +2 -4
- package/lib/command/definitions.js +8 -26
- package/lib/command/generate.js +10 -14
- package/lib/command/gherkin/snippets.js +75 -73
- package/lib/command/gherkin/steps.js +1 -1
- package/lib/command/info.js +42 -8
- package/lib/command/init.js +13 -12
- package/lib/command/interactive.js +10 -2
- package/lib/command/list.js +1 -1
- package/lib/command/run-multiple/chunk.js +48 -45
- package/lib/command/run-multiple.js +12 -35
- package/lib/command/run-workers.js +21 -58
- package/lib/command/utils.js +5 -6
- package/lib/command/workers/runTests.js +262 -220
- package/lib/container.js +386 -238
- package/lib/data/context.js +10 -13
- package/lib/data/dataScenarioConfig.js +8 -8
- package/lib/data/dataTableArgument.js +6 -6
- package/lib/data/table.js +5 -11
- package/lib/effects.js +223 -0
- package/lib/element/WebElement.js +327 -0
- package/lib/els.js +158 -0
- package/lib/event.js +21 -17
- package/lib/heal.js +88 -80
- package/lib/helper/AI.js +2 -1
- package/lib/helper/ApiDataFactory.js +3 -6
- package/lib/helper/Appium.js +47 -51
- package/lib/helper/FileSystem.js +3 -3
- package/lib/helper/GraphQLDataFactory.js +3 -3
- package/lib/helper/JSONResponse.js +75 -37
- package/lib/helper/Mochawesome.js +31 -9
- package/lib/helper/Nightmare.js +35 -53
- package/lib/helper/Playwright.js +262 -267
- package/lib/helper/Protractor.js +54 -77
- package/lib/helper/Puppeteer.js +246 -260
- package/lib/helper/REST.js +5 -17
- package/lib/helper/TestCafe.js +21 -44
- package/lib/helper/WebDriver.js +151 -170
- package/lib/helper/extras/Popup.js +22 -22
- package/lib/helper/testcafe/testcafe-utils.js +26 -27
- package/lib/listener/emptyRun.js +55 -0
- package/lib/listener/exit.js +7 -10
- package/lib/listener/{retry.js → globalRetry.js} +5 -5
- package/lib/listener/globalTimeout.js +165 -0
- package/lib/listener/helpers.js +15 -15
- package/lib/listener/mocha.js +1 -1
- package/lib/listener/result.js +12 -0
- package/lib/listener/retryEnhancer.js +85 -0
- package/lib/listener/steps.js +32 -18
- package/lib/listener/store.js +20 -0
- package/lib/mocha/asyncWrapper.js +231 -0
- package/lib/{interfaces → mocha}/bdd.js +3 -3
- package/lib/mocha/cli.js +308 -0
- package/lib/mocha/factory.js +104 -0
- package/lib/{interfaces → mocha}/featureConfig.js +32 -12
- package/lib/{interfaces → mocha}/gherkin.js +26 -28
- package/lib/mocha/hooks.js +112 -0
- package/lib/mocha/index.js +12 -0
- package/lib/mocha/inject.js +29 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +31 -7
- package/lib/mocha/suite.js +82 -0
- package/lib/mocha/test.js +181 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +232 -0
- package/lib/output.js +82 -62
- package/lib/pause.js +160 -138
- package/lib/plugin/analyze.js +396 -0
- package/lib/plugin/auth.js +435 -0
- package/lib/plugin/autoDelay.js +8 -8
- package/lib/plugin/autoLogin.js +3 -338
- package/lib/plugin/commentStep.js +6 -1
- package/lib/plugin/coverage.js +10 -19
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/customReporter.js +52 -0
- package/lib/plugin/eachElement.js +1 -1
- package/lib/plugin/fakerTransform.js +1 -1
- package/lib/plugin/heal.js +36 -9
- package/lib/plugin/htmlReporter.js +1947 -0
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/retryFailedStep.js +17 -18
- package/lib/plugin/retryTo.js +2 -113
- package/lib/plugin/screenshotOnFail.js +17 -58
- package/lib/plugin/selenoid.js +15 -35
- package/lib/plugin/standardActingHelpers.js +4 -1
- package/lib/plugin/stepByStepReport.js +56 -17
- package/lib/plugin/stepTimeout.js +5 -12
- package/lib/plugin/subtitles.js +4 -4
- package/lib/plugin/tryTo.js +3 -102
- package/lib/plugin/wdio.js +8 -10
- package/lib/recorder.js +155 -124
- package/lib/rerun.js +43 -42
- package/lib/result.js +161 -0
- package/lib/secret.js +1 -1
- package/lib/step/base.js +239 -0
- package/lib/step/comment.js +10 -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 +21 -332
- package/lib/steps.js +50 -0
- package/lib/store.js +37 -5
- package/lib/template/heal.js +2 -11
- package/lib/test-server.js +323 -0
- package/lib/timeout.js +66 -0
- package/lib/utils.js +351 -218
- package/lib/within.js +75 -55
- package/lib/workerStorage.js +2 -1
- package/lib/workers.js +386 -276
- package/package.json +76 -70
- package/translations/de-DE.js +4 -3
- package/translations/fr-FR.js +4 -3
- package/translations/index.js +1 -0
- package/translations/it-IT.js +4 -3
- package/translations/ja-JP.js +4 -3
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +4 -3
- package/translations/pt-BR.js +4 -3
- package/translations/ru-RU.js +4 -3
- package/translations/utils.js +9 -0
- package/translations/zh-CN.js +4 -3
- package/translations/zh-TW.js +4 -3
- package/typings/index.d.ts +188 -186
- package/typings/promiseBasedTypes.d.ts +18 -705
- package/typings/types.d.ts +301 -804
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -113
- package/lib/plugin/debugErrors.js +0 -67
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
package/lib/recorder.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
const debug = require('debug')('codeceptjs:recorder')
|
|
2
|
-
const promiseRetry = require('promise-retry')
|
|
3
|
-
const chalk = require('chalk')
|
|
4
|
-
const { printObjectProperties } = require('./utils')
|
|
5
|
-
const { log } = require('./output')
|
|
6
|
-
|
|
7
|
-
const MAX_TASKS = 100
|
|
8
|
-
|
|
9
|
-
let promise
|
|
10
|
-
let running = false
|
|
11
|
-
let errFn
|
|
12
|
-
let queueId = 0
|
|
13
|
-
let sessionId = null
|
|
14
|
-
let asyncErr = null
|
|
15
|
-
let ignoredErrs = []
|
|
16
|
-
|
|
17
|
-
let tasks = []
|
|
18
|
-
let oldPromises = []
|
|
1
|
+
const debug = require('debug')('codeceptjs:recorder')
|
|
2
|
+
const promiseRetry = require('promise-retry')
|
|
3
|
+
const chalk = require('chalk')
|
|
4
|
+
const { printObjectProperties } = require('./utils')
|
|
5
|
+
const { log } = require('./output')
|
|
6
|
+
const { TimeoutError } = require('./timeout')
|
|
7
|
+
const MAX_TASKS = 100
|
|
8
|
+
|
|
9
|
+
let promise
|
|
10
|
+
let running = false
|
|
11
|
+
let errFn
|
|
12
|
+
let queueId = 0
|
|
13
|
+
let sessionId = null
|
|
14
|
+
let asyncErr = null
|
|
15
|
+
let ignoredErrs = []
|
|
16
|
+
|
|
17
|
+
let tasks = []
|
|
18
|
+
let oldPromises = []
|
|
19
19
|
|
|
20
20
|
const defaultRetryOptions = {
|
|
21
21
|
retries: 0,
|
|
22
22
|
minTimeout: 150,
|
|
23
23
|
maxTimeout: 10000,
|
|
24
|
-
}
|
|
24
|
+
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Singleton object to record all test steps as promises and run them in chain.
|
|
@@ -29,7 +29,6 @@ const defaultRetryOptions = {
|
|
|
29
29
|
* @interface
|
|
30
30
|
*/
|
|
31
31
|
module.exports = {
|
|
32
|
-
|
|
33
32
|
/**
|
|
34
33
|
* @type {Array<Object<string, *>>}
|
|
35
34
|
* @inner
|
|
@@ -43,11 +42,11 @@ module.exports = {
|
|
|
43
42
|
* @inner
|
|
44
43
|
*/
|
|
45
44
|
start() {
|
|
46
|
-
debug('Starting recording promises')
|
|
47
|
-
running = true
|
|
48
|
-
asyncErr = null
|
|
49
|
-
errFn = null
|
|
50
|
-
this.reset()
|
|
45
|
+
debug('Starting recording promises')
|
|
46
|
+
running = true
|
|
47
|
+
asyncErr = null
|
|
48
|
+
errFn = null
|
|
49
|
+
this.reset()
|
|
51
50
|
},
|
|
52
51
|
|
|
53
52
|
/**
|
|
@@ -55,7 +54,7 @@ module.exports = {
|
|
|
55
54
|
* @inner
|
|
56
55
|
*/
|
|
57
56
|
isRunning() {
|
|
58
|
-
return running
|
|
57
|
+
return running
|
|
59
58
|
},
|
|
60
59
|
|
|
61
60
|
/**
|
|
@@ -64,7 +63,7 @@ module.exports = {
|
|
|
64
63
|
*/
|
|
65
64
|
startUnlessRunning() {
|
|
66
65
|
if (!this.isRunning()) {
|
|
67
|
-
this.start()
|
|
66
|
+
this.start()
|
|
68
67
|
}
|
|
69
68
|
},
|
|
70
69
|
|
|
@@ -76,7 +75,7 @@ module.exports = {
|
|
|
76
75
|
* @inner
|
|
77
76
|
*/
|
|
78
77
|
errHandler(fn) {
|
|
79
|
-
errFn = fn
|
|
78
|
+
errFn = fn
|
|
80
79
|
},
|
|
81
80
|
|
|
82
81
|
/**
|
|
@@ -87,16 +86,16 @@ module.exports = {
|
|
|
87
86
|
* @inner
|
|
88
87
|
*/
|
|
89
88
|
reset() {
|
|
90
|
-
if (promise && running) this.catch()
|
|
91
|
-
queueId
|
|
92
|
-
sessionId = null
|
|
93
|
-
asyncErr = null
|
|
94
|
-
log(`${currentQueue()} Starting recording promises`)
|
|
95
|
-
promise = Promise.resolve()
|
|
96
|
-
oldPromises = []
|
|
97
|
-
tasks = []
|
|
98
|
-
ignoredErrs = []
|
|
99
|
-
this.session.running = false
|
|
89
|
+
if (promise && running) this.catch()
|
|
90
|
+
queueId++
|
|
91
|
+
sessionId = null
|
|
92
|
+
asyncErr = null
|
|
93
|
+
log(`${currentQueue()} Starting recording promises`)
|
|
94
|
+
promise = Promise.resolve()
|
|
95
|
+
oldPromises = []
|
|
96
|
+
tasks = []
|
|
97
|
+
ignoredErrs = []
|
|
98
|
+
this.session.running = false
|
|
100
99
|
// reset this retries makes the retryFailedStep plugin won't work if there is Before/BeforeSuit block due to retries is undefined on Scenario
|
|
101
100
|
// this.retries = [];
|
|
102
101
|
},
|
|
@@ -123,12 +122,16 @@ module.exports = {
|
|
|
123
122
|
* @inner
|
|
124
123
|
*/
|
|
125
124
|
start(name) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
125
|
+
if (sessionId) {
|
|
126
|
+
debug(`${currentQueue()}Session already started as ${sessionId}`)
|
|
127
|
+
this.restore(sessionId)
|
|
128
|
+
}
|
|
129
|
+
debug(`${currentQueue()}Starting <${name}> session`)
|
|
130
|
+
tasks.push('--->')
|
|
131
|
+
oldPromises.push(promise)
|
|
132
|
+
this.running = true
|
|
133
|
+
sessionId = name
|
|
134
|
+
promise = Promise.resolve()
|
|
132
135
|
},
|
|
133
136
|
|
|
134
137
|
/**
|
|
@@ -136,12 +139,12 @@ module.exports = {
|
|
|
136
139
|
* @inner
|
|
137
140
|
*/
|
|
138
141
|
restore(name) {
|
|
139
|
-
tasks.push('<---')
|
|
140
|
-
debug(`${currentQueue()}Finalize <${name}> session`)
|
|
141
|
-
this.running = false
|
|
142
|
-
sessionId = null
|
|
143
|
-
this.catch(errFn)
|
|
144
|
-
promise = promise.then(() => oldPromises.pop())
|
|
142
|
+
tasks.push('<---')
|
|
143
|
+
debug(`${currentQueue()}Finalize <${name}> session`)
|
|
144
|
+
this.running = false
|
|
145
|
+
sessionId = null
|
|
146
|
+
this.catch(errFn)
|
|
147
|
+
promise = promise.then(() => oldPromises.pop())
|
|
145
148
|
},
|
|
146
149
|
|
|
147
150
|
/**
|
|
@@ -149,9 +152,8 @@ module.exports = {
|
|
|
149
152
|
* @inner
|
|
150
153
|
*/
|
|
151
154
|
catch(fn) {
|
|
152
|
-
promise = promise.catch(fn)
|
|
155
|
+
promise = promise.catch(fn)
|
|
153
156
|
},
|
|
154
|
-
|
|
155
157
|
},
|
|
156
158
|
|
|
157
159
|
/**
|
|
@@ -171,42 +173,48 @@ module.exports = {
|
|
|
171
173
|
*/
|
|
172
174
|
add(taskName, fn = undefined, force = false, retry = undefined, timeout = undefined) {
|
|
173
175
|
if (typeof taskName === 'function') {
|
|
174
|
-
fn = taskName
|
|
175
|
-
taskName = fn.toString()
|
|
176
|
-
if (retry === undefined) retry = false
|
|
176
|
+
fn = taskName
|
|
177
|
+
taskName = fn.toString()
|
|
178
|
+
if (retry === undefined) retry = false
|
|
177
179
|
}
|
|
178
|
-
if (retry === undefined) retry = true
|
|
180
|
+
if (retry === undefined) retry = true
|
|
179
181
|
if (!running && !force) {
|
|
180
|
-
return
|
|
182
|
+
return Promise.resolve()
|
|
181
183
|
}
|
|
182
|
-
tasks.push(taskName)
|
|
183
|
-
debug(chalk.gray(`${currentQueue()} Queued | ${taskName}`))
|
|
184
|
+
tasks.push(taskName)
|
|
185
|
+
debug(chalk.gray(`${currentQueue()} Queued | ${taskName}`))
|
|
184
186
|
|
|
185
|
-
return promise = Promise.resolve(promise).then(
|
|
187
|
+
return (promise = Promise.resolve(promise).then(res => {
|
|
186
188
|
// prefer options for non-conditional retries
|
|
187
|
-
const retryOpts = this.retries
|
|
189
|
+
const retryOpts = this.retries
|
|
190
|
+
.sort((r1, r2) => r1.when && !r2.when)
|
|
191
|
+
.slice(-1)
|
|
192
|
+
.pop()
|
|
188
193
|
// no retries or unnamed tasks
|
|
194
|
+
debug(`${currentQueue()} Running | ${taskName} | Timeout: ${timeout || 'None'}`)
|
|
195
|
+
if (retryOpts) debug(`${currentQueue()} Retry opts`, JSON.stringify(retryOpts))
|
|
196
|
+
|
|
189
197
|
if (!retryOpts || !taskName || !retry) {
|
|
190
|
-
const [promise, timer] = getTimeoutPromise(timeout, taskName)
|
|
191
|
-
return Promise.race([promise, Promise.resolve(res).then(fn)]).finally(() => clearTimeout(timer))
|
|
198
|
+
const [promise, timer] = getTimeoutPromise(timeout, taskName)
|
|
199
|
+
return Promise.race([promise, Promise.resolve(res).then(fn)]).finally(() => clearTimeout(timer))
|
|
192
200
|
}
|
|
193
201
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const retryRules = this.retries.slice().reverse();
|
|
202
|
+
const retryRules = this.retries.slice().reverse()
|
|
197
203
|
return promiseRetry(Object.assign(defaultRetryOptions, retryOpts), (retry, number) => {
|
|
198
|
-
if (number > 1) log(`${currentQueue()}Retrying... Attempt #${number}`)
|
|
199
|
-
const [promise, timer] = getTimeoutPromise(timeout, taskName)
|
|
200
|
-
return Promise.race([promise, Promise.resolve(res).then(fn)])
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
204
|
+
if (number > 1) log(`${currentQueue()}Retrying... Attempt #${number}`)
|
|
205
|
+
const [promise, timer] = getTimeoutPromise(timeout, taskName)
|
|
206
|
+
return Promise.race([promise, Promise.resolve(res).then(fn)])
|
|
207
|
+
.finally(() => clearTimeout(timer))
|
|
208
|
+
.catch(err => {
|
|
209
|
+
if (ignoredErrs.includes(err)) return
|
|
210
|
+
for (const retryObj of retryRules) {
|
|
211
|
+
if (!retryObj.when) return retry(err)
|
|
212
|
+
if (retryObj.when && retryObj.when(err)) return retry(err)
|
|
213
|
+
}
|
|
214
|
+
throw err
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
}))
|
|
210
218
|
},
|
|
211
219
|
|
|
212
220
|
/**
|
|
@@ -215,15 +223,15 @@ module.exports = {
|
|
|
215
223
|
* @inner
|
|
216
224
|
*/
|
|
217
225
|
retry(opts) {
|
|
218
|
-
if (!promise) return
|
|
226
|
+
if (!promise) return
|
|
219
227
|
|
|
220
228
|
if (opts === null) {
|
|
221
|
-
opts = {}
|
|
229
|
+
opts = {}
|
|
222
230
|
}
|
|
223
231
|
if (Number.isInteger(opts)) {
|
|
224
|
-
opts = { retries: opts }
|
|
232
|
+
opts = { retries: opts }
|
|
225
233
|
}
|
|
226
|
-
return this.add(() => this.retries.push(opts))
|
|
234
|
+
return this.add(() => this.retries.push(opts))
|
|
227
235
|
},
|
|
228
236
|
|
|
229
237
|
/**
|
|
@@ -232,20 +240,25 @@ module.exports = {
|
|
|
232
240
|
* @inner
|
|
233
241
|
*/
|
|
234
242
|
catch(customErrFn) {
|
|
235
|
-
const fnDescription = customErrFn
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
243
|
+
const fnDescription = customErrFn
|
|
244
|
+
?.toString()
|
|
245
|
+
?.replace(/\s{2,}/g, ' ')
|
|
246
|
+
.replace(/\n/g, ' ')
|
|
247
|
+
?.slice(0, 50)
|
|
248
|
+
debug(chalk.gray(`${currentQueue()} Queued | catch with error handler ${fnDescription || ''}`))
|
|
249
|
+
return (promise = promise.catch(err => {
|
|
250
|
+
log(`${currentQueue()}Error | ${err} ${fnDescription}...`)
|
|
251
|
+
if (!(err instanceof Error)) {
|
|
252
|
+
// strange things may happen
|
|
253
|
+
err = new Error(`[Wrapped Error] ${printObjectProperties(err)}`) // we should be prepared for them
|
|
241
254
|
}
|
|
242
255
|
if (customErrFn) {
|
|
243
|
-
customErrFn(err)
|
|
256
|
+
customErrFn(err)
|
|
244
257
|
} else if (errFn) {
|
|
245
|
-
errFn(err)
|
|
258
|
+
errFn(err)
|
|
246
259
|
}
|
|
247
|
-
this.stop()
|
|
248
|
-
})
|
|
260
|
+
this.stop()
|
|
261
|
+
}))
|
|
249
262
|
},
|
|
250
263
|
|
|
251
264
|
/**
|
|
@@ -254,17 +267,22 @@ module.exports = {
|
|
|
254
267
|
* @inner
|
|
255
268
|
*/
|
|
256
269
|
catchWithoutStop(customErrFn) {
|
|
257
|
-
const fnDescription = customErrFn
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
270
|
+
const fnDescription = customErrFn
|
|
271
|
+
?.toString()
|
|
272
|
+
?.replace(/\s{2,}/g, ' ')
|
|
273
|
+
.replace(/\n/g, ' ')
|
|
274
|
+
?.slice(0, 50)
|
|
275
|
+
return (promise = promise.catch(err => {
|
|
276
|
+
if (ignoredErrs.includes(err)) return // already caught
|
|
277
|
+
log(`${currentQueue()} Error (Non-Terminated) | ${err} | ${fnDescription || ''}...`)
|
|
278
|
+
if (!(err instanceof Error)) {
|
|
279
|
+
// strange things may happen
|
|
280
|
+
err = new Error(`[Wrapped Error] ${JSON.stringify(err)}`) // we should be prepared for them
|
|
263
281
|
}
|
|
264
282
|
if (customErrFn) {
|
|
265
|
-
return customErrFn(err)
|
|
283
|
+
return customErrFn(err)
|
|
266
284
|
}
|
|
267
|
-
})
|
|
285
|
+
}))
|
|
268
286
|
},
|
|
269
287
|
|
|
270
288
|
/**
|
|
@@ -276,15 +294,15 @@ module.exports = {
|
|
|
276
294
|
*/
|
|
277
295
|
|
|
278
296
|
throw(err) {
|
|
279
|
-
if (ignoredErrs.includes(err)) return promise
|
|
297
|
+
if (ignoredErrs.includes(err)) return promise // already caught
|
|
280
298
|
return this.add(`throw error: ${err.message}`, () => {
|
|
281
|
-
if (ignoredErrs.includes(err)) return
|
|
282
|
-
throw err
|
|
283
|
-
})
|
|
299
|
+
if (ignoredErrs.includes(err)) return // already caught
|
|
300
|
+
throw err
|
|
301
|
+
})
|
|
284
302
|
},
|
|
285
303
|
|
|
286
304
|
ignoreErr(err) {
|
|
287
|
-
ignoredErrs.push(err)
|
|
305
|
+
ignoredErrs.push(err)
|
|
288
306
|
},
|
|
289
307
|
|
|
290
308
|
/**
|
|
@@ -293,7 +311,7 @@ module.exports = {
|
|
|
293
311
|
*/
|
|
294
312
|
saveFirstAsyncError(err) {
|
|
295
313
|
if (asyncErr === null) {
|
|
296
|
-
asyncErr = err
|
|
314
|
+
asyncErr = err
|
|
297
315
|
}
|
|
298
316
|
},
|
|
299
317
|
|
|
@@ -302,7 +320,7 @@ module.exports = {
|
|
|
302
320
|
* @inner
|
|
303
321
|
*/
|
|
304
322
|
getAsyncErr() {
|
|
305
|
-
return asyncErr
|
|
323
|
+
return asyncErr
|
|
306
324
|
},
|
|
307
325
|
|
|
308
326
|
/**
|
|
@@ -310,7 +328,7 @@ module.exports = {
|
|
|
310
328
|
* @inner
|
|
311
329
|
*/
|
|
312
330
|
cleanAsyncErr() {
|
|
313
|
-
asyncErr = null
|
|
331
|
+
asyncErr = null
|
|
314
332
|
},
|
|
315
333
|
|
|
316
334
|
/**
|
|
@@ -319,9 +337,9 @@ module.exports = {
|
|
|
319
337
|
* @inner
|
|
320
338
|
*/
|
|
321
339
|
stop() {
|
|
322
|
-
debug(this.toString())
|
|
323
|
-
log(`${currentQueue()} Stopping recording promises`)
|
|
324
|
-
running = false
|
|
340
|
+
debug(this.toString())
|
|
341
|
+
log(`${currentQueue()} Stopping recording promises`)
|
|
342
|
+
running = false
|
|
325
343
|
},
|
|
326
344
|
|
|
327
345
|
/**
|
|
@@ -332,7 +350,7 @@ module.exports = {
|
|
|
332
350
|
* @inner
|
|
333
351
|
*/
|
|
334
352
|
promise() {
|
|
335
|
-
return promise
|
|
353
|
+
return promise
|
|
336
354
|
},
|
|
337
355
|
|
|
338
356
|
/**
|
|
@@ -341,7 +359,7 @@ module.exports = {
|
|
|
341
359
|
* @inner
|
|
342
360
|
*/
|
|
343
361
|
scheduled() {
|
|
344
|
-
return tasks.slice(-MAX_TASKS).join('\n')
|
|
362
|
+
return tasks.slice(-MAX_TASKS).join('\n')
|
|
345
363
|
},
|
|
346
364
|
|
|
347
365
|
/**
|
|
@@ -350,7 +368,7 @@ module.exports = {
|
|
|
350
368
|
* @inner
|
|
351
369
|
*/
|
|
352
370
|
getQueueId() {
|
|
353
|
-
return queueId
|
|
371
|
+
return queueId
|
|
354
372
|
},
|
|
355
373
|
|
|
356
374
|
/**
|
|
@@ -359,21 +377,34 @@ module.exports = {
|
|
|
359
377
|
* @inner
|
|
360
378
|
*/
|
|
361
379
|
toString() {
|
|
362
|
-
return `Queue: ${currentQueue()}\n\nTasks: ${this.scheduled()}
|
|
380
|
+
return `Queue: ${currentQueue()}\n\nTasks: ${this.scheduled()}`
|
|
363
381
|
},
|
|
364
382
|
|
|
365
|
-
|
|
383
|
+
/**
|
|
384
|
+
* Get current session ID
|
|
385
|
+
* @return {string|null}
|
|
386
|
+
* @inner
|
|
387
|
+
*/
|
|
388
|
+
getCurrentSessionId() {
|
|
389
|
+
return sessionId
|
|
390
|
+
},
|
|
391
|
+
}
|
|
366
392
|
|
|
367
393
|
function getTimeoutPromise(timeoutMs, taskName) {
|
|
368
|
-
let timer
|
|
369
|
-
if (timeoutMs) debug(`Timing out in ${timeoutMs}ms`)
|
|
370
|
-
return [
|
|
371
|
-
|
|
372
|
-
|
|
394
|
+
let timer
|
|
395
|
+
if (timeoutMs) debug(`Timing out in ${timeoutMs}ms`)
|
|
396
|
+
return [
|
|
397
|
+
new Promise((done, reject) => {
|
|
398
|
+
timer = setTimeout(() => {
|
|
399
|
+
reject(new TimeoutError(`Action ${taskName} was interrupted on timeout ${timeoutMs}ms`))
|
|
400
|
+
}, timeoutMs || 2e9)
|
|
401
|
+
}),
|
|
402
|
+
timer,
|
|
403
|
+
]
|
|
373
404
|
}
|
|
374
405
|
|
|
375
406
|
function currentQueue() {
|
|
376
|
-
let session = ''
|
|
377
|
-
if (sessionId) session = `<${sessionId}>
|
|
378
|
-
return `[${queueId}] ${session}
|
|
407
|
+
let session = ''
|
|
408
|
+
if (sessionId) session = `<${sessionId}> `
|
|
409
|
+
return `[${queueId}] ${session}`
|
|
379
410
|
}
|
package/lib/rerun.js
CHANGED
|
@@ -1,81 +1,82 @@
|
|
|
1
|
-
const fsPath = require('path')
|
|
2
|
-
const container = require('./container')
|
|
3
|
-
const event = require('./event')
|
|
4
|
-
const BaseCodecept = require('./codecept')
|
|
5
|
-
const output = require('./output')
|
|
1
|
+
const fsPath = require('path')
|
|
2
|
+
const container = require('./container')
|
|
3
|
+
const event = require('./event')
|
|
4
|
+
const BaseCodecept = require('./codecept')
|
|
5
|
+
const output = require('./output')
|
|
6
6
|
|
|
7
7
|
class CodeceptRerunner extends BaseCodecept {
|
|
8
8
|
runOnce(test) {
|
|
9
9
|
return new Promise((resolve, reject) => {
|
|
10
10
|
// @ts-ignore
|
|
11
|
-
container.createMocha()
|
|
12
|
-
const mocha = container.mocha()
|
|
13
|
-
this.testFiles.forEach(
|
|
14
|
-
delete require.cache[file]
|
|
15
|
-
})
|
|
16
|
-
mocha.files = this.testFiles
|
|
11
|
+
container.createMocha()
|
|
12
|
+
const mocha = container.mocha()
|
|
13
|
+
this.testFiles.forEach(file => {
|
|
14
|
+
delete require.cache[file]
|
|
15
|
+
})
|
|
16
|
+
mocha.files = this.testFiles
|
|
17
17
|
if (test) {
|
|
18
18
|
if (!fsPath.isAbsolute(test)) {
|
|
19
|
-
test = fsPath.join(global.codecept_dir, test)
|
|
19
|
+
test = fsPath.join(global.codecept_dir, test)
|
|
20
20
|
}
|
|
21
|
-
mocha.files = mocha.files.filter(t => fsPath.basename(t, '.js') === test || t === test)
|
|
21
|
+
mocha.files = mocha.files.filter(t => fsPath.basename(t, '.js') === test || t === test)
|
|
22
22
|
}
|
|
23
23
|
try {
|
|
24
|
-
mocha.run(
|
|
24
|
+
mocha.run(failures => {
|
|
25
25
|
if (failures === 0) {
|
|
26
|
-
resolve()
|
|
26
|
+
resolve()
|
|
27
27
|
} else {
|
|
28
|
-
reject(new Error(`${failures} tests fail`))
|
|
28
|
+
reject(new Error(`${failures} tests fail`))
|
|
29
29
|
}
|
|
30
|
-
})
|
|
30
|
+
})
|
|
31
31
|
} catch (e) {
|
|
32
|
-
reject(e)
|
|
32
|
+
reject(e)
|
|
33
33
|
}
|
|
34
|
-
})
|
|
34
|
+
})
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
async runTests(test) {
|
|
38
|
-
const configRerun = this.config.rerun || {}
|
|
39
|
-
const minSuccess = configRerun.minSuccess || 1
|
|
40
|
-
const maxReruns = configRerun.maxReruns || 1
|
|
38
|
+
const configRerun = this.config.rerun || {}
|
|
39
|
+
const minSuccess = configRerun.minSuccess || 1
|
|
40
|
+
const maxReruns = configRerun.maxReruns || 1
|
|
41
41
|
if (minSuccess > maxReruns) {
|
|
42
|
-
process.exitCode = 1
|
|
43
|
-
throw new Error(`run-rerun Configuration Error: minSuccess must be less than maxReruns. Current values: minSuccess=${minSuccess} maxReruns=${maxReruns}`)
|
|
42
|
+
process.exitCode = 1
|
|
43
|
+
throw new Error(`run-rerun Configuration Error: minSuccess must be less than maxReruns. Current values: minSuccess=${minSuccess} maxReruns=${maxReruns}`)
|
|
44
44
|
}
|
|
45
45
|
if (maxReruns === 1) {
|
|
46
|
-
await this.runOnce(test)
|
|
47
|
-
return
|
|
46
|
+
await this.runOnce(test)
|
|
47
|
+
return
|
|
48
48
|
}
|
|
49
|
-
let successCounter = 0
|
|
50
|
-
let rerunsCounter = 0
|
|
49
|
+
let successCounter = 0
|
|
50
|
+
let rerunsCounter = 0
|
|
51
51
|
while (rerunsCounter < maxReruns && successCounter < minSuccess) {
|
|
52
|
-
|
|
52
|
+
container.result().reset() // reset result
|
|
53
|
+
rerunsCounter++
|
|
53
54
|
try {
|
|
54
|
-
await this.runOnce(test)
|
|
55
|
-
successCounter
|
|
56
|
-
output.success(`\nProcess run ${rerunsCounter} of max ${maxReruns}, success runs ${successCounter}/${minSuccess}\n`)
|
|
55
|
+
await this.runOnce(test)
|
|
56
|
+
successCounter++
|
|
57
|
+
output.success(`\nProcess run ${rerunsCounter} of max ${maxReruns}, success runs ${successCounter}/${minSuccess}\n`)
|
|
57
58
|
} catch (e) {
|
|
58
|
-
output.error(`\nFail run ${rerunsCounter} of max ${maxReruns}, success runs ${successCounter}/${minSuccess} \n`)
|
|
59
|
-
console.error(e)
|
|
59
|
+
output.error(`\nFail run ${rerunsCounter} of max ${maxReruns}, success runs ${successCounter}/${minSuccess} \n`)
|
|
60
|
+
console.error(e)
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
if (successCounter < minSuccess) {
|
|
63
|
-
throw new Error(`Flaky tests detected! ${successCounter} success runs achieved instead of ${minSuccess} success runs expected`)
|
|
64
|
+
throw new Error(`Flaky tests detected! ${successCounter} success runs achieved instead of ${minSuccess} success runs expected`)
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
async run(test) {
|
|
68
|
-
event.emit(event.all.before, this)
|
|
69
|
+
event.emit(event.all.before, this)
|
|
69
70
|
try {
|
|
70
|
-
await this.runTests(test)
|
|
71
|
+
await this.runTests(test)
|
|
71
72
|
} catch (e) {
|
|
72
|
-
output.error(e.stack)
|
|
73
|
-
throw e
|
|
73
|
+
output.error(e.stack)
|
|
74
|
+
throw e
|
|
74
75
|
} finally {
|
|
75
|
-
event.emit(event.all.result, this)
|
|
76
|
-
event.emit(event.all.after, this)
|
|
76
|
+
event.emit(event.all.result, this)
|
|
77
|
+
event.emit(event.all.after, this)
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
module.exports = CodeceptRerunner
|
|
82
|
+
module.exports = CodeceptRerunner
|