codeceptjs 4.0.0-beta.1 → 4.0.0-beta.11.esm-aria

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 (207) hide show
  1. package/README.md +133 -120
  2. package/bin/codecept.js +107 -96
  3. package/bin/test-server.js +64 -0
  4. package/docs/webapi/clearCookie.mustache +1 -1
  5. package/docs/webapi/click.mustache +5 -1
  6. package/lib/actor.js +71 -103
  7. package/lib/ai.js +159 -188
  8. package/lib/assert/empty.js +22 -24
  9. package/lib/assert/equal.js +30 -37
  10. package/lib/assert/error.js +14 -14
  11. package/lib/assert/include.js +43 -48
  12. package/lib/assert/throws.js +11 -11
  13. package/lib/assert/truth.js +22 -22
  14. package/lib/assert.js +20 -18
  15. package/lib/codecept.js +238 -162
  16. package/lib/colorUtils.js +50 -52
  17. package/lib/command/check.js +206 -0
  18. package/lib/command/configMigrate.js +56 -51
  19. package/lib/command/definitions.js +96 -109
  20. package/lib/command/dryRun.js +77 -79
  21. package/lib/command/generate.js +234 -194
  22. package/lib/command/gherkin/init.js +42 -33
  23. package/lib/command/gherkin/snippets.js +76 -74
  24. package/lib/command/gherkin/steps.js +20 -17
  25. package/lib/command/info.js +74 -38
  26. package/lib/command/init.js +300 -290
  27. package/lib/command/interactive.js +41 -32
  28. package/lib/command/list.js +28 -27
  29. package/lib/command/run-multiple/chunk.js +51 -48
  30. package/lib/command/run-multiple/collection.js +5 -5
  31. package/lib/command/run-multiple/run.js +5 -1
  32. package/lib/command/run-multiple.js +97 -97
  33. package/lib/command/run-rerun.js +19 -25
  34. package/lib/command/run-workers.js +68 -92
  35. package/lib/command/run.js +39 -27
  36. package/lib/command/utils.js +80 -64
  37. package/lib/command/workers/runTests.js +388 -226
  38. package/lib/config.js +124 -50
  39. package/lib/container.js +765 -260
  40. package/lib/data/context.js +60 -61
  41. package/lib/data/dataScenarioConfig.js +47 -47
  42. package/lib/data/dataTableArgument.js +32 -32
  43. package/lib/data/table.js +22 -22
  44. package/lib/effects.js +307 -0
  45. package/lib/element/WebElement.js +327 -0
  46. package/lib/els.js +160 -0
  47. package/lib/event.js +173 -163
  48. package/lib/globals.js +141 -0
  49. package/lib/heal.js +89 -85
  50. package/lib/helper/AI.js +131 -41
  51. package/lib/helper/ApiDataFactory.js +107 -75
  52. package/lib/helper/Appium.js +542 -404
  53. package/lib/helper/FileSystem.js +100 -79
  54. package/lib/helper/GraphQL.js +44 -43
  55. package/lib/helper/GraphQLDataFactory.js +52 -52
  56. package/lib/helper/JSONResponse.js +126 -88
  57. package/lib/helper/Mochawesome.js +54 -29
  58. package/lib/helper/Playwright.js +2547 -1316
  59. package/lib/helper/Puppeteer.js +1578 -1181
  60. package/lib/helper/REST.js +209 -68
  61. package/lib/helper/WebDriver.js +1482 -1342
  62. package/lib/helper/errors/ConnectionRefused.js +6 -6
  63. package/lib/helper/errors/ElementAssertion.js +11 -16
  64. package/lib/helper/errors/ElementNotFound.js +5 -9
  65. package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
  66. package/lib/helper/extras/Console.js +11 -11
  67. package/lib/helper/extras/PlaywrightLocator.js +110 -0
  68. package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
  69. package/lib/helper/extras/PlaywrightReactVueLocator.js +17 -8
  70. package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
  71. package/lib/helper/extras/Popup.js +22 -22
  72. package/lib/helper/extras/React.js +27 -28
  73. package/lib/helper/network/actions.js +36 -42
  74. package/lib/helper/network/utils.js +78 -84
  75. package/lib/helper/scripts/blurElement.js +5 -5
  76. package/lib/helper/scripts/focusElement.js +5 -5
  77. package/lib/helper/scripts/highlightElement.js +8 -8
  78. package/lib/helper/scripts/isElementClickable.js +34 -34
  79. package/lib/helper.js +2 -3
  80. package/lib/history.js +23 -19
  81. package/lib/hooks.js +8 -8
  82. package/lib/html.js +94 -104
  83. package/lib/index.js +38 -27
  84. package/lib/listener/config.js +30 -23
  85. package/lib/listener/emptyRun.js +54 -0
  86. package/lib/listener/enhancedGlobalRetry.js +110 -0
  87. package/lib/listener/exit.js +16 -18
  88. package/lib/listener/globalRetry.js +70 -0
  89. package/lib/listener/globalTimeout.js +181 -0
  90. package/lib/listener/helpers.js +76 -51
  91. package/lib/listener/mocha.js +10 -11
  92. package/lib/listener/result.js +11 -0
  93. package/lib/listener/retryEnhancer.js +85 -0
  94. package/lib/listener/steps.js +71 -59
  95. package/lib/listener/store.js +20 -0
  96. package/lib/locator.js +214 -197
  97. package/lib/mocha/asyncWrapper.js +274 -0
  98. package/lib/mocha/bdd.js +167 -0
  99. package/lib/mocha/cli.js +341 -0
  100. package/lib/mocha/factory.js +163 -0
  101. package/lib/mocha/featureConfig.js +89 -0
  102. package/lib/mocha/gherkin.js +231 -0
  103. package/lib/mocha/hooks.js +121 -0
  104. package/lib/mocha/index.js +21 -0
  105. package/lib/mocha/inject.js +46 -0
  106. package/lib/{interfaces → mocha}/scenarioConfig.js +58 -34
  107. package/lib/mocha/suite.js +89 -0
  108. package/lib/mocha/test.js +184 -0
  109. package/lib/mocha/types.d.ts +42 -0
  110. package/lib/mocha/ui.js +242 -0
  111. package/lib/output.js +141 -71
  112. package/lib/parser.js +47 -44
  113. package/lib/pause.js +173 -145
  114. package/lib/plugin/analyze.js +403 -0
  115. package/lib/plugin/{autoLogin.js → auth.js} +178 -79
  116. package/lib/plugin/autoDelay.js +36 -40
  117. package/lib/plugin/coverage.js +131 -78
  118. package/lib/plugin/customLocator.js +22 -21
  119. package/lib/plugin/customReporter.js +53 -0
  120. package/lib/plugin/enhancedRetryFailedStep.js +99 -0
  121. package/lib/plugin/heal.js +101 -110
  122. package/lib/plugin/htmlReporter.js +3648 -0
  123. package/lib/plugin/pageInfo.js +140 -0
  124. package/lib/plugin/pauseOnFail.js +12 -11
  125. package/lib/plugin/retryFailedStep.js +82 -47
  126. package/lib/plugin/screenshotOnFail.js +111 -92
  127. package/lib/plugin/stepByStepReport.js +159 -101
  128. package/lib/plugin/stepTimeout.js +20 -25
  129. package/lib/plugin/subtitles.js +38 -38
  130. package/lib/recorder.js +193 -130
  131. package/lib/rerun.js +94 -49
  132. package/lib/result.js +238 -0
  133. package/lib/retryCoordinator.js +207 -0
  134. package/lib/secret.js +20 -18
  135. package/lib/session.js +95 -89
  136. package/lib/step/base.js +239 -0
  137. package/lib/step/comment.js +10 -0
  138. package/lib/step/config.js +50 -0
  139. package/lib/step/func.js +46 -0
  140. package/lib/step/helper.js +50 -0
  141. package/lib/step/meta.js +99 -0
  142. package/lib/step/record.js +74 -0
  143. package/lib/step/retry.js +11 -0
  144. package/lib/step/section.js +55 -0
  145. package/lib/step.js +18 -329
  146. package/lib/steps.js +54 -0
  147. package/lib/store.js +38 -7
  148. package/lib/template/heal.js +3 -12
  149. package/lib/template/prompts/generatePageObject.js +31 -0
  150. package/lib/template/prompts/healStep.js +13 -0
  151. package/lib/template/prompts/writeStep.js +9 -0
  152. package/lib/test-server.js +334 -0
  153. package/lib/timeout.js +60 -0
  154. package/lib/transform.js +8 -8
  155. package/lib/translation.js +34 -21
  156. package/lib/utils/mask_data.js +47 -0
  157. package/lib/utils.js +411 -228
  158. package/lib/workerStorage.js +37 -34
  159. package/lib/workers.js +532 -296
  160. package/package.json +115 -95
  161. package/translations/de-DE.js +5 -3
  162. package/translations/fr-FR.js +5 -4
  163. package/translations/index.js +22 -12
  164. package/translations/it-IT.js +4 -3
  165. package/translations/ja-JP.js +4 -3
  166. package/translations/nl-NL.js +76 -0
  167. package/translations/pl-PL.js +4 -3
  168. package/translations/pt-BR.js +4 -3
  169. package/translations/ru-RU.js +4 -3
  170. package/translations/utils.js +10 -0
  171. package/translations/zh-CN.js +4 -3
  172. package/translations/zh-TW.js +4 -3
  173. package/typings/index.d.ts +546 -185
  174. package/typings/promiseBasedTypes.d.ts +150 -879
  175. package/typings/types.d.ts +547 -996
  176. package/lib/cli.js +0 -249
  177. package/lib/dirname.js +0 -5
  178. package/lib/helper/Expect.js +0 -425
  179. package/lib/helper/ExpectHelper.js +0 -399
  180. package/lib/helper/MockServer.js +0 -223
  181. package/lib/helper/Nightmare.js +0 -1411
  182. package/lib/helper/Protractor.js +0 -1835
  183. package/lib/helper/SoftExpectHelper.js +0 -381
  184. package/lib/helper/TestCafe.js +0 -1410
  185. package/lib/helper/clientscripts/nightmare.js +0 -213
  186. package/lib/helper/testcafe/testControllerHolder.js +0 -42
  187. package/lib/helper/testcafe/testcafe-utils.js +0 -63
  188. package/lib/interfaces/bdd.js +0 -98
  189. package/lib/interfaces/featureConfig.js +0 -69
  190. package/lib/interfaces/gherkin.js +0 -195
  191. package/lib/listener/artifacts.js +0 -19
  192. package/lib/listener/retry.js +0 -68
  193. package/lib/listener/timeout.js +0 -109
  194. package/lib/mochaFactory.js +0 -110
  195. package/lib/plugin/allure.js +0 -15
  196. package/lib/plugin/commentStep.js +0 -136
  197. package/lib/plugin/debugErrors.js +0 -67
  198. package/lib/plugin/eachElement.js +0 -127
  199. package/lib/plugin/fakerTransform.js +0 -49
  200. package/lib/plugin/retryTo.js +0 -121
  201. package/lib/plugin/selenoid.js +0 -371
  202. package/lib/plugin/standardActingHelpers.js +0 -9
  203. package/lib/plugin/tryTo.js +0 -105
  204. package/lib/plugin/wdio.js +0 -246
  205. package/lib/scenario.js +0 -222
  206. package/lib/ui.js +0 -238
  207. package/lib/within.js +0 -70
