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/secret.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
import { deepClone } from './utils.js'
|
|
2
2
|
|
|
3
|
-
const maskedString = '*****'
|
|
3
|
+
const maskedString = '*****'
|
|
4
4
|
|
|
5
5
|
/** @param {string} secret */
|
|
6
6
|
class Secret {
|
|
7
7
|
constructor(secret) {
|
|
8
|
-
this._secret = secret
|
|
8
|
+
this._secret = secret
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/** @returns {string} */
|
|
12
12
|
toString() {
|
|
13
|
-
return this._secret
|
|
13
|
+
return this._secret
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
getMasked() {
|
|
17
|
-
return maskedString
|
|
17
|
+
return maskedString
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -23,11 +23,11 @@ class Secret {
|
|
|
23
23
|
*/
|
|
24
24
|
static secret(secret) {
|
|
25
25
|
if (typeof secret === 'object') {
|
|
26
|
-
const fields = Array.from(arguments)
|
|
27
|
-
fields.shift()
|
|
28
|
-
return secretObject(secret, fields)
|
|
26
|
+
const fields = Array.from(arguments)
|
|
27
|
+
fields.shift()
|
|
28
|
+
return secretObject(secret, fields)
|
|
29
29
|
}
|
|
30
|
-
return new Secret(secret)
|
|
30
|
+
return new Secret(secret)
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -36,16 +36,17 @@ function secretObject(obj, fieldsToHide = []) {
|
|
|
36
36
|
get(obj, prop) {
|
|
37
37
|
if (prop === 'toString') {
|
|
38
38
|
return function () {
|
|
39
|
-
const maskedObject = deepClone(obj)
|
|
40
|
-
fieldsToHide.forEach(f => (maskedObject[f] = maskedString))
|
|
41
|
-
return JSON.stringify(maskedObject)
|
|
42
|
-
}
|
|
39
|
+
const maskedObject = deepClone(obj)
|
|
40
|
+
fieldsToHide.forEach(f => (maskedObject[f] = maskedString))
|
|
41
|
+
return JSON.stringify(maskedObject)
|
|
42
|
+
}
|
|
43
43
|
}
|
|
44
|
-
return fieldsToHide.includes(prop) ? new Secret(obj[prop]) : obj[prop]
|
|
44
|
+
return fieldsToHide.includes(prop) ? new Secret(obj[prop]) : obj[prop]
|
|
45
45
|
},
|
|
46
|
-
}
|
|
46
|
+
}
|
|
47
47
|
|
|
48
|
-
return new Proxy(obj, handler)
|
|
48
|
+
return new Proxy(obj, handler)
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
export default Secret
|
|
52
|
+
export const secret = Secret.secret
|
package/lib/session.js
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const sessionColors = [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
'red',
|
|
12
|
-
'magenta',
|
|
13
|
-
'yellow',
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
const savedSessions = {};
|
|
1
|
+
import recorder from './recorder.js'
|
|
2
|
+
import container from './container.js'
|
|
3
|
+
import event from './event.js'
|
|
4
|
+
import output from './output.js'
|
|
5
|
+
import store from './store.js'
|
|
6
|
+
import { isAsyncFunction } from './utils.js'
|
|
7
|
+
|
|
8
|
+
const sessionColors = ['cyan', 'blue', 'red', 'magenta', 'yellow']
|
|
9
|
+
|
|
10
|
+
const savedSessions = {}
|
|
17
11
|
|
|
18
12
|
/**
|
|
19
13
|
* @param {CodeceptJS.LocatorOrString} sessionName
|
|
@@ -24,109 +18,121 @@ const savedSessions = {};
|
|
|
24
18
|
function session(sessionName, config, fn) {
|
|
25
19
|
if (typeof config === 'function') {
|
|
26
20
|
if (typeof fn === 'function') {
|
|
27
|
-
config = config()
|
|
21
|
+
config = config()
|
|
28
22
|
} else {
|
|
29
23
|
// no config but with function
|
|
30
|
-
fn = config
|
|
31
|
-
config = {}
|
|
24
|
+
fn = config
|
|
25
|
+
config = {}
|
|
32
26
|
}
|
|
33
27
|
}
|
|
34
28
|
// session helpers won't use beforeSuite and afterSuite hooks...
|
|
35
29
|
// restart: false options are not allowed as well
|
|
36
30
|
// but those helpers should be already started so inside listener/helpers.js the `_init` method should already be called
|
|
37
31
|
|
|
38
|
-
const helpers = container.helpers()
|
|
32
|
+
const helpers = container.helpers()
|
|
39
33
|
|
|
40
34
|
if (!savedSessions[sessionName]) {
|
|
41
35
|
for (const helper in helpers) {
|
|
42
|
-
if (!helpers[helper]._session) continue
|
|
36
|
+
if (!helpers[helper]._session) continue
|
|
43
37
|
savedSessions[sessionName] = {
|
|
44
38
|
start: () => null,
|
|
45
39
|
stop: () => null,
|
|
46
40
|
loadVars: () => null,
|
|
47
41
|
restoreVars: () => null,
|
|
48
42
|
...(store.dryRun ? {} : helpers[helper]._session()),
|
|
49
|
-
}
|
|
50
|
-
break
|
|
43
|
+
}
|
|
44
|
+
break
|
|
51
45
|
}
|
|
52
46
|
|
|
53
47
|
const closeBrowsers = () => {
|
|
54
|
-
const session = savedSessions[sessionName]
|
|
55
|
-
if (!session) return
|
|
56
|
-
session.stop(session.vars)
|
|
57
|
-
delete savedSessions[sessionName]
|
|
58
|
-
}
|
|
48
|
+
const session = savedSessions[sessionName]
|
|
49
|
+
if (!session) return
|
|
50
|
+
session.stop(session.vars)
|
|
51
|
+
delete savedSessions[sessionName]
|
|
52
|
+
}
|
|
59
53
|
|
|
60
54
|
event.dispatcher.once(event.test.after, () => {
|
|
61
|
-
recorder.add('close session browsers', closeBrowsers)
|
|
62
|
-
})
|
|
55
|
+
recorder.add('close session browsers', closeBrowsers)
|
|
56
|
+
})
|
|
63
57
|
|
|
64
58
|
if (!savedSessions[sessionName]) {
|
|
65
|
-
throw new Error('Configured helpers do not support starting sessions. Please use a helper with session support.')
|
|
59
|
+
throw new Error('Configured helpers do not support starting sessions. Please use a helper with session support.')
|
|
66
60
|
}
|
|
67
61
|
|
|
68
62
|
recorder.add('save vars', async () => {
|
|
69
|
-
savedSessions[sessionName].vars = await savedSessions[sessionName].start(sessionName, config)
|
|
70
|
-
})
|
|
63
|
+
savedSessions[sessionName].vars = await savedSessions[sessionName].start(sessionName, config)
|
|
64
|
+
})
|
|
71
65
|
}
|
|
72
66
|
|
|
73
67
|
// pick random color
|
|
74
|
-
const color = sessionColors[Object.keys(savedSessions).indexOf(sessionName) % sessionColors.length]
|
|
75
|
-
|
|
76
|
-
const addContextToStep = (step) => {
|
|
77
|
-
step.actor = `${output.colors[color](sessionName)}: I`;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
if (!fn) return; // no current session steps
|
|
81
|
-
|
|
82
|
-
return recorder.add('register session wrapper', async () => {
|
|
83
|
-
const session = savedSessions[sessionName];
|
|
84
|
-
recorder.session.start(`session:${sessionName}`);
|
|
85
|
-
event.dispatcher.on(event.step.after, addContextToStep);
|
|
86
|
-
recorder.add('switch to browser', () => {
|
|
87
|
-
const session = savedSessions[sessionName];
|
|
88
|
-
return session.loadVars(session.vars);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const finalize = () => {
|
|
92
|
-
recorder.add('Finalize session', async () => {
|
|
93
|
-
output.stepShift = 0;
|
|
94
|
-
event.dispatcher.removeListener(event.step.after, addContextToStep);
|
|
95
|
-
await session.restoreVars();
|
|
96
|
-
recorder.session.restore(`session:${sessionName}`);
|
|
97
|
-
});
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// Indicate when executing this function that we are in a session
|
|
101
|
-
if (isAsyncFunction(fn)) {
|
|
102
|
-
return fn.apply(null).then((res) => {
|
|
103
|
-
finalize();
|
|
104
|
-
return recorder.promise().then(() => res);
|
|
105
|
-
}).catch((e) => {
|
|
106
|
-
output.stepShift = 0;
|
|
107
|
-
session.restoreVars(sessionName);
|
|
108
|
-
event.dispatcher.removeListener(event.step.after, addContextToStep);
|
|
109
|
-
recorder.throw(e);
|
|
110
|
-
return recorder.promise();
|
|
111
|
-
});
|
|
112
|
-
}
|
|
68
|
+
const color = sessionColors[Object.keys(savedSessions).indexOf(sessionName) % sessionColors.length]
|
|
113
69
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
70
|
+
const addContextToStep = step => {
|
|
71
|
+
step.actor = `${output.colors[color](sessionName)}: I`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!fn) return // no current session steps
|
|
75
|
+
|
|
76
|
+
return recorder.add(
|
|
77
|
+
'register session wrapper',
|
|
78
|
+
async () => {
|
|
79
|
+
const session = savedSessions[sessionName]
|
|
80
|
+
if (!session) {
|
|
81
|
+
throw new Error(`Session "${sessionName}" not found. It may have been closed already.`)
|
|
82
|
+
}
|
|
83
|
+
recorder.session.start(`session:${sessionName}`)
|
|
84
|
+
event.dispatcher.on(event.step.after, addContextToStep)
|
|
85
|
+
recorder.add('switch to browser', () => {
|
|
86
|
+
const session = savedSessions[sessionName]
|
|
87
|
+
return session.loadVars(session.vars)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const finalize = () => {
|
|
91
|
+
recorder.add('Finalize session', async () => {
|
|
92
|
+
output.stepShift = 0
|
|
93
|
+
event.dispatcher.removeListener(event.step.after, addContextToStep)
|
|
94
|
+
await session.restoreVars()
|
|
95
|
+
recorder.session.restore(`session:${sessionName}`)
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Indicate when executing this function that we are in a session
|
|
100
|
+
if (isAsyncFunction(fn)) {
|
|
101
|
+
return fn
|
|
102
|
+
.apply(null)
|
|
103
|
+
.then(async res => {
|
|
104
|
+
finalize()
|
|
105
|
+
await recorder.promise()
|
|
106
|
+
return res
|
|
107
|
+
})
|
|
108
|
+
.catch(e => {
|
|
109
|
+
output.stepShift = 0
|
|
110
|
+
session.restoreVars(sessionName)
|
|
111
|
+
event.dispatcher.removeListener(event.step.after, addContextToStep)
|
|
112
|
+
recorder.throw(e)
|
|
113
|
+
return recorder.promise()
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let res
|
|
118
|
+
try {
|
|
119
|
+
res = fn.apply(null)
|
|
120
|
+
} catch (err) {
|
|
121
|
+
recorder.throw(err)
|
|
122
|
+
} finally {
|
|
123
|
+
recorder.catch(e => {
|
|
124
|
+
session.restoreVars(sessionName)
|
|
125
|
+
output.stepShift = 0
|
|
126
|
+
event.dispatcher.removeListener(event.step.after, addContextToStep)
|
|
127
|
+
throw e
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
finalize()
|
|
131
|
+
return recorder.promise().then(() => res)
|
|
132
|
+
},
|
|
133
|
+
false,
|
|
134
|
+
false,
|
|
135
|
+
)
|
|
130
136
|
}
|
|
131
137
|
|
|
132
|
-
|
|
138
|
+
export default session
|
package/lib/step/base.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import color from 'chalk'
|
|
2
|
+
import Secret from '../secret.js'
|
|
3
|
+
import { getCurrentTimeout } from '../timeout.js'
|
|
4
|
+
import { ucfirst, humanizeString, serializeError } from '../utils.js'
|
|
5
|
+
import recordStep from './record.js'
|
|
6
6
|
|
|
7
7
|
const STACK_LINE = 5
|
|
8
8
|
|
|
@@ -225,7 +225,7 @@ class Step {
|
|
|
225
225
|
processingStep = this
|
|
226
226
|
|
|
227
227
|
while (processingStep.metaStep) {
|
|
228
|
-
if (processingStep.metaStep.actor?.match(/^(Given|When|Then|And)/)) {
|
|
228
|
+
if (processingStep.metaStep.actor?.match(/^(Given|When|Then|And|But)/)) {
|
|
229
229
|
hasBDD = true
|
|
230
230
|
break
|
|
231
231
|
} else {
|
|
@@ -236,4 +236,4 @@ class Step {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
export default Step
|
package/lib/step/comment.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import FuncStep from './func.js'
|
|
2
2
|
|
|
3
3
|
class CommentStep extends FuncStep {
|
|
4
4
|
constructor(name, comment) {
|
|
@@ -7,4 +7,4 @@ class CommentStep extends FuncStep {
|
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
export default CommentStep
|
package/lib/step/config.js
CHANGED
package/lib/step/func.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import BaseStep from './base.js'
|
|
2
|
+
import store from '../store.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Function executed as a step
|
|
@@ -43,4 +43,4 @@ class FuncStep extends BaseStep {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
export default FuncStep
|
package/lib/step/helper.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import Step from './base.js'
|
|
2
|
+
import store from '../store.js'
|
|
3
3
|
|
|
4
4
|
class HelperStep extends Step {
|
|
5
5
|
constructor(helper, name) {
|
|
@@ -38,7 +38,7 @@ class HelperStep extends Step {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
export default HelperStep
|
|
42
42
|
|
|
43
43
|
function dryRunResolver() {
|
|
44
44
|
return {
|
package/lib/step/meta.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import Step from './base.js'
|
|
2
|
+
import event from '../event.js'
|
|
3
|
+
import { humanizeString } from '../utils.js'
|
|
4
4
|
|
|
5
5
|
class MetaStep extends Step {
|
|
6
6
|
constructor(actor, method) {
|
|
@@ -15,7 +15,7 @@ class MetaStep extends Step {
|
|
|
15
15
|
|
|
16
16
|
/** @return {boolean} */
|
|
17
17
|
isBDD() {
|
|
18
|
-
if (this.actor && this.actor.match && this.actor.match(/^(Given|When|Then|And)/)) {
|
|
18
|
+
if (this.actor && this.actor.match && this.actor.match(/^(Given|When|Then|And|But)/)) {
|
|
19
19
|
return true
|
|
20
20
|
}
|
|
21
21
|
return false
|
|
@@ -96,4 +96,4 @@ class MetaStep extends Step {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
export default MetaStep
|
package/lib/step/record.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import event from '../event.js'
|
|
2
|
+
import recorder from '../recorder.js'
|
|
3
|
+
import StepConfig from './config.js'
|
|
4
|
+
import output from '../output.js'
|
|
5
|
+
import store from '../store.js'
|
|
6
|
+
import { TIMEOUT_ORDER } from '../timeout.js'
|
|
7
|
+
import retryStep from './retry.js'
|
|
8
8
|
function recordStep(step, args) {
|
|
9
9
|
step.status = 'queued'
|
|
10
10
|
|
|
@@ -15,12 +15,12 @@ function recordStep(step, args) {
|
|
|
15
15
|
const { opts, timeout, retry } = stepConfig.getConfig()
|
|
16
16
|
|
|
17
17
|
if (opts) {
|
|
18
|
-
debug(`Step ${step.name}: options applied ${JSON.stringify(opts)}`)
|
|
18
|
+
output.debug(`Step ${step.name}: options applied ${JSON.stringify(opts)}`)
|
|
19
19
|
store.stepOptions = opts
|
|
20
20
|
step.opts = opts
|
|
21
21
|
}
|
|
22
22
|
if (timeout) {
|
|
23
|
-
debug(`Step ${step.name} timeout ${timeout}s`)
|
|
23
|
+
output.debug(`Step ${step.name} timeout ${timeout}s`)
|
|
24
24
|
step.setTimeout(timeout * 1000, TIMEOUT_ORDER.codeLimitTime)
|
|
25
25
|
}
|
|
26
26
|
if (retry) retryStep(retry)
|
|
@@ -57,7 +57,7 @@ function recordStep(step, args) {
|
|
|
57
57
|
event.emit(event.step.finished, step)
|
|
58
58
|
})
|
|
59
59
|
|
|
60
|
-
recorder.
|
|
60
|
+
recorder.catch(err => {
|
|
61
61
|
step.status = 'failed'
|
|
62
62
|
step.endTime = +Date.now()
|
|
63
63
|
event.emit(event.step.failed, step, err)
|
|
@@ -71,4 +71,4 @@ function recordStep(step, args) {
|
|
|
71
71
|
return recorder.promise()
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
export default recordStep
|
package/lib/step/retry.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import recorder from '../recorder.js'
|
|
2
|
+
import event from '../event.js'
|
|
3
3
|
|
|
4
4
|
function retryStep(opts) {
|
|
5
5
|
if (opts === undefined) opts = 1
|
|
@@ -8,4 +8,4 @@ function retryStep(opts) {
|
|
|
8
8
|
recorder.add(() => event.dispatcher.once(event.step.finished, () => recorder.retries.pop()))
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
export default retryStep
|
package/lib/step/section.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import MetaStep from './meta.js'
|
|
2
|
+
import event from '../event.js'
|
|
3
3
|
|
|
4
4
|
let currentSection
|
|
5
5
|
|
|
@@ -52,4 +52,4 @@ function getRootMetaStep(step) {
|
|
|
52
52
|
return step
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
export default Section
|
package/lib/step.js
CHANGED
|
@@ -3,24 +3,21 @@
|
|
|
3
3
|
* Step is wrapper around a helper method.
|
|
4
4
|
* It is used to create a new step that is a combination of other steps.
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import BaseStep from './step/base.js'
|
|
7
|
+
import StepConfig from './step/config.js'
|
|
8
|
+
import Step from './step/helper.js'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* MetaStep is a step that is used to wrap other steps.
|
|
12
12
|
* It is used to create a new step that is a combination of other steps.
|
|
13
13
|
* It is used to create a new step that is a combination of other steps.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
import MetaStep from './step/meta.js'
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Step used to execute a single function
|
|
19
19
|
*/
|
|
20
|
-
|
|
20
|
+
import FuncStep from './step/func.js'
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
module.exports.BaseStep = BaseStep
|
|
25
|
-
module.exports.StepConfig = StepConfig
|
|
26
|
-
module.exports.FuncStep = FuncStep
|
|
22
|
+
export default Step
|
|
23
|
+
export { MetaStep, BaseStep, StepConfig, FuncStep }
|
package/lib/steps.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import StepConfig from './step/config.js'
|
|
2
|
+
import SectionClass from './step/section.js'
|
|
3
3
|
function stepOpts(opts = {}) {
|
|
4
4
|
return new StepConfig(opts)
|
|
5
5
|
}
|
|
@@ -14,11 +14,11 @@ function stepRetry(retry) {
|
|
|
14
14
|
|
|
15
15
|
function section(name) {
|
|
16
16
|
if (!name) return endSection()
|
|
17
|
-
return new
|
|
17
|
+
return new SectionClass(name).start()
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function endSection() {
|
|
21
|
-
return
|
|
21
|
+
return SectionClass.current().end()
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// Section function to be added here
|
|
@@ -47,4 +47,8 @@ const step = {
|
|
|
47
47
|
Then: () => section('Then'),
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
export default step
|
|
51
|
+
|
|
52
|
+
// Named exports for ESM compatibility
|
|
53
|
+
export const Section = step.Section
|
|
54
|
+
export const EndSection = step.EndSection
|
package/lib/store.js
CHANGED
package/lib/template/heal.js
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export default (html, extraPrompt = '', rootLocator = null) => [
|
|
2
|
+
{
|
|
3
|
+
role: 'user',
|
|
4
|
+
content: `As a test automation engineer I am creating a Page Object for a web application using CodeceptJS.
|
|
5
|
+
Here is an sample page object:
|
|
6
|
+
|
|
7
|
+
const { I } = inject();
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
|
|
11
|
+
// setting locators
|
|
12
|
+
element1: '#selector',
|
|
13
|
+
element2: '.selector',
|
|
14
|
+
element3: locate().withText('text'),
|
|
15
|
+
|
|
16
|
+
// setting methods
|
|
17
|
+
doSomethingOnPage(params) {
|
|
18
|
+
// ...
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
I want to generate a Page Object for the page I provide.
|
|
23
|
+
Write JavaScript code in similar manner to list all locators on the page.
|
|
24
|
+
Use locators in order of preference: by text (use locate().withText()), label, CSS, XPath.
|
|
25
|
+
Avoid TailwindCSS, Bootstrap or React style formatting classes in locators.
|
|
26
|
+
Add methods to interact with page when needed.
|
|
27
|
+
${extraPrompt}
|
|
28
|
+
${rootLocator ? `All provided elements are inside '${rootLocator}'. Declare it as root variable and for every locator use locate(...).inside(root)` : ''}
|
|
29
|
+
Add only locators from this HTML: \n\n${html}`,
|
|
30
|
+
},
|
|
31
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default (html, { step, error, prevSteps }) => {
|
|
2
|
+
return [
|
|
3
|
+
{
|
|
4
|
+
role: 'user',
|
|
5
|
+
content: `As a test automation engineer I am testing web application using CodeceptJS.
|
|
6
|
+
I want to heal a test that fails. Here is the list of executed steps: ${prevSteps.map(s => s.toString()).join(', ')}
|
|
7
|
+
Propose how to adjust ${step.toCode()} step to fix the test.
|
|
8
|
+
Use locators in order of preference: semantic locator by text, CSS, XPath. Use codeblocks marked with \`\`\`
|
|
9
|
+
Here is the error message: ${error.message}
|
|
10
|
+
Here is HTML code of a page where the failure has happened: \n\n${html}`,
|
|
11
|
+
},
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export default (html, input) => [
|
|
2
|
+
{
|
|
3
|
+
role: 'user',
|
|
4
|
+
content: `I am test engineer writing test in CodeceptJS
|
|
5
|
+
I have opened web page and I want to use CodeceptJS to ${input} on this page
|
|
6
|
+
Provide me valid CodeceptJS code to accomplish it
|
|
7
|
+
Use only locators from this HTML: \n\n${html}`,
|
|
8
|
+
},
|
|
9
|
+
]
|
package/lib/test-server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import path from 'path'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Internal API test server to replace json-server dependency
|
|
@@ -13,6 +13,7 @@ class TestServer {
|
|
|
13
13
|
this.port = config.port || 8010
|
|
14
14
|
this.host = config.host || 'localhost'
|
|
15
15
|
this.dbFile = config.dbFile || path.join(__dirname, '../test/data/rest/db.json')
|
|
16
|
+
this.readOnly = config.readOnly || false
|
|
16
17
|
this.lastModified = null
|
|
17
18
|
this.data = this.loadData()
|
|
18
19
|
|
|
@@ -49,6 +50,10 @@ class TestServer {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
saveData() {
|
|
53
|
+
if (this.readOnly) {
|
|
54
|
+
console.log('[Save] Skipping save - running in read-only mode')
|
|
55
|
+
return
|
|
56
|
+
}
|
|
52
57
|
try {
|
|
53
58
|
fs.writeFileSync(this.dbFile, JSON.stringify(this.data, null, 2))
|
|
54
59
|
console.log('[Save] Data saved to file')
|
|
@@ -297,10 +302,16 @@ class TestServer {
|
|
|
297
302
|
}
|
|
298
303
|
}
|
|
299
304
|
|
|
300
|
-
|
|
305
|
+
export default TestServer
|
|
306
|
+
|
|
307
|
+
// CLI usage - Import meta for ESM
|
|
308
|
+
import { fileURLToPath } from 'url'
|
|
309
|
+
import { dirname } from 'path'
|
|
310
|
+
|
|
311
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
312
|
+
const __dirname = dirname(__filename)
|
|
301
313
|
|
|
302
|
-
|
|
303
|
-
if (require.main === module) {
|
|
314
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
304
315
|
const config = {
|
|
305
316
|
port: process.env.PORT || 8010,
|
|
306
317
|
host: process.env.HOST || '0.0.0.0',
|