codeceptjs 4.0.0-beta.4 → 4.0.0-beta.6.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 (188) hide show
  1. package/README.md +89 -119
  2. package/bin/codecept.js +53 -54
  3. package/docs/webapi/clearCookie.mustache +1 -1
  4. package/lib/actor.js +70 -102
  5. package/lib/ai.js +131 -121
  6. package/lib/assert/empty.js +11 -12
  7. package/lib/assert/equal.js +16 -21
  8. package/lib/assert/error.js +2 -2
  9. package/lib/assert/include.js +11 -15
  10. package/lib/assert/throws.js +3 -5
  11. package/lib/assert/truth.js +10 -7
  12. package/lib/assert.js +18 -18
  13. package/lib/codecept.js +112 -101
  14. package/lib/colorUtils.js +48 -50
  15. package/lib/command/check.js +206 -0
  16. package/lib/command/configMigrate.js +13 -14
  17. package/lib/command/definitions.js +24 -36
  18. package/lib/command/dryRun.js +16 -16
  19. package/lib/command/generate.js +38 -39
  20. package/lib/command/gherkin/init.js +36 -38
  21. package/lib/command/gherkin/snippets.js +76 -74
  22. package/lib/command/gherkin/steps.js +21 -18
  23. package/lib/command/info.js +49 -15
  24. package/lib/command/init.js +41 -37
  25. package/lib/command/interactive.js +22 -13
  26. package/lib/command/list.js +11 -10
  27. package/lib/command/run-multiple/chunk.js +50 -47
  28. package/lib/command/run-multiple/collection.js +5 -5
  29. package/lib/command/run-multiple/run.js +3 -3
  30. package/lib/command/run-multiple.js +27 -47
  31. package/lib/command/run-rerun.js +6 -7
  32. package/lib/command/run-workers.js +15 -66
  33. package/lib/command/run.js +8 -8
  34. package/lib/command/utils.js +22 -21
  35. package/lib/command/workers/runTests.js +131 -241
  36. package/lib/config.js +111 -49
  37. package/lib/container.js +589 -244
  38. package/lib/data/context.js +16 -18
  39. package/lib/data/dataScenarioConfig.js +9 -9
  40. package/lib/data/dataTableArgument.js +7 -7
  41. package/lib/data/table.js +6 -12
  42. package/lib/effects.js +307 -0
  43. package/lib/els.js +160 -0
  44. package/lib/event.js +24 -19
  45. package/lib/globals.js +141 -0
  46. package/lib/heal.js +89 -81
  47. package/lib/helper/AI.js +3 -2
  48. package/lib/helper/ApiDataFactory.js +19 -19
  49. package/lib/helper/Appium.js +47 -51
  50. package/lib/helper/FileSystem.js +35 -15
  51. package/lib/helper/GraphQL.js +1 -1
  52. package/lib/helper/GraphQLDataFactory.js +4 -4
  53. package/lib/helper/JSONResponse.js +72 -45
  54. package/lib/helper/Mochawesome.js +14 -11
  55. package/lib/helper/Playwright.js +832 -434
  56. package/lib/helper/Puppeteer.js +393 -292
  57. package/lib/helper/REST.js +32 -27
  58. package/lib/helper/WebDriver.js +320 -219
  59. package/lib/helper/errors/ConnectionRefused.js +6 -6
  60. package/lib/helper/errors/ElementAssertion.js +11 -16
  61. package/lib/helper/errors/ElementNotFound.js +5 -9
  62. package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
  63. package/lib/helper/extras/Console.js +11 -11
  64. package/lib/helper/extras/PlaywrightLocator.js +110 -0
  65. package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
  66. package/lib/helper/extras/PlaywrightRestartOpts.js +23 -23
  67. package/lib/helper/extras/Popup.js +22 -22
  68. package/lib/helper/extras/React.js +29 -30
  69. package/lib/helper/network/actions.js +33 -48
  70. package/lib/helper/network/utils.js +76 -83
  71. package/lib/helper/scripts/blurElement.js +6 -6
  72. package/lib/helper/scripts/focusElement.js +6 -6
  73. package/lib/helper/scripts/highlightElement.js +9 -9
  74. package/lib/helper/scripts/isElementClickable.js +34 -34
  75. package/lib/helper.js +2 -1
  76. package/lib/history.js +23 -20
  77. package/lib/hooks.js +10 -10
  78. package/lib/html.js +90 -100
  79. package/lib/index.js +48 -21
  80. package/lib/listener/config.js +8 -9
  81. package/lib/listener/emptyRun.js +54 -0
  82. package/lib/listener/exit.js +10 -12
  83. package/lib/listener/{retry.js → globalRetry.js} +10 -10
  84. package/lib/listener/globalTimeout.js +166 -0
  85. package/lib/listener/helpers.js +43 -24
  86. package/lib/listener/mocha.js +4 -5
  87. package/lib/listener/result.js +11 -0
  88. package/lib/listener/steps.js +26 -23
  89. package/lib/listener/store.js +20 -0
  90. package/lib/locator.js +213 -192
  91. package/lib/mocha/asyncWrapper.js +264 -0
  92. package/lib/mocha/bdd.js +167 -0
  93. package/lib/mocha/cli.js +341 -0
  94. package/lib/mocha/factory.js +160 -0
  95. package/lib/{interfaces → mocha}/featureConfig.js +33 -13
  96. package/lib/{interfaces → mocha}/gherkin.js +75 -45
  97. package/lib/mocha/hooks.js +121 -0
  98. package/lib/mocha/index.js +21 -0
  99. package/lib/mocha/inject.js +46 -0
  100. package/lib/{interfaces → mocha}/scenarioConfig.js +32 -8
  101. package/lib/mocha/suite.js +89 -0
  102. package/lib/mocha/test.js +178 -0
  103. package/lib/mocha/types.d.ts +42 -0
  104. package/lib/mocha/ui.js +229 -0
  105. package/lib/output.js +86 -64
  106. package/lib/parser.js +44 -44
  107. package/lib/pause.js +160 -139
  108. package/lib/plugin/analyze.js +403 -0
  109. package/lib/plugin/{autoLogin.js → auth.js} +137 -43
  110. package/lib/plugin/autoDelay.js +19 -15
  111. package/lib/plugin/coverage.js +22 -27
  112. package/lib/plugin/customLocator.js +5 -5
  113. package/lib/plugin/customReporter.js +53 -0
  114. package/lib/plugin/heal.js +49 -17
  115. package/lib/plugin/pageInfo.js +140 -0
  116. package/lib/plugin/pauseOnFail.js +4 -3
  117. package/lib/plugin/retryFailedStep.js +60 -19
  118. package/lib/plugin/screenshotOnFail.js +80 -83
  119. package/lib/plugin/stepByStepReport.js +70 -31
  120. package/lib/plugin/stepTimeout.js +7 -13
  121. package/lib/plugin/subtitles.js +10 -9
  122. package/lib/recorder.js +167 -126
  123. package/lib/rerun.js +94 -50
  124. package/lib/result.js +161 -0
  125. package/lib/secret.js +18 -17
  126. package/lib/session.js +95 -89
  127. package/lib/step/base.js +239 -0
  128. package/lib/step/comment.js +10 -0
  129. package/lib/step/config.js +50 -0
  130. package/lib/step/func.js +46 -0
  131. package/lib/step/helper.js +50 -0
  132. package/lib/step/meta.js +99 -0
  133. package/lib/step/record.js +74 -0
  134. package/lib/step/retry.js +11 -0
  135. package/lib/step/section.js +55 -0
  136. package/lib/step.js +18 -332
  137. package/lib/steps.js +54 -0
  138. package/lib/store.js +37 -5
  139. package/lib/template/heal.js +2 -11
  140. package/lib/timeout.js +60 -0
  141. package/lib/transform.js +8 -8
  142. package/lib/translation.js +32 -18
  143. package/lib/utils.js +354 -250
  144. package/lib/workerStorage.js +16 -16
  145. package/lib/workers.js +366 -282
  146. package/package.json +107 -95
  147. package/translations/de-DE.js +5 -4
  148. package/translations/fr-FR.js +5 -4
  149. package/translations/index.js +23 -9
  150. package/translations/it-IT.js +5 -4
  151. package/translations/ja-JP.js +5 -4
  152. package/translations/nl-NL.js +76 -0
  153. package/translations/pl-PL.js +5 -4
  154. package/translations/pt-BR.js +5 -4
  155. package/translations/ru-RU.js +5 -4
  156. package/translations/utils.js +18 -0
  157. package/translations/zh-CN.js +5 -4
  158. package/translations/zh-TW.js +5 -4
  159. package/typings/index.d.ts +177 -186
  160. package/typings/promiseBasedTypes.d.ts +3573 -5941
  161. package/typings/types.d.ts +4042 -6370
  162. package/lib/cli.js +0 -256
  163. package/lib/helper/ExpectHelper.js +0 -391
  164. package/lib/helper/Nightmare.js +0 -1504
  165. package/lib/helper/Protractor.js +0 -1863
  166. package/lib/helper/SoftExpectHelper.js +0 -381
  167. package/lib/helper/TestCafe.js +0 -1414
  168. package/lib/helper/clientscripts/nightmare.js +0 -213
  169. package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -43
  170. package/lib/helper/testcafe/testControllerHolder.js +0 -42
  171. package/lib/helper/testcafe/testcafe-utils.js +0 -62
  172. package/lib/interfaces/bdd.js +0 -81
  173. package/lib/listener/artifacts.js +0 -19
  174. package/lib/listener/timeout.js +0 -109
  175. package/lib/mochaFactory.js +0 -113
  176. package/lib/plugin/allure.js +0 -15
  177. package/lib/plugin/commentStep.js +0 -136
  178. package/lib/plugin/debugErrors.js +0 -67
  179. package/lib/plugin/eachElement.js +0 -127
  180. package/lib/plugin/fakerTransform.js +0 -49
  181. package/lib/plugin/retryTo.js +0 -127
  182. package/lib/plugin/selenoid.js +0 -384
  183. package/lib/plugin/standardActingHelpers.js +0 -3
  184. package/lib/plugin/tryTo.js +0 -115
  185. package/lib/plugin/wdio.js +0 -249
  186. package/lib/scenario.js +0 -224
  187. package/lib/ui.js +0 -236
  188. package/lib/within.js +0 -70
