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.
Files changed (105) hide show
  1. package/README.md +81 -110
  2. package/bin/codecept.js +2 -2
  3. package/docs/webapi/clearCookie.mustache +1 -1
  4. package/lib/actor.js +46 -36
  5. package/lib/assert/empty.js +3 -5
  6. package/lib/assert/equal.js +4 -7
  7. package/lib/assert/include.js +4 -6
  8. package/lib/assert/throws.js +2 -4
  9. package/lib/assert/truth.js +2 -2
  10. package/lib/codecept.js +87 -83
  11. package/lib/command/configMigrate.js +2 -4
  12. package/lib/command/definitions.js +5 -25
  13. package/lib/command/generate.js +10 -14
  14. package/lib/command/gherkin/snippets.js +10 -8
  15. package/lib/command/gherkin/steps.js +1 -1
  16. package/lib/command/info.js +1 -3
  17. package/lib/command/init.js +8 -12
  18. package/lib/command/interactive.js +1 -1
  19. package/lib/command/list.js +1 -1
  20. package/lib/command/run-multiple.js +12 -35
  21. package/lib/command/run-workers.js +10 -10
  22. package/lib/command/utils.js +5 -6
  23. package/lib/command/workers/runTests.js +14 -17
  24. package/lib/container.js +327 -237
  25. package/lib/data/context.js +10 -13
  26. package/lib/data/dataScenarioConfig.js +8 -8
  27. package/lib/data/dataTableArgument.js +6 -6
  28. package/lib/data/table.js +5 -11
  29. package/lib/els.js +177 -0
  30. package/lib/event.js +1 -0
  31. package/lib/heal.js +78 -80
  32. package/lib/helper/ApiDataFactory.js +3 -6
  33. package/lib/helper/Appium.js +15 -30
  34. package/lib/helper/FileSystem.js +3 -3
  35. package/lib/helper/GraphQLDataFactory.js +3 -3
  36. package/lib/helper/JSONResponse.js +57 -37
  37. package/lib/helper/Nightmare.js +35 -53
  38. package/lib/helper/Playwright.js +189 -251
  39. package/lib/helper/Protractor.js +54 -77
  40. package/lib/helper/Puppeteer.js +134 -232
  41. package/lib/helper/REST.js +5 -17
  42. package/lib/helper/TestCafe.js +21 -44
  43. package/lib/helper/WebDriver.js +103 -162
  44. package/lib/helper/testcafe/testcafe-utils.js +26 -27
  45. package/lib/listener/artifacts.js +2 -2
  46. package/lib/listener/emptyRun.js +58 -0
  47. package/lib/listener/exit.js +4 -4
  48. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  49. package/lib/listener/{timeout.js → globalTimeout.js} +8 -8
  50. package/lib/listener/helpers.js +15 -15
  51. package/lib/listener/mocha.js +1 -1
  52. package/lib/listener/steps.js +17 -12
  53. package/lib/listener/store.js +12 -0
  54. package/lib/mocha/asyncWrapper.js +204 -0
  55. package/lib/{interfaces → mocha}/bdd.js +3 -3
  56. package/lib/mocha/cli.js +257 -0
  57. package/lib/mocha/factory.js +104 -0
  58. package/lib/{interfaces → mocha}/featureConfig.js +11 -12
  59. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  60. package/lib/mocha/hooks.js +83 -0
  61. package/lib/mocha/index.js +12 -0
  62. package/lib/mocha/inject.js +24 -0
  63. package/lib/{interfaces → mocha}/scenarioConfig.js +10 -6
  64. package/lib/mocha/suite.js +55 -0
  65. package/lib/mocha/test.js +60 -0
  66. package/lib/mocha/types.d.ts +31 -0
  67. package/lib/mocha/ui.js +219 -0
  68. package/lib/output.js +28 -10
  69. package/lib/pause.js +159 -135
  70. package/lib/plugin/autoDelay.js +4 -4
  71. package/lib/plugin/autoLogin.js +6 -7
  72. package/lib/plugin/commentStep.js +1 -1
  73. package/lib/plugin/coverage.js +10 -19
  74. package/lib/plugin/customLocator.js +3 -3
  75. package/lib/plugin/debugErrors.js +2 -2
  76. package/lib/plugin/eachElement.js +1 -1
  77. package/lib/plugin/fakerTransform.js +1 -1
  78. package/lib/plugin/heal.js +6 -9
  79. package/lib/plugin/retryFailedStep.js +4 -4
  80. package/lib/plugin/retryTo.js +2 -2
  81. package/lib/plugin/screenshotOnFail.js +9 -36
  82. package/lib/plugin/selenoid.js +15 -35
  83. package/lib/plugin/stepByStepReport.js +51 -13
  84. package/lib/plugin/stepTimeout.js +4 -11
  85. package/lib/plugin/subtitles.js +4 -4
  86. package/lib/plugin/tryTo.js +1 -1
  87. package/lib/plugin/wdio.js +8 -10
  88. package/lib/recorder.js +142 -121
  89. package/lib/secret.js +1 -1
  90. package/lib/step.js +160 -144
  91. package/lib/store.js +6 -2
  92. package/lib/template/heal.js +2 -11
  93. package/lib/utils.js +224 -216
  94. package/lib/within.js +73 -55
  95. package/lib/workers.js +265 -261
  96. package/package.json +46 -47
  97. package/typings/index.d.ts +172 -184
  98. package/typings/promiseBasedTypes.d.ts +53 -516
  99. package/typings/types.d.ts +127 -587
  100. package/lib/cli.js +0 -256
  101. package/lib/helper/ExpectHelper.js +0 -391
  102. package/lib/helper/SoftExpectHelper.js +0 -381
  103. package/lib/mochaFactory.js +0 -113
  104. package/lib/scenario.js +0 -224
  105. package/lib/ui.js +0 -236
