codeceptjs 3.3.5 → 3.3.7-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 +37 -7
- package/bin/codecept.js +40 -3
- package/docs/basics.md +24 -1
- package/docs/build/Appium.js +41 -22
- package/docs/build/FileSystem.js +1 -1
- package/docs/build/Nightmare.js +106 -57
- package/docs/build/Playwright.js +187 -111
- package/docs/build/Protractor.js +132 -70
- package/docs/build/Puppeteer.js +143 -76
- package/docs/build/REST.js +2 -2
- package/docs/build/TestCafe.js +107 -57
- package/docs/build/WebDriver.js +162 -89
- package/docs/changelog.md +37 -7
- package/docs/commands.md +5 -3
- package/docs/configuration.md +5 -5
- package/docs/helpers/Appium.md +25 -23
- package/docs/helpers/FileSystem.md +1 -1
- package/docs/helpers/Nightmare.md +57 -57
- package/docs/helpers/Playwright.md +76 -75
- package/docs/helpers/Puppeteer.md +76 -85
- package/docs/helpers/REST.md +1 -1
- package/docs/helpers/TestCafe.md +57 -57
- package/docs/helpers/WebDriver.md +84 -97
- package/docs/quickstart.md +1 -1
- package/docs/reports.md +1 -1
- package/docs/secrets.md +1 -1
- package/docs/webapi/appendField.mustache +1 -1
- package/docs/webapi/attachFile.mustache +3 -3
- package/docs/webapi/checkOption.mustache +1 -1
- package/docs/webapi/clearCookie.mustache +1 -1
- package/docs/webapi/clearField.mustache +1 -1
- package/docs/webapi/click.mustache +1 -1
- package/docs/webapi/clickLink.mustache +1 -1
- package/docs/webapi/closeCurrentTab.mustache +1 -1
- package/docs/webapi/closeOtherTabs.mustache +1 -1
- package/docs/webapi/dontSee.mustache +1 -1
- package/docs/webapi/dontSeeCheckboxIsChecked.mustache +1 -1
- package/docs/webapi/dontSeeCookie.mustache +1 -1
- package/docs/webapi/dontSeeCurrentUrlEquals.mustache +1 -1
- package/docs/webapi/dontSeeElement.mustache +1 -1
- package/docs/webapi/dontSeeElementInDOM.mustache +1 -1
- package/docs/webapi/dontSeeInCurrentUrl.mustache +1 -1
- package/docs/webapi/dontSeeInField.mustache +1 -1
- package/docs/webapi/dontSeeInSource.mustache +1 -1
- package/docs/webapi/dontSeeInTitle.mustache +1 -1
- package/docs/webapi/doubleClick.mustache +1 -1
- package/docs/webapi/downloadFile.mustache +1 -1
- package/docs/webapi/dragAndDrop.mustache +1 -1
- package/docs/webapi/dragSlider.mustache +1 -1
- package/docs/webapi/executeAsyncScript.mustache +1 -1
- package/docs/webapi/executeScript.mustache +1 -1
- package/docs/webapi/fillField.mustache +1 -1
- package/docs/webapi/forceClick.mustache +1 -1
- package/docs/webapi/forceRightClick.mustache +1 -1
- package/docs/webapi/moveCursorTo.mustache +1 -1
- package/docs/webapi/openNewTab.mustache +1 -1
- package/docs/webapi/pressKey.mustache +1 -1
- package/docs/webapi/pressKeyDown.mustache +1 -1
- package/docs/webapi/pressKeyUp.mustache +1 -1
- package/docs/webapi/pressKeyWithKeyNormalization.mustache +1 -1
- package/docs/webapi/refreshPage.mustache +1 -1
- package/docs/webapi/resizeWindow.mustache +1 -1
- package/docs/webapi/rightClick.mustache +1 -1
- package/docs/webapi/saveElementScreenshot.mustache +2 -2
- package/docs/webapi/saveScreenshot.mustache +2 -2
- package/docs/webapi/say.mustache +1 -1
- package/docs/webapi/scrollIntoView.mustache +1 -1
- package/docs/webapi/scrollPageToBottom.mustache +1 -1
- package/docs/webapi/scrollPageToTop.mustache +1 -1
- package/docs/webapi/scrollTo.mustache +1 -1
- package/docs/webapi/see.mustache +1 -1
- package/docs/webapi/seeAttributesOnElements.mustache +1 -1
- package/docs/webapi/seeCheckboxIsChecked.mustache +1 -1
- package/docs/webapi/seeCookie.mustache +1 -1
- package/docs/webapi/seeCssPropertiesOnElements.mustache +1 -1
- package/docs/webapi/seeCurrentUrlEquals.mustache +1 -1
- package/docs/webapi/seeElement.mustache +1 -1
- package/docs/webapi/seeElementInDOM.mustache +1 -1
- package/docs/webapi/seeInCurrentUrl.mustache +1 -1
- package/docs/webapi/seeInField.mustache +1 -1
- package/docs/webapi/seeInPopup.mustache +1 -1
- package/docs/webapi/seeInSource.mustache +1 -1
- package/docs/webapi/seeInTitle.mustache +1 -1
- package/docs/webapi/seeNumberOfElements.mustache +1 -1
- package/docs/webapi/seeNumberOfVisibleElements.mustache +1 -1
- package/docs/webapi/seeTextEquals.mustache +1 -1
- package/docs/webapi/seeTitleEquals.mustache +1 -1
- package/docs/webapi/selectOption.mustache +1 -1
- package/docs/webapi/setCookie.mustache +1 -1
- package/docs/webapi/setGeoLocation.mustache +1 -1
- package/docs/webapi/switchTo.mustache +1 -1
- package/docs/webapi/switchToNextTab.mustache +1 -1
- package/docs/webapi/switchToPreviousTab.mustache +1 -1
- package/docs/webapi/type.mustache +1 -1
- package/docs/webapi/uncheckOption.mustache +1 -1
- package/docs/webapi/wait.mustache +1 -1
- package/docs/webapi/waitForClickable.mustache +1 -1
- package/docs/webapi/waitForDetached.mustache +1 -1
- package/docs/webapi/waitForElement.mustache +1 -1
- package/docs/webapi/waitForEnabled.mustache +1 -1
- package/docs/webapi/waitForFunction.mustache +1 -1
- package/docs/webapi/waitForInvisible.mustache +1 -1
- package/docs/webapi/waitForText.mustache +1 -1
- package/docs/webapi/waitForValue.mustache +1 -1
- package/docs/webapi/waitForVisible.mustache +1 -1
- package/docs/webapi/waitInUrl.mustache +1 -1
- package/docs/webapi/waitNumberOfVisibleElements.mustache +1 -1
- package/docs/webapi/waitToHide.mustache +1 -1
- package/docs/webapi/waitUrlEquals.mustache +1 -1
- package/lib/command/configMigrate.js +1 -1
- package/lib/command/dryRun.js +2 -1
- package/lib/command/generate.js +35 -18
- package/lib/command/run-rerun.js +38 -0
- package/lib/command/utils.js +13 -3
- package/lib/config.js +2 -2
- package/lib/data/context.js +7 -0
- package/lib/helper/Appium.js +6 -4
- package/lib/helper/FileSystem.js +1 -1
- package/lib/helper/Nightmare.js +1 -1
- package/lib/helper/Playwright.js +50 -38
- package/lib/helper/Protractor.js +1 -1
- package/lib/helper/REST.js +2 -2
- package/lib/helper/TestCafe.js +1 -1
- package/lib/helper/WebDriver.js +7 -7
- package/lib/plugin/wdio.js +11 -2
- package/lib/utils.js +3 -0
- package/package.json +2 -2
- package/typings/index.d.ts +46 -46
- package/typings/types.d.ts +497 -443
|
@@ -7,4 +7,4 @@ I.waitForInvisible('#popup');
|
|
|
7
7
|
|
|
8
8
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
@@ -10,4 +10,4 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal');
|
|
|
10
10
|
@param {string }text to wait for.
|
|
11
11
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
12
12
|
@param {CodeceptJS.LocatorOrString} [context] (optional) element located by CSS|XPath|strict locator.
|
|
13
|
-
|
|
13
|
+
⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
@@ -7,4 +7,4 @@ I.waitForValue('//input', "GoodValue");
|
|
|
7
7
|
@param {LocatorOrString} field input field.
|
|
8
8
|
@param {string }value expected value.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
@@ -7,4 +7,4 @@ I.waitForVisible('#popup');
|
|
|
7
7
|
|
|
8
8
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
@@ -6,4 +6,4 @@ I.waitInUrl('/info', 2);
|
|
|
6
6
|
|
|
7
7
|
@param {string} urlPart value to check.
|
|
8
8
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
9
|
-
|
|
9
|
+
⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
@@ -7,4 +7,4 @@ I.waitNumberOfVisibleElements('a', 3);
|
|
|
7
7
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
8
8
|
@param {number} num number of elements.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
@@ -7,4 +7,4 @@ I.waitToHide('#popup');
|
|
|
7
7
|
|
|
8
8
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
@@ -7,4 +7,4 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
|
|
|
7
7
|
|
|
8
8
|
@param {string} urlPart value to check.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
@@ -50,7 +50,7 @@ module.exports = function (initPath) {
|
|
|
50
50
|
},
|
|
51
51
|
]).then((result) => {
|
|
52
52
|
if (result.configFile) {
|
|
53
|
-
const jsonConfigFile = path.join(testsPath, 'codecept.
|
|
53
|
+
const jsonConfigFile = path.join(testsPath, 'codecept.js');
|
|
54
54
|
const config = JSON.parse(fs.readFileSync(jsonConfigFile, 'utf8'));
|
|
55
55
|
config.name = testsPath.split(path.sep).pop();
|
|
56
56
|
|
package/lib/command/dryRun.js
CHANGED
|
@@ -27,7 +27,7 @@ module.exports = async function (test, options) {
|
|
|
27
27
|
codecept = new Codecept(config, options);
|
|
28
28
|
codecept.init(testRoot);
|
|
29
29
|
|
|
30
|
-
if (options.bootstrap) await codecept.
|
|
30
|
+
if (options.bootstrap) await codecept.bootstrap();
|
|
31
31
|
|
|
32
32
|
codecept.loadTests();
|
|
33
33
|
store.dryRun = true;
|
|
@@ -71,6 +71,7 @@ function printTests(files) {
|
|
|
71
71
|
output.print('');
|
|
72
72
|
output.success(` Total: ${numOfSuites} suites | ${numOfTests} tests `);
|
|
73
73
|
printFooter();
|
|
74
|
+
process.exit(0);
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
function printFooter() {
|
package/lib/command/generate.js
CHANGED
|
@@ -3,13 +3,16 @@ const fs = require('fs');
|
|
|
3
3
|
const inquirer = require('inquirer');
|
|
4
4
|
const mkdirp = require('mkdirp');
|
|
5
5
|
const path = require('path');
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const {
|
|
7
|
+
fileExists, ucfirst, lcfirst, beautify,
|
|
8
|
+
} = require('../utils');
|
|
8
9
|
const output = require('../output');
|
|
9
10
|
const {
|
|
10
|
-
getConfig, getTestRoot, safeFileWrite,
|
|
11
|
+
getConfig, getTestRoot, safeFileWrite, readConfig,
|
|
11
12
|
} = require('./utils');
|
|
12
13
|
|
|
14
|
+
let extension = 'js';
|
|
15
|
+
|
|
13
16
|
const testTemplate = `Feature('{{feature}}');
|
|
14
17
|
|
|
15
18
|
Scenario('test something', ({ {{actor}} }) => {
|
|
@@ -26,7 +29,7 @@ module.exports.test = function (genPath) {
|
|
|
26
29
|
output.print('Creating a new test...');
|
|
27
30
|
output.print('----------------------');
|
|
28
31
|
|
|
29
|
-
const defaultExt = config.tests.match(/([^\*/]*?)$/)[1] ||
|
|
32
|
+
const defaultExt = config.tests.match(/([^\*/]*?)$/)[1] || `_test.${extension}`;
|
|
30
33
|
|
|
31
34
|
return inquirer.prompt([
|
|
32
35
|
{
|
|
@@ -79,25 +82,34 @@ module.exports.pageObject = function (genPath, opts) {
|
|
|
79
82
|
const kind = opts.T || 'page';
|
|
80
83
|
if (!config) return;
|
|
81
84
|
|
|
85
|
+
let configFile = path.join(testsPath, `codecept.conf.${extension}`);
|
|
86
|
+
|
|
87
|
+
if (!fileExists(configFile)) {
|
|
88
|
+
extension = 'ts';
|
|
89
|
+
configFile = path.join(testsPath, `codecept.conf.${extension}`);
|
|
90
|
+
}
|
|
82
91
|
output.print(`Creating a new ${kind} object`);
|
|
83
92
|
output.print('--------------------------');
|
|
84
93
|
|
|
85
|
-
return inquirer.prompt([
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
return inquirer.prompt([
|
|
95
|
+
{
|
|
96
|
+
type: 'input',
|
|
97
|
+
name: 'name',
|
|
98
|
+
message: `Name of a ${kind} object`,
|
|
99
|
+
validate: (val) => !!val,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
type: 'input',
|
|
103
|
+
name: 'filename',
|
|
104
|
+
message: 'Where should it be stored',
|
|
105
|
+
default: answers => `./${kind}s/${answers.name}.${extension}`,
|
|
106
|
+
}]).then((result) => {
|
|
96
107
|
const pageObjectFile = path.join(testsPath, result.filename);
|
|
97
108
|
const dir = path.dirname(pageObjectFile);
|
|
98
109
|
if (!fileExists(dir)) fs.mkdirSync(dir);
|
|
99
110
|
|
|
100
111
|
let actor = 'actor';
|
|
112
|
+
|
|
101
113
|
if (config.include.I) {
|
|
102
114
|
let actorPath = config.include.I;
|
|
103
115
|
if (actorPath.charAt(0) === '.') { // relative path
|
|
@@ -107,16 +119,21 @@ module.exports.pageObject = function (genPath, opts) {
|
|
|
107
119
|
}
|
|
108
120
|
if (!safeFileWrite(pageObjectFile, pageObjectTemplate.replace('{{actor}}', actor))) return;
|
|
109
121
|
const name = lcfirst(result.name) + ucfirst(kind);
|
|
122
|
+
let data = readConfig(configFile);
|
|
110
123
|
config.include[name] = result.filename;
|
|
124
|
+
data = data.replace(/include[\s\S][^\}]*/i, `include: ${JSON.stringify(config.include).slice(0, -1)}`);
|
|
125
|
+
|
|
126
|
+
fs.writeFileSync(configFile, beautify(data), 'utf-8');
|
|
111
127
|
|
|
112
128
|
output.success(`${ucfirst(kind)} object for ${result.name} was created in ${pageObjectFile}`);
|
|
113
|
-
output.print(`
|
|
129
|
+
output.print(`Your config file (${colors.cyan('include')} section) has included the new created PO:
|
|
114
130
|
|
|
115
131
|
include: {
|
|
132
|
+
...
|
|
116
133
|
${name}: '${result.filename}',
|
|
117
134
|
},`);
|
|
118
135
|
|
|
119
|
-
output.print(`Use ${output.colors.bold(name)} as parameter in test scenarios to access this object`);
|
|
136
|
+
output.print(`Use ${output.colors.bold(colors.cyan(name))} as parameter in test scenarios to access this object`);
|
|
120
137
|
});
|
|
121
138
|
};
|
|
122
139
|
|
|
@@ -163,7 +180,7 @@ module.exports.helper = function (genPath) {
|
|
|
163
180
|
type: 'input',
|
|
164
181
|
name: 'filename',
|
|
165
182
|
message: 'Where should it be stored',
|
|
166
|
-
default: answers => `./${answers.name.toLowerCase()}_helper
|
|
183
|
+
default: answers => `./${answers.name.toLowerCase()}_helper.${extension}`,
|
|
167
184
|
}]).then((result) => {
|
|
168
185
|
const name = ucfirst(result.name);
|
|
169
186
|
const helperFile = path.join(testsPath, result.filename);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const { getConfig, getTestRoot } = require('./utils');
|
|
2
|
+
const { printError, createOutputDir } = require('./utils');
|
|
3
|
+
const Config = require('../config');
|
|
4
|
+
const Codecept = require('../rerun');
|
|
5
|
+
|
|
6
|
+
module.exports = async function (test, options) {
|
|
7
|
+
// registering options globally to use in config
|
|
8
|
+
// Backward compatibility for --profile
|
|
9
|
+
process.profile = options.profile;
|
|
10
|
+
process.env.profile = options.profile;
|
|
11
|
+
const configFile = options.config;
|
|
12
|
+
|
|
13
|
+
let config = getConfig(configFile);
|
|
14
|
+
if (options.override) {
|
|
15
|
+
config = Config.append(JSON.parse(options.override));
|
|
16
|
+
}
|
|
17
|
+
const testRoot = getTestRoot(configFile);
|
|
18
|
+
createOutputDir(config, testRoot);
|
|
19
|
+
|
|
20
|
+
function processError(err) {
|
|
21
|
+
printError(err);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const codecept = new Codecept(config, options);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
codecept.init(testRoot);
|
|
28
|
+
|
|
29
|
+
await codecept.bootstrap();
|
|
30
|
+
codecept.loadTests(test);
|
|
31
|
+
await codecept.run();
|
|
32
|
+
} catch (err) {
|
|
33
|
+
printError(err);
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
} finally {
|
|
36
|
+
await codecept.teardown();
|
|
37
|
+
}
|
|
38
|
+
};
|
package/lib/command/utils.js
CHANGED
|
@@ -18,6 +18,15 @@ module.exports.getConfig = function (configFile) {
|
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
module.exports.readConfig = function (configFile) {
|
|
22
|
+
try {
|
|
23
|
+
const data = fs.readFileSync(configFile, 'utf8');
|
|
24
|
+
return data;
|
|
25
|
+
} catch (err) {
|
|
26
|
+
output.error(err);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
21
30
|
function getTestRoot(currentPath) {
|
|
22
31
|
if (!currentPath) currentPath = '.';
|
|
23
32
|
if (!path.isAbsolute(currentPath)) currentPath = path.join(process.cwd(), currentPath);
|
|
@@ -33,11 +42,12 @@ function fail(msg) {
|
|
|
33
42
|
|
|
34
43
|
module.exports.fail = fail;
|
|
35
44
|
|
|
36
|
-
function updateConfig(testsPath, config, key) {
|
|
37
|
-
const configFile = path.join(testsPath,
|
|
45
|
+
function updateConfig(testsPath, config, key, extension = 'js') {
|
|
46
|
+
const configFile = path.join(testsPath, `codecept.conf.${extension}`);
|
|
38
47
|
if (!fileExists(configFile)) {
|
|
39
48
|
console.log();
|
|
40
|
-
|
|
49
|
+
const msg = `codecept.conf.${extension} config can\'t be updated automatically`;
|
|
50
|
+
console.log(`${output.colors.bold.red(msg)}`);
|
|
41
51
|
console.log('Please update it manually:');
|
|
42
52
|
console.log();
|
|
43
53
|
console.log(`${key}: ${config[key]}`);
|
package/lib/config.js
CHANGED
|
@@ -41,7 +41,7 @@ let config = {};
|
|
|
41
41
|
const configFileNames = [
|
|
42
42
|
'codecept.config.js',
|
|
43
43
|
'codecept.conf.js',
|
|
44
|
-
'codecept.
|
|
44
|
+
'codecept.js',
|
|
45
45
|
'codecept.config.ts',
|
|
46
46
|
'codecept.conf.ts',
|
|
47
47
|
];
|
|
@@ -69,7 +69,7 @@ class Config {
|
|
|
69
69
|
* If directory provided:
|
|
70
70
|
* * try to load `codecept.config.js` from it
|
|
71
71
|
* * try to load `codecept.conf.js` from it
|
|
72
|
-
* * try to load `codecept.
|
|
72
|
+
* * try to load `codecept.js` from it
|
|
73
73
|
* If none of above: fail.
|
|
74
74
|
*
|
|
75
75
|
* @param {string} configFile
|
package/lib/data/context.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { isGenerator } = require('../utils');
|
|
2
2
|
const DataTable = require('./table');
|
|
3
3
|
const DataScenarioConfig = require('./dataScenarioConfig');
|
|
4
|
+
const Secret = require('../secret');
|
|
4
5
|
|
|
5
6
|
module.exports = function (context) {
|
|
6
7
|
context.Data = function (dataTable) {
|
|
@@ -70,6 +71,12 @@ function replaceTitle(title, dataRow) {
|
|
|
70
71
|
// it should be printed
|
|
71
72
|
if (Object.prototype.toString.call(dataRow.data) === (Object()).toString()
|
|
72
73
|
&& dataRow.data.toString() !== (Object()).toString()) {
|
|
74
|
+
Object.entries(dataRow.data).forEach(entry => {
|
|
75
|
+
const [key, value] = entry;
|
|
76
|
+
if (value instanceof Secret) {
|
|
77
|
+
dataRow.data[key] = value.getMasked();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
73
80
|
return `${title} | ${dataRow.data}`;
|
|
74
81
|
}
|
|
75
82
|
|
package/lib/helper/Appium.js
CHANGED
|
@@ -34,7 +34,7 @@ const webRoot = 'body';
|
|
|
34
34
|
*
|
|
35
35
|
* ## Helper configuration
|
|
36
36
|
*
|
|
37
|
-
* This helper should be configured in codecept.
|
|
37
|
+
* This helper should be configured in codecept.conf.ts or codecept.conf.js
|
|
38
38
|
*
|
|
39
39
|
* * `app`: Application path. Local path or remote URL to an .ipa or .apk file, or a .zip containing one of these. Alias to desiredCapabilities.appPackage
|
|
40
40
|
* * `host`: (default: 'localhost') Appium host
|
|
@@ -788,9 +788,11 @@ class Appium extends Webdriver {
|
|
|
788
788
|
* I.startActivity('io.selendroid.testapp', '.RegisterUserActivity');
|
|
789
789
|
* ```
|
|
790
790
|
*
|
|
791
|
-
* @return {Promise<void>}
|
|
792
|
-
*
|
|
793
791
|
* Appium: support only Android
|
|
792
|
+
*
|
|
793
|
+
* @param {string} appPackage
|
|
794
|
+
* @param {string} appActivity
|
|
795
|
+
* @return {Promise<void>}
|
|
794
796
|
*/
|
|
795
797
|
async startActivity(appPackage, appActivity) {
|
|
796
798
|
onlyForApps.call(this, 'Android');
|
|
@@ -1458,7 +1460,7 @@ class Appium extends Webdriver {
|
|
|
1458
1460
|
}
|
|
1459
1461
|
|
|
1460
1462
|
/**
|
|
1461
|
-
* Saves a screenshot to ouput folder (set in codecept.
|
|
1463
|
+
* Saves a screenshot to ouput folder (set in codecept.conf.ts or codecept.conf.js).
|
|
1462
1464
|
* Filename is relative to output folder.
|
|
1463
1465
|
*
|
|
1464
1466
|
* ```js
|
package/lib/helper/FileSystem.js
CHANGED
package/lib/helper/Nightmare.js
CHANGED
|
@@ -35,7 +35,7 @@ let withinStatus = false;
|
|
|
35
35
|
*
|
|
36
36
|
* ## Configuration
|
|
37
37
|
*
|
|
38
|
-
* This helper should be configured in codecept.
|
|
38
|
+
* This helper should be configured in codecept.conf.ts or codecept.conf.js
|
|
39
39
|
*
|
|
40
40
|
* * `url` - base url of website to be tested
|
|
41
41
|
* * `restart` (optional, default: true) - restart browser between tests.
|
package/lib/helper/Playwright.js
CHANGED
|
@@ -62,13 +62,14 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
62
62
|
* @prop {boolean} [disableScreenshots=false] - don't save screenshot on failure.
|
|
63
63
|
* @prop {any} [emulate] - browser in device emulation mode.
|
|
64
64
|
* @prop {boolean} [video=false] - enables video recording for failed tests; videos are saved into `output/videos` folder
|
|
65
|
+
* @prop {boolean} [keepVideoForPassedTests=false] - save videos for passed tests; videos are saved into `output/videos` folder
|
|
65
66
|
* @prop {boolean} [trace=false] - record [tracing information](https://playwright.dev/docs/trace-viewer) with screenshots and snapshots.
|
|
66
67
|
* @prop {boolean} [fullPageScreenshots=false] - make full page screenshots on failure.
|
|
67
68
|
* @prop {boolean} [uniqueScreenshotNames=false] - option to prevent screenshot override if you have scenarios with the same name in different suites.
|
|
68
69
|
* @prop {boolean} [keepBrowserState=false] - keep browser state between tests when `restart` is set to 'session'.
|
|
69
70
|
* @prop {boolean} [keepCookies=false] - keep cookies between tests when `restart` is set to 'session'.
|
|
70
71
|
* @prop {number} [waitForAction] - how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
|
|
71
|
-
* @prop {
|
|
72
|
+
* @prop {string} [waitForNavigation] - When to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle`. Choose one of those options is possible. See [Playwright API](https://playwright.dev/docs/api/class-page#page-wait-for-navigation).
|
|
72
73
|
* @prop {number} [pressKeyDelay=10] - Delay between key presses in ms. Used when calling Playwrights page.type(...) in fillField/appendField
|
|
73
74
|
* @prop {number} [getPageTimeout] - config option to set maximum navigation time in milliseconds.
|
|
74
75
|
* @prop {number} [waitForTimeout] - default wait* timeout in ms. Default: 1000.
|
|
@@ -476,6 +477,8 @@ class Playwright extends Helper {
|
|
|
476
477
|
|
|
477
478
|
// close other sessions
|
|
478
479
|
try {
|
|
480
|
+
if (!this.browser.contexts) return this.browser;
|
|
481
|
+
|
|
479
482
|
const contexts = await this.browser.contexts();
|
|
480
483
|
const currentContext = contexts[0];
|
|
481
484
|
if (currentContext && (this.options.keepCookies || this.options.keepBrowserState)) {
|
|
@@ -548,22 +551,22 @@ class Playwright extends Helper {
|
|
|
548
551
|
}
|
|
549
552
|
|
|
550
553
|
/**
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
554
|
+
* Use Playwright API inside a test.
|
|
555
|
+
*
|
|
556
|
+
* First argument is a description of an action.
|
|
557
|
+
* Second argument is async function that gets this helper as parameter.
|
|
558
|
+
*
|
|
559
|
+
* { [`page`](https://github.com/microsoft/playwright/blob/main/docs/src/api/class-page.md), [`browserContext`](https://github.com/microsoft/playwright/blob/main/docs/src/api/class-browsercontext.md) [`browser`](https://github.com/microsoft/playwright/blob/main/docs/src/api/class-browser.md) } objects from Playwright API are available.
|
|
560
|
+
*
|
|
561
|
+
* ```js
|
|
562
|
+
* I.usePlaywrightTo('emulate offline mode', async ({ browserContext }) => {
|
|
563
|
+
* await browserContext.setOffline(true);
|
|
564
|
+
* });
|
|
565
|
+
* ```
|
|
566
|
+
*
|
|
567
|
+
* @param {string} description used to show in logs.
|
|
568
|
+
* @param {function} fn async function that executed with Playwright helper as argumen
|
|
569
|
+
*/
|
|
567
570
|
usePlaywrightTo(description, fn) {
|
|
568
571
|
return this._useTo(...arguments);
|
|
569
572
|
}
|
|
@@ -2029,7 +2032,11 @@ class Playwright extends Helper {
|
|
|
2029
2032
|
}
|
|
2030
2033
|
|
|
2031
2034
|
if (this.options.recordVideo && this.page && this.page.video()) {
|
|
2035
|
+
const videoPath = `${global.output_dir}/videos/${clearString(test.title)}.failed.webm`;
|
|
2032
2036
|
test.artifacts.video = await this.page.video().path();
|
|
2037
|
+
fs.rename(test.artifacts.video, videoPath, (() => {
|
|
2038
|
+
test.artifacts.video = videoPath;
|
|
2039
|
+
}));
|
|
2033
2040
|
}
|
|
2034
2041
|
|
|
2035
2042
|
if (this.options.trace) {
|
|
@@ -2041,8 +2048,13 @@ class Playwright extends Helper {
|
|
|
2041
2048
|
|
|
2042
2049
|
async _passed(test) {
|
|
2043
2050
|
if (this.options.recordVideo && this.page && this.page.video()) {
|
|
2051
|
+
const videoPath = `${global.output_dir}/videos/${clearString(test.title)}.passed.webm`;
|
|
2052
|
+
|
|
2044
2053
|
if (this.options.keepVideoForPassedTests) {
|
|
2045
2054
|
test.artifacts.video = await this.page.video().path();
|
|
2055
|
+
fs.rename(test.artifacts.video, videoPath, (() => {
|
|
2056
|
+
test.artifacts.video = videoPath;
|
|
2057
|
+
}));
|
|
2046
2058
|
} else {
|
|
2047
2059
|
this.page.video().delete().catch(e => {});
|
|
2048
2060
|
}
|
|
@@ -2456,32 +2468,32 @@ class Playwright extends Helper {
|
|
|
2456
2468
|
}
|
|
2457
2469
|
|
|
2458
2470
|
/**
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2471
|
+
* Mocks network request using [`browserContext.route`](https://playwright.dev/docs/api/class-browsercontext#browser-context-route) of Playwright
|
|
2472
|
+
*
|
|
2473
|
+
* ```js
|
|
2474
|
+
* I.mockRoute(/(\.png$)|(\.jpg$)/, route => route.abort());
|
|
2475
|
+
* ```
|
|
2476
|
+
* This method allows intercepting and mocking requests & responses. [Learn more about it](https://playwright.dev/docs/network#handle-requests)
|
|
2477
|
+
*
|
|
2478
|
+
* @param {string|RegExp} [url] URL, regex or pattern for to match URL
|
|
2479
|
+
* @param {function} [handler] a function to process reques
|
|
2480
|
+
*/
|
|
2469
2481
|
async mockRoute(url, handler) {
|
|
2470
2482
|
return this.browserContext.route(...arguments);
|
|
2471
2483
|
}
|
|
2472
2484
|
|
|
2473
2485
|
/**
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2486
|
+
* Stops network mocking created by `mockRoute`.
|
|
2487
|
+
*
|
|
2488
|
+
* ```js
|
|
2489
|
+
* I.stopMockingRoute(/(\.png$)|(\.jpg$)/);
|
|
2490
|
+
* I.stopMockingRoute(/(\.png$)|(\.jpg$)/, previouslySetHandler);
|
|
2491
|
+
* ```
|
|
2492
|
+
* If no handler is passed, all mock requests for the rote are disabled.
|
|
2493
|
+
*
|
|
2494
|
+
* @param {string|RegExp} [url] URL, regex or pattern for to match URL
|
|
2495
|
+
* @param {function} [handler] a function to process reques
|
|
2496
|
+
*/
|
|
2485
2497
|
async stopMockingRoute(url, handler) {
|
|
2486
2498
|
return this.browserContext.unroute(...arguments);
|
|
2487
2499
|
}
|
package/lib/helper/Protractor.js
CHANGED
|
@@ -36,7 +36,7 @@ let Runner;
|
|
|
36
36
|
*
|
|
37
37
|
* ### Configuration
|
|
38
38
|
*
|
|
39
|
-
* This helper should be configured in codecept.
|
|
39
|
+
* This helper should be configured in codecept.conf.ts or codecept.conf.js
|
|
40
40
|
*
|
|
41
41
|
* * `url` - base url of website to be tested
|
|
42
42
|
* * `browser` - browser in which perform testing
|
package/lib/helper/REST.js
CHANGED
|
@@ -9,7 +9,7 @@ const { beautify } = require('../utils');
|
|
|
9
9
|
*
|
|
10
10
|
* @typedef RESTConfig
|
|
11
11
|
* @type {object}
|
|
12
|
-
* @prop {string} endpoint - API base URL
|
|
12
|
+
* @prop {string} [endpoint] - API base URL
|
|
13
13
|
* @prop {boolean} [prettyPrintJson=false] - pretty print json for response/request on console logs
|
|
14
14
|
* @prop {number} [timeout=1000] - timeout for requests in milliseconds. 10000ms by default
|
|
15
15
|
* @prop {object} [defaultHeaders] - a list of default headers
|
|
@@ -138,7 +138,7 @@ class REST extends Helper {
|
|
|
138
138
|
|
|
139
139
|
if (request.data instanceof Secret) {
|
|
140
140
|
_debugRequest.data = '*****';
|
|
141
|
-
request.data = typeof request.data === 'object' ? { ...request.data.toString() } : request.data.toString();
|
|
141
|
+
request.data = (typeof request.data === 'object' && !(request.data instanceof Secret)) ? { ...request.data.toString() } : request.data.toString();
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
if ((typeof request.data) === 'string') {
|
package/lib/helper/TestCafe.js
CHANGED
|
@@ -42,7 +42,7 @@ const getHtmlSource = t => ClientFunction(() => document.getElementsByTagName('h
|
|
|
42
42
|
*
|
|
43
43
|
* ## Configuration
|
|
44
44
|
*
|
|
45
|
-
* This helper should be configured in codecept.
|
|
45
|
+
* This helper should be configured in codecept.conf.ts or codecept.conf.js
|
|
46
46
|
*
|
|
47
47
|
* * `url`: base url of website to be tested
|
|
48
48
|
* * `show`: (optional, default: false) - show browser window.
|
package/lib/helper/WebDriver.js
CHANGED
|
@@ -44,11 +44,11 @@ let version;
|
|
|
44
44
|
* @prop {string} browser browser in which to perform testing.
|
|
45
45
|
* @prop {string} [basicAuth] - (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
46
46
|
* @prop {string} [host=localhost] - WebDriver host to connect.
|
|
47
|
-
* @prop {
|
|
47
|
+
* @prop {number} [port=4444] - WebDriver port to connect.
|
|
48
48
|
* @prop {string} [protocol=http] - protocol for WebDriver server.
|
|
49
49
|
* @prop {string} [path=/wd/hub] - path to WebDriver server,
|
|
50
50
|
* @prop {boolean} [restart=true] - restart browser between tests.
|
|
51
|
-
* @prop {boolean} [smartWait=false] - **enables [SmartWait](http://codecept.io/acceptance/#smartwait)**; wait for additional milliseconds for element to appear. Enable for 5 secs: "smartWait": 5000.
|
|
51
|
+
* @prop {boolean|number} [smartWait=false] - **enables [SmartWait](http://codecept.io/acceptance/#smartwait)**; wait for additional milliseconds for element to appear. Enable for 5 secs: "smartWait": 5000.
|
|
52
52
|
* @prop {boolean} [disableScreenshots=false] - don't save screenshots on failure.
|
|
53
53
|
* @prop {boolean} [fullPageScreenshots=false] (optional - make full page screenshots on failure.
|
|
54
54
|
* @prop {boolean} [uniqueScreenshotNames=false] - option to prevent screenshot override if you have scenarios with the same name in different suites.
|
|
@@ -916,7 +916,7 @@ class WebDriver extends Helper {
|
|
|
916
916
|
* {{ react }}
|
|
917
917
|
*/
|
|
918
918
|
async click(locator, context = null) {
|
|
919
|
-
const clickMethod = this.browser.isMobile && this.browser.
|
|
919
|
+
const clickMethod = this.browser.isMobile && !this.browser.isW3C ? 'touchClick' : 'elementClick';
|
|
920
920
|
const locateFn = prepareLocateFn.call(this, context);
|
|
921
921
|
|
|
922
922
|
const res = await findClickable.call(this, locator, locateFn);
|
|
@@ -1126,7 +1126,7 @@ class WebDriver extends Helper {
|
|
|
1126
1126
|
* Appium: not tested
|
|
1127
1127
|
*/
|
|
1128
1128
|
async checkOption(field, context = null) {
|
|
1129
|
-
const clickMethod = this.browser.isMobile && this.browser.
|
|
1129
|
+
const clickMethod = this.browser.isMobile && !this.browser.isW3C ? 'touchClick' : 'elementClick';
|
|
1130
1130
|
const locateFn = prepareLocateFn.call(this, context);
|
|
1131
1131
|
|
|
1132
1132
|
const res = await findCheckable.call(this, field, locateFn);
|
|
@@ -1145,7 +1145,7 @@ class WebDriver extends Helper {
|
|
|
1145
1145
|
* Appium: not tested
|
|
1146
1146
|
*/
|
|
1147
1147
|
async uncheckOption(field, context = null) {
|
|
1148
|
-
const clickMethod = this.browser.isMobile && this.browser.
|
|
1148
|
+
const clickMethod = this.browser.isMobile && !this.browser.isW3C ? 'touchClick' : 'elementClick';
|
|
1149
1149
|
const locateFn = prepareLocateFn.call(this, context);
|
|
1150
1150
|
|
|
1151
1151
|
const res = await findCheckable.call(this, field, locateFn);
|
|
@@ -1632,7 +1632,7 @@ class WebDriver extends Helper {
|
|
|
1632
1632
|
assertElementExists(res);
|
|
1633
1633
|
const elem = usingFirstElement(res);
|
|
1634
1634
|
const elementId = getElementId(elem);
|
|
1635
|
-
if (this.browser.isMobile && this.browser.
|
|
1635
|
+
if (this.browser.isMobile && !this.browser.isW3C) return this.browser.touchScroll(offsetX, offsetY, elementId);
|
|
1636
1636
|
const location = await elem.getLocation();
|
|
1637
1637
|
assertElementExists(location, 'Failed to receive', 'location');
|
|
1638
1638
|
/* eslint-disable prefer-arrow-callback */
|
|
@@ -1640,7 +1640,7 @@ class WebDriver extends Helper {
|
|
|
1640
1640
|
/* eslint-enable */
|
|
1641
1641
|
}
|
|
1642
1642
|
|
|
1643
|
-
if (this.browser.isMobile && this.browser.
|
|
1643
|
+
if (this.browser.isMobile && !this.browser.isW3C) return this.browser.touchScroll(locator, offsetX, offsetY);
|
|
1644
1644
|
|
|
1645
1645
|
/* eslint-disable prefer-arrow-callback, comma-dangle */
|
|
1646
1646
|
return this.browser.execute(function (x, y) { return window.scrollTo(x, y); }, offsetX, offsetY);
|
package/lib/plugin/wdio.js
CHANGED
|
@@ -85,6 +85,8 @@ let restartsSession;
|
|
|
85
85
|
*
|
|
86
86
|
*/
|
|
87
87
|
module.exports = (config) => {
|
|
88
|
+
// Keep initial configs to pass as options to wdio services
|
|
89
|
+
const wdioOptions = { ...config };
|
|
88
90
|
const webDriver = container.helpers('WebDriver');
|
|
89
91
|
if (webDriver) {
|
|
90
92
|
config = Object.assign(webDriver.options, config);
|
|
@@ -108,7 +110,9 @@ module.exports = (config) => {
|
|
|
108
110
|
if (version.indexOf('5') === 0) {
|
|
109
111
|
launchers.push(new Launcher(config));
|
|
110
112
|
} else {
|
|
111
|
-
const options = {
|
|
113
|
+
const options = {
|
|
114
|
+
logPath: global.output_dir, installArgs: seleniumInstallArgs, args: seleniumArgs, ...wdioOptions,
|
|
115
|
+
};
|
|
112
116
|
launchers.push(new Launcher(options, [config.capabilities], config));
|
|
113
117
|
}
|
|
114
118
|
}
|
|
@@ -215,7 +219,12 @@ module.exports = (config) => {
|
|
|
215
219
|
if (launcher.onPrepare) {
|
|
216
220
|
event.dispatcher.on(event.all.before, () => {
|
|
217
221
|
recorder.add(`launcher ${name} start`, async () => {
|
|
218
|
-
|
|
222
|
+
// browserstack-service expects capabilities as array
|
|
223
|
+
if (launcher.constructor.name === 'BrowserstackLauncherService') {
|
|
224
|
+
await launcher.onPrepare(config, [config.capabilities]);
|
|
225
|
+
} else {
|
|
226
|
+
await launcher.onPrepare(config, config.capabilities);
|
|
227
|
+
}
|
|
219
228
|
output.debug(`Started ${name}`);
|
|
220
229
|
});
|
|
221
230
|
});
|
package/lib/utils.js
CHANGED