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
|
@@ -1,291 +1,181 @@
|
|
|
1
|
-
|
|
1
|
+
import tty from 'tty'
|
|
2
2
|
|
|
3
3
|
if (!tty.getWindowSize) {
|
|
4
4
|
// this is really old method, long removed from Node, but Mocha
|
|
5
5
|
// reporters fall back on it if they cannot use `process.stdout.getWindowSize`
|
|
6
6
|
// we need to polyfill it.
|
|
7
|
-
tty.getWindowSize = () => [40, 80]
|
|
7
|
+
tty.getWindowSize = () => [40, 80]
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
import { parentPort, workerData } from 'worker_threads'
|
|
11
|
+
import event from '../../event.js'
|
|
12
|
+
import container from '../../container.js'
|
|
13
|
+
import { getConfig } from '../utils.js'
|
|
14
|
+
import { tryOrDefault, deepMerge } from '../../utils.js'
|
|
15
15
|
|
|
16
|
-
let stdout = ''
|
|
16
|
+
let stdout = ''
|
|
17
17
|
|
|
18
|
-
const stderr = ''
|
|
18
|
+
const stderr = ''
|
|
19
19
|
|
|
20
|
-
//
|
|
21
|
-
|
|
20
|
+
// Importing of Codecept need to be after tty.getWindowSize is available.
|
|
21
|
+
import Codecept from '../../codecept.js'
|
|
22
22
|
|
|
23
|
-
const {
|
|
24
|
-
options, tests, testRoot, workerIndex,
|
|
25
|
-
} = workerData;
|
|
23
|
+
const { options, tests, testRoot, workerIndex } = workerData
|
|
26
24
|
|
|
27
25
|
// hide worker output
|
|
28
|
-
if (!options.debug && !options.verbose)
|
|
26
|
+
if (!options.debug && !options.verbose)
|
|
27
|
+
process.stdout.write = string => {
|
|
28
|
+
stdout += string
|
|
29
|
+
return true
|
|
30
|
+
}
|
|
29
31
|
|
|
30
|
-
const overrideConfigs = tryOrDefault(() => JSON.parse(options.override), {})
|
|
32
|
+
const overrideConfigs = tryOrDefault(() => JSON.parse(options.override), {})
|
|
31
33
|
|
|
32
34
|
// important deep merge so dynamic things e.g. functions on config are not overridden
|
|
33
|
-
const config = deepMerge(getConfig(options.config || testRoot), overrideConfigs)
|
|
35
|
+
const config = deepMerge(getConfig(options.config || testRoot), overrideConfigs)
|
|
34
36
|
|
|
35
37
|
// Load test and run
|
|
36
|
-
|
|
37
|
-
codecept
|
|
38
|
-
codecept.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
await codecept.bootstrap();
|
|
51
|
-
} catch (err) {
|
|
52
|
-
throw new Error(`Error while running bootstrap file :${err}`);
|
|
53
|
-
}
|
|
54
|
-
listenToParentThread();
|
|
55
|
-
initializeListeners();
|
|
56
|
-
disablePause();
|
|
57
|
-
try {
|
|
58
|
-
await codecept.run();
|
|
59
|
-
} finally {
|
|
60
|
-
await codecept.teardown();
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function filterTests() {
|
|
65
|
-
const files = codecept.testFiles;
|
|
66
|
-
mocha.files = files;
|
|
67
|
-
mocha.loadFiles();
|
|
68
|
-
|
|
69
|
-
for (const suite of mocha.suite.suites) {
|
|
70
|
-
suite.tests = suite.tests.filter(test => tests.indexOf(test.uid) >= 0);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function initializeListeners() {
|
|
75
|
-
function simplifyError(error) {
|
|
76
|
-
if (error) {
|
|
77
|
-
const {
|
|
78
|
-
stack,
|
|
79
|
-
uncaught,
|
|
80
|
-
message,
|
|
81
|
-
actual,
|
|
82
|
-
expected,
|
|
83
|
-
} = error;
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
stack,
|
|
87
|
-
uncaught,
|
|
88
|
-
message,
|
|
89
|
-
actual,
|
|
90
|
-
expected,
|
|
91
|
-
};
|
|
38
|
+
;(async function () {
|
|
39
|
+
const codecept = new Codecept(config, options)
|
|
40
|
+
await codecept.init(testRoot)
|
|
41
|
+
codecept.loadTests()
|
|
42
|
+
const mocha = container.mocha()
|
|
43
|
+
|
|
44
|
+
function filterTests() {
|
|
45
|
+
const files = codecept.testFiles
|
|
46
|
+
mocha.files = files
|
|
47
|
+
mocha.loadFiles()
|
|
48
|
+
|
|
49
|
+
for (const suite of mocha.suite.suites) {
|
|
50
|
+
suite.tests = suite.tests.filter(test => tests.indexOf(test.uid) >= 0)
|
|
92
51
|
}
|
|
93
|
-
|
|
94
|
-
return null;
|
|
95
52
|
}
|
|
96
|
-
function simplifyTest(test, err = null) {
|
|
97
|
-
test = { ...test };
|
|
98
53
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (test.err) {
|
|
105
|
-
err = simplifyError(test.err);
|
|
106
|
-
test.status = 'failed';
|
|
107
|
-
} else if (err) {
|
|
108
|
-
err = simplifyError(err);
|
|
109
|
-
test.status = 'failed';
|
|
110
|
-
}
|
|
111
|
-
const parent = {};
|
|
112
|
-
if (test.parent) {
|
|
113
|
-
parent.title = test.parent.title;
|
|
54
|
+
async function runTests() {
|
|
55
|
+
try {
|
|
56
|
+
await codecept.bootstrap()
|
|
57
|
+
} catch (err) {
|
|
58
|
+
throw new Error(`Error while running bootstrap file :${err}`)
|
|
114
59
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return {
|
|
124
|
-
opts: test.opts || {},
|
|
125
|
-
tags: test.tags || [],
|
|
126
|
-
uid: test.uid,
|
|
127
|
-
workerIndex,
|
|
128
|
-
retries: test._retries,
|
|
129
|
-
title: test.title,
|
|
130
|
-
status: test.status,
|
|
131
|
-
duration: test.duration || 0,
|
|
132
|
-
err,
|
|
133
|
-
parent,
|
|
134
|
-
steps: test.steps && test.steps.length > 0 ? simplifyStepsInTestObject(test.steps, err) : [],
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function simplifyStepsInTestObject(steps, err) {
|
|
139
|
-
steps = [...steps];
|
|
140
|
-
const _steps = [];
|
|
141
|
-
|
|
142
|
-
for (step of steps) {
|
|
143
|
-
const _args = [];
|
|
144
|
-
|
|
145
|
-
if (step.args) {
|
|
146
|
-
for (const arg of step.args) {
|
|
147
|
-
// check if arg is a JOI object
|
|
148
|
-
if (arg && arg.$_root) {
|
|
149
|
-
_args.push(JSON.stringify(arg).slice(0, 300));
|
|
150
|
-
// check if arg is a function
|
|
151
|
-
} else if (arg && typeof arg === 'function') {
|
|
152
|
-
_args.push(arg.name);
|
|
153
|
-
} else {
|
|
154
|
-
_args.push(arg);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
_steps.push({
|
|
160
|
-
actor: step.actor,
|
|
161
|
-
name: step.name,
|
|
162
|
-
status: step.status,
|
|
163
|
-
args: JSON.stringify(_args),
|
|
164
|
-
startedAt: step.startedAt,
|
|
165
|
-
startTime: step.startTime,
|
|
166
|
-
endTime: step.endTime,
|
|
167
|
-
finishedAt: step.finishedAt,
|
|
168
|
-
duration: step.duration,
|
|
169
|
-
err,
|
|
170
|
-
});
|
|
60
|
+
listenToParentThread()
|
|
61
|
+
initializeListeners()
|
|
62
|
+
disablePause()
|
|
63
|
+
try {
|
|
64
|
+
await codecept.run()
|
|
65
|
+
} finally {
|
|
66
|
+
await codecept.teardown()
|
|
171
67
|
}
|
|
172
|
-
|
|
173
|
-
return _steps;
|
|
174
68
|
}
|
|
175
69
|
|
|
176
|
-
|
|
177
|
-
step = { ...step };
|
|
178
|
-
|
|
179
|
-
if (step.startTime && !step.duration) {
|
|
180
|
-
const end = new Date();
|
|
181
|
-
step.duration = end - step.startTime;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (step.err) {
|
|
185
|
-
err = simplifyError(step.err);
|
|
186
|
-
step.status = 'failed';
|
|
187
|
-
} else if (err) {
|
|
188
|
-
err = simplifyError(err);
|
|
189
|
-
step.status = 'failed';
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const parent = {};
|
|
193
|
-
if (step.metaStep) {
|
|
194
|
-
parent.title = step.metaStep.actor;
|
|
195
|
-
}
|
|
70
|
+
filterTests()
|
|
196
71
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (typeof step.opts[k] === 'function') delete step.opts[k];
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return {
|
|
205
|
-
opts: step.opts || {},
|
|
206
|
-
workerIndex,
|
|
207
|
-
title: step.name,
|
|
208
|
-
status: step.status,
|
|
209
|
-
duration: step.duration || 0,
|
|
210
|
-
err,
|
|
211
|
-
parent,
|
|
212
|
-
test: simplifyTest(step.test),
|
|
213
|
-
};
|
|
72
|
+
// run tests
|
|
73
|
+
if (mocha.suite.total()) {
|
|
74
|
+
await runTests()
|
|
214
75
|
}
|
|
76
|
+
})()
|
|
215
77
|
|
|
216
|
-
|
|
78
|
+
function initializeListeners() {
|
|
217
79
|
// suite
|
|
218
|
-
event.dispatcher.on(event.suite.before, suite =>
|
|
219
|
-
event.dispatcher.on(event.suite.after, suite =>
|
|
80
|
+
event.dispatcher.on(event.suite.before, suite => safelySendToParent({ event: event.suite.before, workerIndex, data: suite.simplify() }))
|
|
81
|
+
event.dispatcher.on(event.suite.after, suite => safelySendToParent({ event: event.suite.after, workerIndex, data: suite.simplify() }))
|
|
220
82
|
|
|
221
83
|
// calculate duration
|
|
222
|
-
event.dispatcher.on(event.test.started, test => test.start = new Date())
|
|
84
|
+
event.dispatcher.on(event.test.started, test => (test.start = new Date()))
|
|
223
85
|
|
|
224
86
|
// tests
|
|
225
|
-
event.dispatcher.on(event.test.before, test =>
|
|
226
|
-
event.dispatcher.on(event.test.after, test =>
|
|
87
|
+
event.dispatcher.on(event.test.before, test => safelySendToParent({ event: event.test.before, workerIndex, data: test.simplify() }))
|
|
88
|
+
event.dispatcher.on(event.test.after, test => safelySendToParent({ event: event.test.after, workerIndex, data: test.simplify() }))
|
|
227
89
|
// we should force-send correct errors to prevent race condition
|
|
228
|
-
event.dispatcher.on(event.test.finished, (test, err) =>
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
90
|
+
event.dispatcher.on(event.test.finished, (test, err) => {
|
|
91
|
+
const simplifiedData = test.simplify()
|
|
92
|
+
const serializableErr = serializeError(err)
|
|
93
|
+
safelySendToParent({ event: event.test.finished, workerIndex, data: { ...simplifiedData, err: serializableErr } })
|
|
94
|
+
})
|
|
95
|
+
event.dispatcher.on(event.test.failed, (test, err) => {
|
|
96
|
+
const simplifiedData = test.simplify()
|
|
97
|
+
const serializableErr = serializeError(err)
|
|
98
|
+
safelySendToParent({ event: event.test.failed, workerIndex, data: { ...simplifiedData, err: serializableErr } })
|
|
99
|
+
})
|
|
100
|
+
event.dispatcher.on(event.test.passed, (test, err) => safelySendToParent({ event: event.test.passed, workerIndex, data: { ...test.simplify(), err } }))
|
|
101
|
+
event.dispatcher.on(event.test.started, test => safelySendToParent({ event: event.test.started, workerIndex, data: test.simplify() }))
|
|
102
|
+
event.dispatcher.on(event.test.skipped, test => safelySendToParent({ event: event.test.skipped, workerIndex, data: test.simplify() }))
|
|
233
103
|
|
|
234
104
|
// steps
|
|
235
|
-
event.dispatcher.on(event.step.finished, step =>
|
|
236
|
-
event.dispatcher.on(event.step.started, step =>
|
|
237
|
-
event.dispatcher.on(event.step.passed, step =>
|
|
238
|
-
event.dispatcher.on(event.step.failed, step =>
|
|
239
|
-
|
|
240
|
-
event.dispatcher.on(event.hook.failed, (
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
105
|
+
event.dispatcher.on(event.step.finished, step => safelySendToParent({ event: event.step.finished, workerIndex, data: step.simplify() }))
|
|
106
|
+
event.dispatcher.on(event.step.started, step => safelySendToParent({ event: event.step.started, workerIndex, data: step.simplify() }))
|
|
107
|
+
event.dispatcher.on(event.step.passed, step => safelySendToParent({ event: event.step.passed, workerIndex, data: step.simplify() }))
|
|
108
|
+
event.dispatcher.on(event.step.failed, step => safelySendToParent({ event: event.step.failed, workerIndex, data: step.simplify() }))
|
|
109
|
+
|
|
110
|
+
event.dispatcher.on(event.hook.failed, (hook, err) => {
|
|
111
|
+
const serializableErr = serializeError(err)
|
|
112
|
+
safelySendToParent({ event: event.hook.failed, workerIndex, data: { ...hook.simplify(), err: serializableErr } })
|
|
113
|
+
})
|
|
114
|
+
event.dispatcher.on(event.hook.passed, hook => safelySendToParent({ event: event.hook.passed, workerIndex, data: hook.simplify() }))
|
|
115
|
+
event.dispatcher.on(event.hook.finished, hook => safelySendToParent({ event: event.hook.finished, workerIndex, data: hook.simplify() }))
|
|
116
|
+
|
|
117
|
+
event.dispatcher.once(event.all.after, () => safelySendToParent({ event: event.all.after, workerIndex, data: container.result().simplify() }))
|
|
244
118
|
// all
|
|
245
|
-
event.dispatcher.once(event.all.result, () =>
|
|
119
|
+
event.dispatcher.once(event.all.result, () => {
|
|
120
|
+
safelySendToParent({ event: event.all.result, workerIndex, data: container.result().simplify() })
|
|
121
|
+
parentPort?.close()
|
|
122
|
+
})
|
|
246
123
|
}
|
|
247
124
|
|
|
248
125
|
function disablePause() {
|
|
249
|
-
global.pause = () => {}
|
|
126
|
+
global.pause = () => {}
|
|
250
127
|
}
|
|
251
128
|
|
|
252
|
-
function
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
stats.skipped++;
|
|
262
|
-
});
|
|
263
|
-
event.dispatcher.on(event.test.passed, () => {
|
|
264
|
-
stats.passes++;
|
|
265
|
-
});
|
|
266
|
-
event.dispatcher.on(event.test.failed, (test) => {
|
|
267
|
-
if (test.ctx._runnable.title.includes('hook: AfterSuite')) {
|
|
268
|
-
stats.failedHooks += 1;
|
|
129
|
+
function serializeError(err) {
|
|
130
|
+
if (!err) return null
|
|
131
|
+
try {
|
|
132
|
+
return {
|
|
133
|
+
message: err.message,
|
|
134
|
+
stack: err.stack,
|
|
135
|
+
name: err.name,
|
|
136
|
+
actual: err.actual,
|
|
137
|
+
expected: err.expected,
|
|
269
138
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
139
|
+
} catch {
|
|
140
|
+
return { message: 'Error could not be serialized', name: 'Error' }
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function safelySendToParent(data) {
|
|
145
|
+
try {
|
|
146
|
+
parentPort?.postMessage(data)
|
|
147
|
+
} catch (cloneError) {
|
|
148
|
+
// Fallback for non-serializable data
|
|
149
|
+
const fallbackData = { ...data }
|
|
150
|
+
|
|
151
|
+
// Try to serialize error objects if present
|
|
152
|
+
if (fallbackData.data && fallbackData.data.err) {
|
|
153
|
+
fallbackData.data.err = serializeError(fallbackData.data.err)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// If still fails, send minimal data
|
|
157
|
+
try {
|
|
158
|
+
parentPort?.postMessage(fallbackData)
|
|
159
|
+
} catch (finalError) {
|
|
160
|
+
parentPort?.postMessage({
|
|
161
|
+
event: data.event,
|
|
162
|
+
workerIndex,
|
|
163
|
+
data: {
|
|
164
|
+
title: fallbackData.data?.title || 'Unknown',
|
|
165
|
+
state: fallbackData.data?.state || 'error',
|
|
166
|
+
err: { message: 'Data could not be serialized' },
|
|
167
|
+
},
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
}
|
|
281
171
|
}
|
|
282
172
|
|
|
283
173
|
function sendToParentThread(data) {
|
|
284
|
-
parentPort
|
|
174
|
+
parentPort?.postMessage(data)
|
|
285
175
|
}
|
|
286
176
|
|
|
287
177
|
function listenToParentThread() {
|
|
288
|
-
parentPort
|
|
289
|
-
container.append({ support: eventData.data })
|
|
290
|
-
})
|
|
178
|
+
parentPort?.on('message', eventData => {
|
|
179
|
+
container.append({ support: eventData.data })
|
|
180
|
+
})
|
|
291
181
|
}
|
package/lib/config.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
isFile,
|
|
6
|
-
deepMerge,
|
|
7
|
-
deepClone,
|
|
8
|
-
} = require('./utils');
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { createRequire } from 'module'
|
|
4
|
+
import { fileExists, isFile, deepMerge, deepClone } from './utils.js'
|
|
9
5
|
|
|
10
6
|
const defaultConfig = {
|
|
11
7
|
output: './_output',
|
|
@@ -33,18 +29,12 @@ const defaultConfig = {
|
|
|
33
29
|
timeout: 0,
|
|
34
30
|
},
|
|
35
31
|
],
|
|
36
|
-
}
|
|
32
|
+
}
|
|
37
33
|
|
|
38
|
-
let hooks = []
|
|
39
|
-
let config = {}
|
|
34
|
+
let hooks = []
|
|
35
|
+
let config = {}
|
|
40
36
|
|
|
41
|
-
const configFileNames = [
|
|
42
|
-
'codecept.config.js',
|
|
43
|
-
'codecept.conf.js',
|
|
44
|
-
'codecept.js',
|
|
45
|
-
'codecept.config.ts',
|
|
46
|
-
'codecept.conf.ts',
|
|
47
|
-
];
|
|
37
|
+
const configFileNames = ['codecept.config.js', 'codecept.conf.js', 'codecept.js', 'codecept.config.ts', 'codecept.conf.ts']
|
|
48
38
|
|
|
49
39
|
/**
|
|
50
40
|
* Current configuration
|
|
@@ -57,9 +47,9 @@ class Config {
|
|
|
57
47
|
* @return {Object<string, *>}
|
|
58
48
|
*/
|
|
59
49
|
static create(newConfig) {
|
|
60
|
-
config = deepMerge(deepClone(defaultConfig), newConfig)
|
|
61
|
-
hooks.forEach(f => f(config))
|
|
62
|
-
return config
|
|
50
|
+
config = deepMerge(deepClone(defaultConfig), newConfig)
|
|
51
|
+
hooks.forEach(f => f(config))
|
|
52
|
+
return config
|
|
63
53
|
}
|
|
64
54
|
|
|
65
55
|
/**
|
|
@@ -75,34 +65,34 @@ class Config {
|
|
|
75
65
|
* @param {string} configFile
|
|
76
66
|
* @return {*}
|
|
77
67
|
*/
|
|
78
|
-
static load(configFile) {
|
|
79
|
-
configFile = path.resolve(configFile || '.')
|
|
68
|
+
static async load(configFile) {
|
|
69
|
+
configFile = path.resolve(configFile || '.')
|
|
80
70
|
|
|
81
71
|
if (!fileExists(configFile)) {
|
|
82
|
-
configFile = configFile.replace('.js', '.ts')
|
|
72
|
+
configFile = configFile.replace('.js', '.ts')
|
|
83
73
|
|
|
84
74
|
if (!fileExists(configFile)) {
|
|
85
|
-
throw new Error(`Config file ${configFile} does not exist. Execute 'codeceptjs init' to create config`)
|
|
75
|
+
throw new Error(`Config file ${configFile} does not exist. Execute 'codeceptjs init' to create config`)
|
|
86
76
|
}
|
|
87
77
|
}
|
|
88
78
|
|
|
89
79
|
// is config file
|
|
90
80
|
if (isFile(configFile)) {
|
|
91
|
-
return loadConfigFile(configFile)
|
|
81
|
+
return await loadConfigFile(configFile)
|
|
92
82
|
}
|
|
93
83
|
|
|
94
84
|
for (const name of configFileNames) {
|
|
95
85
|
// is path to directory
|
|
96
|
-
const jsConfig = path.join(configFile, name)
|
|
86
|
+
const jsConfig = path.join(configFile, name)
|
|
97
87
|
|
|
98
88
|
if (isFile(jsConfig)) {
|
|
99
|
-
return loadConfigFile(jsConfig)
|
|
89
|
+
return await loadConfigFile(jsConfig)
|
|
100
90
|
}
|
|
101
91
|
}
|
|
102
92
|
|
|
103
|
-
const configPaths = configFileNames.map(name => path.join(configFile, name)).join(' or ')
|
|
93
|
+
const configPaths = configFileNames.map(name => path.join(configFile, name)).join(' or ')
|
|
104
94
|
|
|
105
|
-
throw new Error(`Can not load config from ${configPaths}\nCodeceptJS is not initialized in this dir. Execute 'codeceptjs init' to start`)
|
|
95
|
+
throw new Error(`Can not load config from ${configPaths}\nCodeceptJS is not initialized in this dir. Execute 'codeceptjs init' to start`)
|
|
106
96
|
}
|
|
107
97
|
|
|
108
98
|
/**
|
|
@@ -113,13 +103,13 @@ class Config {
|
|
|
113
103
|
*/
|
|
114
104
|
static get(key, val) {
|
|
115
105
|
if (key) {
|
|
116
|
-
return config[key] || val
|
|
106
|
+
return config[key] || val
|
|
117
107
|
}
|
|
118
|
-
return config
|
|
108
|
+
return config
|
|
119
109
|
}
|
|
120
110
|
|
|
121
111
|
static addHook(fn) {
|
|
122
|
-
hooks.push(fn)
|
|
112
|
+
hooks.push(fn)
|
|
123
113
|
}
|
|
124
114
|
|
|
125
115
|
/**
|
|
@@ -129,7 +119,7 @@ class Config {
|
|
|
129
119
|
* @return {Object<string, *>}
|
|
130
120
|
*/
|
|
131
121
|
static append(additionalConfig) {
|
|
132
|
-
return config = deepMerge(config, additionalConfig)
|
|
122
|
+
return (config = deepMerge(config, additionalConfig))
|
|
133
123
|
}
|
|
134
124
|
|
|
135
125
|
/**
|
|
@@ -137,33 +127,105 @@ class Config {
|
|
|
137
127
|
* @return {Object<string, *>}
|
|
138
128
|
*/
|
|
139
129
|
static reset() {
|
|
140
|
-
hooks = []
|
|
141
|
-
return config = { ...defaultConfig }
|
|
130
|
+
hooks = []
|
|
131
|
+
return (config = { ...defaultConfig })
|
|
142
132
|
}
|
|
143
133
|
}
|
|
144
134
|
|
|
145
|
-
|
|
135
|
+
export default Config
|
|
146
136
|
|
|
147
|
-
function loadConfigFile(configFile) {
|
|
148
|
-
const
|
|
137
|
+
async function loadConfigFile(configFile) {
|
|
138
|
+
const require = createRequire(import.meta.url)
|
|
139
|
+
const extensionName = path.extname(configFile)
|
|
149
140
|
|
|
150
|
-
|
|
141
|
+
// .conf.js config file
|
|
142
|
+
if (extensionName === '.js' || extensionName === '.ts' || extensionName === '.cjs') {
|
|
143
|
+
let configModule
|
|
151
144
|
try {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
145
|
+
// For .ts files, try to compile and load as JavaScript
|
|
146
|
+
if (extensionName === '.ts') {
|
|
147
|
+
try {
|
|
148
|
+
// Try to load ts-node and compile the file
|
|
149
|
+
const { transpile } = require('typescript')
|
|
150
|
+
const tsContent = fs.readFileSync(configFile, 'utf8')
|
|
151
|
+
|
|
152
|
+
// Transpile TypeScript to JavaScript with ES module output
|
|
153
|
+
const jsContent = transpile(tsContent, {
|
|
154
|
+
module: 99, // ModuleKind.ESNext
|
|
155
|
+
target: 99, // ScriptTarget.ESNext
|
|
156
|
+
esModuleInterop: true,
|
|
157
|
+
allowSyntheticDefaultImports: true,
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// Create a temporary JS file with .mjs extension to force ES module treatment
|
|
161
|
+
const tempJsFile = configFile.replace('.ts', '.temp.mjs')
|
|
162
|
+
fs.writeFileSync(tempJsFile, jsContent)
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
configModule = await import(tempJsFile)
|
|
166
|
+
// Clean up temp file
|
|
167
|
+
fs.unlinkSync(tempJsFile)
|
|
168
|
+
} catch (err) {
|
|
169
|
+
// Clean up temp file even on error
|
|
170
|
+
if (fs.existsSync(tempJsFile)) {
|
|
171
|
+
fs.unlinkSync(tempJsFile)
|
|
172
|
+
}
|
|
173
|
+
throw err
|
|
174
|
+
}
|
|
175
|
+
} catch (tsError) {
|
|
176
|
+
// If TypeScript compilation fails, fallback to ts-node
|
|
177
|
+
try {
|
|
178
|
+
require('ts-node/register')
|
|
179
|
+
configModule = require(configFile)
|
|
180
|
+
} catch (tsNodeError) {
|
|
181
|
+
throw new Error(`Failed to load TypeScript config: ${tsError.message}`)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
// Try ESM import first for JS files
|
|
186
|
+
configModule = await import(configFile)
|
|
187
|
+
}
|
|
188
|
+
} catch (importError) {
|
|
189
|
+
try {
|
|
190
|
+
// Fall back to CommonJS require for .js/.cjs files
|
|
191
|
+
if (extensionName !== '.ts') {
|
|
192
|
+
configModule = require(configFile)
|
|
193
|
+
} else {
|
|
194
|
+
throw importError
|
|
195
|
+
}
|
|
196
|
+
} catch (requireError) {
|
|
197
|
+
throw new Error(`Failed to load config file ${configFile}: ${importError.message}`)
|
|
198
|
+
}
|
|
155
199
|
}
|
|
156
|
-
}
|
|
157
200
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
201
|
+
const rawConfig = configModule.config || configModule.default?.config || configModule
|
|
202
|
+
|
|
203
|
+
// Process helpers to extract imported classes
|
|
204
|
+
if (rawConfig.helpers) {
|
|
205
|
+
const processedHelpers = {}
|
|
206
|
+
for (const [helperName, helperConfig] of Object.entries(rawConfig.helpers)) {
|
|
207
|
+
// Check if the helper name itself is a class (ESM import)
|
|
208
|
+
if (typeof helperName === 'function' && helperName.prototype) {
|
|
209
|
+
// This is an imported class, use its constructor name
|
|
210
|
+
const className = helperName.name
|
|
211
|
+
processedHelpers[className] = {
|
|
212
|
+
...helperConfig,
|
|
213
|
+
_helperClass: helperName,
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
processedHelpers[helperName] = helperConfig
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
rawConfig.helpers = processedHelpers
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return Config.create(rawConfig)
|
|
161
223
|
}
|
|
162
224
|
|
|
163
225
|
// json config provided
|
|
164
226
|
if (extensionName === '.json') {
|
|
165
|
-
return Config.create(JSON.parse(fs.readFileSync(configFile, 'utf8')))
|
|
227
|
+
return Config.create(JSON.parse(fs.readFileSync(configFile, 'utf8')))
|
|
166
228
|
}
|
|
167
229
|
|
|
168
|
-
throw new Error(`Config file ${configFile} can't be loaded`)
|
|
230
|
+
throw new Error(`Config file ${configFile} can't be loaded`)
|
|
169
231
|
}
|