codeceptjs 3.3.3 → 3.3.5-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- 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/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/command/generate.js +3 -0
- package/lib/command/init.js +88 -41
- package/lib/command/interactive.js +1 -1
- package/lib/config.js +9 -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/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/helper/Puppeteer.js
CHANGED
|
@@ -39,6 +39,35 @@ let perfTiming;
|
|
|
39
39
|
const popupStore = new Popup();
|
|
40
40
|
const consoleLogStore = new Console();
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* ## Configuration
|
|
44
|
+
*
|
|
45
|
+
* This helper should be configured in codecept.conf.js
|
|
46
|
+
*
|
|
47
|
+
* @typedef PuppeteerConfig
|
|
48
|
+
* @type {object}
|
|
49
|
+
* @prop {string} url - base url of website to be tested
|
|
50
|
+
* @prop {object} [basicAuth] (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
51
|
+
* @prop {boolean} [show] - show Google Chrome window for debug.
|
|
52
|
+
* @prop {boolean} [restart=true] - restart browser between tests.
|
|
53
|
+
* @prop {boolean} [disableScreenshots=false] - don't save screenshot on failure.
|
|
54
|
+
* @prop {boolean} [fullPageScreenshots=false] - make full page screenshots on failure.
|
|
55
|
+
* @prop {boolean} [uniqueScreenshotNames=false] - option to prevent screenshot override if you have scenarios with the same name in different suites.
|
|
56
|
+
* @prop {boolean} [keepBrowserState=false] - keep browser state between tests when `restart` is set to false.
|
|
57
|
+
* @prop {boolean} [keepCookies=false] - keep cookies between tests when `restart` is set to false.
|
|
58
|
+
* @prop {number} [waitForAction=100] - how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
|
|
59
|
+
* @prop {string} [waitForNavigation=load] - when to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle0`, `networkidle2`. See [Puppeteer API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions). Array values are accepted as well.
|
|
60
|
+
* @prop {number} [pressKeyDelay=10] - delay between key presses in ms. Used when calling Puppeteers page.type(...) in fillField/appendField
|
|
61
|
+
* @prop {number} [getPageTimeout=30000] - config option to set maximum navigation time in milliseconds. If the timeout is set to 0, then timeout will be disabled.
|
|
62
|
+
* @prop {number} [waitForTimeout=1000] - default wait* timeout in ms.
|
|
63
|
+
* @prop {string} [windowSize] - default window size. Set a dimension in format WIDTHxHEIGHT like `640x480`.
|
|
64
|
+
* @prop {string} [userAgent] - user-agent string.
|
|
65
|
+
* @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
|
|
66
|
+
* @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
|
|
67
|
+
* @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
|
|
68
|
+
*/
|
|
69
|
+
const config = {};
|
|
70
|
+
|
|
42
71
|
/**
|
|
43
72
|
* Uses [Google Chrome's Puppeteer](https://github.com/GoogleChrome/puppeteer) library to run tests inside headless Chrome.
|
|
44
73
|
* Browser control is executed via DevTools Protocol (instead of Selenium).
|
|
@@ -56,30 +85,7 @@ const consoleLogStore = new Console();
|
|
|
56
85
|
*
|
|
57
86
|
* > Experimental Firefox support [can be activated](https://codecept.io/helpers/Puppeteer-firefox).
|
|
58
87
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* This helper should be configured in codecept.json or codecept.conf.js
|
|
62
|
-
*
|
|
63
|
-
* * `url`: base url of website to be tested
|
|
64
|
-
* * `basicAuth`: (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
65
|
-
* * `show`: (optional, default: false) - show Google Chrome window for debug.
|
|
66
|
-
* * `restart`: (optional, default: true) - restart browser between tests.
|
|
67
|
-
* * `disableScreenshots`: (optional, default: false) - don't save screenshot on failure.
|
|
68
|
-
* * `fullPageScreenshots` (optional, default: false) - make full page screenshots on failure.
|
|
69
|
-
* * `uniqueScreenshotNames`: (optional, default: false) - option to prevent screenshot override if you have scenarios with the same name in different suites.
|
|
70
|
-
* * `keepBrowserState`: (optional, default: false) - keep browser state between tests when `restart` is set to false.
|
|
71
|
-
* * `keepCookies`: (optional, default: false) - keep cookies between tests when `restart` is set to false.
|
|
72
|
-
* * `waitForAction`: (optional) how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
|
|
73
|
-
* * `waitForNavigation`: (optional, default: 'load'). When to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle0`, `networkidle2`. See [Puppeteer API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions). Array values are accepted as well.
|
|
74
|
-
* * `pressKeyDelay`: (optional, default: '10'). Delay between key presses in ms. Used when calling Puppeteers page.type(...) in fillField/appendField
|
|
75
|
-
* * `getPageTimeout` (optional, default: '30000') config option to set maximum navigation time in milliseconds. If the timeout is set to 0, then timeout will be disabled.
|
|
76
|
-
* * `waitForTimeout`: (optional) default wait* timeout in ms. Default: 1000.
|
|
77
|
-
* * `windowSize`: (optional) default window size. Set a dimension like `640x480`.
|
|
78
|
-
* * `userAgent`: (optional) user-agent string.
|
|
79
|
-
* * `manualStart`: (optional, default: false) - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
|
|
80
|
-
* * `browser`: (optional, default: chrome) - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
|
|
81
|
-
* * `chrome`: (optional) pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
|
|
82
|
-
*
|
|
88
|
+
* <!-- configuration -->
|
|
83
89
|
*
|
|
84
90
|
* #### Example #1: Wait for 0 network connections.
|
|
85
91
|
*
|
|
@@ -192,7 +198,6 @@ class Puppeteer extends Helper {
|
|
|
192
198
|
super(config);
|
|
193
199
|
|
|
194
200
|
puppeteer = requireWithFallback('puppeteer', 'puppeteer-core');
|
|
195
|
-
|
|
196
201
|
// set defaults
|
|
197
202
|
this.isRemoteBrowser = false;
|
|
198
203
|
this.isRunning = false;
|
|
@@ -372,22 +377,22 @@ class Puppeteer extends Helper {
|
|
|
372
377
|
}
|
|
373
378
|
|
|
374
379
|
/**
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
380
|
+
* Use Puppeteer API inside a test.
|
|
381
|
+
*
|
|
382
|
+
* First argument is a description of an action.
|
|
383
|
+
* Second argument is async function that gets this helper as parameter.
|
|
384
|
+
*
|
|
385
|
+
* { [`page`](https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#class-page), [`browser`](https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#class-browser) } from Puppeteer API are available.
|
|
386
|
+
*
|
|
387
|
+
* ```js
|
|
388
|
+
* I.usePuppeteerTo('emulate offline mode', async ({ page }) {
|
|
389
|
+
* await page.setOfflineMode(true);
|
|
390
|
+
* });
|
|
391
|
+
* ```
|
|
392
|
+
*
|
|
393
|
+
* @param {string} description used to show in logs.
|
|
394
|
+
* @param {function} fn async function that is executed with Puppeteer as argument
|
|
395
|
+
*/
|
|
391
396
|
usePuppeteerTo(description, fn) {
|
|
392
397
|
return this._useTo(...arguments);
|
|
393
398
|
}
|
|
@@ -749,7 +754,8 @@ class Puppeteer extends Helper {
|
|
|
749
754
|
if (locator) {
|
|
750
755
|
const els = await this._locate(locator);
|
|
751
756
|
assertElementExists(els, locator, 'Element');
|
|
752
|
-
|
|
757
|
+
const el = els[0];
|
|
758
|
+
await el.evaluate((el) => el.scrollIntoView());
|
|
753
759
|
const elementCoordinates = await getClickablePoint(els[0]);
|
|
754
760
|
await this.executeScript((x, y) => window.scrollBy(x, y), elementCoordinates.x + offsetX, elementCoordinates.y + offsetY);
|
|
755
761
|
} else {
|
|
@@ -1069,7 +1075,12 @@ class Puppeteer extends Helper {
|
|
|
1069
1075
|
fs.mkdirSync(downloadPath, '0777');
|
|
1070
1076
|
}
|
|
1071
1077
|
fsExtra.emptyDirSync(downloadPath);
|
|
1072
|
-
|
|
1078
|
+
|
|
1079
|
+
try {
|
|
1080
|
+
return this.page._client.send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath });
|
|
1081
|
+
} catch (e) {
|
|
1082
|
+
return this.page._client().send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath });
|
|
1083
|
+
}
|
|
1073
1084
|
}
|
|
1074
1085
|
|
|
1075
1086
|
/**
|
package/lib/helper/REST.js
CHANGED
|
@@ -2,18 +2,28 @@ const axios = require('axios').default;
|
|
|
2
2
|
const Secret = require('../secret');
|
|
3
3
|
|
|
4
4
|
const Helper = require('../helper');
|
|
5
|
+
const { beautify } = require('../utils');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ## Configuration
|
|
9
|
+
*
|
|
10
|
+
* @typedef RESTConfig
|
|
11
|
+
* @type {object}
|
|
12
|
+
* @prop {string} endpoint - API base URL
|
|
13
|
+
* @prop {boolean} [prettyPrintJson=false] - pretty print json for response/request on console logs
|
|
14
|
+
* @prop {number} [timeout=1000] - timeout for requests in milliseconds. 10000ms by default
|
|
15
|
+
* @prop {object} [defaultHeaders] - a list of default headers
|
|
16
|
+
* @prop {function} [onRequest] - a async function which can update request object.
|
|
17
|
+
* @prop {function} [onResponse] - a async function which can update response object.
|
|
18
|
+
* @prop {number} [maxUploadFileSize] - set the max content file size in MB when performing api calls.
|
|
19
|
+
*/
|
|
20
|
+
const config = {};
|
|
5
21
|
|
|
6
22
|
/**
|
|
7
23
|
* REST helper allows to send additional requests to the REST API during acceptance tests.
|
|
8
24
|
* [Axios](https://github.com/axios/axios) library is used to perform requests.
|
|
9
25
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* * endpoint: API base URL
|
|
13
|
-
* * timeout: timeout for requests in milliseconds. 10000ms by default
|
|
14
|
-
* * defaultHeaders: a list of default headers
|
|
15
|
-
* * onRequest: a async function which can update request object.
|
|
16
|
-
* * maxUploadFileSize: set the max content file size in MB when performing api calls.
|
|
26
|
+
* <!-- configuration -->
|
|
17
27
|
*
|
|
18
28
|
* ## Example
|
|
19
29
|
*
|
|
@@ -22,6 +32,7 @@ const Helper = require('../helper');
|
|
|
22
32
|
* helpers: {
|
|
23
33
|
* REST: {
|
|
24
34
|
* endpoint: 'http://site.com/api',
|
|
35
|
+
* prettyPrintJson: true,
|
|
25
36
|
* onRequest: (request) => {
|
|
26
37
|
* request.headers.auth = '123';
|
|
27
38
|
* }
|
|
@@ -49,6 +60,9 @@ class REST extends Helper {
|
|
|
49
60
|
timeout: 10000,
|
|
50
61
|
defaultHeaders: {},
|
|
51
62
|
endpoint: '',
|
|
63
|
+
prettyPrintJson: false,
|
|
64
|
+
onRequest: null,
|
|
65
|
+
onResponse: null,
|
|
52
66
|
};
|
|
53
67
|
|
|
54
68
|
if (this.options.maxContentLength) {
|
|
@@ -137,7 +151,7 @@ class REST extends Helper {
|
|
|
137
151
|
await this.config.onRequest(request);
|
|
138
152
|
}
|
|
139
153
|
|
|
140
|
-
this.debugSection('Request', JSON.stringify(_debugRequest));
|
|
154
|
+
this.options.prettyPrintJson ? this.debugSection('Request', beautify(JSON.stringify(_debugRequest))) : this.debugSection('Request', JSON.stringify(_debugRequest));
|
|
141
155
|
|
|
142
156
|
let response;
|
|
143
157
|
try {
|
|
@@ -150,7 +164,7 @@ class REST extends Helper {
|
|
|
150
164
|
if (this.config.onResponse) {
|
|
151
165
|
await this.config.onResponse(response);
|
|
152
166
|
}
|
|
153
|
-
this.debugSection('Response', JSON.stringify(response.data));
|
|
167
|
+
this.options.prettyPrintJson ? this.debugSection('Response', beautify(JSON.stringify(response.data))) : this.debugSection('Response', JSON.stringify(response.data));
|
|
154
168
|
return response;
|
|
155
169
|
}
|
|
156
170
|
|
package/lib/helper/WebDriver.js
CHANGED
|
@@ -32,37 +32,43 @@ const SHADOW = 'shadow';
|
|
|
32
32
|
const webRoot = 'body';
|
|
33
33
|
|
|
34
34
|
let version;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* ## Configuration
|
|
38
|
+
*
|
|
39
|
+
* This helper should be configured in codecept.conf.js
|
|
40
|
+
*
|
|
41
|
+
* @typedef WebDriverConfig
|
|
42
|
+
* @type {object}
|
|
43
|
+
* @prop {string} url - base url of website to be tested.
|
|
44
|
+
* @prop {string} browser browser in which to perform testing.
|
|
45
|
+
* @prop {string} [basicAuth] - (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
46
|
+
* @prop {string} [host=localhost] - WebDriver host to connect.
|
|
47
|
+
* @prop {string} [port=4444] - WebDriver port to connect.
|
|
48
|
+
* @prop {string} [protocol=http] - protocol for WebDriver server.
|
|
49
|
+
* @prop {string} [path=/wd/hub] - path to WebDriver server,
|
|
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.
|
|
52
|
+
* @prop {boolean} [disableScreenshots=false] - don't save screenshots on failure.
|
|
53
|
+
* @prop {boolean} [fullPageScreenshots=false] (optional - make full page screenshots on failure.
|
|
54
|
+
* @prop {boolean} [uniqueScreenshotNames=false] - option to prevent screenshot override if you have scenarios with the same name in different suites.
|
|
55
|
+
* @prop {boolean} [keepBrowserState=false] - keep browser state between tests when `restart` is set to false.
|
|
56
|
+
* @prop {boolean} [keepCookies=false] - keep cookies between tests when `restart` set to false.
|
|
57
|
+
* @prop {string} [windowSize=window] default window size. Set to `maximize` or a dimension in the format `640x480`.
|
|
58
|
+
* @prop {number} [waitForTimeout=1000] sets default wait time in *ms* for all `wait*` functions.
|
|
59
|
+
* @prop {object} [desiredCapabilities] Selenium's [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities).
|
|
60
|
+
* @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["WebDriver"]._startBrowser()`.
|
|
61
|
+
* @prop {object} [timeouts] [WebDriver timeouts](http://webdriver.io/docs/timeouts.html) defined as hash.
|
|
62
|
+
*/
|
|
63
|
+
const config = {};
|
|
64
|
+
|
|
35
65
|
/**
|
|
36
66
|
* WebDriver helper which wraps [webdriverio](http://webdriver.io/) library to
|
|
37
67
|
* manipulate browser using Selenium WebDriver or PhantomJS.
|
|
38
68
|
*
|
|
39
69
|
* WebDriver requires Selenium Server and ChromeDriver/GeckoDriver to be installed. Those tools can be easily installed via NPM. Please check [Testing with WebDriver](https://codecept.io/webdriver/#testing-with-webdriver) for more details.
|
|
40
70
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* This helper should be configured in codecept.json or codecept.conf.js
|
|
44
|
-
*
|
|
45
|
-
* * `url`: base url of website to be tested.
|
|
46
|
-
* * `basicAuth`: (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
47
|
-
* * `browser`: browser in which to perform testing.
|
|
48
|
-
* * `host`: (optional, default: localhost) - WebDriver host to connect.
|
|
49
|
-
* * `port`: (optional, default: 4444) - WebDriver port to connect.
|
|
50
|
-
* * `protocol`: (optional, default: http) - protocol for WebDriver server.
|
|
51
|
-
* * `path`: (optional, default: /wd/hub) - path to WebDriver server,
|
|
52
|
-
* * `restart`: (optional, default: true) - restart browser between tests.
|
|
53
|
-
* * `smartWait`: (optional) **enables [SmartWait](http://codecept.io/acceptance/#smartwait)**; wait for additional milliseconds for element to appear. Enable for 5 secs: "smartWait": 5000.
|
|
54
|
-
* * `disableScreenshots`: (optional, default: false) - don't save screenshots on failure.
|
|
55
|
-
* * `fullPageScreenshots` (optional, default: false) - make full page screenshots on failure.
|
|
56
|
-
* * `uniqueScreenshotNames`: (optional, default: false) - option to prevent screenshot override if you have scenarios with the same name in different suites.
|
|
57
|
-
* * `keepBrowserState`: (optional, default: false) - keep browser state between tests when `restart` is set to false.
|
|
58
|
-
* * `keepCookies`: (optional, default: false) - keep cookies between tests when `restart` set to false.
|
|
59
|
-
* * `windowSize`: (optional) default window size. Set to `maximize` or a dimension in the format `640x480`.
|
|
60
|
-
* * `waitForTimeout`: (optional, default: 1000) sets default wait time in *ms* for all `wait*` functions.
|
|
61
|
-
* * `desiredCapabilities`: Selenium's [desired
|
|
62
|
-
* capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities).
|
|
63
|
-
* * `manualStart`: (optional, default: false) - do not start browser before a test, start it manually inside a helper
|
|
64
|
-
* with `this.helpers["WebDriver"]._startBrowser()`.
|
|
65
|
-
* * `timeouts`: [WebDriver timeouts](http://webdriver.io/docs/timeouts.html) defined as hash.
|
|
71
|
+
* <!-- configuration -->
|
|
66
72
|
*
|
|
67
73
|
* Example:
|
|
68
74
|
*
|
|
@@ -567,6 +573,9 @@ class WebDriver extends Helper {
|
|
|
567
573
|
});
|
|
568
574
|
}
|
|
569
575
|
|
|
576
|
+
if (this.browser.capabilities && this.browser.capabilities.platformName) {
|
|
577
|
+
this.browser.capabilities.platformName = this.browser.capabilities.platformName.toLowerCase();
|
|
578
|
+
}
|
|
570
579
|
return this.browser;
|
|
571
580
|
}
|
|
572
581
|
|
|
@@ -907,7 +916,7 @@ class WebDriver extends Helper {
|
|
|
907
916
|
* {{ react }}
|
|
908
917
|
*/
|
|
909
918
|
async click(locator, context = null) {
|
|
910
|
-
const clickMethod = this.browser.isMobile ? 'touchClick' : 'elementClick';
|
|
919
|
+
const clickMethod = this.browser.isMobile && this.browser.capabilities.platformName !== 'android' ? 'touchClick' : 'elementClick';
|
|
911
920
|
const locateFn = prepareLocateFn.call(this, context);
|
|
912
921
|
|
|
913
922
|
const res = await findClickable.call(this, locator, locateFn);
|
|
@@ -1117,7 +1126,7 @@ class WebDriver extends Helper {
|
|
|
1117
1126
|
* Appium: not tested
|
|
1118
1127
|
*/
|
|
1119
1128
|
async checkOption(field, context = null) {
|
|
1120
|
-
const clickMethod = this.browser.isMobile ? 'touchClick' : 'elementClick';
|
|
1129
|
+
const clickMethod = this.browser.isMobile && this.browser.capabilities.platformName !== 'android' ? 'touchClick' : 'elementClick';
|
|
1121
1130
|
const locateFn = prepareLocateFn.call(this, context);
|
|
1122
1131
|
|
|
1123
1132
|
const res = await findCheckable.call(this, field, locateFn);
|
|
@@ -1136,7 +1145,7 @@ class WebDriver extends Helper {
|
|
|
1136
1145
|
* Appium: not tested
|
|
1137
1146
|
*/
|
|
1138
1147
|
async uncheckOption(field, context = null) {
|
|
1139
|
-
const clickMethod = this.browser.isMobile ? 'touchClick' : 'elementClick';
|
|
1148
|
+
const clickMethod = this.browser.isMobile && this.browser.capabilities.platformName !== 'android' ? 'touchClick' : 'elementClick';
|
|
1140
1149
|
const locateFn = prepareLocateFn.call(this, context);
|
|
1141
1150
|
|
|
1142
1151
|
const res = await findCheckable.call(this, field, locateFn);
|
|
@@ -1623,7 +1632,7 @@ class WebDriver extends Helper {
|
|
|
1623
1632
|
assertElementExists(res);
|
|
1624
1633
|
const elem = usingFirstElement(res);
|
|
1625
1634
|
const elementId = getElementId(elem);
|
|
1626
|
-
if (this.browser.isMobile) return this.browser.touchScroll(offsetX, offsetY, elementId);
|
|
1635
|
+
if (this.browser.isMobile && this.browser.capabilities.platformName !== 'android') return this.browser.touchScroll(offsetX, offsetY, elementId);
|
|
1627
1636
|
const location = await elem.getLocation();
|
|
1628
1637
|
assertElementExists(location, 'Failed to receive', 'location');
|
|
1629
1638
|
/* eslint-disable prefer-arrow-callback */
|
|
@@ -1631,7 +1640,7 @@ class WebDriver extends Helper {
|
|
|
1631
1640
|
/* eslint-enable */
|
|
1632
1641
|
}
|
|
1633
1642
|
|
|
1634
|
-
if (this.browser.isMobile) return this.browser.touchScroll(locator, offsetX, offsetY);
|
|
1643
|
+
if (this.browser.isMobile && this.browser.capabilities.platformName !== 'android') return this.browser.touchScroll(locator, offsetX, offsetY);
|
|
1635
1644
|
|
|
1636
1645
|
/* eslint-disable prefer-arrow-callback, comma-dangle */
|
|
1637
1646
|
return this.browser.execute(function (x, y) { return window.scrollTo(x, y); }, offsetX, offsetY);
|
|
@@ -91,7 +91,7 @@ module.exports = (text, file) => {
|
|
|
91
91
|
current[placeholder] = value;
|
|
92
92
|
exampleSteps = exampleSteps.map((step) => {
|
|
93
93
|
step = { ...step };
|
|
94
|
-
step.text = step.text.
|
|
94
|
+
step.text = step.text.split(`<${placeholder}>`).join(value);
|
|
95
95
|
return step;
|
|
96
96
|
});
|
|
97
97
|
}
|
|
@@ -70,23 +70,70 @@ const defaultConfig = {
|
|
|
70
70
|
* I.seeElement('=user'); // matches => [data-qa=user]
|
|
71
71
|
* I.click('=sign-up'); // matches => [data-qa=sign-up]
|
|
72
72
|
* ```
|
|
73
|
+
*
|
|
74
|
+
* Using `data-qa` OR `data-test` attribute with `=` prefix:
|
|
75
|
+
*
|
|
76
|
+
* ```js
|
|
77
|
+
* // in codecept.conf.js
|
|
78
|
+
* plugins: {
|
|
79
|
+
* customLocator: {
|
|
80
|
+
* enabled: true,
|
|
81
|
+
* prefix: '=',
|
|
82
|
+
* attribute: ['data-qa', 'data-test'],
|
|
83
|
+
* strategy: 'xpath'
|
|
84
|
+
* }
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* In a test:
|
|
89
|
+
*
|
|
90
|
+
* ```js
|
|
91
|
+
* I.seeElement('=user'); // matches => //*[@data-qa=user or @data-test=user]
|
|
92
|
+
* I.click('=sign-up'); // matches => //*[data-qa=sign-up or @data-test=sign-up]
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* ```js
|
|
96
|
+
* // in codecept.conf.js
|
|
97
|
+
* plugins: {
|
|
98
|
+
* customLocator: {
|
|
99
|
+
* enabled: true,
|
|
100
|
+
* prefix: '=',
|
|
101
|
+
* attribute: ['data-qa', 'data-test'],
|
|
102
|
+
* strategy: 'css'
|
|
103
|
+
* }
|
|
104
|
+
* }
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* In a test:
|
|
108
|
+
*
|
|
109
|
+
* ```js
|
|
110
|
+
* I.seeElement('=user'); // matches => [data-qa=user],[data-test=user]
|
|
111
|
+
* I.click('=sign-up'); // matches => [data-qa=sign-up],[data-test=sign-up]
|
|
112
|
+
* ```
|
|
73
113
|
*/
|
|
74
114
|
module.exports = (config) => {
|
|
75
|
-
config =
|
|
115
|
+
config = { ...defaultConfig, ...config };
|
|
76
116
|
|
|
77
117
|
Locator.addFilter((value, locatorObj) => {
|
|
78
118
|
if (typeof value !== 'string') return;
|
|
79
119
|
if (!value.startsWith(config.prefix)) return;
|
|
80
120
|
|
|
121
|
+
if (!['String', 'Array'].includes(config.attribute.constructor.name)) return;
|
|
122
|
+
|
|
81
123
|
const val = value.substr(config.prefix.length);
|
|
82
124
|
|
|
83
125
|
if (config.strategy.toLowerCase() === 'xpath') {
|
|
84
|
-
locatorObj.value = `.//*[
|
|
126
|
+
locatorObj.value = `.//*[${
|
|
127
|
+
[].concat(config.attribute)
|
|
128
|
+
.map((attr) => `@${attr}=${xpathLocator.literal(val)}`)
|
|
129
|
+
.join(' or ')}]`;
|
|
85
130
|
locatorObj.type = 'xpath';
|
|
86
131
|
}
|
|
87
132
|
|
|
88
133
|
if (config.strategy.toLowerCase() === 'css') {
|
|
89
|
-
locatorObj.value =
|
|
134
|
+
locatorObj.value = [].concat(config.attribute)
|
|
135
|
+
.map((attr) => `[${attr}=${val}]`)
|
|
136
|
+
.join(',');
|
|
90
137
|
locatorObj.type = 'css';
|
|
91
138
|
}
|
|
92
139
|
|
package/lib/plugin/retryTo.js
CHANGED
|
@@ -101,27 +101,20 @@ module.exports = function (config) {
|
|
|
101
101
|
err = e;
|
|
102
102
|
recorder.session.restore(`retryTo ${tries}`);
|
|
103
103
|
tries++;
|
|
104
|
-
// recorder.session.restore(`retryTo`);
|
|
105
104
|
if (tries <= maxTries) {
|
|
106
105
|
debug(`Error ${err}... Retrying`);
|
|
107
106
|
err = null;
|
|
108
107
|
|
|
109
|
-
recorder.add(`retryTo ${tries}`, () =>
|
|
110
|
-
tryBlock();
|
|
111
|
-
// recorder.add(() => new Promise(done => setTimeout(done, pollInterval)));
|
|
112
|
-
});
|
|
108
|
+
recorder.add(`retryTo ${tries}`, () => setTimeout(tryBlock, pollInterval));
|
|
113
109
|
} else {
|
|
114
|
-
// recorder.throw(err);
|
|
115
110
|
done(null);
|
|
116
111
|
}
|
|
117
112
|
});
|
|
118
|
-
// return recorder.promise();
|
|
119
113
|
};
|
|
120
114
|
|
|
121
115
|
recorder.add('retryTo', async () => {
|
|
122
116
|
store.debugMode = true;
|
|
123
117
|
tryBlock();
|
|
124
|
-
// recorder.add(() => recorder.session.restore(`retryTo ${tries-1}`));
|
|
125
118
|
});
|
|
126
119
|
}).then(() => {
|
|
127
120
|
if (err) recorder.throw(err);
|
package/lib/secret.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/* eslint-disable max-classes-per-file */
|
|
2
|
+
const { deepClone } = require('./utils');
|
|
3
|
+
|
|
1
4
|
/** @param {string} secret */
|
|
2
5
|
class Secret {
|
|
3
6
|
constructor(secret) {
|
|
@@ -9,13 +12,40 @@ class Secret {
|
|
|
9
12
|
return this._secret;
|
|
10
13
|
}
|
|
11
14
|
|
|
15
|
+
getMasked() {
|
|
16
|
+
return '*****';
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
/**
|
|
13
|
-
* @param {
|
|
20
|
+
* @param {...*} secret
|
|
14
21
|
* @returns {Secret}
|
|
15
22
|
*/
|
|
16
23
|
static secret(secret) {
|
|
24
|
+
if (typeof secret === 'object') {
|
|
25
|
+
const fields = Array.from(arguments);
|
|
26
|
+
fields.shift();
|
|
27
|
+
return secretObject(secret, fields);
|
|
28
|
+
}
|
|
17
29
|
return new Secret(secret);
|
|
18
30
|
}
|
|
19
31
|
}
|
|
20
32
|
|
|
33
|
+
function secretObject(obj, fieldsToHide = []) {
|
|
34
|
+
const handler = {
|
|
35
|
+
get(obj, prop) {
|
|
36
|
+
if (prop === 'toString') {
|
|
37
|
+
return function () {
|
|
38
|
+
const maskedObject = deepClone(obj);
|
|
39
|
+
fieldsToHide.forEach(f => maskedObject[f] = '****');
|
|
40
|
+
return JSON.stringify(maskedObject);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return obj[prop];
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return new Proxy(obj, handler);
|
|
49
|
+
}
|
|
50
|
+
|
|
21
51
|
module.exports = Secret;
|
package/lib/step.js
CHANGED
|
@@ -168,7 +168,7 @@ class Step {
|
|
|
168
168
|
} else if (typeof arg === 'undefined') {
|
|
169
169
|
return `${arg}`;
|
|
170
170
|
} else if (arg instanceof Secret) {
|
|
171
|
-
return
|
|
171
|
+
return arg.getMasked();
|
|
172
172
|
} else if (arg.toString && arg.toString() !== '[object Object]') {
|
|
173
173
|
return arg.toString();
|
|
174
174
|
} else if (typeof arg === 'object') {
|
|
@@ -269,15 +269,27 @@ class MetaStep extends Step {
|
|
|
269
269
|
};
|
|
270
270
|
event.dispatcher.prependListener(event.step.before, registerStep);
|
|
271
271
|
let rethrownError = null;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
272
|
+
if (fn.constructor.name === 'AsyncFunction') {
|
|
273
|
+
result = fn.apply(this.context, this.args).then((result) => {
|
|
274
|
+
return result;
|
|
275
|
+
}).catch(function (error) {
|
|
276
|
+
this.setStatus('failed');
|
|
277
|
+
rethrownError = error;
|
|
278
|
+
}).finally(() => {
|
|
279
|
+
this.endTime = Date.now();
|
|
280
|
+
event.dispatcher.removeListener(event.step.before, registerStep);
|
|
281
|
+
});
|
|
282
|
+
} else {
|
|
283
|
+
try {
|
|
284
|
+
this.startTime = Date.now();
|
|
285
|
+
result = fn.apply(this.context, this.args);
|
|
286
|
+
} catch (error) {
|
|
287
|
+
this.setStatus('failed');
|
|
288
|
+
rethrownError = error;
|
|
289
|
+
} finally {
|
|
290
|
+
this.endTime = Date.now();
|
|
291
|
+
event.dispatcher.removeListener(event.step.before, registerStep);
|
|
292
|
+
}
|
|
281
293
|
}
|
|
282
294
|
if (rethrownError) { throw rethrownError; }
|
|
283
295
|
return result;
|
package/lib/utils.js
CHANGED
|
@@ -33,12 +33,7 @@ const isAsyncFunction = module.exports.isAsyncFunction = function (fn) {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
module.exports.fileExists = function (filePath) {
|
|
36
|
-
|
|
37
|
-
fs.statSync(filePath);
|
|
38
|
-
} catch (err) {
|
|
39
|
-
if (err.code === 'ENOENT') return false;
|
|
40
|
-
}
|
|
41
|
-
return true;
|
|
36
|
+
return fs.existsSync(filePath);
|
|
42
37
|
};
|
|
43
38
|
|
|
44
39
|
module.exports.isFile = function (filePath) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeceptjs",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.5-beta.2",
|
|
4
4
|
"description": "Supercharged End 2 End Testing Framework for NodeJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"acceptance",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"@wdio/selenium-standalone-service": "^5.16.10",
|
|
102
102
|
"@wdio/utils": "^5.23.0",
|
|
103
103
|
"apollo-server-express": "^2.25.3",
|
|
104
|
-
"chai-as-promised": "^
|
|
104
|
+
"chai-as-promised": "^7.1.1",
|
|
105
105
|
"chai-subset": "^1.6.0",
|
|
106
106
|
"contributor-faces": "^1.0.3",
|
|
107
107
|
"documentation": "^12.3.0",
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
"mocha-parallel-tests": "^2.3.0",
|
|
123
123
|
"nightmare": "^3.0.2",
|
|
124
124
|
"nodemon": "^1.19.4",
|
|
125
|
-
"playwright": "^1.
|
|
125
|
+
"playwright": "^1.23.2",
|
|
126
126
|
"puppeteer": "^10.4.0",
|
|
127
127
|
"qrcode-terminal": "^0.12.0",
|
|
128
128
|
"rosie": "^1.6.0",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"sinon-chai": "^3.7.0",
|
|
133
133
|
"testcafe": "^1.18.3",
|
|
134
134
|
"ts-morph": "^3.1.3",
|
|
135
|
-
"tsd-jsdoc": "
|
|
135
|
+
"tsd-jsdoc": "https://github.com/englercj/tsd-jsdoc.git",
|
|
136
136
|
"typescript": "^4.4.3",
|
|
137
137
|
"wdio-docker-service": "^1.5.0",
|
|
138
138
|
"webdriverio": "^7.16.14",
|