codeceptjs 3.4.0 â 3.5.0
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 +70 -0
- package/README.md +9 -7
- package/bin/codecept.js +1 -1
- package/docs/ai.md +246 -0
- package/docs/build/Appium.js +47 -7
- package/docs/build/JSONResponse.js +4 -4
- package/docs/build/Nightmare.js +3 -1
- package/docs/build/OpenAI.js +122 -0
- package/docs/build/Playwright.js +193 -45
- package/docs/build/Protractor.js +3 -1
- package/docs/build/Puppeteer.js +45 -12
- package/docs/build/REST.js +15 -5
- package/docs/build/TestCafe.js +3 -1
- package/docs/build/WebDriver.js +30 -5
- package/docs/changelog.md +70 -0
- package/docs/helpers/Appium.md +152 -147
- package/docs/helpers/JSONResponse.md +4 -4
- package/docs/helpers/Nightmare.md +2 -0
- package/docs/helpers/OpenAI.md +70 -0
- package/docs/helpers/Playwright.md +194 -152
- package/docs/helpers/Puppeteer.md +6 -0
- package/docs/helpers/REST.md +6 -5
- package/docs/helpers/TestCafe.md +2 -0
- package/docs/helpers/WebDriver.md +10 -4
- package/docs/mobile.md +49 -2
- package/docs/parallel.md +56 -0
- package/docs/plugins.md +87 -33
- package/docs/secrets.md +6 -0
- package/docs/tutorial.md +5 -5
- package/docs/webapi/appendField.mustache +2 -0
- package/docs/webapi/type.mustache +3 -0
- package/lib/ai.js +171 -0
- package/lib/cli.js +1 -1
- package/lib/codecept.js +4 -0
- package/lib/command/dryRun.js +9 -1
- package/lib/command/generate.js +46 -3
- package/lib/command/init.js +13 -1
- package/lib/command/interactive.js +15 -1
- package/lib/command/run-workers.js +2 -1
- package/lib/container.js +13 -3
- package/lib/helper/Appium.js +45 -7
- package/lib/helper/JSONResponse.js +4 -4
- package/lib/helper/Nightmare.js +1 -1
- package/lib/helper/OpenAI.js +122 -0
- package/lib/helper/Playwright.js +190 -38
- package/lib/helper/Protractor.js +1 -1
- package/lib/helper/Puppeteer.js +40 -12
- package/lib/helper/REST.js +15 -5
- package/lib/helper/TestCafe.js +1 -1
- package/lib/helper/WebDriver.js +25 -5
- package/lib/helper/scripts/highlightElement.js +20 -0
- package/lib/html.js +258 -0
- package/lib/listener/retry.js +2 -1
- package/lib/pause.js +73 -17
- package/lib/plugin/debugErrors.js +67 -0
- package/lib/plugin/fakerTransform.js +4 -6
- package/lib/plugin/heal.js +179 -0
- package/lib/plugin/screenshotOnFail.js +11 -2
- package/lib/plugin/wdio.js +4 -12
- package/lib/recorder.js +4 -4
- package/lib/scenario.js +6 -4
- package/lib/secret.js +5 -4
- package/lib/step.js +6 -1
- package/lib/ui.js +4 -3
- package/lib/utils.js +4 -0
- package/lib/workers.js +57 -9
- package/package.json +26 -14
- package/translations/ja-JP.js +9 -9
- package/typings/index.d.ts +43 -9
- package/typings/promiseBasedTypes.d.ts +124 -24
- package/typings/types.d.ts +138 -30
package/docs/build/Puppeteer.js
CHANGED
|
@@ -6,6 +6,7 @@ const path = require('path');
|
|
|
6
6
|
const Helper = require('@codeceptjs/helper');
|
|
7
7
|
const Locator = require('../locator');
|
|
8
8
|
const recorder = require('../recorder');
|
|
9
|
+
const store = require('../store');
|
|
9
10
|
const stringIncludes = require('../assert/include').includes;
|
|
10
11
|
const { urlEquals } = require('../assert/equal');
|
|
11
12
|
const { equals } = require('../assert/equal');
|
|
@@ -33,6 +34,7 @@ const RemoteBrowserConnectionRefused = require('./errors/RemoteBrowserConnection
|
|
|
33
34
|
const Popup = require('./extras/Popup');
|
|
34
35
|
const Console = require('./extras/Console');
|
|
35
36
|
const findReact = require('./extras/React');
|
|
37
|
+
const { highlightElement } = require('./scripts/highlightElement');
|
|
36
38
|
|
|
37
39
|
let puppeteer;
|
|
38
40
|
let perfTiming;
|
|
@@ -65,6 +67,7 @@ const consoleLogStore = new Console();
|
|
|
65
67
|
* @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
|
|
66
68
|
* @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
|
|
67
69
|
* @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
|
|
70
|
+
* @prop {boolean} [highlightElement] - highlight the interacting elements
|
|
68
71
|
*/
|
|
69
72
|
const config = {};
|
|
70
73
|
|
|
@@ -1643,6 +1646,9 @@ class Puppeteer extends Helper {
|
|
|
1643
1646
|
*
|
|
1644
1647
|
* // passing in an array
|
|
1645
1648
|
* I.type(['T', 'E', 'X', 'T']);
|
|
1649
|
+
*
|
|
1650
|
+
* // passing a secret
|
|
1651
|
+
* I.type(secret('123456'));
|
|
1646
1652
|
* ```
|
|
1647
1653
|
*
|
|
1648
1654
|
* @param {string|string[]} key or array of keys to type.
|
|
@@ -1652,6 +1658,7 @@ class Puppeteer extends Helper {
|
|
|
1652
1658
|
*/
|
|
1653
1659
|
async type(keys, delay = null) {
|
|
1654
1660
|
if (!Array.isArray(keys)) {
|
|
1661
|
+
keys = keys.toString();
|
|
1655
1662
|
keys = keys.split('');
|
|
1656
1663
|
}
|
|
1657
1664
|
|
|
@@ -1692,7 +1699,10 @@ class Puppeteer extends Helper {
|
|
|
1692
1699
|
} else if (editable) {
|
|
1693
1700
|
await this._evaluateHandeInContext(el => el.innerHTML = '', el);
|
|
1694
1701
|
}
|
|
1702
|
+
|
|
1703
|
+
highlightActiveElement.call(this, el, this.page);
|
|
1695
1704
|
await el.type(value.toString(), { delay: this.options.pressKeyDelay });
|
|
1705
|
+
|
|
1696
1706
|
return this._waitForAction();
|
|
1697
1707
|
}
|
|
1698
1708
|
|
|
@@ -1718,6 +1728,8 @@ class Puppeteer extends Helper {
|
|
|
1718
1728
|
*
|
|
1719
1729
|
* ```js
|
|
1720
1730
|
* I.appendField('#myTextField', 'appended');
|
|
1731
|
+
* // typing secret
|
|
1732
|
+
* I.appendField('password', secret('123456'));
|
|
1721
1733
|
* ```
|
|
1722
1734
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator
|
|
1723
1735
|
* @param {string} value text value to append.
|
|
@@ -1729,8 +1741,9 @@ class Puppeteer extends Helper {
|
|
|
1729
1741
|
async appendField(field, value) {
|
|
1730
1742
|
const els = await findVisibleFields.call(this, field);
|
|
1731
1743
|
assertElementExists(els, field, 'Field');
|
|
1744
|
+
highlightActiveElement.call(this, els[0], this.page);
|
|
1732
1745
|
await els[0].press('End');
|
|
1733
|
-
await els[0].type(value, { delay: this.options.pressKeyDelay });
|
|
1746
|
+
await els[0].type(value.toString(), { delay: this.options.pressKeyDelay });
|
|
1734
1747
|
return this._waitForAction();
|
|
1735
1748
|
}
|
|
1736
1749
|
|
|
@@ -1831,6 +1844,7 @@ class Puppeteer extends Helper {
|
|
|
1831
1844
|
if (await el.getProperty('tagName').then(t => t.jsonValue()) !== 'SELECT') {
|
|
1832
1845
|
throw new Error('Element is not <select>');
|
|
1833
1846
|
}
|
|
1847
|
+
highlightActiveElement.call(this, els[0], this.page);
|
|
1834
1848
|
if (!Array.isArray(option)) option = [option];
|
|
1835
1849
|
|
|
1836
1850
|
for (const key in option) {
|
|
@@ -2680,23 +2694,32 @@ class Puppeteer extends Helper {
|
|
|
2680
2694
|
*/
|
|
2681
2695
|
async saveScreenshot(fileName, fullPage) {
|
|
2682
2696
|
const fullPageOption = fullPage || this.options.fullPageScreenshots;
|
|
2683
|
-
|
|
2697
|
+
let outputFile = screenshotOutputFolder(fileName);
|
|
2684
2698
|
|
|
2685
2699
|
this.debug(`Screenshot is saving to ${outputFile}`);
|
|
2686
2700
|
|
|
2701
|
+
await this.page.screenshot({
|
|
2702
|
+
path: outputFile,
|
|
2703
|
+
fullPage: fullPageOption,
|
|
2704
|
+
type: 'png',
|
|
2705
|
+
});
|
|
2706
|
+
|
|
2687
2707
|
if (this.activeSessionName) {
|
|
2688
|
-
const
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2708
|
+
for (const sessionName in this.sessionPages) {
|
|
2709
|
+
const activeSessionPage = this.sessionPages[sessionName];
|
|
2710
|
+
outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`);
|
|
2711
|
+
|
|
2712
|
+
this.debug(`${sessionName} - Screenshot is saving to ${outputFile}`);
|
|
2713
|
+
|
|
2714
|
+
if (activeSessionPage) {
|
|
2715
|
+
await activeSessionPage.screenshot({
|
|
2716
|
+
path: outputFile,
|
|
2717
|
+
fullPage: fullPageOption,
|
|
2718
|
+
type: 'png',
|
|
2719
|
+
});
|
|
2720
|
+
}
|
|
2696
2721
|
}
|
|
2697
2722
|
}
|
|
2698
|
-
|
|
2699
|
-
return this.page.screenshot({ path: outputFile, fullPage: fullPageOption, type: 'png' });
|
|
2700
2723
|
}
|
|
2701
2724
|
|
|
2702
2725
|
async _failed() {
|
|
@@ -3358,12 +3381,16 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
3358
3381
|
} else {
|
|
3359
3382
|
assertElementExists(els, locator, 'Clickable element');
|
|
3360
3383
|
}
|
|
3384
|
+
|
|
3385
|
+
highlightActiveElement.call(this, els[0], this.page);
|
|
3386
|
+
|
|
3361
3387
|
await els[0].click(options);
|
|
3362
3388
|
const promises = [];
|
|
3363
3389
|
if (options.waitForNavigation) {
|
|
3364
3390
|
promises.push(this.waitForNavigation());
|
|
3365
3391
|
}
|
|
3366
3392
|
promises.push(this._waitForAction());
|
|
3393
|
+
|
|
3367
3394
|
return Promise.all(promises);
|
|
3368
3395
|
}
|
|
3369
3396
|
|
|
@@ -3708,3 +3735,9 @@ function getNormalizedKey(key) {
|
|
|
3708
3735
|
}
|
|
3709
3736
|
return normalizedKey;
|
|
3710
3737
|
}
|
|
3738
|
+
|
|
3739
|
+
function highlightActiveElement(element, context) {
|
|
3740
|
+
if (!this.options.enableHighlight && !store.debugMode) return;
|
|
3741
|
+
|
|
3742
|
+
highlightElement(element, context);
|
|
3743
|
+
}
|
package/docs/build/REST.js
CHANGED
|
@@ -34,7 +34,8 @@ const config = {};
|
|
|
34
34
|
* endpoint: 'http://site.com/api',
|
|
35
35
|
* prettyPrintJson: true,
|
|
36
36
|
* onRequest: (request) => {
|
|
37
|
-
*
|
|
37
|
+
* request.headers.auth = '123';
|
|
38
|
+
* }
|
|
38
39
|
* }
|
|
39
40
|
* }
|
|
40
41
|
*}
|
|
@@ -136,6 +137,15 @@ class REST extends Helper {
|
|
|
136
137
|
request.auth = this.headers.auth;
|
|
137
138
|
}
|
|
138
139
|
|
|
140
|
+
if (typeof request.data === 'object') {
|
|
141
|
+
const returnedValue = {};
|
|
142
|
+
for (const [key, value] of Object.entries(request.data)) {
|
|
143
|
+
returnedValue[key] = value;
|
|
144
|
+
if (value instanceof Secret) returnedValue[key] = value.getMasked();
|
|
145
|
+
}
|
|
146
|
+
_debugRequest.data = returnedValue;
|
|
147
|
+
}
|
|
148
|
+
|
|
139
149
|
if (request.data instanceof Secret) {
|
|
140
150
|
_debugRequest.data = '*****';
|
|
141
151
|
request.data = (typeof request.data === 'object' && !(request.data instanceof Secret)) ? { ...request.data.toString() } : request.data.toString();
|
|
@@ -198,7 +208,7 @@ class REST extends Helper {
|
|
|
198
208
|
* ```
|
|
199
209
|
*
|
|
200
210
|
* @param {*} url
|
|
201
|
-
* @param {object} [headers={}] - the headers object to be sent. By default it is sent as an empty object
|
|
211
|
+
* @param {object} [headers={}] - the headers object to be sent. By default, it is sent as an empty object
|
|
202
212
|
*
|
|
203
213
|
* @returns {Promise<*>} response
|
|
204
214
|
*/
|
|
@@ -222,8 +232,8 @@ class REST extends Helper {
|
|
|
222
232
|
* ```
|
|
223
233
|
*
|
|
224
234
|
* @param {*} url
|
|
225
|
-
* @param {*} [payload={}] - the payload to be sent. By default it is sent as an empty object
|
|
226
|
-
* @param {object} [headers={}] - the headers object to be sent. By default it is sent as an empty object
|
|
235
|
+
* @param {*} [payload={}] - the payload to be sent. By default, it is sent as an empty object
|
|
236
|
+
* @param {object} [headers={}] - the headers object to be sent. By default, it is sent as an empty object
|
|
227
237
|
*
|
|
228
238
|
* @returns {Promise<*>} response
|
|
229
239
|
*/
|
|
@@ -317,7 +327,7 @@ class REST extends Helper {
|
|
|
317
327
|
* ```
|
|
318
328
|
*
|
|
319
329
|
* @param {*} url
|
|
320
|
-
* @param {object} [headers={}] - the headers object to be sent. By default it is sent as an empty object
|
|
330
|
+
* @param {object} [headers={}] - the headers object to be sent. By default, it is sent as an empty object
|
|
321
331
|
*
|
|
322
332
|
* @returns {Promise<*>} response
|
|
323
333
|
*/
|
package/docs/build/TestCafe.js
CHANGED
|
@@ -488,6 +488,8 @@ class TestCafe extends Helper {
|
|
|
488
488
|
*
|
|
489
489
|
* ```js
|
|
490
490
|
* I.appendField('#myTextField', 'appended');
|
|
491
|
+
* // typing secret
|
|
492
|
+
* I.appendField('password', secret('123456'));
|
|
491
493
|
* ```
|
|
492
494
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator
|
|
493
495
|
* @param {string} value text value to append.
|
|
@@ -501,7 +503,7 @@ class TestCafe extends Helper {
|
|
|
501
503
|
const el = await els.nth(0);
|
|
502
504
|
|
|
503
505
|
return this.t
|
|
504
|
-
.typeText(el, value, { replace: false })
|
|
506
|
+
.typeText(el, value.toString(), { replace: false })
|
|
505
507
|
.catch(mapError);
|
|
506
508
|
}
|
|
507
509
|
|
package/docs/build/WebDriver.js
CHANGED
|
@@ -5,6 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
|
|
7
7
|
const Helper = require('@codeceptjs/helper');
|
|
8
|
+
const crypto = require('crypto');
|
|
8
9
|
const stringIncludes = require('../assert/include').includes;
|
|
9
10
|
const { urlEquals, equals } = require('../assert/equal');
|
|
10
11
|
const { debug } = require('../output');
|
|
@@ -27,6 +28,8 @@ const {
|
|
|
27
28
|
const ElementNotFound = require('./errors/ElementNotFound');
|
|
28
29
|
const ConnectionRefused = require('./errors/ConnectionRefused');
|
|
29
30
|
const Locator = require('../locator');
|
|
31
|
+
const { highlightElement } = require('./scripts/highlightElement');
|
|
32
|
+
const store = require('../store');
|
|
30
33
|
|
|
31
34
|
const SHADOW = 'shadow';
|
|
32
35
|
const webRoot = 'body';
|
|
@@ -39,7 +42,7 @@ const webRoot = 'body';
|
|
|
39
42
|
* @typedef WebDriverConfig
|
|
40
43
|
* @type {object}
|
|
41
44
|
* @prop {string} url - base url of website to be tested.
|
|
42
|
-
* @prop {string} browser
|
|
45
|
+
* @prop {string} browser - Browser in which to perform testing.
|
|
43
46
|
* @prop {string} [basicAuth] - (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
44
47
|
* @prop {string} [host=localhost] - WebDriver host to connect.
|
|
45
48
|
* @prop {number} [port=4444] - WebDriver port to connect.
|
|
@@ -57,6 +60,7 @@ const webRoot = 'body';
|
|
|
57
60
|
* @prop {object} [desiredCapabilities] Selenium's [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities).
|
|
58
61
|
* @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["WebDriver"]._startBrowser()`.
|
|
59
62
|
* @prop {object} [timeouts] [WebDriver timeouts](http://webdriver.io/docs/timeouts.html) defined as hash.
|
|
63
|
+
* @prop {boolean} [highlightElement] - highlight the interacting elements
|
|
60
64
|
*/
|
|
61
65
|
const config = {};
|
|
62
66
|
|
|
@@ -822,7 +826,7 @@ class WebDriver extends Helper {
|
|
|
822
826
|
}
|
|
823
827
|
|
|
824
828
|
/**
|
|
825
|
-
* Find a checkbox by providing human
|
|
829
|
+
* Find a checkbox by providing human-readable text:
|
|
826
830
|
*
|
|
827
831
|
* ```js
|
|
828
832
|
* this.helpers['WebDriver']._locateCheckable('I agree with terms and conditions').then // ...
|
|
@@ -835,7 +839,7 @@ class WebDriver extends Helper {
|
|
|
835
839
|
}
|
|
836
840
|
|
|
837
841
|
/**
|
|
838
|
-
* Find a clickable element by providing human
|
|
842
|
+
* Find a clickable element by providing human-readable text:
|
|
839
843
|
*
|
|
840
844
|
* ```js
|
|
841
845
|
* const els = await this.helpers.WebDriver._locateClickable('Next page');
|
|
@@ -850,7 +854,7 @@ class WebDriver extends Helper {
|
|
|
850
854
|
}
|
|
851
855
|
|
|
852
856
|
/**
|
|
853
|
-
* Find field elements by providing human
|
|
857
|
+
* Find field elements by providing human-readable text:
|
|
854
858
|
*
|
|
855
859
|
* ```js
|
|
856
860
|
* this.helpers['WebDriver']._locateFields('Your email').then // ...
|
|
@@ -949,6 +953,7 @@ class WebDriver extends Helper {
|
|
|
949
953
|
assertElementExists(res, locator, 'Clickable element');
|
|
950
954
|
}
|
|
951
955
|
const elem = usingFirstElement(res);
|
|
956
|
+
highlightActiveElement.call(this, elem);
|
|
952
957
|
return this.browser[clickMethod](getElementId(elem));
|
|
953
958
|
}
|
|
954
959
|
|
|
@@ -995,6 +1000,7 @@ class WebDriver extends Helper {
|
|
|
995
1000
|
assertElementExists(res, locator, 'Clickable element');
|
|
996
1001
|
}
|
|
997
1002
|
const elem = usingFirstElement(res);
|
|
1003
|
+
highlightActiveElement.call(this, elem);
|
|
998
1004
|
|
|
999
1005
|
return this.executeScript((el) => {
|
|
1000
1006
|
if (document.activeElement instanceof HTMLElement) {
|
|
@@ -1035,6 +1041,7 @@ class WebDriver extends Helper {
|
|
|
1035
1041
|
}
|
|
1036
1042
|
|
|
1037
1043
|
const elem = usingFirstElement(res);
|
|
1044
|
+
highlightActiveElement.call(this, elem);
|
|
1038
1045
|
return elem.doubleClick();
|
|
1039
1046
|
}
|
|
1040
1047
|
|
|
@@ -1148,6 +1155,7 @@ class WebDriver extends Helper {
|
|
|
1148
1155
|
const res = await findFields.call(this, field);
|
|
1149
1156
|
assertElementExists(res, field, 'Field');
|
|
1150
1157
|
const elem = usingFirstElement(res);
|
|
1158
|
+
highlightActiveElement.call(this, elem);
|
|
1151
1159
|
return elem.setValue(value.toString());
|
|
1152
1160
|
}
|
|
1153
1161
|
|
|
@@ -1157,6 +1165,8 @@ class WebDriver extends Helper {
|
|
|
1157
1165
|
*
|
|
1158
1166
|
* ```js
|
|
1159
1167
|
* I.appendField('#myTextField', 'appended');
|
|
1168
|
+
* // typing secret
|
|
1169
|
+
* I.appendField('password', secret('123456'));
|
|
1160
1170
|
* ```
|
|
1161
1171
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator
|
|
1162
1172
|
* @param {string} value text value to append.
|
|
@@ -1168,7 +1178,8 @@ class WebDriver extends Helper {
|
|
|
1168
1178
|
const res = await findFields.call(this, field);
|
|
1169
1179
|
assertElementExists(res, field, 'Field');
|
|
1170
1180
|
const elem = usingFirstElement(res);
|
|
1171
|
-
|
|
1181
|
+
highlightActiveElement.call(this, elem);
|
|
1182
|
+
return elem.addValue(value.toString());
|
|
1172
1183
|
}
|
|
1173
1184
|
|
|
1174
1185
|
/**
|
|
@@ -1188,6 +1199,7 @@ class WebDriver extends Helper {
|
|
|
1188
1199
|
const res = await findFields.call(this, field);
|
|
1189
1200
|
assertElementExists(res, field, 'Field');
|
|
1190
1201
|
const elem = usingFirstElement(res);
|
|
1202
|
+
highlightActiveElement.call(this, elem);
|
|
1191
1203
|
return elem.clearValue(getElementId(elem));
|
|
1192
1204
|
}
|
|
1193
1205
|
|
|
@@ -1219,6 +1231,7 @@ class WebDriver extends Helper {
|
|
|
1219
1231
|
const res = await findFields.call(this, select);
|
|
1220
1232
|
assertElementExists(res, select, 'Selectable field');
|
|
1221
1233
|
const elem = usingFirstElement(res);
|
|
1234
|
+
highlightActiveElement.call(this, elem);
|
|
1222
1235
|
|
|
1223
1236
|
if (!Array.isArray(option)) {
|
|
1224
1237
|
option = [option];
|
|
@@ -1310,6 +1323,7 @@ class WebDriver extends Helper {
|
|
|
1310
1323
|
assertElementExists(res, field, 'Checkable');
|
|
1311
1324
|
const elem = usingFirstElement(res);
|
|
1312
1325
|
const elementId = getElementId(elem);
|
|
1326
|
+
highlightActiveElement.call(this, elem);
|
|
1313
1327
|
|
|
1314
1328
|
const isSelected = await this.browser.isElementSelected(elementId);
|
|
1315
1329
|
if (isSelected) return Promise.resolve(true);
|
|
@@ -1342,6 +1356,7 @@ class WebDriver extends Helper {
|
|
|
1342
1356
|
assertElementExists(res, field, 'Checkable');
|
|
1343
1357
|
const elem = usingFirstElement(res);
|
|
1344
1358
|
const elementId = getElementId(elem);
|
|
1359
|
+
highlightActiveElement.call(this, elem);
|
|
1345
1360
|
|
|
1346
1361
|
const isSelected = await this.browser.isElementSelected(elementId);
|
|
1347
1362
|
if (!isSelected) return Promise.resolve(true);
|
|
@@ -2689,6 +2704,9 @@ class WebDriver extends Helper {
|
|
|
2689
2704
|
*
|
|
2690
2705
|
* // passing in an array
|
|
2691
2706
|
* I.type(['T', 'E', 'X', 'T']);
|
|
2707
|
+
*
|
|
2708
|
+
* // passing a secret
|
|
2709
|
+
* I.type(secret('123456'));
|
|
2692
2710
|
* ```
|
|
2693
2711
|
*
|
|
2694
2712
|
* @param {string|string[]} key or array of keys to type.
|
|
@@ -2698,6 +2716,7 @@ class WebDriver extends Helper {
|
|
|
2698
2716
|
*/
|
|
2699
2717
|
async type(keys, delay = null) {
|
|
2700
2718
|
if (!Array.isArray(keys)) {
|
|
2719
|
+
keys = keys.toString();
|
|
2701
2720
|
keys = keys.split('');
|
|
2702
2721
|
}
|
|
2703
2722
|
if (delay) {
|
|
@@ -4000,6 +4019,12 @@ function isModifierKey(key) {
|
|
|
4000
4019
|
return unicodeModifierKeys.includes(key);
|
|
4001
4020
|
}
|
|
4002
4021
|
|
|
4022
|
+
function highlightActiveElement(element) {
|
|
4023
|
+
if (!this.options.enableHighlight && !store.debugMode) return;
|
|
4024
|
+
|
|
4025
|
+
highlightElement(element, this.browser);
|
|
4026
|
+
}
|
|
4027
|
+
|
|
4003
4028
|
function prepareLocateFn(context) {
|
|
4004
4029
|
if (!context) return this._locate.bind(this);
|
|
4005
4030
|
return (l) => {
|
package/docs/changelog.md
CHANGED
|
@@ -7,6 +7,76 @@ layout: Section
|
|
|
7
7
|
|
|
8
8
|
# Releases
|
|
9
9
|
|
|
10
|
+
## 3.5.0
|
|
11
|
+
|
|
12
|
+
đŠī¸ Features
|
|
13
|
+
|
|
14
|
+
- **đĒ [AI Powered Test Automation](/ai)** - use OpenAI as a copilot for test automation. [#3713](https://github.com/codeceptjs/CodeceptJS/issues/3713) By **[davertmik](https://github.com/davertmik)**
|
|
15
|
+

|
|
16
|
+
* [AI guide](/ai) added
|
|
17
|
+
* added support for OpenAI in `pause()`
|
|
18
|
+
* added [`heal` plugin](/plugins#heal) for self-healing tests
|
|
19
|
+
* added [`OpenAI`](/helpers/openai) helper
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
- [Playwright][Puppeteer][WebDriver] Highlight the interacting elements in debug mode or with `highlightElement` option set ([#3672](https://github.com/codeceptjs/CodeceptJS/issues/3672)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
- **[Playwright]** Support for APIs in Playwright ([#3665](https://github.com/codeceptjs/CodeceptJS/issues/3665)) - by Egor Bodnar
|
|
27
|
+
* `clearField` replaced to use new Playwright API
|
|
28
|
+
* `blur` added
|
|
29
|
+
* `focus` added
|
|
30
|
+
|
|
31
|
+
- **[Added support for multiple browsers](/parallel#Parallel-Execution-by-Workers-on-Multiple-Browsers)** in `run-workers` ([#3606](https://github.com/codeceptjs/CodeceptJS/issues/3606)) by **[karanshah-browserstack](https://github.com/karanshah-browserstack)** :
|
|
32
|
+
|
|
33
|
+
Multiple browsers configured as profiles:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
exports.config = {
|
|
37
|
+
helpers: {
|
|
38
|
+
WebDriver: {
|
|
39
|
+
url: 'http://localhost:3000',
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
multiple: {
|
|
43
|
+
profile1: {
|
|
44
|
+
browsers: [
|
|
45
|
+
{
|
|
46
|
+
browser: "firefox",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
browser: "chrome",
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
```
|
|
54
|
+
And executed via `run-workers` with `all` argument
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
npx codeceptjs run-workers 2 all
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- **[Appium]** Add Appium v2 support ([#3622](https://github.com/codeceptjs/CodeceptJS/issues/3622)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
61
|
+
- Improve `gpo` command to create page objects as modules or as classes ([#3625](https://github.com/codeceptjs/CodeceptJS/issues/3625)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
62
|
+
- Added `emptyOutputFolder` config to clean up output before running tests ([#3604](https://github.com/codeceptjs/CodeceptJS/issues/3604)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
63
|
+
- Add `secret()` function support to `append()` and `type()` ([#3615](https://github.com/codeceptjs/CodeceptJS/issues/3615)) - by **[anils92](https://github.com/anils92)**
|
|
64
|
+
- **[Playwright]** Add `bypassCSP` option to helper's config ([#3641](https://github.com/codeceptjs/CodeceptJS/issues/3641)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
65
|
+
- Print number of tests for each suite in dryRun ([#3620](https://github.com/codeceptjs/CodeceptJS/issues/3620)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
66
|
+
|
|
67
|
+
đ Bug Fixes
|
|
68
|
+
|
|
69
|
+
- Support `--grep` in dry-run command ([#3673](https://github.com/codeceptjs/CodeceptJS/issues/3673)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
70
|
+
- Fix typings improvements in playwright ([#3650](https://github.com/codeceptjs/CodeceptJS/issues/3650)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
71
|
+
- Fixed global retry [#3667](https://github.com/codeceptjs/CodeceptJS/issues/3667) by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
72
|
+
- Fixed creating JavaScript test using "codeceptjs gt" ([#3611](https://github.com/codeceptjs/CodeceptJS/issues/3611)) - by Jaromir Obr
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## 3.4.1
|
|
76
|
+
|
|
77
|
+
* Updated mocha to v 10.2. Fixes [#3591](https://github.com/codeceptjs/CodeceptJS/issues/3591)
|
|
78
|
+
* Fixes executing a faling Before hook. Resolves [#3592](https://github.com/codeceptjs/CodeceptJS/issues/3592)
|
|
79
|
+
|
|
10
80
|
## 3.4.0
|
|
11
81
|
|
|
12
82
|
* **Updated to latest mocha and modern Cucumber**
|