codeceptjs 4.0.0-beta.2 → 4.0.0-beta.20
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.
- package/README.md +133 -120
- package/bin/codecept.js +107 -96
- package/bin/test-server.js +64 -0
- package/docs/webapi/clearCookie.mustache +1 -1
- package/docs/webapi/click.mustache +5 -1
- package/lib/actor.js +71 -103
- package/lib/ai.js +159 -188
- package/lib/assert/empty.js +22 -24
- package/lib/assert/equal.js +30 -37
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +43 -48
- package/lib/assert/throws.js +11 -11
- package/lib/assert/truth.js +22 -22
- package/lib/assert.js +20 -18
- package/lib/codecept.js +262 -162
- package/lib/colorUtils.js +50 -52
- package/lib/command/check.js +206 -0
- package/lib/command/configMigrate.js +56 -51
- package/lib/command/definitions.js +96 -109
- package/lib/command/dryRun.js +77 -79
- package/lib/command/generate.js +234 -194
- package/lib/command/gherkin/init.js +42 -33
- package/lib/command/gherkin/snippets.js +76 -74
- package/lib/command/gherkin/steps.js +20 -17
- package/lib/command/info.js +74 -38
- package/lib/command/init.js +301 -290
- package/lib/command/interactive.js +41 -32
- package/lib/command/list.js +28 -27
- package/lib/command/run-multiple/chunk.js +51 -48
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +5 -1
- package/lib/command/run-multiple.js +97 -97
- package/lib/command/run-rerun.js +19 -25
- package/lib/command/run-workers.js +68 -92
- package/lib/command/run.js +39 -27
- package/lib/command/utils.js +80 -64
- package/lib/command/workers/runTests.js +388 -226
- package/lib/config.js +109 -50
- package/lib/container.js +641 -261
- package/lib/data/context.js +60 -61
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +32 -32
- package/lib/data/table.js +22 -22
- package/lib/effects.js +307 -0
- package/lib/element/WebElement.js +327 -0
- package/lib/els.js +160 -0
- package/lib/event.js +173 -163
- package/lib/globals.js +141 -0
- package/lib/heal.js +89 -85
- package/lib/helper/AI.js +131 -41
- package/lib/helper/ApiDataFactory.js +107 -75
- package/lib/helper/Appium.js +542 -404
- package/lib/helper/FileSystem.js +100 -79
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +52 -52
- package/lib/helper/JSONResponse.js +126 -88
- package/lib/helper/Mochawesome.js +54 -29
- package/lib/helper/Playwright.js +2547 -1316
- package/lib/helper/Puppeteer.js +1578 -1181
- package/lib/helper/REST.js +209 -68
- package/lib/helper/WebDriver.js +1482 -1342
- package/lib/helper/errors/ConnectionRefused.js +6 -6
- package/lib/helper/errors/ElementAssertion.js +11 -16
- package/lib/helper/errors/ElementNotFound.js +5 -9
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
- package/lib/helper/extras/Console.js +11 -11
- package/lib/helper/extras/PlaywrightLocator.js +110 -0
- package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
- package/lib/helper/extras/PlaywrightReactVueLocator.js +17 -8
- package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
- package/lib/helper/extras/Popup.js +22 -22
- package/lib/helper/extras/React.js +27 -28
- package/lib/helper/network/actions.js +36 -42
- package/lib/helper/network/utils.js +78 -84
- package/lib/helper/scripts/blurElement.js +5 -5
- package/lib/helper/scripts/focusElement.js +5 -5
- package/lib/helper/scripts/highlightElement.js +8 -8
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -3
- package/lib/history.js +23 -19
- package/lib/hooks.js +8 -8
- package/lib/html.js +94 -104
- package/lib/index.js +38 -27
- package/lib/listener/config.js +30 -23
- package/lib/listener/emptyRun.js +54 -0
- package/lib/listener/enhancedGlobalRetry.js +110 -0
- package/lib/listener/exit.js +16 -18
- package/lib/listener/globalRetry.js +70 -0
- package/lib/listener/globalTimeout.js +181 -0
- package/lib/listener/helpers.js +76 -51
- package/lib/listener/mocha.js +10 -11
- package/lib/listener/result.js +11 -0
- package/lib/listener/retryEnhancer.js +85 -0
- package/lib/listener/steps.js +71 -59
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +214 -197
- package/lib/mocha/asyncWrapper.js +274 -0
- package/lib/mocha/bdd.js +167 -0
- package/lib/mocha/cli.js +341 -0
- package/lib/mocha/factory.js +163 -0
- package/lib/mocha/featureConfig.js +89 -0
- package/lib/mocha/gherkin.js +231 -0
- package/lib/mocha/hooks.js +121 -0
- package/lib/mocha/index.js +21 -0
- package/lib/mocha/inject.js +46 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +58 -34
- package/lib/mocha/suite.js +89 -0
- package/lib/mocha/test.js +184 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +242 -0
- package/lib/output.js +141 -71
- package/lib/parser.js +47 -44
- package/lib/pause.js +173 -145
- package/lib/plugin/analyze.js +403 -0
- package/lib/plugin/{autoLogin.js → auth.js} +178 -79
- package/lib/plugin/autoDelay.js +36 -40
- package/lib/plugin/coverage.js +131 -78
- package/lib/plugin/customLocator.js +22 -21
- package/lib/plugin/customReporter.js +53 -0
- package/lib/plugin/enhancedRetryFailedStep.js +99 -0
- package/lib/plugin/heal.js +101 -110
- package/lib/plugin/htmlReporter.js +3648 -0
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/pauseOnFail.js +12 -11
- package/lib/plugin/retryFailedStep.js +82 -47
- package/lib/plugin/screenshotOnFail.js +111 -92
- package/lib/plugin/stepByStepReport.js +159 -101
- package/lib/plugin/stepTimeout.js +20 -25
- package/lib/plugin/subtitles.js +38 -38
- package/lib/recorder.js +193 -130
- package/lib/rerun.js +94 -49
- package/lib/result.js +238 -0
- package/lib/retryCoordinator.js +207 -0
- package/lib/secret.js +20 -18
- package/lib/session.js +95 -89
- package/lib/step/base.js +239 -0
- package/lib/step/comment.js +10 -0
- package/lib/step/config.js +50 -0
- package/lib/step/func.js +46 -0
- package/lib/step/helper.js +50 -0
- package/lib/step/meta.js +99 -0
- package/lib/step/record.js +74 -0
- package/lib/step/retry.js +11 -0
- package/lib/step/section.js +55 -0
- package/lib/step.js +18 -329
- package/lib/steps.js +54 -0
- package/lib/store.js +38 -7
- package/lib/template/heal.js +3 -12
- package/lib/template/prompts/generatePageObject.js +31 -0
- package/lib/template/prompts/healStep.js +13 -0
- package/lib/template/prompts/writeStep.js +9 -0
- package/lib/test-server.js +334 -0
- package/lib/timeout.js +60 -0
- package/lib/transform.js +8 -8
- package/lib/translation.js +34 -21
- package/lib/utils/loaderCheck.js +124 -0
- package/lib/utils/mask_data.js +47 -0
- package/lib/utils/typescript.js +237 -0
- package/lib/utils.js +411 -228
- package/lib/workerStorage.js +37 -34
- package/lib/workers.js +532 -296
- package/package.json +124 -95
- package/translations/de-DE.js +5 -3
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +22 -12
- package/translations/it-IT.js +4 -3
- package/translations/ja-JP.js +4 -3
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +4 -3
- package/translations/pt-BR.js +4 -3
- package/translations/ru-RU.js +4 -3
- package/translations/utils.js +10 -0
- package/translations/zh-CN.js +4 -3
- package/translations/zh-TW.js +4 -3
- package/typings/index.d.ts +546 -185
- package/typings/promiseBasedTypes.d.ts +150 -875
- package/typings/types.d.ts +547 -992
- package/lib/cli.js +0 -249
- package/lib/dirname.js +0 -5
- package/lib/helper/Expect.js +0 -425
- package/lib/helper/ExpectHelper.js +0 -399
- package/lib/helper/MockServer.js +0 -223
- package/lib/helper/Nightmare.js +0 -1411
- package/lib/helper/Protractor.js +0 -1835
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/helper/TestCafe.js +0 -1410
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -63
- package/lib/interfaces/bdd.js +0 -98
- package/lib/interfaces/featureConfig.js +0 -69
- package/lib/interfaces/gherkin.js +0 -195
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/retry.js +0 -68
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -110
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/commentStep.js +0 -136
- package/lib/plugin/debugErrors.js +0 -67
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/retryTo.js +0 -121
- package/lib/plugin/selenoid.js +0 -371
- package/lib/plugin/standardActingHelpers.js +0 -9
- package/lib/plugin/tryTo.js +0 -105
- package/lib/plugin/wdio.js +0 -246
- package/lib/scenario.js +0 -222
- package/lib/ui.js +0 -238
- package/lib/within.js +0 -70
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import promiseRetry from 'promise-retry'
|
|
2
|
+
import event from '../event.js'
|
|
3
|
+
import recorder from '../recorder.js'
|
|
4
|
+
import assertThrown from '../assert/throws.js'
|
|
5
|
+
import { ucfirst, isAsyncFunction } from '../utils.js'
|
|
6
|
+
import { getInjectedArguments } from './inject.js'
|
|
7
|
+
import { fireHook } from './hooks.js'
|
|
8
|
+
|
|
9
|
+
const injectHook = function (inject, suite) {
|
|
10
|
+
try {
|
|
11
|
+
inject()
|
|
12
|
+
} catch (err) {
|
|
13
|
+
recorder.throw(err)
|
|
14
|
+
}
|
|
15
|
+
recorder.catch(err => {
|
|
16
|
+
suiteTestFailedHookError(suite, err)
|
|
17
|
+
throw err
|
|
18
|
+
})
|
|
19
|
+
return recorder.promise()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function suiteTestFailedHookError(suite, err, hookName) {
|
|
23
|
+
suite.eachTest(test => {
|
|
24
|
+
test.err = err
|
|
25
|
+
if (hookName) hookName = ucfirst(hookName)
|
|
26
|
+
event.emit(event.test.failed, test, err, hookName)
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function makeDoneCallableOnce(done) {
|
|
31
|
+
let called = false
|
|
32
|
+
return function (err) {
|
|
33
|
+
if (called) {
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
called = true
|
|
37
|
+
return done(err)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Wraps test function, injects support objects from container,
|
|
43
|
+
* starts promise chain with recorder, performs before/after hooks
|
|
44
|
+
* through event system.
|
|
45
|
+
*/
|
|
46
|
+
export function test(test) {
|
|
47
|
+
const testFn = test.fn
|
|
48
|
+
if (!testFn) {
|
|
49
|
+
return test
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
test.timeout(0)
|
|
53
|
+
test.async = true
|
|
54
|
+
|
|
55
|
+
test.fn = function (done) {
|
|
56
|
+
const doneFn = makeDoneCallableOnce(done)
|
|
57
|
+
recorder.errHandler(err => {
|
|
58
|
+
recorder.session.start('teardown')
|
|
59
|
+
recorder.cleanAsyncErr()
|
|
60
|
+
if (test.throws) {
|
|
61
|
+
// check that test should actually fail
|
|
62
|
+
try {
|
|
63
|
+
assertThrown(err, test.throws)
|
|
64
|
+
event.emit(event.test.passed, test)
|
|
65
|
+
event.emit(event.test.finished, test)
|
|
66
|
+
recorder.add(doneFn)
|
|
67
|
+
return
|
|
68
|
+
} catch (newErr) {
|
|
69
|
+
err = newErr
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
test.err = err
|
|
73
|
+
event.emit(event.test.failed, test, err)
|
|
74
|
+
event.emit(event.test.finished, test)
|
|
75
|
+
recorder.add(() => doneFn(err))
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
event.emit(event.test.started, test)
|
|
79
|
+
|
|
80
|
+
getInjectedArguments(testFn, test)
|
|
81
|
+
.then(args => {
|
|
82
|
+
// Start recorder to ensure any steps added within test function are executed
|
|
83
|
+
recorder.startUnlessRunning()
|
|
84
|
+
|
|
85
|
+
// Execute test function
|
|
86
|
+
const result = testFn.call(test, args)
|
|
87
|
+
|
|
88
|
+
// Wait for all recorder steps to complete
|
|
89
|
+
if (result && result.then) {
|
|
90
|
+
return result.then(() => recorder.promise())
|
|
91
|
+
}
|
|
92
|
+
return recorder.promise()
|
|
93
|
+
})
|
|
94
|
+
.then(() => {
|
|
95
|
+
recorder.add('fire test.passed', () => {
|
|
96
|
+
event.emit(event.test.passed, test)
|
|
97
|
+
event.emit(event.test.finished, test)
|
|
98
|
+
})
|
|
99
|
+
recorder.add('finish test', doneFn)
|
|
100
|
+
})
|
|
101
|
+
.catch(err => {
|
|
102
|
+
recorder.throw(err)
|
|
103
|
+
})
|
|
104
|
+
.finally(() => {
|
|
105
|
+
recorder.catch()
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
return test
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Injects arguments to function from controller
|
|
113
|
+
*/
|
|
114
|
+
export function injected(fn, suite, hookName) {
|
|
115
|
+
return function (done) {
|
|
116
|
+
const doneFn = makeDoneCallableOnce(done)
|
|
117
|
+
const errHandler = err => {
|
|
118
|
+
recorder.session.start('teardown')
|
|
119
|
+
recorder.cleanAsyncErr()
|
|
120
|
+
if (['before', 'beforeSuite'].includes(hookName)) {
|
|
121
|
+
suiteTestFailedHookError(suite, err, hookName)
|
|
122
|
+
}
|
|
123
|
+
if (hookName === 'after') {
|
|
124
|
+
suiteTestFailedHookError(suite, err, hookName)
|
|
125
|
+
suite.eachTest(test => {
|
|
126
|
+
event.emit(event.test.after, test)
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
if (hookName === 'afterSuite') {
|
|
130
|
+
suiteTestFailedHookError(suite, err, hookName)
|
|
131
|
+
event.emit(event.suite.after, suite)
|
|
132
|
+
}
|
|
133
|
+
recorder.add(() => doneFn(err))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
recorder.errHandler(err => {
|
|
137
|
+
errHandler(err)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
if (!fn) throw new Error('fn is not defined')
|
|
141
|
+
|
|
142
|
+
fireHook(event.hook.started, suite)
|
|
143
|
+
|
|
144
|
+
this.test.body = fn.toString()
|
|
145
|
+
|
|
146
|
+
if (!recorder.isRunning()) {
|
|
147
|
+
recorder.errHandler(err => {
|
|
148
|
+
errHandler(err)
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const opts = suite.opts || {}
|
|
153
|
+
const retries = opts[`retry${ucfirst(hookName)}`] || 0
|
|
154
|
+
|
|
155
|
+
const currentTest = hookName === 'before' || hookName === 'after' ? suite?.ctx?.currentTest : null
|
|
156
|
+
|
|
157
|
+
promiseRetry(
|
|
158
|
+
async (retry, number) => {
|
|
159
|
+
try {
|
|
160
|
+
recorder.startUnlessRunning()
|
|
161
|
+
const injectedArgs = await getInjectedArguments(fn, null, suite)
|
|
162
|
+
await fn.call(this, { ...injectedArgs, suite, test: currentTest })
|
|
163
|
+
await recorder.promise().catch(err => retry(err))
|
|
164
|
+
} catch (err) {
|
|
165
|
+
retry(err)
|
|
166
|
+
} finally {
|
|
167
|
+
if (number < retries) {
|
|
168
|
+
recorder.stop()
|
|
169
|
+
recorder.start()
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
{ retries },
|
|
174
|
+
)
|
|
175
|
+
.then(() => {
|
|
176
|
+
recorder.add('fire hook.passed', () => fireHook(event.hook.passed, suite))
|
|
177
|
+
recorder.add('fire hook.finished', () => fireHook(event.hook.finished, suite))
|
|
178
|
+
recorder.add(`finish ${hookName} hook`, doneFn)
|
|
179
|
+
recorder.catch()
|
|
180
|
+
})
|
|
181
|
+
.catch(e => {
|
|
182
|
+
recorder.throw(e)
|
|
183
|
+
recorder.catch(e => {
|
|
184
|
+
const err = recorder.getAsyncErr() === null ? e : recorder.getAsyncErr()
|
|
185
|
+
errHandler(err)
|
|
186
|
+
})
|
|
187
|
+
recorder.add('fire hook.failed', () => fireHook(event.hook.failed, suite, e))
|
|
188
|
+
recorder.add('fire hook.finished', () => fireHook(event.hook.finished, suite))
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Starts promise chain, so helpers could enqueue their hooks
|
|
195
|
+
*/
|
|
196
|
+
export function setup(suite) {
|
|
197
|
+
return function (done) {
|
|
198
|
+
const doneFn = makeDoneCallableOnce(done)
|
|
199
|
+
recorder.startUnlessRunning()
|
|
200
|
+
import('./test.js').then(testModule => {
|
|
201
|
+
const { enhanceMochaTest } = testModule.default || testModule
|
|
202
|
+
event.emit(event.test.before, enhanceMochaTest(suite?.ctx?.currentTest))
|
|
203
|
+
recorder.add(() => doneFn())
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function teardown(suite) {
|
|
209
|
+
return function (done) {
|
|
210
|
+
const doneFn = makeDoneCallableOnce(done)
|
|
211
|
+
recorder.startUnlessRunning()
|
|
212
|
+
import('./test.js').then(testModule => {
|
|
213
|
+
const { enhanceMochaTest } = testModule.default || testModule
|
|
214
|
+
event.emit(event.test.after, enhanceMochaTest(suite?.ctx?.currentTest))
|
|
215
|
+
recorder.add(() => doneFn())
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function suiteSetup(suite) {
|
|
221
|
+
return function (done) {
|
|
222
|
+
const doneFn = makeDoneCallableOnce(done)
|
|
223
|
+
recorder.startUnlessRunning()
|
|
224
|
+
|
|
225
|
+
// Set up error handler for suite setup
|
|
226
|
+
recorder.errHandler(err => {
|
|
227
|
+
doneFn(err)
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
import('./suite.js')
|
|
231
|
+
.then(suiteModule => {
|
|
232
|
+
const { enhanceMochaSuite } = suiteModule.default || suiteModule
|
|
233
|
+
event.emit(event.suite.before, enhanceMochaSuite(suite))
|
|
234
|
+
recorder.add(() => doneFn())
|
|
235
|
+
})
|
|
236
|
+
.catch(err => {
|
|
237
|
+
doneFn(err)
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function suiteTeardown(suite) {
|
|
243
|
+
return function (done) {
|
|
244
|
+
const doneFn = makeDoneCallableOnce(done)
|
|
245
|
+
recorder.startUnlessRunning()
|
|
246
|
+
|
|
247
|
+
// Set up error handler for suite teardown
|
|
248
|
+
recorder.errHandler(err => {
|
|
249
|
+
doneFn(err)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
import('./suite.js')
|
|
253
|
+
.then(suiteModule => {
|
|
254
|
+
const { enhanceMochaSuite } = suiteModule.default || suiteModule
|
|
255
|
+
event.emit(event.suite.after, enhanceMochaSuite(suite))
|
|
256
|
+
recorder.add(() => doneFn())
|
|
257
|
+
})
|
|
258
|
+
.catch(err => {
|
|
259
|
+
doneFn(err)
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export { getInjectedArguments }
|
|
265
|
+
|
|
266
|
+
export default {
|
|
267
|
+
test,
|
|
268
|
+
injected,
|
|
269
|
+
setup,
|
|
270
|
+
teardown,
|
|
271
|
+
suiteSetup,
|
|
272
|
+
suiteTeardown,
|
|
273
|
+
getInjectedArguments,
|
|
274
|
+
}
|
package/lib/mocha/bdd.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { CucumberExpression, ParameterTypeRegistry, ParameterType } from '@cucumber/cucumber-expressions'
|
|
2
|
+
import event from '../event.js'
|
|
3
|
+
|
|
4
|
+
let steps = {}
|
|
5
|
+
let Config
|
|
6
|
+
|
|
7
|
+
const STACK_POSITION = 2
|
|
8
|
+
|
|
9
|
+
async function getConfig() {
|
|
10
|
+
if (!Config) {
|
|
11
|
+
const ConfigModule = await import('../config.js')
|
|
12
|
+
Config = ConfigModule.default || ConfigModule
|
|
13
|
+
}
|
|
14
|
+
return Config
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {*} step
|
|
19
|
+
* @param {*} fn
|
|
20
|
+
*/
|
|
21
|
+
// Current file being loaded for step tracking
|
|
22
|
+
let currentStepFile = null
|
|
23
|
+
|
|
24
|
+
export function setCurrentStepFile(filePath) {
|
|
25
|
+
currentStepFile = filePath
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function clearCurrentStepFile() {
|
|
29
|
+
currentStepFile = null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const addStep = async (step, fn) => {
|
|
33
|
+
const config = await getConfig()
|
|
34
|
+
const avoidDuplicateSteps = config.get('gherkin', {}).avoidDuplicateSteps || false
|
|
35
|
+
if (avoidDuplicateSteps && steps[step]) {
|
|
36
|
+
throw new Error(`Step '${step}' is already defined`)
|
|
37
|
+
}
|
|
38
|
+
steps[step] = fn
|
|
39
|
+
|
|
40
|
+
// Use the current step file context if available (fallback for old usage)
|
|
41
|
+
if (currentStepFile) {
|
|
42
|
+
let relativePath = currentStepFile
|
|
43
|
+
|
|
44
|
+
// Remove any leading './' and keep step_definitions/ path
|
|
45
|
+
relativePath = relativePath.replace(/^\.\//, '').replace(/^.*\/(?=step_definitions)/, '')
|
|
46
|
+
|
|
47
|
+
fn.line = `${relativePath}:3:1`
|
|
48
|
+
} else {
|
|
49
|
+
fn.line = 'unknown_file:1:1'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const parameterTypeRegistry = new ParameterTypeRegistry()
|
|
54
|
+
|
|
55
|
+
const matchStep = step => {
|
|
56
|
+
for (const stepName in steps) {
|
|
57
|
+
if (stepName.indexOf('/') === 0) {
|
|
58
|
+
const regExpArr = stepName.match(/^\/(.*?)\/([gimy]*)$/) || []
|
|
59
|
+
const res = step.match(new RegExp(regExpArr[1], regExpArr[2]))
|
|
60
|
+
if (res) {
|
|
61
|
+
const fn = steps[stepName]
|
|
62
|
+
fn.params = res.slice(1)
|
|
63
|
+
return fn
|
|
64
|
+
}
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
const expression = new CucumberExpression(stepName, parameterTypeRegistry)
|
|
68
|
+
const res = expression.match(step)
|
|
69
|
+
if (res) {
|
|
70
|
+
const fn = steps[stepName]
|
|
71
|
+
fn.params = res.map(arg => arg.getValue(null))
|
|
72
|
+
return fn
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
throw new Error(`No steps matching "${step.toString()}"`)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const clearSteps = () => {
|
|
79
|
+
steps = {}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const getSteps = () => {
|
|
83
|
+
return steps
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const defineParameterType = options => {
|
|
87
|
+
const parameterType = buildParameterType(options)
|
|
88
|
+
parameterTypeRegistry.defineParameterType(parameterType)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const buildParameterType = ({ name, regexp, transformer, useForSnippets, preferForRegexpMatch }) => {
|
|
92
|
+
if (typeof useForSnippets !== 'boolean') useForSnippets = true
|
|
93
|
+
if (typeof preferForRegexpMatch !== 'boolean') preferForRegexpMatch = false
|
|
94
|
+
return new ParameterType(name, regexp, null, transformer, useForSnippets, preferForRegexpMatch)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Create wrapper functions that capture the call context
|
|
98
|
+
const createStepFunction = stepType => {
|
|
99
|
+
return (step, fn) => {
|
|
100
|
+
// Capture the stack trace at the point where Given/When/Then is called
|
|
101
|
+
const callStack = new Error().stack
|
|
102
|
+
|
|
103
|
+
// Find the caller (step definition file) in the stack
|
|
104
|
+
let callerInfo = 'unknown_file:1:1'
|
|
105
|
+
if (callStack) {
|
|
106
|
+
const stackLines = callStack.split('\n')
|
|
107
|
+
for (let i = 1; i < stackLines.length; i++) {
|
|
108
|
+
const line = stackLines[i]
|
|
109
|
+
if (line.includes('step_definitions') && (line.includes('.js') || line.includes('.mjs'))) {
|
|
110
|
+
// Extract file path and use line 3:1 as consistent reference (import line)
|
|
111
|
+
const match = line.match(/file:\/\/.*\/(step_definitions\/[^:]+):(\d+):(\d+)/)
|
|
112
|
+
if (match) {
|
|
113
|
+
callerInfo = `${match[1]}:3:1` // Use line 3:1 consistently (import line)
|
|
114
|
+
break
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Instead of using global currentStepFile, pass the caller info directly to addStep
|
|
121
|
+
return addStepWithCaller(step, fn, callerInfo)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// New function that accepts caller info directly
|
|
126
|
+
const addStepWithCaller = async (step, fn, callerInfo) => {
|
|
127
|
+
const config = await getConfig()
|
|
128
|
+
const avoidDuplicateSteps = config.get('gherkin', {}).avoidDuplicateSteps || false
|
|
129
|
+
if (avoidDuplicateSteps && steps[step]) {
|
|
130
|
+
throw new Error(`Step '${step}' is already defined`)
|
|
131
|
+
}
|
|
132
|
+
steps[step] = fn
|
|
133
|
+
|
|
134
|
+
// Use the caller info passed directly
|
|
135
|
+
fn.line = callerInfo
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const Given = createStepFunction('Given')
|
|
139
|
+
const When = createStepFunction('When')
|
|
140
|
+
const Then = createStepFunction('Then')
|
|
141
|
+
const And = createStepFunction('And')
|
|
142
|
+
|
|
143
|
+
// Before/After hooks for BDD - these are global event listeners
|
|
144
|
+
const Before = fn => {
|
|
145
|
+
event.dispatcher.on(event.test.started, fn)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const After = fn => {
|
|
149
|
+
event.dispatcher.on(event.test.finished, fn)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const Fail = fn => {
|
|
153
|
+
event.dispatcher.on(event.test.failed, fn)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export { Given, When, Then, And, Before, After, Fail, matchStep, getSteps, clearSteps, defineParameterType }
|
|
157
|
+
|
|
158
|
+
export default {
|
|
159
|
+
Given: addStep,
|
|
160
|
+
When: addStep,
|
|
161
|
+
Then: addStep,
|
|
162
|
+
And: addStep,
|
|
163
|
+
matchStep,
|
|
164
|
+
getSteps,
|
|
165
|
+
clearSteps,
|
|
166
|
+
defineParameterType,
|
|
167
|
+
}
|