package/lib/step.js CHANGED
@@ -1,10 +1,12 @@
1
1
  // TODO: place MetaStep in other file, disable rule
2
2
 
3
- const store = require('./store');
4
- const Secret = require('./secret');
5
- const event = require('./event');
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')
6
8
 
7
- const STACK_LINE = 4;
9
+ const STACK_LINE = 4
8
10
 
9
11
  /**
10
12
  * Each command in test executed through `I.` object is wrapped in Step.
@@ -31,59 +33,58 @@ class Step {
31
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
32
34
  */
33
35
  stepTimeoutSoft: 25,
34
- };
36
+ }
35
37
  }
36
38
 
37
39
  constructor(helper, name) {
38
40
  /** @member {string} */
39
- this.actor = 'I'; // I = actor
41
+ this.actor = 'I' // I = actor
40
42
  /** @member {CodeceptJS.Helper} */
41
- this.helper = helper; // corresponding helper
43
+ this.helper = helper // corresponding helper
42
44
  /** @member {string} */
43
- this.name = name; // name of a step console
45
+ this.name = name // name of a step console
44
46
  /** @member {string} */
45
- this.helperMethod = name; // helper method
47
+ this.helperMethod = name // helper method
46
48
  /** @member {string} */
47
- this.status = 'pending';
49
+ this.status = 'pending'
48
50
  /**
49
51
  * @member {string} suffix
50
52
  * @memberof CodeceptJS.Step#
51
53
  */
52
54
  /** @member {string} */
53
- this.prefix = this.suffix = '';
55
+ this.prefix = this.suffix = ''
54
56
  /** @member {string} */
55
- this.comment = '';
57
+ this.comment = ''
56
58
  /** @member {Array<*>} */
57
- this.args = [];
59
+ this.args = []
58
60
  /** @member {MetaStep} */
59
- this.metaStep = undefined;
61
+ this.metaStep = undefined
60
62
  /** @member {string} */
61
- this.stack = '';
63
+ this.stack = ''
62
64
 
63
- const timeouts = new Map();
65
+ const timeouts = new Map()
64
66
  /**
65
67
  * @method
66
68
  * @returns {number|undefined}
67
69
  */
68
70
  this.getTimeout = function () {
69
- let totalTimeout;
71
+ let totalTimeout
70
72
  // iterate over all timeouts starting from highest values of order
71
73
  new Map([...timeouts.entries()].sort().reverse()).forEach((timeout, order) => {
72
- if (timeout !== undefined && (
74
+ if (
75
+ timeout !== undefined &&
73
76
  // when orders >= 0 - timeout value overrides those set with higher order elements
74
- order >= 0
75
-
76
- // when `order < 0 && totalTimeout === undefined` - timeout is used when nothing is set by elements with higher order
77
- || totalTimeout === undefined
78
-
79
- // when `order < 0` - timeout overrides higher values of timeout or 'no timeout' (totalTimeout === 0) set by elements with higher order
80
- || timeout > 0 && (timeout < totalTimeout || totalTimeout === 0)
81
- )) {
82
- totalTimeout = timeout;
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
83
84
  }
84
- });
85
- return totalTimeout;
86
- };
85
+ })
86
+ return totalTimeout
87
+ }
87
88
  /**
88
89
  * @method
89
90
  * @param {number} timeout - timeout in milliseconds or 0 if no timeout
@@ -91,20 +92,20 @@ class Step {
91
92
  * When order below 0 value of timeout only override if new value is lower
92
93
  */
