codeceptjs 3.6.10 → 3.7.0-beta.10

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