codeceptjs 4.0.0-beta.4 → 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 +89 -119
- package/bin/codecept.js +53 -54
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +70 -102
- package/lib/ai.js +131 -121
- package/lib/assert/empty.js +11 -12
- package/lib/assert/equal.js +16 -21
- package/lib/assert/error.js +2 -2
- package/lib/assert/include.js +11 -15
- package/lib/assert/throws.js +3 -5
- package/lib/assert/truth.js +10 -7
- package/lib/assert.js +18 -18
- package/lib/codecept.js +112 -101
- package/lib/colorUtils.js +48 -50
- package/lib/command/check.js +206 -0
- package/lib/command/configMigrate.js +13 -14
- package/lib/command/definitions.js +24 -36
- package/lib/command/dryRun.js +16 -16
- package/lib/command/generate.js +38 -39
- package/lib/command/gherkin/init.js +36 -38
- package/lib/command/gherkin/snippets.js +76 -74
- package/lib/command/gherkin/steps.js +21 -18
- package/lib/command/info.js +49 -15
- package/lib/command/init.js +41 -37
- package/lib/command/interactive.js +22 -13
- package/lib/command/list.js +11 -10
- package/lib/command/run-multiple/chunk.js +50 -47
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +3 -3
- package/lib/command/run-multiple.js +27 -47
- package/lib/command/run-rerun.js +6 -7
- package/lib/command/run-workers.js +15 -66
- package/lib/command/run.js +8 -8
- package/lib/command/utils.js +22 -21
- package/lib/command/workers/runTests.js +131 -241
- package/lib/config.js +111 -49
- package/lib/container.js +589 -244
- package/lib/data/context.js +16 -18
- package/lib/data/dataScenarioConfig.js +9 -9
- package/lib/data/dataTableArgument.js +7 -7
- package/lib/data/table.js +6 -12
- package/lib/effects.js +307 -0
- package/lib/els.js +160 -0
- package/lib/event.js +24 -19
- package/lib/globals.js +141 -0
- package/lib/heal.js +89 -81
- package/lib/helper/AI.js +3 -2
- package/lib/helper/ApiDataFactory.js +19 -19
- package/lib/helper/Appium.js +47 -51
- package/lib/helper/FileSystem.js +35 -15
- package/lib/helper/GraphQL.js +1 -1
- package/lib/helper/GraphQLDataFactory.js +4 -4
- package/lib/helper/JSONResponse.js +72 -45
- package/lib/helper/Mochawesome.js +14 -11
- package/lib/helper/Playwright.js +832 -434
- package/lib/helper/Puppeteer.js +393 -292
- package/lib/helper/REST.js +32 -27
- package/lib/helper/WebDriver.js +320 -219
- 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 +22 -22
- 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 +54 -0
- package/lib/listener/exit.js +10 -12
- package/lib/listener/{retry.js → globalRetry.js} +10 -10
- package/lib/listener/globalTimeout.js +166 -0
- package/lib/listener/helpers.js +43 -24
- package/lib/listener/mocha.js +4 -5
- package/lib/listener/result.js +11 -0
- package/lib/listener/steps.js +26 -23
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +264 -0
- package/lib/mocha/bdd.js +167 -0
- package/lib/mocha/cli.js +341 -0
- package/lib/mocha/factory.js +160 -0
- package/lib/{interfaces → mocha}/featureConfig.js +33 -13
- package/lib/{interfaces → mocha}/gherkin.js +75 -45
- package/lib/mocha/hooks.js +121 -0
- package/lib/mocha/index.js +21 -0
- package/lib/mocha/inject.js +46 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +32 -8
- package/lib/mocha/suite.js +89 -0
- package/lib/mocha/test.js +178 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +229 -0
- package/lib/output.js +86 -64
- package/lib/parser.js +44 -44
- package/lib/pause.js +160 -139
- package/lib/plugin/analyze.js +403 -0
- package/lib/plugin/{autoLogin.js → auth.js} +137 -43
- package/lib/plugin/autoDelay.js +19 -15
- package/lib/plugin/coverage.js +22 -27
- package/lib/plugin/customLocator.js +5 -5
- package/lib/plugin/customReporter.js +53 -0
- package/lib/plugin/heal.js +49 -17
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +60 -19
- package/lib/plugin/screenshotOnFail.js +80 -83
- package/lib/plugin/stepByStepReport.js +70 -31
- package/lib/plugin/stepTimeout.js +7 -13
- package/lib/plugin/subtitles.js +10 -9
- package/lib/recorder.js +167 -126
- package/lib/rerun.js +94 -50
- package/lib/result.js +161 -0
- package/lib/secret.js +18 -17
- package/lib/session.js +95 -89
- package/lib/step/base.js +239 -0
- package/lib/step/comment.js +10 -0
- package/lib/step/config.js +50 -0
- package/lib/step/func.js +46 -0
- package/lib/step/helper.js +50 -0
- package/lib/step/meta.js +99 -0
- package/lib/step/record.js +74 -0
- package/lib/step/retry.js +11 -0
- package/lib/step/section.js +55 -0
- package/lib/step.js +18 -332
- package/lib/steps.js +54 -0
- package/lib/store.js +37 -5
- package/lib/template/heal.js +2 -11
- package/lib/timeout.js +60 -0
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils.js +354 -250
- package/lib/workerStorage.js +16 -16
- package/lib/workers.js +366 -282
- package/package.json +107 -95
- package/translations/de-DE.js +5 -4
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +23 -9
- package/translations/it-IT.js +5 -4
- package/translations/ja-JP.js +5 -4
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +5 -4
- package/translations/pt-BR.js +5 -4
- package/translations/ru-RU.js +5 -4
- package/translations/utils.js +18 -0
- package/translations/zh-CN.js +5 -4
- package/translations/zh-TW.js +5 -4
- package/typings/index.d.ts +177 -186
- package/typings/promiseBasedTypes.d.ts +3573 -5941
- package/typings/types.d.ts +4042 -6370
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/Nightmare.js +0 -1504
- package/lib/helper/Protractor.js +0 -1863
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/helper/TestCafe.js +0 -1414
- 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 -62
- package/lib/interfaces/bdd.js +0 -81
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -113
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/commentStep.js +0 -136
- package/lib/plugin/debugErrors.js +0 -67
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/retryTo.js +0 -127
- package/lib/plugin/selenoid.js +0 -384
- package/lib/plugin/standardActingHelpers.js +0 -3
- package/lib/plugin/tryTo.js +0 -115
- package/lib/plugin/wdio.js +0 -249
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
- package/lib/within.js +0 -70
package/lib/els.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
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
|
+
|
|
10
|
+
function element(purpose, locator, fn) {
|
|
11
|
+
let stepConfig
|
|
12
|
+
if (arguments[arguments.length - 1] instanceof StepConfig) {
|
|
13
|
+
stepConfig = arguments[arguments.length - 1]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!fn || fn === stepConfig) {
|
|
17
|
+
fn = locator
|
|
18
|
+
locator = purpose
|
|
19
|
+
purpose = 'first element'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const step = prepareStep(purpose, locator, fn)
|
|
23
|
+
if (!step) return
|
|
24
|
+
|
|
25
|
+
return executeStep(
|
|
26
|
+
step,
|
|
27
|
+
async () => {
|
|
28
|
+
const els = await step.helper._locate(locator)
|
|
29
|
+
output.debug(`Found ${els.length} elements, using first element`)
|
|
30
|
+
|
|
31
|
+
return fn(els[0])
|
|
32
|
+
},
|
|
33
|
+
stepConfig,
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function eachElement(purpose, locator, fn) {
|
|
38
|
+
if (!fn) {
|
|
39
|
+
fn = locator
|
|
40
|
+
locator = purpose
|
|
41
|
+
purpose = 'for each element'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const step = prepareStep(purpose, locator, fn)
|
|
45
|
+
if (!step) return
|
|
46
|
+
|
|
47
|
+
return executeStep(step, async () => {
|
|
48
|
+
const els = await step.helper._locate(locator)
|
|
49
|
+
output.debug(`Found ${els.length} elements for each elements to iterate`)
|
|
50
|
+
|
|
51
|
+
const errs = []
|
|
52
|
+
let i = 0
|
|
53
|
+
for (const el of els) {
|
|
54
|
+
try {
|
|
55
|
+
await fn(el, i)
|
|
56
|
+
} catch (err) {
|
|
57
|
+
output.error(`eachElement: failed operation on element #${i} ${el}`)
|
|
58
|
+
errs.push(err)
|
|
59
|
+
}
|
|
60
|
+
i++
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (errs.length) {
|
|
64
|
+
throw errs[0]
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function expectElement(locator, fn) {
|
|
70
|
+
const step = prepareStep('expect element to be', locator, fn)
|
|
71
|
+
if (!step) return
|
|
72
|
+
|
|
73
|
+
return executeStep(step, async () => {
|
|
74
|
+
const els = await step.helper._locate(locator)
|
|
75
|
+
output.debug(`Found ${els.length} elements, first will be used for assertion`)
|
|
76
|
+
|
|
77
|
+
const result = await fn(els[0])
|
|
78
|
+
const assertion = truth(`element (${locator})`, fn.toString())
|
|
79
|
+
assertion.assert(result)
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function expectAnyElement(locator, fn) {
|
|
84
|
+
const step = prepareStep('expect any element to be', locator, fn)
|
|
85
|
+
if (!step) return
|
|
86
|
+
|
|
87
|
+
return executeStep(step, async () => {
|
|
88
|
+
const els = await step.helper._locate(locator)
|
|
89
|
+
output.debug(`Found ${els.length} elements, at least one should pass the assertion`)
|
|
90
|
+
|
|
91
|
+
const assertion = truth(`any element of (${locator})`, fn.toString())
|
|
92
|
+
|
|
93
|
+
let found = false
|
|
94
|
+
for (const el of els) {
|
|
95
|
+
const result = await fn(el)
|
|
96
|
+
if (result) {
|
|
97
|
+
found = true
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (!found) throw assertion.getException()
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function expectAllElements(locator, fn) {
|
|
106
|
+
const step = prepareStep('expect all elements', locator, fn)
|
|
107
|
+
if (!step) return
|
|
108
|
+
|
|
109
|
+
return executeStep(step, async () => {
|
|
110
|
+
const els = await step.helper._locate(locator)
|
|
111
|
+
output.debug(`Found ${els.length} elements, all should pass the assertion`)
|
|
112
|
+
|
|
113
|
+
let i = 1
|
|
114
|
+
for (const el of els) {
|
|
115
|
+
output.debug(`checking element #${i}: ${el}`)
|
|
116
|
+
const result = await fn(el)
|
|
117
|
+
const assertion = truth(`element #${i} of (${locator})`, humanizeFunction(fn))
|
|
118
|
+
assertion.assert(result)
|
|
119
|
+
i++
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export { element, eachElement, expectElement, expectAnyElement, expectAllElements }
|
|
125
|
+
|
|
126
|
+
export default {
|
|
127
|
+
element,
|
|
128
|
+
eachElement,
|
|
129
|
+
expectElement,
|
|
130
|
+
expectAnyElement,
|
|
131
|
+
expectAllElements,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function prepareStep(purpose, locator, fn) {
|
|
135
|
+
if (store.dryRun) return
|
|
136
|
+
const helpers = Object.values(container.helpers())
|
|
137
|
+
|
|
138
|
+
const helper = helpers.filter(h => !!h._locate)[0]
|
|
139
|
+
|
|
140
|
+
if (!helper) {
|
|
141
|
+
throw new Error('No helper enabled with _locate method with returns a list of elements.')
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!isAsyncFunction(fn)) {
|
|
145
|
+
throw new Error('Async function should be passed into each element')
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const isAssertion = purpose.startsWith('expect')
|
|
149
|
+
|
|
150
|
+
const step = new FuncStep(`${purpose} within "${locator}" ${isAssertion ? 'to be' : 'to'}`)
|
|
151
|
+
step.setHelper(helper)
|
|
152
|
+
step.setArguments([humanizeFunction(fn)]) // user defined function is a passed argument
|
|
153
|
+
|
|
154
|
+
return step
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function executeStep(step, action, stepConfig = {}) {
|
|
158
|
+
step.setCallable(action)
|
|
159
|
+
return recordStep(step, [stepConfig])
|
|
160
|
+
}
|
package/lib/event.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
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
|
-
const dispatcher = new events.EventEmitter()
|
|
6
|
+
const dispatcher = new events.EventEmitter()
|
|
6
7
|
|
|
7
|
-
dispatcher.setMaxListeners(50)
|
|
8
|
+
dispatcher.setMaxListeners(50)
|
|
8
9
|
/**
|
|
9
10
|
* @namespace
|
|
10
11
|
* @alias event
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
+
export default {
|
|
13
14
|
/**
|
|
14
15
|
* @type {NodeJS.EventEmitter}
|
|
15
16
|
* @constant
|
|
@@ -54,12 +55,16 @@ module.exports = {
|
|
|
54
55
|
* @inner
|
|
55
56
|
* @property {'hook.start'} started
|
|
56
57
|
* @property {'hook.passed'} passed
|
|
58
|
+
* @property {'hook.failed'} failed
|
|
59
|
+
* @property {'hook.finished'} finished
|
|
57
60
|
*/
|
|
58
61
|
hook: {
|
|
59
62
|
started: 'hook.start',
|
|
60
63
|
passed: 'hook.passed',
|
|
61
64
|
failed: 'hook.failed',
|
|
65
|
+
finished: 'hook.finished',
|
|
62
66
|
},
|
|
67
|
+
|
|
63
68
|
/**
|
|
64
69
|
* @type {object}
|
|
65
70
|
* @constant
|
|
@@ -140,33 +145,33 @@ module.exports = {
|
|
|
140
145
|
* @param {*} [param]
|
|
141
146
|
*/
|
|
142
147
|
emit(event, param) {
|
|
143
|
-
let msg = `Emitted | ${event}
|
|
148
|
+
let msg = `Emitted | ${event}`
|
|
144
149
|
if (param && param.toString()) {
|
|
145
|
-
msg += ` (${param.toString()})
|
|
150
|
+
msg += ` (${param.toString()})`
|
|
146
151
|
}
|
|
147
|
-
debug(msg)
|
|
152
|
+
debug(msg)
|
|
148
153
|
try {
|
|
149
|
-
this.dispatcher.emit.apply(this.dispatcher, arguments)
|
|
154
|
+
this.dispatcher.emit.apply(this.dispatcher, arguments)
|
|
150
155
|
} catch (err) {
|
|
151
|
-
error(`Error processing ${event} event:`)
|
|
152
|
-
error(err.stack)
|
|
156
|
+
output.error(`Error processing ${event} event:`)
|
|
157
|
+
output.error(err.stack)
|
|
153
158
|
}
|
|
154
159
|
},
|
|
155
160
|
|
|
156
161
|
/** for testing only! */
|
|
157
|
-
cleanDispatcher
|
|
158
|
-
let event
|
|
162
|
+
cleanDispatcher() {
|
|
163
|
+
let event
|
|
159
164
|
for (event in this.test) {
|
|
160
|
-
this.dispatcher.removeAllListeners(this.test[event])
|
|
165
|
+
this.dispatcher.removeAllListeners(this.test[event])
|
|
161
166
|
}
|
|
162
167
|
for (event in this.suite) {
|
|
163
|
-
this.dispatcher.removeAllListeners(this.
|
|
168
|
+
this.dispatcher.removeAllListeners(this.suite[event])
|
|
164
169
|
}
|
|
165
170
|
for (event in this.step) {
|
|
166
|
-
this.dispatcher.removeAllListeners(this.
|
|
171
|
+
this.dispatcher.removeAllListeners(this.step[event])
|
|
167
172
|
}
|
|
168
173
|
for (event in this.all) {
|
|
169
|
-
this.dispatcher.removeAllListeners(this.
|
|
174
|
+
this.dispatcher.removeAllListeners(this.all[event])
|
|
170
175
|
}
|
|
171
176
|
},
|
|
172
|
-
}
|
|
177
|
+
}
|
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,122 +1,125 @@
|
|
|
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
|
|
10
10
|
*/
|
|
11
11
|
class Heal {
|
|
12
12
|
constructor() {
|
|
13
|
-
this.recipes = {}
|
|
14
|
-
this.fixes = []
|
|
15
|
-
this.prepareFns = []
|
|
16
|
-
this.contextName = null
|
|
17
|
-
this.numHealed = 0
|
|
13
|
+
this.recipes = {}
|
|
14
|
+
this.fixes = []
|
|
15
|
+
this.prepareFns = []
|
|
16
|
+
this.contextName = null
|
|
17
|
+
this.numHealed = 0
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
clear() {
|
|
21
|
-
this.recipes = {}
|
|
22
|
-
this.fixes = []
|
|
23
|
-
this.prepareFns = []
|
|
24
|
-
this.contextName = null
|
|
25
|
-
this.numHealed = 0
|
|
21
|
+
this.recipes = {}
|
|
22
|
+
this.fixes = []
|
|
23
|
+
this.prepareFns = []
|
|
24
|
+
this.contextName = null
|
|
25
|
+
this.numHealed = 0
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
addRecipe(name, opts = {}) {
|
|
29
|
-
if (!opts.priority) opts.priority = 0
|
|
29
|
+
if (!opts.priority) opts.priority = 0
|
|
30
30
|
|
|
31
|
-
if (!opts.fn) throw new Error(`Recipe ${name} should have a function 'fn' to execute`)
|
|
31
|
+
if (!opts.fn) throw new Error(`Recipe ${name} should have a function 'fn' to execute`)
|
|
32
32
|
|
|
33
|
-
this.recipes[name] = opts
|
|
33
|
+
this.recipes[name] = opts
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
connectToEvents() {
|
|
37
|
-
event.dispatcher.on(event.suite.before,
|
|
38
|
-
this.contextName = suite.title
|
|
39
|
-
})
|
|
37
|
+
event.dispatcher.on(event.suite.before, suite => {
|
|
38
|
+
this.contextName = suite.title
|
|
39
|
+
})
|
|
40
40
|
|
|
41
|
-
event.dispatcher.on(event.test.started,
|
|
42
|
-
this.contextName = test.fullTitle()
|
|
43
|
-
})
|
|
41
|
+
event.dispatcher.on(event.test.started, test => {
|
|
42
|
+
this.contextName = test.fullTitle()
|
|
43
|
+
})
|
|
44
44
|
|
|
45
45
|
event.dispatcher.on(event.test.finished, () => {
|
|
46
|
-
this.contextName = null
|
|
47
|
-
})
|
|
46
|
+
this.contextName = null
|
|
47
|
+
})
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
hasCorrespondingRecipes(step) {
|
|
51
|
-
return matchRecipes(this.recipes, this.contextName)
|
|
52
|
-
.filter(r => !r.steps || r.steps.includes(step.name))
|
|
53
|
-
.length > 0;
|
|
51
|
+
return matchRecipes(this.recipes, this.contextName).filter(r => !r.steps || r.steps.includes(step.name)).length > 0
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
async getCodeSuggestions(context) {
|
|
57
|
-
const suggestions = []
|
|
58
|
-
const recipes = matchRecipes(this.recipes, this.contextName)
|
|
55
|
+
const suggestions = []
|
|
56
|
+
const recipes = matchRecipes(this.recipes, this.contextName)
|
|
59
57
|
|
|
60
|
-
debug('Recipes', recipes)
|
|
58
|
+
debug('Recipes', recipes)
|
|
61
59
|
|
|
62
|
-
const currentOutputLevel = output.level()
|
|
63
|
-
output.level(0)
|
|
60
|
+
const currentOutputLevel = output.level()
|
|
61
|
+
output.level(0)
|
|
64
62
|
|
|
65
|
-
for (const [property, prepareFn] of Object.entries(
|
|
66
|
-
|
|
63
|
+
for (const [property, prepareFn] of Object.entries(
|
|
64
|
+
recipes
|
|
65
|
+
.map(r => r.prepare)
|
|
66
|
+
.filter(p => !!p)
|
|
67
|
+
.reduce((acc, obj) => ({ ...acc, ...obj }), {}),
|
|
68
|
+
)) {
|
|
69
|
+
if (!prepareFn) continue
|
|
67
70
|
|
|
68
|
-
if (context[property]) continue
|
|
69
|
-
context[property] = await prepareFn(
|
|
71
|
+
if (context[property]) continue
|
|
72
|
+
context[property] = await prepareFn(global.inject())
|
|
70
73
|
}
|
|
71
74
|
|
|
72
|
-
output.level(currentOutputLevel)
|
|
75
|
+
output.level(currentOutputLevel)
|
|
73
76
|
|
|
74
77
|
for (const recipe of recipes) {
|
|
75
|
-
let snippets = await recipe.fn(context)
|
|
76
|
-
if (!Array.isArray(snippets)) snippets = [snippets]
|
|
78
|
+
let snippets = await recipe.fn(context)
|
|
79
|
+
if (!Array.isArray(snippets)) snippets = [snippets]
|
|
77
80
|
|
|
78
81
|
suggestions.push({
|
|
79
82
|
name: recipe.name,
|
|
80
83
|
snippets,
|
|
81
|
-
})
|
|
84
|
+
})
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
return suggestions.filter(s => !isBlank(s.snippets))
|
|
87
|
+
return suggestions.filter(s => !isBlank(s.snippets))
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
async healStep(failedStep, error, failureContext = {}) {
|
|
88
|
-
output.debug(`Trying to heal ${failedStep.toCode()} step`)
|
|
91
|
+
output.debug(`Trying to heal ${failedStep.toCode()} step`)
|
|
89
92
|
|
|
90
93
|
Object.assign(failureContext, {
|
|
91
94
|
error,
|
|
92
95
|
step: failedStep,
|
|
93
96
|
prevSteps: failureContext?.test?.steps?.slice(0, -1) || [],
|
|
94
|
-
})
|
|
97
|
+
})
|
|
95
98
|
|
|
96
|
-
const suggestions = await this.getCodeSuggestions(failureContext)
|
|
99
|
+
const suggestions = await this.getCodeSuggestions(failureContext)
|
|
97
100
|
|
|
98
101
|
if (suggestions.length === 0) {
|
|
99
|
-
debug('No healing suggestions found')
|
|
100
|
-
throw error
|
|
102
|
+
debug('No healing suggestions found')
|
|
103
|
+
throw error
|
|
101
104
|
}
|
|
102
105
|
|
|
103
|
-
output.debug(`Received ${suggestions.length} suggestion${suggestions.length === 1 ? '' : 's'}`)
|
|
106
|
+
output.debug(`Received ${suggestions.length} suggestion${suggestions.length === 1 ? '' : 's'}`)
|
|
104
107
|
|
|
105
|
-
debug(suggestions)
|
|
108
|
+
debug(suggestions)
|
|
106
109
|
|
|
107
110
|
for (const suggestion of suggestions) {
|
|
108
111
|
for (const codeSnippet of suggestion.snippets) {
|
|
109
112
|
try {
|
|
110
|
-
debug('Executing', codeSnippet)
|
|
111
|
-
recorder.catch(
|
|
112
|
-
debug(e)
|
|
113
|
-
})
|
|
113
|
+
debug('Executing', codeSnippet)
|
|
114
|
+
recorder.catch(e => {
|
|
115
|
+
debug(e)
|
|
116
|
+
})
|
|
114
117
|
|
|
115
118
|
if (typeof codeSnippet === 'string') {
|
|
116
|
-
const I =
|
|
117
|
-
await eval(codeSnippet)
|
|
119
|
+
const I = global.container.support('I')
|
|
120
|
+
await eval(codeSnippet)
|
|
118
121
|
} else if (typeof codeSnippet === 'function') {
|
|
119
|
-
await codeSnippet(
|
|
122
|
+
await codeSnippet(global.container.support())
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
this.fixes.push({
|
|
@@ -124,49 +127,54 @@ class Heal {
|
|
|
124
127
|
test: failureContext?.test,
|
|
125
128
|
step: failedStep,
|
|
126
129
|
snippet: codeSnippet,
|
|
127
|
-
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
if (failureContext?.test) {
|
|
133
|
+
const test = failureContext.test
|
|
134
|
+
let note = `This test was healed by '${suggestion.name}'`
|
|
135
|
+
note += `\n\nReplace the failed code:\n\n`
|
|
136
|
+
note += colors.red(`- ${failedStep.toCode()}\n`)
|
|
137
|
+
note += colors.green(`+ ${codeSnippet}\n`)
|
|
138
|
+
test.addNote('heal', note)
|
|
139
|
+
test.meta.healed = true
|
|
140
|
+
}
|
|
128
141
|
|
|
129
|
-
recorder.add('healed', () => output.print(colors.bold.green(` Code healed successfully by ${suggestion.name}`), colors.gray('(no errors thrown)')))
|
|
130
|
-
this.numHealed
|
|
142
|
+
recorder.add('healed', () => output.print(colors.bold.green(` Code healed successfully by ${suggestion.name}`), colors.gray('(no errors thrown)')))
|
|
143
|
+
this.numHealed++
|
|
131
144
|
// recorder.session.restore();
|
|
132
|
-
return
|
|
145
|
+
return
|
|
133
146
|
} catch (err) {
|
|
134
|
-
debug('Failed to execute code', err)
|
|
135
|
-
recorder.ignoreErr(err)
|
|
136
|
-
recorder.catchWithoutStop(err)
|
|
137
|
-
await recorder.promise()
|
|
147
|
+
debug('Failed to execute code', err)
|
|
148
|
+
recorder.ignoreErr(err) // healing did not help
|
|
149
|
+
recorder.catchWithoutStop(err)
|
|
150
|
+
await recorder.promise() // wait for all promises to resolve
|
|
138
151
|
}
|
|
139
152
|
}
|
|
140
153
|
}
|
|
141
|
-
output.debug(`Couldn't heal the code for ${failedStep.toCode()}`)
|
|
142
|
-
recorder.throw(error)
|
|
154
|
+
output.debug(`Couldn't heal the code for ${failedStep.toCode()}`)
|
|
155
|
+
recorder.throw(error)
|
|
143
156
|
}
|
|
144
157
|
|
|
145
|
-
static setDefaultHealers() {
|
|
146
|
-
|
|
158
|
+
static async setDefaultHealers() {
|
|
159
|
+
await import('./template/heal.js')
|
|
147
160
|
}
|
|
148
161
|
}
|
|
149
162
|
|
|
150
|
-
const heal = new Heal()
|
|
163
|
+
const heal = new Heal()
|
|
151
164
|
|
|
152
|
-
|
|
165
|
+
export default heal
|
|
153
166
|
|
|
154
167
|
function matchRecipes(recipes, contextName) {
|
|
155
168
|
return Object.entries(recipes)
|
|
156
169
|
.filter(([, recipe]) => !contextName || !recipe.grep || new RegExp(recipe.grep).test(contextName))
|
|
157
170
|
.sort(([, a], [, b]) => a.priority - b.priority)
|
|
158
171
|
.map(([name, recipe]) => {
|
|
159
|
-
recipe.name = name
|
|
160
|
-
return recipe
|
|
172
|
+
recipe.name = name
|
|
173
|
+
return recipe
|
|
161
174
|
})
|
|
162
|
-
.filter(r => !!r.fn)
|
|
175
|
+
.filter(r => !!r.fn)
|
|
163
176
|
}
|
|
164
177
|
|
|
165
178
|
function isBlank(value) {
|
|
166
|
-
return (
|
|
167
|
-
value == null
|
|
168
|
-
|| (Array.isArray(value) && value.length === 0)
|
|
169
|
-
|| (typeof value === 'object' && Object.keys(value).length === 0)
|
|
170
|
-
|| (typeof value === 'string' && value.trim() === '')
|
|
171
|
-
);
|
|
179
|
+
return value == null || (Array.isArray(value) && value.length === 0) || (typeof value === 'object' && Object.keys(value).length === 0) || (typeof value === 'string' && value.trim() === '')
|
|
172
180
|
}
|
package/lib/helper/AI.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
const
|
|
1
|
+
const HelperModule = require('@codeceptjs/helper')
|
|
2
2
|
const ora = require('ora-classic')
|
|
3
3
|
const fs = require('fs')
|
|
4
4
|
const path = require('path')
|
|
5
5
|
const ai = require('../ai')
|
|
6
|
-
const standardActingHelpers = require('../plugin/standardActingHelpers')
|
|
7
6
|
const Container = require('../container')
|
|
8
7
|
const { splitByChunks, minifyHtml } = require('../html')
|
|
9
8
|
const { beautify } = require('../utils')
|
|
10
9
|
const output = require('../output')
|
|
11
10
|
const { registerVariable } = require('../pause')
|
|
12
11
|
|
|
12
|
+
const standardActingHelpers = Container.STANDARD_ACTING_HELPERS
|
|
13
|
+
|
|
13
14
|
const gtpRole = {
|
|
14
15
|
user: 'user',
|
|
15
16
|
}
|