93
94
  this.setTimeout = function (timeout, order) {
94
- timeouts.set(order, timeout);
95
- };
95
+ timeouts.set(order, timeout)
96
+ }
96
97
 
97
- this.setTrace();
98
+ this.setTrace()
98
99
  }
99
100
 
100
101
  /** @function */
101
102
  setTrace() {
102
- Error.captureStackTrace(this);
103
+ Error.captureStackTrace(this)
103
104
  }
104
105
 
105
106
  /** @param {Array<*>} args */
106
107
  setArguments(args) {
107
- this.args = args;
108
+ this.args = args
108
109
  }
109
110
 
110
111
  /**
@@ -112,226 +113,241 @@ class Step {
112
113
  * @return {*}
113
114
  */
114
115
  run() {
115
- this.args = Array.prototype.slice.call(arguments);
116
+ this.args = Array.prototype.slice.call(arguments)
116
117
  if (store.dryRun) {
117
- this.setStatus('success');
118
- return Promise.resolve(new Proxy({}, dryRunResolver()));
118
+ this.setStatus('success')
119
+ return Promise.resolve(new Proxy({}, dryRunResolver()))
119
120
  }
120
- let result;
121
+ let result
121
122
  try {
122
123
  if (this.helperMethod !== 'say') {
123
- result = this.helper[this.helperMethod].apply(this.helper, this.args);
124
+ result = this.helper[this.helperMethod].apply(this.helper, this.args)
124
125
  }
125
- this.setStatus('success');
126
+ this.setStatus('success')
126
127
  } catch (err) {
127
- this.setStatus('failed');
128
- throw err;
128
+ this.setStatus('failed')
129
+ throw err
129
130
  }
130
- return result;
131
+ return result
132
+ }
133
+
134
+ setActor(actor) {
135
+ this.actor = actor || ''
131
136
  }
132
137
 
133
138
  /** @param {string} status */
134
139
  setStatus(status) {
135
- this.status = status;
140
+ this.status = status
136
141
  if (this.metaStep) {
137
- this.metaStep.setStatus(status);
142
+ this.metaStep.setStatus(status)
138
143
  }
139
144
  }
140
145
 
141
146
  /** @return {string} */
142
147
  humanize() {
143
- return humanizeString(this.name);
148
+ return humanizeString(this.name)
144
149
  }
145
150
 
146
151
  /** @return {string} */