@@ -0,0 +1,110 @@
1
+ import event from '../event.js'
2
+ import output from '../output.js'
3
+ import Config from '../config.js'
4
+ import { isNotSet } from '../utils.js'
5
+
6
+ const hooks = ['Before', 'After', 'BeforeSuite', 'AfterSuite']
7
+
8
+ /**
9
+ * Priority levels for retry mechanisms (higher number = higher priority)
10
+ * This ensures consistent behavior when multiple retry mechanisms are active
11
+ */
12
+ const RETRY_PRIORITIES = {
13
+ MANUAL_STEP: 100, // I.retry() or step.retry() - highest priority
14
+ STEP_PLUGIN: 50, // retryFailedStep plugin
15
+ SCENARIO_CONFIG: 30, // Global scenario retry config
16
+ FEATURE_CONFIG: 20, // Global feature retry config
17
+ HOOK_CONFIG: 10, // Hook retry config - lowest priority
18
+ }
19
+
20
+ /**
21
+ * Enhanced global retry mechanism that coordinates with other retry types
22
+ */
23
+ export default function () {
24
+ event.dispatcher.on(event.suite.before, suite => {
25
+ let retryConfig = Config.get('retry')
26
+ if (!retryConfig) return
27
+
28
+ if (Number.isInteger(+retryConfig)) {
29
+ // is number - apply as feature-level retry
30
+ const retryNum = +retryConfig
31
+ output.log(`[Global Retry] Feature retries: ${retryNum}`)
32
+
33
+ // Only set if not already set by higher priority mechanism
34
+ if (isNotSet(suite.retries())) {
35
+ suite.retries(retryNum)
36
+ suite.opts.retryPriority = RETRY_PRIORITIES.FEATURE_CONFIG
37
+ }
38
+ return
39
+ }
40
+
41
+ if (!Array.isArray(retryConfig)) {
42
+ retryConfig = [retryConfig]
43
+ }
44
+
45
+ for (const config of retryConfig) {
46
+ if (config.grep) {
47
+ if (!suite.title.includes(config.grep)) continue
48
+ }
49
+
50
+ // Handle hook retries with priority awareness
51
+ hooks
52
+ .filter(hook => !!config[hook])
53
+ .forEach(hook => {
54
+ const retryKey = `retry${hook}`
55
+ if (isNotSet(suite.opts[retryKey])) {
56
+ suite.opts[retryKey] = config[hook]
57
+ suite.opts[`${retryKey}Priority`] = RETRY_PRIORITIES.HOOK_CONFIG
58
+ }
59
+ })
60
+
61
+ // Handle feature-level retries
62
+ if (config.Feature) {
63
+ if (isNotSet(suite.retries()) || (suite.opts.retryPriority || 0) <= RETRY_PRIORITIES.FEATURE_CONFIG) {
64
+ suite.retries(config.Feature)
65
+ suite.opts.retryPriority = RETRY_PRIORITIES.FEATURE_CONFIG
66
+ output.log(`[Global Retry] Feature retries: ${config.Feature}`)
67
+ }
68
+ }
69
+ }
70
+ })
71
+
72
+ event.dispatcher.on(event.test.before, test => {
73
+ let retryConfig = Config.get('retry')
74
+ if (!retryConfig) return
75
+
76
+ if (Number.isInteger(+retryConfig)) {
77
+ // Only set if not already set by higher priority mechanism
78
+ if (test.retries() === -1) {
79
+ test.retries(retryConfig)
80
+ test.opts.retryPriority = RETRY_PRIORITIES.SCENARIO_CONFIG
81
+ output.log(`[Global Retry] Scenario retries: ${retryConfig}`)
82
+ }
83
+ return
84
+ }
85
+
86
+ if (!Array.isArray(retryConfig)) {
87
+ retryConfig = [retryConfig]
88
+ }
89
+
90
+ retryConfig = retryConfig.filter(config => !!config.Scenario)
91
+
92
+ for (const config of retryConfig) {
93
+ if (config.grep) {
94
+ if (!test.fullTitle().includes(config.grep)) continue
95
+ }
96
+
97
+ if (config.Scenario) {
98
+ // Respect priority system
99
+ if (test.retries() === -1 || (test.opts.retryPriority || 0) <= RETRY_PRIORITIES.SCENARIO_CONFIG) {
100
+ test.retries(config.Scenario)
101
+ test.opts.retryPriority = RETRY_PRIORITIES.SCENARIO_CONFIG
102
+ output.log(`[Global Retry] Scenario retries: ${config.Scenario}`)
103
+ }
104
+ }
105
+ }
106
+ })
107
+ }
108
+
109
+ // Export priority constants for use by other retry mechanisms
110
+ export { RETRY_PRIORITIES }
@@ -1,30 +1,28 @@
1
- import * as event from '../event.js';
1
+ import event from '../event.js'
2
+ import debugModule from 'debug'
3
+ const debug = debugModule('codeceptjs:exit')
2
4
 
