codeceptjs 3.6.7 → 4.0.0-beta.2
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/bin/codecept.js +81 -84
- package/lib/actor.js +13 -13
- package/lib/ai.js +13 -10
- package/lib/assert/empty.js +21 -20
- package/lib/assert/equal.js +39 -37
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +47 -46
- package/lib/assert/throws.js +11 -13
- package/lib/assert/truth.js +22 -19
- package/lib/assert.js +2 -4
- package/lib/cli.js +49 -57
- package/lib/codecept.js +155 -142
- package/lib/colorUtils.js +3 -3
- package/lib/command/configMigrate.js +52 -58
- package/lib/command/definitions.js +89 -88
- package/lib/command/dryRun.js +68 -71
- package/lib/command/generate.js +188 -197
- package/lib/command/gherkin/init.js +16 -27
- package/lib/command/gherkin/snippets.js +20 -20
- package/lib/command/gherkin/steps.js +8 -8
- package/lib/command/info.js +38 -40
- package/lib/command/init.js +288 -290
- package/lib/command/interactive.js +32 -32
- package/lib/command/list.js +26 -26
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +3 -3
- package/lib/command/run-multiple/run.js +2 -6
- package/lib/command/run-multiple.js +93 -113
- package/lib/command/run-rerun.js +25 -20
- package/lib/command/run-workers.js +66 -64
- package/lib/command/run.js +29 -26
- package/lib/command/utils.js +65 -80
- package/lib/command/workers/runTests.js +10 -10
- package/lib/config.js +9 -10
- package/lib/container.js +48 -40
- package/lib/data/context.js +59 -60
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +29 -29
- package/lib/data/table.js +20 -26
- package/lib/dirname.js +5 -0
- package/lib/event.js +167 -163
- package/lib/heal.js +17 -13
- package/lib/helper/AI.js +41 -130
- package/lib/helper/ApiDataFactory.js +69 -73
- package/lib/helper/Appium.js +381 -412
- package/lib/helper/Expect.js +425 -0
- package/lib/helper/ExpectHelper.js +48 -40
- package/lib/helper/FileSystem.js +79 -80
- package/lib/helper/GraphQL.js +43 -44
- package/lib/helper/GraphQLDataFactory.js +50 -50
- package/lib/helper/JSONResponse.js +62 -65
- package/lib/helper/Mochawesome.js +28 -28
- package/lib/helper/MockServer.js +14 -12
- package/lib/helper/Nightmare.js +566 -662
- package/lib/helper/Playwright.js +1216 -1361
- package/lib/helper/Protractor.js +627 -663
- package/lib/helper/Puppeteer.js +1128 -1231
- package/lib/helper/REST.js +68 -159
- package/lib/helper/SoftExpectHelper.js +2 -2
- package/lib/helper/TestCafe.js +484 -490
- package/lib/helper/WebDriver.js +1156 -1297
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- package/lib/helper/errors/ConnectionRefused.js +1 -1
- package/lib/helper/errors/ElementAssertion.js +2 -2
- package/lib/helper/errors/ElementNotFound.js +2 -2
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +1 -1
- package/lib/helper/extras/Console.js +1 -1
- package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
- package/lib/helper/extras/PlaywrightReactVueLocator.js +1 -1
- package/lib/helper/extras/PlaywrightRestartOpts.js +18 -21
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +3 -3
- package/lib/helper/network/actions.js +7 -14
- package/lib/helper/network/utils.js +2 -3
- package/lib/helper/scripts/blurElement.js +1 -1
- package/lib/helper/scripts/focusElement.js +1 -1
- package/lib/helper/scripts/highlightElement.js +1 -1
- package/lib/helper/scripts/isElementClickable.js +1 -1
- package/lib/helper/testcafe/testControllerHolder.js +1 -1
- package/lib/helper/testcafe/testcafe-utils.js +7 -6
- package/lib/helper.js +3 -1
- package/lib/history.js +5 -6
- package/lib/hooks.js +6 -6
- package/lib/html.js +7 -7
- package/lib/index.js +41 -25
- package/lib/interfaces/bdd.js +64 -47
- package/lib/interfaces/featureConfig.js +19 -19
- package/lib/interfaces/gherkin.js +118 -124
- package/lib/interfaces/scenarioConfig.js +29 -29
- package/lib/listener/artifacts.js +9 -9
- package/lib/listener/config.js +24 -24
- package/lib/listener/exit.js +12 -12
- package/lib/listener/helpers.js +42 -42
- package/lib/listener/mocha.js +11 -11
- package/lib/listener/retry.js +30 -32
- package/lib/listener/steps.js +53 -50
- package/lib/listener/timeout.js +54 -54
- package/lib/locator.js +10 -6
- package/lib/mochaFactory.js +15 -18
- package/lib/output.js +10 -6
- package/lib/parser.js +12 -15
- package/lib/pause.js +33 -40
- package/lib/plugin/allure.js +15 -15
- package/lib/plugin/autoDelay.js +37 -29
- package/lib/plugin/autoLogin.js +65 -70
- package/lib/plugin/commentStep.js +18 -18
- package/lib/plugin/coverage.js +67 -115
- package/lib/plugin/customLocator.js +20 -21
- package/lib/plugin/debugErrors.js +24 -24
- package/lib/plugin/eachElement.js +38 -38
- package/lib/plugin/fakerTransform.js +6 -6
- package/lib/plugin/heal.js +108 -67
- package/lib/plugin/pauseOnFail.js +11 -11
- package/lib/plugin/retryFailedStep.js +39 -32
- package/lib/plugin/retryTo.js +40 -46
- package/lib/plugin/screenshotOnFail.js +87 -109
- package/lib/plugin/selenoid.js +118 -131
- package/lib/plugin/standardActingHelpers.js +8 -2
- package/lib/plugin/stepByStepReport.js +91 -110
- package/lib/plugin/stepTimeout.js +23 -24
- package/lib/plugin/subtitles.js +35 -34
- package/lib/plugin/tryTo.js +30 -40
- package/lib/plugin/wdio.js +75 -78
- package/lib/recorder.js +17 -14
- package/lib/rerun.js +10 -11
- package/lib/scenario.js +23 -25
- package/lib/secret.js +2 -4
- package/lib/session.js +10 -10
- package/lib/step.js +9 -12
- package/lib/store.js +3 -2
- package/lib/transform.js +1 -1
- package/lib/translation.js +8 -7
- package/lib/ui.js +14 -12
- package/lib/utils.js +72 -70
- package/lib/within.js +10 -10
- package/lib/workerStorage.js +25 -27
- package/lib/workers.js +32 -29
- package/package.json +53 -51
- package/translations/de-DE.js +1 -1
- package/translations/fr-FR.js +1 -1
- package/translations/index.js +13 -9
- package/translations/it-IT.js +1 -1
- package/translations/ja-JP.js +1 -1
- package/translations/pl-PL.js +1 -1
- package/translations/pt-BR.js +1 -1
- package/translations/ru-RU.js +1 -1
- package/translations/zh-CN.js +1 -1
- package/translations/zh-TW.js +1 -1
- package/typings/index.d.ts +65 -415
package/lib/plugin/wdio.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
import debug from 'debug';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import container from '../container.js';
|
|
4
|
+
import mainConfig from '../config.js';
|
|
5
|
+
import recorder from '../recorder.js';
|
|
6
|
+
import * as event from '../event.js';
|
|
7
|
+
import * as output from '../output.js';
|
|
8
|
+
|
|
9
|
+
debug('codeceptjs:plugin:wdio');
|
|
8
10
|
|
|
9
11
|
const defaultConfig = {
|
|
10
12
|
services: [],
|
|
11
13
|
capabilities: {},
|
|
12
|
-
}
|
|
14
|
+
};
|
|
13
15
|
|
|
14
|
-
let restartsSession
|
|
16
|
+
let restartsSession;
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Webdriverio services runner.
|
|
@@ -81,169 +83,164 @@ let restartsSession
|
|
|
81
83
|
* * ... - additional configuration passed into services.
|
|
82
84
|
*
|
|
83
85
|
*/
|
|
84
|
-
|
|
86
|
+
export default (config) => {
|
|
85
87
|
// Keep initial configs to pass as options to wdio services
|
|
86
|
-
const wdioOptions = { ...config }
|
|
87
|
-
const webDriver = container.helpers('WebDriver')
|
|
88
|
+
const wdioOptions = { ...config };
|
|
89
|
+
const webDriver = container.helpers('WebDriver');
|
|
88
90
|
if (webDriver) {
|
|
89
|
-
config = Object.assign(webDriver.options, config)
|
|
90
|
-
restartsSession = !!config.restart
|
|
91
|
+
config = Object.assign(webDriver.options, config);
|
|
92
|
+
restartsSession = !!config.restart;
|
|
91
93
|
}
|
|
92
|
-
config = Object.assign(defaultConfig, config)
|
|
93
|
-
const seleniumInstallArgs = { ...config.seleniumInstallArgs }
|
|
94
|
-
const seleniumArgs = { ...config.seleniumArgs }
|
|
94
|
+
config = Object.assign(defaultConfig, config);
|
|
95
|
+
const seleniumInstallArgs = { ...config.seleniumInstallArgs };
|
|
96
|
+
const seleniumArgs = { ...config.seleniumArgs };
|
|
95
97
|
|
|
96
|
-
const services = []
|
|
97
|
-
const launchers = []
|
|
98
|
+
const services = [];
|
|
99
|
+
const launchers = [];
|
|
98
100
|
|
|
99
101
|
for (const name of config.services) {
|
|
100
|
-
const Service = safeRequire(`@wdio/${name.toLowerCase()}-service`)
|
|
102
|
+
const Service = safeRequire(`@wdio/${name.toLowerCase()}-service`);
|
|
101
103
|
if (Service) {
|
|
102
104
|
if (Service.launcher && typeof Service.launcher === 'function') {
|
|
103
|
-
const Launcher = Service.launcher
|
|
105
|
+
const Launcher = Service.launcher;
|
|
104
106
|
|
|
105
107
|
const options = {
|
|
106
|
-
logPath: global.output_dir,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
...wdioOptions,
|
|
110
|
-
}
|
|
111
|
-
launchers.push(new Launcher(options, [config.capabilities], config))
|
|
108
|
+
logPath: global.output_dir, installArgs: seleniumInstallArgs, args: seleniumArgs, ...wdioOptions,
|
|
109
|
+
};
|
|
110
|
+
launchers.push(new Launcher(options, [config.capabilities], config));
|
|
112
111
|
}
|
|
113
112
|
if (typeof Service === 'function') {
|
|
114
|
-
services.push(new Service(config, config.capabilities))
|
|
113
|
+
services.push(new Service(config, config.capabilities));
|
|
115
114
|
}
|
|
116
|
-
continue
|
|
115
|
+
continue;
|
|
117
116
|
}
|
|
118
117
|
|
|
119
|
-
throw new Error(
|
|
120
|
-
`Couldn't initialize service ${name} from wdio plugin config.\nIt should be available either in '@wdio/${name.toLowerCase()}-service' package`,
|
|
121
|
-
)
|
|
118
|
+
throw new Error(`Couldn't initialize service ${name} from wdio plugin config.\nIt should be available either in '@wdio/${name.toLowerCase()}-service' package`);
|
|
122
119
|
}
|
|
123
120
|
|
|
124
|
-
debug(`services ${services}, launchers ${launchers}`)
|
|
121
|
+
debug(`services ${services}, launchers ${launchers}`);
|
|
125
122
|
|
|
126
|
-
recorder.startUnlessRunning()
|
|
123
|
+
recorder.startUnlessRunning();
|
|
127
124
|
|
|
128
125
|
for (const launcher of launchers) {
|
|
129
|
-
registerLauncher(launcher)
|
|
126
|
+
registerLauncher(launcher);
|
|
130
127
|
}
|
|
131
128
|
|
|
132
129
|
for (const service of services) {
|
|
133
|
-
registerService(service)
|
|
130
|
+
registerService(service);
|
|
134
131
|
}
|
|
135
132
|
|
|
136
133
|
function registerService(service) {
|
|
137
|
-
const name = service.constructor.name
|
|
134
|
+
const name = service.constructor.name;
|
|
138
135
|
if (service.beforeSession) {
|
|
139
136
|
event.dispatcher.on(event.all.before, () => {
|
|
140
137
|
recorder.add(`service ${name} all.before`, async () => {
|
|
141
|
-
await service.beforeSession(config, config.capabilities)
|
|
142
|
-
})
|
|
143
|
-
})
|
|
138
|
+
await service.beforeSession(config, config.capabilities);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
144
141
|
}
|
|
145
142
|
|
|
146
143
|
if (service.afterSession) {
|
|
147
144
|
event.dispatcher.on(event.all.result, (result) => {
|
|
148
145
|
recorder.add(`service ${name} all.after`, async () => {
|
|
149
|
-
await service.afterSession(result)
|
|
150
|
-
})
|
|
151
|
-
})
|
|
146
|
+
await service.afterSession(result);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
152
149
|
}
|
|
153
150
|
|
|
154
151
|
if (service.beforeSuite) {
|
|
155
152
|
event.dispatcher.on(event.suite.before, (suite) => {
|
|
156
|
-
debug(`suite started: ${suite.title}`)
|
|
157
|
-
recorder.add(`service ${name} suite.before`, () => service.beforeSuite(suite))
|
|
158
|
-
})
|
|
153
|
+
debug(`suite started: ${suite.title}`);
|
|
154
|
+
recorder.add(`service ${name} suite.before`, () => service.beforeSuite(suite));
|
|
155
|
+
});
|
|
159
156
|
}
|
|
160
157
|
|
|
161
158
|
if (service.afterSuite) {
|
|
162
159
|
event.dispatcher.on(event.suite.after, (suite) => {
|
|
163
|
-
debug(`suite finished: ${suite.title}`)
|
|
164
|
-
recorder.add(`service ${name} suite.after`, () => service.afterSuite(suite))
|
|
165
|
-
})
|
|
160
|
+
debug(`suite finished: ${suite.title}`);
|
|
161
|
+
recorder.add(`service ${name} suite.after`, () => service.afterSuite(suite));
|
|
162
|
+
});
|
|
166
163
|
}
|
|
167
164
|
|
|
168
165
|
if (service.beforeTest) {
|
|
169
166
|
event.dispatcher.on(event.test.started, async (test) => {
|
|
170
167
|
if (test.parent) {
|
|
171
|
-
test.parent.toString = () => test.parent.title
|
|
168
|
+
test.parent.toString = () => test.parent.title;
|
|
172
169
|
}
|
|
173
170
|
// test.parent = test.parent ? test.parent.title : null;
|
|
174
|
-
debug(`test started: ${test.title}`)
|
|
171
|
+
debug(`test started: ${test.title}`);
|
|
175
172
|
if (webDriver) {
|
|
176
|
-
global.browser = webDriver.browser
|
|
177
|
-
global.browser.config = Object.assign(mainConfig.get('test', 1), global.browser.config)
|
|
173
|
+
global.browser = webDriver.browser;
|
|
174
|
+
global.browser.config = Object.assign(mainConfig.get('test', 1), global.browser.config);
|
|
178
175
|
}
|
|
179
|
-
await service.beforeTest(test)
|
|
180
|
-
})
|
|
176
|
+
await service.beforeTest(test);
|
|
177
|
+
});
|
|
181
178
|
}
|
|
182
179
|
|
|
183
180
|
if (service.afterTest) {
|
|
184
181
|
event.dispatcher.on(event.test.finished, async (test) => {
|
|
185
|
-
debug(`test finished: ${test.title}`)
|
|
186
|
-
await service.afterTest(test)
|
|
187
|
-
})
|
|
182
|
+
debug(`test finished: ${test.title}`);
|
|
183
|
+
await service.afterTest(test);
|
|
184
|
+
});
|
|
188
185
|
}
|
|
189
186
|
|
|
190
187
|
if (restartsSession && service.before) {
|
|
191
|
-
event.dispatcher.on(event.test.started, () => service.before())
|
|
188
|
+
event.dispatcher.on(event.test.started, () => service.before());
|
|
192
189
|
}
|
|
193
190
|
|
|
194
191
|
if (restartsSession && service.after) {
|
|
195
|
-
event.dispatcher.on(event.test.finished, () => service.after())
|
|
192
|
+
event.dispatcher.on(event.test.finished, () => service.after());
|
|
196
193
|
}
|
|
197
194
|
|
|
198
195
|
if (!restartsSession && service.before) {
|
|
199
|
-
let initializedBrowser = false
|
|
196
|
+
let initializedBrowser = false;
|
|
200
197
|
event.dispatcher.on(event.test.started, async () => {
|
|
201
198
|
if (!initializedBrowser) {
|
|
202
|
-
await service.before()
|
|
203
|
-
initializedBrowser = true
|
|
199
|
+
await service.before();
|
|
200
|
+
initializedBrowser = true;
|
|
204
201
|
}
|
|
205
|
-
})
|
|
202
|
+
});
|
|
206
203
|
}
|
|
207
204
|
|
|
208
205
|
if (!restartsSession && service.after) {
|
|
209
|
-
event.dispatcher.on(event.all.result,
|
|
206
|
+
event.dispatcher.on(event.all.result, result => service.after(result));
|
|
210
207
|
}
|
|
211
208
|
}
|
|
212
209
|
|
|
213
210
|
function registerLauncher(launcher) {
|
|
214
|
-
const name = launcher.constructor.name
|
|
211
|
+
const name = launcher.constructor.name;
|
|
215
212
|
if (launcher.onPrepare) {
|
|
216
213
|
event.dispatcher.on(event.all.before, () => {
|
|
217
214
|
recorder.add(`launcher ${name} start`, async () => {
|
|
218
215
|
// browserstack-service expects capabilities as array
|
|
219
216
|
if (launcher.constructor.name === 'BrowserstackLauncherService') {
|
|
220
|
-
await launcher.onPrepare(config, [config.capabilities])
|
|
217
|
+
await launcher.onPrepare(config, [config.capabilities]);
|
|
221
218
|
} else {
|
|
222
|
-
await launcher.onPrepare(config, config.capabilities)
|
|
219
|
+
await launcher.onPrepare(config, config.capabilities);
|
|
223
220
|
}
|
|
224
|
-
output.debug(`Started ${name}`)
|
|
225
|
-
})
|
|
226
|
-
})
|
|
221
|
+
output.debug(`Started ${name}`);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
227
224
|
}
|
|
228
225
|
|
|
229
226
|
if (launcher.onComplete) {
|
|
230
227
|
event.dispatcher.on(event.all.after, () => {
|
|
231
228
|
recorder.add(`launcher ${name} start`, async () => {
|
|
232
|
-
await launcher.onComplete(process.exitCode, config, config.capabilities)
|
|
233
|
-
output.debug(`Stopped ${name}`)
|
|
234
|
-
})
|
|
235
|
-
})
|
|
229
|
+
await launcher.onComplete(process.exitCode, config, config.capabilities);
|
|
230
|
+
output.debug(`Stopped ${name}`);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
236
233
|
}
|
|
237
234
|
}
|
|
238
|
-
}
|
|
235
|
+
};
|
|
239
236
|
|
|
240
237
|
function safeRequire(name) {
|
|
241
238
|
try {
|
|
242
|
-
return require(name)
|
|
239
|
+
return require(name);
|
|
243
240
|
} catch (e) {
|
|
244
241
|
if (!e.message.match(`Cannot find module '${name}'`)) {
|
|
245
|
-
throw new Error(`Couldn't initialise "${name}".\n${e.stack}`)
|
|
242
|
+
throw new Error(`Couldn't initialise "${name}".\n${e.stack}`);
|
|
246
243
|
}
|
|
247
|
-
return null
|
|
244
|
+
return null;
|
|
248
245
|
}
|
|
249
246
|
}
|
package/lib/recorder.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
import promiseRetry from 'promise-retry';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { printObjectProperties } from './utils.js';
|
|
5
|
+
import * as output from './output.js';
|
|
6
6
|
|
|
7
|
+
debug('codeceptjs:recorder');
|
|
7
8
|
const MAX_TASKS = 100;
|
|
8
9
|
|
|
9
10
|
let promise;
|
|
@@ -28,7 +29,7 @@ const defaultRetryOptions = {
|
|
|
28
29
|
* @alias recorder
|
|
29
30
|
* @interface
|
|
30
31
|
*/
|
|
31
|
-
|
|
32
|
+
export default {
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* @type {Array<Object<string, *>>}
|
|
@@ -47,6 +48,7 @@ module.exports = {
|
|
|
47
48
|
running = true;
|
|
48
49
|
asyncErr = null;
|
|
49
50
|
errFn = null;
|
|
51
|
+
// @ts-ignore
|
|
50
52
|
this.reset();
|
|
51
53
|
},
|
|
52
54
|
|
|
@@ -91,7 +93,7 @@ module.exports = {
|
|
|
91
93
|
queueId++;
|
|
92
94
|
sessionId = null;
|
|
93
95
|
asyncErr = null;
|
|
94
|
-
log(`${currentQueue()} Starting recording promises`);
|
|
96
|
+
output.output.log(`${currentQueue()} Starting recording promises`);
|
|
95
97
|
promise = Promise.resolve();
|
|
96
98
|
oldPromises = [];
|
|
97
99
|
tasks = [];
|
|
@@ -163,10 +165,10 @@ module.exports = {
|
|
|
163
165
|
* @param {boolean} [force=false]
|
|
164
166
|
* @param {boolean} [retry]
|
|
165
167
|
* undefined: `add(fn)` -> `false` and `add('step',fn)` -> `true`
|
|
166
|
-
* true: it will
|
|
168
|
+
* true: it will retry if `retryOpts` set.
|
|
167
169
|
* false: ignore `retryOpts` and won't retry.
|
|
168
170
|
* @param {number} [timeout]
|
|
169
|
-
* @return {Promise
|
|
171
|
+
* @return {Promise<*>|undefined}
|
|
170
172
|
* @inner
|
|
171
173
|
*/
|
|
172
174
|
add(taskName, fn = undefined, force = false, retry = undefined, timeout = undefined) {
|
|
@@ -195,7 +197,7 @@ module.exports = {
|
|
|
195
197
|
|
|
196
198
|
const retryRules = this.retries.slice().reverse();
|
|
197
199
|
return promiseRetry(Object.assign(defaultRetryOptions, retryOpts), (retry, number) => {
|
|
198
|
-
if (number > 1) log(`${currentQueue()}Retrying... Attempt #${number}`);
|
|
200
|
+
if (number > 1) output.output.log(`${currentQueue()}Retrying... Attempt #${number}`);
|
|
199
201
|
const [promise, timer] = getTimeoutPromise(timeout, taskName);
|
|
200
202
|
return Promise.race([promise, Promise.resolve(res).then(fn)]).finally(() => clearTimeout(timer)).catch((err) => {
|
|
201
203
|
if (ignoredErrs.includes(err)) return;
|
|
@@ -235,7 +237,7 @@ module.exports = {
|
|
|
235
237
|
const fnDescription = customErrFn?.toString()?.replace(/\s{2,}/g, ' ').replace(/\n/g, ' ')?.slice(0, 50);
|
|
236
238
|
debug(chalk.gray(`${currentQueue()} Queued | catch with error handler ${fnDescription || ''}`));
|
|
237
239
|
return promise = promise.catch((err) => {
|
|
238
|
-
log(`${currentQueue()}Error | ${err} ${fnDescription}...`);
|
|
240
|
+
output.output.log(`${currentQueue()}Error | ${err} ${fnDescription}...`);
|
|
239
241
|
if (!(err instanceof Error)) { // strange things may happen
|
|
240
242
|
err = new Error(`[Wrapped Error] ${printObjectProperties(err)}`); // we should be prepared for them
|
|
241
243
|
}
|
|
@@ -257,7 +259,7 @@ module.exports = {
|
|
|
257
259
|
const fnDescription = customErrFn?.toString()?.replace(/\s{2,}/g, ' ').replace(/\n/g, ' ')?.slice(0, 50);
|
|
258
260
|
return promise = promise.catch((err) => {
|
|
259
261
|
if (ignoredErrs.includes(err)) return; // already caught
|
|
260
|
-
log(`${currentQueue()}
|
|
262
|
+
output.output.log(`${currentQueue()}Error (Non-Terminated) | ${err} | ${fnDescription || ''}...`);
|
|
261
263
|
if (!(err instanceof Error)) { // strange things may happen
|
|
262
264
|
err = new Error(`[Wrapped Error] ${JSON.stringify(err)}`); // we should be prepared for them
|
|
263
265
|
}
|
|
@@ -319,8 +321,10 @@ module.exports = {
|
|
|
319
321
|
* @inner
|
|
320
322
|
*/
|
|
321
323
|
stop() {
|
|
324
|
+
if (process.env.DEBUG) debug(this.toString());
|
|
325
|
+
output.output.log(`${currentQueue()}Stopping recording promises`);
|
|
322
326
|
debug(this.toString());
|
|
323
|
-
|
|
327
|
+
|
|
324
328
|
running = false;
|
|
325
329
|
},
|
|
326
330
|
|
|
@@ -361,7 +365,6 @@ module.exports = {
|
|
|
361
365
|
toString() {
|
|
362
366
|
return `Queue: ${currentQueue()}\n\nTasks: ${this.scheduled()}`;
|
|
363
367
|
},
|
|
364
|
-
|
|
365
368
|
};
|
|
366
369
|
|
|
367
370
|
function getTimeoutPromise(timeoutMs, taskName) {
|
package/lib/rerun.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import fsPath from 'path';
|
|
2
|
+
import container from './container.js';
|
|
3
|
+
import * as event from './event.js';
|
|
4
|
+
import BaseCodecept from './codecept.js';
|
|
5
|
+
import * as output from './output.js';
|
|
6
6
|
|
|
7
7
|
class CodeceptRerunner extends BaseCodecept {
|
|
8
8
|
runOnce(test) {
|
|
@@ -11,7 +11,7 @@ class CodeceptRerunner extends BaseCodecept {
|
|
|
11
11
|
container.createMocha();
|
|
12
12
|
const mocha = container.mocha();
|
|
13
13
|
this.testFiles.forEach((file) => {
|
|
14
|
-
delete require.cache[file];
|
|
14
|
+
// delete require.cache[file];
|
|
15
15
|
});
|
|
16
16
|
mocha.files = this.testFiles;
|
|
17
17
|
if (test) {
|
|
@@ -53,9 +53,9 @@ class CodeceptRerunner extends BaseCodecept {
|
|
|
53
53
|
try {
|
|
54
54
|
await this.runOnce(test);
|
|
55
55
|
successCounter++;
|
|
56
|
-
output.success(`\nProcess run ${rerunsCounter} of max ${maxReruns}, success runs ${successCounter}/${minSuccess}\n`);
|
|
56
|
+
output.output.success(`\nProcess run ${rerunsCounter} of max ${maxReruns}, success runs ${successCounter}/${minSuccess}\n`);
|
|
57
57
|
} catch (e) {
|
|
58
|
-
output.error(`\nFail run ${rerunsCounter} of max ${maxReruns}, success runs ${successCounter}/${minSuccess} \n`);
|
|
58
|
+
output.output.error(`\nFail run ${rerunsCounter} of max ${maxReruns}, success runs ${successCounter}/${minSuccess} \n`);
|
|
59
59
|
console.error(e);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -69,8 +69,7 @@ class CodeceptRerunner extends BaseCodecept {
|
|
|
69
69
|
try {
|
|
70
70
|
await this.runTests(test);
|
|
71
71
|
} catch (e) {
|
|
72
|
-
output.error(e.stack);
|
|
73
|
-
throw e;
|
|
72
|
+
output.output.error(e.stack);
|
|
74
73
|
} finally {
|
|
75
74
|
event.emit(event.all.result, this);
|
|
76
75
|
event.emit(event.all.after, this);
|
|
@@ -78,4 +77,4 @@ class CodeceptRerunner extends BaseCodecept {
|
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
|
|
80
|
+
export default CodeceptRerunner;
|
package/lib/scenario.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import promiseRetry from 'promise-retry';
|
|
2
|
+
import * as event from './event.js';
|
|
3
|
+
import recorder from './recorder.js';
|
|
4
|
+
import assertThrown from './assert/throws.js';
|
|
5
|
+
import { ucfirst, isAsyncFunction } from './utils.js';
|
|
6
|
+
import * as parser from './parser.js';
|
|
7
|
+
import Container from './container.js';
|
|
7
8
|
|
|
8
9
|
const injectHook = function (inject, suite) {
|
|
9
10
|
try {
|
|
@@ -33,7 +34,7 @@ function makeDoneCallableOnce(done) {
|
|
|
33
34
|
* starts promise chain with recorder, performs before/after hooks
|
|
34
35
|
* through event system.
|
|
35
36
|
*/
|
|
36
|
-
|
|
37
|
+
export function test(test) {
|
|
37
38
|
const testFn = test.fn;
|
|
38
39
|
if (!testFn) {
|
|
39
40
|
return test;
|
|
@@ -100,12 +101,12 @@ module.exports.test = (test) => {
|
|
|
100
101
|
}
|
|
101
102
|
};
|
|
102
103
|
return test;
|
|
103
|
-
}
|
|
104
|
+
}
|
|
104
105
|
|
|
105
106
|
/**
|
|
106
107
|
* Injects arguments to function from controller
|
|
107
108
|
*/
|
|
108
|
-
|
|
109
|
+
export function injected(fn, suite, hookName) {
|
|
109
110
|
return function (done) {
|
|
110
111
|
const doneFn = makeDoneCallableOnce(done);
|
|
111
112
|
const errHandler = (err) => {
|
|
@@ -167,44 +168,43 @@ module.exports.injected = function (fn, suite, hookName) {
|
|
|
167
168
|
recorder.add('fire hook.failed', () => event.emit(event.hook.failed, suite, e));
|
|
168
169
|
});
|
|
169
170
|
};
|
|
170
|
-
}
|
|
171
|
+
}
|
|
171
172
|
|
|
172
173
|
/**
|
|
173
174
|
* Starts promise chain, so helpers could enqueue their hooks
|
|
174
175
|
*/
|
|
175
|
-
|
|
176
|
+
export function setup(suite) {
|
|
176
177
|
return injectHook(() => {
|
|
177
178
|
recorder.startUnlessRunning();
|
|
178
179
|
event.emit(event.test.before, suite && suite.ctx && suite.ctx.currentTest);
|
|
179
180
|
}, suite);
|
|
180
|
-
}
|
|
181
|
+
}
|
|
181
182
|
|
|
182
|
-
|
|
183
|
+
export function teardown(suite) {
|
|
183
184
|
return injectHook(() => {
|
|
184
185
|
recorder.startUnlessRunning();
|
|
185
186
|
event.emit(event.test.after, suite && suite.ctx && suite.ctx.currentTest);
|
|
186
187
|
}, suite);
|
|
187
|
-
}
|
|
188
|
+
}
|
|
188
189
|
|
|
189
|
-
|
|
190
|
+
export function suiteSetup(suite) {
|
|
190
191
|
return injectHook(() => {
|
|
191
192
|
recorder.startUnlessRunning();
|
|
192
193
|
event.emit(event.suite.before, suite);
|
|
193
194
|
}, suite);
|
|
194
|
-
}
|
|
195
|
+
}
|
|
195
196
|
|
|
196
|
-
|
|
197
|
+
export function suiteTeardown(suite) {
|
|
197
198
|
return injectHook(() => {
|
|
198
199
|
recorder.startUnlessRunning();
|
|
199
200
|
event.emit(event.suite.after, suite);
|
|
200
201
|
}, suite);
|
|
201
|
-
}
|
|
202
|
+
}
|
|
202
203
|
|
|
203
|
-
|
|
204
|
-
const container = require('./container');
|
|
204
|
+
export function getInjectedArguments(fn, test) {
|
|
205
205
|
const testArgs = {};
|
|
206
206
|
const params = parser.getParams(fn) || [];
|
|
207
|
-
const objects =
|
|
207
|
+
const objects = Container.support();
|
|
208
208
|
for (const key of params) {
|
|
209
209
|
testArgs[key] = {};
|
|
210
210
|
if (test && test.inject && test.inject[key]) {
|
|
@@ -215,10 +215,8 @@ const getInjectedArguments = (fn, test) => {
|
|
|
215
215
|
if (!objects[key]) {
|
|
216
216
|
throw new Error(`Object of type ${key} is not defined in container`);
|
|
217
217
|
}
|
|
218
|
-
testArgs[key] =
|
|
218
|
+
testArgs[key] = Container.support(key);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
return testArgs;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
module.exports.getInjectedArguments = getInjectedArguments;
|
|
222
|
+
}
|
package/lib/secret.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint-disable max-classes-per-file */
|
|
2
|
-
|
|
2
|
+
import { deepClone } from './utils.js';
|
|
3
3
|
|
|
4
4
|
const maskedString = '*****';
|
|
5
5
|
|
|
6
6
|
/** @param {string} secret */
|
|
7
|
-
class Secret {
|
|
7
|
+
export default class Secret {
|
|
8
8
|
constructor(secret) {
|
|
9
9
|
this._secret = secret;
|
|
10
10
|
}
|
|
@@ -48,5 +48,3 @@ function secretObject(obj, fieldsToHide = []) {
|
|
|
48
48
|
|
|
49
49
|
return new Proxy(obj, handler);
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
module.exports = Secret;
|
package/lib/session.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import recorder from './recorder.js';
|
|
2
|
+
import container from './container.js';
|
|
3
|
+
import * as event from './event.js';
|
|
4
|
+
import * as output from './output.js';
|
|
5
|
+
import { store } from './store.js';
|
|
6
|
+
import { isAsyncFunction } from './utils.js';
|
|
7
7
|
|
|
8
8
|
const sessionColors = [
|
|
9
9
|
'cyan',
|
|
@@ -90,7 +90,7 @@ function session(sessionName, config, fn) {
|
|
|
90
90
|
|
|
91
91
|
const finalize = () => {
|
|
92
92
|
recorder.add('Finalize session', async () => {
|
|
93
|
-
output.stepShift = 0;
|
|
93
|
+
output.output.stepShift = 0;
|
|
94
94
|
event.dispatcher.removeListener(event.step.after, addContextToStep);
|
|
95
95
|
await session.restoreVars();
|
|
96
96
|
recorder.session.restore(`session:${sessionName}`);
|
|
@@ -103,7 +103,7 @@ function session(sessionName, config, fn) {
|
|
|
103
103
|
finalize();
|
|
104
104
|
return recorder.promise().then(() => res);
|
|
105
105
|
}).catch((e) => {
|
|
106
|
-
output.stepShift = 0;
|
|
106
|
+
output.output.stepShift = 0;
|
|
107
107
|
session.restoreVars(sessionName);
|
|
108
108
|
event.dispatcher.removeListener(event.step.after, addContextToStep);
|
|
109
109
|
recorder.throw(e);
|
|
@@ -119,7 +119,7 @@ function session(sessionName, config, fn) {
|
|
|
119
119
|
} finally {
|
|
120
120
|
recorder.catch((e) => {
|
|
121
121
|
session.restoreVars(sessionName);
|
|
122
|
-
output.stepShift = 0;
|
|
122
|
+
output.output.stepShift = 0;
|
|
123
123
|
event.dispatcher.removeListener(event.step.after, addContextToStep);
|
|
124
124
|
throw e;
|
|
125
125
|
});
|
|
@@ -129,4 +129,4 @@ function session(sessionName, config, fn) {
|
|
|
129
129
|
}, false, false);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
export default session;
|
package/lib/step.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// TODO: place MetaStep in other file, disable rule
|
|
2
2
|
/* eslint-disable max-classes-per-file */
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { store } from './store.js';
|
|
4
|
+
|
|
5
|
+
import Secret from './secret.js';
|
|
6
|
+
import * as event from './event.js';
|
|
6
7
|
|
|
7
8
|
const STACK_LINE = 4;
|
|
8
9
|
|
|
@@ -12,7 +13,7 @@ const STACK_LINE = 4;
|
|
|
12
13
|
* @param {CodeceptJS.Helper} helper
|
|
13
14
|
* @param {string} name
|
|
14
15
|
*/
|
|
15
|
-
class Step {
|
|
16
|
+
export class Step {
|
|
16
17
|
static get TIMEOUT_ORDER() {
|
|
17
18
|
return {
|
|
18
19
|
/**
|
|
@@ -71,13 +72,11 @@ class Step {
|
|
|
71
72
|
new Map([...timeouts.entries()].sort().reverse()).forEach((timeout, order) => {
|
|
72
73
|
if (timeout !== undefined && (
|
|
73
74
|
// when orders >= 0 - timeout value overrides those set with higher order elements
|
|
74
|
-
order
|
|
75
|
+
(// when `order < 0` - timeout overrides higher values of timeout or 'no timeout' (totalTimeout === 0) set by elements with higher order
|
|
76
|
+
(order >= 0
|
|
75
77
|
|
|
76
78
|
// when `order < 0 && totalTimeout === undefined` - timeout is used when nothing is set by elements with higher order
|
|
77
|
-
|| totalTimeout === undefined
|
|
78
|
-
|
|
79
|
-
// when `order < 0` - timeout overrides higher values of timeout or 'no timeout' (totalTimeout === 0) set by elements with higher order
|
|
80
|
-
|| timeout > 0 && (timeout < totalTimeout || totalTimeout === 0)
|
|
79
|
+
|| totalTimeout === undefined || timeout > 0 && (timeout < totalTimeout || totalTimeout === 0)))
|
|
81
80
|
)) {
|
|
82
81
|
totalTimeout = timeout;
|
|
83
82
|
}
|
|
@@ -221,7 +220,7 @@ class Step {
|
|
|
221
220
|
}
|
|
222
221
|
|
|
223
222
|
/** @extends Step */
|
|
224
|
-
class MetaStep extends Step {
|
|
223
|
+
export class MetaStep extends Step {
|
|
225
224
|
constructor(obj, method) {
|
|
226
225
|
super(null, method);
|
|
227
226
|
this.actor = obj;
|
|
@@ -311,8 +310,6 @@ Step.TIMEOUTS = {};
|
|
|
311
310
|
/** @type {Class<MetaStep>} */
|
|
312
311
|
Step.MetaStep = MetaStep;
|
|
313
312
|
|
|
314
|
-
module.exports = Step;
|
|
315
|
-
|
|
316
313
|
function dryRunResolver() {
|
|
317
314
|
return {
|
|
318
315
|
get(target, prop) {
|
package/lib/store.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* global values for current session
|
|
3
3
|
* @namespace
|
|
4
4
|
*/
|
|
5
|
-
const store = {
|
|
5
|
+
export const store = {
|
|
6
6
|
/** @type {boolean} */
|
|
7
7
|
debugMode: false,
|
|
8
8
|
/** @type {boolean} */
|
|
@@ -11,4 +11,5 @@ const store = {
|
|
|
11
11
|
dryRun: false,
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/** @type {boolean} */
|
|
15
|
+
export const timeouts = true;
|
package/lib/transform.js
CHANGED