147
152
  humanizeArgs() {
148
- return this.args.map((arg) => {
149
- if (!arg) {
150
- return '';
151
- }
152
- if (typeof arg === 'string') {
153
- return `"${arg}"`;
154
- }
155
- if (Array.isArray(arg)) {
156
- try {
157
- const res = JSON.stringify(arg);
158
- return res;
159
- } catch (err) {
160
- return `[${arg.toString()}]`;
153
+ return this.args
154
+ .map(arg => {
155
+ if (!arg) {
156
+ return ''
161
157
  }
162
- } else if (typeof arg === 'function') {
163
- return arg.toString();
164
- } else if (typeof arg === 'undefined') {
165
- return `${arg}`;
166
- } else if (arg instanceof Secret) {
167
- return arg.getMasked();
168
- } else if (arg.toString && arg.toString() !== '[object Object]') {
169
- return arg.toString();
170
- } else if (typeof arg === 'object') {
171
- const returnedArg = {};
172
- for (const [key, value] of Object.entries(arg)) {
173
- returnedArg[key] = value;
174
- if (value instanceof Secret) returnedArg[key] = value.getMasked();
158
+ if (typeof arg === 'string') {
159
+ return `"${arg}"`
175
160
  }
176
- return JSON.stringify(returnedArg);
177
- }
178
- return arg;
179
- }).join(', ');
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(', ')
180
187
  }
181
188
 
182
189
  /** @return {string} */
183
190
  line() {
184
- const lines = this.stack.split('\n');
191
+ const lines = this.stack.split('\n')
185
192
  if (lines[STACK_LINE]) {
186
- return lines[STACK_LINE].trim().replace(global.codecept_dir || '', '.').trim();
193
+ return lines[STACK_LINE].trim()
194
+ .replace(global.codecept_dir || '', '.')
195
+ .trim()
187
196
  }
188
- return '';
197
+ return ''
189
198
  }
190
199
 
191
200
  /** @return {string} */
192
201
  toString() {
193
- return `${this.prefix}${this.actor} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`;
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}`
194
208
  }
195
209
 
196
210
  /** @return {string} */
197
211
  toCode() {
198
- return `${this.prefix}${this.actor}.${this.name}(${this.humanizeArgs()})${this.suffix}`;
212
+ return `${this.prefix}${this.actor}.${this.name}(${this.humanizeArgs()})${this.suffix}`
199
213
  }
200
214
 
201
215
  isMetaStep() {
202
- return this.constructor.name === 'MetaStep';
216
+ return this.constructor.name === 'MetaStep'
203
217
  }
204
218
 
205
219
  /** @return {boolean} */
206
220
  hasBDDAncestor() {
207
- let hasBDD = false;
208
- let processingStep;
209
- processingStep = this;
221
+ let hasBDD = false
222
+ let processingStep
223
+ processingStep = this
210
224
 
211
225
  while (processingStep.metaStep) {
212
226
  if (processingStep.metaStep.actor.match(/^(Given|When|Then|And)/)) {
213
- hasBDD = true;
214
- break;
227
+ hasBDD = true
228
+ break
215
229
  } else {
216
- processingStep = processingStep.metaStep;
230
+ processingStep = processingStep.metaStep
217
231
  }
218
232
  }
219
- return hasBDD;
233
+ return hasBDD
220
234
  }
221
235
  }
222
236
 
223
237
  /** @extends Step */
224
238
  class MetaStep extends Step {
225
239
  constructor(obj, method) {
226
- super(null, method);
227
- this.actor = obj;
240
+ if (!method) method = ''
241
+ super(null, method)
242
+ this.actor = obj
228
243
  }
229
244
 
230
245
  /** @return {boolean} */
231
246
  isBDD() {
232
247
  if (this.actor && this.actor.match && this.actor.match(/^(Given|When|Then|And)/)) {
233
- return true;
248
+ return true
234
249
  }
235
- return false;
250
+ return false
236
251
  }
237
252
 
238
- isWithin() {
239
- if (this.actor && this.actor.match && this.actor.match(/^(Within)/)) {
240
- return true;
241
- }
242
- return false;
253
+ toCliStyled() {
254
+ return this.toString()
243
255
  }
244
256
 
245
257
  toString() {
246
- const actorText = this.actor;
258
+ const actorText = this.actor
247
259
 
248
- if (this.isBDD() || this.isWithin()) {
249
- return `${this.prefix}${actorText} ${this.name} "${this.humanizeArgs()}${this.suffix}"`;
260
+ if (this.isBDD()) {
261
+ return `${this.prefix}${actorText} ${this.name} "${this.humanizeArgs()}${this.suffix}"`
250
262
  }
251
263
 
252
264
  if (actorText === 'I') {
253
- return `${this.prefix}${actorText} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`;
265
+ return `${this.prefix}${actorText} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`
254
266
  }
255
267
 
256
- return `On ${this.prefix}${actorText}: ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`;
268
+ return `On ${this.prefix}${actorText}: ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`
257
269
  }
258
270
 
259
271
  humanize() {
260
- return humanizeString(this.name);
272
+ return humanizeString(this.name)
261
273
  }
262
274
 
263
- setTrace() {
264
- }
275
+ setTrace() {}
265
276
 
266
277
  setContext(context) {
267
- this.context = context;
278
+ this.context = context
268
279
  }
269
280
 
270
281
  /** @return {*} */
271
282
  run(fn) {
272
- this.status = 'queued';
273
- this.setArguments(Array.from(arguments).slice(1));
274
- let result;
275
-
276
- const registerStep = (step) => {
277
- this.metaStep = null;
278
- step.metaStep = this;
279
- };
280
- event.dispatcher.prependListener(event.step.before, registerStep);
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)
281
292
  // Handle async and sync methods.
282
293
  if (fn.constructor.name === 'AsyncFunction') {
283
- result = fn.apply(this.context, this.args).then((result) => {
284
- return result;
285
- }).catch((error) => {
286
- this.setStatus('failed');
287
- throw error;
288
- }).finally(() => {
289
- this.endTime = Date.now();
290
- event.dispatcher.removeListener(event.step.before, registerStep);
291
- });
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
+ })
292
307
  } else {
293
308
  try {
294
- this.startTime = Date.now();
295
- result = fn.apply(this.context, this.args);
309
+ this.startTime = Date.now()
310
+ result = fn.apply(this.context, this.args)
296
311
  } catch (error) {
297
- this.setStatus('failed');
298
- throw error;
312
+ this.setStatus('failed')
313
+ throw error
299
314
  } finally {
300
- this.endTime = Date.now();
301
- event.dispatcher.removeListener(event.step.before, registerStep);
315
+ this.endTime = Date.now()
316
+ event.dispatcher.removeListener(event.step.before, registerStep)
302
317
  }
303
318
  }
304
319
 
305
- return result;
320
+ return result
306
321
  }
307
322
  }
308
323
 
309
- Step.TIMEOUTS = {};
324
+ Step.TIMEOUTS = {}
310
325
 
311
326
  /** @type {Class<MetaStep>} */
312
- Step.MetaStep = MetaStep;
327
+ Step.MetaStep = MetaStep
313
328
 
314
- module.exports = Step;
329
+ module.exports = Step
315
330
 
316
331
  function dryRunResolver() {
317
332
  return {
318
333
  get(target, prop) {
319
- if (prop === 'toString') return () => '<VALUE>';
320
- return new Proxy({}, dryRunResolver());
334
+ if (prop === 'toString') return () => '<VALUE>'
335
+ return new Proxy({}, dryRunResolver())
321
336
  },
322
- };
337
+ }
323
338
  }
324
339
 
325
340
  function humanizeString(string) {
326
341
  // split strings by words, then make them all lowercase
327
- const _result = string.replace(/([a-z](?=[A-Z]))/g, '$1 ')
342
+ const _result = string
343
+ .replace(/([a-z](?=[A-Z]))/g, '$1 ')
328
344
  .split(' ')
329
- .map(word => word.toLowerCase());
345
+ .map(word => word.toLowerCase())
330
346
 
331
- _result[0] = _result[0] === 'i' ? capitalizeFLetter(_result[0]) : _result[0];
332
- return _result.join(' ').trim();
347
+ _result[0] = _result[0] === 'i' ? capitalizeFLetter(_result[0]) : _result[0]
348
+ return _result.join(' ').trim()
333
349
  }
334
350
 
335
351
  function capitalizeFLetter(string) {
336
- return (string[0].toUpperCase() + string.slice(1));
352
+ return string[0].toUpperCase() + string.slice(1)
337
353
  }
package/lib/store.js CHANGED
@@ -9,6 +9,10 @@ const store = {
9
9
  timeouts: true,
10
10
  /** @type {boolean} */
11
11
  dryRun: false,
12
- };
12
+ /** @type {boolean} */
13
+ onPause: false,
14
+ /** @type {CodeceptJS.Test | null} */
15
+ currentTest: null,
16
+ }
13
17
 
14
- module.exports = store;
18
+ module.exports = store
@@ -6,17 +6,8 @@ heal.addRecipe('ai', {
6
6
  html: ({ I }) => I.grabHTMLFrom('body'),
7
7
  },
8
8
  suggest: true,
9
- steps: [
10
- 'click',
11
- 'fillField',
12
- 'appendField',
13
- 'selectOption',
14
- 'attachFile',
15
- 'checkOption',
16
- 'uncheckOption',
17
- 'doubleClick',
18
- ],
19
- fn: async (args) => {
9
+ steps: ['click', 'fillField', 'appendField', 'selectOption', 'attachFile', 'checkOption', 'uncheckOption', 'doubleClick'],
10
+ fn: async args => {
20
11
  return ai.healFailedStep(args)
21
12
  },
22
13
  })