codeceptjs 3.7.0-beta.4 → 3.7.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.
Files changed (44) hide show
  1. package/README.md +9 -10
  2. package/bin/codecept.js +7 -0
  3. package/lib/actor.js +47 -92
  4. package/lib/command/check.js +173 -0
  5. package/lib/command/definitions.js +2 -0
  6. package/lib/command/run-workers.js +1 -1
  7. package/lib/command/workers/runTests.js +112 -109
  8. package/lib/container.js +9 -0
  9. package/lib/effects.js +123 -0
  10. package/lib/heal.js +10 -0
  11. package/lib/helper/Appium.js +27 -16
  12. package/lib/helper/Playwright.js +15 -0
  13. package/lib/helper/Puppeteer.js +5 -0
  14. package/lib/helper/WebDriver.js +9 -1
  15. package/lib/listener/emptyRun.js +2 -5
  16. package/lib/listener/globalTimeout.js +15 -3
  17. package/lib/listener/steps.js +3 -0
  18. package/lib/mocha/cli.js +22 -5
  19. package/lib/mocha/featureConfig.js +13 -0
  20. package/lib/mocha/scenarioConfig.js +11 -0
  21. package/lib/mocha/test.js +15 -0
  22. package/lib/mocha/types.d.ts +6 -0
  23. package/lib/output.js +74 -73
  24. package/lib/pause.js +3 -7
  25. package/lib/plugin/heal.js +30 -0
  26. package/lib/plugin/stepTimeout.js +1 -1
  27. package/lib/recorder.js +1 -1
  28. package/lib/step/base.js +180 -0
  29. package/lib/step/config.js +50 -0
  30. package/lib/step/helper.js +47 -0
  31. package/lib/step/meta.js +91 -0
  32. package/lib/step/record.js +74 -0
  33. package/lib/step/retry.js +11 -0
  34. package/lib/step/timeout.js +42 -0
  35. package/lib/step.js +15 -348
  36. package/lib/steps.js +23 -0
  37. package/lib/store.js +2 -0
  38. package/lib/utils.js +58 -0
  39. package/lib/within.js +2 -2
  40. package/lib/workers.js +2 -12
  41. package/package.json +7 -5
  42. package/typings/index.d.ts +5 -4
  43. package/typings/promiseBasedTypes.d.ts +12 -0
  44. package/typings/types.d.ts +12 -0
package/lib/output.js CHANGED
@@ -1,6 +1,6 @@
1
- const colors = require('chalk');
2
- const figures = require('figures');
3
- const { maskSensitiveData } = require('invisi-data');
1
+ const colors = require('chalk')
2
+ const figures = require('figures')
3
+ const { maskSensitiveData } = require('invisi-data')
4
4
 
5
5
  const styles = {
6
6
  error: colors.bgRed.white.bold,
@@ -10,11 +10,12 @@ const styles = {
10
10
  debug: colors.cyan,
11
11
  log: colors.grey,
12
12
  bold: colors.bold,
13
- };
13
+ section: colors.white.dim.bold,
14
+ }
14
15
 
15
- let outputLevel = 0;
16
- let outputProcess = '';
17
- let newline = true;
16
+ let outputLevel = 0
17
+ let outputProcess = ''
18
+ let newline = true
18
19
 
19
20
  /**
20
21
  * @alias output
@@ -28,7 +29,7 @@ module.exports = {
28
29
  stepShift: 0,
29
30
 
30
31
  standWithUkraine() {
31
- return `#${colors.bold.yellow('StandWith')}${colors.bold.cyan('Ukraine')}`;
32
+ return `#${colors.bold.yellow('StandWith')}${colors.bold.cyan('Ukraine')}`
32
33
  },
33
34
 
34
35
  /**
@@ -37,8 +38,8 @@ module.exports = {
37
38
  * @returns {number}
38
39
  */
39
40
  level(level) {
40
- if (level !== undefined) outputLevel = level;
41
- return outputLevel;
41
+ if (level !== undefined) outputLevel = level
42
+ return outputLevel
42
43
  },
43
44
 
