codeceptjs 4.0.0-beta.1 → 4.0.0-beta.10.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 +751 -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,231 @@
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()
18
+ const builder = new Gherkin.AstBuilder(uuidFn)
19
+ const matcher = new Gherkin.GherkinClassicTokenMatcher()
20
+ const parser = new Gherkin.Parser(builder, matcher)
21
+ parser.stopAtFirstError = false
22
+
23
+ const gherkinParser = (text, file) => {
24
+ const ast = parser.parse(text)
25
+ let currentLanguage
26
+
27
+ if (ast.feature) {
28
+ // Ensure translations are loaded before trying to access them
29
+ currentLanguage = getTranslation(ast.feature.language)
30
+ }
31
+
32
+ if (!ast.feature) {
33
+ throw new Error(`No 'Features' available in Gherkin '${file}' provided!`)
34
+ }
35
+ const suite = new Suite(ast.feature.name, new Context())
36
+ enhanceMochaSuite(suite)
37
+ const tags = ast.feature.tags.map(t => t.name)
38
+ suite.title = `${suite.title} ${tags.join(' ')}`.trim()
39
+ suite.tags = tags || []
40
+ suite.comment = ast.feature.description
41
+ suite.feature = ast.feature
42
+ suite.file = file
43
+ suite.timeout(0)
44
+
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 => {
57
+ for (const step of steps) {
58
+ const metaStep = new MetaStep(null, step.text)
59
+ metaStep.actor = step.keyword.trim()
60
+ let helperStep
61
+ const setMetaStep = step => {
62
+ helperStep = step
63
+ if (step.metaStep) {
64
+ if (step.metaStep === metaStep) {
65
+ return
66
+ }
67
+ setMetaStep(step.metaStep)
68
+ return
69
+ }
70
+ step.metaStep = metaStep
71
+ }
72
+ const fn = matchStep(step.text)
73
+
74
+ if (step.dataTable) {
75
+ fn.params.push({
76
+ ...step.dataTable,
77
+ parse: () => new DataTableArgument(step.dataTable),
78
+ })
79
+ metaStep.comment = `\n${transformTable(step.dataTable)}`
80
+ }
81
+
82
+ if (step.docString) {
83
+ fn.params.push(step.docString)
84
+ metaStep.comment = `\n"""\n${step.docString.content}\n"""`
85
+ }
86
+
87
+ step.startTime = Date.now()
88
+ step.match = fn.line
89
+ event.emit(event.bddStep.before, step)
90
+ event.emit(event.bddStep.started, metaStep)
91
+ event.dispatcher.prependListener(event.step.before, setMetaStep)
92
+ try {
93
+ debug(`Step '${step.text}' started...`)
94
+ await fn(...fn.params)
95
+ debug('Step passed')
96
+ step.status = 'passed'
97
+ } catch (err) {
98
+ debug(`Step failed: ${err?.message}`)
99
+ step.status = 'failed'
100
+ step.err = err
101
+ throw err
102
+ } finally {
103
+ step.endTime = Date.now()
104
+ event.dispatcher.removeListener(event.step.before, setMetaStep)
105
+ }
106
+ event.emit(event.bddStep.finished, metaStep)
107
+ event.emit(event.bddStep.after, step)
108
+ }
109
+ }
110
+
111
+ for (const child of ast.feature.children) {
112
+ if (child.background) {
113
+ suite.beforeEach(
114
+ 'Before',
115
+ injected(async () => runSteps(child.background.steps), suite, 'before'),
116
+ )
117
+ continue
118
+ }
119
+ if (child.scenario && (currentLanguage ? currentLanguage.contexts.ScenarioOutline === child.scenario.keyword : child.scenario.keyword === 'Scenario Outline')) {
120
+ for (const examples of child.scenario.examples) {
121
+ const fields = examples.tableHeader.cells.map(c => c.value)
122
+ for (const example of examples.tableBody) {
123
+ let exampleSteps = [...child.scenario.steps]
124
+ const current = {}
125
+ for (const index in example.cells) {
126
+ const placeholder = fields[index]
127
+ const value = transform('gherkin.examples', example.cells[index].value)
128
+ example.cells[index].value = value
129
+ current[placeholder] = value
130
+ exampleSteps = exampleSteps.map(step => {
131
+ step = { ...step }
132
+ step.text = step.text.split(`<${placeholder}>`).join(value)
133
+ return step
134
+ })
135
+ }
136
+ const tags = child.scenario.tags.map(t => t.name).concat(examples.tags.map(t => t.name))
137
+ let title = `${child.scenario.name} ${JSON.stringify(current)} ${tags.join(' ')}`.trim()
138
+
139
+ for (const [key, value] of Object.entries(current)) {
140
+ if (title.includes(`<${key}>`)) {
141
+ title = title.replace(JSON.stringify(current), '').replace(`<${key}>`, value)
142
+ }
143
+ }
144
+
145
+ const test = createTest(title, async () => runSteps(addExampleInTable(exampleSteps, current)))
146
+ test.addToSuite(suite)
147
+ test.tags = suite.tags.concat(tags)
148
+ test.file = file
149
+ }
150
+ }
151
+ continue
152
+ }
153
+
154
+ if (child.scenario) {
155
+ const tags = child.scenario.tags.map(t => t.name)
156
+ const title = `${child.scenario.name} ${tags.join(' ')}`.trim()
157
+ const test = createTest(title, async () => runSteps(child.scenario.steps))
158
+ test.addToSuite(suite)
159
+ test.tags = suite.tags.concat(tags)
160
+ test.file = file
161
+ }
162
+ }
163
+
164
+ return suite
165
+ }
166
+
167
+ function transformTable(table) {
168
+ let str = ''
169
+ for (const id in table.rows) {
170
+ const cells = table.rows[id].cells
171
+ str += cells
172
+ .map(c => c.value)
173
+ .map(c => c.padEnd(15))
174
+ .join(' | ')
175
+ str += '\n'
176
+ }
177
+ return str
178
+ }
179
+ function addExampleInTable(exampleSteps, placeholders) {
180
+ const steps = JSON.parse(JSON.stringify(exampleSteps))
181
+ for (const placeholder in placeholders) {
182
+ steps.map(step => {
183
+ step = { ...step }
184
+ if (step.dataTable) {
185
+ for (const id in step.dataTable.rows) {
186
+ const cells = step.dataTable.rows[id].cells
187
+ cells.map(c => (c.value = c.value.replace(`<${placeholder}>`, placeholders[placeholder])))
188
+ }
189
+ }
190
+ return step
191
+ })
192
+ }
193
+ return steps
194
+ }
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
+
211
+ function getTranslation(language) {
212
+ if (!translations) {
213
+ // Translations not loaded yet, return null (will use default)
214
+ return null
215
+ }
216
+
217
+ const translationKeys = Object.keys(translations)
218
+ for (const availableTranslation of translationKeys) {
219
+ if (!language) {
220
+ break
221
+ }
222
+
223
+ if (availableTranslation.includes(language)) {
224
+ return translations[availableTranslation]
225
+ }
226
+ }
227
+ return null
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,7 +1,12 @@
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
- this.test = test;
9
+ this.test = test
5
10
  }
