codeceptjs 3.7.0-beta.1 → 3.7.0-beta.11
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 +9 -10
- package/bin/codecept.js +7 -0
- package/lib/actor.js +46 -92
- package/lib/ai.js +130 -121
- package/lib/codecept.js +2 -2
- package/lib/command/check.js +186 -0
- package/lib/command/definitions.js +3 -1
- package/lib/command/interactive.js +1 -1
- package/lib/command/run-workers.js +2 -54
- package/lib/command/workers/runTests.js +64 -225
- package/lib/container.js +32 -0
- package/lib/effects.js +218 -0
- package/lib/els.js +87 -106
- package/lib/event.js +18 -17
- package/lib/heal.js +10 -0
- package/lib/helper/AI.js +2 -1
- package/lib/helper/Appium.js +31 -22
- package/lib/helper/Playwright.js +22 -1
- package/lib/helper/Puppeteer.js +5 -0
- package/lib/helper/WebDriver.js +29 -8
- package/lib/listener/emptyRun.js +2 -5
- package/lib/listener/exit.js +5 -8
- package/lib/listener/globalTimeout.js +66 -10
- package/lib/listener/result.js +12 -0
- package/lib/listener/steps.js +3 -6
- package/lib/listener/store.js +9 -1
- package/lib/mocha/asyncWrapper.js +15 -3
- package/lib/mocha/cli.js +79 -28
- package/lib/mocha/featureConfig.js +13 -0
- package/lib/mocha/hooks.js +32 -3
- package/lib/mocha/inject.js +5 -0
- package/lib/mocha/scenarioConfig.js +11 -0
- package/lib/mocha/suite.js +27 -1
- package/lib/mocha/test.js +102 -3
- package/lib/mocha/types.d.ts +11 -0
- package/lib/output.js +75 -73
- package/lib/pause.js +3 -10
- package/lib/plugin/analyze.js +349 -0
- package/lib/plugin/autoDelay.js +2 -2
- package/lib/plugin/commentStep.js +5 -0
- package/lib/plugin/customReporter.js +52 -0
- package/lib/plugin/heal.js +30 -0
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/retryTo.js +18 -118
- package/lib/plugin/screenshotOnFail.js +12 -17
- package/lib/plugin/standardActingHelpers.js +4 -1
- package/lib/plugin/stepByStepReport.js +6 -5
- package/lib/plugin/stepTimeout.js +1 -1
- package/lib/plugin/tryTo.js +17 -107
- package/lib/recorder.js +5 -5
- package/lib/rerun.js +43 -42
- package/lib/result.js +161 -0
- package/lib/step/base.js +228 -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 +20 -347
- package/lib/steps.js +50 -0
- package/lib/store.js +4 -0
- package/lib/timeout.js +66 -0
- package/lib/utils.js +93 -0
- package/lib/within.js +2 -2
- package/lib/workers.js +29 -49
- package/package.json +23 -20
- package/typings/index.d.ts +5 -4
- package/typings/promiseBasedTypes.d.ts +507 -49
- package/typings/types.d.ts +623 -73
- package/lib/listener/artifacts.js +0 -19
- package/lib/plugin/debugErrors.js +0 -67
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const recorder = require('../recorder')
|
|
2
|
+
const event = require('../event')
|
|
3
|
+
|
|
4
|
+
function retryStep(opts) {
|
|
5
|
+
if (opts === undefined) opts = 1
|
|
6
|
+
recorder.retry(opts)
|
|
7
|
+
// remove retry once the step passed
|
|
8
|
+
recorder.add(() => event.dispatcher.once(event.step.finished, () => recorder.retries.pop()))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = retryStep
|
|
@@ -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
|
@@ -1,353 +1,26 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
const color = require('chalk')
|
|
4
|
-
const store = require('./store')
|
|
5
|
-
const Secret = require('./secret')
|
|
6
|
-
const event = require('./event')
|
|
7
|
-
const { ucfirst } = require('./utils')
|
|
8
|
-
|
|
9
|
-
const STACK_LINE = 4
|
|
10
|
-
|
|
1
|
+
// refactored step class, moved to helper
|
|
11
2
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @param {CodeceptJS.Helper} helper
|
|
15
|
-
* @param {string} name
|
|
3
|
+
* Step is wrapper around a helper method.
|
|
4
|
+
* It is used to create a new step that is a combination of other steps.
|
|
16
5
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* timeouts set with order below zero only override timeouts of higher order if their value is smaller
|
|
22
|
-
*/
|
|
23
|
-
testOrSuite: -5,
|
|
24
|
-
/**
|
|
25
|
-
* 0-9 - designated for override of timeouts set from code, 5 is used by stepTimeout plugin when stepTimeout.config.overrideStepLimits=true
|
|
26
|
-
*/
|
|
27
|
-
stepTimeoutHard: 5,
|
|
28
|
-
/**
|
|
29
|
-
* 10-19 - designated for timeouts set from code, 15 is order of I.setTimeout(t) operation
|
|
30
|
-
*/
|
|
31
|
-
codeLimitTime: 15,
|
|
32
|
-
/**
|
|
33
|
-
* 20-29 - designated for timeout settings which could be overriden in tests code, 25 is used by stepTimeout plugin when stepTimeout.config.overrideStepLimits=false
|
|
34
|
-
*/
|
|
35
|
-
stepTimeoutSoft: 25,
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
constructor(helper, name) {
|
|
40
|
-
/** @member {string} */
|
|
41
|
-
this.actor = 'I' // I = actor
|
|
42
|
-
/** @member {CodeceptJS.Helper} */
|
|
43
|
-
this.helper = helper // corresponding helper
|
|
44
|
-
/** @member {string} */
|
|
45
|
-
this.name = name // name of a step console
|
|
46
|
-
/** @member {string} */
|
|
47
|
-
this.helperMethod = name // helper method
|
|
48
|
-
/** @member {string} */
|
|
49
|
-
this.status = 'pending'
|
|
50
|
-
/**
|
|
51
|
-
* @member {string} suffix
|
|
52
|
-
* @memberof CodeceptJS.Step#
|
|
53
|
-
*/
|
|
54
|
-
/** @member {string} */
|
|
55
|
-
this.prefix = this.suffix = ''
|
|
56
|
-
/** @member {string} */
|
|
57
|
-
this.comment = ''
|
|
58
|
-
/** @member {Array<*>} */
|
|
59
|
-
this.args = []
|
|
60
|
-
/** @member {MetaStep} */
|
|
61
|
-
this.metaStep = undefined
|
|
62
|
-
/** @member {string} */
|
|
63
|
-
this.stack = ''
|
|
64
|
-
|
|
65
|
-
const timeouts = new Map()
|
|
66
|
-
/**
|
|
67
|
-
* @method
|
|
68
|
-
* @returns {number|undefined}
|
|
69
|
-
*/
|
|
70
|
-
this.getTimeout = function () {
|
|
71
|
-
let totalTimeout
|
|
72
|
-
// iterate over all timeouts starting from highest values of order
|
|
73
|
-
new Map([...timeouts.entries()].sort().reverse()).forEach((timeout, order) => {
|
|
74
|
-
if (
|
|
75
|
-
timeout !== undefined &&
|
|
76
|
-
// when orders >= 0 - timeout value overrides those set with higher order elements
|
|
77
|
-
(order >= 0 ||
|
|
78
|
-
// when `order < 0 && totalTimeout === undefined` - timeout is used when nothing is set by elements with higher order
|
|
79
|
-
totalTimeout === undefined ||
|
|
80
|
-
// when `order < 0` - timeout overrides higher values of timeout or 'no timeout' (totalTimeout === 0) set by elements with higher order
|
|
81
|
-
(timeout > 0 && (timeout < totalTimeout || totalTimeout === 0)))
|
|
82
|
-
) {
|
|
83
|
-
totalTimeout = timeout
|
|
84
|
-
}
|
|
85
|
-
})
|
|
86
|
-
return totalTimeout
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* @method
|
|
90
|
-
* @param {number} timeout - timeout in milliseconds or 0 if no timeout
|
|
91
|
-
* @param {number} order - order defines the priority of timeout, timeouts set with lower order override those set with higher order.
|
|
92
|
-
* When order below 0 value of timeout only override if new value is lower
|
|
93
|
-
*/
|
|
94
|
-
this.setTimeout = function (timeout, order) {
|
|
95
|
-
timeouts.set(order, timeout)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
this.setTrace()
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** @function */
|
|
102
|
-
setTrace() {
|
|
103
|
-
Error.captureStackTrace(this)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** @param {Array<*>} args */
|
|
107
|
-
setArguments(args) {
|
|
108
|
-
this.args = args
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* @param {...any} args
|
|
113
|
-
* @return {*}
|
|
114
|
-
*/
|
|
115
|
-
run() {
|
|
116
|
-
this.args = Array.prototype.slice.call(arguments)
|
|
117
|
-
if (store.dryRun) {
|
|
118
|
-
this.setStatus('success')
|
|
119
|
-
return Promise.resolve(new Proxy({}, dryRunResolver()))
|
|
120
|
-
}
|
|
121
|
-
let result
|
|
122
|
-
try {
|
|
123
|
-
if (this.helperMethod !== 'say') {
|
|
124
|
-
result = this.helper[this.helperMethod].apply(this.helper, this.args)
|
|
125
|
-
}
|
|
126
|
-
this.setStatus('success')
|
|
127
|
-
} catch (err) {
|
|
128
|
-
this.setStatus('failed')
|
|
129
|
-
throw err
|
|
130
|
-
}
|
|
131
|
-
return result
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
setActor(actor) {
|
|
135
|
-
this.actor = actor || ''
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** @param {string} status */
|
|
139
|
-
setStatus(status) {
|
|
140
|
-
this.status = status
|
|
141
|
-
if (this.metaStep) {
|
|
142
|
-
this.metaStep.setStatus(status)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** @return {string} */
|
|
147
|
-
humanize() {
|
|
148
|
-
return humanizeString(this.name)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** @return {string} */
|
|
152
|
-
humanizeArgs() {
|
|
153
|
-
return this.args
|
|
154
|
-
.map(arg => {
|
|
155
|
-
if (!arg) {
|
|
156
|
-
return ''
|
|
157
|
-
}
|
|
158
|
-
if (typeof arg === 'string') {
|
|
159
|
-
return `"${arg}"`
|
|
160
|
-
}
|
|
161
|
-
if (Array.isArray(arg)) {
|
|
162
|
-
try {
|
|
163
|
-
const res = JSON.stringify(arg)
|
|
164
|
-
return res
|
|
165
|
-
} catch (err) {
|
|
166
|
-
return `[${arg.toString()}]`
|
|
167
|
-
}
|
|
168
|
-
} else if (typeof arg === 'function') {
|
|
169
|
-
return arg.toString()
|
|
170
|
-
} else if (typeof arg === 'undefined') {
|
|
171
|
-
return `${arg}`
|
|
172
|
-
} else if (arg instanceof Secret) {
|
|
173
|
-
return arg.getMasked()
|
|
174
|
-
} else if (arg.toString && arg.toString() !== '[object Object]') {
|
|
175
|
-
return arg.toString()
|
|
176
|
-
} else if (typeof arg === 'object') {
|
|
177
|
-
const returnedArg = {}
|
|
178
|
-
for (const [key, value] of Object.entries(arg)) {
|
|
179
|
-
returnedArg[key] = value
|
|
180
|
-
if (value instanceof Secret) returnedArg[key] = value.getMasked()
|
|
181
|
-
}
|
|
182
|
-
return JSON.stringify(returnedArg)
|
|
183
|
-
}
|
|
184
|
-
return arg
|
|
185
|
-
})
|
|
186
|
-
.join(', ')
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/** @return {string} */
|
|
190
|
-
line() {
|
|
191
|
-
const lines = this.stack.split('\n')
|
|
192
|
-
if (lines[STACK_LINE]) {
|
|
193
|
-
return lines[STACK_LINE].trim()
|
|
194
|
-
.replace(global.codecept_dir || '', '.')
|
|
195
|
-
.trim()
|
|
196
|
-
}
|
|
197
|
-
return ''
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/** @return {string} */
|
|
201
|
-
toString() {
|
|
202
|
-
return ucfirst(`${this.prefix}${this.actor} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`).trim()
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/** @return {string} */
|
|
206
|
-
toCliStyled() {
|
|
207
|
-
return `${this.prefix}${this.actor} ${color.italic(this.humanize())} ${color.yellow(this.humanizeArgs())}${this.suffix}`
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/** @return {string} */
|
|
211
|
-
toCode() {
|
|
212
|
-
return `${this.prefix}${this.actor}.${this.name}(${this.humanizeArgs()})${this.suffix}`
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
isMetaStep() {
|
|
216
|
-
return this.constructor.name === 'MetaStep'
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/** @return {boolean} */
|
|
220
|
-
hasBDDAncestor() {
|
|
221
|
-
let hasBDD = false
|
|
222
|
-
let processingStep
|
|
223
|
-
processingStep = this
|
|
224
|
-
|
|
225
|
-
while (processingStep.metaStep) {
|
|
226
|
-
if (processingStep.metaStep.actor.match(/^(Given|When|Then|And)/)) {
|
|
227
|
-
hasBDD = true
|
|
228
|
-
break
|
|
229
|
-
} else {
|
|
230
|
-
processingStep = processingStep.metaStep
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return hasBDD
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/** @extends Step */
|
|
238
|
-
class MetaStep extends Step {
|
|
239
|
-
constructor(obj, method) {
|
|
240
|
-
if (!method) method = ''
|
|
241
|
-
super(null, method)
|
|
242
|
-
this.actor = obj
|
|
243
|
-
}
|
|
6
|
+
const BaseStep = require('./step/base')
|
|
7
|
+
const StepConfig = require('./step/config')
|
|
8
|
+
const Step = require('./step/helper')
|
|
244
9
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
toCliStyled() {
|
|
254
|
-
return this.toString()
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
toString() {
|
|
258
|
-
const actorText = this.actor
|
|
259
|
-
|
|
260
|
-
if (this.isBDD()) {
|
|
261
|
-
return `${this.prefix}${actorText} ${this.name} "${this.humanizeArgs()}${this.suffix}"`
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (actorText === 'I') {
|
|
265
|
-
return `${this.prefix}${actorText} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return `On ${this.prefix}${actorText}: ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
humanize() {
|
|
272
|
-
return humanizeString(this.name)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
setTrace() {}
|
|
276
|
-
|
|
277
|
-
setContext(context) {
|
|
278
|
-
this.context = context
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/** @return {*} */
|
|
282
|
-
run(fn) {
|
|
283
|
-
this.status = 'queued'
|
|
284
|
-
this.setArguments(Array.from(arguments).slice(1))
|
|
285
|
-
let result
|
|
286
|
-
|
|
287
|
-
const registerStep = step => {
|
|
288
|
-
this.metaStep = null
|
|
289
|
-
step.metaStep = this
|
|
290
|
-
}
|
|
291
|
-
event.dispatcher.prependListener(event.step.before, registerStep)
|
|
292
|
-
// Handle async and sync methods.
|
|
293
|
-
if (fn.constructor.name === 'AsyncFunction') {
|
|
294
|
-
result = fn
|
|
295
|
-
.apply(this.context, this.args)
|
|
296
|
-
.then(result => {
|
|
297
|
-
return result
|
|
298
|
-
})
|
|
299
|
-
.catch(error => {
|
|
300
|
-
this.setStatus('failed')
|
|
301
|
-
throw error
|
|
302
|
-
})
|
|
303
|
-
.finally(() => {
|
|
304
|
-
this.endTime = Date.now()
|
|
305
|
-
event.dispatcher.removeListener(event.step.before, registerStep)
|
|
306
|
-
})
|
|
307
|
-
} else {
|
|
308
|
-
try {
|
|
309
|
-
this.startTime = Date.now()
|
|
310
|
-
result = fn.apply(this.context, this.args)
|
|
311
|
-
} catch (error) {
|
|
312
|
-
this.setStatus('failed')
|
|
313
|
-
throw error
|
|
314
|
-
} finally {
|
|
315
|
-
this.endTime = Date.now()
|
|
316
|
-
event.dispatcher.removeListener(event.step.before, registerStep)
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return result
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
Step.TIMEOUTS = {}
|
|
10
|
+
/**
|
|
11
|
+
* MetaStep is a step that is used to wrap other steps.
|
|
12
|
+
* It is used to create a new step that is a combination of other steps.
|
|
13
|
+
* It is used to create a new step that is a combination of other steps.
|
|
14
|
+
*/
|
|
15
|
+
const MetaStep = require('./step/meta')
|
|
325
16
|
|
|
326
|
-
/**
|
|
327
|
-
Step
|
|
17
|
+
/**
|
|
18
|
+
* Step used to execute a single function
|
|
19
|
+
*/
|
|
20
|
+
const FuncStep = require('./step/func')
|
|
328
21
|
|
|
329
22
|
module.exports = Step
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (prop === 'toString') return () => '<VALUE>'
|
|
335
|
-
return new Proxy({}, dryRunResolver())
|
|
336
|
-
},
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function humanizeString(string) {
|
|
341
|
-
// split strings by words, then make them all lowercase
|
|
342
|
-
const _result = string
|
|
343
|
-
.replace(/([a-z](?=[A-Z]))/g, '$1 ')
|
|
344
|
-
.split(' ')
|
|
345
|
-
.map(word => word.toLowerCase())
|
|
346
|
-
|
|
347
|
-
_result[0] = _result[0] === 'i' ? capitalizeFLetter(_result[0]) : _result[0]
|
|
348
|
-
return _result.join(' ').trim()
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function capitalizeFLetter(string) {
|
|
352
|
-
return string[0].toUpperCase() + string.slice(1)
|
|
353
|
-
}
|
|
23
|
+
module.exports.MetaStep = MetaStep
|
|
24
|
+
module.exports.BaseStep = BaseStep
|
|
25
|
+
module.exports.StepConfig = StepConfig
|
|
26
|
+
module.exports.FuncStep = FuncStep
|
package/lib/steps.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const StepConfig = require('./step/config')
|
|
2
|
+
const Section = require('./step/section')
|
|
3
|
+
function stepOpts(opts = {}) {
|
|
4
|
+
return new StepConfig(opts)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function stepTimeout(timeout) {
|
|
8
|
+
return new StepConfig().timeout(timeout)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function stepRetry(retry) {
|
|
12
|
+
return new StepConfig().retry(retry)
|
|
13
|
+
}
|
|
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
|
+
|
|
24
|
+
// Section function to be added here
|
|
25
|
+
|
|
26
|
+
const step = {
|
|
27
|
+
// steps.opts syntax
|
|
28
|
+
opts: stepOpts,
|
|
29
|
+
timeout: stepTimeout,
|
|
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'),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = step
|
package/lib/store.js
CHANGED
package/lib/timeout.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const TIMEOUT_ORDER = {
|
|
2
|
+
/**
|
|
3
|
+
* timeouts set with order below zero only override timeouts of higher order if their value is smaller
|
|
4
|
+
*/
|
|
5
|
+
testOrSuite: -5,
|
|
6
|
+
/**
|
|
7
|
+
* 0-9 - designated for override of timeouts set from code, 5 is used by stepTimeout plugin when stepTimeout.config.overrideStepLimits=true
|
|
8
|
+
*/
|
|
9
|
+
stepTimeoutHard: 5,
|
|
10
|
+
/**
|
|
11
|
+
* 10-19 - designated for timeouts set from code, 15 is order of I.setTimeout(t) operation
|
|
12
|
+
*/
|
|
13
|
+
codeLimitTime: 15,
|
|
14
|
+
/**
|
|
15
|
+
* 20-29 - designated for timeout settings which could be overriden in tests code, 25 is used by stepTimeout plugin when stepTimeout.config.overrideStepLimits=false
|
|
16
|
+
*/
|
|
17
|
+
stepTimeoutSoft: 25,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getCurrentTimeout(timeouts) {
|
|
21
|
+
let totalTimeout
|
|
22
|
+
// iterate over all timeouts starting from highest values of order
|
|
23
|
+
new Map([...timeouts.entries()].sort().reverse()).forEach((timeout, order) => {
|
|
24
|
+
if (
|
|
25
|
+
timeout !== undefined &&
|
|
26
|
+
// when orders >= 0 - timeout value overrides those set with higher order elements
|
|
27
|
+
(order >= 0 ||
|
|
28
|
+
// when `order < 0 && totalTimeout === undefined` - timeout is used when nothing is set by elements with higher order
|
|
29
|
+
totalTimeout === undefined ||
|
|
30
|
+
// when `order < 0` - timeout overrides higher values of timeout or 'no timeout' (totalTimeout === 0) set by elements with higher order
|
|
31
|
+
(timeout > 0 && (timeout < totalTimeout || totalTimeout === 0)))
|
|
32
|
+
) {
|
|
33
|
+
totalTimeout = timeout
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
return totalTimeout
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class TimeoutError extends Error {
|
|
40
|
+
constructor(message) {
|
|
41
|
+
super(message)
|
|
42
|
+
this.name = 'TimeoutError'
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
class TestTimeoutError extends TimeoutError {
|
|
47
|
+
constructor(timeout) {
|
|
48
|
+
super(`Timeout ${timeout}s exceeded (with Before hook)`)
|
|
49
|
+
this.name = 'TestTimeoutError'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class StepTimeoutError extends TimeoutError {
|
|
54
|
+
constructor(timeout, step) {
|
|
55
|
+
super(`Step ${step.toCode().trim()} timed out after ${timeout}s`)
|
|
56
|
+
this.name = 'StepTimeoutError'
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = {
|
|
61
|
+
TIMEOUT_ORDER,
|
|
62
|
+
getCurrentTimeout,
|
|
63
|
+
TimeoutError,
|
|
64
|
+
TestTimeoutError,
|
|
65
|
+
StepTimeoutError,
|
|
66
|
+
}
|
package/lib/utils.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const os = require('os')
|
|
3
3
|
const path = require('path')
|
|
4
|
+
const chalk = require('chalk')
|
|
4
5
|
const getFunctionArguments = require('fn-args')
|
|
5
6
|
const deepClone = require('lodash.clonedeep')
|
|
6
7
|
const { convertColorToRGBA, isColorProperty } = require('./colorUtils')
|
|
8
|
+
const Fuse = require('fuse.js')
|
|
7
9
|
|
|
8
10
|
function deepMerge(target, source) {
|
|
9
11
|
const merge = require('lodash.merge')
|
|
@@ -484,3 +486,94 @@ module.exports.humanizeFunction = function (fn) {
|
|
|
484
486
|
|
|
485
487
|
return simplified
|
|
486
488
|
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Searches through a given data source using the Fuse.js library for fuzzy searching.
|
|
492
|
+
*
|
|
493
|
+
* @function searchWithFusejs
|
|
494
|
+
* @param {Array|Object} source - The data source to search through. This can be an array of objects or strings.
|
|
495
|
+
* @param {string} searchString - The search query string to match against the source.
|
|
496
|
+
* @param {Object} [opts] - Optional configuration object for Fuse.js.
|
|
497
|
+
* @param {boolean} [opts.includeScore=true] - Whether to include the score of the match in the results.
|
|
498
|
+
* @param {number} [opts.threshold=0.6] - Determines the match threshold; lower values mean stricter matching.
|
|
499
|
+
* @param {boolean} [opts.caseSensitive=false] - Whether the search should be case-sensitive.
|
|
500
|
+
* @param {number} [opts.distance=100] - Determines how far apart the search term is allowed to be from the target.
|
|
501
|
+
* @param {number} [opts.maxPatternLength=32] - The maximum length of the search pattern. Patterns longer than this are ignored.
|
|
502
|
+
* @param {boolean} [opts.ignoreLocation=false] - Whether the location of the match is ignored when scoring.
|
|
503
|
+
* @param {boolean} [opts.ignoreFieldNorm=false] - When true, the field's length is not considered when scoring.
|
|
504
|
+
* @param {Array<string>} [opts.keys=[]] - List of keys to search in the objects of the source array.
|
|
505
|
+
* @param {boolean} [opts.shouldSort=true] - Whether the results should be sorted by score.
|
|
506
|
+
* @param {string} [opts.sortFn] - A custom sorting function for sorting results.
|
|
507
|
+
* @param {number} [opts.minMatchCharLength=1] - The minimum number of characters that must match.
|
|
508
|
+
* @param {boolean} [opts.useExtendedSearch=false] - Enables extended search capabilities.
|
|
509
|
+
*
|
|
510
|
+
* @returns {Array<Object>} - An array of search results. Each result contains an item and, if `includeScore` is true, a score.
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* const data = [
|
|
514
|
+
* { title: "Old Man's War", author: "John Scalzi" },
|
|
515
|
+
* { title: "The Lock Artist", author: "Steve Hamilton" },
|
|
516
|
+
* ];
|
|
517
|
+
*
|
|
518
|
+
* const options = {
|
|
519
|
+
* keys: ['title', 'author'],
|
|
520
|
+
* includeScore: true,
|
|
521
|
+
* threshold: 0.4,
|
|
522
|
+
* caseSensitive: false,
|
|
523
|
+
* distance: 50,
|
|
524
|
+
* ignoreLocation: true,
|
|
525
|
+
* };
|
|
526
|
+
*
|
|
527
|
+
* const results = searchWithFusejs(data, 'lock', options);
|
|
528
|
+
* console.log(results);
|
|
529
|
+
*/
|
|
530
|
+
module.exports.searchWithFusejs = function (source, searchString, opts) {
|
|
531
|
+
const fuse = new Fuse(source, opts)
|
|
532
|
+
|
|
533
|
+
return fuse.search(searchString)
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
module.exports.humanizeString = function (string) {
|
|
537
|
+
// split strings by words, then make them all lowercase
|
|
538
|
+
const _result = string
|
|
539
|
+
.replace(/([a-z](?=[A-Z]))/g, '$1 ')
|
|
540
|
+
.split(' ')
|
|
541
|
+
.map(word => word.toLowerCase())
|
|
542
|
+
|
|
543
|
+
_result[0] = _result[0] === 'i' ? this.ucfirst(_result[0]) : _result[0]
|
|
544
|
+
return _result.join(' ').trim()
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
module.exports.serializeError = function (error) {
|
|
548
|
+
if (error) {
|
|
549
|
+
const { stack, uncaught, message, actual, expected } = error
|
|
550
|
+
return { stack, uncaught, message, actual, expected }
|
|
551
|
+
}
|
|
552
|
+
return null
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
module.exports.base64EncodeFile = function (filePath) {
|
|
556
|
+
return Buffer.from(fs.readFileSync(filePath)).toString('base64')
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
module.exports.markdownToAnsi = function (markdown) {
|
|
560
|
+
return (
|
|
561
|
+
markdown
|
|
562
|
+
// Headers (# Text) - make blue and bold
|
|
563
|
+
.replace(/^(#{1,6})\s+(.+)$/gm, (_, hashes, text) => {
|
|
564
|
+
return chalk.bold.blue(`${hashes} ${text}`)
|
|
565
|
+
})
|
|
566
|
+
// Bullet points - replace with yellow bullet character
|
|
567
|
+
.replace(/^[-*]\s+(.+)$/gm, (_, text) => {
|
|
568
|
+
return `${chalk.yellow('•')} ${text}`
|
|
569
|
+
})
|
|
570
|
+
// Bold (**text**) - make bold
|
|
571
|
+
.replace(/\*\*(.+?)\*\*/g, (_, text) => {
|
|
572
|
+
return chalk.bold(text)
|
|
573
|
+
})
|
|
574
|
+
// Italic (*text*) - make italic (dim in terminals)
|
|
575
|
+
.replace(/\*(.+?)\*/g, (_, text) => {
|
|
576
|
+
return chalk.italic(text)
|
|
577
|
+
})
|
|
578
|
+
)
|
|
579
|
+
}
|
package/lib/within.js
CHANGED
|
@@ -3,7 +3,7 @@ const store = require('./store')
|
|
|
3
3
|
const recorder = require('./recorder')
|
|
4
4
|
const container = require('./container')
|
|
5
5
|
const event = require('./event')
|
|
6
|
-
const
|
|
6
|
+
const MetaStep = require('./step/meta')
|
|
7
7
|
const { isAsyncFunction } = require('./utils')
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -76,7 +76,7 @@ function within(context, fn) {
|
|
|
76
76
|
|
|
77
77
|
module.exports = within
|
|
78
78
|
|
|
79
|
-
class WithinStep extends
|
|
79
|
+
class WithinStep extends MetaStep {
|
|
80
80
|
constructor(locator, fn) {
|
|
81
81
|
super('Within')
|
|
82
82
|
this.args = [locator]
|