3
5
  export default function () {
4
- let failedTests = [];
6
+ let failedTests = []
5
7
 
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';
10
- failedTests.push(id);
11
- });
8
+ event.dispatcher.on(event.test.failed, test => {
9
+ const id = test.uid || (test.ctx && test.ctx.test.uid) || 'empty'
10
+ failedTests.push(id)
11
+ })
12
12
 
13
13
  // 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);
19
- });
14
+ event.dispatcher.on(event.test.passed, test => {
15
+ const id = test.uid || (test.ctx && test.ctx.test.uid) || 'empty'
16
+ failedTests = failedTests.filter(failed => id !== failed)
17
+ })
20
18
 
21
- process.on('beforeExit', (code) => {
19
+ process.on('beforeExit', code => {
22
20
  if (failedTests.length) {
23
- code = 1;
21
+ code = 1
24
22
  }
25
23
 
26
24
  if (code) {
27
- process.exit(code);
25
+ process.exit(code)
28
26
  }
29
- });
27
+ })
30
28
  }
@@ -0,0 +1,70 @@
1
+ import event from '../event.js'
2
+ import output from '../output.js'
3
+ import Config from '../config.js'
4
+ import { isNotSet } from '../utils.js'
5
+
6
+ const hooks = ['Before', 'After', 'BeforeSuite', 'AfterSuite']
7
+
8
+ export default function () {
9
+ event.dispatcher.on(event.suite.before, suite => {
10
+ let retryConfig = Config.get('retry')
11
+ if (!retryConfig) return
12
+
13
+ if (Number.isInteger(+retryConfig)) {
14
+ // is number
15
+ const retryNum = +retryConfig
16
+ output.log(`Retries: ${retryNum}`)
17
+ suite.retries(retryNum)
18
+ return
19
+ }
20
+
21
+ if (!Array.isArray(retryConfig)) {
22
+ retryConfig = [retryConfig]
23
+ }
24
+
25
+ for (const config of retryConfig) {
26
+ if (config.grep) {
27
+ if (!suite.title.includes(config.grep)) continue
28
+ }
29
+
30
+ hooks
31
+ .filter(hook => !!config[hook])
32
+ .forEach(hook => {
33
+ if (isNotSet(suite.opts[`retry${hook}`])) suite.opts[`retry${hook}`] = config[hook]
34
+ })
35
+
36
+ if (config.Feature) {
37
+ if (isNotSet(suite.retries())) suite.retries(config.Feature)
38
+ }
39
+
40
+ output.log(`Retries: ${JSON.stringify(config)}`)
41
+ }
42
+ })
43
+
44
+ event.dispatcher.on(event.test.before, test => {
45
+ let retryConfig = Config.get('retry')
46
+ if (!retryConfig) return
47
+
48
+ if (Number.isInteger(+retryConfig)) {
49
+ if (test.retries() === -1) test.retries(retryConfig)
50
+ return
51
+ }
52
+
53
+ if (!Array.isArray(retryConfig)) {
54
+ retryConfig = [retryConfig]
55
+ }
56
+
57
+ retryConfig = retryConfig.filter(config => !!config.Scenario)
58
+
59
+ for (const config of retryConfig) {
60
+ if (config.grep) {
61
+ if (!test.fullTitle().includes(config.grep)) continue
62
+ }
63
+
64
+ if (config.Scenario) {
65
+ if (test.retries() === -1) test.retries(config.Scenario)
66
+ output.log(`Retries: ${config.Scenario}`)
67
+ }
68
+ }
69
+ })
70
+ }
@@ -0,0 +1,181 @@
1
+ import event from '../event.js'
2
+ import output from '../output.js'
3
+ import recorder from '../recorder.js'
4
+ import Config from '../config.js'
5
+ import store from '../store.js'
6
+ import debugModule from 'debug'
7
+ const debug = debugModule('codeceptjs:timeout')
8
+ import { TIMEOUT_ORDER, TimeoutError, TestTimeoutError, StepTimeoutError } from '../timeout.js'
9
+ import { BeforeSuiteHook, AfterSuiteHook } from '../mocha/hooks.js'
10
+
11
+ export default function () {
12
+ let timeout
13
+ let suiteTimeout = []
14
+ let currentTest
15
+ let currentTimeout
16
+
17
+ if (!store.timeouts) {
18
+ console.log('Timeouts were disabled')
19
+ return
20
+ }
21
+
22
+ // disable timeout for BeforeSuite/AfterSuite hooks
23
+ // add separate configs to them?
24
+ // When a BeforeSuite/AfterSuite hook starts we want to disable the
25
+ // per-test timeout during that hook execution only. Previously the
26
+ // code cleared `suiteTimeout` permanently which caused the suite
27
+ // level timeout to be lost for subsequent tests. Save previous
28
+ // values and restore them when the hook finishes.
29
+ let __prevTimeout = undefined
30
+ let __prevSuiteTimeout = undefined
31
+
32
+ event.dispatcher.on(event.hook.started, hook => {
33
+ if (hook instanceof BeforeSuiteHook || hook instanceof AfterSuiteHook) {
34
+ __prevTimeout = timeout
35
+ // copy array to preserve original values
36
+ __prevSuiteTimeout = suiteTimeout.slice()
37
+ timeout = null
38
+ suiteTimeout = []
39
+ }
40
+ })
41
+
42
+ event.dispatcher.on(event.hook.finished, hook => {
43
+ if (hook instanceof BeforeSuiteHook || hook instanceof AfterSuiteHook) {
44
+ // restore previously stored values
45
+ timeout = __prevTimeout
46
+ suiteTimeout = __prevSuiteTimeout.slice()
47
+ }
48
+ })
49
+
50
+ event.dispatcher.on(event.suite.before, suite => {
51
+ suiteTimeout = []
52
+ let timeoutConfig = Config.get('timeout')
53
+
54
+ if (timeoutConfig) {
55
+ debug('config:', timeoutConfig)
56
+ if (!Number.isNaN(+timeoutConfig)) {
57
+ checkForSeconds(timeoutConfig)
58
+ suiteTimeout.push(timeoutConfig)
59
+ }
60
+
61
+ if (!Array.isArray(timeoutConfig)) {
62
+ timeoutConfig = [timeoutConfig]
63
+ }
64
+
65
+ for (const config of timeoutConfig.filter(c => !!c.Feature)) {
66
+ if (config.grep) {
67
+ if (!suite.title.includes(config.grep)) continue
68
+ }
69
+ suiteTimeout.push(config.Feature)
70
+ }
71
+ }
72
+
73
+ if (suite.totalTimeout) suiteTimeout.push(suite.totalTimeout)
74
+ output.log(`Timeouts: ${suiteTimeout}`)
75
+
76
+ if (suiteTimeout.length > 0) debug(suite.title, 'timeout', suiteTimeout)
77
+ })
78
+
79
+ event.dispatcher.on(event.test.before, test => {
80
+ currentTest = test
81
+ let testTimeout = null
82
+
83
+ let timeoutConfig = Config.get('timeout')
84
+
85
+ if (typeof timeoutConfig === 'object' || Array.isArray(timeoutConfig)) {
86
+ if (!Array.isArray(timeoutConfig)) {
87
+ timeoutConfig = [timeoutConfig]
88
+ }
89
+
90
+ for (const config of timeoutConfig.filter(c => !!c.Scenario)) {
91
+ console.log('Test Timeout', config, test.title.includes(config.grep))
92
+ if (config.grep) {
93
+ if (!test.title.includes(config.grep)) continue
94
+ }
95
+ testTimeout = config.Scenario
96
+ }
97
+ }
98
+
99
+ timeout = test.totalTimeout || testTimeout || suiteTimeout[suiteTimeout.length - 1]
100
+ if (!timeout) return
101
+
102
+ debug(test.title, 'timeout', {
103
+ 'config from file': testTimeout,
104
+ 'suite timeout': suiteTimeout,
105
+ 'dynamic config': test.totalTimeout,
106
+ })
107
+
108
+ currentTimeout = timeout
109
+ output.debug(`Test Timeout: ${timeout}s`)
110
+ timeout *= 1000
111
+ })
112
+
113
+ event.dispatcher.on(event.test.passed, test => {
114
+ currentTest = null
115
+ })
116
+
117
+ event.dispatcher.on(event.test.failed, test => {
118
+ currentTest = null
119
+ })
120
+
121
+ event.dispatcher.on(event.step.before, step => {
122
+ if (typeof timeout !== 'number') return
123
+
124
+ if (!store.timeouts) {
125
+ debug('step', step.toCode().trim(), 'timeout disabled')
126
+ return
127
+ }
128
+
129
+ if (timeout < 0) {
130
+ debug('Previous steps timed out, setting timeout to 0.01s')
131
+ step.setTimeout(0.01, TIMEOUT_ORDER.testOrSuite)
132
+ } else {
133
+ debug(`Setting timeout ${timeout}ms for step ${step.toCode().trim()}`)
134
+ step.setTimeout(timeout, TIMEOUT_ORDER.testOrSuite)
135
+ }
136
+ })
137
+
138
+ event.dispatcher.on(event.step.after, step => {
139
+ if (typeof timeout !== 'number') return
140
+ if (!store.timeouts) return
141
+
142
+ recorder.catchWithoutStop(err => {
143
+ // we wrap timeout errors in a StepTimeoutError
144
+ // but only if global timeout is set
145
+ // should we wrap all timeout errors?
146
+ if (err instanceof TimeoutError) {
147
+ const testTimeoutExceeded = timeout && +Date.now() - step.startTime >= timeout
148
+ debug('Step failed due to global test or suite timeout')
149
+ if (testTimeoutExceeded) {
150
+ debug('Test failed due to global test or suite timeout')
151
+ throw new TestTimeoutError(currentTimeout)
152
+ }
153
+ throw new StepTimeoutError(currentTimeout, step)
154
+ }
155
+ throw err
156
+ })
157
+ })
158
+
159
+ event.dispatcher.on(event.step.finished, step => {
160
+ if (!store.timeouts) {
161
+ debug('step', step.toCode().trim(), 'timeout disabled')
162
+ return
163
+ }
164
+
165
+ if (typeof timeout === 'number') debug('Timeout', timeout)
166
+
167
+ debug(`step ${step.toCode().trim()}:${step.status} duration`, step.duration)
168
+ if (typeof timeout === 'number' && !Number.isNaN(timeout)) timeout -= step.duration
169
+
170
+ if (typeof timeout === 'number' && timeout <= 0 && recorder.isRunning()) {
171
+ debug(`step ${step.toCode().trim()} timed out`)
172
+ recorder.throw(new TestTimeoutError(currentTimeout))
173
+ }
174
+ })
175
+ }
176
+
177
+ function checkForSeconds(timeout) {
178
+ if (timeout >= 1000) {
179
+ console.log(`Warning: Timeout was set to ${timeout}secs.\nGlobal timeout should be specified in seconds.`)
180
+ }
181
+ }
@@ -1,79 +1,104 @@
1
- import * as event from '../event.js';
2
- import container from '../container.js';
3
- import recorder from '../recorder.js';
4
- import { store } from '../store.js';
5
- import * as output from '../output.js';
6
-
1
+ import path from 'path'
2
+ import event from '../event.js'
3
+ import recorder from '../recorder.js'
4
+ import store from '../store.js'
5
+ import output from '../output.js'
7
6
  /**
8
7
  * Enable Helpers to listen to test events
9
8
  */
