codeceptjs 4.0.0-beta.3 → 4.0.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 (155) hide show
  1. package/README.md +134 -119
  2. package/bin/codecept.js +12 -2
  3. package/bin/test-server.js +53 -0
  4. package/docs/webapi/clearCookie.mustache +1 -1
  5. package/lib/actor.js +66 -102
  6. package/lib/ai.js +130 -121
  7. package/lib/assert/empty.js +3 -5
  8. package/lib/assert/equal.js +4 -7
  9. package/lib/assert/include.js +4 -6
  10. package/lib/assert/throws.js +2 -4
  11. package/lib/assert/truth.js +2 -2
  12. package/lib/codecept.js +141 -86
  13. package/lib/command/check.js +201 -0
  14. package/lib/command/configMigrate.js +2 -4
  15. package/lib/command/definitions.js +8 -26
  16. package/lib/command/dryRun.js +30 -35
  17. package/lib/command/generate.js +10 -14
  18. package/lib/command/gherkin/snippets.js +75 -73
  19. package/lib/command/gherkin/steps.js +1 -1
  20. package/lib/command/info.js +42 -8
  21. package/lib/command/init.js +13 -12
  22. package/lib/command/interactive.js +10 -2
  23. package/lib/command/list.js +1 -1
  24. package/lib/command/run-multiple/chunk.js +48 -45
  25. package/lib/command/run-multiple.js +12 -35
  26. package/lib/command/run-workers.js +21 -58
  27. package/lib/command/utils.js +5 -6
  28. package/lib/command/workers/runTests.js +263 -222
  29. package/lib/container.js +386 -238
  30. package/lib/data/context.js +10 -13
  31. package/lib/data/dataScenarioConfig.js +8 -8
  32. package/lib/data/dataTableArgument.js +6 -6
  33. package/lib/data/table.js +5 -11
  34. package/lib/effects.js +223 -0
  35. package/lib/element/WebElement.js +327 -0
  36. package/lib/els.js +158 -0
  37. package/lib/event.js +21 -17
  38. package/lib/heal.js +88 -80
  39. package/lib/helper/AI.js +2 -1
  40. package/lib/helper/ApiDataFactory.js +4 -7
  41. package/lib/helper/Appium.js +50 -57
  42. package/lib/helper/FileSystem.js +3 -3
  43. package/lib/helper/GraphQLDataFactory.js +4 -4
  44. package/lib/helper/JSONResponse.js +75 -37
  45. package/lib/helper/Mochawesome.js +31 -9
  46. package/lib/helper/Nightmare.js +37 -58
  47. package/lib/helper/Playwright.js +267 -272
  48. package/lib/helper/Protractor.js +56 -87
  49. package/lib/helper/Puppeteer.js +247 -264
  50. package/lib/helper/REST.js +29 -17
  51. package/lib/helper/TestCafe.js +22 -47
  52. package/lib/helper/WebDriver.js +157 -368
  53. package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
  54. package/lib/helper/extras/Popup.js +22 -22
  55. package/lib/helper/network/utils.js +1 -1
  56. package/lib/helper/testcafe/testcafe-utils.js +27 -28
  57. package/lib/listener/emptyRun.js +55 -0
  58. package/lib/listener/exit.js +7 -10
  59. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  60. package/lib/listener/globalTimeout.js +165 -0
  61. package/lib/listener/helpers.js +15 -15
  62. package/lib/listener/mocha.js +1 -1
  63. package/lib/listener/result.js +12 -0
  64. package/lib/listener/retryEnhancer.js +85 -0
  65. package/lib/listener/steps.js +32 -18
  66. package/lib/listener/store.js +20 -0
  67. package/lib/locator.js +1 -1
  68. package/lib/mocha/asyncWrapper.js +231 -0
  69. package/lib/{interfaces → mocha}/bdd.js +3 -3
  70. package/lib/mocha/cli.js +308 -0
  71. package/lib/mocha/factory.js +104 -0
  72. package/lib/{interfaces → mocha}/featureConfig.js +32 -12
  73. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  74. package/lib/mocha/hooks.js +112 -0
  75. package/lib/mocha/index.js +12 -0
  76. package/lib/mocha/inject.js +29 -0
  77. package/lib/{interfaces → mocha}/scenarioConfig.js +31 -7
  78. package/lib/mocha/suite.js +82 -0
  79. package/lib/mocha/test.js +181 -0
  80. package/lib/mocha/types.d.ts +42 -0
  81. package/lib/mocha/ui.js +232 -0
  82. package/lib/output.js +93 -65
  83. package/lib/pause.js +160 -138
  84. package/lib/plugin/analyze.js +396 -0
  85. package/lib/plugin/auth.js +435 -0
  86. package/lib/plugin/autoDelay.js +8 -8
  87. package/lib/plugin/autoLogin.js +3 -338
  88. package/lib/plugin/commentStep.js +6 -1
  89. package/lib/plugin/coverage.js +10 -22
  90. package/lib/plugin/customLocator.js +3 -3
  91. package/lib/plugin/customReporter.js +52 -0
  92. package/lib/plugin/eachElement.js +1 -1
  93. package/lib/plugin/fakerTransform.js +1 -1
  94. package/lib/plugin/heal.js +36 -9
  95. package/lib/plugin/htmlReporter.js +1947 -0
  96. package/lib/plugin/pageInfo.js +140 -0
  97. package/lib/plugin/retryFailedStep.js +17 -18
  98. package/lib/plugin/retryTo.js +2 -113
  99. package/lib/plugin/screenshotOnFail.js +17 -58
  100. package/lib/plugin/selenoid.js +15 -35
  101. package/lib/plugin/standardActingHelpers.js +4 -1
  102. package/lib/plugin/stepByStepReport.js +56 -17
  103. package/lib/plugin/stepTimeout.js +5 -12
  104. package/lib/plugin/subtitles.js +4 -4
  105. package/lib/plugin/tryTo.js +3 -102
  106. package/lib/plugin/wdio.js +8 -10
  107. package/lib/recorder.js +155 -124
  108. package/lib/rerun.js +43 -42
  109. package/lib/result.js +161 -0
  110. package/lib/secret.js +1 -2
  111. package/lib/step/base.js +239 -0
  112. package/lib/step/comment.js +10 -0
  113. package/lib/step/config.js +50 -0
  114. package/lib/step/func.js +46 -0
  115. package/lib/step/helper.js +50 -0
  116. package/lib/step/meta.js +99 -0
  117. package/lib/step/record.js +74 -0
  118. package/lib/step/retry.js +11 -0
  119. package/lib/step/section.js +55 -0
  120. package/lib/step.js +21 -332
  121. package/lib/steps.js +50 -0
  122. package/lib/store.js +37 -5
  123. package/lib/template/heal.js +2 -11
  124. package/lib/test-server.js +323 -0
  125. package/lib/timeout.js +66 -0
  126. package/lib/utils.js +351 -218
  127. package/lib/within.js +75 -55
  128. package/lib/workerStorage.js +2 -1
  129. package/lib/workers.js +386 -277
  130. package/package.json +81 -75
  131. package/translations/de-DE.js +5 -3
  132. package/translations/fr-FR.js +5 -4
  133. package/translations/index.js +1 -0
  134. package/translations/it-IT.js +4 -3
  135. package/translations/ja-JP.js +4 -3
  136. package/translations/nl-NL.js +76 -0
  137. package/translations/pl-PL.js +4 -3
  138. package/translations/pt-BR.js +4 -3
  139. package/translations/ru-RU.js +4 -3
  140. package/translations/utils.js +9 -0
  141. package/translations/zh-CN.js +4 -3
  142. package/translations/zh-TW.js +4 -3
  143. package/typings/index.d.ts +197 -187
  144. package/typings/promiseBasedTypes.d.ts +53 -903
  145. package/typings/types.d.ts +372 -1042
  146. package/lib/cli.js +0 -257
  147. package/lib/helper/ExpectHelper.js +0 -391
  148. package/lib/helper/MockServer.js +0 -221
  149. package/lib/helper/SoftExpectHelper.js +0 -381
  150. package/lib/listener/artifacts.js +0 -19
  151. package/lib/listener/timeout.js +0 -109
  152. package/lib/mochaFactory.js +0 -113
  153. package/lib/plugin/debugErrors.js +0 -67
  154. package/lib/scenario.js +0 -224
  155. package/lib/ui.js +0 -236
