codeceptjs 3.6.6 → 4.0.0-beta.1
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/typings/promiseBasedTypes.d.ts +32 -0
- package/typings/types.d.ts +32 -0
package/lib/output.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import colors from 'chalk';
|
|
2
|
+
import figures from 'figures';
|
|
3
3
|
|
|
4
4
|
const styles = {
|
|
5
5
|
error: colors.bgRed.white.bold,
|
|
@@ -19,7 +19,7 @@ let newline = true;
|
|
|
19
19
|
* @alias output
|
|
20
20
|
* @namespace
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
export const output = {
|
|
23
23
|
colors,
|
|
24
24
|
styles,
|
|
25
25
|
print,
|
|
@@ -62,6 +62,10 @@ module.exports = {
|
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
|
|
65
|
+
debugSection(section, msg) {
|
|
66
|
+
this.debug(`[${section}] ${msg}`);
|
|
67
|
+
},
|
|
68
|
+
|
|
65
69
|
/**
|
|
66
70
|
* Print information in --verbose mode
|
|
67
71
|
* @param {string} msg
|
|
@@ -113,7 +117,7 @@ module.exports = {
|
|
|
113
117
|
|
|
114
118
|
let stepLine = step.toString();
|
|
115
119
|
if (step.metaStep && outputLevel >= 1) {
|
|
116
|
-
|
|
120
|
+
this.stepShift += 2;
|
|
117
121
|
stepLine = colors.green(truncate(stepLine, this.spaceShift));
|
|
118
122
|
}
|
|
119
123
|
if (step.comment) {
|
|
@@ -167,7 +171,7 @@ module.exports = {
|
|
|
167
171
|
scenario: {
|
|
168
172
|
/**
|
|
169
173
|
* @param {Mocha.Test} test
|
|
170
|
-
|
|
174
|
+
*/
|
|
171
175
|
/* eslint-disable */
|
|
172
176
|
started(test) {},
|
|
173
177
|
/* eslint-enable */
|
|
@@ -232,7 +236,7 @@ module.exports = {
|
|
|
232
236
|
},
|
|
233
237
|
};
|
|
234
238
|
|
|
235
|
-
function print(...msg) {
|
|
239
|
+
export function print(...msg) {
|
|
236
240
|
if (outputProcess) {
|
|
237
241
|
msg.unshift(outputProcess);
|
|
238
242
|
}
|
package/lib/parser.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const { error } = require('./output');
|
|
1
|
+
import { parse as acornParse } from 'acorn';
|
|
2
|
+
import parseFunction from 'parse-function';
|
|
3
|
+
import * as output from './output.js';
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const app = parseFunction({ parse: acornParse, ecmaVersion: 11, plugins: ['objectRestSpread'] });
|
|
6
|
+
app.use(destructuredArgs);
|
|
7
|
+
export function getParamsToString(fn) {
|
|
9
8
|
const newFn = fn.toString().replace(/^async/, 'async function');
|
|
10
9
|
return getParams(newFn).join(', ');
|
|
11
|
-
}
|
|
10
|
+
}
|
|
12
11
|
|
|
13
|
-
function getParams(fn) {
|
|
12
|
+
export function getParams(fn) {
|
|
14
13
|
if (fn.isSinonProxy) return [];
|
|
15
14
|
try {
|
|
16
|
-
const reflected =
|
|
15
|
+
const reflected = app.parse(fn);
|
|
17
16
|
if (reflected.args.length > 1 || reflected.args[0] === 'I') {
|
|
18
|
-
error('Error: old CodeceptJS v2 format detected. Upgrade your project to the new format -> https://bit.ly/codecept3Up');
|
|
17
|
+
output.output.error('Error: old CodeceptJS v2 format detected. Upgrade your project to the new format -> https://bit.ly/codecept3Up');
|
|
19
18
|
}
|
|
20
19
|
if (reflected.destructuredArgs.length > 0) reflected.args = [...reflected.destructuredArgs];
|
|
21
20
|
const params = reflected.args.map((p) => {
|
|
@@ -28,13 +27,11 @@ function getParams(fn) {
|
|
|
28
27
|
return params;
|
|
29
28
|
} catch (err) {
|
|
30
29
|
console.log(`Error in ${fn.toString()}`);
|
|
31
|
-
error(err);
|
|
30
|
+
output.output.error(err);
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
function destructuredArgs() {
|
|
34
|
+
export function destructuredArgs() {
|
|
38
35
|
return (node, result) => {
|
|
39
36
|
result.destructuredArgs = result.destructuredArgs || [];
|
|
40
37
|
|
package/lib/pause.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import colors from 'chalk';
|
|
2
|
+
import readline from 'readline';
|
|
3
|
+
import ora from 'ora-classic';
|
|
4
|
+
import debug from 'debug';
|
|
5
|
+
|
|
6
|
+
import container from './container.js';
|
|
7
|
+
import history from './history.js';
|
|
8
|
+
import { store } from './store.js';
|
|
9
|
+
import AiAssistant from './ai.js';
|
|
10
|
+
import recorder from './recorder.js';
|
|
11
|
+
import * as event from './event.js';
|
|
12
|
+
import * as output from './output.js';
|
|
13
|
+
import { methodsOfObject } from './utils.js';
|
|
14
|
+
|
|
15
|
+
debug('codeceptjs:pause');
|
|
14
16
|
|
|
15
17
|
// npm install colors
|
|
16
18
|
let rl;
|
|
@@ -18,6 +20,7 @@ let nextStep;
|
|
|
18
20
|
let finish;
|
|
19
21
|
let next;
|
|
20
22
|
let registeredVariables = {};
|
|
23
|
+
let aiAssistant;
|
|
21
24
|
/**
|
|
22
25
|
* Pauses test execution and starts interactive shell
|
|
23
26
|
* @param {Object<string, *>} [passedObject]
|
|
@@ -43,6 +46,8 @@ function pauseSession(passedObject = {}) {
|
|
|
43
46
|
let vars = Object.keys(registeredVariables).join(', ');
|
|
44
47
|
if (vars) vars = `(vars: ${vars})`;
|
|
45
48
|
|
|
49
|
+
aiAssistant = AiAssistant.getInstance();
|
|
50
|
+
|
|
46
51
|
output.print(colors.yellow(' Interactive shell started'));
|
|
47
52
|
output.print(colors.yellow(' Use JavaScript syntax to try steps in action'));
|
|
48
53
|
output.print(colors.yellow(` - Press ${colors.bold('ENTER')} to run the next step`));
|
|
@@ -51,20 +56,14 @@ function pauseSession(passedObject = {}) {
|
|
|
51
56
|
output.print(colors.yellow(` - Prefix ${colors.bold('=>')} to run js commands ${colors.bold(vars)}`));
|
|
52
57
|
|
|
53
58
|
if (aiAssistant.isEnabled) {
|
|
54
|
-
output.print(colors.blue(` ${colors.bold('
|
|
55
|
-
output.print(colors.blue(' Please note, only HTML fragments with interactive elements are sent to
|
|
59
|
+
output.print(colors.blue(` ${colors.bold('OpenAI is enabled! (experimental)')} Write what you want and make OpenAI run it`));
|
|
60
|
+
output.print(colors.blue(' Please note, only HTML fragments with interactive elements are sent to OpenAI'));
|
|
56
61
|
output.print(colors.blue(' Ideas: ask it to fill forms for you or to click'));
|
|
62
|
+
} else {
|
|
63
|
+
output.print(colors.blue(` Enable OpenAI assistant by setting ${colors.bold('OPENAI_API_KEY')} env variable`));
|
|
57
64
|
}
|
|
58
65
|
}
|
|
59
|
-
|
|
60
|
-
rl = readline.createInterface({
|
|
61
|
-
input: process.stdin,
|
|
62
|
-
output: process.stdout,
|
|
63
|
-
terminal: true,
|
|
64
|
-
completer,
|
|
65
|
-
history: history.load(),
|
|
66
|
-
historySize: 50, // Adjust the history size as needed
|
|
67
|
-
});
|
|
66
|
+
rl = readline.createInterface(process.stdin, process.stdout, completer);
|
|
68
67
|
|
|
69
68
|
rl.on('line', parseInput);
|
|
70
69
|
rl.on('close', () => {
|
|
@@ -113,7 +112,7 @@ async function parseInput(cmd) {
|
|
|
113
112
|
if (cmd.trim().startsWith('=>')) {
|
|
114
113
|
isCustomCommand = true;
|
|
115
114
|
cmd = cmd.trim().substring(2, cmd.length);
|
|
116
|
-
} else if (aiAssistant.isEnabled &&
|
|
115
|
+
} else if (aiAssistant.isEnabled && !cmd.match(/^\w+\(/) && cmd.includes(' ')) {
|
|
117
116
|
const currentOutputLevel = output.level();
|
|
118
117
|
output.level(0);
|
|
119
118
|
const res = I.grabSource();
|
|
@@ -123,13 +122,13 @@ async function parseInput(cmd) {
|
|
|
123
122
|
const html = await res;
|
|
124
123
|
await aiAssistant.setHtmlContext(html);
|
|
125
124
|
} catch (err) {
|
|
126
|
-
output.print(output.styles.error(' ERROR '), 'Can\'t get HTML context', err.stack);
|
|
125
|
+
output.print(output.output.styles.error(' ERROR '), 'Can\'t get HTML context', err.stack);
|
|
127
126
|
return;
|
|
128
127
|
} finally {
|
|
129
128
|
output.level(currentOutputLevel);
|
|
130
129
|
}
|
|
131
|
-
|
|
132
|
-
const spinner = ora("Processing
|
|
130
|
+
// aiAssistant.mockResponse("```js\nI.click('Sign in');\n```");
|
|
131
|
+
const spinner = ora("Processing OpenAI request...").start();
|
|
133
132
|
cmd = await aiAssistant.writeSteps(cmd);
|
|
134
133
|
spinner.stop();
|
|
135
134
|
output.print('');
|
|
@@ -147,7 +146,7 @@ async function parseInput(cmd) {
|
|
|
147
146
|
}).catch((err) => {
|
|
148
147
|
debug(err);
|
|
149
148
|
if (isAiCommand) return;
|
|
150
|
-
if (!lastError) output.print(output.styles.error(' ERROR '), err.message);
|
|
149
|
+
if (!lastError) output.print(output.output.styles.error(' ERROR '), err.message);
|
|
151
150
|
debug(err.stack)
|
|
152
151
|
|
|
153
152
|
lastError = err.message;
|
|
@@ -161,15 +160,15 @@ async function parseInput(cmd) {
|
|
|
161
160
|
}
|
|
162
161
|
|
|
163
162
|
if (cmd?.startsWith('I.see') || cmd?.startsWith('I.dontSee')) {
|
|
164
|
-
output.print(output.styles.success(' OK '), cmd);
|
|
163
|
+
output.print(output.output.styles.success(' OK '), cmd);
|
|
165
164
|
}
|
|
166
165
|
if (cmd?.startsWith('I.grab')) {
|
|
167
|
-
output.print(output.styles.debug(val));
|
|
166
|
+
output.print(output.output.styles.debug(val));
|
|
168
167
|
}
|
|
169
168
|
|
|
170
169
|
history.push(cmd); // add command to history when successful
|
|
171
170
|
} catch (err) {
|
|
172
|
-
if (!lastError) output.print(output.styles.error(' ERROR '), err.message);
|
|
171
|
+
if (!lastError) output.print(output.output.styles.error(' ERROR '), err.message);
|
|
173
172
|
lastError = err.message;
|
|
174
173
|
}
|
|
175
174
|
recorder.session.catch((err) => {
|
|
@@ -179,7 +178,7 @@ async function parseInput(cmd) {
|
|
|
179
178
|
history.pop();
|
|
180
179
|
|
|
181
180
|
if (isAiCommand) return;
|
|
182
|
-
if (!lastError) output.print(output.styles.error(' FAIL '), msg);
|
|
181
|
+
if (!lastError) output.print(output.output.styles.error(' FAIL '), msg);
|
|
183
182
|
lastError = err.message;
|
|
184
183
|
});
|
|
185
184
|
recorder.add('ask for next step', askForStep);
|
|
@@ -208,10 +207,4 @@ function completer(line) {
|
|
|
208
207
|
return [hits && hits.length ? hits : completions, line];
|
|
209
208
|
}
|
|
210
209
|
|
|
211
|
-
|
|
212
|
-
registeredVariables[name] = value;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
module.exports = pause;
|
|
216
|
-
|
|
217
|
-
module.exports.registerVariable = registerVariable;
|
|
210
|
+
export default pause;
|
package/lib/plugin/allure.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
console.log('Allure plugin was moved to @codeceptjs/allure-legacy. Please install it and update your config')
|
|
3
|
-
console.log()
|
|
4
|
-
console.log('npm install @codeceptjs/allure-legacy --save-dev')
|
|
5
|
-
console.log()
|
|
6
|
-
console.log('Then update your config to use it:')
|
|
7
|
-
console.log()
|
|
8
|
-
console.log('plugins: {')
|
|
9
|
-
console.log(' allure: {')
|
|
10
|
-
console.log(' enabled: true,')
|
|
11
|
-
console.log(
|
|
12
|
-
console.log(' }')
|
|
13
|
-
console.log('}')
|
|
14
|
-
console.log()
|
|
15
|
-
}
|
|
1
|
+
export default () => {
|
|
2
|
+
console.log('Allure plugin was moved to @codeceptjs/allure-legacy. Please install it and update your config');
|
|
3
|
+
console.log();
|
|
4
|
+
console.log('npm install @codeceptjs/allure-legacy --save-dev');
|
|
5
|
+
console.log();
|
|
6
|
+
console.log('Then update your config to use it:');
|
|
7
|
+
console.log();
|
|
8
|
+
console.log('plugins: {');
|
|
9
|
+
console.log(' allure: {');
|
|
10
|
+
console.log(' enabled: true,');
|
|
11
|
+
console.log(' require: \'@codeceptjs/allure-legacy\',');
|
|
12
|
+
console.log(' }');
|
|
13
|
+
console.log('}');
|
|
14
|
+
console.log();
|
|
15
|
+
};
|
package/lib/plugin/autoDelay.js
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const supportedHelpers = require('./standardActingHelpers').slice()
|
|
1
|
+
import Container from '../container.js';
|
|
2
|
+
import { store } from '../store.js';
|
|
3
|
+
import recorder from '../recorder.js';
|
|
4
|
+
import * as event from '../event.js';
|
|
5
|
+
import { log } from '../output.js';
|
|
7
6
|
|
|
8
|
-
const
|
|
7
|
+
const supportedHelpers = require('./standardActingHelpers.js').slice();
|
|
8
|
+
|
|
9
|
+
const methodsToDelay = [
|
|
10
|
+
'click',
|
|
11
|
+
'fillField',
|
|
12
|
+
'checkOption',
|
|
13
|
+
'pressKey',
|
|
14
|
+
'doubleClick',
|
|
15
|
+
'rightClick',
|
|
16
|
+
];
|
|
9
17
|
|
|
10
18
|
const defaultConfig = {
|
|
11
19
|
methods: methodsToDelay,
|
|
12
20
|
delayBefore: 100,
|
|
13
21
|
delayAfter: 200,
|
|
14
|
-
}
|
|
22
|
+
};
|
|
15
23
|
|
|
16
24
|
/**
|
|
17
25
|
*
|
|
@@ -51,42 +59,42 @@ const defaultConfig = {
|
|
|
51
59
|
* * `delayAfter`: put a delay after a command. 200ms by default
|
|
52
60
|
*
|
|
53
61
|
*/
|
|
54
|
-
|
|
55
|
-
supportedHelpers.push('REST')
|
|
56
|
-
const helpers = Container.helpers()
|
|
57
|
-
let helper
|
|
62
|
+
export default function (config) {
|
|
63
|
+
supportedHelpers.push('REST');
|
|
64
|
+
const helpers = Container.helpers();
|
|
65
|
+
let helper;
|
|
58
66
|
|
|
59
|
-
config = Object.assign(defaultConfig, config)
|
|
67
|
+
config = Object.assign(defaultConfig, config);
|
|
60
68
|
|
|
61
69
|
for (const helperName of supportedHelpers) {
|
|
62
70
|
if (Object.keys(helpers).indexOf(helperName) > -1) {
|
|
63
|
-
helper = helpers[helperName]
|
|
71
|
+
helper = helpers[helperName];
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
74
|
|
|
67
|
-
if (!helper) return // no helpers for auto-delay
|
|
75
|
+
if (!helper) return; // no helpers for auto-delay
|
|
68
76
|
|
|
69
77
|
event.dispatcher.on(event.step.before, (step) => {
|
|
70
|
-
if (config.methods.indexOf(step.helperMethod) < 0) return // skip non-actions
|
|
78
|
+
if (config.methods.indexOf(step.helperMethod) < 0) return; // skip non-actions
|
|
71
79
|
|
|
72
80
|
recorder.add('auto-delay', async () => {
|
|
73
|
-
if (store.debugMode) return // no need to delay in debug
|
|
74
|
-
log(`Delaying for ${config.delayBefore}ms`)
|
|
81
|
+
if (store.debugMode) return; // no need to delay in debug
|
|
82
|
+
log(`Delaying for ${config.delayBefore}ms`);
|
|
75
83
|
return new Promise((resolve) => {
|
|
76
|
-
setTimeout(resolve, config.delayBefore)
|
|
77
|
-
})
|
|
78
|
-
})
|
|
79
|
-
})
|
|
84
|
+
setTimeout(resolve, config.delayBefore);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
80
88
|
|
|
81
89
|
event.dispatcher.on(event.step.after, (step) => {
|
|
82
|
-
if (config.methods.indexOf(step.helperMethod) < 0) return // skip non-actions
|
|
90
|
+
if (config.methods.indexOf(step.helperMethod) < 0) return; // skip non-actions
|
|
83
91
|
|
|
84
92
|
recorder.add('auto-delay', async () => {
|
|
85
|
-
if (store.debugMode) return // no need to delay in debug
|
|
86
|
-
log(`Delaying for ${config.delayAfter}ms`)
|
|
93
|
+
if (store.debugMode) return; // no need to delay in debug
|
|
94
|
+
log(`Delaying for ${config.delayAfter}ms`);
|
|
87
95
|
return new Promise((resolve) => {
|
|
88
|
-
setTimeout(resolve, config.delayAfter)
|
|
89
|
-
})
|
|
90
|
-
})
|
|
91
|
-
})
|
|
96
|
+
setTimeout(resolve, config.delayAfter);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
92
100
|
}
|
package/lib/plugin/autoLogin.js
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const isAsyncFunction = require('../utils').isAsyncFunction
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileExists, isAsyncFunction } from '../utils.js';
|
|
4
|
+
import container from '../container.js';
|
|
5
|
+
import { store } from '../store.js';
|
|
6
|
+
import recorder from '../recorder.js';
|
|
7
|
+
import { debug } from '../output.js';
|
|
9
8
|
|
|
10
9
|
const defaultUser = {
|
|
11
|
-
fetch:
|
|
10
|
+
fetch: I => I.grabCookie(),
|
|
12
11
|
check: () => {},
|
|
13
12
|
restore: (I, cookies) => {
|
|
14
|
-
I.amOnPage('/') // open a page
|
|
15
|
-
I.setCookie(cookies)
|
|
13
|
+
I.amOnPage('/'); // open a page
|
|
14
|
+
I.setCookie(cookies);
|
|
16
15
|
},
|
|
17
|
-
}
|
|
16
|
+
};
|
|
18
17
|
|
|
19
18
|
const defaultConfig = {
|
|
20
19
|
saveToFile: false,
|
|
21
20
|
inject: 'login',
|
|
22
|
-
}
|
|
21
|
+
};
|
|
23
22
|
|
|
24
23
|
/**
|
|
25
24
|
* Logs user in for the first test and reuses session for next tests.
|
|
@@ -249,92 +248,88 @@ const defaultConfig = {
|
|
|
249
248
|
* })
|
|
250
249
|
*
|
|
251
250
|
*
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
config = Object.assign(defaultConfig, config)
|
|
255
|
-
Object.keys(config.users).map(
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
...config.users[u],
|
|
260
|
-
}),
|
|
261
|
-
)
|
|
251
|
+
*/
|
|
252
|
+
export default function (config) {
|
|
253
|
+
config = Object.assign(defaultConfig, config);
|
|
254
|
+
Object.keys(config.users).map(u => config.users[u] = {
|
|
255
|
+
...defaultUser,
|
|
256
|
+
...config.users[u],
|
|
257
|
+
});
|
|
262
258
|
|
|
263
259
|
if (config.saveToFile) {
|
|
264
260
|
// loading from file
|
|
265
261
|
for (const name in config.users) {
|
|
266
|
-
const fileName = path.join(global.output_dir, `${name}_session.json`)
|
|
267
|
-
if (!fileExists(fileName)) continue
|
|
268
|
-
const data = fs.readFileSync(fileName).toString()
|
|
262
|
+
const fileName = path.join(global.output_dir, `${name}_session.json`);
|
|
263
|
+
if (!fileExists(fileName)) continue;
|
|
264
|
+
const data = fs.readFileSync(fileName).toString();
|
|
269
265
|
try {
|
|
270
|
-
store[`${name}_session`] = JSON.parse(data)
|
|
266
|
+
store[`${name}_session`] = JSON.parse(data);
|
|
271
267
|
} catch (err) {
|
|
272
|
-
throw new Error(`Could not load session from ${fileName}\n${err}`)
|
|
268
|
+
throw new Error(`Could not load session from ${fileName}\n${err}`);
|
|
273
269
|
}
|
|
274
|
-
debug(`Loaded user session for ${name}`)
|
|
270
|
+
debug(`Loaded user session for ${name}`);
|
|
275
271
|
}
|
|
276
272
|
}
|
|
277
273
|
|
|
278
274
|
const loginFunction = async (name) => {
|
|
279
|
-
const userSession = config.users[name]
|
|
280
|
-
const I = container.support('I')
|
|
281
|
-
const cookies = store[`${name}_session`]
|
|
282
|
-
const shouldAwait =
|
|
283
|
-
|
|
275
|
+
const userSession = config.users[name];
|
|
276
|
+
const I = container.support('I');
|
|
277
|
+
const cookies = store[`${name}_session`];
|
|
278
|
+
const shouldAwait = isAsyncFunction(userSession.login)
|
|
279
|
+
|| isAsyncFunction(userSession.restore)
|
|
280
|
+
|| isAsyncFunction(userSession.check);
|
|
284
281
|
|
|
285
282
|
const loginAndSave = async () => {
|
|
286
283
|
if (shouldAwait) {
|
|
287
|
-
await userSession.login(I)
|
|
284
|
+
await userSession.login(I);
|
|
288
285
|
} else {
|
|
289
|
-
userSession.login(I)
|
|
286
|
+
userSession.login(I);
|
|
290
287
|
}
|
|
291
288
|
|
|
292
|
-
const cookies = await userSession.fetch(I)
|
|
289
|
+
const cookies = await userSession.fetch(I);
|
|
293
290
|
if (!cookies) {
|
|
294
|
-
debug(
|
|
295
|
-
return
|
|
291
|
+
debug('Cannot save user session with empty cookies from auto login\'s fetch method');
|
|
292
|
+
return;
|
|
296
293
|
}
|
|
297
294
|
if (config.saveToFile) {
|
|
298
|
-
debug(`Saved user session into file for ${name}`)
|
|
299
|
-
fs.writeFileSync(path.join(global.output_dir, `${name}_session.json`), JSON.stringify(cookies))
|
|
295
|
+
debug(`Saved user session into file for ${name}`);
|
|
296
|
+
fs.writeFileSync(path.join(global.output_dir, `${name}_session.json`), JSON.stringify(cookies));
|
|
300
297
|
}
|
|
301
|
-
store[`${name}_session`] = cookies
|
|
302
|
-
}
|
|
298
|
+
store[`${name}_session`] = cookies;
|
|
299
|
+
};
|
|
303
300
|
|
|
304
|
-
if (!cookies) return loginAndSave()
|
|
301
|
+
if (!cookies) return loginAndSave();
|
|
305
302
|
|
|
306
|
-
recorder.session.start('check login')
|
|
303
|
+
recorder.session.start('check login');
|
|
307
304
|
if (shouldAwait) {
|
|
308
|
-
await userSession.restore(I, cookies)
|
|
309
|
-
await userSession.check(I, cookies)
|
|
305
|
+
await userSession.restore(I, cookies);
|
|
306
|
+
await userSession.check(I, cookies);
|
|
310
307
|
} else {
|
|
311
|
-
userSession.restore(I, cookies)
|
|
312
|
-
userSession.check(I, cookies)
|
|
308
|
+
userSession.restore(I, cookies);
|
|
309
|
+
userSession.check(I, cookies);
|
|
313
310
|
}
|
|
314
311
|
recorder.session.catch((err) => {
|
|
315
|
-
debug(`Failed auto login for ${name} due to ${err}`)
|
|
316
|
-
debug('Logging in again')
|
|
317
|
-
recorder.session.start('auto login')
|
|
318
|
-
return loginAndSave()
|
|
319
|
-
.
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
.
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
})
|
|
328
|
-
})
|
|
312
|
+
debug(`Failed auto login for ${name} due to ${err}`);
|
|
313
|
+
debug('Logging in again');
|
|
314
|
+
recorder.session.start('auto login');
|
|
315
|
+
return loginAndSave().then(() => {
|
|
316
|
+
recorder.add(() => recorder.session.restore('auto login'));
|
|
317
|
+
recorder.catch(() => debug('continue'));
|
|
318
|
+
}).catch((err) => {
|
|
319
|
+
recorder.session.restore('auto login');
|
|
320
|
+
recorder.session.restore('check login');
|
|
321
|
+
recorder.throw(err);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
329
324
|
recorder.add(() => {
|
|
330
|
-
recorder.session.restore('check login')
|
|
331
|
-
})
|
|
325
|
+
recorder.session.restore('check login');
|
|
326
|
+
});
|
|
332
327
|
|
|
333
|
-
return recorder.promise()
|
|
334
|
-
}
|
|
328
|
+
return recorder.promise();
|
|
329
|
+
};
|
|
335
330
|
|
|
336
331
|
// adding this to DI container
|
|
337
|
-
const support = {}
|
|
338
|
-
support[config.inject] = loginFunction
|
|
339
|
-
container.append({ support })
|
|
332
|
+
const support = {};
|
|
333
|
+
support[config.inject] = loginFunction;
|
|
334
|
+
container.append({ support });
|
|
340
335
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import * as event from '../event.js';
|
|
2
|
+
import recorder from '../recorder.js';
|
|
3
|
+
import { MetaStep } from '../step.js';
|
|
4
4
|
|
|
5
|
-
let currentCommentStep
|
|
5
|
+
let currentCommentStep;
|
|
6
6
|
|
|
7
|
-
const defaultGlobalName = '__'
|
|
7
|
+
const defaultGlobalName = '__';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Add descriptive nested steps for your tests:
|
|
@@ -99,38 +99,38 @@ const defaultGlobalName = '__'
|
|
|
99
99
|
* });
|
|
100
100
|
* ```
|
|
101
101
|
*/
|
|
102
|
-
|
|
102
|
+
export default function commentStep(config) {
|
|
103
103
|
event.dispatcher.on(event.test.started, () => {
|
|
104
|
-
currentCommentStep = null
|
|
105
|
-
})
|
|
104
|
+
currentCommentStep = null;
|
|
105
|
+
});
|
|
106
106
|
|
|
107
107
|
event.dispatcher.on(event.step.started, (step) => {
|
|
108
108
|
if (currentCommentStep) {
|
|
109
|
-
const metaStep = getRootMetaStep(step)
|
|
109
|
+
const metaStep = getRootMetaStep(step);
|
|
110
110
|
|
|
111
111
|
if (metaStep !== currentCommentStep) {
|
|
112
|
-
metaStep.metaStep = currentCommentStep
|
|
112
|
+
metaStep.metaStep = currentCommentStep;
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
-
})
|
|
115
|
+
});
|
|
116
116
|
|
|
117
117
|
if (config.registerGlobal) {
|
|
118
118
|
if (config.registerGlobal === true) {
|
|
119
|
-
config.registerGlobal = defaultGlobalName
|
|
119
|
+
config.registerGlobal = defaultGlobalName;
|
|
120
120
|
}
|
|
121
|
-
global[config.registerGlobal] = setCommentString
|
|
121
|
+
global[config.registerGlobal] = setCommentString;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
return setCommentString
|
|
124
|
+
return setCommentString;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
function getRootMetaStep(step) {
|
|
128
|
-
if (step.metaStep) return getRootMetaStep(step.metaStep)
|
|
129
|
-
return step
|
|
128
|
+
if (step.metaStep) return getRootMetaStep(step.metaStep);
|
|
129
|
+
return step;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
function setCommentString(string) {
|
|
133
133
|
recorder.add('set comment metastep', () => {
|
|
134
|
-
currentCommentStep = new MetaStep(String.raw(string), '')
|
|
135
|
-
})
|
|
134
|
+
currentCommentStep = new MetaStep(String.raw(string), '');
|
|
135
|
+
});
|
|
136
136
|
}
|