10
9
  export default function () {
11
- const helpers = container.helpers();
10
+ const helpers = global.container.helpers()
12
11
 
13
12
  const runHelpersHook = (hook, param) => {
14
- if (store.dryRun) return;
15
- Object.values(helpers).forEach((helper) => {
13
+ if (store.dryRun) return
14
+ Object.values(helpers).forEach(helper => {
16
15
  if (helper[hook]) {
17
- helper[hook](param);
16
+ helper[hook](param)
18
17
  }
19
- });
20
- };
18
+ })
19
+ }
21
20
 
22
21
  const runAsyncHelpersHook = (hook, param, force) => {
23
- if (store.dryRun) return;
24
- Object.keys(helpers).forEach((key) => {
25
- if (!helpers[key][hook]) return;
26
- recorder.add(`hook ${key}.${hook}()`, () => helpers[key][hook](param), force, false);
27
- });
28
- };
22
+ if (store.dryRun) return
23
+ Object.keys(helpers).forEach(key => {
24
+ if (!helpers[key][hook]) return
25
+ recorder.add(`hook ${key}.${hook}()`, () => helpers[key][hook](param), force, false)
26
+ })
27
+ }
29
28
 
30
- event.dispatcher.on(event.suite.before, (suite) => {
29
+ event.dispatcher.on(event.suite.before, suite => {
31
30
  // if (suite.parent) return; // only for root suite
32
- runAsyncHelpersHook('_beforeSuite', suite, true);
33
- });
31
+ runAsyncHelpersHook('_beforeSuite', suite, true)
32
+ })
34
33
 