44
45
  /**
@@ -48,9 +49,9 @@ module.exports = {
48
49
  * @returns {string}
49
50
  */
50
51
  process(process) {
51
- if (process === null) return (outputProcess = '');
52
- if (process) outputProcess = String(process).length === 1 ? `[0${process}]` : `[${process}]`;
53
- return outputProcess;
52
+ if (process === null) return (outputProcess = '')
53
+ if (process) outputProcess = String(process).length === 1 ? `[0${process}]` : `[${process}]`
54
+ return outputProcess
54
55
  },
55
56
 
56
57
  /**
@@ -58,9 +59,9 @@ module.exports = {
58
59
  * @param {string} msg
59
60
  */
60
61
  debug(msg) {
61
- const _msg = isMaskedData() ? maskSensitiveData(msg) : msg;
62
+ const _msg = isMaskedData() ? maskSensitiveData(msg) : msg
62
63
  if (outputLevel >= 2) {
63
- print(' '.repeat(this.stepShift), styles.debug(`${figures.pointerSmall} ${_msg}`));
64
+ print(' '.repeat(this.stepShift), styles.debug(`${figures.pointerSmall} ${_msg}`))
64
65
  }
65
66
  },
66
67
 
@@ -69,9 +70,9 @@ module.exports = {
69
70
  * @param {string} msg
70
71
  */
71
72
  log(msg) {
72
- const _msg = isMaskedData() ? maskSensitiveData(msg) : msg;
73
+ const _msg = isMaskedData() ? maskSensitiveData(msg) : msg
73
74
  if (outputLevel >= 3) {
74
- print(' '.repeat(this.stepShift), styles.log(truncate(` ${_msg}`, this.spaceShift)));
75
+ print(' '.repeat(this.stepShift), styles.log(truncate(` ${_msg}`, this.spaceShift)))
75
76
  }
76
77
  },
77
78
 
@@ -80,7 +81,7 @@ module.exports = {
80
81
  * @param {string} msg
81
82
  */
82
83
  error(msg) {
83
- print(styles.error(msg));
84
+ print(styles.error(msg))
84
85
  },
85
86
 
86
87
  /**
@@ -88,7 +89,7 @@ module.exports = {
88
89
  * @param {string} msg
89
90
  */
90
91
  success(msg) {
91
- print(styles.success(msg));
92
+ print(styles.success(msg))
92
93
  },
93
94
 
94
95
  /**
@@ -97,7 +98,7 @@ module.exports = {
97
98
  * @param {string} msg
98
99
  */
99
100
  plugin(pluginName, msg = '') {
100
- this.debug(`<${pluginName}> ${msg}`);
101
+ this.debug(`<${pluginName}> ${msg}`)
101
102
  },
102
103
 
103
104
  /**
@@ -105,26 +106,26 @@ module.exports = {
105
106
  * @param {CodeceptJS.Step} step
106
107
  */
107
108
  step(step) {
108
- if (outputLevel === 0) return;
109
- if (!step) return;
109
+ if (outputLevel === 0) return
110
+ if (!step) return
110
111
  // Avoid to print non-gherkin steps, when gherkin is running for --steps mode
111
112
  if (outputLevel === 1) {
112
113
  if (typeof step === 'object' && step.hasBDDAncestor()) {
113
- return;
114
+ return
114
115
  }
115
116
  }
116
117
 
117
- let stepLine = step.toCliStyled();
118
+ let stepLine = step.toCliStyled()
118
119
  if (step.metaStep && outputLevel >= 1) {
119
120
  // this.stepShift += 2;
120
- stepLine = colors.dim(truncate(stepLine, this.spaceShift));
121
+ stepLine = colors.dim(truncate(stepLine, this.spaceShift))
121
122
  }
122
123
  if (step.comment) {
123
- stepLine += colors.grey(step.comment.split('\n').join('\n' + ' '.repeat(4)));
124
+ stepLine += colors.grey(step.comment.split('\n').join('\n' + ' '.repeat(4)))
124
125
  }
125
126
 
126
- const _stepLine = isMaskedData() ? maskSensitiveData(stepLine) : stepLine;
127
- print(' '.repeat(this.stepShift), truncate(_stepLine, this.spaceShift));
127
+ const _stepLine = isMaskedData() ? maskSensitiveData(stepLine) : stepLine
128
+ print(' '.repeat(this.stepShift), truncate(_stepLine, this.spaceShift))
128
129
  },
129
130
 
130
131
  /** @namespace */
@@ -133,9 +134,9 @@ module.exports = {
133
134
  * @param {Mocha.Suite} suite
134
135
  */
135
136
  started: suite => {
136
- if (!suite.title) return;
137
- print(`${colors.bold(suite.title)} --`);
138
- if (suite.comment) print(suite.comment);
137
+ if (!suite.title) return
138
+ print(`${colors.bold(suite.title)} --`)
139
+ if (suite.comment) print(suite.comment)
139
140
  },
140
141
  },
141
142
 
@@ -145,25 +146,25 @@ module.exports = {
145
146
  * @param {Mocha.Test} test
146
147
  */
147
148
  started(test) {
148
- print(` ${colors.magenta.bold(test.title)}`);
149
+ print(` ${colors.magenta.bold(test.title)}`)
149
150
  },
150
151
  /**
151
152
  * @param {Mocha.Test} test
152
153
  */
153
154
  passed(test) {
154
- print(` ${colors.green.bold(figures.tick)} ${test.title} ${colors.grey(`in ${test.duration}ms`)}`);
155
+ print(` ${colors.green.bold(figures.tick)} ${test.title} ${colors.grey(`in ${test.duration}ms`)}`)
155
156
  },
156
157
  /**
157
158
  * @param {Mocha.Test} test
158
159
  */
159
160
  failed(test) {
160
- print(` ${colors.red.bold(figures.cross)} ${test.title} ${colors.grey(`in ${test.duration}ms`)}`);
161
+ print(` ${colors.red.bold(figures.cross)} ${test.title} ${colors.grey(`in ${test.duration}ms`)}`)
161
162
  },
162
163
  /**
163
164
  * @param {Mocha.Test} test
164
165
  */
165
166
  skipped(test) {
166
- print(` ${colors.yellow.bold('S')} ${test.title}`);
167
+ print(` ${colors.yellow.bold('S')} ${test.title}`)
167
168
  },
168
169
  },
169
170
 
@@ -174,38 +175,38 @@ module.exports = {
174
175
  */
175
176
 
176
177
  started(test) {
177
- if (outputLevel < 1) return;
178
- print(` ${colors.dim.bold('Scenario()')}`);
178
+ if (outputLevel < 1) return
179
+ print(` ${colors.dim.bold('Scenario()')}`)
179
180
  },
180
181
 
181
182
  /**
182
183
  * @param {Mocha.Test} test
183
184
  */
184
185
  passed(test) {
185
- print(` ${colors.green.bold(`${figures.tick} OK`)} ${colors.grey(`in ${test.duration}ms`)}`);
186
- print();
186
+ print(` ${colors.green.bold(`${figures.tick} OK`)} ${colors.grey(`in ${test.duration}ms`)}`)
187
+ print()
187
188
  },
188
189
  /**
189
190
  * @param {Mocha.Test} test
190
191
  */
191
192
  failed(test) {
192
- print(` ${colors.red.bold(`${figures.cross} FAILED`)} ${colors.grey(`in ${test.duration}ms`)}`);
193
- print();
193
+ print(` ${colors.red.bold(`${figures.cross} FAILED`)} ${colors.grey(`in ${test.duration}ms`)}`)
194
+ print()
194
195
  },
195
196
  },
196
197
 
197
198
  hook: {
198
199
  started(hook) {
199
- if (outputLevel < 1) return;
200
- print(` ${colors.dim.bold(hook.toCode())}`);
200
+ if (outputLevel < 1) return
201
+ print(` ${colors.dim.bold(hook.toCode())}`)
201
202
  },
202
203
  passed(hook) {
203
- if (outputLevel < 1) return;
204
- print();
204
+ if (outputLevel < 1) return
205
+ print()
205
206
  },
206
207
  failed(hook) {
207
- if (outputLevel < 1) return;
208
- print(` ${colors.red.bold(hook.toCode())}`);
208
+ if (outputLevel < 1) return
209
+ print(` ${colors.red.bold(hook.toCode())}`)
209
210
  },
210
211
  },
211
212
 
@@ -217,9 +218,9 @@ module.exports = {
217
218
  */
218
219
  say(message, color = 'cyan') {
219
220
  if (colors[color] === undefined) {
220
- color = 'cyan';
221
+ color = 'cyan'
221
222
  }
222
- if (outputLevel >= 1) print(` ${colors[color].bold(message)}`);
223
+ if (outputLevel >= 1) print(` ${colors[color].bold(message)}`)
223
224
  },
224
225
 
225
226
  /**
@@ -229,54 +230,54 @@ module.exports = {
229
230
  * @param {number|string} duration
230
231
  */
231
232
  result(passed, failed, skipped, duration, failedHooks = 0) {
232
- let style = colors.bgGreen;
233
- let msg = ` ${passed || 0} passed`;
234
- let status = style.bold(' OK ');
233
+ let style = colors.bgGreen
234
+ let msg = ` ${passed || 0} passed`
235
+ let status = style.bold(' OK ')
235
236
  if (failed) {
236
- style = style.bgRed;
237
- status = style.bold(' FAIL ');
238
- msg += `, ${failed} failed`;
237
+ style = style.bgRed
238
+ status = style.bold(' FAIL ')
239
+ msg += `, ${failed} failed`
239
240
  }
240
241
 
241
242
  if (failedHooks > 0) {
242
- style = style.bgRed;
243
- status = style.bold(' FAIL ');
244
- msg += `, ${failedHooks} failedHooks`;
243
+ style = style.bgRed
244
+ status = style.bold(' FAIL ')
245
+ msg += `, ${failedHooks} failedHooks`
245
246
  }
246
- status += style.grey(' |');
247
+ status += style.grey(' |')
247
248
 
248
249
  if (skipped) {
249
- if (!failed) style = style.bgYellow;
250
- msg += `, ${skipped} skipped`;
250
+ if (!failed) style = style.bgYellow
251
+ msg += `, ${skipped} skipped`
251
252
  }
252
- msg += ' ';
253
- print(status + style(msg) + colors.grey(` // ${duration}`));
253
+ msg += ' '
254
+ print(status + style(msg) + colors.grey(` // ${duration}`))
254
255
  },
255
- };
256
+ }
256
257
 
257
258
  function print(...msg) {
258
259
  if (outputProcess) {
259
- msg.unshift(outputProcess);
260
+ msg.unshift(outputProcess)
260
261
  }
261
262
  if (!newline) {
262
- console.log();
263
- newline = true;
263
+ console.log()
264
+ newline = true
264
265
  }
265
266
 
266
- console.log.apply(this, msg);
267
+ console.log.apply(this, msg)
267
268
  }
268
269
 
269
270
  function truncate(msg, gap = 0) {
270
271
  if (msg.indexOf('\n') > 0 || outputLevel >= 3) {
271
- return msg; // don't cut multi line steps or on verbose log level
272
+ return msg // don't cut multi line steps or on verbose log level
272
273
  }
273
- const width = (process.stdout.columns || 200) - gap - 4;
274
+ const width = (process.stdout.columns || 200) - gap - 4
274
275
  if (msg.length > width) {
275
- msg = msg.substr(0, width - 1) + figures.ellipsis;
276
+ msg = msg.substr(0, width - 1) + figures.ellipsis
276
277
  }
277
- return msg;
278
+ return msg
278
279
  }
279
280
 
280
281
  function isMaskedData() {
281
- return global.maskSensitiveData === true || false;
282
+ return global.maskSensitiveData === true || false
282
283
  }
package/lib/pause.js CHANGED
@@ -2,7 +2,6 @@ const colors = require('chalk')
2
2
  const readline = require('readline')
3
3
  const ora = require('ora-classic')
4
4
  const debug = require('debug')('codeceptjs:pause')
5
- const Fuse = require('fuse.js')
6
5
 
7
6
  const container = require('./container')
8
7
  const history = require('./history')
@@ -11,7 +10,7 @@ const aiAssistant = require('./ai')
11
10
  const recorder = require('./recorder')
12
11
  const event = require('./event')
13
12
  const output = require('./output')
14
- const { methodsOfObject } = require('./utils')
13
+ const { methodsOfObject, searchWithFusejs } = require('./utils')
15
14
 
16
15
  // npm install colors
17
16
  let rl
@@ -218,15 +217,12 @@ function completer(line) {
218
217
  return [completions, line]
219
218
  }
220
219
 
221
- // Initialize Fuse with completions
222
- const fuse = new Fuse(completions, {
220
+ // Search using Fuse.js
221
+ const searchResults = searchWithFusejs(completions, line, {
223
222
  threshold: 0.3,
224
223
  distance: 100,
225
224
  minMatchCharLength: 1,
226
225
  })
227
-
228
- // Search using Fuse.js
229
- const searchResults = fuse.search(line)
230
226
  const hits = searchResults.map(result => result.item)
231
227
 
232
228
  return [hits, line]
@@ -5,6 +5,7 @@ const event = require('../event')
5
5
  const output = require('../output')
6
6
  const heal = require('../heal')
7
7
  const store = require('../store')
8
+ const container = require('../container')
8
9
 
9
10
  const defaultConfig = {
10
11
  healLimit: 2,
@@ -115,4 +116,33 @@ module.exports = function (config = {}) {
115
116
  i++
116
117
  }
117
118
  })
119
+
120
+ event.dispatcher.on(event.workers.result, ({ tests }) => {
121
+ const { print } = output
122
+
123
+ const healedTests = Object.values(tests)
124
+ .flat()
125
+ .filter(test => test.notes.some(note => note.type === 'heal'))
126
+ if (!healedTests.length) return
127
+
128
+ setTimeout(() => {
129
+ print('')
130
+ print('===================')
131
+ print(colors.bold.green('Self-Healing Report:'))
132
+
133
+ print('')
134
+ print('Suggested changes:')
135
+ print('')
136
+
137
+ healedTests.forEach(test => {
138
+ print(`${colors.bold.magenta(test.title)}`)
139
+ test.notes
140
+ .filter(note => note.type === 'heal')
141
+ .forEach(note => {
142
+ print(note.text)
143
+ print('')
144
+ })
145
+ })
146
+ }, 0)
147
+ })
118
148
  }
@@ -1,5 +1,5 @@
1
1
  const event = require('../event')
2
- const TIMEOUT_ORDER = require('../step').TIMEOUT_ORDER
2
+ const { TIMEOUT_ORDER } = require('../step/timeout')
3
3
 
4
4
  const defaultConfig = {
5
5
  timeout: 150,
package/lib/recorder.js CHANGED
@@ -179,7 +179,7 @@ module.exports = {
179
179
  }
180
180
  if (retry === undefined) retry = true
181
181
  if (!running && !force) {
182
- return
182
+ return Promise.resolve()
183
183
  }
184
184
  tasks.push(taskName)
185
185
  debug(chalk.gray(`${currentQueue()} Queued | ${taskName}`))
@@ -0,0 +1,180 @@
1
+ const color = require('chalk')
2
+ const Secret = require('../secret')
3
+ const { getCurrentTimeout } = require('./timeout')
4
+ const { ucfirst, humanizeString } = require('../utils')
5
+
6
+ const STACK_LINE = 4
7
+
8
+ /**
9
+ * Each command in test executed through `I.` object is wrapped in Step.
10
+ * Step allows logging executed commands and triggers hook before and after step execution.
11
+ * @param {string} name
12
+ */
13
+ class Step {
14
+ constructor(name) {
15
+ /** @member {string} */
16
+ this.name = name
17
+ /** @member {Map<number, number>} */
18
+ this.timeouts = new Map()
19
+
20
+ /** @member {Array<*>} */
21
+ this.args = []
22
+
23
+ /** @member {Record<string,any>} */
24
+ this.opts = {}
25
+ /** @member {string} */
26
+ this.actor = 'I' // I = actor
27
+ /** @member {string} */
28
+ this.helperMethod = name // helper method
29
+ /** @member {string} */
30
+ this.status = 'pending'
31
+ /** @member {string} */
32
+ this.prefix = this.suffix = ''
33
+ /** @member {string} */
34
+ this.comment = ''
35
+ /** @member {any} */
36
+ this.metaStep = undefined
37
+ /** @member {string} */
38
+ this.stack = ''
39
+
40
+ this.setTrace()
41
+ }
42
+
43
+ setMetaStep(metaStep) {
44
+ this.metaStep = metaStep
45
+ }
46
+
47
+ run() {
48
+ throw new Error('Not implemented')
49
+ }
50
+
51
+ /**
52
+ * @returns {number|undefined}
53
+ */
54
+ get timeout() {
55
+ return getCurrentTimeout(this.timeouts)
56
+ }
57
+
58
+ /**
59
+ * @param {number} timeout - timeout in milliseconds or 0 if no timeout
60
+ * @param {number} order - order defines the priority of timeout, timeouts set with lower order override those set with higher order.
61
+ * When order below 0 value of timeout only override if new value is lower
62
+ */
63
+ setTimeout(timeout, order) {
64
+ this.timeouts.set(order, timeout)
65
+ }
66
+
67
+ /** @function */
68
+ setTrace() {
69
+ Error.captureStackTrace(this)
70
+ }
71
+
72
+ /** @param {Array<*>} args */
73
+ setArguments(args) {
74
+ this.args = args
75
+ }
76
+
77
+ setActor(actor) {
78
+ this.actor = actor || ''
79
+ }
80
+
81
+ /** @param {string} status */
82
+ setStatus(status) {
83
+ this.status = status
84
+ if (this.metaStep) {
85
+ this.metaStep.setStatus(status)
86
+ }
87
+ }
88
+
89
+ /** @return {string} */
90
+ humanize() {
91
+ return humanizeString(this.name)
92
+ }
93
+
94
+ /** @return {string} */
95
+ humanizeArgs() {
96
+ return this.args
97
+ .map(arg => {
98
+ if (!arg) {
99
+ return ''
100
+ }
101
+ if (typeof arg === 'string') {
102
+ return `"${arg}"`
103
+ }
104
+ if (Array.isArray(arg)) {
105
+ try {
106
+ const res = JSON.stringify(arg)
107
+ return res
108
+ } catch (err) {
109
+ return `[${arg.toString()}]`
110
+ }
111
+ } else if (typeof arg === 'function') {
112
+ return arg.toString()
113
+ } else if (typeof arg === 'undefined') {
114
+ return `${arg}`
115
+ } else if (arg instanceof Secret) {
116
+ return arg.getMasked()
117
+ } else if (arg.toString && arg.toString() !== '[object Object]') {
118
+ return arg.toString()
119
+ } else if (typeof arg === 'object') {
120
+ const returnedArg = {}
121
+ for (const [key, value] of Object.entries(arg)) {
122
+ returnedArg[key] = value
123
+ if (value instanceof Secret) returnedArg[key] = value.getMasked()
124
+ }
125
+ return JSON.stringify(returnedArg)
126
+ }
127
+ return arg
128
+ })
129
+ .join(', ')
130
+ }
131
+
132
+ /** @return {string} */
133
+ line() {
134
+ const lines = this.stack.split('\n')
135
+ if (lines[STACK_LINE]) {
136
+ return lines[STACK_LINE].trim()
137
+ .replace(global.codecept_dir || '', '.')
138
+ .trim()
139
+ }
140
+ return ''
141
+ }
142
+
143
+ /** @return {string} */
144
+ toString() {
145
+ return ucfirst(`${this.prefix}${this.actor} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`).trim()
146
+ }
147
+
148
+ /** @return {string} */
149
+ toCliStyled() {
150
+ return `${this.prefix}${this.actor} ${color.italic(this.humanize())} ${color.yellow(this.humanizeArgs())}${this.suffix}`
151
+ }
152
+
153
+ /** @return {string} */
154
+ toCode() {
155
+ return `${this.prefix}${this.actor}.${this.name}(${this.humanizeArgs()})${this.suffix}`
156
+ }
157
+
158
+ isMetaStep() {
159
+ return this.constructor.name === 'MetaStep'
160
+ }
161
+
162
+ /** @return {boolean} */
163
+ hasBDDAncestor() {
164
+ let hasBDD = false
165
+ let processingStep
166
+ processingStep = this
167
+
168
+ while (processingStep.metaStep) {
169
+ if (processingStep.metaStep.actor.match(/^(Given|When|Then|And)/)) {
170
+ hasBDD = true
171
+ break
172
+ } else {
173
+ processingStep = processingStep.metaStep
174
+ }
175
+ }
176
+ return hasBDD
177
+ }
178
+ }
179
+
180
+ module.exports = Step
@@ -0,0 +1,50 @@
1
+ /**
2
+ * StepConfig is a configuration object for a step.
3
+ * It is used to create a new step that is a combination of other steps.
4
+ */
5
+ class StepConfig {
6
+ constructor(opts = {}) {
7
+ /** @member {{ opts: Record<string, any>, timeout: number|undefined, retry: number|undefined }} */
8
+ this.config = {
9
+ opts,
10
+ timeout: undefined,
11
+ retry: undefined,
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Set the options for the step.
17
+ * @param {object} opts - The options for the step.
18
+ * @returns {StepConfig} - The step configuration object.
19
+ */
20
+ opts(opts) {
21
+ this.config.opts = opts
22
+ return this
23
+ }
24
+
25
+ /**
26
+ * Set the timeout for the step.
27
+ * @param {number} timeout - The timeout for the step.
28
+ * @returns {StepConfig} - The step configuration object.
29
+ */
30
+ timeout(timeout) {
31
+ this.config.timeout = timeout
32
+ return this
33
+ }
34
+
35
+ /**
36
+ * Set the retry for the step.
37
+ * @param {number} retry - The retry for the step.
38
+ * @returns {StepConfig} - The step configuration object.
39
+ */
40
+ retry(retry) {
41
+ this.config.retry = retry
42
+ return this
43
+ }
44
+
45
+ getConfig() {
46
+ return this.config
47
+ }
48
+ }
49
+
50
+ module.exports = StepConfig
@@ -0,0 +1,47 @@
1
+ const Step = require('./base')
2
+ const store = require('../store')
3
+
4
+ class HelperStep extends Step {
5
+ constructor(helper, name) {
6
+ super(name)
7
+ /** @member {CodeceptJS.Helper} helper corresponding helper */
8
+ this.helper = helper
9
+ /** @member {string} helperMethod name of method to be executed */
10
+ this.helperMethod = name
11
+ }
12
+
13
+ /**
14
+ * @param {...any} args
15
+ * @return {*}
16
+ */
17
+ run() {
18
+ this.args = Array.prototype.slice.call(arguments)
19
+
20
+ if (store.dryRun) {
21
+ this.setStatus('success')
22
+ return Promise.resolve(new Proxy({}, dryRunResolver()))
23
+ }
24
+ let result
25
+ try {
26
+ if (this.helperMethod !== 'say') {
27
+ result = this.helper[this.helperMethod].apply(this.helper, this.args)
28
+ }
29
+ this.setStatus('success')
30
+ } catch (err) {
31
+ this.setStatus('failed')
32
+ throw err
33
+ }
34
+ return result
35
+ }
36
+ }
37
+
38
+ module.exports = HelperStep
39
+
40
+ function dryRunResolver() {
41
+ return {
42
+ get(target, prop) {
43
+ if (prop === 'toString') return () => '<VALUE>'
44
+ return new Proxy({}, dryRunResolver())
45
+ },
46
+ }
47
+ }