@@ -2,7 +2,7 @@ module.exports.createValueEngine = () => {
2
2
  return {
3
3
  // Creates a selector that matches given target when queried at the root.
4
4
  // Can return undefined if unable to create one.
5
- // eslint-disable-next-line no-unused-vars
5
+
6
6
  create(root, target) {
7
7
  return null;
8
8
  },
@@ -29,7 +29,7 @@ module.exports.createDisabledEngine = () => {
29
29
  return {
30
30
  // Creates a selector that matches given target when queried at the root.
31
31
  // Can return undefined if unable to create one.
32
- // eslint-disable-next-line no-unused-vars
32
+
33
33
  create(root, target) {
34
34
  return null;
35
35
  },
@@ -2,66 +2,66 @@
2
2
  * Class to handle the interaction with the Dialog (Popup) Class from Puppeteer
3
3
  */
4
4
  class Popup {
5
- constructor(popup, defaultAction) {
6
- this._popup = popup || null;
7
- this._actionType = '';
8
- this._defaultAction = defaultAction || '';
5
+ constructor(popup = null, defaultAction = '') {
6
+ this._popup = popup
7
+ this._actionType = ''
8
+ this._defaultAction = defaultAction
9
9
  }
10
10
 
11
11
  _assertValidActionType(action) {
12
12
  if (['accept', 'cancel'].indexOf(action) === -1) {
13
- throw new Error('Invalid Popup action type. Only "accept" or "cancel" actions are accepted');
13
+ throw new Error('Invalid Popup action type. Only "accept" or "cancel" actions are accepted')
14
14
  }
15
15
  }
16
16
 
17
17
  set defaultAction(action) {
18
- this._assertValidActionType(action);
19
- this._defaultAction = action;
18
+ this._assertValidActionType(action)
19
+ this._defaultAction = action
20
20
  }
21
21
 
22
22
  get defaultAction() {
23
- return this._defaultAction;
23
+ return this._defaultAction
24
24
  }
25
25
 
26
26
  get popup() {
27
- return this._popup;
27
+ return this._popup
28
28
  }
29
29
 
30
30
  set popup(popup) {
31
31
  if (this._popup) {
32
- console.error('Popup already exists and was not closed. Popups must always be closed by calling either I.acceptPopup() or I.cancelPopup()');
32
+ return
33
33
  }
34
- this._popup = popup;
34
+ this._popup = popup
35
35
  }
36
36
 
37
37
  get actionType() {
38
- return this._actionType;
38
+ return this._actionType
39
39
  }
40
40
 
41
41
  set actionType(action) {
42
- this._assertValidActionType(action);
43
- this._actionType = action;
42
+ this._assertValidActionType(action)
43
+ this._actionType = action
44
44
  }
45
45
 
46
46
  clear() {
47
- this._popup = null;
48
- this._actionType = '';
47
+ this._popup = null
48
+ this._actionType = ''
49
49
  }
50
50
 
51
51
  assertPopupVisible() {
52
52
  if (!this._popup) {
53
- throw new Error('There is no Popup visible');
53
+ throw new Error('There is no Popup visible')
54
54
  }
55
55
  }
56
56
 
57
57
  assertPopupActionType(type) {
58
- this.assertPopupVisible();
59
- const expectedAction = this._actionType || this._defaultAction;
58
+ this.assertPopupVisible()
59
+ const expectedAction = this._actionType || this._defaultAction
60
60
  if (expectedAction !== type) {
61
- throw new Error(`Popup action does not fit the expected action type. Expected popup action to be '${expectedAction}' not '${type}`);
61
+ throw new Error(`Popup action does not fit the expected action type. Expected popup action to be '${expectedAction}' not '${type}`)
62
62
  }
63
- this.clear();
63
+ this.clear()
64
64
  }
65
65
  }
66
66
 
67
- module.exports = Popup;
67
+ module.exports = Popup
@@ -34,7 +34,7 @@ const extractQueryObjects = (queryString) => {
34
34
  queryParameters.forEach((queryParameter) => {
35
35
  const keyValue = queryParameter.split('=');
36
36
  const queryObject = {};
37
- // eslint-disable-next-line prefer-destructuring
37
+
38
38
  queryObject.name = keyValue[0];
39
39
  queryObject.value = decodeURIComponent(keyValue[1]);
40
40
  queryObjects.push(queryObject);
@@ -1,15 +1,15 @@
1
- const { ClientFunction } = require('testcafe');
1
+ const { ClientFunction } = require('testcafe')
2
2
 
3
- const assert = require('assert');
4
- const fs = require('fs');
5
- const path = require('path');
6
- const { getParamNames } = require('../../utils');
3
+ const assert = require('assert')
4
+ const fs = require('fs')
5
+ const path = require('path')
6
+ const { getParamNames } = require('../../utils')
7
7
 
8
8
  const createTestFile = () => {
9
- assert(global.output_dir, 'global.output_dir must be set');
9
+ assert(global.output_dir, 'global.output_dir must be set')
10
10
 
11
- const testFile = path.join(global.output_dir, `${Date.now()}_test.js`);
12
- const testControllerHolderDir = __dirname.replace(/\\/g, '/');
11
+ const testFile = path.join(global.output_dir, `${Date.now()}_test.js`)
12
+ const testControllerHolderDir = __dirname.replace(/\\/g, '/')
13
13
 
14
14
  fs.writeFileSync(
15
15
  testFile,
@@ -17,40 +17,39 @@ const createTestFile = () => {
17
17
  fixture("fixture")\n
18
18
  test\n
19
19
  ("test", testControllerHolder.capture)`,
20
- );
20
+ )
21
21
 
22
- return testFile;
23
- };
22
+ return testFile
23
+ }
24
24
 
25
25
  // TODO Better error mapping (actual, expected)
26
- const mapError = (testcafeError) => {
26
+ const mapError = testcafeError => {
27
27
  // console.log('TODO map error better', JSON.stringify(testcafeError, null, 2));
28
28
  if (testcafeError.errMsg) {
29
- throw new Error(testcafeError.errMsg);
29
+ throw new Error(testcafeError.errMsg)
30
30
  }
31
- const errorInfo = `${testcafeError.callsite ? JSON.stringify(testcafeError.callsite) : ''} ${testcafeError.apiFnChain || JSON.stringify(testcafeError)}`;
32
- throw new Error(`TestCafe Error: ${errorInfo}`);
33
- };
31
+ const errorInfo = `${testcafeError.callsite ? JSON.stringify(testcafeError.callsite) : ''} ${testcafeError.apiFnChain || JSON.stringify(testcafeError)}`
32
+ throw new Error(`TestCafe Error: ${errorInfo}`)
33
+ }
34
34
 
35
35
  function createClientFunction(func, args) {
36
36
  if (!args || !args.length) {
37
- return ClientFunction(func);
37
+ return ClientFunction(func)
38
38
  }
39
- const paramNames = getParamNames(func);
40
- const dependencies = {};
41
- paramNames.forEach((param, i) => dependencies[param] = args[i]);
39
+ const paramNames = getParamNames(func)
40
+ const dependencies = {}
41
+ paramNames.forEach((param, i) => (dependencies[param] = args[i]))
42
42
 
43
- return ClientFunction(getFuncBody(func), { dependencies });
43
+ return ClientFunction(getFuncBody(func), { dependencies })
44
44
  }
45
45
 
46
46
  function getFuncBody(func) {
47
- let fnStr = func.toString();
48
- const arrowIndex = fnStr.indexOf('=>');
47
+ let fnStr = func.toString()
48
+ const arrowIndex = fnStr.indexOf('=>')
49
49
  if (arrowIndex >= 0) {
50
- fnStr = fnStr.slice(arrowIndex + 2);
51
- // eslint-disable-next-line no-new-func
52
- // eslint-disable-next-line no-eval
53
- return eval(`() => ${fnStr}`);
50
+ fnStr = fnStr.slice(arrowIndex + 2)
51
+
52
+ return eval(`() => ${fnStr}`)
54
53
  }
55
54
  // TODO: support general functions
56
55
  }
@@ -59,4 +58,4 @@ module.exports = {
59
58
  createTestFile,
60
59
  mapError,
61
60
  createClientFunction,
62
- };
61
+ }
@@ -0,0 +1,55 @@
1
+ const figures = require('figures')
2
+ const Container = require('../container')
3
+ const event = require('../event')
4
+ const output = require('../output')
5
+ const { searchWithFusejs } = require('../utils')
6
+
7
+ module.exports = function () {
8
+ let isEmptyRun = true
9
+
10
+ event.dispatcher.on(event.test.before, test => {
11
+ isEmptyRun = false
12
+ })
13
+
14
+ event.dispatcher.on(event.all.result, () => {
15
+ if (isEmptyRun) {
16
+ const mocha = Container.mocha()
17
+
18
+ if (mocha.options.grep) {
19
+ output.print()
20
+ output.print('No tests found by pattern: ' + mocha.options.grep)
21
+
22
+ const allTests = []
23
+ mocha.suite.suites.forEach(suite => {
24
+ suite.tests.forEach(test => {
25
+ allTests.push(test.fullTitle())
26
+ })
27
+ })
28
+
29
+ const results = searchWithFusejs(allTests, mocha.options.grep.toString(), {
30
+ includeScore: true,
31
+ threshold: 0.6,
32
+ caseSensitive: false,
33
+ })
34
+
35
+ if (results.length > 0) {
36
+ output.print()
37
+ output.print('Maybe you wanted to run one of these tests?')
38
+ results.forEach(result => {
39
+ output.print(figures.checkboxOff, output.styles.log(result.item))
40
+ })
41
+
42
+ output.print()
43
+ output.print(output.styles.debug('To run the first test use the following command:'))
44
+ output.print(output.styles.bold('npx codeceptjs run --debug --grep "' + results[0].item + '"'))
45
+ }
46
+ }
47
+ if (process.env.CI && !process.env.DONT_FAIL_ON_EMPTY_RUN) {
48
+ output.print()
49
+ output.error('No tests were executed. Failing on CI to avoid false positives')
50
+ output.error('To disable this check, set `DONT_FAIL_ON_EMPTY_RUN` environment variable to true in CI config')
51
+ process.exitCode = 1
52
+ }
53
+ }
54
+ })
55
+ }
@@ -1,24 +1,21 @@
1
1
  const event = require('../event')
2
+ const debug = require('debug')('codeceptjs:exit')
2
3
 
3
4
  module.exports = function () {
4
5
  let failedTests = []
5
6
 
6
- event.dispatcher.on(event.test.failed, (testOrSuite) => {
7
- // NOTE When an error happens in one of the hooks (BeforeAll/BeforeEach...) the event object
8
- // is a suite and not a test
9
- const id = testOrSuite.uid || (testOrSuite.ctx && testOrSuite.ctx.test.uid) || 'empty'
7
+ event.dispatcher.on(event.test.failed, test => {
8
+ const id = test.uid || (test.ctx && test.ctx.test.uid) || 'empty'
10
9
  failedTests.push(id)
11
10
  })
12
11
 
13
12
  // if test was successful after retries
14
- event.dispatcher.on(event.test.passed, (testOrSuite) => {
15
- // NOTE When an error happens in one of the hooks (BeforeAll/BeforeEach...) the event object
16
- // is a suite and not a test
17
- const id = testOrSuite.uid || (testOrSuite.ctx && testOrSuite.ctx.test.uid) || 'empty'
18
- failedTests = failedTests.filter((failed) => id !== failed)
13
+ event.dispatcher.on(event.test.passed, test => {
14
+ const id = test.uid || (test.ctx && test.ctx.test.uid) || 'empty'
15
+ failedTests = failedTests.filter(failed => id !== failed)
19
16
  })
20
17
 
21
- process.on('beforeExit', (code) => {
18
+ process.on('beforeExit', code => {
22
19
  if (failedTests.length) {
23
20
  code = 1
24
21
  }
@@ -6,7 +6,7 @@ const { isNotSet } = require('../utils')
6
6
  const hooks = ['Before', 'After', 'BeforeSuite', 'AfterSuite']
7
7
 
8
8
  module.exports = function () {
9
- event.dispatcher.on(event.suite.before, (suite) => {
9
+ event.dispatcher.on(event.suite.before, suite => {
10
10
  let retryConfig = Config.get('retry')
11
11
  if (!retryConfig) return
12
12
 
@@ -28,8 +28,8 @@ module.exports = function () {
28
28
  }
29
29
 
30
30
  hooks
31
- .filter((hook) => !!config[hook])
32
- .forEach((hook) => {
31
+ .filter(hook => !!config[hook])
32
+ .forEach(hook => {
33
33
  if (isNotSet(suite.opts[`retry${hook}`])) suite.opts[`retry${hook}`] = config[hook]
34
34
  })
35
35
 
@@ -41,7 +41,7 @@ module.exports = function () {
41
41
  }
42
42
  })
43
43
 
44
- event.dispatcher.on(event.test.before, (test) => {
44
+ event.dispatcher.on(event.test.before, test => {
45
45
  let retryConfig = Config.get('retry')
46
46
  if (!retryConfig) return
47
47
 
@@ -54,7 +54,7 @@ module.exports = function () {
54
54
  retryConfig = [retryConfig]
55
55
  }
56
56
 
57
- retryConfig = retryConfig.filter((config) => !!config.Scenario)
57
+ retryConfig = retryConfig.filter(config => !!config.Scenario)
58
58
 
59
59
  for (const config of retryConfig) {
60
60
  if (config.grep) {
@@ -0,0 +1,165 @@
1
+ const event = require('../event')
2
+ const output = require('../output')
3
+ const recorder = require('../recorder')
4
+ const Config = require('../config')
5
+ const store = require('../store')
6
+ const debug = require('debug')('codeceptjs:timeout')
7
+ const { TIMEOUT_ORDER, TimeoutError, TestTimeoutError, StepTimeoutError } = require('../timeout')
8
+ const { BeforeSuiteHook, AfterSuiteHook } = require('../mocha/hooks')
9
+
10
+ module.exports = function () {
11
+ let timeout
12
+ let suiteTimeout = []
13
+ let currentTest
14
+ let currentTimeout
15
+
16
+ if (!store.timeouts) {
17
+ console.log('Timeouts were disabled')
18
+ return
19
+ }
20
+
21
+ // disable timeout for BeforeSuite/AfterSuite hooks
22
+ // add separate configs to them?
23
+ event.dispatcher.on(event.hook.started, hook => {
24
+ if (hook instanceof BeforeSuiteHook) {
25
+ timeout = null
26
+ suiteTimeout = []
27
+ }
28
+ if (hook instanceof AfterSuiteHook) {
29
+ timeout = null
30
+ suiteTimeout = []
31
+ }
32
+ })
33
+
34
+ event.dispatcher.on(event.suite.before, suite => {
35
+ suiteTimeout = []
36
+ let timeoutConfig = Config.get('timeout')
37
+
38
+ if (timeoutConfig) {
39
+ debug('config:', timeoutConfig)
40
+ if (!Number.isNaN(+timeoutConfig)) {
41
+ checkForSeconds(timeoutConfig)
42
+ suiteTimeout.push(timeoutConfig)
43
+ }
44
+
45
+ if (!Array.isArray(timeoutConfig)) {
46
+ timeoutConfig = [timeoutConfig]
47
+ }
48
+
49
+ for (const config of timeoutConfig.filter(c => !!c.Feature)) {
50
+ if (config.grep) {
51
+ if (!suite.title.includes(config.grep)) continue
52
+ }
53
+ suiteTimeout.push(config.Feature)
54
+ }
55
+ }
56
+
57
+ if (suite.totalTimeout) suiteTimeout.push(suite.totalTimeout)
58
+ output.log(`Timeouts: ${suiteTimeout}`)
59
+
60
+ if (suiteTimeout.length > 0) debug(suite.title, 'timeout', suiteTimeout)
61
+ })
62
+
63
+ event.dispatcher.on(event.test.before, test => {
64
+ currentTest = test
65
+ let testTimeout = null
66
+
67
+ let timeoutConfig = Config.get('timeout')
68
+
69
+ if (typeof timeoutConfig === 'object' || Array.isArray(timeoutConfig)) {
70
+ if (!Array.isArray(timeoutConfig)) {
71
+ timeoutConfig = [timeoutConfig]
72
+ }
73
+
74
+ for (const config of timeoutConfig.filter(c => !!c.Scenario)) {
75
+ console.log('Test Timeout', config, test.title.includes(config.grep))
76
+ if (config.grep) {
77
+ if (!test.title.includes(config.grep)) continue
78
+ }
79
+ testTimeout = config.Scenario
80
+ }
81
+ }
82
+
83
+ timeout = test.totalTimeout || testTimeout || suiteTimeout[suiteTimeout.length - 1]
84
+ if (!timeout) return
85
+
86
+ debug(test.title, 'timeout', {
87
+ 'config from file': testTimeout,
88
+ 'suite timeout': suiteTimeout,
89
+ 'dynamic config': test.totalTimeout,
90
+ })
91
+
92
+ currentTimeout = timeout
93
+ output.debug(`Test Timeout: ${timeout}s`)
94
+ timeout *= 1000
95
+ })
96
+
97
+ event.dispatcher.on(event.test.passed, test => {
98
+ currentTest = null
99
+ })
100
+
101
+ event.dispatcher.on(event.test.failed, test => {
102
+ currentTest = null
103
+ })
104
+
105
+ event.dispatcher.on(event.step.before, step => {
106
+ if (typeof timeout !== 'number') return
107
+
108
+ if (!store.timeouts) {
109
+ debug('step', step.toCode().trim(), 'timeout disabled')
110
+ return
111
+ }
112
+
113
+ if (timeout < 0) {
114
+ debug('Previous steps timed out, setting timeout to 0.01s')
115
+ step.setTimeout(0.01, TIMEOUT_ORDER.testOrSuite)
116
+ } else {
117
+ debug(`Setting timeout ${timeout}ms for step ${step.toCode().trim()}`)
118
+ step.setTimeout(timeout, TIMEOUT_ORDER.testOrSuite)
119
+ }
120
+ })
121
+
122
+ event.dispatcher.on(event.step.after, step => {
123
+ if (typeof timeout !== 'number') return
124
+ if (!store.timeouts) return
125
+
126
+ recorder.catchWithoutStop(err => {
127
+ // we wrap timeout errors in a StepTimeoutError
128
+ // but only if global timeout is set
129
+ // should we wrap all timeout errors?
130
+ if (err instanceof TimeoutError) {
131
+ const testTimeoutExceeded = timeout && +Date.now() - step.startTime >= timeout
132
+ debug('Step failed due to global test or suite timeout')
133
+ if (testTimeoutExceeded) {
134
+ debug('Test failed due to global test or suite timeout')
135
+ throw new TestTimeoutError(currentTimeout)
136
+ }
137
+ throw new StepTimeoutError(currentTimeout, step)
138
+ }
139
+ throw err
140
+ })
141
+ })
142
+
143
+ event.dispatcher.on(event.step.finished, step => {
144
+ if (!store.timeouts) {
145
+ debug('step', step.toCode().trim(), 'timeout disabled')
146
+ return
147
+ }
148
+
149
+ if (typeof timeout === 'number') debug('Timeout', timeout)
150
+
151
+ debug(`step ${step.toCode().trim()}:${step.status} duration`, step.duration)
152
+ if (typeof timeout === 'number' && !Number.isNaN(timeout)) timeout -= step.duration
153
+
154
+ if (typeof timeout === 'number' && timeout <= 0 && recorder.isRunning()) {
155
+ debug(`step ${step.toCode().trim()} timed out`)
156
+ recorder.throw(new TestTimeoutError(currentTimeout))
157
+ }
158
+ })
159
+ }
160
+
161
+ function checkForSeconds(timeout) {
162
+ if (timeout >= 1000) {
163
+ console.log(`Warning: Timeout was set to ${timeout}secs.\nGlobal timeout should be specified in seconds.`)
164
+ }
165
+ }
@@ -12,7 +12,7 @@ module.exports = function () {
12
12
 
13
13
  const runHelpersHook = (hook, param) => {
14
14
  if (store.dryRun) return
15
- Object.values(helpers).forEach((helper) => {
15
+ Object.values(helpers).forEach(helper => {
16
16
  if (helper[hook]) {
17
17
  helper[hook](param)
18
18
  }
@@ -21,55 +21,55 @@ module.exports = function () {
21
21
 
22
22
  const runAsyncHelpersHook = (hook, param, force) => {
23
23
  if (store.dryRun) return
24
- Object.keys(helpers).forEach((key) => {
24
+ Object.keys(helpers).forEach(key => {
25
25
  if (!helpers[key][hook]) return
26
26
  recorder.add(`hook ${key}.${hook}()`, () => helpers[key][hook](param), force, false)
27
27
  })
28
28
  }
29
29
 
30
- event.dispatcher.on(event.suite.before, (suite) => {
30
+ event.dispatcher.on(event.suite.before, suite => {
31
31
  // if (suite.parent) return; // only for root suite
32
32
  runAsyncHelpersHook('_beforeSuite', suite, true)
33
33
  })
34
34
 
35
- event.dispatcher.on(event.suite.after, (suite) => {
35
+ event.dispatcher.on(event.suite.after, suite => {
36
36
  // if (suite.parent) return; // only for root suite
37
37
  runAsyncHelpersHook('_afterSuite', suite, true)
38
38
  })
39
39
 
40
- event.dispatcher.on(event.test.started, (test) => {
40
+ event.dispatcher.on(event.test.started, test => {
41
41
  runHelpersHook('_test', test)
42
- recorder.catch((e) => error(e))
42
+ recorder.catch(e => error(e))
43
43
  })
44
44
 
45
- event.dispatcher.on(event.test.before, (test) => {
45
+ event.dispatcher.on(event.test.before, test => {
46
46
  // schedule config to revert changes
47
47
  runAsyncHelpersHook('_before', test, true)
48
- recorder.catchWithoutStop((e) => error(e))
48
+ recorder.catchWithoutStop(e => error(e))
49
49
  })
50
50
 
51
- event.dispatcher.on(event.test.passed, (test) => {
51
+ event.dispatcher.on(event.test.passed, test => {
52
52
  runAsyncHelpersHook('_passed', test, true)
53
53
  // should not fail test execution, so errors should be caught
54
- recorder.catchWithoutStop((e) => error(e))
54
+ recorder.catchWithoutStop(e => error(e))
55
55
  })
56
56
 
57
- event.dispatcher.on(event.test.failed, (test) => {
57
+ event.dispatcher.on(event.test.failed, test => {
58
58
  runAsyncHelpersHook('_failed', test, true)
59
59
  // should not fail test execution, so errors should be caught
60
- recorder.catchWithoutStop((e) => error(e))
60
+ recorder.catchWithoutStop(e => error(e))
61
61
  })
62
62
 
63
63
  event.dispatcher.on(event.test.after, () => {
64
64
  runAsyncHelpersHook('_after', {}, true)
65
- recorder.catchWithoutStop((e) => error(e))
65
+ recorder.catchWithoutStop(e => error(e))
66
66
  })
67
67
 
68
- event.dispatcher.on(event.step.before, (step) => {
68
+ event.dispatcher.on(event.step.before, step => {
69
69
  runAsyncHelpersHook('_beforeStep', step)
70
70
  })
71
71
 
72
- event.dispatcher.on(event.step.after, (step) => {
72
+ event.dispatcher.on(event.step.after, step => {
73
73
  runAsyncHelpersHook('_afterStep', step)
74
74
  })
75
75
 
@@ -8,7 +8,7 @@ module.exports = function () {
8
8
  mocha = container.mocha()
9
9
  })
10
10
 
11
- event.dispatcher.on(event.test.passed, (test) => {
11
+ event.dispatcher.on(event.test.passed, test => {
12
12
  mocha.Runner.emit('pass', test)
13
13
  })
14
14
 
@@ -0,0 +1,12 @@
1
+ const event = require('../event')
2
+ const container = require('../container')
3
+
4
+ module.exports = function () {
5
+ event.dispatcher.on(event.hook.failed, err => {
6
+ container.result().addStats({ failedHooks: 1 })
7
+ })
8
+
9
+ event.dispatcher.on(event.test.before, test => {
10
+ container.result().addTest(test)
11
+ })
12
+ }