6
11
 
7
12
  /**
@@ -12,8 +17,8 @@ class ScenarioConfig {
12
17
  * @returns {this}
13
18
  */
14
19
  throws(err) {
15
- this.test.throws = err;
16
- return this;
20
+ this.test.throws = err
21
+ return this
17
22
  }
18
23
 
19
24
  /**
@@ -24,8 +29,8 @@ class ScenarioConfig {
24
29
  * @returns {this}
25
30
  */
26
31
  fails() {
27
- this.test.throws = new Error();
28
- return this;
32
+ this.test.throws = new Error()
33
+ return this
29
34
  }
30
35
 
31
36
  /**
@@ -35,9 +40,20 @@ class ScenarioConfig {
35
40
  * @returns {this}
36
41
  */
37
42
  retry(retries) {
38
- if (process.env.SCENARIO_ONLY) retries = -retries;
39
- this.test.retries(retries);
40
- return this;
43
+ if (process.env.SCENARIO_ONLY) retries = -retries
44
+ this.test.retries(retries)
45
+ return this
46
+ }
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
41
57
  }
42
58
 
43
59
  /**
@@ -46,12 +62,12 @@ class ScenarioConfig {
46
62
  * @returns {this}
47
63
  */
48
64
  timeout(timeout) {
49
- console.log(`Scenario('${this.test.title}', () => {}).timeout(${timeout}) is deprecated!`);
50
- console.log(`Please use Scenario('${this.test.title}', { timeout: ${timeout / 1000} }, () => {}) instead`);
51
- console.log('Timeout should be set in seconds');
65
+ console.log(`Scenario('${this.test.title}', () => {}).timeout(${timeout}) is deprecated!`)
66
+ console.log(`Please use Scenario('${this.test.title}', { timeout: ${timeout / 1000} }, () => {}) instead`)
67
+ console.log('Timeout should be set in seconds')
52
68
 
53
- this.test.timeout(timeout);
54
- return this;
69
+ this.test.timeout(timeout)
70
+ return this
55
71
  }
56
72
 
57
73
  /**
@@ -60,30 +76,38 @@ class ScenarioConfig {
60
76
  * @returns {this}
61
77
  */
62
78
  inject(obj) {
63
- this.test.inject = obj;
64
- return this;
79
+ this.test.inject = obj
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
- obj = helper;
77
- helper = 0;
98
+ obj = helper
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
  }
85
- this.test.config[helper] = obj;
86
- return this;
108
+
109
+ this.test.config[helper] = obj
110
+ return this
87
111
  }
88
112
 
89
113
  /**
@@ -92,10 +116,10 @@ class ScenarioConfig {
92
116
  * @returns {this}
93
117
  */
94
118
  tag(tagName) {
95
- if (tagName[0] !== '@') tagName = `@${tagName}`;
96
- this.test.tags.push(tagName);
97
- this.test.title = `${this.test.title.trim()} ${tagName}`;
98
- return this;
119
+ if (tagName[0] !== '@') tagName = `@${tagName}`
120
+ this.test.tags.push(tagName)
121
+ this.test.title = `${this.test.title.trim()} ${tagName}`
122
+ return this
99
123
  }
100
124
 
101
125
  /**
@@ -104,11 +128,11 @@ class ScenarioConfig {
104
128
  * @returns {this}
105
129
  */
106
130
  injectDependencies(dependencies) {
107
- Object.keys(dependencies).forEach((key) => {
108
- this.test.inject[key] = dependencies[key];
109
- });
110
- return this;
131
+ Object.keys(dependencies).forEach(key => {
132
+ this.test.inject[key] = dependencies[key]
133
+ })
134
+ return this
111
135
  }
112
136
  }
113
137
 
114
- export default 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
+ }