35
- event.dispatcher.on(event.suite.after, (suite) => {
34
+ event.dispatcher.on(event.suite.after, suite => {
36
35
  // if (suite.parent) return; // only for root suite
37
- runAsyncHelpersHook('_afterSuite', suite, true);
38
- });
36
+ runAsyncHelpersHook('_afterSuite', suite, true)
37
+ })
39
38
 
40
- event.dispatcher.on(event.test.started, (test) => {
41
- runHelpersHook('_test', test);
42
- recorder.catch(e => output.output.error(e));
43
- });
39
+ event.dispatcher.on(event.test.started, test => {
40
+ runHelpersHook('_test', test)
41
+ recorder.catch(e => output.error(e))
42
+ })
44
43
 
45
- event.dispatcher.on(event.test.before, (test) => {
44
+ event.dispatcher.on(event.test.before, test => {
46
45
  // schedule config to revert changes
47
- runAsyncHelpersHook('_before', test, true);
48
- recorder.catchWithoutStop(e => output.output.error(e));
49
- });
46
+ runAsyncHelpersHook('_before', test, true)
47
+ recorder.catchWithoutStop(e => output.error(e))
48
+ })
50
49
 
51
- event.dispatcher.on(event.test.passed, (test) => {
52
- runAsyncHelpersHook('_passed', test, true);
50
+ event.dispatcher.on(event.test.passed, test => {
51
+ runAsyncHelpersHook('_passed', test, true)
53
52
  // should not fail test execution, so errors should be caught
54
- recorder.catchWithoutStop(e => output.output.error(e));
55
- });
53
+ recorder.catchWithoutStop(e => output.error(e))
54
+ })
56
55
 
