codeceptjs 3.7.6-beta.4 → 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.
- package/README.md +1 -3
- package/bin/codecept.js +51 -53
- package/bin/test-server.js +14 -3
- package/docs/webapi/click.mustache +5 -1
- package/lib/actor.js +15 -11
- package/lib/ai.js +72 -107
- 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 +102 -75
- 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 +62 -27
- 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 +36 -29
- 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 +24 -9
- package/lib/command/run.js +23 -8
- package/lib/command/utils.js +20 -18
- package/lib/command/workers/runTests.js +197 -114
- package/lib/config.js +124 -51
- package/lib/container.js +438 -87
- 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/element/WebElement.js +2 -2
- 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 +11 -11
- package/lib/helper/ApiDataFactory.js +50 -19
- package/lib/helper/Appium.js +19 -27
- package/lib/helper/FileSystem.js +32 -12
- package/lib/helper/GraphQL.js +3 -3
- package/lib/helper/GraphQLDataFactory.js +4 -4
- package/lib/helper/JSONResponse.js +25 -29
- package/lib/helper/Mochawesome.js +7 -4
- package/lib/helper/Playwright.js +902 -164
- package/lib/helper/Puppeteer.js +383 -76
- package/lib/helper/REST.js +29 -12
- package/lib/helper/WebDriver.js +268 -61
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- 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 +18 -9
- package/lib/helper/extras/PlaywrightRestartOpts.js +34 -23
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +29 -30
- package/lib/helper/network/actions.js +29 -44
- 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 +19 -12
- package/lib/listener/emptyRun.js +6 -7
- package/lib/listener/enhancedGlobalRetry.js +6 -6
- package/lib/listener/exit.js +4 -3
- package/lib/listener/globalRetry.js +5 -5
- package/lib/listener/globalTimeout.js +30 -14
- package/lib/listener/helpers.js +39 -14
- package/lib/listener/mocha.js +3 -4
- package/lib/listener/result.js +4 -5
- package/lib/listener/retryEnhancer.js +3 -3
- package/lib/listener/steps.js +8 -7
- package/lib/listener/store.js +3 -3
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +105 -62
- package/lib/mocha/bdd.js +99 -13
- package/lib/mocha/cli.js +59 -26
- package/lib/mocha/factory.js +78 -19
- package/lib/mocha/featureConfig.js +1 -1
- package/lib/mocha/gherkin.js +56 -24
- 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 -7
- package/lib/mocha/ui.js +28 -18
- package/lib/output.js +10 -8
- 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 +28 -11
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/customReporter.js +3 -2
- package/lib/plugin/enhancedRetryFailedStep.js +6 -6
- package/lib/plugin/heal.js +14 -9
- package/lib/plugin/htmlReporter.js +724 -99
- package/lib/plugin/pageInfo.js +10 -10
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +48 -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 -14
- package/lib/rerun.js +69 -26
- package/lib/result.js +4 -4
- package/lib/retryCoordinator.js +2 -2
- package/lib/secret.js +18 -17
- package/lib/session.js +95 -89
- package/lib/step/base.js +7 -7
- package/lib/step/comment.js +2 -2
- 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 +5 -5
- 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/template/heal.js +1 -1
- 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 +17 -6
- package/lib/timeout.js +1 -7
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils/mask_data.js +4 -10
- package/lib/utils.js +66 -64
- package/lib/workerStorage.js +17 -17
- package/lib/workers.js +214 -84
- package/package.json +41 -37
- 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 +4 -3
- package/translations/zh-CN.js +2 -2
- package/translations/zh-TW.js +2 -2
- package/typings/index.d.ts +5 -3
- package/typings/promiseBasedTypes.d.ts +4 -0
- package/typings/types.d.ts +4 -0
- 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/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -61
- 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/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/within.js +0 -90
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 {
|
package/lib/data/table.js
CHANGED
package/lib/effects.js
CHANGED
|
@@ -1,8 +1,89 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import recorder from './recorder.js'
|
|
2
|
+
import output from './output.js'
|
|
3
|
+
import store from './store.js'
|
|
4
|
+
import event from './event.js'
|
|
5
|
+
import container from './container.js'
|
|
6
|
+
import MetaStep from './step/meta.js'
|
|
7
|
+
import { isAsyncFunction } from './utils.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {CodeceptJS.LocatorOrString} context
|
|
11
|
+
* @param {Function} fn
|
|
12
|
+
* @return {Promise<*> | undefined}
|
|
13
|
+
*/
|
|
14
|
+
function within(context, fn) {
|
|
15
|
+
const helpers = store.dryRun ? {} : container.helpers()
|
|
16
|
+
const locator = typeof context === 'object' ? JSON.stringify(context) : context
|
|
17
|
+
|
|
18
|
+
return recorder.add(
|
|
19
|
+
'register within wrapper',
|
|
20
|
+
() => {
|
|
21
|
+
const metaStep = new WithinStep(locator, fn)
|
|
22
|
+
const defineMetaStep = step => (step.metaStep = metaStep)
|
|
23
|
+
recorder.session.start('within')
|
|
24
|
+
|
|
25
|
+
event.dispatcher.prependListener(event.step.before, defineMetaStep)
|
|
26
|
+
|
|
27
|
+
Object.keys(helpers).forEach(helper => {
|
|
28
|
+
if (helpers[helper]._withinBegin) recorder.add(`[${helper}] start within`, () => helpers[helper]._withinBegin(context))
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const finalize = () => {
|
|
32
|
+
event.dispatcher.removeListener(event.step.before, defineMetaStep)
|
|
33
|
+
recorder.add('Finalize session within session', () => {
|
|
34
|
+
output.stepShift = 1
|
|
35
|
+
recorder.session.restore('within')
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
const finishHelpers = () => {
|
|
39
|
+
Object.keys(helpers).forEach(helper => {
|
|
40
|
+
if (helpers[helper]._withinEnd) recorder.add(`[${helper}] finish within`, () => helpers[helper]._withinEnd())
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (isAsyncFunction(fn)) {
|
|
45
|
+
return fn()
|
|
46
|
+
.then(res => {
|
|
47
|
+
finishHelpers()
|
|
48
|
+
finalize()
|
|
49
|
+
return recorder.promise().then(() => res)
|
|
50
|
+
})
|
|
51
|
+
.catch(e => {
|
|
52
|
+
finalize()
|
|
53
|
+
recorder.throw(e)
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let res
|
|
58
|
+
try {
|
|
59
|
+
res = fn()
|
|
60
|
+
} catch (err) {
|
|
61
|
+
recorder.throw(err)
|
|
62
|
+
} finally {
|
|
63
|
+
finishHelpers()
|
|
64
|
+
recorder.catch(err => {
|
|
65
|
+
output.stepShift = 1
|
|
66
|
+
throw err
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
finalize()
|
|
70
|
+
return recorder.promise().then(() => res)
|
|
71
|
+
},
|
|
72
|
+
false,
|
|
73
|
+
false,
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
class WithinStep extends MetaStep {
|
|
78
|
+
constructor(locator, fn) {
|
|
79
|
+
super('Within')
|
|
80
|
+
this.args = [locator]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
toString() {
|
|
84
|
+
return `${this.prefix}Within ${this.humanizeArgs()}${this.suffix}`
|
|
85
|
+
}
|
|
86
|
+
}
|
|
6
87
|
|
|
7
88
|
/**
|
|
8
89
|
* A utility function for CodeceptJS tests that acts as a soft assertion.
|
|
@@ -49,8 +130,9 @@ async function hopeThat(callback) {
|
|
|
49
130
|
recorder.session.catch(err => {
|
|
50
131
|
result = false
|
|
51
132
|
const msg = err.inspect ? err.inspect() : err.toString()
|
|
52
|
-
debug(`Unsuccessful assertion > ${msg}`)
|
|
133
|
+
output.debug(`Unsuccessful assertion > ${msg}`)
|
|
53
134
|
event.dispatcher.once(event.test.finished, test => {
|
|
135
|
+
if (!test.notes) test.notes = []
|
|
54
136
|
test.notes.push({ type: 'conditionalError', text: msg })
|
|
55
137
|
})
|
|
56
138
|
recorder.session.restore(sessionName)
|
|
@@ -130,7 +212,7 @@ async function retryTo(callback, maxTries, pollInterval = 200) {
|
|
|
130
212
|
recorder.session.catch(err => {
|
|
131
213
|
recorder.session.restore(`${sessionName} ${tries}`)
|
|
132
214
|
if (tries <= maxTries) {
|
|
133
|
-
debug(`Error ${err}... Retrying`)
|
|
215
|
+
output.debug(`Error ${err}... Retrying`)
|
|
134
216
|
recorder.add(`${sessionName} ${tries}`, () => setTimeout(tryBlock, pollInterval))
|
|
135
217
|
} else {
|
|
136
218
|
// if maxTries reached
|
|
@@ -185,7 +267,7 @@ async function tryTo(callback) {
|
|
|
185
267
|
() => {
|
|
186
268
|
recorder.session.start(sessionName)
|
|
187
269
|
isAutoRetriesEnabled = store.autoRetries
|
|
188
|
-
if (isAutoRetriesEnabled) debug('Auto retries disabled inside tryTo effect')
|
|
270
|
+
if (isAutoRetriesEnabled) output.debug('Auto retries disabled inside tryTo effect')
|
|
189
271
|
store.autoRetries = false
|
|
190
272
|
callback()
|
|
191
273
|
recorder.add(() => {
|
|
@@ -196,7 +278,7 @@ async function tryTo(callback) {
|
|
|
196
278
|
recorder.session.catch(err => {
|
|
197
279
|
result = false
|
|
198
280
|
const msg = err.inspect ? err.inspect() : err.toString()
|
|
199
|
-
debug(`Unsuccessful try > ${msg}`)
|
|
281
|
+
output.debug(`Unsuccessful try > ${msg}`)
|
|
200
282
|
recorder.session.restore(sessionName)
|
|
201
283
|
return result
|
|
202
284
|
})
|
|
@@ -215,7 +297,9 @@ async function tryTo(callback) {
|
|
|
215
297
|
)
|
|
216
298
|
}
|
|
217
299
|
|
|
218
|
-
|
|
300
|
+
export { hopeThat, retryTo, tryTo, within }
|
|
301
|
+
|
|
302
|
+
export default {
|
|
219
303
|
hopeThat,
|
|
220
304
|
retryTo,
|
|
221
305
|
tryTo,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import assert from 'assert'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Unified WebElement class that wraps native element instances from different helpers
|
|
@@ -324,4 +324,4 @@ class WebElement {
|
|
|
324
324
|
}
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
-
|
|
327
|
+
export default WebElement
|
package/lib/els.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import output from './output.js'
|
|
2
|
+
import store from './store.js'
|
|
3
|
+
import container from './container.js'
|
|
4
|
+
import StepConfig from './step/config.js'
|
|
5
|
+
import recordStep from './step/record.js'
|
|
6
|
+
import FuncStep from './step/func.js'
|
|
7
|
+
import { truth } from './assert/truth.js'
|
|
8
|
+
import { isAsyncFunction, humanizeFunction } from './utils.js'
|
|
9
9
|
|
|
10
10
|
function element(purpose, locator, fn) {
|
|
11
11
|
let stepConfig
|
|
@@ -121,7 +121,9 @@ function expectAllElements(locator, fn) {
|
|
|
121
121
|
})
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
export { element, eachElement, expectElement, expectAnyElement, expectAllElements }
|
|
125
|
+
|
|
126
|
+
export default {
|
|
125
127
|
element,
|
|
126
128
|
eachElement,
|
|
127
129
|
expectElement,
|
package/lib/event.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
|
|
1
|
+
import debugModule from 'debug'
|
|
2
|
+
const debug = debugModule('codeceptjs:event')
|
|
3
|
+
import events from 'events'
|
|
4
|
+
import output from './output.js'
|
|
4
5
|
|
|
5
6
|
const MAX_LISTENERS = 200
|
|
6
7
|
|
|
@@ -18,7 +19,7 @@ if (typeof process.setMaxListeners === 'function') {
|
|
|
18
19
|
* @alias event
|
|
19
20
|
*/
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
export default {
|
|
22
23
|
/**
|
|
23
24
|
* @type {NodeJS.EventEmitter}
|
|
24
25
|
* @constant
|
|
@@ -161,25 +162,25 @@ module.exports = {
|
|
|
161
162
|
try {
|
|
162
163
|
this.dispatcher.emit.apply(this.dispatcher, arguments)
|
|
163
164
|
} catch (err) {
|
|
164
|
-
error(`Error processing ${event} event:`)
|
|
165
|
-
error(err.stack)
|
|
165
|
+
output.error(`Error processing ${event} event:`)
|
|
166
|
+
output.error(err.stack)
|
|
166
167
|
}
|
|
167
168
|
},
|
|
168
169
|
|
|
169
170
|
/** for testing only! */
|
|
170
|
-
cleanDispatcher
|
|
171
|
+
cleanDispatcher() {
|
|
171
172
|
let event
|
|
172
173
|
for (event in this.test) {
|
|
173
174
|
this.dispatcher.removeAllListeners(this.test[event])
|
|
174
175
|
}
|
|
175
176
|
for (event in this.suite) {
|
|
176
|
-
this.dispatcher.removeAllListeners(this.
|
|
177
|
+
this.dispatcher.removeAllListeners(this.suite[event])
|
|
177
178
|
}
|
|
178
179
|
for (event in this.step) {
|
|
179
|
-
this.dispatcher.removeAllListeners(this.
|
|
180
|
+
this.dispatcher.removeAllListeners(this.step[event])
|
|
180
181
|
}
|
|
181
182
|
for (event in this.all) {
|
|
182
|
-
this.dispatcher.removeAllListeners(this.
|
|
183
|
+
this.dispatcher.removeAllListeners(this.all[event])
|
|
183
184
|
}
|
|
184
185
|
},
|
|
185
186
|
}
|
package/lib/globals.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global variables initialization module
|
|
3
|
+
*
|
|
4
|
+
* Centralizes all global variable setup for CodeceptJS from codecept.js and mocha/ui.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fsPath from 'path'
|
|
8
|
+
import ActorFactory from './actor.js'
|
|
9
|
+
import output from './output.js'
|
|
10
|
+
import locator from './locator.js'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Initialize CodeceptJS core globals
|
|
14
|
+
* Called from Codecept.initGlobals()
|
|
15
|
+
*/
|
|
16
|
+
export async function initCodeceptGlobals(dir, config, container) {
|
|
17
|
+
global.codecept_dir = dir
|
|
18
|
+
global.output_dir = fsPath.resolve(dir, config.output)
|
|
19
|
+
|
|
20
|
+
if (config.noGlobals) return;
|
|
21
|
+
// Set up actor global - will use container when available
|
|
22
|
+
global.actor = global.codecept_actor = (obj) => {
|
|
23
|
+
return ActorFactory(obj, global.container || container)
|
|
24
|
+
}
|
|
25
|
+
global.Actor = global.actor
|
|
26
|
+
|
|
27
|
+
// Use dynamic imports for modules to avoid circular dependencies
|
|
28
|
+
global.pause = async (...args) => {
|
|
29
|
+
const pauseModule = await import('./pause.js')
|
|
30
|
+
return (pauseModule.default || pauseModule)(...args)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
global.within = async (...args) => {
|
|
34
|
+
return (await import('./effects.js')).within(...args)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
global.session = async (...args) => {
|
|
38
|
+
const sessionModule = await import('./session.js')
|
|
39
|
+
return (sessionModule.default || sessionModule)(...args)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const dataTableModule = await import('./data/table.js')
|
|
43
|
+
global.DataTable = dataTableModule.default || dataTableModule
|
|
44
|
+
|
|
45
|
+
global.locate = locatorQuery => {
|
|
46
|
+
return locator.build(locatorQuery)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
global.inject = () => container.support()
|
|
50
|
+
global.share = container.share
|
|
51
|
+
|
|
52
|
+
const secretModule = await import('./secret.js')
|
|
53
|
+
global.secret = secretModule.secret || (secretModule.default && secretModule.default.secret)
|
|
54
|
+
|
|
55
|
+
global.codecept_debug = output.debug
|
|
56
|
+
|
|
57
|
+
const codeceptjsModule = await import('./index.js') // load all objects
|
|
58
|
+
global.codeceptjs = codeceptjsModule.default || codeceptjsModule
|
|
59
|
+
|
|
60
|
+
// BDD step definitions
|
|
61
|
+
const stepDefinitionsModule = await import('./mocha/bdd.js')
|
|
62
|
+
const stepDefinitions = stepDefinitionsModule.default || stepDefinitionsModule
|
|
63
|
+
global.Given = stepDefinitions.Given
|
|
64
|
+
global.When = stepDefinitions.When
|
|
65
|
+
global.Then = stepDefinitions.Then
|
|
66
|
+
global.DefineParameterType = stepDefinitions.defineParameterType
|
|
67
|
+
|
|
68
|
+
// debug mode
|
|
69
|
+
global.debugMode = false
|
|
70
|
+
|
|
71
|
+
// mask sensitive data
|
|
72
|
+
global.maskSensitiveData = config.maskSensitiveData || false
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Initialize Mocha test framework globals (Feature, Scenario, etc.)
|
|
78
|
+
* Called from mocha/ui.js pre-require event
|
|
79
|
+
*/
|
|
80
|
+
export function initMochaGlobals(context) {
|
|
81
|
+
// Mocha test framework globals
|
|
82
|
+
global.BeforeAll = context.BeforeAll
|
|
83
|
+
global.AfterAll = context.AfterAll
|
|
84
|
+
global.Feature = context.Feature
|
|
85
|
+
global.xFeature = context.xFeature
|
|
86
|
+
global.BeforeSuite = context.BeforeSuite
|
|
87
|
+
global.AfterSuite = context.AfterSuite
|
|
88
|
+
global.Background = context.Background
|
|
89
|
+
global.Before = context.Before
|
|
90
|
+
global.After = context.After
|
|
91
|
+
global.Scenario = context.Scenario
|
|
92
|
+
global.xScenario = context.xScenario
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Clear all CodeceptJS globals (useful for testing/cleanup)
|
|
97
|
+
*/
|
|
98
|
+
export function clearGlobals() {
|
|
99
|
+
getGlobalNames().forEach(name => delete global[name]);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get list of all CodeceptJS global variable names
|
|
104
|
+
*/
|
|
105
|
+
export function getGlobalNames() {
|
|
106
|
+
return [
|
|
107
|
+
'codecept_dir',
|
|
108
|
+
'output_dir',
|
|
109
|
+
'actor',
|
|
110
|
+
'codecept_actor',
|
|
111
|
+
'Actor',
|
|
112
|
+
'pause',
|
|
113
|
+
'within',
|
|
114
|
+
'session',
|
|
115
|
+
'DataTable',
|
|
116
|
+
'locate',
|
|
117
|
+
'inject',
|
|
118
|
+
'share',
|
|
119
|
+
'secret',
|
|
120
|
+
'codecept_debug',
|
|
121
|
+
'codeceptjs',
|
|
122
|
+
'Given',
|
|
123
|
+
'When',
|
|
124
|
+
'Then',
|
|
125
|
+
'DefineParameterType',
|
|
126
|
+
'debugMode',
|
|
127
|
+
'maskSensitiveData',
|
|
128
|
+
'BeforeAll',
|
|
129
|
+
'AfterAll',
|
|
130
|
+
'Feature',
|
|
131
|
+
'xFeature',
|
|
132
|
+
'BeforeSuite',
|
|
133
|
+
'AfterSuite',
|
|
134
|
+
'Background',
|
|
135
|
+
'Before',
|
|
136
|
+
'After',
|
|
137
|
+
'Scenario',
|
|
138
|
+
'xScenario',
|
|
139
|
+
'container'
|
|
140
|
+
]
|
|
141
|
+
}
|
package/lib/heal.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import debugModule from 'debug'
|
|
2
|
+
const debug = debugModule('codeceptjs:heal')
|
|
3
|
+
import colors from 'chalk'
|
|
4
|
+
import recorder from './recorder.js'
|
|
5
|
+
import output from './output.js'
|
|
6
|
+
import event from './event.js'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @class
|
|
@@ -69,7 +69,7 @@ class Heal {
|
|
|
69
69
|
if (!prepareFn) continue
|
|
70
70
|
|
|
71
71
|
if (context[property]) continue
|
|
72
|
-
context[property] = await prepareFn(
|
|
72
|
+
context[property] = await prepareFn(global.inject())
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
output.level(currentOutputLevel)
|
|
@@ -116,10 +116,10 @@ class Heal {
|
|
|
116
116
|
})
|
|
117
117
|
|
|
118
118
|
if (typeof codeSnippet === 'string') {
|
|
119
|
-
const I =
|
|
119
|
+
const I = global.container.support('I')
|
|
120
120
|
await eval(codeSnippet)
|
|
121
121
|
} else if (typeof codeSnippet === 'function') {
|
|
122
|
-
await codeSnippet(
|
|
122
|
+
await codeSnippet(global.container.support())
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
this.fixes.push({
|
|
@@ -155,14 +155,14 @@ class Heal {
|
|
|
155
155
|
recorder.throw(error)
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
static setDefaultHealers() {
|
|
159
|
-
|
|
158
|
+
static async setDefaultHealers() {
|
|
159
|
+
await import('./template/heal.js')
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
const heal = new Heal()
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
export default heal
|
|
166
166
|
|
|
167
167
|
function matchRecipes(recipes, contextName) {
|
|
168
168
|
return Object.entries(recipes)
|
package/lib/helper/AI.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import HelperModule from '@codeceptjs/helper'
|
|
2
|
+
import ora from 'ora-classic'
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import ai from '../ai.js'
|
|
6
|
+
import Container from '../container.js'
|
|
7
|
+
import { splitByChunks, minifyHtml } from '../html.js'
|
|
8
|
+
import { beautify } from '../utils.js'
|
|
9
|
+
import output from '../output.js'
|
|
10
|
+
import { registerVariable } from '../pause.js'
|
|
11
11
|
|
|
12
12
|
const standardActingHelpers = Container.STANDARD_ACTING_HELPERS
|
|
13
13
|
|
|
@@ -211,4 +211,4 @@ class AI extends Helper {
|
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
export default AI
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const REST = require('./REST')
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import Helper from '@codeceptjs/helper'
|
|
3
|
+
import REST from './REST.js'
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Helper for managing remote data using REST API.
|
|
@@ -222,10 +221,11 @@ class ApiDataFactory extends Helper {
|
|
|
222
221
|
|
|
223
222
|
static _checkRequirements() {
|
|
224
223
|
try {
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
// In ESM, dependencies are already imported at the top
|
|
225
|
+
// The import will fail at module load time if dependencies are missing
|
|
226
|
+
return null
|
|
227
227
|
} catch (e) {
|
|
228
|
-
return ['axios'
|
|
228
|
+
return ['axios']
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
231
|
|
|
@@ -241,10 +241,25 @@ class ApiDataFactory extends Helper {
|
|
|
241
241
|
if (!createdItems.length) continue
|
|
242
242
|
this.debug(`Deleting ${createdItems.length} ${factoryName}(s)`)
|
|
243
243
|
for (const id in createdItems) {
|
|
244
|
-
|
|
244
|
+
const deletePromise = this._requestDelete(factoryName, createdItems[id])
|
|
245
|
+
if (deletePromise) {
|
|
246
|
+
promises.push(deletePromise.catch(err => {
|
|
247
|
+
this.debugSection('Delete Error', `Failed to delete ${factoryName} with id ${createdItems[id]}: ${err.message}`)
|
|
248
|
+
// Don't reject Promise.all, just log the error
|
|
249
|
+
return Promise.resolve()
|
|
250
|
+
}))
|
|
251
|
+
}
|
|
245
252
|
}
|
|
246
253
|
}
|
|
247
|
-
return Promise.all(promises)
|
|
254
|
+
return Promise.all(promises).then(() => {
|
|
255
|
+
// Clear the created items after successful cleanup
|
|
256
|
+
for (const factoryName in this.created) {
|
|
257
|
+
this.created[factoryName] = []
|
|
258
|
+
}
|
|
259
|
+
// Add a small delay to ensure file system changes propagate in Docker environments
|
|
260
|
+
// This helps avoid race conditions where GET requests after DELETE don't see the changes yet
|
|
261
|
+
return new Promise(resolve => setTimeout(resolve, 100))
|
|
262
|
+
})
|
|
248
263
|
}
|
|
249
264
|
|
|
250
265
|
/**
|
|
@@ -265,8 +280,8 @@ class ApiDataFactory extends Helper {
|
|
|
265
280
|
* @param {*} [options] options for programmatically generate the attributes
|
|
266
281
|
* @returns {Promise<*>}
|
|
267
282
|
*/
|
|
268
|
-
have(factory, params, options) {
|
|
269
|
-
const item = this._createItem(factory, params, options)
|
|
283
|
+
async have(factory, params, options) {
|
|
284
|
+
const item = await this._createItem(factory, params, options)
|
|
270
285
|
this.debug(`Creating ${factory} ${JSON.stringify(item)}`)
|
|
271
286
|
return this._requestCreate(factory, item)
|
|
272
287
|
}
|
|
@@ -298,19 +313,22 @@ class ApiDataFactory extends Helper {
|
|
|
298
313
|
return Promise.all(promises)
|
|
299
314
|
}
|
|
300
315
|
|
|
301
|
-
_createItem(model, data, options) {
|
|
316
|
+
async _createItem(model, data, options) {
|
|
302
317
|
if (!this.factories[model]) {
|
|
303
318
|
throw new Error(`Factory ${model} is not defined in config`)
|
|
304
319
|
}
|
|
305
320
|
let modulePath = this.factories[model].factory
|
|
306
321
|
try {
|
|
307
322
|
try {
|
|
308
|
-
|
|
323
|
+
// Try to resolve the path as-is first
|
|
324
|
+
await import.meta.resolve(modulePath)
|
|
309
325
|
} catch (e) {
|
|
326
|
+
// If not found, try relative to codecept_dir
|
|
310
327
|
modulePath = path.join(global.codecept_dir, modulePath)
|
|
311
328
|
}
|
|
312
329
|
// check if the new syntax `export default new Factory()` is used and loads the builder, otherwise loads the module that used old syntax `module.exports = new Factory()`.
|
|
313
|
-
const
|
|
330
|
+
const module = await import(modulePath)
|
|
331
|
+
const builder = module.default || module
|
|
314
332
|
return builder.build(data, options)
|
|
315
333
|
} catch (err) {
|
|
316
334
|
throw new Error(`Couldn't load factory file from ${modulePath}, check that
|
|
@@ -383,26 +401,39 @@ Current file error: ${err.message}`)
|
|
|
383
401
|
const url = this.factories[factory].delete[method].replace('{id}', id)
|
|
384
402
|
|
|
385
403
|
request = {
|
|
386
|
-
method,
|
|
404
|
+
method: method.toUpperCase(), // Ensure HTTP method is uppercase
|
|
387
405
|
url,
|
|
388
406
|
}
|
|
389
407
|
}
|
|
390
408
|
|
|
409
|
+
// Ensure method is uppercase (some servers require uppercase HTTP methods)
|
|
410
|
+
if (request.method) {
|
|
411
|
+
request.method = request.method.toUpperCase()
|
|
412
|
+
}
|
|
413
|
+
|
|
391
414
|
request.baseURL = this.config.endpoint
|
|
392
415
|
|
|
393
416
|
if (request.url.match(/^undefined/)) {
|
|
394
417
|
return this.debugSection('Please configure the delete request in your ApiDataFactory helper', "delete: () => ({ method: 'DELETE', url: '/api/users' })")
|
|
395
418
|
}
|
|
396
419
|
|
|
397
|
-
|
|
420
|
+
this.debugSection('Deleting', `${request.method} ${request.baseURL}${request.url} (ID: ${id})`)
|
|
421
|
+
|
|
422
|
+
return this.restHelper._executeRequest(request).then((resp) => {
|
|
398
423
|
const idx = this.created[factory].indexOf(id)
|
|
399
|
-
this.debugSection('Deleted
|
|
400
|
-
|
|
424
|
+
this.debugSection('Deleted Successfully', `${factory} ID: ${id}, Status: ${resp.status || 'OK'}`)
|
|
425
|
+
if (idx !== -1) {
|
|
426
|
+
this.created[factory].splice(idx, 1)
|
|
427
|
+
}
|
|
428
|
+
return resp
|
|
429
|
+
}).catch(err => {
|
|
430
|
+
this.debugSection('Delete Failed', `${factory} ID: ${id}, Error: ${err.message}`)
|
|
431
|
+
throw err
|
|
401
432
|
})
|
|
402
433
|
}
|
|
403
434
|
}
|
|
404
435
|
|
|
405
|
-
|
|
436
|
+
export { ApiDataFactory as default }
|
|
406
437
|
|
|
407
438
|
function createRequestFromFunction(param, data) {
|
|
408
439
|
if (typeof param !== 'function') return
|