codeceptjs 3.6.10-beta.1 → 3.7.0-beta.1
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 +81 -110
- package/bin/codecept.js +2 -2
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +46 -36
- package/lib/assert/empty.js +3 -5
- package/lib/assert/equal.js +4 -7
- package/lib/assert/include.js +4 -6
- package/lib/assert/throws.js +2 -4
- package/lib/assert/truth.js +2 -2
- package/lib/codecept.js +87 -83
- package/lib/command/configMigrate.js +2 -4
- package/lib/command/definitions.js +5 -25
- package/lib/command/generate.js +10 -14
- package/lib/command/gherkin/snippets.js +10 -8
- package/lib/command/gherkin/steps.js +1 -1
- package/lib/command/info.js +1 -3
- package/lib/command/init.js +8 -12
- package/lib/command/interactive.js +1 -1
- package/lib/command/list.js +1 -1
- package/lib/command/run-multiple.js +12 -35
- package/lib/command/run-workers.js +10 -10
- package/lib/command/utils.js +5 -6
- package/lib/command/workers/runTests.js +14 -17
- package/lib/container.js +327 -237
- package/lib/data/context.js +10 -13
- package/lib/data/dataScenarioConfig.js +8 -8
- package/lib/data/dataTableArgument.js +6 -6
- package/lib/data/table.js +5 -11
- package/lib/els.js +177 -0
- package/lib/event.js +1 -0
- package/lib/heal.js +78 -80
- package/lib/helper/ApiDataFactory.js +3 -6
- package/lib/helper/Appium.js +15 -30
- package/lib/helper/FileSystem.js +3 -3
- package/lib/helper/GraphQLDataFactory.js +3 -3
- package/lib/helper/JSONResponse.js +57 -37
- package/lib/helper/Nightmare.js +35 -53
- package/lib/helper/Playwright.js +189 -251
- package/lib/helper/Protractor.js +54 -77
- package/lib/helper/Puppeteer.js +134 -232
- package/lib/helper/REST.js +5 -17
- package/lib/helper/TestCafe.js +21 -44
- package/lib/helper/WebDriver.js +103 -162
- package/lib/helper/testcafe/testcafe-utils.js +26 -27
- package/lib/listener/artifacts.js +2 -2
- package/lib/listener/emptyRun.js +58 -0
- package/lib/listener/exit.js +4 -4
- package/lib/listener/{retry.js → globalRetry.js} +5 -5
- package/lib/listener/{timeout.js → globalTimeout.js} +8 -8
- package/lib/listener/helpers.js +15 -15
- package/lib/listener/mocha.js +1 -1
- package/lib/listener/steps.js +17 -12
- package/lib/listener/store.js +12 -0
- package/lib/mocha/asyncWrapper.js +204 -0
- package/lib/{interfaces → mocha}/bdd.js +3 -3
- package/lib/mocha/cli.js +257 -0
- package/lib/mocha/factory.js +104 -0
- package/lib/{interfaces → mocha}/featureConfig.js +11 -12
- package/lib/{interfaces → mocha}/gherkin.js +26 -28
- package/lib/mocha/hooks.js +83 -0
- package/lib/mocha/index.js +12 -0
- package/lib/mocha/inject.js +24 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +10 -6
- package/lib/mocha/suite.js +55 -0
- package/lib/mocha/test.js +60 -0
- package/lib/mocha/types.d.ts +31 -0
- package/lib/mocha/ui.js +219 -0
- package/lib/output.js +28 -10
- package/lib/pause.js +159 -135
- package/lib/plugin/autoDelay.js +4 -4
- package/lib/plugin/autoLogin.js +6 -7
- package/lib/plugin/commentStep.js +1 -1
- package/lib/plugin/coverage.js +10 -19
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/debugErrors.js +2 -2
- package/lib/plugin/eachElement.js +1 -1
- package/lib/plugin/fakerTransform.js +1 -1
- package/lib/plugin/heal.js +6 -9
- package/lib/plugin/retryFailedStep.js +4 -4
- package/lib/plugin/retryTo.js +2 -2
- package/lib/plugin/screenshotOnFail.js +9 -36
- package/lib/plugin/selenoid.js +15 -35
- package/lib/plugin/stepByStepReport.js +51 -13
- package/lib/plugin/stepTimeout.js +4 -11
- package/lib/plugin/subtitles.js +4 -4
- package/lib/plugin/tryTo.js +1 -1
- package/lib/plugin/wdio.js +8 -10
- package/lib/recorder.js +142 -121
- package/lib/secret.js +1 -1
- package/lib/step.js +160 -144
- package/lib/store.js +6 -2
- package/lib/template/heal.js +2 -11
- package/lib/utils.js +224 -216
- package/lib/within.js +73 -55
- package/lib/workers.js +265 -261
- package/package.json +45 -46
- package/typings/index.d.ts +172 -184
- package/typings/promiseBasedTypes.d.ts +53 -516
- package/typings/types.d.ts +127 -587
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/mochaFactory.js +0 -113
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
package/lib/container.js
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
const glob = require('glob')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
const { MetaStep } = require('./step')
|
|
4
|
-
const { fileExists, isFunction, isAsyncFunction } = require('./utils')
|
|
5
|
-
const Translation = require('./translation')
|
|
6
|
-
const MochaFactory = require('./
|
|
7
|
-
const recorder = require('./recorder')
|
|
8
|
-
const event = require('./event')
|
|
9
|
-
const WorkerStorage = require('./workerStorage')
|
|
10
|
-
const store = require('./store')
|
|
11
|
-
const ai = require('./ai')
|
|
1
|
+
const glob = require('glob')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { MetaStep } = require('./step')
|
|
4
|
+
const { methodsOfObject, fileExists, isFunction, isAsyncFunction, installedLocally } = require('./utils')
|
|
5
|
+
const Translation = require('./translation')
|
|
6
|
+
const MochaFactory = require('./mocha/factory')
|
|
7
|
+
const recorder = require('./recorder')
|
|
8
|
+
const event = require('./event')
|
|
9
|
+
const WorkerStorage = require('./workerStorage')
|
|
10
|
+
const store = require('./store')
|
|
11
|
+
const ai = require('./ai')
|
|
12
|
+
|
|
13
|
+
let asyncHelperPromise
|
|
12
14
|
|
|
13
15
|
let container = {
|
|
14
16
|
helpers: {},
|
|
15
17
|
support: {},
|
|
18
|
+
proxySupport: {},
|
|
16
19
|
plugins: {},
|
|
20
|
+
actor: null,
|
|
17
21
|
/**
|
|
18
22
|
* @type {Mocha | {}}
|
|
19
23
|
* @ignore
|
|
20
24
|
*/
|
|
21
25
|
mocha: {},
|
|
22
26
|
translation: {},
|
|
23
|
-
}
|
|
27
|
+
}
|
|
24
28
|
|
|
25
29
|
/**
|
|
26
30
|
* Dependency Injection Container
|
|
@@ -34,21 +38,30 @@ class Container {
|
|
|
34
38
|
* @param {*} opts
|
|
35
39
|
*/
|
|
36
40
|
static create(config, opts) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
container.
|
|
47
|
-
container.
|
|
48
|
-
container.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
asyncHelperPromise = Promise.resolve()
|
|
42
|
+
|
|
43
|
+
// dynamically create mocha instance
|
|
44
|
+
const mochaConfig = config.mocha || {}
|
|
45
|
+
if (config.grep && !opts.grep) mochaConfig.grep = config.grep
|
|
46
|
+
this.createMocha = () => (container.mocha = MochaFactory.create(mochaConfig, opts || {}))
|
|
47
|
+
this.createMocha()
|
|
48
|
+
|
|
49
|
+
// create support objects
|
|
50
|
+
container.support = {}
|
|
51
|
+
container.helpers = createHelpers(config.helpers || {})
|
|
52
|
+
container.translation = loadTranslation(config.translation || null, config.vocabularies || [])
|
|
53
|
+
container.proxySupport = createSupportObjects(config.include || {})
|
|
54
|
+
container.plugins = createPlugins(config.plugins || {}, opts)
|
|
55
|
+
|
|
56
|
+
createActor(config.include?.I)
|
|
57
|
+
|
|
58
|
+
if (opts && opts.ai) ai.enable(config.ai) // enable AI Assistant
|
|
59
|
+
if (config.gherkin) loadGherkinSteps(config.gherkin.steps || [])
|
|
60
|
+
if (opts && typeof opts.timeouts === 'boolean') store.timeouts = opts.timeouts
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static actor() {
|
|
64
|
+
return container.support.I
|
|
52
65
|
}
|
|
53
66
|
|
|
54
67
|
/**
|
|
@@ -60,9 +73,9 @@ class Container {
|
|
|
60
73
|
*/
|
|
61
74
|
static plugins(name) {
|
|
62
75
|
if (!name) {
|
|
63
|
-
return container.plugins
|
|
76
|
+
return container.plugins
|
|
64
77
|
}
|
|
65
|
-
return container.plugins[name]
|
|
78
|
+
return container.plugins[name]
|
|
66
79
|
}
|
|
67
80
|
|
|
68
81
|
/**
|
|
@@ -74,9 +87,9 @@ class Container {
|
|
|
74
87
|
*/
|
|
75
88
|
static support(name) {
|
|
76
89
|
if (!name) {
|
|
77
|
-
return container.
|
|
90
|
+
return container.proxySupport
|
|
78
91
|
}
|
|
79
|
-
return container.support[name]
|
|
92
|
+
return container.support[name] || container.proxySupport[name]
|
|
80
93
|
}
|
|
81
94
|
|
|
82
95
|
/**
|
|
@@ -88,9 +101,9 @@ class Container {
|
|
|
88
101
|
*/
|
|
89
102
|
static helpers(name) {
|
|
90
103
|
if (!name) {
|
|
91
|
-
return container.helpers
|
|
104
|
+
return container.helpers
|
|
92
105
|
}
|
|
93
|
-
return container.helpers[name]
|
|
106
|
+
return container.helpers[name]
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
/**
|
|
@@ -99,7 +112,7 @@ class Container {
|
|
|
99
112
|
* @api
|
|
100
113
|
*/
|
|
101
114
|
static translation() {
|
|
102
|
-
return container.translation
|
|
115
|
+
return container.translation
|
|
103
116
|
}
|
|
104
117
|
|
|
105
118
|
/**
|
|
@@ -109,7 +122,7 @@ class Container {
|
|
|
109
122
|
* @returns { * }
|
|
110
123
|
*/
|
|
111
124
|
static mocha() {
|
|
112
|
-
return container.mocha
|
|
125
|
+
return container.mocha
|
|
113
126
|
}
|
|
114
127
|
|
|
115
128
|
/**
|
|
@@ -119,8 +132,8 @@ class Container {
|
|
|
119
132
|
* @param {Object<string, *>} newContainer
|
|
120
133
|
*/
|
|
121
134
|
static append(newContainer) {
|
|
122
|
-
const deepMerge = require('./utils').deepMerge
|
|
123
|
-
container = deepMerge(container, newContainer)
|
|
135
|
+
const deepMerge = require('./utils').deepMerge
|
|
136
|
+
container = deepMerge(container, newContainer)
|
|
124
137
|
}
|
|
125
138
|
|
|
126
139
|
/**
|
|
@@ -131,10 +144,23 @@ class Container {
|
|
|
131
144
|
* @param {Object<string, *>} newPlugins
|
|
132
145
|
*/
|
|
133
146
|
static clear(newHelpers, newSupport, newPlugins) {
|
|
134
|
-
container.helpers = newHelpers || {}
|
|
135
|
-
container.
|
|
136
|
-
container.
|
|
137
|
-
container.
|
|
147
|
+
container.helpers = newHelpers || {}
|
|
148
|
+
container.translation = loadTranslation()
|
|
149
|
+
container.proxySupport = createSupportObjects(newSupport || {})
|
|
150
|
+
container.plugins = newPlugins || {}
|
|
151
|
+
asyncHelperPromise = Promise.resolve()
|
|
152
|
+
store.actor = null
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @param {Function} fn
|
|
157
|
+
* @returns {Promise<void>}
|
|
158
|
+
*/
|
|
159
|
+
static async started(fn = null) {
|
|
160
|
+
if (fn) {
|
|
161
|
+
asyncHelperPromise = asyncHelperPromise.then(fn)
|
|
162
|
+
}
|
|
163
|
+
return asyncHelperPromise
|
|
138
164
|
}
|
|
139
165
|
|
|
140
166
|
/**
|
|
@@ -144,299 +170,363 @@ class Container {
|
|
|
144
170
|
* @param {Object} options - set {local: true} to not share among workers
|
|
145
171
|
*/
|
|
146
172
|
static share(data, options = {}) {
|
|
147
|
-
Container.append({ support: data })
|
|
173
|
+
Container.append({ support: data })
|
|
148
174
|
if (!options.local) {
|
|
149
|
-
WorkerStorage.share(data)
|
|
175
|
+
WorkerStorage.share(data)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static createMocha(config = {}, opts = {}) {
|
|
180
|
+
const mochaConfig = config?.mocha || {}
|
|
181
|
+
if (config?.grep && !opts?.grep) {
|
|
182
|
+
mochaConfig.grep = config.grep
|
|
150
183
|
}
|
|
184
|
+
container.mocha = MochaFactory.create(mochaConfig, opts || {})
|
|
151
185
|
}
|
|
152
186
|
}
|
|
153
187
|
|
|
154
|
-
module.exports = Container
|
|
188
|
+
module.exports = Container
|
|
155
189
|
|
|
156
190
|
function createHelpers(config) {
|
|
157
|
-
const helpers = {}
|
|
158
|
-
let
|
|
159
|
-
for (const helperName in config) {
|
|
191
|
+
const helpers = {}
|
|
192
|
+
for (let helperName in config) {
|
|
160
193
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
} else {
|
|
168
|
-
moduleName = `./helper/${helperName}`; // built-in helper
|
|
194
|
+
let HelperClass
|
|
195
|
+
|
|
196
|
+
// ESM import
|
|
197
|
+
if (helperName?.constructor === Function && helperName.prototype) {
|
|
198
|
+
HelperClass = helperName
|
|
199
|
+
helperName = HelperClass.constructor.name
|
|
169
200
|
}
|
|
170
201
|
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (moduleName.startsWith('./helper/')) {
|
|
175
|
-
HelperClass = require(moduleName);
|
|
176
|
-
} else {
|
|
177
|
-
// check if the new syntax export default HelperName is used and loads the Helper, otherwise loads the module that used old syntax export = HelperName.
|
|
178
|
-
HelperClass = require(moduleName).default || require(moduleName);
|
|
202
|
+
// classical require
|
|
203
|
+
if (!HelperClass) {
|
|
204
|
+
HelperClass = requireHelperFromModule(helperName, config)
|
|
179
205
|
}
|
|
180
206
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
207
|
+
// handle async CJS modules that use dynamic import
|
|
208
|
+
if (isAsyncFunction(HelperClass)) {
|
|
209
|
+
helpers[helperName] = {}
|
|
210
|
+
|
|
211
|
+
asyncHelperPromise = asyncHelperPromise
|
|
212
|
+
.then(() => HelperClass())
|
|
213
|
+
.then(ResolvedHelperClass => {
|
|
214
|
+
// Check if ResolvedHelperClass is a constructor function
|
|
215
|
+
if (typeof ResolvedHelperClass?.constructor !== 'function') {
|
|
216
|
+
throw new Error(`Helper class from module '${helperName}' is not a class. Use CJS async module syntax.`)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
helpers[helperName] = new ResolvedHelperClass(config[helperName])
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
continue
|
|
193
223
|
}
|
|
194
|
-
|
|
224
|
+
|
|
225
|
+
checkHelperRequirements(HelperClass)
|
|
226
|
+
|
|
227
|
+
helpers[helperName] = new HelperClass(config[helperName])
|
|
195
228
|
} catch (err) {
|
|
196
|
-
throw new Error(`Could not load helper ${helperName}
|
|
229
|
+
throw new Error(`Could not load helper ${helperName} (${err.message})`)
|
|
197
230
|
}
|
|
198
231
|
}
|
|
199
232
|
|
|
200
233
|
for (const name in helpers) {
|
|
201
|
-
if (helpers[name]._init) helpers[name]._init()
|
|
234
|
+
if (helpers[name]._init) helpers[name]._init()
|
|
202
235
|
}
|
|
203
|
-
return helpers
|
|
236
|
+
return helpers
|
|
204
237
|
}
|
|
205
238
|
|
|
206
|
-
function
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
239
|
+
function checkHelperRequirements(HelperClass) {
|
|
240
|
+
if (HelperClass._checkRequirements) {
|
|
241
|
+
const requirements = HelperClass._checkRequirements()
|
|
242
|
+
if (requirements) {
|
|
243
|
+
let install
|
|
244
|
+
if (installedLocally()) {
|
|
245
|
+
install = `npm install --save-dev ${requirements.join(' ')}`
|
|
246
|
+
} else {
|
|
247
|
+
console.log('WARNING: CodeceptJS is not installed locally. It is recommended to switch to local installation')
|
|
248
|
+
install = `[sudo] npm install -g ${requirements.join(' ')}`
|
|
249
|
+
}
|
|
250
|
+
throw new Error(`Required modules are not installed.\n\nRUN: ${install}`)
|
|
218
251
|
}
|
|
219
252
|
}
|
|
253
|
+
}
|
|
220
254
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
255
|
+
function requireHelperFromModule(helperName, config, HelperClass) {
|
|
256
|
+
const moduleName = getHelperModuleName(helperName, config)
|
|
257
|
+
if (moduleName.startsWith('./helper/')) {
|
|
258
|
+
HelperClass = require(moduleName)
|
|
259
|
+
} else {
|
|
260
|
+
// check if the new syntax export default HelperName is used and loads the Helper, otherwise loads the module that used old syntax export = HelperName.
|
|
225
261
|
try {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
newObj._init();
|
|
262
|
+
const mod = require(moduleName)
|
|
263
|
+
if (!mod && !mod.default) {
|
|
264
|
+
throw new Error(`Helper module '${moduleName}' was not found. Make sure you have installed the package correctly.`)
|
|
230
265
|
}
|
|
266
|
+
HelperClass = mod.default || mod
|
|
231
267
|
} catch (err) {
|
|
232
|
-
|
|
268
|
+
if (err.code === 'MODULE_NOT_FOUND') {
|
|
269
|
+
throw new Error(`Helper module '${moduleName}' was not found. Make sure you have installed the package correctly.`)
|
|
270
|
+
}
|
|
271
|
+
throw err
|
|
233
272
|
}
|
|
234
|
-
return newObj;
|
|
235
273
|
}
|
|
274
|
+
return HelperClass
|
|
275
|
+
}
|
|
236
276
|
|
|
277
|
+
function createSupportObjects(config) {
|
|
237
278
|
const asyncWrapper = function (f) {
|
|
238
279
|
return function () {
|
|
239
|
-
return f.apply(this, arguments).catch(
|
|
240
|
-
recorder.saveFirstAsyncError(e)
|
|
241
|
-
throw e
|
|
242
|
-
})
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
Object.keys(objects).forEach((object) => {
|
|
247
|
-
const currentObject = objects[object];
|
|
248
|
-
Object.keys(currentObject).forEach((method) => {
|
|
249
|
-
const currentMethod = currentObject[method];
|
|
250
|
-
if (currentMethod && currentMethod[Symbol.toStringTag] === 'AsyncFunction') {
|
|
251
|
-
objects[object][method] = asyncWrapper(currentMethod);
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
});
|
|
280
|
+
return f.apply(this, arguments).catch(e => {
|
|
281
|
+
recorder.saveFirstAsyncError(e)
|
|
282
|
+
throw e
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
}
|
|
255
286
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
287
|
+
function lazyLoad(name) {
|
|
288
|
+
return new Proxy(
|
|
289
|
+
{},
|
|
290
|
+
{
|
|
291
|
+
get(target, prop) {
|
|
292
|
+
// behavr like array or
|
|
293
|
+
if (prop === 'length') return Object.keys(config).length
|
|
294
|
+
if (prop === Symbol.iterator) {
|
|
295
|
+
return function* () {
|
|
296
|
+
for (let i = 0; i < Object.keys(config).length; i++) {
|
|
297
|
+
yield target[i]
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// load actual name from vocabulary
|
|
303
|
+
if (container.translation.name) {
|
|
304
|
+
name = container.translation.name
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (name === 'I') {
|
|
308
|
+
const actor = createActor(config.I)
|
|
309
|
+
methodsOfObject(actor)
|
|
310
|
+
return actor[prop]
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (!container.support[name] && typeof config[name] === 'object') {
|
|
314
|
+
container.support[name] = config[name]
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!container.support[name]) {
|
|
318
|
+
// Load object on first access
|
|
319
|
+
const supportObject = loadSupportObject(config[name])
|
|
320
|
+
container.support[name] = supportObject
|
|
321
|
+
try {
|
|
322
|
+
if (container.support[name]._init) {
|
|
323
|
+
container.support[name]._init()
|
|
324
|
+
}
|
|
325
|
+
} catch (err) {
|
|
326
|
+
throw new Error(`Initialization failed for ${name}: ${container.support[name]}\n${err.message}\n${err.stack}`)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const currentObject = container.support[name]
|
|
331
|
+
let currentValue = currentObject[prop]
|
|
270
332
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
333
|
+
if (isFunction(currentValue) || isAsyncFunction(currentValue)) {
|
|
334
|
+
const ms = new MetaStep(name, prop)
|
|
335
|
+
ms.setContext(currentObject)
|
|
336
|
+
if (isAsyncFunction(currentValue)) currentValue = asyncWrapper(currentValue)
|
|
337
|
+
return ms.run.bind(ms, currentValue)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return currentValue
|
|
341
|
+
},
|
|
342
|
+
has(target, prop) {
|
|
343
|
+
container.support[name] = container.support[name] || loadSupportObject(config[name])
|
|
344
|
+
return prop in container.support[name]
|
|
345
|
+
},
|
|
346
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
347
|
+
container.support[name] = container.support[name] || loadSupportObject(config[name])
|
|
348
|
+
return {
|
|
349
|
+
enumerable: true,
|
|
350
|
+
configurable: true,
|
|
351
|
+
value: this.get(target, prop),
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
ownKeys() {
|
|
355
|
+
container.support[name] = container.support[name] || loadSupportObject(config[name])
|
|
356
|
+
return Reflect.ownKeys(container.support[name])
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const keys = Reflect.ownKeys(config)
|
|
363
|
+
return new Proxy(
|
|
364
|
+
{},
|
|
365
|
+
{
|
|
366
|
+
has(target, key) {
|
|
367
|
+
return keys.includes(key)
|
|
368
|
+
},
|
|
369
|
+
ownKeys() {
|
|
370
|
+
return keys
|
|
371
|
+
},
|
|
372
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
373
|
+
return {
|
|
374
|
+
enumerable: true,
|
|
375
|
+
configurable: true,
|
|
376
|
+
value: this.get(target, prop),
|
|
276
377
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
378
|
+
},
|
|
379
|
+
get(target, key) {
|
|
380
|
+
return lazyLoad(key)
|
|
381
|
+
},
|
|
280
382
|
},
|
|
281
|
-
|
|
383
|
+
)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function createActor(actorPath) {
|
|
387
|
+
if (container.support.I) return container.support.I
|
|
388
|
+
|
|
389
|
+
if (actorPath) {
|
|
390
|
+
container.support.I = loadSupportObject(actorPath)
|
|
391
|
+
} else {
|
|
392
|
+
const actor = require('./actor')
|
|
393
|
+
container.support.I = actor()
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return container.support.I
|
|
282
397
|
}
|
|
283
398
|
|
|
284
399
|
function createPlugins(config, options = {}) {
|
|
285
|
-
const plugins = {}
|
|
400
|
+
const plugins = {}
|
|
286
401
|
|
|
287
|
-
const enabledPluginsByOptions = (options.plugins || '').split(',')
|
|
402
|
+
const enabledPluginsByOptions = (options.plugins || '').split(',')
|
|
288
403
|
for (const pluginName in config) {
|
|
289
|
-
if (!config[pluginName]) config[pluginName] = {}
|
|
290
|
-
if (!config[pluginName].enabled &&
|
|
291
|
-
continue
|
|
404
|
+
if (!config[pluginName]) config[pluginName] = {}
|
|
405
|
+
if (!config[pluginName].enabled && enabledPluginsByOptions.indexOf(pluginName) < 0) {
|
|
406
|
+
continue // plugin is disabled
|
|
292
407
|
}
|
|
293
|
-
let module
|
|
408
|
+
let module
|
|
294
409
|
try {
|
|
295
410
|
if (config[pluginName].require) {
|
|
296
|
-
module = config[pluginName].require
|
|
297
|
-
if (module.startsWith('.')) {
|
|
298
|
-
|
|
411
|
+
module = config[pluginName].require
|
|
412
|
+
if (module.startsWith('.')) {
|
|
413
|
+
// local
|
|
414
|
+
module = path.resolve(global.codecept_dir, module) // custom plugin
|
|
299
415
|
}
|
|
300
416
|
} else {
|
|
301
|
-
module = `./plugin/${pluginName}
|
|
417
|
+
module = `./plugin/${pluginName}`
|
|
302
418
|
}
|
|
303
|
-
plugins[pluginName] = require(module)(config[pluginName])
|
|
419
|
+
plugins[pluginName] = require(module)(config[pluginName])
|
|
304
420
|
} catch (err) {
|
|
305
|
-
throw new Error(`Could not load plugin ${pluginName} from module '${module}':\n${err.message}\n${err.stack}`)
|
|
421
|
+
throw new Error(`Could not load plugin ${pluginName} from module '${module}':\n${err.message}\n${err.stack}`)
|
|
306
422
|
}
|
|
307
423
|
}
|
|
308
|
-
return plugins
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function getSupportObject(config, name) {
|
|
312
|
-
const module = config[name];
|
|
313
|
-
if (typeof module === 'string') {
|
|
314
|
-
return loadSupportObject(module, name);
|
|
315
|
-
}
|
|
316
|
-
return module;
|
|
424
|
+
return plugins
|
|
317
425
|
}
|
|
318
426
|
|
|
319
427
|
function loadGherkinSteps(paths) {
|
|
320
|
-
global.Before = fn => event.dispatcher.on(event.test.started, fn)
|
|
321
|
-
global.After = fn => event.dispatcher.on(event.test.finished, fn)
|
|
322
|
-
global.Fail = fn => event.dispatcher.on(event.test.failed, fn)
|
|
428
|
+
global.Before = fn => event.dispatcher.on(event.test.started, fn)
|
|
429
|
+
global.After = fn => event.dispatcher.on(event.test.finished, fn)
|
|
430
|
+
global.Fail = fn => event.dispatcher.on(event.test.failed, fn)
|
|
323
431
|
|
|
324
432
|
// If gherkin.steps is string, then this will iterate through that folder and send all step def js files to loadSupportObject
|
|
325
433
|
// If gherkin.steps is Array, it will go the old way
|
|
326
434
|
// This is done so that we need not enter all Step Definition files under config.gherkin.steps
|
|
327
435
|
if (Array.isArray(paths)) {
|
|
328
436
|
for (const path of paths) {
|
|
329
|
-
loadSupportObject(path, `Step Definition from ${path}`)
|
|
437
|
+
loadSupportObject(path, `Step Definition from ${path}`)
|
|
330
438
|
}
|
|
331
439
|
} else {
|
|
332
|
-
const folderPath = paths.startsWith('.') ? path.join(global.codecept_dir, paths) : ''
|
|
440
|
+
const folderPath = paths.startsWith('.') ? path.join(global.codecept_dir, paths) : ''
|
|
333
441
|
if (folderPath !== '') {
|
|
334
|
-
glob.sync(folderPath).forEach(
|
|
335
|
-
loadSupportObject(file, `Step Definition from ${file}`)
|
|
336
|
-
})
|
|
442
|
+
glob.sync(folderPath).forEach(file => {
|
|
443
|
+
loadSupportObject(file, `Step Definition from ${file}`)
|
|
444
|
+
})
|
|
337
445
|
}
|
|
338
446
|
}
|
|
339
447
|
|
|
340
|
-
delete global.Before
|
|
341
|
-
delete global.After
|
|
342
|
-
delete global.Fail
|
|
448
|
+
delete global.Before
|
|
449
|
+
delete global.After
|
|
450
|
+
delete global.Fail
|
|
343
451
|
}
|
|
344
452
|
|
|
345
453
|
function loadSupportObject(modulePath, supportObjectName) {
|
|
454
|
+
if (!modulePath) {
|
|
455
|
+
throw new Error(`Support object "${supportObjectName}" is not defined`)
|
|
456
|
+
}
|
|
346
457
|
if (modulePath.charAt(0) === '.') {
|
|
347
|
-
modulePath = path.join(global.codecept_dir, modulePath)
|
|
458
|
+
modulePath = path.join(global.codecept_dir, modulePath)
|
|
348
459
|
}
|
|
349
460
|
try {
|
|
350
|
-
const obj = require(modulePath)
|
|
461
|
+
const obj = require(modulePath)
|
|
351
462
|
|
|
463
|
+
// Handle different types of imports
|
|
352
464
|
if (typeof obj === 'function') {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
Object.keys(methods)
|
|
358
|
-
.forEach(key => {
|
|
359
|
-
fobj[key] = methods[key];
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
return methods;
|
|
465
|
+
// If it's a class (constructor function)
|
|
466
|
+
if (obj.prototype && obj.prototype.constructor === obj) {
|
|
467
|
+
const ClassName = obj
|
|
468
|
+
return new ClassName()
|
|
363
469
|
}
|
|
470
|
+
// If it's a regular function
|
|
471
|
+
return obj()
|
|
364
472
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
) {
|
|
369
|
-
const methods = getObjectMethods(obj);
|
|
370
|
-
Object.keys(methods)
|
|
371
|
-
.filter(key => !key.startsWith('_'))
|
|
372
|
-
.forEach(key => {
|
|
373
|
-
const currentMethod = methods[key];
|
|
374
|
-
if (isFunction(currentMethod) || isAsyncFunction(currentMethod)) {
|
|
375
|
-
const ms = new MetaStep(supportObjectName, key);
|
|
376
|
-
ms.setContext(methods);
|
|
377
|
-
methods[key] = ms.run.bind(ms, currentMethod);
|
|
378
|
-
}
|
|
379
|
-
});
|
|
380
|
-
return methods;
|
|
473
|
+
|
|
474
|
+
if (obj && Array.isArray(obj)) {
|
|
475
|
+
return obj
|
|
381
476
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const currentMethod = obj[key];
|
|
387
|
-
if (isFunction(currentMethod) || isAsyncFunction(currentMethod)) {
|
|
388
|
-
const ms = new MetaStep(supportObjectName, key);
|
|
389
|
-
ms.setContext(obj);
|
|
390
|
-
obj[key] = ms.run.bind(ms, currentMethod);
|
|
391
|
-
}
|
|
392
|
-
});
|
|
477
|
+
|
|
478
|
+
// If it's a plain object
|
|
479
|
+
if (obj && typeof obj === 'object') {
|
|
480
|
+
return obj
|
|
393
481
|
}
|
|
394
482
|
|
|
395
|
-
|
|
483
|
+
throw new Error(`Support object "${supportObjectName}" should be an object, class, or function, but got ${typeof obj}`)
|
|
396
484
|
} catch (err) {
|
|
397
|
-
throw new Error(`Could not include object ${supportObjectName} from module '${modulePath}'\n${err.message}\n${err.stack}`)
|
|
485
|
+
throw new Error(`Could not include object ${supportObjectName} from module '${modulePath}'\n${err.message}\n${err.stack}`)
|
|
398
486
|
}
|
|
399
487
|
}
|
|
400
488
|
|
|
401
489
|
/**
|
|
402
490
|
* Method collect own property and prototype
|
|
403
491
|
*/
|
|
404
|
-
function getObjectMethods(obj) {
|
|
405
|
-
const methodsSet = new Set();
|
|
406
|
-
let protoObj = Reflect.getPrototypeOf(obj);
|
|
407
|
-
do {
|
|
408
|
-
if (protoObj.constructor.prototype !== Object.prototype) {
|
|
409
|
-
const keys = Reflect.ownKeys(protoObj);
|
|
410
|
-
keys.forEach(k => methodsSet.add(k));
|
|
411
|
-
}
|
|
412
|
-
} while (protoObj = Reflect.getPrototypeOf(protoObj));
|
|
413
|
-
Reflect.ownKeys(obj).forEach(k => methodsSet.add(k));
|
|
414
|
-
const methods = {};
|
|
415
|
-
for (const key of methodsSet.keys()) {
|
|
416
|
-
if (key !== 'constructor') methods[key] = obj[key];
|
|
417
|
-
}
|
|
418
|
-
return methods;
|
|
419
|
-
}
|
|
420
492
|
|
|
421
493
|
function loadTranslation(locale, vocabularies) {
|
|
422
494
|
if (!locale) {
|
|
423
|
-
return Translation.createEmpty()
|
|
495
|
+
return Translation.createEmpty()
|
|
424
496
|
}
|
|
425
497
|
|
|
426
|
-
let translation
|
|
498
|
+
let translation
|
|
427
499
|
|
|
428
500
|
// check if it is a known translation
|
|
429
501
|
if (Translation.langs[locale]) {
|
|
430
|
-
translation = new Translation(Translation.langs[locale])
|
|
502
|
+
translation = new Translation(Translation.langs[locale])
|
|
431
503
|
} else if (fileExists(path.join(global.codecept_dir, locale))) {
|
|
432
504
|
// get from a provided file instead
|
|
433
|
-
translation = Translation.createDefault()
|
|
434
|
-
translation.loadVocabulary(locale)
|
|
505
|
+
translation = Translation.createDefault()
|
|
506
|
+
translation.loadVocabulary(locale)
|
|
435
507
|
} else {
|
|
436
|
-
translation = Translation.createDefault()
|
|
508
|
+
translation = Translation.createDefault()
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
vocabularies.forEach(v => translation.loadVocabulary(v))
|
|
512
|
+
|
|
513
|
+
return translation
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function getHelperModuleName(helperName, config) {
|
|
517
|
+
// classical require
|
|
518
|
+
if (config[helperName].require) {
|
|
519
|
+
if (config[helperName].require.startsWith('.')) {
|
|
520
|
+
return path.resolve(global.codecept_dir, config[helperName].require) // custom helper
|
|
521
|
+
}
|
|
522
|
+
return config[helperName].require // plugin helper
|
|
437
523
|
}
|
|
438
524
|
|
|
439
|
-
|
|
525
|
+
// built-in helpers
|
|
526
|
+
if (helperName.startsWith('@codeceptjs/')) {
|
|
527
|
+
return helperName
|
|
528
|
+
}
|
|
440
529
|
|
|
441
|
-
|
|
530
|
+
// built-in helpers
|
|
531
|
+
return `./helper/${helperName}`
|
|
442
532
|
}
|