@@ -1,26 +1,31 @@
1
- const Gherkin = require('@cucumber/gherkin')
2
- const Messages = require('@cucumber/messages')
3
- const { Context, Suite, Test } = require('mocha')
4
- const debug = require('debug')('codeceptjs:bdd')
5
-
6
- const { matchStep } = require('./bdd')
7
- const event = require('../event')
8
- const scenario = require('../scenario')
9
- const Step = require('../step')
10
- const DataTableArgument = require('../data/dataTableArgument')
11
- const transform = require('../transform')
12
-
13
- const uuidFn = Messages.IdGenerator.uuid()
1
+ import Gherkin from '@cucumber/gherkin'
2
+ import { IdGenerator } from '@cucumber/messages'
3
+ import { Context, Suite } from 'mocha'
4
+ import debug from 'debug'
5
+ const debugBdd = debug('codeceptjs:bdd')
6
+
7
+ import event from '../event.js'
8
+ import { injected, setup, teardown, suiteSetup, suiteTeardown } from './asyncWrapper.js'
9
+ import step from '../step/base.js'
10
+ import MetaStep from '../step/meta.js'
11
+ import DataTableArgument from '../data/dataTableArgument.js'
12
+ import transform from '../transform.js'
13
+ import { enhanceMochaSuite } from './suite.js'
14
+ import { createTest } from './test.js'
15
+ import { matchStep } from './bdd.js'
16
+
17
+ const uuidFn = IdGenerator.uuid()
14
18
  const builder = new Gherkin.AstBuilder(uuidFn)
