codeceptjs 4.0.0-beta.5 → 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.
- package/README.md +0 -45
- package/bin/codecept.js +46 -57
- package/lib/actor.js +15 -11
- package/lib/ai.js +6 -5
- package/lib/assert/empty.js +9 -8
- package/lib/assert/equal.js +15 -17
- package/lib/assert/error.js +2 -2
- package/lib/assert/include.js +9 -11
- package/lib/assert/throws.js +1 -1
- package/lib/assert/truth.js +8 -5
- package/lib/assert.js +18 -18
- package/lib/codecept.js +66 -107
- package/lib/colorUtils.js +48 -50
- package/lib/command/check.js +32 -27
- package/lib/command/configMigrate.js +11 -10
- package/lib/command/definitions.js +16 -10
- package/lib/command/dryRun.js +16 -16
- package/lib/command/generate.js +29 -26
- package/lib/command/gherkin/init.js +36 -38
- package/lib/command/gherkin/snippets.js +14 -14
- package/lib/command/gherkin/steps.js +21 -18
- package/lib/command/info.js +8 -8
- package/lib/command/init.js +34 -31
- package/lib/command/interactive.js +11 -10
- package/lib/command/list.js +10 -9
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +3 -3
- package/lib/command/run-multiple.js +16 -13
- package/lib/command/run-rerun.js +6 -7
- package/lib/command/run-workers.js +10 -24
- package/lib/command/run.js +8 -8
- package/lib/command/utils.js +20 -18
- package/lib/command/workers/runTests.js +117 -269
- package/lib/config.js +111 -49
- package/lib/container.js +299 -102
- package/lib/data/context.js +6 -5
- package/lib/data/dataScenarioConfig.js +1 -1
- package/lib/data/dataTableArgument.js +1 -1
- package/lib/data/table.js +1 -1
- package/lib/effects.js +94 -10
- package/lib/els.js +11 -9
- package/lib/event.js +11 -10
- package/lib/globals.js +141 -0
- package/lib/heal.js +12 -12
- package/lib/helper/AI.js +1 -1
- package/lib/helper/ApiDataFactory.js +16 -13
- package/lib/helper/FileSystem.js +32 -12
- package/lib/helper/GraphQL.js +1 -1
- package/lib/helper/GraphQLDataFactory.js +1 -1
- package/lib/helper/JSONResponse.js +19 -30
- package/lib/helper/Mochawesome.js +9 -28
- package/lib/helper/Playwright.js +668 -265
- package/lib/helper/Puppeteer.js +284 -169
- package/lib/helper/REST.js +29 -12
- package/lib/helper/WebDriver.js +191 -71
- 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/PlaywrightRestartOpts.js +23 -23
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +29 -30
- package/lib/helper/network/actions.js +33 -48
- package/lib/helper/network/utils.js +76 -83
- package/lib/helper/scripts/blurElement.js +6 -6
- package/lib/helper/scripts/focusElement.js +6 -6
- package/lib/helper/scripts/highlightElement.js +9 -9
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -1
- package/lib/history.js +23 -20
- package/lib/hooks.js +10 -10
- package/lib/html.js +90 -100
- package/lib/index.js +48 -21
- package/lib/listener/config.js +8 -9
- package/lib/listener/emptyRun.js +6 -7
- package/lib/listener/exit.js +4 -3
- package/lib/listener/globalRetry.js +5 -5
- package/lib/listener/globalTimeout.js +11 -10
- package/lib/listener/helpers.js +33 -14
- package/lib/listener/mocha.js +3 -4
- package/lib/listener/result.js +4 -5
- package/lib/listener/steps.js +7 -18
- package/lib/listener/store.js +3 -3
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +108 -75
- package/lib/mocha/bdd.js +99 -13
- package/lib/mocha/cli.js +60 -27
- package/lib/mocha/factory.js +75 -19
- package/lib/mocha/featureConfig.js +1 -1
- package/lib/mocha/gherkin.js +57 -25
- package/lib/mocha/hooks.js +12 -3
- package/lib/mocha/index.js +13 -4
- package/lib/mocha/inject.js +22 -5
- package/lib/mocha/scenarioConfig.js +2 -2
- package/lib/mocha/suite.js +9 -2
- package/lib/mocha/test.js +10 -13
- package/lib/mocha/ui.js +28 -31
- package/lib/output.js +11 -9
- package/lib/parser.js +44 -44
- package/lib/pause.js +15 -16
- package/lib/plugin/analyze.js +19 -12
- package/lib/plugin/auth.js +20 -21
- package/lib/plugin/autoDelay.js +12 -8
- package/lib/plugin/coverage.js +12 -8
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/customReporter.js +3 -2
- package/lib/plugin/heal.js +14 -9
- package/lib/plugin/pageInfo.js +10 -10
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +47 -5
- package/lib/plugin/screenshotOnFail.js +75 -37
- package/lib/plugin/stepByStepReport.js +14 -14
- package/lib/plugin/stepTimeout.js +4 -3
- package/lib/plugin/subtitles.js +6 -5
- package/lib/recorder.js +33 -23
- package/lib/rerun.js +69 -26
- package/lib/result.js +4 -4
- package/lib/secret.js +18 -17
- package/lib/session.js +95 -89
- package/lib/step/base.js +6 -6
- package/lib/step/config.js +1 -1
- package/lib/step/func.js +3 -3
- package/lib/step/helper.js +3 -3
- package/lib/step/meta.js +4 -4
- package/lib/step/record.js +11 -11
- package/lib/step/retry.js +3 -3
- package/lib/step/section.js +3 -3
- package/lib/step.js +7 -10
- package/lib/steps.js +9 -5
- package/lib/store.js +1 -1
- package/lib/timeout.js +1 -7
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils.js +68 -97
- package/lib/workerStorage.js +16 -17
- package/lib/workers.js +145 -171
- package/package.json +63 -57
- package/translations/de-DE.js +2 -2
- package/translations/fr-FR.js +2 -2
- package/translations/index.js +23 -10
- package/translations/it-IT.js +2 -2
- package/translations/ja-JP.js +2 -2
- package/translations/nl-NL.js +2 -2
- package/translations/pl-PL.js +2 -2
- package/translations/pt-BR.js +2 -2
- package/translations/ru-RU.js +2 -2
- package/translations/utils.js +11 -2
- package/translations/zh-CN.js +2 -2
- package/translations/zh-TW.js +2 -2
- package/typings/index.d.ts +7 -18
- package/typings/promiseBasedTypes.d.ts +3769 -5450
- package/typings/types.d.ts +3953 -5778
- package/bin/test-server.js +0 -53
- package/lib/element/WebElement.js +0 -327
- package/lib/helper/Nightmare.js +0 -1486
- package/lib/helper/Protractor.js +0 -1840
- package/lib/helper/TestCafe.js +0 -1391
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -43
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -61
- package/lib/listener/retryEnhancer.js +0 -85
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/autoLogin.js +0 -5
- package/lib/plugin/commentStep.js +0 -141
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/htmlReporter.js +0 -1947
- package/lib/plugin/retryTo.js +0 -16
- package/lib/plugin/selenoid.js +0 -364
- package/lib/plugin/standardActingHelpers.js +0 -6
- package/lib/plugin/tryTo.js +0 -16
- package/lib/plugin/wdio.js +0 -247
- package/lib/test-server.js +0 -323
- package/lib/within.js +0 -90
package/lib/container.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import { globSync } from 'glob'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import debugModule from 'debug'
|
|
4
|
+
const debug = debugModule('codeceptjs:container')
|
|
5
|
+
import { MetaStep } from './step.js'
|
|
6
|
+
import { methodsOfObject, fileExists, isFunction, isAsyncFunction, installedLocally, deepMerge } from './utils.js'
|
|
7
|
+
import Translation from './translation.js'
|
|
8
|
+
import MochaFactory from './mocha/factory.js'
|
|
9
|
+
import recorder from './recorder.js'
|
|
10
|
+
import event from './event.js'
|
|
11
|
+
import WorkerStorage from './workerStorage.js'
|
|
12
|
+
import store from './store.js'
|
|
13
|
+
import Result from './result.js'
|
|
14
|
+
import ai from './ai.js'
|
|
15
|
+
import actorFactory from './actor.js'
|
|
14
16
|
|
|
15
17
|
let asyncHelperPromise
|
|
16
18
|
|
|
@@ -28,7 +30,6 @@ let container = {
|
|
|
28
30
|
translation: {},
|
|
29
31
|
/** @type {Result | null} */
|
|
30
32
|
result: null,
|
|
31
|
-
sharedKeys: new Set() // Track keys shared via share() function
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/**
|
|
@@ -40,7 +41,7 @@ class Container {
|
|
|
40
41
|
*
|
|
41
42
|
*/
|
|
42
43
|
static get STANDARD_ACTING_HELPERS() {
|
|
43
|
-
return ['Playwright', 'WebDriver', 'Puppeteer', 'Appium'
|
|
44
|
+
return ['Playwright', 'WebDriver', 'Puppeteer', 'Appium']
|
|
44
45
|
}
|
|
45
46
|
/**
|
|
46
47
|
* Create container with all required helpers and support objects
|
|
@@ -49,7 +50,7 @@ class Container {
|
|
|
49
50
|
* @param {*} config
|
|
50
51
|
* @param {*} opts
|
|
51
52
|
*/
|
|
52
|
-
static create(config, opts) {
|
|
53
|
+
static async create(config, opts) {
|
|
53
54
|
debug('creating container')
|
|
54
55
|
asyncHelperPromise = Promise.resolve()
|
|
55
56
|
|
|
@@ -61,16 +62,53 @@ class Container {
|
|
|
61
62
|
|
|
62
63
|
// create support objects
|
|
63
64
|
container.support = {}
|
|
64
|
-
container.helpers = createHelpers(config.helpers || {})
|
|
65
|
-
container.translation = loadTranslation(config.translation || null, config.vocabularies || [])
|
|
65
|
+
container.helpers = await createHelpers(config.helpers || {})
|
|
66
|
+
container.translation = await loadTranslation(config.translation || null, config.vocabularies || [])
|
|
66
67
|
container.proxySupport = createSupportObjects(config.include || {})
|
|
67
|
-
container.plugins = createPlugins(config.plugins || {}, opts)
|
|
68
|
+
container.plugins = await createPlugins(config.plugins || {}, opts)
|
|
68
69
|
container.result = new Result()
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
// Preload includes (so proxies can expose real objects synchronously)
|
|
72
|
+
const includes = config.include || {}
|
|
73
|
+
|
|
74
|
+
// Ensure I is available for DI modules at import time
|
|
75
|
+
if (Object.prototype.hasOwnProperty.call(includes, 'I')) {
|
|
76
|
+
try {
|
|
77
|
+
const mod = includes.I
|
|
78
|
+
if (typeof mod === 'string') {
|
|
79
|
+
container.support.I = await loadSupportObject(mod, 'I')
|
|
80
|
+
} else if (typeof mod === 'function') {
|
|
81
|
+
container.support.I = await loadSupportObject(mod, 'I')
|
|
82
|
+
} else if (mod && typeof mod === 'object') {
|
|
83
|
+
container.support.I = mod
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
throw new Error(`Could not include object I: ${e.message}`)
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
// Create default actor if not provided via includes
|
|
90
|
+
createActor()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Load remaining includes except I
|
|
94
|
+
for (const [name, mod] of Object.entries(includes)) {
|
|
95
|
+
if (name === 'I') continue
|
|
96
|
+
try {
|
|
97
|
+
if (typeof mod === 'string') {
|
|
98
|
+
container.support[name] = await loadSupportObject(mod, name)
|
|
99
|
+
} else if (typeof mod === 'function') {
|
|
100
|
+
// function or class
|
|
101
|
+
container.support[name] = await loadSupportObject(mod, name)
|
|
102
|
+
} else if (mod && typeof mod === 'object') {
|
|
103
|
+
container.support[name] = mod
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
throw new Error(`Could not include object ${name}: ${e.message}`)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
71
109
|
|
|
72
110
|
if (opts && opts.ai) ai.enable(config.ai) // enable AI Assistant
|
|
73
|
-
if (config.gherkin)
|
|
111
|
+
if (config.gherkin) await loadGherkinStepsAsync(config.gherkin.steps || [])
|
|
74
112
|
if (opts && typeof opts.timeouts === 'boolean') store.timeouts = opts.timeouts
|
|
75
113
|
}
|
|
76
114
|
|
|
@@ -103,7 +141,8 @@ class Container {
|
|
|
103
141
|
if (!name) {
|
|
104
142
|
return container.proxySupport
|
|
105
143
|
}
|
|
106
|
-
return
|
|
144
|
+
// Always return the proxy to ensure MetaStep creation works
|
|
145
|
+
return container.proxySupport[name]
|
|
107
146
|
}
|
|
108
147
|
|
|
109
148
|
/**
|
|
@@ -158,8 +197,14 @@ class Container {
|
|
|
158
197
|
* @param {Object<string, *>} newContainer
|
|
159
198
|
*/
|
|
160
199
|
static append(newContainer) {
|
|
161
|
-
const deepMerge = require('./utils').deepMerge
|
|
162
200
|
container = deepMerge(container, newContainer)
|
|
201
|
+
|
|
202
|
+
// If new support objects are added, update the proxy support
|
|
203
|
+
if (newContainer.support) {
|
|
204
|
+
const newProxySupport = createSupportObjects(newContainer.support)
|
|
205
|
+
container.proxySupport = { ...container.proxySupport, ...newProxySupport }
|
|
206
|
+
}
|
|
207
|
+
|
|
163
208
|
debug('appended', JSON.stringify(newContainer).slice(0, 300))
|
|
164
209
|
}
|
|
165
210
|
|
|
@@ -170,12 +215,11 @@ class Container {
|
|
|
170
215
|
* @param {Object<string, *>} newSupport
|
|
171
216
|
* @param {Object<string, *>} newPlugins
|
|
172
217
|
*/
|
|
173
|
-
static clear(newHelpers = {}, newSupport = {}, newPlugins = {}) {
|
|
218
|
+
static async clear(newHelpers = {}, newSupport = {}, newPlugins = {}) {
|
|
174
219
|
container.helpers = newHelpers
|
|
175
|
-
container.translation = loadTranslation()
|
|
220
|
+
container.translation = await loadTranslation()
|
|
176
221
|
container.proxySupport = createSupportObjects(newSupport)
|
|
177
222
|
container.plugins = newPlugins
|
|
178
|
-
container.sharedKeys = new Set() // Clear shared keys
|
|
179
223
|
asyncHelperPromise = Promise.resolve()
|
|
180
224
|
store.actor = null
|
|
181
225
|
debug('container cleared')
|
|
@@ -199,13 +243,7 @@ class Container {
|
|
|
199
243
|
* @param {Object} options - set {local: true} to not share among workers
|
|
200
244
|
*/
|
|
201
245
|
static share(data, options = {}) {
|
|
202
|
-
|
|
203
|
-
// directly update the support object to maintain proxy references
|
|
204
|
-
Object.assign(container.support, data)
|
|
205
|
-
|
|
206
|
-
// Track which keys were explicitly shared
|
|
207
|
-
Object.keys(data).forEach(key => container.sharedKeys.add(key))
|
|
208
|
-
|
|
246
|
+
Container.append({ support: data })
|
|
209
247
|
if (!options.local) {
|
|
210
248
|
WorkerStorage.share(data)
|
|
211
249
|
}
|
|
@@ -220,23 +258,56 @@ class Container {
|
|
|
220
258
|
}
|
|
221
259
|
}
|
|
222
260
|
|
|
223
|
-
|
|
261
|
+
export default Container
|
|
224
262
|
|
|
225
|
-
function createHelpers(config) {
|
|
263
|
+
async function createHelpers(config) {
|
|
226
264
|
const helpers = {}
|
|
227
265
|
for (let helperName in config) {
|
|
228
266
|
try {
|
|
229
267
|
let HelperClass
|
|
230
268
|
|
|
231
|
-
// ESM import
|
|
232
|
-
if (helperName
|
|
269
|
+
// Check if helper class was stored in config during ESM import processing
|
|
270
|
+
if (config[helperName]._helperClass) {
|
|
271
|
+
HelperClass = config[helperName]._helperClass
|
|
272
|
+
debug(`helper ${helperName} loaded from ESM import`)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ESM import (legacy check)
|
|
276
|
+
if (!HelperClass && typeof helperName === 'function' && helperName.prototype) {
|
|
233
277
|
HelperClass = helperName
|
|
234
278
|
helperName = HelperClass.constructor.name
|
|
235
279
|
}
|
|
236
280
|
|
|
237
|
-
// classical require
|
|
281
|
+
// classical require - may be async for ESM modules
|
|
238
282
|
if (!HelperClass) {
|
|
239
|
-
|
|
283
|
+
const helperResult = requireHelperFromModule(helperName, config)
|
|
284
|
+
if (helperResult instanceof Promise) {
|
|
285
|
+
// Handle async ESM loading
|
|
286
|
+
helpers[helperName] = {}
|
|
287
|
+
asyncHelperPromise = asyncHelperPromise
|
|
288
|
+
.then(() => helperResult)
|
|
289
|
+
.then(async ResolvedHelperClass => {
|
|
290
|
+
debug(`helper ${helperName} resolved type: ${typeof ResolvedHelperClass}`, ResolvedHelperClass)
|
|
291
|
+
|
|
292
|
+
// Extract default export from ESM module wrapper if needed
|
|
293
|
+
if (ResolvedHelperClass && ResolvedHelperClass.__esModule && ResolvedHelperClass.default) {
|
|
294
|
+
ResolvedHelperClass = ResolvedHelperClass.default
|
|
295
|
+
debug(`extracted default export for ${helperName}, new type: ${typeof ResolvedHelperClass}`)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (typeof ResolvedHelperClass !== 'function') {
|
|
299
|
+
throw new Error(`Helper '${helperName}' is not a class. Got: ${typeof ResolvedHelperClass}`)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
checkHelperRequirements(ResolvedHelperClass)
|
|
303
|
+
helpers[helperName] = new ResolvedHelperClass(config[helperName])
|
|
304
|
+
if (helpers[helperName]._init) await helpers[helperName]._init()
|
|
305
|
+
debug(`helper ${helperName} async initialized`)
|
|
306
|
+
})
|
|
307
|
+
continue
|
|
308
|
+
} else {
|
|
309
|
+
HelperClass = helperResult
|
|
310
|
+
}
|
|
240
311
|
}
|
|
241
312
|
|
|
242
313
|
// handle async CJS modules that use dynamic import
|
|
@@ -269,7 +340,7 @@ function createHelpers(config) {
|
|
|
269
340
|
}
|
|
270
341
|
|
|
271
342
|
for (const name in helpers) {
|
|
272
|
-
if (helpers[name]._init) helpers[name]._init()
|
|
343
|
+
if (helpers[name]._init) await helpers[name]._init()
|
|
273
344
|
}
|
|
274
345
|
return helpers
|
|
275
346
|
}
|
|
@@ -290,23 +361,43 @@ function checkHelperRequirements(HelperClass) {
|
|
|
290
361
|
}
|
|
291
362
|
}
|
|
292
363
|
|
|
293
|
-
function requireHelperFromModule(helperName, config, HelperClass) {
|
|
364
|
+
async function requireHelperFromModule(helperName, config, HelperClass) {
|
|
294
365
|
const moduleName = getHelperModuleName(helperName, config)
|
|
295
366
|
if (moduleName.startsWith('./helper/')) {
|
|
296
|
-
|
|
367
|
+
try {
|
|
368
|
+
// For built-in helpers, use direct relative import with .js extension
|
|
369
|
+
const helperPath = `${moduleName}.js`
|
|
370
|
+
const mod = await import(helperPath)
|
|
371
|
+
HelperClass = mod.default || mod
|
|
372
|
+
} catch (err) {
|
|
373
|
+
throw err
|
|
374
|
+
}
|
|
297
375
|
} else {
|
|
298
376
|
// check if the new syntax export default HelperName is used and loads the Helper, otherwise loads the module that used old syntax export = HelperName.
|
|
299
377
|
try {
|
|
300
|
-
|
|
378
|
+
// Try dynamic import for both CommonJS and ESM modules
|
|
379
|
+
const mod = await import(moduleName)
|
|
301
380
|
if (!mod && !mod.default) {
|
|
302
381
|
throw new Error(`Helper module '${moduleName}' was not found. Make sure you have installed the package correctly.`)
|
|
303
382
|
}
|
|
304
383
|
HelperClass = mod.default || mod
|
|
305
384
|
} catch (err) {
|
|
306
|
-
if (err.code === '
|
|
385
|
+
if (err.code === 'ERR_REQUIRE_ESM' || (err.message && err.message.includes('ES module'))) {
|
|
386
|
+
// This is an ESM module, use dynamic import
|
|
387
|
+
try {
|
|
388
|
+
const pathModule = await import('path')
|
|
389
|
+
const absolutePath = pathModule.default.resolve(moduleName)
|
|
390
|
+
const mod = await import(absolutePath)
|
|
391
|
+
HelperClass = mod.default || mod
|
|
392
|
+
debug(`helper ${helperName} loaded via ESM import`)
|
|
393
|
+
} catch (importErr) {
|
|
394
|
+
throw new Error(`Helper module '${moduleName}' could not be imported as ESM: ${importErr.message}`)
|
|
395
|
+
}
|
|
396
|
+
} else if (err.code === 'MODULE_NOT_FOUND') {
|
|
307
397
|
throw new Error(`Helper module '${moduleName}' was not found. Make sure you have installed the package correctly.`)
|
|
398
|
+
} else {
|
|
399
|
+
throw err
|
|
308
400
|
}
|
|
309
|
-
throw err
|
|
310
401
|
}
|
|
311
402
|
}
|
|
312
403
|
return HelperClass
|
|
@@ -338,14 +429,21 @@ function createSupportObjects(config) {
|
|
|
338
429
|
}
|
|
339
430
|
|
|
340
431
|
// load actual name from vocabulary
|
|
341
|
-
if (container.translation.name) {
|
|
342
|
-
name
|
|
432
|
+
if (container.translation && container.translation.I && name === 'I') {
|
|
433
|
+
// Use translated name for I
|
|
434
|
+
const actualName = container.translation.I
|
|
435
|
+
if (actualName !== 'I') {
|
|
436
|
+
name = actualName
|
|
437
|
+
}
|
|
343
438
|
}
|
|
344
439
|
|
|
345
440
|
if (name === 'I') {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
441
|
+
if (!container.support.I) {
|
|
442
|
+
// Actor will be created during container.create()
|
|
443
|
+
return undefined
|
|
444
|
+
}
|
|
445
|
+
methodsOfObject(container.support.I)
|
|
446
|
+
return container.support.I[prop]
|
|
349
447
|
}
|
|
350
448
|
|
|
351
449
|
if (!container.support[name] && typeof config[name] === 'object') {
|
|
@@ -353,17 +451,10 @@ function createSupportObjects(config) {
|
|
|
353
451
|
}
|
|
354
452
|
|
|
355
453
|
if (!container.support[name]) {
|
|
356
|
-
//
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
if (container.support[name]._init) {
|
|
361
|
-
container.support[name]._init()
|
|
362
|
-
}
|
|
363
|
-
debug(`support object ${name} initialized`)
|
|
364
|
-
} catch (err) {
|
|
365
|
-
throw new Error(`Initialization failed for ${name}: ${container.support[name]}\n${err.message}\n${err.stack}`)
|
|
366
|
-
}
|
|
454
|
+
// Cannot load object synchronously in proxy getter
|
|
455
|
+
// Return undefined and log warning - object should be pre-loaded during container creation
|
|
456
|
+
debug(`Support object ${name} not pre-loaded, returning undefined`)
|
|
457
|
+
return undefined
|
|
367
458
|
}
|
|
368
459
|
|
|
369
460
|
const currentObject = container.support[name]
|
|
@@ -380,19 +471,32 @@ function createSupportObjects(config) {
|
|
|
380
471
|
return currentValue
|
|
381
472
|
},
|
|
382
473
|
has(target, prop) {
|
|
383
|
-
|
|
474
|
+
if (!container.support[name]) {
|
|
475
|
+
// Note: This is sync, so we can't use async loadSupportObject here
|
|
476
|
+
// The object will be loaded lazily on first property access
|
|
477
|
+
return false
|
|
478
|
+
}
|
|
384
479
|
return prop in container.support[name]
|
|
385
480
|
},
|
|
386
481
|
getOwnPropertyDescriptor(target, prop) {
|
|
387
|
-
|
|
482
|
+
if (!container.support[name]) {
|
|
483
|
+
// Object will be loaded on property access
|
|
484
|
+
return {
|
|
485
|
+
enumerable: true,
|
|
486
|
+
configurable: true,
|
|
487
|
+
value: undefined,
|
|
488
|
+
}
|
|
489
|
+
}
|
|
388
490
|
return {
|
|
389
491
|
enumerable: true,
|
|
390
492
|
configurable: true,
|
|
391
|
-
value:
|
|
493
|
+
value: container.support[name][prop],
|
|
392
494
|
}
|
|
393
495
|
},
|
|
394
496
|
ownKeys() {
|
|
395
|
-
|
|
497
|
+
if (!container.support[name]) {
|
|
498
|
+
return []
|
|
499
|
+
}
|
|
396
500
|
return Reflect.ownKeys(container.support[name])
|
|
397
501
|
},
|
|
398
502
|
},
|
|
@@ -404,23 +508,29 @@ function createSupportObjects(config) {
|
|
|
404
508
|
{},
|
|
405
509
|
{
|
|
406
510
|
has(target, key) {
|
|
407
|
-
return keys.includes(key)
|
|
511
|
+
return keys.includes(key)
|
|
408
512
|
},
|
|
409
513
|
ownKeys() {
|
|
410
|
-
|
|
411
|
-
return [...new Set([...keys, ...container.sharedKeys])]
|
|
514
|
+
return keys
|
|
412
515
|
},
|
|
413
516
|
getOwnPropertyDescriptor(target, prop) {
|
|
414
517
|
return {
|
|
415
518
|
enumerable: true,
|
|
416
519
|
configurable: true,
|
|
417
|
-
value:
|
|
520
|
+
value: target[prop],
|
|
418
521
|
}
|
|
419
522
|
},
|
|
420
523
|
get(target, key) {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
return
|
|
524
|
+
if (typeof key === 'symbol') {
|
|
525
|
+
// safely ignore symbol-based meta properties used by tooling
|
|
526
|
+
return undefined
|
|
527
|
+
}
|
|
528
|
+
// Allow special I even if not declared in includes
|
|
529
|
+
if (key === 'I') {
|
|
530
|
+
return lazyLoad('I')
|
|
531
|
+
}
|
|
532
|
+
if (!keys.includes(key)) {
|
|
533
|
+
throw new Error(`Support object "${String(key)}" is not defined`)
|
|
424
534
|
}
|
|
425
535
|
return lazyLoad(key)
|
|
426
536
|
},
|
|
@@ -431,17 +541,35 @@ function createSupportObjects(config) {
|
|
|
431
541
|
function createActor(actorPath) {
|
|
432
542
|
if (container.support.I) return container.support.I
|
|
433
543
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
} else {
|
|
437
|
-
const actor = require('./actor')
|
|
438
|
-
container.support.I = actor()
|
|
439
|
-
}
|
|
544
|
+
// Default actor
|
|
545
|
+
container.support.I = actorFactory({}, Container)
|
|
440
546
|
|
|
441
547
|
return container.support.I
|
|
442
548
|
}
|
|
443
549
|
|
|
444
|
-
function
|
|
550
|
+
async function loadPluginAsync(modulePath, config) {
|
|
551
|
+
let pluginMod
|
|
552
|
+
try {
|
|
553
|
+
// Try dynamic import first (works for both ESM and CJS)
|
|
554
|
+
pluginMod = await import(modulePath)
|
|
555
|
+
} catch (err) {
|
|
556
|
+
throw new Error(`Could not load plugin from '${modulePath}': ${err.message}`)
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const pluginFactory = pluginMod.default || pluginMod
|
|
560
|
+
if (typeof pluginFactory !== 'function') {
|
|
561
|
+
throw new Error(`Plugin '${modulePath}' is not a function. Expected a plugin factory function.`)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return pluginFactory(config)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
async function loadPluginFallback(modulePath, config) {
|
|
568
|
+
// This function is kept for backwards compatibility but now uses dynamic import
|
|
569
|
+
return await loadPluginAsync(modulePath, config)
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async function createPlugins(config, options = {}) {
|
|
445
573
|
const plugins = {}
|
|
446
574
|
|
|
447
575
|
const enabledPluginsByOptions = (options.plugins || '').split(',')
|
|
@@ -459,9 +587,12 @@ function createPlugins(config, options = {}) {
|
|
|
459
587
|
module = path.resolve(global.codecept_dir, module) // custom plugin
|
|
460
588
|
}
|
|
461
589
|
} else {
|
|
462
|
-
module = `./plugin/${pluginName}`
|
|
590
|
+
module = `./plugin/${pluginName}.js`
|
|
463
591
|
}
|
|
464
|
-
|
|
592
|
+
|
|
593
|
+
// Use async loading for all plugins (ESM and CJS)
|
|
594
|
+
plugins[pluginName] = await loadPluginAsync(module, config[pluginName])
|
|
595
|
+
debug(`plugin ${pluginName} loaded via async import`)
|
|
465
596
|
} catch (err) {
|
|
466
597
|
throw new Error(`Could not load plugin ${pluginName} from module '${module}':\n${err.message}\n${err.stack}`)
|
|
467
598
|
}
|
|
@@ -469,24 +600,34 @@ function createPlugins(config, options = {}) {
|
|
|
469
600
|
return plugins
|
|
470
601
|
}
|
|
471
602
|
|
|
472
|
-
function
|
|
603
|
+
async function loadGherkinStepsAsync(paths) {
|
|
473
604
|
global.Before = fn => event.dispatcher.on(event.test.started, fn)
|
|
474
605
|
global.After = fn => event.dispatcher.on(event.test.finished, fn)
|
|
475
606
|
global.Fail = fn => event.dispatcher.on(event.test.failed, fn)
|
|
476
607
|
|
|
608
|
+
// Import BDD module to access step file tracking functions
|
|
609
|
+
const bddModule = await import('./mocha/bdd.js')
|
|
610
|
+
|
|
477
611
|
// If gherkin.steps is string, then this will iterate through that folder and send all step def js files to loadSupportObject
|
|
478
612
|
// If gherkin.steps is Array, it will go the old way
|
|
479
613
|
// This is done so that we need not enter all Step Definition files under config.gherkin.steps
|
|
480
614
|
if (Array.isArray(paths)) {
|
|
481
615
|
for (const path of paths) {
|
|
482
|
-
|
|
616
|
+
// Set context for step definition file location tracking
|
|
617
|
+
bddModule.setCurrentStepFile(path)
|
|
618
|
+
await loadSupportObject(path, `Step Definition from ${path}`)
|
|
619
|
+
bddModule.clearCurrentStepFile()
|
|
483
620
|
}
|
|
484
621
|
} else {
|
|
485
622
|
const folderPath = paths.startsWith('.') ? normalizeAndJoin(global.codecept_dir, paths) : ''
|
|
486
623
|
if (folderPath !== '') {
|
|
487
|
-
globSync(folderPath)
|
|
488
|
-
|
|
489
|
-
|
|
624
|
+
const files = globSync(folderPath)
|
|
625
|
+
for (const file of files) {
|
|
626
|
+
// Set context for step definition file location tracking
|
|
627
|
+
bddModule.setCurrentStepFile(file)
|
|
628
|
+
await loadSupportObject(file, `Step Definition from ${file}`)
|
|
629
|
+
bddModule.clearCurrentStepFile()
|
|
630
|
+
}
|
|
490
631
|
}
|
|
491
632
|
}
|
|
492
633
|
|
|
@@ -495,47 +636,97 @@ function loadGherkinSteps(paths) {
|
|
|
495
636
|
delete global.Fail
|
|
496
637
|
}
|
|
497
638
|
|
|
498
|
-
function
|
|
639
|
+
function loadGherkinSteps(paths) {
|
|
640
|
+
global.Before = fn => event.dispatcher.on(event.test.started, fn)
|
|
641
|
+
global.After = fn => event.dispatcher.on(event.test.finished, fn)
|
|
642
|
+
global.Fail = fn => event.dispatcher.on(event.test.failed, fn)
|
|
643
|
+
|
|
644
|
+
// Gherkin step loading must be handled asynchronously
|
|
645
|
+
throw new Error('Gherkin step loading must be converted to async. Use loadGherkinStepsAsync() instead.')
|
|
646
|
+
|
|
647
|
+
delete global.Before
|
|
648
|
+
delete global.After
|
|
649
|
+
delete global.Fail
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
async function loadSupportObject(modulePath, supportObjectName) {
|
|
499
653
|
if (!modulePath) {
|
|
500
654
|
throw new Error(`Support object "${supportObjectName}" is not defined`)
|
|
501
655
|
}
|
|
502
|
-
|
|
656
|
+
// If function/class provided directly
|
|
657
|
+
if (typeof modulePath === 'function') {
|
|
658
|
+
try {
|
|
659
|
+
// class constructor
|
|
660
|
+
if (modulePath.prototype && modulePath.prototype.constructor === modulePath) {
|
|
661
|
+
return new modulePath()
|
|
662
|
+
}
|
|
663
|
+
// plain function factory
|
|
664
|
+
return modulePath()
|
|
665
|
+
} catch (err) {
|
|
666
|
+
throw new Error(`Could not include object ${supportObjectName} from function: ${err.message}`)
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
if (typeof modulePath === 'string' && modulePath.charAt(0) === '.') {
|
|
503
670
|
modulePath = path.join(global.codecept_dir, modulePath)
|
|
504
671
|
}
|
|
505
672
|
try {
|
|
506
|
-
|
|
673
|
+
// Use dynamic import for both ESM and CJS modules
|
|
674
|
+
let importPath = modulePath
|
|
675
|
+
// Append .js if no extension provided (ESM resolution requires it)
|
|
676
|
+
if (typeof importPath === 'string') {
|
|
677
|
+
const ext = path.extname(importPath)
|
|
678
|
+
if (!ext) importPath = `${importPath}.js`
|
|
679
|
+
}
|
|
680
|
+
const obj = await import(importPath)
|
|
681
|
+
|
|
682
|
+
// Handle ESM module wrapper
|
|
683
|
+
let actualObj = obj
|
|
684
|
+
if (obj && obj.__esModule && obj.default) {
|
|
685
|
+
actualObj = obj.default
|
|
686
|
+
} else if (obj.default) {
|
|
687
|
+
actualObj = obj.default
|
|
688
|
+
}
|
|
507
689
|
|
|
508
690
|
// Handle different types of imports
|
|
509
|
-
if (typeof
|
|
691
|
+
if (typeof actualObj === 'function') {
|
|
510
692
|
// If it's a class (constructor function)
|
|
511
|
-
if (
|
|
512
|
-
const ClassName =
|
|
693
|
+
if (actualObj.prototype && actualObj.prototype.constructor === actualObj) {
|
|
694
|
+
const ClassName = actualObj
|
|
513
695
|
return new ClassName()
|
|
514
696
|
}
|
|
515
697
|
// If it's a regular function
|
|
516
|
-
return
|
|
698
|
+
return actualObj()
|
|
517
699
|
}
|
|
518
700
|
|
|
519
|
-
if (
|
|
520
|
-
return
|
|
701
|
+
if (actualObj && Array.isArray(actualObj)) {
|
|
702
|
+
return actualObj
|
|
521
703
|
}
|
|
522
704
|
|
|
523
705
|
// If it's a plain object
|
|
524
|
-
if (
|
|
525
|
-
|
|
706
|
+
if (actualObj && typeof actualObj === 'object') {
|
|
707
|
+
// Call _init if it exists (for page objects)
|
|
708
|
+
if (actualObj._init && typeof actualObj._init === 'function') {
|
|
709
|
+
actualObj._init()
|
|
710
|
+
}
|
|
711
|
+
return actualObj
|
|
526
712
|
}
|
|
527
713
|
|
|
528
|
-
throw new Error(`Support object "${supportObjectName}" should be an object, class, or function, but got ${typeof
|
|
714
|
+
throw new Error(`Support object "${supportObjectName}" should be an object, class, or function, but got ${typeof actualObj}`)
|
|
529
715
|
} catch (err) {
|
|
530
716
|
throw new Error(`Could not include object ${supportObjectName} from module '${modulePath}'\n${err.message}\n${err.stack}`)
|
|
531
717
|
}
|
|
532
718
|
}
|
|
533
719
|
|
|
720
|
+
// Backwards compatibility function that throws an error for sync usage
|
|
721
|
+
function loadSupportObjectSync(modulePath, supportObjectName) {
|
|
722
|
+
throw new Error(`loadSupportObjectSync is deprecated. Support object "${supportObjectName || 'undefined'}" from '${modulePath}' must be loaded asynchronously. Use loadSupportObject() instead.`)
|
|
723
|
+
}
|
|
724
|
+
|
|
534
725
|
/**
|
|
535
726
|
* Method collect own property and prototype
|
|
536
727
|
*/
|
|
537
728
|
|
|
538
|
-
function loadTranslation(locale, vocabularies) {
|
|
729
|
+
async function loadTranslation(locale, vocabularies) {
|
|
539
730
|
if (!locale) {
|
|
540
731
|
return Translation.createEmpty()
|
|
541
732
|
}
|
|
@@ -543,8 +734,9 @@ function loadTranslation(locale, vocabularies) {
|
|
|
543
734
|
let translation
|
|
544
735
|
|
|
545
736
|
// check if it is a known translation
|
|
546
|
-
|
|
547
|
-
|
|
737
|
+
const langs = await Translation.getLangs()
|
|
738
|
+
if (langs[locale]) {
|
|
739
|
+
translation = new Translation(langs[locale])
|
|
548
740
|
} else if (fileExists(path.join(global.codecept_dir, locale))) {
|
|
549
741
|
// get from a provided file instead
|
|
550
742
|
translation = Translation.createDefault()
|
|
@@ -562,7 +754,12 @@ function getHelperModuleName(helperName, config) {
|
|
|
562
754
|
// classical require
|
|
563
755
|
if (config[helperName].require) {
|
|
564
756
|
if (config[helperName].require.startsWith('.')) {
|
|
565
|
-
|
|
757
|
+
let helperPath = path.resolve(global.codecept_dir, config[helperName].require)
|
|
758
|
+
// Add .js extension if not present for ESM compatibility
|
|
759
|
+
if (!path.extname(helperPath)) {
|
|
760
|
+
helperPath += '.js'
|
|
761
|
+
}
|
|
762
|
+
return helperPath // custom helper
|
|
566
763
|
}
|
|
567
764
|
return config[helperName].require // plugin helper
|
|
568
765
|
}
|
package/lib/data/context.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { isGenerator } from '../utils.js'
|
|
2
|
+
import DataTable from './table.js'
|
|
3
|
+
import DataScenarioConfig from './dataScenarioConfig.js'
|
|
4
|
+
import secretModule from '../secret.js'
|
|
5
|
+
const Secret = secretModule.default || secretModule
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
export default function (context) {
|
|
7
8
|
context.Data = function (dataTable) {
|
|
8
9
|
const data = detectDataType(dataTable)
|
|
9
10
|
return {
|