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
package/lib/plugin/tryTo.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
const { tryTo } = require('../effects')
|
|
2
|
+
|
|
3
|
+
module.exports = function (config) {
|
|
2
4
|
console.log(`
|
|
3
5
|
Deprecated Warning: 'tryTo' has been moved to the effects module.
|
|
4
6
|
You should update your tests to use it as follows:
|
|
@@ -14,4 +16,10 @@ await tryTo(() => {
|
|
|
14
16
|
|
|
15
17
|
For more details, refer to the documentation.
|
|
16
18
|
`)
|
|
19
|
+
|
|
20
|
+
if (config.registerGlobal) {
|
|
21
|
+
global.tryTo = tryTo
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return tryTo
|
|
17
25
|
}
|
package/lib/recorder.js
CHANGED
|
@@ -3,7 +3,7 @@ const promiseRetry = require('promise-retry')
|
|
|
3
3
|
const chalk = require('chalk')
|
|
4
4
|
const { printObjectProperties } = require('./utils')
|
|
5
5
|
const { log } = require('./output')
|
|
6
|
-
|
|
6
|
+
const { TimeoutError } = require('./timeout')
|
|
7
7
|
const MAX_TASKS = 100
|
|
8
8
|
|
|
9
9
|
let promise
|
|
@@ -191,13 +191,13 @@ module.exports = {
|
|
|
191
191
|
.slice(-1)
|
|
192
192
|
.pop()
|
|
193
193
|
// no retries or unnamed tasks
|
|
194
|
+
debug(`${currentQueue()} Running | ${taskName} | Timeout: ${timeout || 'None'}`)
|
|
195
|
+
|
|
194
196
|
if (!retryOpts || !taskName || !retry) {
|
|
195
197
|
const [promise, timer] = getTimeoutPromise(timeout, taskName)
|
|
196
198
|
return Promise.race([promise, Promise.resolve(res).then(fn)]).finally(() => clearTimeout(timer))
|
|
197
199
|
}
|
|
198
200
|
|
|
199
|
-
debug(`${currentQueue()} Running | ${taskName}`)
|
|
200
|
-
|
|
201
201
|
const retryRules = this.retries.slice().reverse()
|
|
202
202
|
return promiseRetry(Object.assign(defaultRetryOptions, retryOpts), (retry, number) => {
|
|
203
203
|
if (number > 1) log(`${currentQueue()}Retrying... Attempt #${number}`)
|
|
@@ -386,7 +386,7 @@ function getTimeoutPromise(timeoutMs, taskName) {
|
|
|
386
386
|
return [
|
|
387
387
|
new Promise((done, reject) => {
|
|
388
388
|
timer = setTimeout(() => {
|
|
389
|
-
reject(new
|
|
389
|
+
reject(new TimeoutError(`Action ${taskName} was interrupted on timeout ${timeoutMs}ms`))
|
|
390
390
|
}, timeoutMs || 2e9)
|
|
391
391
|
}),
|
|
392
392
|
timer,
|
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
|
package/lib/result.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { serializeTest } = require('./mocha/test')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Result of the test run
|
|
7
|
+
*
|
|
8
|
+
* @typedef {Object} Stats
|
|
9
|
+
* @property {number} passes
|
|
10
|
+
* @property {number} failures
|
|
11
|
+
* @property {number} tests
|
|
12
|
+
* @property {number} pending
|
|
13
|
+
* @property {number} failedHooks
|
|
14
|
+
* @property {Date} start
|
|
15
|
+
* @property {Date} end
|
|
16
|
+
* @property {number} duration
|
|
17
|
+
*/
|
|
18
|
+
class Result {
|
|
19
|
+
/**
|
|
20
|
+
* Create Result of the test run
|
|
21
|
+
*/
|
|
22
|
+
constructor() {
|
|
23
|
+
this._startTime = new Date()
|
|
24
|
+
this._endTime = null
|
|
25
|
+
|
|
26
|
+
this.reset()
|
|
27
|
+
this.start()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
reset() {
|
|
31
|
+
this._stats = {
|
|
32
|
+
passes: 0,
|
|
33
|
+
failures: 0,
|
|
34
|
+
tests: 0,
|
|
35
|
+
pending: 0,
|
|
36
|
+
failedHooks: 0,
|
|
37
|
+
start: null,
|
|
38
|
+
end: null,
|
|
39
|
+
duration: 0,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** @type {CodeceptJS.Test[]} */
|
|
43
|
+
this._tests = []
|
|
44
|
+
|
|
45
|
+
/** @type {String[]} */
|
|
46
|
+
this._failures = []
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
start() {
|
|
50
|
+
this._startTime = new Date()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
finish() {
|
|
54
|
+
this._endTime = new Date()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get hasFailed() {
|
|
58
|
+
return this._stats.failures > 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get tests() {
|
|
62
|
+
return this._tests
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get failures() {
|
|
66
|
+
return this._failures.filter(f => f && (!Array.isArray(f) || f.length > 0))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get stats() {
|
|
70
|
+
return this._stats
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get startTime() {
|
|
74
|
+
return this._startTime
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Add test to result
|
|
79
|
+
*
|
|
80
|
+
* @param {CodeceptJS.Test} test
|
|
81
|
+
*/
|
|
82
|
+
addTest(test) {
|
|
83
|
+
const existingTestIndex = this._tests.findIndex(t => !!t.uid && t.uid === test.uid)
|
|
84
|
+
if (existingTestIndex >= 0) {
|
|
85
|
+
this._tests[existingTestIndex] = test
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this._tests.push(test)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Add failures to result
|
|
94
|
+
*
|
|
95
|
+
* @param {String[]} newFailures
|
|
96
|
+
*/
|
|
97
|
+
addFailures(newFailures) {
|
|
98
|
+
this._failures.push(...newFailures)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get hasFailures() {
|
|
102
|
+
return this.stats.failures > 0
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
get duration() {
|
|
106
|
+
return this._endTime ? +this._endTime - +this._startTime : 0
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get failedTests() {
|
|
110
|
+
return this._tests.filter(test => test.state === 'failed')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get passedTests() {
|
|
114
|
+
return this._tests.filter(test => test.state === 'passed')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
get skippedTests() {
|
|
118
|
+
return this._tests.filter(test => test.state === 'skipped' || test.state === 'pending')
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
simplify() {
|
|
122
|
+
return {
|
|
123
|
+
hasFailed: this.hasFailed,
|
|
124
|
+
stats: this.stats,
|
|
125
|
+
duration: this.duration,
|
|
126
|
+
tests: this._tests.map(test => serializeTest(test)),
|
|
127
|
+
failures: this._failures,
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Save result to json file
|
|
133
|
+
*
|
|
134
|
+
* @param {string} fileName
|
|
135
|
+
*/
|
|
136
|
+
save(fileName) {
|
|
137
|
+
if (!fileName) fileName = 'result.json'
|
|
138
|
+
fs.writeFileSync(path.join(global.output_dir, fileName), JSON.stringify(this.simplify(), null, 2))
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Add stats to result
|
|
143
|
+
*
|
|
144
|
+
* @param {object} newStats
|
|
145
|
+
*/
|
|
146
|
+
addStats(newStats = {}) {
|
|
147
|
+
this._stats.passes += newStats.passes || 0
|
|
148
|
+
this._stats.failures += newStats.failures || 0
|
|
149
|
+
this._stats.tests += newStats.tests || 0
|
|
150
|
+
this._stats.pending += newStats.pending || 0
|
|
151
|
+
this._stats.failedHooks += newStats.failedHooks || 0
|
|
152
|
+
|
|
153
|
+
// do not override start time
|
|
154
|
+
this._stats.start = this._stats.start || newStats.start
|
|
155
|
+
|
|
156
|
+
this._stats.end = newStats.end || this._stats.end
|
|
157
|
+
this._stats.duration = newStats.duration
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = Result
|
package/lib/step/base.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const color = require('chalk')
|
|
2
2
|
const Secret = require('../secret')
|
|
3
|
-
const { getCurrentTimeout } = require('
|
|
4
|
-
const { ucfirst, humanizeString } = require('../utils')
|
|
3
|
+
const { getCurrentTimeout } = require('../timeout')
|
|
4
|
+
const { ucfirst, humanizeString, serializeError } = require('../utils')
|
|
5
5
|
|
|
6
|
-
const STACK_LINE =
|
|
6
|
+
const STACK_LINE = 5
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Each command in test executed through `I.` object is wrapped in Step.
|
|
@@ -37,6 +37,9 @@ class Step {
|
|
|
37
37
|
/** @member {string} */
|
|
38
38
|
this.stack = ''
|
|
39
39
|
|
|
40
|
+
this.startTime = 0
|
|
41
|
+
this.endTime = 0
|
|
42
|
+
|
|
40
43
|
this.setTrace()
|
|
41
44
|
}
|
|
42
45
|
|
|
@@ -159,6 +162,51 @@ class Step {
|
|
|
159
162
|
return this.constructor.name === 'MetaStep'
|
|
160
163
|
}
|
|
161
164
|
|
|
165
|
+
get duration() {
|
|
166
|
+
if (!this.startTime || !this.endTime) return 0
|
|
167
|
+
return this.endTime - this.startTime
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
simplify() {
|
|
171
|
+
const step = this
|
|
172
|
+
|
|
173
|
+
const parent = {}
|
|
174
|
+
if (step.metaStep) {
|
|
175
|
+
parent.title = step.metaStep.actor
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (step.opts) {
|
|
179
|
+
Object.keys(step.opts).forEach(k => {
|
|
180
|
+
if (typeof step.opts[k] === 'object') delete step.opts[k]
|
|
181
|
+
if (typeof step.opts[k] === 'function') delete step.opts[k]
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const args = []
|
|
186
|
+
if (step.args) {
|
|
187
|
+
for (const arg of step.args) {
|
|
188
|
+
// check if arg is a JOI object
|
|
189
|
+
if (arg && typeof arg === 'function') {
|
|
190
|
+
args.push(arg.name)
|
|
191
|
+
} else if (typeof arg == 'string') {
|
|
192
|
+
args.push(arg)
|
|
193
|
+
} else {
|
|
194
|
+
args.push(JSON.stringify(arg).slice(0, 300))
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
opts: step.opts || {},
|
|
201
|
+
title: step.name,
|
|
202
|
+
args: args,
|
|
203
|
+
status: step.status,
|
|
204
|
+
startTime: step.startTime,
|
|
205
|
+
endTime: step.endTime,
|
|
206
|
+
parent,
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
162
210
|
/** @return {boolean} */
|
|
163
211
|
hasBDDAncestor() {
|
|
164
212
|
let hasBDD = false
|
|
@@ -166,7 +214,7 @@ class Step {
|
|
|
166
214
|
processingStep = this
|
|
167
215
|
|
|
168
216
|
while (processingStep.metaStep) {
|
|
169
|
-
if (processingStep.metaStep.actor
|
|
217
|
+
if (processingStep.metaStep.actor?.match(/^(Given|When|Then|And)/)) {
|
|
170
218
|
hasBDD = true
|
|
171
219
|
break
|
|
172
220
|
} else {
|
package/lib/step/func.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const BaseStep = require('./base')
|
|
2
|
+
const store = require('../store')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Function executed as a step
|
|
6
|
+
*/
|
|
7
|
+
class FuncStep extends BaseStep {
|
|
8
|
+
// this is actual function that should be executed within step
|
|
9
|
+
setCallable(fn) {
|
|
10
|
+
this.fn = fn
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// helper is optional, if we need to allow step to access helper methods
|
|
14
|
+
setHelper(helper) {
|
|
15
|
+
this.helper = helper
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
run() {
|
|
19
|
+
if (!this.fn) throw new Error('Function is not set')
|
|
20
|
+
|
|
21
|
+
// we wrap that function to track time and status
|
|
22
|
+
// and disable it in dry run mode
|
|
23
|
+
this.args = Array.prototype.slice.call(arguments)
|
|
24
|
+
this.startTime = +Date.now()
|
|
25
|
+
|
|
26
|
+
if (store.dryRun) {
|
|
27
|
+
this.setStatus('success')
|
|
28
|
+
// should we add Proxy and dry run resolver here?
|
|
29
|
+
return Promise.resolve(true)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let result
|
|
33
|
+
try {
|
|
34
|
+
result = this.fn.apply(this.helper, this.args)
|
|
35
|
+
this.setStatus('success')
|
|
36
|
+
this.endTime = +Date.now()
|
|
37
|
+
} catch (err) {
|
|
38
|
+
this.endTime = +Date.now()
|
|
39
|
+
this.setStatus('failed')
|
|
40
|
+
throw err
|
|
41
|
+
}
|
|
42
|
+
return result
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = FuncStep
|
package/lib/step/helper.js
CHANGED
|
@@ -16,6 +16,7 @@ class HelperStep extends Step {
|
|
|
16
16
|
*/
|
|
17
17
|
run() {
|
|
18
18
|
this.args = Array.prototype.slice.call(arguments)
|
|
19
|
+
this.startTime = +Date.now()
|
|
19
20
|
|
|
20
21
|
if (store.dryRun) {
|
|
21
22
|
this.setStatus('success')
|
|
@@ -27,7 +28,9 @@ class HelperStep extends Step {
|
|
|
27
28
|
result = this.helper[this.helperMethod].apply(this.helper, this.args)
|
|
28
29
|
}
|
|
29
30
|
this.setStatus('success')
|
|
31
|
+
this.endTime = +Date.now()
|
|
30
32
|
} catch (err) {
|
|
33
|
+
this.endTime = +Date.now()
|
|
31
34
|
this.setStatus('failed')
|
|
32
35
|
throw err
|
|
33
36
|
}
|
package/lib/step/meta.js
CHANGED
|
@@ -6,6 +6,10 @@ class MetaStep extends Step {
|
|
|
6
6
|
constructor(actor, method) {
|
|
7
7
|
if (!method) method = ''
|
|
8
8
|
super(method)
|
|
9
|
+
|
|
10
|
+
/** @member {boolean} collsapsed hide children steps from output */
|
|
11
|
+
this.collapsed = false
|
|
12
|
+
|
|
9
13
|
this.actor = actor
|
|
10
14
|
}
|
|
11
15
|
|
|
@@ -32,7 +36,11 @@ class MetaStep extends Step {
|
|
|
32
36
|
return `${this.prefix}${actorText} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
if (!this.actor) {
|
|
40
|
+
return `${this.name} ${this.humanizeArgs()}${this.suffix}`.trim()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return `On ${this.prefix}${actorText}: ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`.trim()
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
humanize() {
|
package/lib/step/record.js
CHANGED
|
@@ -3,7 +3,7 @@ const recorder = require('../recorder')
|
|
|
3
3
|
const StepConfig = require('./config')
|
|
4
4
|
const { debug } = require('../output')
|
|
5
5
|
const store = require('../store')
|
|
6
|
-
const { TIMEOUT_ORDER } = require('
|
|
6
|
+
const { TIMEOUT_ORDER } = require('../timeout')
|
|
7
7
|
const retryStep = require('./retry')
|
|
8
8
|
function recordStep(step, args) {
|
|
9
9
|
step.status = 'queued'
|
|
@@ -40,7 +40,7 @@ function recordStep(step, args) {
|
|
|
40
40
|
if (!step.startTime) {
|
|
41
41
|
// step can be retries
|
|
42
42
|
event.emit(event.step.started, step)
|
|
43
|
-
step.startTime = Date.now()
|
|
43
|
+
step.startTime = +Date.now()
|
|
44
44
|
}
|
|
45
45
|
return (val = step.run(...args))
|
|
46
46
|
},
|
|
@@ -52,15 +52,15 @@ function recordStep(step, args) {
|
|
|
52
52
|
event.emit(event.step.after, step)
|
|
53
53
|
|
|
54
54
|
recorder.add('step passed', () => {
|
|
55
|
-
step.endTime = Date.now()
|
|
55
|
+
step.endTime = +Date.now()
|
|
56
56
|
event.emit(event.step.passed, step, val)
|
|
57
57
|
event.emit(event.step.finished, step)
|
|
58
58
|
})
|
|
59
59
|
|
|
60
60
|
recorder.catchWithoutStop(err => {
|
|
61
61
|
step.status = 'failed'
|
|
62
|
-
step.endTime = Date.now()
|
|
63
|
-
event.emit(event.step.failed, step)
|
|
62
|
+
step.endTime = +Date.now()
|
|
63
|
+
event.emit(event.step.failed, step, err)
|
|
64
64
|
event.emit(event.step.finished, step)
|
|
65
65
|
throw err
|
|
66
66
|
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const MetaStep = require('./meta')
|
|
2
|
+
const event = require('../event')
|
|
3
|
+
|
|
4
|
+
let currentSection
|
|
5
|
+
|
|
6
|
+
class Section {
|
|
7
|
+
constructor(name = '') {
|
|
8
|
+
this.name = name
|
|
9
|
+
|
|
10
|
+
this.metaStep = new MetaStep(null, name)
|
|
11
|
+
|
|
12
|
+
this.attachMetaStep = step => {
|
|
13
|
+
if (currentSection !== this) return
|
|
14
|
+
if (!step) return
|
|
15
|
+
const metaStep = getRootMetaStep(step)
|
|
16
|
+
|
|
17
|
+
if (metaStep !== this.metaStep) {
|
|
18
|
+
metaStep.metaStep = this.metaStep
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
hidden() {
|
|
24
|
+
this.metaStep.collapsed = true
|
|
25
|
+
return this
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
start() {
|
|
29
|
+
if (currentSection) currentSection.end()
|
|
30
|
+
currentSection = this
|
|
31
|
+
event.dispatcher.prependListener(event.step.before, this.attachMetaStep)
|
|
32
|
+
event.dispatcher.once(event.test.finished, () => this.end())
|
|
33
|
+
return this
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
end() {
|
|
37
|
+
currentSection = null
|
|
38
|
+
event.dispatcher.off(event.step.started, this.attachMetaStep)
|
|
39
|
+
return this
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @returns {Section}
|
|
44
|
+
*/
|
|
45
|
+
static current() {
|
|
46
|
+
return currentSection
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getRootMetaStep(step) {
|
|
51
|
+
if (step.metaStep) return getRootMetaStep(step.metaStep)
|
|
52
|
+
return step
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = Section
|
package/lib/step.js
CHANGED
|
@@ -14,7 +14,13 @@ const Step = require('./step/helper')
|
|
|
14
14
|
*/
|
|
15
15
|
const MetaStep = require('./step/meta')
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Step used to execute a single function
|
|
19
|
+
*/
|
|
20
|
+
const FuncStep = require('./step/func')
|
|
21
|
+
|
|
17
22
|
module.exports = Step
|
|
18
23
|
module.exports.MetaStep = MetaStep
|
|
19
24
|
module.exports.BaseStep = BaseStep
|
|
20
25
|
module.exports.StepConfig = StepConfig
|
|
26
|
+
module.exports.FuncStep = FuncStep
|
package/lib/steps.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const StepConfig = require('./step/config')
|
|
2
|
-
|
|
2
|
+
const Section = require('./step/section')
|
|
3
3
|
function stepOpts(opts = {}) {
|
|
4
4
|
return new StepConfig(opts)
|
|
5
5
|
}
|
|
@@ -12,12 +12,39 @@ function stepRetry(retry) {
|
|
|
12
12
|
return new StepConfig().retry(retry)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
function section(name) {
|
|
16
|
+
if (!name) return endSection()
|
|
17
|
+
return new Section(name).start()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function endSection() {
|
|
21
|
+
return Section.current().end()
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
// Section function to be added here
|
|
16
25
|
|
|
17
26
|
const step = {
|
|
27
|
+
// steps.opts syntax
|
|
18
28
|
opts: stepOpts,
|
|
19
29
|
timeout: stepTimeout,
|
|
20
30
|
retry: stepRetry,
|
|
31
|
+
|
|
32
|
+
// one-function syntax
|
|
33
|
+
stepTimeout,
|
|
34
|
+
stepRetry,
|
|
35
|
+
stepOpts,
|
|
36
|
+
|
|
37
|
+
// sections
|
|
38
|
+
section,
|
|
39
|
+
endSection,
|
|
40
|
+
|
|
41
|
+
Section: section,
|
|
42
|
+
EndSection: endSection,
|
|
43
|
+
|
|
44
|
+
// shortcuts
|
|
45
|
+
Given: () => section('Given'),
|
|
46
|
+
When: () => section('When'),
|
|
47
|
+
Then: () => section('Then'),
|
|
21
48
|
}
|
|
22
49
|
|
|
23
50
|
module.exports = step
|