57
- event.dispatcher.on(event.test.failed, (test) => {
58
- runAsyncHelpersHook('_failed', test, true);
56
+ event.dispatcher.on(event.test.failed, test => {
57
+ runAsyncHelpersHook('_failed', test, true)
59
58
  // should not fail test execution, so errors should be caught
60
- recorder.catchWithoutStop(e => output.output.error(e));
61
- });
59
+ recorder.catchWithoutStop(e => output.error(e))
60
+ })
62
61
 
63
62
  event.dispatcher.on(event.test.after, () => {
64
- runAsyncHelpersHook('_after', {}, true);
65
- recorder.catchWithoutStop(e => output.output.error(e));
66
- });
63
+ runAsyncHelpersHook('_after', {}, true)
64
+ recorder.catchWithoutStop(e => output.error(e))
65
+ })
67
66
 
68
- event.dispatcher.on(event.step.before, (step) => {
69
- runAsyncHelpersHook('_beforeStep', step);
70
- });
67
+ event.dispatcher.on(event.step.before, step => {
68
+ runAsyncHelpersHook('_beforeStep', step)
69
+ })
71
70
 
72
- event.dispatcher.on(event.step.after, (step) => {
73
- runAsyncHelpersHook('_afterStep', step);
74
- });
71
+ event.dispatcher.on(event.step.after, step => {
72
+ runAsyncHelpersHook('_afterStep', step)
73
+ })
75
74
 