15
19
  const matcher = new Gherkin.GherkinClassicTokenMatcher()
16
20
  const parser = new Gherkin.Parser(builder, matcher)
17
21
  parser.stopAtFirstError = false
18
22
 
19
- module.exports = (text, file) => {
23
+ const gherkinParser = (text, file) => {
20
24
  const ast = parser.parse(text)
21
25
  let currentLanguage
22
26
 
23
27
  if (ast.feature) {
28
+ // Ensure translations are loaded before trying to access them
24
29
  currentLanguage = getTranslation(ast.feature.language)
25
30
  }
26
31
 
@@ -28,7 +33,8 @@ module.exports = (text, file) => {
28
33
  throw new Error(`No 'Features' available in Gherkin '${file}' provided!`)
29
34
  }
30
35
  const suite = new Suite(ast.feature.name, new Context())
31
- const tags = ast.feature.tags.map((t) => t.name)
36
+ enhanceMochaSuite(suite)
37
+ const tags = ast.feature.tags.map(t => t.name)
32
38
  suite.title = `${suite.title} ${tags.join(' ')}`.trim()
33
39
  suite.tags = tags || []
34
40
  suite.comment = ast.feature.description
@@ -36,17 +42,23 @@ module.exports = (text, file) => {
36
42
  suite.file = file
37
43
  suite.timeout(0)
38
44
 
39
- suite.beforeEach('codeceptjs.before', () => scenario.setup(suite))
40
- suite.afterEach('codeceptjs.after', () => scenario.teardown(suite))
41
- suite.beforeAll('codeceptjs.beforeSuite', () => scenario.suiteSetup(suite))
42
- suite.afterAll('codeceptjs.afterSuite', () => scenario.suiteTeardown(suite))
43
-
44
- const runSteps = async (steps) => {
45
+ suite.beforeEach('codeceptjs.before', function () {
46
+ // In Mocha, 'this' refers to the current test in beforeEach/afterEach hooks
47
+ setup(this)(() => {})
48
+ })
49
+ suite.afterEach('codeceptjs.after', function () {
50
+ // In Mocha, 'this' refers to the current test in beforeEach/afterEach hooks
51
+ teardown(this)(() => {})
52
+ })
53
+ suite.beforeAll('codeceptjs.beforeSuite', suiteSetup(suite))
54
+ suite.afterAll('codeceptjs.afterSuite', suiteTeardown(suite))
55
+
56
+ const runSteps = async steps => {
45
57
  for (const step of steps) {
46
- const metaStep = new Step.MetaStep(null, step.text)
58
+ const metaStep = new MetaStep(null, step.text)
47
59
  metaStep.actor = step.keyword.trim()
48
60
  let helperStep
49
- const setMetaStep = (step) => {
61
+ const setMetaStep = step => {
50
62
  helperStep = step
51
63
  if (step.metaStep) {
52
64
  if (step.metaStep === metaStep) {
@@ -100,18 +112,13 @@ module.exports = (text, file) => {
100
112
  if (child.background) {
101
113
  suite.beforeEach(
102
114
  'Before',
103
- scenario.injected(async () => runSteps(child.background.steps), suite, 'before'),
115
+ injected(async () => runSteps(child.background.steps), suite, 'before'),
104
116
  )
105
117
  continue
106
118
  }
107
- if (
108
- child.scenario &&
109
- (currentLanguage
110
- ? child.scenario.keyword === currentLanguage.contexts.ScenarioOutline
111
- : child.scenario.keyword === 'Scenario Outline')
112
- ) {
119
+ if (child.scenario && (currentLanguage ? currentLanguage.contexts.ScenarioOutline.includes(child.scenario.keyword) : child.scenario.keyword === 'Scenario Outline')) {
113
120
  for (const examples of child.scenario.examples) {
114
- const fields = examples.tableHeader.cells.map((c) => c.value)
121
+ const fields = examples.tableHeader.cells.map(c => c.value)
115
122
  for (const example of examples.tableBody) {
116
123
  let exampleSteps = [...child.scenario.steps]
117
124
  const current = {}
@@ -120,13 +127,13 @@ module.exports = (text, file) => {
120
127
  const value = transform('gherkin.examples', example.cells[index].value)
121
128
  example.cells[index].value = value
122
129
  current[placeholder] = value
123
- exampleSteps = exampleSteps.map((step) => {
130
+ exampleSteps = exampleSteps.map(step => {
124
131
  step = { ...step }
125
132
  step.text = step.text.split(`<${placeholder}>`).join(value)
126
133
  return step
127
134
  })
128
135
  }
129
- const tags = child.scenario.tags.map((t) => t.name).concat(examples.tags.map((t) => t.name))
136
+ const tags = child.scenario.tags.map(t => t.name).concat(examples.tags.map(t => t.name))
130
137
  let title = `${child.scenario.name} ${JSON.stringify(current)} ${tags.join(' ')}`.trim()
131
138
 
132
139
  for (const [key, value] of Object.entries(current)) {
@@ -135,22 +142,22 @@ module.exports = (text, file) => {
135
142
  }
136
143
  }
137
144
 
138
- const test = new Test(title, async () => runSteps(addExampleInTable(exampleSteps, current)))
145
+ const test = createTest(title, async () => runSteps(addExampleInTable(exampleSteps, current)))
146
+ test.addToSuite(suite)
139
147
  test.tags = suite.tags.concat(tags)
140
148
  test.file = file
141
- suite.addTest(scenario.test(test))
142
149
  }
143
150
  }
144
151
  continue
145
152
  }
146
153
 
147
154
  if (child.scenario) {
148
- const tags = child.scenario.tags.map((t) => t.name)
155
+ const tags = child.scenario.tags.map(t => t.name)
149
156
  const title = `${child.scenario.name} ${tags.join(' ')}`.trim()
150
- const test = new Test(title, async () => runSteps(child.scenario.steps))
157
+ const test = createTest(title, async () => runSteps(child.scenario.steps))
158
+ test.addToSuite(suite)
151
159
  test.tags = suite.tags.concat(tags)
152
160
  test.file = file
153
- suite.addTest(scenario.test(test))
154
161
  }
155
162
  }
156
163
 
@@ -162,8 +169,8 @@ function transformTable(table) {
162
169
  for (const id in table.rows) {
163
170
  const cells = table.rows[id].cells
164
171
  str += cells
165
- .map((c) => c.value)
166
- .map((c) => c.padEnd(15))
172
+ .map(c => c.value)
173
+ .map(c => c.padEnd(15))
167
174
  .join(' | ')
168
175
  str += '\n'
169
176
  }
@@ -172,12 +179,12 @@ function transformTable(table) {
172
179
  function addExampleInTable(exampleSteps, placeholders) {
173
180
  const steps = JSON.parse(JSON.stringify(exampleSteps))
174
181
  for (const placeholder in placeholders) {
175
- steps.map((step) => {
182
+ steps.map(step => {
176
183
  step = { ...step }
177
184
  if (step.dataTable) {
178
185
  for (const id in step.dataTable.rows) {
179
186
  const cells = step.dataTable.rows[id].cells
180
- cells.map((c) => (c.value = c.value.replace(`<${placeholder}>`, placeholders[placeholder])))
187
+ cells.map(c => (c.value = c.value.replace(`<${placeholder}>`, placeholders[placeholder])))
181
188
  }
182
189
  }
183
190
  return step
@@ -186,16 +193,39 @@ function addExampleInTable(exampleSteps, placeholders) {
186
193
  return steps
187
194
  }
188
195
 
196
+ // Import translations at module level to avoid async in parser
197
+ let translations = null
198
+ async function loadTranslations() {
199
+ if (!translations) {
200
+ // Import container to ensure it's initialized
201
+ const Container = await import('../container.js')
202
+ await Container.default.started()
203
+
204
+ // Now load translations
205
+ const translationsModule = await import('../../translations/index.js')
206
+ translations = translationsModule.default || translationsModule
207
+ }
208
+ return translations
209
+ }
210
+
189
211
  function getTranslation(language) {
190
- const translations = Object.keys(require('../../translations'))
212
+ if (!translations) {
213
+ // Translations not loaded yet, return null (will use default)
214
+ return null
215
+ }
191
216
 
192
- for (const availableTranslation of translations) {
217
+ const translationKeys = Object.keys(translations)
218
+ for (const availableTranslation of translationKeys) {
193
219
  if (!language) {
194
220
  break
195
221
  }
196
222
 
197
223
  if (availableTranslation.includes(language)) {
198
- return require('../../translations')[availableTranslation]
224
+ return translations[availableTranslation]
199
225
  }
200
226
  }
227
+ return null
201
228
  }
229
+
230
+ export { loadTranslations }
231
+ export default gherkinParser
@@ -0,0 +1,121 @@
1
+ import event from '../event.js'
2
+ import { serializeError } from '../utils.js'
3
+ // const { serializeTest } = require('./test')
4
+
5
+ /**
6
+ * Represents a test hook in the testing framework
7
+ * @class
8
+ * @property {Object} suite - The test suite this hook belongs to
9
+ * @property {Object} test - The test object associated with this hook
10
+ * @property {Object} runnable - The current test being executed
11
+ * @property {Object} ctx - The context object
12
+ * @property {Error|null} err - The error that occurred during hook execution, if any
13
+ */
14
+ class Hook {
15
+ /**
16
+ * Creates a new Hook instance
17
+ * @param {Object} context - The context object containing suite and test information
18
+ * @param {Object} context.suite - The test suite
19
+ * @param {Object} context.test - The test object
20
+ * @param {Object} context.ctx - The context object
21
+ * @param {Error} error - The error object if hook execution failed
22
+ */
23
+ constructor(context, error) {
24
+ this.suite = context.suite
25
+ this.test = context.test
26
+ this.runnable = context?.ctx?.test
27
+ this.ctx = context.ctx
28
+ this.err = error
29
+ }
30
+
31
+ get hookName() {
32
+ return this.constructor.name.replace('Hook', '')
33
+ }
34
+
35
+ simplify() {
36
+ return {
37
+ hookName: this.hookName,
38
+ title: this.title,
39
+ // test: this.test ? serializeTest(this.test) : null,
40
+ // suite: this.suite ? serializeSuite(this.suite) : null,
41
+ error: this.err ? serializeError(this.err) : null,
42
+ }
43
+ }
44
+
45
+ toString() {
46
+ return this.hookName
47
+ }
48
+
49
+ toCode() {
50
+ return this.toString() + '()'
51
+ }
52
+
53
+ retry(n) {
54
+ this.suite.opts[`retry${this.hookName}`] = n
55
+ }
56
+
57
+ get title() {
58
+ return this.ctx?.test?.title || this.name
59
+ }
60
+
61
+ get name() {
62
+ return this.constructor.name
63
+ }
64
+ }
65
+
66
+ class BeforeHook extends Hook {}
67
+
68
+ class AfterHook extends Hook {}
69
+
70
+ class BeforeSuiteHook extends Hook {}
71
+
72
+ class AfterSuiteHook extends Hook {}
73
+
74
+ function fireHook(eventType, suite, error) {
75
+ const hook = suite.ctx?.test?.title?.match(/"([^"]*)"/)[1]
76
+ switch (hook) {
77
+ case 'before each':
78
+ event.emit(eventType, new BeforeHook(suite, error))
79
+ break
80
+ case 'after each':
81
+ event.emit(eventType, new AfterHook(suite, error))
82
+ break
83
+ case 'before all':
84
+ event.emit(eventType, new BeforeSuiteHook(suite, error))
85
+ break
86
+ case 'after all':
87
+ event.emit(eventType, new AfterSuiteHook(suite, error))
88
+ break
89
+ default:
90
+ event.emit(eventType, suite, error)
91
+ }
92
+ }
93
+
94
+ class HookConfig {
95
+ constructor(hook) {
96
+ this.hook = hook
97
+ }
98
+
99
+ retry(n) {
100
+ this.hook.retry(n)
101
+ return this
102
+ }
103
+ }
104
+
105
+ export {
106
+ BeforeHook,
107
+ AfterHook,
108
+ BeforeSuiteHook,
109
+ AfterSuiteHook,
110
+ fireHook,
111
+ HookConfig,
112
+ }
113
+
114
+ export default {
115
+ BeforeHook,
116
+ AfterHook,
117
+ BeforeSuiteHook,
118
+ AfterSuiteHook,
119
+ fireHook,
120
+ HookConfig,
121
+ }
@@ -0,0 +1,21 @@
1
+ import Suite from 'mocha/lib/suite.js'
2
+ import Test from 'mocha/lib/test.js'
3
+ import { BeforeHook, AfterHook, BeforeSuiteHook, AfterSuiteHook } from './hooks.js'
4
+
5
+ export {
6
+ Suite,
7
+ Test,
8
+ BeforeHook,
9
+ AfterHook,
10
+ BeforeSuiteHook,
11
+ AfterSuiteHook,
12
+ }
13
+
14
+ export default {
15
+ Suite,
16
+ Test,
17
+ BeforeHook,
18
+ AfterHook,
19
+ BeforeSuiteHook,
20
+ AfterSuiteHook,
21
+ }
@@ -0,0 +1,46 @@
1
+ import { getParams } from '../parser.js'
2
+
3
+ const getInjectedArguments = async (fn, test, suite) => {
4
+ const containerModule = await import('../container.js')
5
+ const container = containerModule.default || containerModule
6
+
7
+ const testArgs = {}
8
+ const params = getParams(fn) || []
9
+ const objects = container.support()
10
+
11
+ for (const key of params) {
12
+ testArgs[key] = {}
13
+
14
+ // Handle special built-in objects first
15
+ if (key === 'suite') {
16
+ if (test) {
17
+ testArgs[key] = test.parent || test
18
+ } else if (suite) {
19
+ testArgs[key] = suite
20
+ }
21
+ continue
22
+ }
23
+ if (key === 'test') {
24
+ testArgs[key] = test
25
+ continue
26
+ }
27
+
28
+ if (test && test.inject && test.inject[key]) {
29
+ // @FIX: need fix got inject
30
+ testArgs[key] = test.inject[key]
31
+ continue
32
+ }
33
+ if (!objects[key]) {
34
+ throw new Error(`Object of type ${key} is not defined in container`)
35
+ }
36
+ testArgs[key] = container.support(key)
37
+ }
38
+
39
+ if (test) {
40
+ testArgs.suite = test?.parent
41
+ testArgs.test = test
42
+ }
43
+ return testArgs
44
+ }
45
+
46
+ export { getInjectedArguments }
@@ -1,5 +1,10 @@
1
+ import { isAsyncFunction } from '../utils.js'
2
+
1
3
  /** @class */
2
4
  class ScenarioConfig {
5
+ /**
6
+ * @param {CodeceptJS.Test} test
7
+ */
3
8
  constructor(test) {
4
9
  this.test = test
5
10
  }
@@ -40,6 +45,17 @@ class ScenarioConfig {
40
45
  return this
41
46
  }
42
47
 
48
+ /**
49
+ * Set metadata for this test
50
+ * @param {string} key
51
+ * @param {string} value
52
+ * @returns {this}
53
+ */
54
+ meta(key, value) {
55
+ this.test.meta[key] = value
56
+ return this
57
+ }
58
+
43
59
  /**
44
60
  * Set timeout for this test
45
61
  * @param {number} timeout
@@ -64,24 +80,32 @@ class ScenarioConfig {
64
80
  return this
65
81
  }
66
82
 
83
+ /**
84
+ * @callback ScenarioConfigCallback
85
+ * @param {CodeceptJS.Test} test
86
+ * @returns {Object<string, any>}
87
+ */
88
+
67
89
  /**
68
90
  * Configures a helper.
69
91
  * Helper name can be omitted and values will be applied to first helper.
70
- * @param {string | Object<string, any>} helper
92
+ * @param {string | Object<string, any> | ScenarioConfigCallback} helper
71
93
  * @param {Object<string, any>} [obj]
72
94
  * @returns {this}
73
95
  */
74
- async config(helper, obj) {
96
+ config(helper, obj) {
75
97
  if (!obj) {
76
98
  obj = helper
77
99
  helper = 0
78
100
  }
79
101
  if (typeof obj === 'function') {
80
- obj = await obj(this.test)
81
- }
82
- if (!this.test.config) {
83
- this.test.config = {}
102
+ if (isAsyncFunction(obj)) {
103
+ obj(this.test).then(res => (this.test.config[helper] = res))
104
+ return this
105
+ }
106
+ obj = obj(this.test)
84
107
  }
108
+
85
109
  this.test.config[helper] = obj
86
110
  return this
87
111
  }
@@ -104,11 +128,11 @@ class ScenarioConfig {
104
128
  * @returns {this}
105
129
  */
106
130
  injectDependencies(dependencies) {
107
- Object.keys(dependencies).forEach((key) => {
131
+ Object.keys(dependencies).forEach(key => {
108
132
  this.test.inject[key] = dependencies[key]
109
133
  })
110
134
  return this
111
135
  }
112
136
  }
113
137
 
114
- module.exports = ScenarioConfig
138
+ export default ScenarioConfig
@@ -0,0 +1,89 @@
1
+ import MochaSuite from 'mocha/lib/suite.js'
2
+ /**
3
+ * @typedef {import('mocha')} Mocha
4
+ */
5
+
6
+ /**
7
+ * Enhances MochaSuite with CodeceptJS specific functionality using composition
8
+ */
9
+ function enhanceMochaSuite(suite) {
10
+ if (!suite) suite = new MochaSuite('Suite', null, false)
11
+ // already enhanced
12
+ if (suite.codeceptjs) return suite
13
+
14
+ suite.codeceptjs = true
15
+ // Add properties
16
+ suite.tags = suite.title.match(/(\@[a-zA-Z0-9-_]+)/g) || []
17
+ suite.opts = {}
18
+ // suite.totalTimeout = undefined
19
+
20
+ // Override fullTitle method
21
+ suite.fullTitle = () => `${suite.title}:`
22
+
23
+ // Add new methods
24
+ suite.applyOptions = function (opts) {
25
+ if (!opts) opts = {}
26
+ suite.opts = opts
27
+
28
+ if (opts.retries) suite.retries(opts.retries)
29
+ if (opts.timeout) suite.totalTimeout = opts.timeout
30
+
31
+ if (opts.skipInfo && opts.skipInfo.skipped) {
32
+ suite.pending = true
33
+ suite.opts = { ...this.opts, skipInfo: opts.skipInfo }
34
+ }
35
+ }
36
+
37
+ suite.simplify = function () {
38
+ return serializeSuite(this)
39
+ }
40
+
41
+ return suite
42
+ }
43
+
44
+ /**
45
+ * Factory function to create enhanced suites
46
+ * @param {Mocha.Suite} parent - Parent suite
47
+ * @param {string} title - Suite title
48
+ * @returns {CodeceptJS.Suite & Mocha.Suite} New enhanced suite instance
49
+ */
50
+ function createSuite(parent, title) {
51
+ const suite = MochaSuite.create(parent, title)
52
+ suite.timeout(0)
53
+ return enhanceMochaSuite(suite)
54
+ }
55
+
56
+ function serializeSuite(suite) {
57
+ suite = { ...suite }
58
+
59
+ return {
60
+ opts: suite.opts || {},
61
+ tags: suite.tags || [],
62
+ retries: suite._retries,
63
+ title: suite.title,
64
+ status: suite.status,
65
+ notes: suite.notes || [],
66
+ meta: suite.meta || {},
67
+ duration: suite.duration || 0,
68
+ }
69
+ }
70
+
71
+ function deserializeSuite(suite) {
72
+ suite = Object.assign(new MochaSuite(suite.title), suite)
73
+ enhanceMochaSuite(suite)
74
+ return suite
75
+ }
76
+
77
+ export {
78
+ createSuite,
79
+ enhanceMochaSuite,
80
+ serializeSuite,
81
+ deserializeSuite,
82
+ }
83
+
84
+ export default {
85
+ createSuite,
86
+ enhanceMochaSuite,
87
+ serializeSuite,
88
+ deserializeSuite,
89
+ }