codeceptjs 3.3.2 → 3.3.5-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/CHANGELOG.md +44 -2
- package/docs/api.md +4 -0
- package/docs/basics.md +2 -0
- package/docs/bdd.md +12 -0
- package/docs/build/JSONResponse.js +44 -3
- package/docs/build/Playwright.js +63 -40
- package/docs/build/Puppeteer.js +54 -43
- package/docs/build/REST.js +23 -9
- package/docs/build/WebDriver.js +39 -30
- package/docs/changelog.md +6 -2
- package/docs/community-helpers.md +1 -0
- package/docs/configuration.md +21 -18
- package/docs/helpers/Appium.md +0 -723
- package/docs/helpers/JSONResponse.md +24 -0
- package/docs/helpers/Playwright.md +276 -264
- package/docs/helpers/Puppeteer.md +230 -222
- package/docs/helpers/REST.md +21 -6
- package/docs/helpers/WebDriver.md +265 -259
- package/docs/plugins.md +41 -1
- package/docs/reports.md +11 -0
- package/docs/secrets.md +30 -0
- package/docs/wiki/.git/FETCH_HEAD +1 -0
- package/docs/wiki/.git/HEAD +1 -0
- package/docs/wiki/.git/ORIG_HEAD +1 -0
- package/docs/wiki/.git/config +11 -0
- package/docs/wiki/.git/description +1 -0
- package/docs/wiki/.git/hooks/applypatch-msg.sample +15 -0
- package/docs/wiki/.git/hooks/commit-msg.sample +24 -0
- package/docs/wiki/.git/hooks/fsmonitor-watchman.sample +173 -0
- package/docs/wiki/.git/hooks/post-update.sample +8 -0
- package/docs/wiki/.git/hooks/pre-applypatch.sample +14 -0
- package/docs/wiki/.git/hooks/pre-commit.sample +49 -0
- package/docs/wiki/.git/hooks/pre-merge-commit.sample +13 -0
- package/docs/wiki/.git/hooks/pre-push.sample +53 -0
- package/docs/wiki/.git/hooks/pre-rebase.sample +169 -0
- package/docs/wiki/.git/hooks/pre-receive.sample +24 -0
- package/docs/wiki/.git/hooks/prepare-commit-msg.sample +42 -0
- package/docs/wiki/.git/hooks/push-to-checkout.sample +78 -0
- package/docs/wiki/.git/hooks/update.sample +128 -0
- package/docs/wiki/.git/index +0 -0
- package/docs/wiki/.git/info/exclude +6 -0
- package/docs/wiki/.git/logs/HEAD +1 -0
- package/docs/wiki/.git/logs/refs/heads/master +1 -0
- package/docs/wiki/.git/logs/refs/remotes/origin/HEAD +1 -0
- package/docs/wiki/.git/objects/pack/pack-5938044f9d30daf1c195fda4dec1d54850933935.idx +0 -0
- package/docs/wiki/.git/objects/pack/pack-5938044f9d30daf1c195fda4dec1d54850933935.pack +0 -0
- package/docs/wiki/.git/packed-refs +2 -0
- package/docs/wiki/.git/refs/heads/master +1 -0
- package/docs/wiki/.git/refs/remotes/origin/HEAD +1 -0
- package/docs/wiki/Community-Helpers-&-Plugins.md +7 -3
- package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +29 -0
- package/docs/wiki/Examples.md +39 -48
- package/docs/wiki/Release-Process.md +8 -8
- package/docs/wiki/Tests.md +62 -60
- package/docs/wiki/Upgrading-to-CodeceptJS-3.md +2 -2
- package/lib/cli.js +1 -1
- package/lib/command/generate.js +3 -0
- package/lib/command/init.js +83 -24
- package/lib/command/interactive.js +1 -1
- package/lib/command/run-workers.js +1 -1
- package/lib/command/workers/runTests.js +15 -0
- package/lib/helper/JSONResponse.js +44 -3
- package/lib/helper/Playwright.js +63 -40
- package/lib/helper/Puppeteer.js +54 -43
- package/lib/helper/REST.js +23 -9
- package/lib/helper/WebDriver.js +39 -30
- package/lib/interfaces/gherkin.js +1 -1
- package/lib/output.js +4 -0
- package/lib/plugin/customLocator.js +50 -3
- package/lib/plugin/retryFailedStep.js +1 -1
- package/lib/plugin/retryTo.js +1 -8
- package/lib/secret.js +31 -1
- package/lib/step.js +22 -10
- package/lib/utils.js +1 -6
- package/package.json +4 -4
- package/typings/index.d.ts +158 -0
- package/typings/types.d.ts +367 -96
package/lib/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ class Cli extends Base {
|
|
|
19
19
|
if (opts.debug) level = 2;
|
|
20
20
|
if (opts.verbose) level = 3;
|
|
21
21
|
output.level(level);
|
|
22
|
-
output.print(`CodeceptJS v${require('./codecept').version()}`);
|
|
22
|
+
output.print(`CodeceptJS v${require('./codecept').version()} ${output.standWithUkraine()}`);
|
|
23
23
|
output.print(`Using test root "${global.codecept_dir}"`);
|
|
24
24
|
|
|
25
25
|
const showSteps = level >= 1;
|
package/lib/command/generate.js
CHANGED
|
@@ -33,6 +33,7 @@ module.exports.test = function (genPath) {
|
|
|
33
33
|
type: 'input',
|
|
34
34
|
name: 'feature',
|
|
35
35
|
message: 'Feature which is being tested (ex: account, login, etc)',
|
|
36
|
+
validate: (val) => !!val,
|
|
36
37
|
},
|
|
37
38
|
{
|
|
38
39
|
type: 'input',
|
|
@@ -85,6 +86,7 @@ module.exports.pageObject = function (genPath, opts) {
|
|
|
85
86
|
type: 'input',
|
|
86
87
|
name: 'name',
|
|
87
88
|
message: `Name of a ${kind} object`,
|
|
89
|
+
validate: (val) => !!val,
|
|
88
90
|
}, {
|
|
89
91
|
type: 'input',
|
|
90
92
|
name: 'filename',
|
|
@@ -156,6 +158,7 @@ module.exports.helper = function (genPath) {
|
|
|
156
158
|
type: 'input',
|
|
157
159
|
name: 'name',
|
|
158
160
|
message: 'Name of a Helper',
|
|
161
|
+
validate: (val) => !!val,
|
|
159
162
|
}, {
|
|
160
163
|
type: 'input',
|
|
161
164
|
name: 'filename',
|
package/lib/command/init.js
CHANGED
|
@@ -18,8 +18,6 @@ const defaultConfig = {
|
|
|
18
18
|
output: '',
|
|
19
19
|
helpers: {},
|
|
20
20
|
include: {},
|
|
21
|
-
bootstrap: null,
|
|
22
|
-
mocha: {},
|
|
23
21
|
};
|
|
24
22
|
|
|
25
23
|
const helpers = ['Playwright', 'WebDriver', 'Puppeteer', 'REST', 'GraphQL', 'Appium', 'TestCafe', 'Nightmare'];
|
|
@@ -28,10 +26,13 @@ const translations = Object.keys(require('../../translations'));
|
|
|
28
26
|
const noTranslation = 'English (no localization)';
|
|
29
27
|
translations.unshift(noTranslation);
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
const packages = [];
|
|
30
|
+
let isTypeScript = false;
|
|
31
|
+
let extension = 'js';
|
|
32
32
|
|
|
33
33
|
const configHeader = `const { setHeadlessWhen, setCommonPlugins } = require('@codeceptjs/configure');
|
|
34
34
|
|
|
35
|
+
|
|
35
36
|
// turn on headless mode when running with HEADLESS=true environment variable
|
|
36
37
|
// export HEADLESS=true && npx codeceptjs run
|
|
37
38
|
setHeadlessWhen(process.env.HEADLESS);
|
|
@@ -53,6 +54,10 @@ module.exports = function() {
|
|
|
53
54
|
}
|
|
54
55
|
`;
|
|
55
56
|
|
|
57
|
+
const tsNodeRequired = `require('ts-node/register');
|
|
58
|
+
|
|
59
|
+
`;
|
|
60
|
+
|
|
56
61
|
module.exports = function (initPath) {
|
|
57
62
|
const testsPath = getTestRoot(initPath);
|
|
58
63
|
|
|
@@ -80,11 +85,23 @@ module.exports = function (initPath) {
|
|
|
80
85
|
return;
|
|
81
86
|
}
|
|
82
87
|
|
|
88
|
+
const typeScriptconfigFile = path.join(testsPath, 'codecept.conf.ts');
|
|
89
|
+
if (fileExists(typeScriptconfigFile)) {
|
|
90
|
+
error(`Config is already created at ${typeScriptconfigFile}`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
83
94
|
inquirer.prompt([
|
|
95
|
+
{
|
|
96
|
+
name: 'typescript',
|
|
97
|
+
type: 'confirm',
|
|
98
|
+
default: false,
|
|
99
|
+
message: 'Do you plan to write tests in TypeScript?',
|
|
100
|
+
},
|
|
84
101
|
{
|
|
85
102
|
name: 'tests',
|
|
86
103
|
type: 'input',
|
|
87
|
-
default:
|
|
104
|
+
default: (answers) => `./*_test.${answers.typescript ? 'ts' : 'js'}`,
|
|
88
105
|
message: 'Where are your tests located?',
|
|
89
106
|
},
|
|
90
107
|
{
|
|
@@ -105,23 +122,29 @@ module.exports = function (initPath) {
|
|
|
105
122
|
choices: translations,
|
|
106
123
|
},
|
|
107
124
|
]).then((result) => {
|
|
125
|
+
if (result.typescript === true) {
|
|
126
|
+
isTypeScript = true;
|
|
127
|
+
extension = isTypeScript === true ? 'ts' : 'js';
|
|
128
|
+
packages.push('typescript');
|
|
129
|
+
packages.push('ts-node');
|
|
130
|
+
packages.push('@types/node');
|
|
131
|
+
}
|
|
132
|
+
|
|
108
133
|
const config = defaultConfig;
|
|
109
134
|
config.name = testsPath.split(path.sep).pop();
|
|
110
135
|
config.output = result.output;
|
|
111
136
|
|
|
112
137
|
config.tests = result.tests;
|
|
138
|
+
if (isTypeScript) {
|
|
139
|
+
config.tests = `${config.tests.replace(/\.js$/, `.${extension}`)}`;
|
|
140
|
+
}
|
|
141
|
+
|
|
113
142
|
// create a directory tests if it is included in tests path
|
|
114
143
|
const matchResults = config.tests.match(/[^*.]+/);
|
|
115
144
|
if (matchResults) {
|
|
116
145
|
mkdirp.sync(path.join(testsPath, matchResults[0]));
|
|
117
146
|
}
|
|
118
147
|
|
|
119
|
-
// append file mask to the end of tests
|
|
120
|
-
if (!config.tests.match(/\*(.*?)$/)) {
|
|
121
|
-
config.tests = `${config.tests.replace(/\/+$/, '')}/*_test.js`;
|
|
122
|
-
print(`Adding default test mask: ${config.tests}`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
148
|
if (result.translation !== noTranslation) config.translation = result.translation;
|
|
126
149
|
|
|
127
150
|
const helperName = result.helper;
|
|
@@ -132,7 +155,7 @@ module.exports = function (initPath) {
|
|
|
132
155
|
try {
|
|
133
156
|
const Helper = require(`../helper/${helperName}`);
|
|
134
157
|
if (Helper._checkRequirements) {
|
|
135
|
-
packages
|
|
158
|
+
packages.concat(Helper._checkRequirements());
|
|
136
159
|
}
|
|
137
160
|
|
|
138
161
|
if (!Helper._config()) return;
|
|
@@ -148,20 +171,28 @@ module.exports = function (initPath) {
|
|
|
148
171
|
|
|
149
172
|
const finish = async () => {
|
|
150
173
|
// create steps file by default
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
174
|
+
if (!isTypeScript) { // no extra step file for typescript (as it doesn't match TS conventions)
|
|
175
|
+
const stepFile = `./steps_file.${extension}`;
|
|
176
|
+
fs.writeFileSync(path.join(testsPath, stepFile), defaultActor);
|
|
177
|
+
config.include.I = stepFile;
|
|
178
|
+
print(`Steps file created at ${stepFile}`);
|
|
179
|
+
}
|
|
155
180
|
|
|
156
|
-
let configSource = beautify(
|
|
181
|
+
let configSource = beautify(`/** @type {CodeceptJS.MainConfig} */\nexports.config = ${inspect(config, false, 4, false)}`);
|
|
157
182
|
|
|
158
183
|
if (require.resolve('@codeceptjs/configure') && isLocal && !initPath) {
|
|
159
184
|
// prepend @codeceptjs/configure only when this module can be required in config
|
|
160
185
|
configSource = configHeader + configSource;
|
|
161
186
|
}
|
|
162
187
|
|
|
163
|
-
|
|
164
|
-
|
|
188
|
+
if (isTypeScript) {
|
|
189
|
+
configSource = `${tsNodeRequired}\n${configSource}`;
|
|
190
|
+
fs.writeFileSync(typeScriptconfigFile, configSource, 'utf-8');
|
|
191
|
+
print(`Config created at ${typeScriptconfigFile}`);
|
|
192
|
+
} else {
|
|
193
|
+
fs.writeFileSync(configFile, configSource, 'utf-8');
|
|
194
|
+
print(`Config created at ${configFile}`);
|
|
195
|
+
}
|
|
165
196
|
|
|
166
197
|
if (config.output) {
|
|
167
198
|
if (!fileExists(config.output)) {
|
|
@@ -177,13 +208,41 @@ module.exports = function (initPath) {
|
|
|
177
208
|
allowJs: true,
|
|
178
209
|
},
|
|
179
210
|
};
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
211
|
+
|
|
212
|
+
const tsconfig = {
|
|
213
|
+
'ts-node': {
|
|
214
|
+
files: true,
|
|
215
|
+
},
|
|
216
|
+
compilerOptions: {
|
|
217
|
+
target: 'es2018',
|
|
218
|
+
lib: ['es2018', 'DOM'],
|
|
219
|
+
esModuleInterop: true,
|
|
220
|
+
module: 'commonjs',
|
|
221
|
+
strictNullChecks: false,
|
|
222
|
+
types: ['codeceptjs', 'node'],
|
|
223
|
+
declaration: true,
|
|
224
|
+
skipLibCheck: true,
|
|
225
|
+
},
|
|
226
|
+
exclude: ['node_modules'],
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
if (isTypeScript) {
|
|
230
|
+
const tsconfigJson = beautify(JSON.stringify(tsconfig));
|
|
231
|
+
const tsconfigFile = path.join(testsPath, 'tsconfig.json');
|
|
232
|
+
if (fileExists(tsconfigFile)) {
|
|
233
|
+
print(`tsconfig.json already exists at ${tsconfigFile}`);
|
|
234
|
+
} else {
|
|
235
|
+
fs.writeFileSync(tsconfigFile, tsconfigJson);
|
|
236
|
+
}
|
|
184
237
|
} else {
|
|
185
|
-
|
|
186
|
-
|
|
238
|
+
const jsconfigJson = beautify(JSON.stringify(jsconfig));
|
|
239
|
+
const jsconfigFile = path.join(testsPath, 'jsconfig.json');
|
|
240
|
+
if (fileExists(jsconfigFile)) {
|
|
241
|
+
print(`jsconfig.json already exists at ${jsconfigFile}`);
|
|
242
|
+
} else {
|
|
243
|
+
fs.writeFileSync(jsconfigFile, jsconfigJson);
|
|
244
|
+
print(`Intellisense enabled in ${jsconfigFile}`);
|
|
245
|
+
}
|
|
187
246
|
}
|
|
188
247
|
|
|
189
248
|
const generateDefinitionsManually = colors.bold(`To get auto-completion support, please generate type definitions: ${colors.green('npx codeceptjs def')}`);
|
|
@@ -21,7 +21,7 @@ module.exports = async function (path, options) {
|
|
|
21
21
|
|
|
22
22
|
if (options.verbose) output.level(3);
|
|
23
23
|
|
|
24
|
-
output.print('
|
|
24
|
+
output.print('Starting interactive shell for current suite...');
|
|
25
25
|
recorder.start();
|
|
26
26
|
event.emit(event.suite.before, {
|
|
27
27
|
fullTitle: () => 'Interactive Shell',
|
|
@@ -25,7 +25,7 @@ module.exports = async function (workerCount, options) {
|
|
|
25
25
|
|
|
26
26
|
const numberOfWorkers = parseInt(workerCount, 10);
|
|
27
27
|
|
|
28
|
-
output.print(`CodeceptJS v${require('../codecept').version()}`);
|
|
28
|
+
output.print(`CodeceptJS v${require('../codecept').version()} ${output.standWithUkraine()}`);
|
|
29
29
|
output.print(`Running tests in ${output.styles.bold(numberOfWorkers)} workers...`);
|
|
30
30
|
output.print();
|
|
31
31
|
|
|
@@ -114,6 +114,13 @@ function initializeListeners() {
|
|
|
114
114
|
parent.title = test.parent.title;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
if (test.opts) {
|
|
118
|
+
Object.keys(test.opts).forEach(k => {
|
|
119
|
+
if (typeof test.opts[k] === 'object') delete test.opts[k];
|
|
120
|
+
if (typeof test.opts[k] === 'function') delete test.opts[k];
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
117
124
|
return {
|
|
118
125
|
opts: test.opts || {},
|
|
119
126
|
tags: test.tags || [],
|
|
@@ -148,6 +155,14 @@ function initializeListeners() {
|
|
|
148
155
|
if (step.metaStep) {
|
|
149
156
|
parent.title = step.metaStep.actor;
|
|
150
157
|
}
|
|
158
|
+
|
|
159
|
+
if (step.opts) {
|
|
160
|
+
Object.keys(step.opts).forEach(k => {
|
|
161
|
+
if (typeof step.opts[k] === 'object') delete step.opts[k];
|
|
162
|
+
if (typeof step.opts[k] === 'function') delete step.opts[k];
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
151
166
|
return {
|
|
152
167
|
opts: step.opts || {},
|
|
153
168
|
workerIndex,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
1
2
|
const chai = require('chai');
|
|
2
3
|
const joi = require('joi');
|
|
3
4
|
const chaiDeepMatch = require('chai-deep-match');
|
|
@@ -173,12 +174,30 @@ class JSONResponse extends Helper {
|
|
|
173
174
|
*
|
|
174
175
|
* I.seeResponseContainsJson({ user: { email: 'jon@doe.com' } });
|
|
175
176
|
* ```
|
|
177
|
+
* If an array is received, checks that at least one element contains JSON
|
|
178
|
+
* ```js
|
|
179
|
+
* // response.data == [{ user: { name: 'jon', email: 'jon@doe.com' } }]
|
|
180
|
+
*
|
|
181
|
+
* I.seeResponseContainsJson({ user: { email: 'jon@doe.com' } });
|
|
182
|
+
* ```
|
|
176
183
|
*
|
|
177
184
|
* @param {object} json
|
|
178
185
|
*/
|
|
179
186
|
seeResponseContainsJson(json = {}) {
|
|
180
187
|
this._checkResponseReady();
|
|
181
|
-
|
|
188
|
+
if (Array.isArray(this.response.data)) {
|
|
189
|
+
let fails = 0;
|
|
190
|
+
for (const el of this.response.data) {
|
|
191
|
+
try {
|
|
192
|
+
expect(el).to.deep.match(json);
|
|
193
|
+
} catch (err) {
|
|
194
|
+
fails++;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
assert.ok(fails < this.response.data.length, `No elements in array matched ${JSON.stringify(json)}`);
|
|
198
|
+
} else {
|
|
199
|
+
expect(this.response.data).to.deep.match(json);
|
|
200
|
+
}
|
|
182
201
|
}
|
|
183
202
|
|
|
184
203
|
/**
|
|
@@ -189,12 +208,22 @@ class JSONResponse extends Helper {
|
|
|
189
208
|
*
|
|
190
209
|
* I.dontSeeResponseContainsJson({ user: 2 });
|
|
191
210
|
* ```
|
|
211
|
+
* If an array is received, checks that no element of array contains json:
|
|
212
|
+
* ```js
|
|
213
|
+
* // response.data == [{ user: 1 }, { user: 3 }]
|
|
214
|
+
*
|
|
215
|
+
* I.dontSeeResponseContainsJson({ user: 2 });
|
|
216
|
+
* ```
|
|
192
217
|
*
|
|
193
218
|
* @param {object} json
|
|
194
219
|
*/
|
|
195
220
|
dontSeeResponseContainsJson(json = {}) {
|
|
196
221
|
this._checkResponseReady();
|
|
197
|
-
|
|
222
|
+
if (Array.isArray(this.response.data)) {
|
|
223
|
+
this.response.data.forEach(data => expect(data).not.to.deep.match(json));
|
|
224
|
+
} else {
|
|
225
|
+
expect(this.response.data).not.to.deep.match(json);
|
|
226
|
+
}
|
|
198
227
|
}
|
|
199
228
|
|
|
200
229
|
/**
|
|
@@ -206,11 +235,23 @@ class JSONResponse extends Helper {
|
|
|
206
235
|
* I.seeResponseContainsKeys(['user']);
|
|
207
236
|
* ```
|
|
208
237
|
*
|
|
238
|
+
* If an array is received, check is performed for each element of array:
|
|
239
|
+
*
|
|
240
|
+
* ```js
|
|
241
|
+
* // response.data == [{ user: 'jon' }, { user: 'matt'}]
|
|
242
|
+
*
|
|
243
|
+
* I.seeResponseContainsKeys(['user']);
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
209
246
|
* @param {array} keys
|
|
210
247
|
*/
|
|
211
248
|
seeResponseContainsKeys(keys = []) {
|
|
212
249
|
this._checkResponseReady();
|
|
213
|
-
|
|
250
|
+
if (Array.isArray(this.response.data)) {
|
|
251
|
+
this.response.data.forEach(data => expect(data).to.include.keys(keys));
|
|
252
|
+
} else {
|
|
253
|
+
expect(this.response.data).to.include.keys(keys);
|
|
254
|
+
}
|
|
214
255
|
}
|
|
215
256
|
|
|
216
257
|
/**
|
package/lib/helper/Playwright.js
CHANGED
|
@@ -44,6 +44,46 @@ const {
|
|
|
44
44
|
} = require('./extras/PlaywrightRestartOpts');
|
|
45
45
|
const { createValueEngine, createDisabledEngine } = require('./extras/PlaywrightPropEngine');
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* ## Configuration
|
|
49
|
+
*
|
|
50
|
+
* This helper should be configured in codecept.conf.js
|
|
51
|
+
*
|
|
52
|
+
* @typedef PlaywrightConfig
|
|
53
|
+
* @type {object}
|
|
54
|
+
* @prop {string} url - base url of website to be tested
|
|
55
|
+
* @prop {string} browser - a browser to test on, either: `chromium`, `firefox`, `webkit`, `electron`. Default: chromium.
|
|
56
|
+
* @prop {boolean} [show=false] - show browser window.
|
|
57
|
+
* @prop {string|boolean} [restart=false] - restart strategy between tests. Possible values:
|
|
58
|
+
* * 'context' or **false** - restarts [browser context](https://playwright.dev/docs/api/class-browsercontext) but keeps running browser. Recommended by Playwright team to keep tests isolated.
|
|
59
|
+
* * 'browser' or **true** - closes browser and opens it again between tests.
|
|
60
|
+
* * 'session' or 'keep' - keeps browser context and session, but cleans up cookies and localStorage between tests. The fastest option when running tests in windowed mode. Works with `keepCookies` and `keepBrowserState` options. This behavior was default before CodeceptJS 3.1
|
|
61
|
+
* @prop {number} [timeout=1000] - - [timeout](https://playwright.dev/docs/api/class-page#page-set-default-timeout) in ms of all Playwright actions .
|
|
62
|
+
* @prop {boolean} [disableScreenshots=false] - don't save screenshot on failure.
|
|
63
|
+
* @prop {any} [emulate] - browser in device emulation mode.
|
|
64
|
+
* @prop {boolean} [video=false] - enables video recording for failed tests; videos are saved into `output/videos` folder
|
|
65
|
+
* @prop {boolean} [trace=false] - record [tracing information](https://playwright.dev/docs/trace-viewer) with screenshots and snapshots.
|
|
66
|
+
* @prop {boolean} [fullPageScreenshots=false] - make full page screenshots on failure.
|
|
67
|
+
* @prop {boolean} [uniqueScreenshotNames=false] - option to prevent screenshot override if you have scenarios with the same name in different suites.
|
|
68
|
+
* @prop {boolean} [keepBrowserState=false] - keep browser state between tests when `restart` is set to 'session'.
|
|
69
|
+
* @prop {boolean} [keepCookies=false] - keep cookies between tests when `restart` is set to 'session'.
|
|
70
|
+
* @prop {number} [waitForAction] - how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
|
|
71
|
+
* @prop {number} [waitForNavigation] - When to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle`. Choose one of those options is possible. See [Playwright API](https://github.com/microsoft/playwright/blob/main/docs/api.md#pagewaitfornavigationoptions).
|
|
72
|
+
* @prop {number} [pressKeyDelay=10] - Delay between key presses in ms. Used when calling Playwrights page.type(...) in fillField/appendField
|
|
73
|
+
* @prop {number} [getPageTimeout] - config option to set maximum navigation time in milliseconds.
|
|
74
|
+
* @prop {number} [waitForTimeout] - default wait* timeout in ms. Default: 1000.
|
|
75
|
+
* @prop {object} [basicAuth] - the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
76
|
+
* @prop {string} [windowSize] - default window size. Set a dimension like `640x480`.
|
|
77
|
+
* @prop {string} [colorScheme] - default color scheme. Possible values: `dark` | `light` | `no-preference`.
|
|
78
|
+
* @prop {string} [userAgent] - user-agent string.
|
|
79
|
+
* @prop {string} [locale] - locale string. Example: 'en-GB', 'de-DE', 'fr-FR', ...
|
|
80
|
+
* @prop {boolean} [manualStart] - do not start browser before a test, start it manually inside a helper with `this.helpers["Playwright"]._startBrowser()`.
|
|
81
|
+
* @prop {object} [chromium] - pass additional chromium options
|
|
82
|
+
* @prop {object} [electron] - (pass additional electron options
|
|
83
|
+
* @prop {any} [channel] - (While Playwright can operate against the stock Google Chrome and Microsoft Edge browsers available on the machine. In particular, current Playwright version will support Stable and Beta channels of these browsers. See [Google Chrome & Microsoft Edge](https://playwright.dev/docs/browsers/#google-chrome--microsoft-edge).
|
|
84
|
+
*/
|
|
85
|
+
const config = {};
|
|
86
|
+
|
|
47
87
|
/**
|
|
48
88
|
* Uses [Playwright](https://github.com/microsoft/playwright) library to run tests inside:
|
|
49
89
|
*
|
|
@@ -65,46 +105,14 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
65
105
|
*
|
|
66
106
|
* Using playwright-core package, will prevent the download of browser binaries and allow connecting to an existing browser installation or for connecting to a remote one.
|
|
67
107
|
*
|
|
68
|
-
* ## Configuration
|
|
69
108
|
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* * `url`: base url of website to be tested
|
|
73
|
-
* * `browser`: a browser to test on, either: `chromium`, `firefox`, `webkit`, `electron`. Default: chromium.
|
|
74
|
-
* * `show`: (optional, default: false) - show browser window.
|
|
75
|
-
* * `restart`: (optional, default: false) - restart strategy between tests. Possible values:
|
|
76
|
-
* * 'context' or **false** - restarts [browser context](https://playwright.dev/docs/api/class-browsercontext) but keeps running browser. Recommended by Playwright team to keep tests isolated.
|
|
77
|
-
* * 'browser' or **true** - closes browser and opens it again between tests.
|
|
78
|
-
* * 'session' or 'keep' - keeps browser context and session, but cleans up cookies and localStorage between tests. The fastest option when running tests in windowed mode. Works with `keepCookies` and `keepBrowserState` options. This behavior was default before CodeceptJS 3.1
|
|
79
|
-
* * `timeout`: (optional, default: 1000) - [timeout](https://playwright.dev/docs/api/class-page#page-set-default-timeout) in ms of all Playwright actions .
|
|
80
|
-
* * `disableScreenshots`: (optional, default: false) - don't save screenshot on failure.
|
|
81
|
-
* * `emulate`: (optional, default: {}) launch browser in device emulation mode.
|
|
82
|
-
* * `video`: (optional, default: false) enables video recording for failed tests; videos are saved into `output/videos` folder
|
|
83
|
-
* * `trace`: (optional, default: false) record [tracing information](https://playwright.dev/docs/trace-viewer) with screenshots and snapshots.
|
|
84
|
-
* * `fullPageScreenshots` (optional, default: false) - make full page screenshots on failure.
|
|
85
|
-
* * `uniqueScreenshotNames`: (optional, default: false) - option to prevent screenshot override if you have scenarios with the same name in different suites.
|
|
86
|
-
* * `keepBrowserState`: (optional, default: false) - keep browser state between tests when `restart` is set to 'session'.
|
|
87
|
-
* * `keepCookies`: (optional, default: false) - keep cookies between tests when `restart` is set to 'session'.
|
|
88
|
-
* * `waitForAction`: (optional) how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
|
|
89
|
-
* * `waitForNavigation`: (optional, default: 'load'). When to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle`. Choose one of those options is possible. See [Playwright API](https://github.com/microsoft/playwright/blob/main/docs/api.md#pagewaitfornavigationoptions).
|
|
90
|
-
* * `pressKeyDelay`: (optional, default: '10'). Delay between key presses in ms. Used when calling Playwrights page.type(...) in fillField/appendField
|
|
91
|
-
* * `getPageTimeout` (optional, default: '0') config option to set maximum navigation time in milliseconds.
|
|
92
|
-
* * `waitForTimeout`: (optional) default wait* timeout in ms. Default: 1000.
|
|
93
|
-
* * `basicAuth`: (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
94
|
-
* * `windowSize`: (optional) default window size. Set a dimension like `640x480`.
|
|
95
|
-
* * `colorScheme`: (optional) default color scheme. Possible values: `dark` | `light` | `no-preference`.
|
|
96
|
-
* * `userAgent`: (optional) user-agent string.
|
|
97
|
-
* * `locale`: (optional) locale string. Example: 'en-GB', 'de-DE', 'fr-FR', ...
|
|
98
|
-
* * `manualStart`: (optional, default: false) - do not start browser before a test, start it manually inside a helper with `this.helpers["Playwright"]._startBrowser()`.
|
|
99
|
-
* * `chromium`: (optional) pass additional chromium options
|
|
100
|
-
* * `electron`: (optional) pass additional electron options
|
|
101
|
-
* * `channel`: (optional) While Playwright can operate against the stock Google Chrome and Microsoft Edge browsers available on the machine. In particular, current Playwright version will support Stable and Beta channels of these browsers. See [Google Chrome & Microsoft Edge](https://playwright.dev/docs/browsers/#google-chrome--microsoft-edge).
|
|
109
|
+
* <!-- configuration -->
|
|
102
110
|
*
|
|
103
111
|
* #### Video Recording Customization
|
|
104
112
|
*
|
|
105
113
|
* By default, video is saved to `output/video` dir. You can customize this path by passing `dir` option to `recordVideo` option.
|
|
106
114
|
*
|
|
107
|
-
*
|
|
115
|
+
* `video`: enables video recording for failed tests; videos are saved into `output/videos` folder
|
|
108
116
|
* * `keepVideoForPassedTests`: - save videos for passed tests
|
|
109
117
|
* * `recordVideo`: [additional options for videos customization](https://playwright.dev/docs/next/api/class-browser#browser-new-context)
|
|
110
118
|
*
|
|
@@ -167,7 +175,8 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
167
175
|
* Playwright: {
|
|
168
176
|
* url: "http://localhost",
|
|
169
177
|
* chromium: {
|
|
170
|
-
* browserWSEndpoint: 'ws://localhost:9222/devtools/browser/c5aa6160-b5bc-4d53-bb49-6ecb36cd2e0a'
|
|
178
|
+
* browserWSEndpoint: 'ws://localhost:9222/devtools/browser/c5aa6160-b5bc-4d53-bb49-6ecb36cd2e0a',
|
|
179
|
+
* cdpConnection: false // default is false
|
|
171
180
|
* }
|
|
172
181
|
* }
|
|
173
182
|
* }
|
|
@@ -256,8 +265,6 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
256
265
|
* const { browserContext } = this.helpers.Playwright;
|
|
257
266
|
* await browserContext.cookies(); // get current browser context
|
|
258
267
|
* ```
|
|
259
|
-
*
|
|
260
|
-
* ## Methods
|
|
261
268
|
*/
|
|
262
269
|
class Playwright extends Helper {
|
|
263
270
|
constructor(config) {
|
|
@@ -272,6 +279,7 @@ class Playwright extends Helper {
|
|
|
272
279
|
this.sessionPages = {};
|
|
273
280
|
this.activeSessionName = '';
|
|
274
281
|
this.isElectron = false;
|
|
282
|
+
this.isCDPConnection = false;
|
|
275
283
|
this.electronSessions = [];
|
|
276
284
|
this.storageState = null;
|
|
277
285
|
|
|
@@ -347,6 +355,7 @@ class Playwright extends Helper {
|
|
|
347
355
|
this.isRemoteBrowser = !!this.playwrightOptions.browserWSEndpoint;
|
|
348
356
|
this.isElectron = this.options.browser === 'electron';
|
|
349
357
|
this.userDataDir = this.playwrightOptions.userDataDir;
|
|
358
|
+
this.isCDPConnection = this.playwrightOptions.cdpConnection;
|
|
350
359
|
popupStore.defaultAction = this.options.defaultPopupAction;
|
|
351
360
|
}
|
|
352
361
|
|
|
@@ -692,6 +701,15 @@ class Playwright extends Helper {
|
|
|
692
701
|
async _startBrowser() {
|
|
693
702
|
if (this.isElectron) {
|
|
694
703
|
this.browser = await playwright._electron.launch(this.playwrightOptions);
|
|
704
|
+
} else if (this.isRemoteBrowser && this.isCDPConnection) {
|
|
705
|
+
try {
|
|
706
|
+
this.browser = await playwright[this.options.browser].connectOverCDP(this.playwrightOptions);
|
|
707
|
+
} catch (err) {
|
|
708
|
+
if (err.toString().indexOf('ECONNREFUSED')) {
|
|
709
|
+
throw new RemoteBrowserConnectionRefused(err);
|
|
710
|
+
}
|
|
711
|
+
throw err;
|
|
712
|
+
}
|
|
695
713
|
} else if (this.isRemoteBrowser) {
|
|
696
714
|
try {
|
|
697
715
|
this.browser = await playwright[this.options.browser].connect(this.playwrightOptions);
|
|
@@ -1054,6 +1072,7 @@ class Playwright extends Helper {
|
|
|
1054
1072
|
if (!page) {
|
|
1055
1073
|
throw new Error(`There is no ability to switch to next tab with offset ${num}`);
|
|
1056
1074
|
}
|
|
1075
|
+
targetCreatedHandler.call(this, page);
|
|
1057
1076
|
await this._setPage(page);
|
|
1058
1077
|
return this._waitForAction();
|
|
1059
1078
|
}
|
|
@@ -1136,7 +1155,9 @@ class Playwright extends Helper {
|
|
|
1136
1155
|
if (this.isElectron) {
|
|
1137
1156
|
throw new Error('Cannot open new tabs inside an Electron container');
|
|
1138
1157
|
}
|
|
1139
|
-
|
|
1158
|
+
const page = await this.browserContext.newPage(options);
|
|
1159
|
+
targetCreatedHandler.call(this, page);
|
|
1160
|
+
await this._setPage(page);
|
|
1140
1161
|
return this._waitForAction();
|
|
1141
1162
|
}
|
|
1142
1163
|
|
|
@@ -1558,9 +1579,11 @@ class Playwright extends Helper {
|
|
|
1558
1579
|
* Get JS log from browser.
|
|
1559
1580
|
*
|
|
1560
1581
|
* ```js
|
|
1561
|
-
*
|
|
1562
|
-
*
|
|
1582
|
+
* const logs = await I.grabBrowserLogs();
|
|
1583
|
+
* const errors = logs.map(l => ({ type: l.type(), text: l.text() })).filter(l => l.type === 'error');
|
|
1584
|
+
* console.log(JSON.stringify(errors));
|
|
1563
1585
|
* ```
|
|
1586
|
+
* [Learn more about console messages](https://playwright.dev/docs/api/class-consolemessage)
|
|
1564
1587
|
* @return {Promise<any[]>}
|
|
1565
1588
|
*/
|
|
1566
1589
|
async grabBrowserLogs() {
|