76
75
  event.dispatcher.on(event.all.result, () => {
77
- runAsyncHelpersHook('_finishTest', {}, true);
78
- });
76
+ // Skip _finishTest for all helpers if any browser helper restarts to avoid double cleanup
77
+ const hasBrowserRestart = Object.values(helpers).some(helper =>
78
+ (helper.config && (helper.config.restart === 'browser' || helper.config.restart === 'context' || helper.config.restart === true)) ||
79
+ (helper.options && (helper.options.restart === 'browser' || helper.options.restart === 'context' || helper.options.restart === true))
80
+ )
81
+
82
+ Object.keys(helpers).forEach(key => {
83
+ const helper = helpers[key]
84
+ if (helper._finishTest && !hasBrowserRestart) {
85
+ recorder.add(`hook ${key}._finishTest()`, () => helper._finishTest(), true, false)
86
+ }
87
+ })
88
+ })
89
+
90
+ event.dispatcher.on(event.all.after, () => {
91
+ // Skip _cleanup for all helpers if any browser helper restarts to avoid double cleanup
92
+ const hasBrowserRestart = Object.values(helpers).some(helper =>
93
+ (helper.config && (helper.config.restart === 'browser' || helper.config.restart === 'context' || helper.config.restart === true)) ||
94
+ (helper.options && (helper.options.restart === 'browser' || helper.options.restart === 'context' || helper.options.restart === true))
95
+ )
96
+
97
+ Object.keys(helpers).forEach(key => {
98
+ const helper = helpers[key]
99
+ if (helper._cleanup && !hasBrowserRestart) {
100
+ recorder.add(`hook ${key}._cleanup()`, () => helper._cleanup(), true, false)
101
+ }
102
+ })
103
+ })
79
104
  }
