codeceptjs 3.6.10 → 3.7.0-beta.1
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 +81 -110
- package/bin/codecept.js +2 -2
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +46 -36
- 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 +87 -83
- package/lib/command/configMigrate.js +2 -4
- package/lib/command/definitions.js +5 -25
- package/lib/command/generate.js +10 -14
- package/lib/command/gherkin/snippets.js +10 -8
- package/lib/command/gherkin/steps.js +1 -1
- package/lib/command/info.js +1 -3
- package/lib/command/init.js +8 -12
- package/lib/command/interactive.js +1 -1
- package/lib/command/list.js +1 -1
- package/lib/command/run-multiple.js +12 -35
- package/lib/command/run-workers.js +10 -10
- package/lib/command/utils.js +5 -6
- package/lib/command/workers/runTests.js +14 -17
- package/lib/container.js +327 -237
- 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/els.js +177 -0
- package/lib/event.js +1 -0
- package/lib/heal.js +78 -80
- package/lib/helper/ApiDataFactory.js +3 -6
- package/lib/helper/Appium.js +15 -30
- package/lib/helper/FileSystem.js +3 -3
- package/lib/helper/GraphQLDataFactory.js +3 -3
- package/lib/helper/JSONResponse.js +57 -37
- package/lib/helper/Nightmare.js +35 -53
- package/lib/helper/Playwright.js +189 -251
- package/lib/helper/Protractor.js +54 -77
- package/lib/helper/Puppeteer.js +134 -232
- package/lib/helper/REST.js +5 -17
- package/lib/helper/TestCafe.js +21 -44
- package/lib/helper/WebDriver.js +103 -162
- package/lib/helper/testcafe/testcafe-utils.js +26 -27
- package/lib/listener/artifacts.js +2 -2
- package/lib/listener/emptyRun.js +58 -0
- package/lib/listener/exit.js +4 -4
- package/lib/listener/{retry.js → globalRetry.js} +5 -5
- package/lib/listener/{timeout.js → globalTimeout.js} +8 -8
- package/lib/listener/helpers.js +15 -15
- package/lib/listener/mocha.js +1 -1
- package/lib/listener/steps.js +17 -12
- package/lib/listener/store.js +12 -0
- package/lib/mocha/asyncWrapper.js +204 -0
- package/lib/{interfaces → mocha}/bdd.js +3 -3
- package/lib/mocha/cli.js +257 -0
- package/lib/mocha/factory.js +104 -0
- package/lib/{interfaces → mocha}/featureConfig.js +11 -12
- package/lib/{interfaces → mocha}/gherkin.js +26 -28
- package/lib/mocha/hooks.js +83 -0
- package/lib/mocha/index.js +12 -0
- package/lib/mocha/inject.js +24 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +10 -6
- package/lib/mocha/suite.js +55 -0
- package/lib/mocha/test.js +60 -0
- package/lib/mocha/types.d.ts +31 -0
- package/lib/mocha/ui.js +219 -0
- package/lib/output.js +28 -10
- package/lib/pause.js +159 -135
- package/lib/plugin/autoDelay.js +4 -4
- package/lib/plugin/autoLogin.js +6 -7
- package/lib/plugin/commentStep.js +1 -1
- package/lib/plugin/coverage.js +10 -19
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/debugErrors.js +2 -2
- package/lib/plugin/eachElement.js +1 -1
- package/lib/plugin/fakerTransform.js +1 -1
- package/lib/plugin/heal.js +6 -9
- package/lib/plugin/retryFailedStep.js +4 -4
- package/lib/plugin/retryTo.js +2 -2
- package/lib/plugin/screenshotOnFail.js +9 -36
- package/lib/plugin/selenoid.js +15 -35
- package/lib/plugin/stepByStepReport.js +51 -13
- package/lib/plugin/stepTimeout.js +4 -11
- package/lib/plugin/subtitles.js +4 -4
- package/lib/plugin/tryTo.js +1 -1
- package/lib/plugin/wdio.js +8 -10
- package/lib/recorder.js +142 -121
- package/lib/secret.js +1 -1
- package/lib/step.js +160 -144
- package/lib/store.js +6 -2
- package/lib/template/heal.js +2 -11
- package/lib/utils.js +224 -216
- package/lib/within.js +73 -55
- package/lib/workers.js +265 -261
- package/package.json +46 -47
- package/typings/index.d.ts +172 -184
- package/typings/promiseBasedTypes.d.ts +53 -516
- package/typings/types.d.ts +127 -587
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/mochaFactory.js +0 -113
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
package/lib/pause.js
CHANGED
|
@@ -1,59 +1,70 @@
|
|
|
1
|
-
const colors = require('chalk')
|
|
2
|
-
const readline = require('readline')
|
|
3
|
-
const ora = require('ora-classic')
|
|
4
|
-
const debug = require('debug')('codeceptjs:pause')
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
1
|
+
const colors = require('chalk')
|
|
2
|
+
const readline = require('readline')
|
|
3
|
+
const ora = require('ora-classic')
|
|
4
|
+
const debug = require('debug')('codeceptjs:pause')
|
|
5
|
+
const Fuse = require('fuse.js')
|
|
6
|
+
|
|
7
|
+
const container = require('./container')
|
|
8
|
+
const history = require('./history')
|
|
9
|
+
const store = require('./store')
|
|
10
|
+
const aiAssistant = require('./ai')
|
|
11
|
+
const recorder = require('./recorder')
|
|
12
|
+
const event = require('./event')
|
|
13
|
+
const output = require('./output')
|
|
14
|
+
const { methodsOfObject } = require('./utils')
|
|
14
15
|
|
|
15
16
|
// npm install colors
|
|
16
|
-
let rl
|
|
17
|
-
let nextStep
|
|
18
|
-
let finish
|
|
19
|
-
let next
|
|
20
|
-
let registeredVariables = {}
|
|
17
|
+
let rl
|
|
18
|
+
let nextStep
|
|
19
|
+
let finish
|
|
20
|
+
let next
|
|
21
|
+
let registeredVariables = {}
|
|
21
22
|
/**
|
|
22
23
|
* Pauses test execution and starts interactive shell
|
|
23
24
|
* @param {Object<string, *>} [passedObject]
|
|
24
25
|
*/
|
|
25
26
|
const pause = function (passedObject = {}) {
|
|
26
|
-
if (store.dryRun) return
|
|
27
|
+
if (store.dryRun) return
|
|
27
28
|
|
|
28
|
-
next = false
|
|
29
|
+
next = false
|
|
29
30
|
// add listener to all next steps to provide next() functionality
|
|
30
31
|
event.dispatcher.on(event.step.after, () => {
|
|
31
32
|
recorder.add('Start next pause session', () => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
33
|
+
// test already finished, nothing to pause
|
|
34
|
+
if (!store.currentTest) return
|
|
35
|
+
if (!next) return
|
|
36
|
+
return pauseSession()
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
event.dispatcher.on(event.test.finished, () => {
|
|
41
|
+
finish()
|
|
42
|
+
recorder.session.restore('pause')
|
|
43
|
+
rl.close()
|
|
44
|
+
history.save()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
recorder.add('Start new session', () => pauseSession(passedObject))
|
|
48
|
+
}
|
|
38
49
|
|
|
39
50
|
function pauseSession(passedObject = {}) {
|
|
40
|
-
registeredVariables = passedObject
|
|
41
|
-
recorder.session.start('pause')
|
|
51
|
+
registeredVariables = passedObject
|
|
52
|
+
recorder.session.start('pause')
|
|
42
53
|
if (!next) {
|
|
43
|
-
let vars = Object.keys(registeredVariables).join(', ')
|
|
44
|
-
if (vars) vars = `(vars: ${vars})
|
|
54
|
+
let vars = Object.keys(registeredVariables).join(', ')
|
|
55
|
+
if (vars) vars = `(vars: ${vars})`
|
|
45
56
|
|
|
46
|
-
output.print(colors.yellow(' Interactive shell started'))
|
|
47
|
-
output.print(colors.yellow(' Use JavaScript syntax to try steps in action'))
|
|
48
|
-
output.print(colors.yellow(` - Press ${colors.bold('ENTER')} to run the next step`))
|
|
49
|
-
output.print(colors.yellow(` - Press ${colors.bold('TAB')} twice to see all available commands`))
|
|
50
|
-
output.print(colors.yellow(` - Type ${colors.bold('exit')} + Enter to exit the interactive shell`))
|
|
51
|
-
output.print(colors.yellow(` - Prefix ${colors.bold('=>')} to run js commands ${colors.bold(vars)}`))
|
|
57
|
+
output.print(colors.yellow(' Interactive shell started'))
|
|
58
|
+
output.print(colors.yellow(' Use JavaScript syntax to try steps in action'))
|
|
59
|
+
output.print(colors.yellow(` - Press ${colors.bold('ENTER')} to run the next step`))
|
|
60
|
+
output.print(colors.yellow(` - Press ${colors.bold('TAB')} twice to see all available commands`))
|
|
61
|
+
output.print(colors.yellow(` - Type ${colors.bold('exit')} + Enter to exit the interactive shell`))
|
|
62
|
+
output.print(colors.yellow(` - Prefix ${colors.bold('=>')} to run js commands ${colors.bold(vars)}`))
|
|
52
63
|
|
|
53
64
|
if (aiAssistant.isEnabled) {
|
|
54
|
-
output.print(colors.blue(` ${colors.bold('AI is enabled! (experimental)')} Write what you want and make AI run it`))
|
|
55
|
-
output.print(colors.blue(' Please note, only HTML fragments with interactive elements are sent to AI provider'))
|
|
56
|
-
output.print(colors.blue(' Ideas: ask it to fill forms for you or to click'))
|
|
65
|
+
output.print(colors.blue(` ${colors.bold('AI is enabled! (experimental)')} Write what you want and make AI run it`))
|
|
66
|
+
output.print(colors.blue(' Please note, only HTML fragments with interactive elements are sent to AI provider'))
|
|
67
|
+
output.print(colors.blue(' Ideas: ask it to fill forms for you or to click'))
|
|
57
68
|
}
|
|
58
69
|
}
|
|
59
70
|
|
|
@@ -64,154 +75,167 @@ function pauseSession(passedObject = {}) {
|
|
|
64
75
|
completer,
|
|
65
76
|
history: history.load(),
|
|
66
77
|
historySize: 50, // Adjust the history size as needed
|
|
67
|
-
})
|
|
78
|
+
})
|
|
68
79
|
|
|
69
|
-
|
|
80
|
+
store.onPause = true
|
|
81
|
+
rl.on('line', parseInput)
|
|
70
82
|
rl.on('close', () => {
|
|
71
|
-
if (!next) console.log('Exiting interactive shell....')
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
if (!next) console.log('Exiting interactive shell....')
|
|
84
|
+
store.onPause = false
|
|
85
|
+
})
|
|
86
|
+
return new Promise(resolve => {
|
|
87
|
+
finish = resolve
|
|
75
88
|
// eslint-disable-next-line
|
|
76
|
-
return askForStep()
|
|
77
|
-
})
|
|
89
|
+
return askForStep()
|
|
90
|
+
})
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
/* eslint-disable */
|
|
81
94
|
async function parseInput(cmd) {
|
|
82
|
-
rl.pause()
|
|
83
|
-
next = false
|
|
84
|
-
recorder.session.start('pause')
|
|
85
|
-
if (cmd === '') next = true
|
|
95
|
+
rl.pause()
|
|
96
|
+
next = false
|
|
97
|
+
recorder.session.start('pause')
|
|
98
|
+
if (cmd === '') next = true
|
|
86
99
|
if (!cmd || cmd === 'resume' || cmd === 'exit') {
|
|
87
|
-
finish()
|
|
88
|
-
recorder.session.restore()
|
|
89
|
-
rl.close()
|
|
90
|
-
history.save()
|
|
91
|
-
return nextStep()
|
|
100
|
+
finish()
|
|
101
|
+
recorder.session.restore('pause')
|
|
102
|
+
rl.close()
|
|
103
|
+
history.save()
|
|
104
|
+
return nextStep()
|
|
92
105
|
}
|
|
93
106
|
for (const k of Object.keys(registeredVariables)) {
|
|
94
|
-
eval(`var ${k} = registeredVariables['${k}'];`)
|
|
107
|
+
eval(`var ${k} = registeredVariables['${k}'];`)
|
|
95
108
|
}
|
|
96
109
|
|
|
97
|
-
let executeCommand = Promise.resolve()
|
|
110
|
+
let executeCommand = Promise.resolve()
|
|
98
111
|
|
|
99
112
|
const getCmd = () => {
|
|
100
113
|
debug('Command:', cmd)
|
|
101
|
-
return cmd
|
|
102
|
-
}
|
|
114
|
+
return cmd
|
|
115
|
+
}
|
|
103
116
|
|
|
104
|
-
let isCustomCommand = false
|
|
105
|
-
let lastError = null
|
|
106
|
-
let isAiCommand = false
|
|
107
|
-
let $res
|
|
117
|
+
let isCustomCommand = false
|
|
118
|
+
let lastError = null
|
|
119
|
+
let isAiCommand = false
|
|
120
|
+
let $res
|
|
108
121
|
try {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const I = container.support('I');
|
|
122
|
+
const locate = global.locate // enable locate in this context
|
|
123
|
+
|
|
124
|
+
const I = container.support('I')
|
|
113
125
|
if (cmd.trim().startsWith('=>')) {
|
|
114
|
-
isCustomCommand = true
|
|
115
|
-
cmd = cmd.trim().substring(2, cmd.length)
|
|
126
|
+
isCustomCommand = true
|
|
127
|
+
cmd = cmd.trim().substring(2, cmd.length)
|
|
116
128
|
} else if (aiAssistant.isEnabled && cmd.trim() && !cmd.match(/^\w+\(/) && cmd.includes(' ')) {
|
|
117
|
-
const currentOutputLevel = output.level()
|
|
118
|
-
output.level(0)
|
|
119
|
-
const res = I.grabSource()
|
|
120
|
-
isAiCommand = true
|
|
129
|
+
const currentOutputLevel = output.level()
|
|
130
|
+
output.level(0)
|
|
131
|
+
const res = I.grabSource()
|
|
132
|
+
isAiCommand = true
|
|
121
133
|
executeCommand = executeCommand.then(async () => {
|
|
122
134
|
try {
|
|
123
|
-
const html = await res
|
|
124
|
-
await aiAssistant.setHtmlContext(html)
|
|
135
|
+
const html = await res
|
|
136
|
+
await aiAssistant.setHtmlContext(html)
|
|
125
137
|
} catch (err) {
|
|
126
|
-
output.print(output.styles.error(' ERROR '),
|
|
127
|
-
return
|
|
138
|
+
output.print(output.styles.error(' ERROR '), "Can't get HTML context", err.stack)
|
|
139
|
+
return
|
|
128
140
|
} finally {
|
|
129
|
-
output.level(currentOutputLevel)
|
|
141
|
+
output.level(currentOutputLevel)
|
|
130
142
|
}
|
|
131
143
|
|
|
132
|
-
const spinner = ora(
|
|
133
|
-
cmd = await aiAssistant.writeSteps(cmd)
|
|
134
|
-
spinner.stop()
|
|
135
|
-
output.print('')
|
|
136
|
-
output.print(colors.blue(aiAssistant.getResponse()))
|
|
137
|
-
output.print('')
|
|
138
|
-
return cmd
|
|
144
|
+
const spinner = ora('Processing AI request...').start()
|
|
145
|
+
cmd = await aiAssistant.writeSteps(cmd)
|
|
146
|
+
spinner.stop()
|
|
147
|
+
output.print('')
|
|
148
|
+
output.print(colors.blue(aiAssistant.getResponse()))
|
|
149
|
+
output.print('')
|
|
150
|
+
return cmd
|
|
139
151
|
})
|
|
140
152
|
} else {
|
|
141
|
-
cmd = `I.${cmd}
|
|
153
|
+
cmd = `I.${cmd}`
|
|
142
154
|
}
|
|
143
|
-
executeCommand = executeCommand
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
})
|
|
155
|
+
executeCommand = executeCommand
|
|
156
|
+
.then(async () => {
|
|
157
|
+
const cmd = getCmd()
|
|
158
|
+
if (!cmd) return
|
|
159
|
+
return eval(cmd)
|
|
160
|
+
})
|
|
161
|
+
.catch(err => {
|
|
162
|
+
debug(err)
|
|
163
|
+
if (isAiCommand) return
|
|
164
|
+
if (!lastError) output.print(output.styles.error(' ERROR '), err.message)
|
|
165
|
+
debug(err.stack)
|
|
155
166
|
|
|
156
|
-
|
|
167
|
+
lastError = err.message
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
const val = await executeCommand
|
|
157
171
|
|
|
158
172
|
if (isCustomCommand) {
|
|
159
|
-
if (val !== undefined) console.log('Result', '$res=', val)
|
|
160
|
-
$res = val
|
|
173
|
+
if (val !== undefined) console.log('Result', '$res=', val)
|
|
174
|
+
$res = val
|
|
161
175
|
}
|
|
162
176
|
|
|
163
177
|
if (cmd?.startsWith('I.see') || cmd?.startsWith('I.dontSee')) {
|
|
164
|
-
output.print(output.styles.success(' OK '), cmd)
|
|
178
|
+
output.print(output.styles.success(' OK '), cmd)
|
|
165
179
|
}
|
|
166
180
|
if (cmd?.startsWith('I.grab')) {
|
|
167
|
-
output.print(output.styles.debug(val))
|
|
181
|
+
output.print(output.styles.debug(val))
|
|
168
182
|
}
|
|
169
183
|
|
|
170
|
-
history.push(cmd)
|
|
184
|
+
history.push(cmd) // add command to history when successful
|
|
171
185
|
} catch (err) {
|
|
172
|
-
if (!lastError) output.print(output.styles.error(' ERROR '), err.message)
|
|
173
|
-
lastError = err.message
|
|
186
|
+
if (!lastError) output.print(output.styles.error(' ERROR '), err.message)
|
|
187
|
+
lastError = err.message
|
|
174
188
|
}
|
|
175
|
-
recorder.session.catch(
|
|
176
|
-
const msg = err.cliMessage ? err.cliMessage() : err.message
|
|
189
|
+
recorder.session.catch(err => {
|
|
190
|
+
const msg = err.cliMessage ? err.cliMessage() : err.message
|
|
177
191
|
|
|
178
192
|
// pop latest command from history because it failed
|
|
179
|
-
history.pop()
|
|
180
|
-
|
|
181
|
-
if (isAiCommand) return
|
|
182
|
-
if (!lastError) output.print(output.styles.error(' FAIL '), msg)
|
|
183
|
-
lastError = err.message
|
|
184
|
-
})
|
|
185
|
-
recorder.add('ask for next step', askForStep)
|
|
186
|
-
nextStep()
|
|
193
|
+
history.pop()
|
|
194
|
+
|
|
195
|
+
if (isAiCommand) return
|
|
196
|
+
if (!lastError) output.print(output.styles.error(' FAIL '), msg)
|
|
197
|
+
lastError = err.message
|
|
198
|
+
})
|
|
199
|
+
recorder.add('ask for next step', askForStep)
|
|
200
|
+
nextStep()
|
|
187
201
|
}
|
|
188
202
|
/* eslint-enable */
|
|
189
203
|
|
|
190
204
|
function askForStep() {
|
|
191
|
-
return new Promise(
|
|
192
|
-
nextStep = resolve
|
|
193
|
-
rl.setPrompt(' I.', 3)
|
|
194
|
-
rl.resume()
|
|
195
|
-
rl.prompt([false])
|
|
196
|
-
})
|
|
205
|
+
return new Promise(resolve => {
|
|
206
|
+
nextStep = resolve
|
|
207
|
+
rl.setPrompt(' I.', 3)
|
|
208
|
+
rl.resume()
|
|
209
|
+
rl.prompt([false])
|
|
210
|
+
})
|
|
197
211
|
}
|
|
198
212
|
|
|
199
213
|
function completer(line) {
|
|
200
|
-
const I = container.support('I')
|
|
201
|
-
const completions = methodsOfObject(I)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
214
|
+
const I = container.support('I')
|
|
215
|
+
const completions = methodsOfObject(I)
|
|
216
|
+
// If no input, return all completions
|
|
217
|
+
if (!line) {
|
|
218
|
+
return [completions, line]
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Initialize Fuse with completions
|
|
222
|
+
const fuse = new Fuse(completions, {
|
|
223
|
+
threshold: 0.3,
|
|
224
|
+
distance: 100,
|
|
225
|
+
minMatchCharLength: 1,
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
// Search using Fuse.js
|
|
229
|
+
const searchResults = fuse.search(line)
|
|
230
|
+
const hits = searchResults.map(result => result.item)
|
|
231
|
+
|
|
232
|
+
return [hits, line]
|
|
209
233
|
}
|
|
210
234
|
|
|
211
235
|
function registerVariable(name, value) {
|
|
212
|
-
registeredVariables[name] = value
|
|
236
|
+
registeredVariables[name] = value
|
|
213
237
|
}
|
|
214
238
|
|
|
215
|
-
module.exports = pause
|
|
239
|
+
module.exports = pause
|
|
216
240
|
|
|
217
|
-
module.exports.registerVariable = registerVariable
|
|
241
|
+
module.exports.registerVariable = registerVariable
|
package/lib/plugin/autoDelay.js
CHANGED
|
@@ -66,25 +66,25 @@ module.exports = function (config) {
|
|
|
66
66
|
|
|
67
67
|
if (!helper) return // no helpers for auto-delay
|
|
68
68
|
|
|
69
|
-
event.dispatcher.on(event.step.before,
|
|
69
|
+
event.dispatcher.on(event.step.before, step => {
|
|
70
70
|
if (config.methods.indexOf(step.helperMethod) < 0) return // skip non-actions
|
|
71
71
|
|
|
72
72
|
recorder.add('auto-delay', async () => {
|
|
73
73
|
if (store.debugMode) return // no need to delay in debug
|
|
74
74
|
log(`Delaying for ${config.delayBefore}ms`)
|
|
75
|
-
return new Promise(
|
|
75
|
+
return new Promise(resolve => {
|
|
76
76
|
setTimeout(resolve, config.delayBefore)
|
|
77
77
|
})
|
|
78
78
|
})
|
|
79
79
|
})
|
|
80
80
|
|
|
81
|
-
event.dispatcher.on(event.step.after,
|
|
81
|
+
event.dispatcher.on(event.step.after, step => {
|
|
82
82
|
if (config.methods.indexOf(step.helperMethod) < 0) return // skip non-actions
|
|
83
83
|
|
|
84
84
|
recorder.add('auto-delay', async () => {
|
|
85
85
|
if (store.debugMode) return // no need to delay in debug
|
|
86
86
|
log(`Delaying for ${config.delayAfter}ms`)
|
|
87
|
-
return new Promise(
|
|
87
|
+
return new Promise(resolve => {
|
|
88
88
|
setTimeout(resolve, config.delayAfter)
|
|
89
89
|
})
|
|
90
90
|
})
|
package/lib/plugin/autoLogin.js
CHANGED
|
@@ -8,7 +8,7 @@ const { debug } = require('../output')
|
|
|
8
8
|
const isAsyncFunction = require('../utils').isAsyncFunction
|
|
9
9
|
|
|
10
10
|
const defaultUser = {
|
|
11
|
-
fetch:
|
|
11
|
+
fetch: I => I.grabCookie(),
|
|
12
12
|
check: () => {},
|
|
13
13
|
restore: (I, cookies) => {
|
|
14
14
|
I.amOnPage('/') // open a page
|
|
@@ -253,7 +253,7 @@ const defaultConfig = {
|
|
|
253
253
|
module.exports = function (config) {
|
|
254
254
|
config = Object.assign(defaultConfig, config)
|
|
255
255
|
Object.keys(config.users).map(
|
|
256
|
-
|
|
256
|
+
u =>
|
|
257
257
|
(config.users[u] = {
|
|
258
258
|
...defaultUser,
|
|
259
259
|
...config.users[u],
|
|
@@ -275,12 +275,11 @@ module.exports = function (config) {
|
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
const loginFunction = async
|
|
278
|
+
const loginFunction = async name => {
|
|
279
279
|
const userSession = config.users[name]
|
|
280
280
|
const I = container.support('I')
|
|
281
281
|
const cookies = store[`${name}_session`]
|
|
282
|
-
const shouldAwait =
|
|
283
|
-
isAsyncFunction(userSession.login) || isAsyncFunction(userSession.restore) || isAsyncFunction(userSession.check)
|
|
282
|
+
const shouldAwait = isAsyncFunction(userSession.login) || isAsyncFunction(userSession.restore) || isAsyncFunction(userSession.check)
|
|
284
283
|
|
|
285
284
|
const loginAndSave = async () => {
|
|
286
285
|
if (shouldAwait) {
|
|
@@ -311,7 +310,7 @@ module.exports = function (config) {
|
|
|
311
310
|
userSession.restore(I, cookies)
|
|
312
311
|
userSession.check(I, cookies)
|
|
313
312
|
}
|
|
314
|
-
recorder.session.catch(
|
|
313
|
+
recorder.session.catch(err => {
|
|
315
314
|
debug(`Failed auto login for ${name} due to ${err}`)
|
|
316
315
|
debug('Logging in again')
|
|
317
316
|
recorder.session.start('auto login')
|
|
@@ -320,7 +319,7 @@ module.exports = function (config) {
|
|
|
320
319
|
recorder.add(() => recorder.session.restore('auto login'))
|
|
321
320
|
recorder.catch(() => debug('continue'))
|
|
322
321
|
})
|
|
323
|
-
.catch(
|
|
322
|
+
.catch(err => {
|
|
324
323
|
recorder.session.restore('auto login')
|
|
325
324
|
recorder.session.restore('check login')
|
|
326
325
|
recorder.throw(err)
|
|
@@ -104,7 +104,7 @@ module.exports = function (config) {
|
|
|
104
104
|
currentCommentStep = null
|
|
105
105
|
})
|
|
106
106
|
|
|
107
|
-
event.dispatcher.on(event.step.started,
|
|
107
|
+
event.dispatcher.on(event.step.started, step => {
|
|
108
108
|
if (currentCommentStep) {
|
|
109
109
|
const metaStep = getRootMetaStep(step)
|
|
110
110
|
|
package/lib/plugin/coverage.js
CHANGED
|
@@ -15,7 +15,7 @@ const supportedHelpers = ['Puppeteer', 'Playwright', 'WebDriver']
|
|
|
15
15
|
|
|
16
16
|
const v8CoverageHelpers = {
|
|
17
17
|
Playwright: {
|
|
18
|
-
startCoverage: async
|
|
18
|
+
startCoverage: async page => {
|
|
19
19
|
await Promise.all([
|
|
20
20
|
page.coverage.startJSCoverage({
|
|
21
21
|
resetOnNavigation: false,
|
|
@@ -26,16 +26,13 @@ const v8CoverageHelpers = {
|
|
|
26
26
|
])
|
|
27
27
|
},
|
|
28
28
|
takeCoverage: async (page, coverageReport) => {
|
|
29
|
-
const [jsCoverage, cssCoverage] = await Promise.all([
|
|
30
|
-
page.coverage.stopJSCoverage(),
|
|
31
|
-
page.coverage.stopCSSCoverage(),
|
|
32
|
-
])
|
|
29
|
+
const [jsCoverage, cssCoverage] = await Promise.all([page.coverage.stopJSCoverage(), page.coverage.stopCSSCoverage()])
|
|
33
30
|
const coverageList = [...jsCoverage, ...cssCoverage]
|
|
34
31
|
await coverageReport.add(coverageList)
|
|
35
32
|
},
|
|
36
33
|
},
|
|
37
34
|
Puppeteer: {
|
|
38
|
-
startCoverage: async
|
|
35
|
+
startCoverage: async page => {
|
|
39
36
|
await Promise.all([
|
|
40
37
|
page.coverage.startJSCoverage({
|
|
41
38
|
resetOnNavigation: false,
|
|
@@ -47,13 +44,10 @@ const v8CoverageHelpers = {
|
|
|
47
44
|
])
|
|
48
45
|
},
|
|
49
46
|
takeCoverage: async (page, coverageReport) => {
|
|
50
|
-
const [jsCoverage, cssCoverage] = await Promise.all([
|
|
51
|
-
page.coverage.stopJSCoverage(),
|
|
52
|
-
page.coverage.stopCSSCoverage(),
|
|
53
|
-
])
|
|
47
|
+
const [jsCoverage, cssCoverage] = await Promise.all([page.coverage.stopJSCoverage(), page.coverage.stopCSSCoverage()])
|
|
54
48
|
// to raw V8 script coverage
|
|
55
49
|
const coverageList = [
|
|
56
|
-
...jsCoverage.map(
|
|
50
|
+
...jsCoverage.map(it => {
|
|
57
51
|
return {
|
|
58
52
|
source: it.text,
|
|
59
53
|
...it.rawScriptCoverage,
|
|
@@ -65,7 +59,7 @@ const v8CoverageHelpers = {
|
|
|
65
59
|
},
|
|
66
60
|
},
|
|
67
61
|
WebDriver: {
|
|
68
|
-
startCoverage: async
|
|
62
|
+
startCoverage: async page => {
|
|
69
63
|
await Promise.all([
|
|
70
64
|
page.coverage.startJSCoverage({
|
|
71
65
|
resetOnNavigation: false,
|
|
@@ -77,13 +71,10 @@ const v8CoverageHelpers = {
|
|
|
77
71
|
])
|
|
78
72
|
},
|
|
79
73
|
takeCoverage: async (page, coverageReport) => {
|
|
80
|
-
const [jsCoverage, cssCoverage] = await Promise.all([
|
|
81
|
-
page.coverage.stopJSCoverage(),
|
|
82
|
-
page.coverage.stopCSSCoverage(),
|
|
83
|
-
])
|
|
74
|
+
const [jsCoverage, cssCoverage] = await Promise.all([page.coverage.stopJSCoverage(), page.coverage.stopCSSCoverage()])
|
|
84
75
|
// to raw V8 script coverage
|
|
85
76
|
const coverageList = [
|
|
86
|
-
...jsCoverage.map(
|
|
77
|
+
...jsCoverage.map(it => {
|
|
87
78
|
return {
|
|
88
79
|
source: it.text,
|
|
89
80
|
...it.rawScriptCoverage,
|
|
@@ -131,7 +122,7 @@ module.exports = function (config) {
|
|
|
131
122
|
let coverageRunning = false
|
|
132
123
|
|
|
133
124
|
const v8Names = Object.keys(v8CoverageHelpers)
|
|
134
|
-
const helperName = Object.keys(helpers).find(
|
|
125
|
+
const helperName = Object.keys(helpers).find(it => v8Names.includes(it))
|
|
135
126
|
if (!helperName) {
|
|
136
127
|
console.error(`Coverage is only supported in ${supportedHelpers.join(' or ')}`)
|
|
137
128
|
// no helpers for screenshot
|
|
@@ -180,7 +171,7 @@ module.exports = function (config) {
|
|
|
180
171
|
})
|
|
181
172
|
|
|
182
173
|
// Save coverage data after every test run
|
|
183
|
-
event.dispatcher.on(event.test.after,
|
|
174
|
+
event.dispatcher.on(event.test.after, test => {
|
|
184
175
|
recorder.add(
|
|
185
176
|
'take coverage',
|
|
186
177
|
async () => {
|
|
@@ -111,7 +111,7 @@ const defaultConfig = {
|
|
|
111
111
|
* I.click('=sign-up'); // matches => [data-qa=sign-up],[data-test=sign-up]
|
|
112
112
|
* ```
|
|
113
113
|
*/
|
|
114
|
-
module.exports =
|
|
114
|
+
module.exports = config => {
|
|
115
115
|
config = { ...defaultConfig, ...config }
|
|
116
116
|
|
|
117
117
|
Locator.addFilter((value, locatorObj) => {
|
|
@@ -125,7 +125,7 @@ module.exports = (config) => {
|
|
|
125
125
|
if (config.strategy.toLowerCase() === 'xpath') {
|
|
126
126
|
locatorObj.value = `.//*[${[]
|
|
127
127
|
.concat(config.attribute)
|
|
128
|
-
.map(
|
|
128
|
+
.map(attr => `@${attr}=${xpathLocator.literal(val)}`)
|
|
129
129
|
.join(' or ')}]`
|
|
130
130
|
locatorObj.type = 'xpath'
|
|
131
131
|
}
|
|
@@ -133,7 +133,7 @@ module.exports = (config) => {
|
|
|
133
133
|
if (config.strategy.toLowerCase() === 'css') {
|
|
134
134
|
locatorObj.value = []
|
|
135
135
|
.concat(config.attribute)
|
|
136
|
-
.map(
|
|
136
|
+
.map(attr => `[${attr}=${val}]`)
|
|
137
137
|
.join(',')
|
|
138
138
|
locatorObj.type = 'css'
|
|
139
139
|
}
|
|
@@ -43,7 +43,7 @@ module.exports = function (config = {}) {
|
|
|
43
43
|
|
|
44
44
|
if (!helper) return // no helpers for screenshot
|
|
45
45
|
|
|
46
|
-
event.dispatcher.on(event.test.failed,
|
|
46
|
+
event.dispatcher.on(event.test.failed, test => {
|
|
47
47
|
recorder.add('HTML snapshot failed test', async () => {
|
|
48
48
|
try {
|
|
49
49
|
const currentOutputLevel = output.level()
|
|
@@ -56,7 +56,7 @@ module.exports = function (config = {}) {
|
|
|
56
56
|
const errors = scanForErrorMessages(html, config.errorClasses)
|
|
57
57
|
if (errors.length) {
|
|
58
58
|
output.debug('Detected errors in HTML code')
|
|
59
|
-
errors.forEach(
|
|
59
|
+
errors.forEach(error => output.debug(error))
|
|
60
60
|
test.artifacts.errors = errors
|
|
61
61
|
}
|
|
62
62
|
} catch (err) {
|
|
@@ -72,7 +72,7 @@ function eachElement(purpose, locator, fn) {
|
|
|
72
72
|
if (store.dryRun) return
|
|
73
73
|
const helpers = Object.values(container.helpers())
|
|
74
74
|
|
|
75
|
-
const helper = helpers.filter(
|
|
75
|
+
const helper = helpers.filter(h => !!h._locate)[0]
|
|
76
76
|
|
|
77
77
|
if (!helper) {
|
|
78
78
|
throw new Error('No helper enabled with _locate method with returns a list of elements.')
|
|
@@ -40,7 +40,7 @@ const transform = require('../transform')
|
|
|
40
40
|
*
|
|
41
41
|
*/
|
|
42
42
|
module.exports = function (config) {
|
|
43
|
-
transform.addTransformerBeforeAll('gherkin.examples',
|
|
43
|
+
transform.addTransformerBeforeAll('gherkin.examples', value => {
|
|
44
44
|
if (typeof value === 'string' && value.length > 0) {
|
|
45
45
|
return faker.helpers.fake(value)
|
|
46
46
|
}
|
package/lib/plugin/heal.js
CHANGED
|
@@ -31,10 +31,7 @@ const defaultConfig = {
|
|
|
31
31
|
module.exports = function (config = {}) {
|
|
32
32
|
if (store.debugMode && !process.env.DEBUG) {
|
|
33
33
|
event.dispatcher.on(event.test.failed, () => {
|
|
34
|
-
output.plugin(
|
|
35
|
-
'heal',
|
|
36
|
-
'Healing is disabled in --debug mode, use DEBUG="codeceptjs:heal" to enable it in debug mode',
|
|
37
|
-
)
|
|
34
|
+
output.plugin('heal', 'Healing is disabled in --debug mode, use DEBUG="codeceptjs:heal" to enable it in debug mode')
|
|
38
35
|
})
|
|
39
36
|
return
|
|
40
37
|
}
|
|
@@ -48,21 +45,21 @@ module.exports = function (config = {}) {
|
|
|
48
45
|
|
|
49
46
|
config = Object.assign(defaultConfig, config)
|
|
50
47
|
|
|
51
|
-
event.dispatcher.on(event.test.before,
|
|
48
|
+
event.dispatcher.on(event.test.before, test => {
|
|
52
49
|
currentTest = test
|
|
53
50
|
healedSteps = 0
|
|
54
51
|
caughtError = null
|
|
55
52
|
})
|
|
56
53
|
|
|
57
|
-
event.dispatcher.on(event.step.started,
|
|
54
|
+
event.dispatcher.on(event.step.started, step => (currentStep = step))
|
|
58
55
|
|
|
59
|
-
event.dispatcher.on(event.step.after,
|
|
56
|
+
event.dispatcher.on(event.step.after, step => {
|
|
60
57
|
if (isHealing) return
|
|
61
58
|
if (healTries >= config.healLimit) return // out of limit
|
|
62
59
|
|
|
63
60
|
if (!heal.hasCorrespondingRecipes(step)) return
|
|
64
61
|
|
|
65
|
-
recorder.catchWithoutStop(async
|
|
62
|
+
recorder.catchWithoutStop(async err => {
|
|
66
63
|
isHealing = true
|
|
67
64
|
if (caughtError === err) throw err // avoid double handling
|
|
68
65
|
caughtError = err
|
|
@@ -99,7 +96,7 @@ module.exports = function (config = {}) {
|
|
|
99
96
|
|
|
100
97
|
print(`${colors.bold(heal.fixes.length)} ${heal.fixes.length === 1 ? 'step was' : 'steps were'} healed`)
|
|
101
98
|
|
|
102
|
-
const suggestions = heal.fixes.filter(
|
|
99
|
+
const suggestions = heal.fixes.filter(fix => fix.recipe && heal.recipes[fix.recipe].suggest)
|
|
103
100
|
|
|
104
101
|
if (!suggestions.length) return
|
|
105
102
|
|