@@ -1,19 +1,18 @@
1
- import * as event from '../event.js';
2
- import container from '../container.js';
1
+ import event from '../event.js'
3
2
 
4
3
  export default function () {
5
- let mocha;
4
+ let mocha
6
5
 
7
6
  event.dispatcher.on(event.all.before, () => {
8
- mocha = container.mocha();
9
- });
7
+ mocha = global.container.mocha()
8
+ })
10
9
 
11
- event.dispatcher.on(event.test.passed, (test) => {
12
- mocha.Runner.emit('pass', test);
13
- });
10
+ event.dispatcher.on(event.test.passed, test => {
11
+ mocha.Runner.emit('pass', test)
12
+ })
14
13
 
15
14
  event.dispatcher.on(event.test.failed, (test, err) => {
16
- test.state = 'failed';
17
- mocha.Runner.emit('fail', test, err);
18
- });
15
+ test.state = 'failed'
16
+ mocha.Runner.emit('fail', test, err)
17
+ })
19
18
  }
@@ -0,0 +1,11 @@
1
+ import event from '../event.js'
2
+
3
+ export default function () {
4
+ event.dispatcher.on(event.hook.failed, err => {
5
+ global.container.result().addStats({ failedHooks: 1 })
6
+ })
7
+
8
+ event.dispatcher.on(event.test.before, test => {
9
+ global.container.result().addTest(test)
10
+ })
11
+ }