codeceptjs 3.4.1 â 3.5.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 +85 -0
- package/README.md +11 -9
- package/bin/codecept.js +1 -1
- package/docs/ai.md +248 -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 +234 -54
- package/docs/build/Protractor.js +3 -1
- package/docs/build/Puppeteer.js +101 -12
- package/docs/build/REST.js +15 -5
- package/docs/build/TestCafe.js +61 -2
- package/docs/build/WebDriver.js +85 -5
- package/docs/changelog.md +85 -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 +228 -151
- package/docs/helpers/Puppeteer.md +153 -101
- package/docs/helpers/REST.md +6 -5
- package/docs/helpers/TestCafe.md +97 -49
- package/docs/helpers/WebDriver.md +159 -107
- 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 +2 -2
- package/docs/webapi/appendField.mustache +2 -0
- package/docs/webapi/blur.mustache +17 -0
- package/docs/webapi/focus.mustache +12 -0
- package/docs/webapi/type.mustache +3 -0
- package/lib/ai.js +171 -0
- package/lib/cli.js +10 -2
- 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 +23 -1
- package/lib/command/interactive.js +15 -1
- package/lib/command/run-workers.js +2 -1
- package/lib/container.js +13 -3
- package/lib/event.js +2 -0
- 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 +200 -45
- package/lib/helper/Protractor.js +1 -1
- package/lib/helper/Puppeteer.js +67 -12
- package/lib/helper/REST.js +15 -5
- package/lib/helper/TestCafe.js +30 -2
- package/lib/helper/WebDriver.js +51 -5
- package/lib/helper/scripts/blurElement.js +17 -0
- package/lib/helper/scripts/focusElement.js +17 -0
- package/lib/helper/scripts/highlightElement.js +20 -0
- package/lib/html.js +258 -0
- package/lib/interfaces/gherkin.js +8 -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 +177 -0
- package/lib/plugin/screenshotOnFail.js +11 -2
- package/lib/recorder.js +11 -8
- package/lib/secret.js +5 -4
- package/lib/step.js +6 -1
- package/lib/ui.js +4 -3
- package/lib/utils.js +17 -0
- package/lib/workers.js +57 -9
- package/package.json +25 -16
- package/translations/ja-JP.js +9 -9
- package/typings/index.d.ts +43 -9
- package/typings/promiseBasedTypes.d.ts +242 -25
- package/typings/types.d.ts +260 -35
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,9 @@ 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');
|
|
38
|
+
const { blurElement } = require('./scripts/blurElement');
|
|
39
|
+
const { focusElement } = require('./scripts/focusElement');
|
|
36
40
|
|
|
37
41
|
let puppeteer;
|
|
38
42
|
let perfTiming;
|
|
@@ -65,6 +69,7 @@ const consoleLogStore = new Console();
|
|
|
65
69
|
* @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
|
|
66
70
|
* @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
|
|
67
71
|
* @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
|
|
72
|
+
* @prop {boolean} [highlightElement] - highlight the interacting elements
|
|
68
73
|
*/
|
|
69
74
|
const config = {};
|
|
70
75
|
|
|
@@ -740,6 +745,60 @@ class Puppeteer extends Helper {
|
|
|
740
745
|
return this._waitForAction();
|
|
741
746
|
}
|
|
742
747
|
|
|
748
|
+
/**
|
|
749
|
+
* Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the matching element.
|
|
750
|
+
*
|
|
751
|
+
* Examples:
|
|
752
|
+
*
|
|
753
|
+
* ```js
|
|
754
|
+
* I.dontSee('#add-to-cart-btn');
|
|
755
|
+
* I.focus('#product-tile')
|
|
756
|
+
* I.see('#add-to-cart-bnt');
|
|
757
|
+
* ```
|
|
758
|
+
*
|
|
759
|
+
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
|
|
760
|
+
* @param {any} [options] Playwright only: [Additional options](https://playwright.dev/docs/api/class-locator#locator-focus) for available options object as 2nd argument.
|
|
761
|
+
*
|
|
762
|
+
*
|
|
763
|
+
*/
|
|
764
|
+
async focus(locator) {
|
|
765
|
+
const els = await this._locate(locator);
|
|
766
|
+
assertElementExists(els, locator, 'Element to focus');
|
|
767
|
+
const el = els[0];
|
|
768
|
+
|
|
769
|
+
await focusElement(el, this.page);
|
|
770
|
+
return this._waitForAction();
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Remove focus from a text input, button, etc.
|
|
775
|
+
* Calls [blur](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.
|
|
776
|
+
*
|
|
777
|
+
* Examples:
|
|
778
|
+
*
|
|
779
|
+
* ```js
|
|
780
|
+
* I.blur('.text-area')
|
|
781
|
+
* ```
|
|
782
|
+
* ```js
|
|
783
|
+
* //element `#product-tile` is focused
|
|
784
|
+
* I.see('#add-to-cart-btn');
|
|
785
|
+
* I.blur('#product-tile')
|
|
786
|
+
* I.dontSee('#add-to-cart-btn');
|
|
787
|
+
* ```
|
|
788
|
+
*
|
|
789
|
+
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
|
|
790
|
+
* @param {any} [options] Playwright only: [Additional options](https://playwright.dev/docs/api/class-locator#locator-blur) for available options object as 2nd argument.
|
|
791
|
+
*
|
|
792
|
+
*
|
|
793
|
+
*/
|
|
794
|
+
async blur(locator) {
|
|
795
|
+
const els = await this._locate(locator);
|
|
796
|
+
assertElementExists(els, locator, 'Element to blur');
|
|
797
|
+
|
|
798
|
+
await blurElement(els[0], this.page);
|
|
799
|
+
return this._waitForAction();
|
|
800
|
+
}
|
|
801
|
+
|
|
743
802
|
/**
|
|
744
803
|
* Drag an item to a destination element.
|
|
745
804
|
*
|
|
@@ -1643,6 +1702,9 @@ class Puppeteer extends Helper {
|
|
|
1643
1702
|
*
|
|
1644
1703
|
* // passing in an array
|
|
1645
1704
|
* I.type(['T', 'E', 'X', 'T']);
|
|
1705
|
+
*
|
|
1706
|
+
* // passing a secret
|
|
1707
|
+
* I.type(secret('123456'));
|
|
1646
1708
|
* ```
|
|
1647
1709
|
*
|
|
1648
1710
|
* @param {string|string[]} key or array of keys to type.
|
|
@@ -1652,6 +1714,7 @@ class Puppeteer extends Helper {
|
|
|
1652
1714
|
*/
|
|
1653
1715
|
async type(keys, delay = null) {
|
|
1654
1716
|
if (!Array.isArray(keys)) {
|
|
1717
|
+
keys = keys.toString();
|
|
1655
1718
|
keys = keys.split('');
|
|
1656
1719
|
}
|
|
1657
1720
|
|
|
@@ -1692,7 +1755,10 @@ class Puppeteer extends Helper {
|
|
|
1692
1755
|
} else if (editable) {
|
|
1693
1756
|
await this._evaluateHandeInContext(el => el.innerHTML = '', el);
|
|
1694
1757
|
}
|
|
1758
|
+
|
|
1759
|
+
highlightActiveElement.call(this, el, this.page);
|
|
1695
1760
|
await el.type(value.toString(), { delay: this.options.pressKeyDelay });
|
|
1761
|
+
|
|
1696
1762
|
return this._waitForAction();
|
|
1697
1763
|
}
|
|
1698
1764
|
|
|
@@ -1718,6 +1784,8 @@ class Puppeteer extends Helper {
|
|
|
1718
1784
|
*
|
|
1719
1785
|
* ```js
|
|
1720
1786
|
* I.appendField('#myTextField', 'appended');
|
|
1787
|
+
* // typing secret
|
|
1788
|
+
* I.appendField('password', secret('123456'));
|
|
1721
1789
|
* ```
|
|
1722
1790
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator
|
|
1723
1791
|
* @param {string} value text value to append.
|
|
@@ -1729,8 +1797,9 @@ class Puppeteer extends Helper {
|
|
|
1729
1797
|
async appendField(field, value) {
|
|
1730
1798
|
const els = await findVisibleFields.call(this, field);
|
|
1731
1799
|
assertElementExists(els, field, 'Field');
|
|
1800
|
+
highlightActiveElement.call(this, els[0], this.page);
|
|
1732
1801
|
await els[0].press('End');
|
|
1733
|
-
await els[0].type(value, { delay: this.options.pressKeyDelay });
|
|
1802
|
+
await els[0].type(value.toString(), { delay: this.options.pressKeyDelay });
|
|
1734
1803
|
return this._waitForAction();
|
|
1735
1804
|
}
|
|
1736
1805
|
|
|
@@ -1831,6 +1900,7 @@ class Puppeteer extends Helper {
|
|
|
1831
1900
|
if (await el.getProperty('tagName').then(t => t.jsonValue()) !== 'SELECT') {
|
|
1832
1901
|
throw new Error('Element is not <select>');
|
|
1833
1902
|
}
|
|
1903
|
+
highlightActiveElement.call(this, els[0], this.page);
|
|
1834
1904
|
if (!Array.isArray(option)) option = [option];
|
|
1835
1905
|
|
|
1836
1906
|
for (const key in option) {
|
|
@@ -2680,23 +2750,32 @@ class Puppeteer extends Helper {
|
|
|
2680
2750
|
*/
|
|
2681
2751
|
async saveScreenshot(fileName, fullPage) {
|
|
2682
2752
|
const fullPageOption = fullPage || this.options.fullPageScreenshots;
|
|
2683
|
-
|
|
2753
|
+
let outputFile = screenshotOutputFolder(fileName);
|
|
2684
2754
|
|
|
2685
2755
|
this.debug(`Screenshot is saving to ${outputFile}`);
|
|
2686
2756
|
|
|
2757
|
+
await this.page.screenshot({
|
|
2758
|
+
path: outputFile,
|
|
2759
|
+
fullPage: fullPageOption,
|
|
2760
|
+
type: 'png',
|
|
2761
|
+
});
|
|
2762
|
+
|
|
2687
2763
|
if (this.activeSessionName) {
|
|
2688
|
-
const
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2764
|
+
for (const sessionName in this.sessionPages) {
|
|
2765
|
+
const activeSessionPage = this.sessionPages[sessionName];
|
|
2766
|
+
outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`);
|
|
2767
|
+
|
|
2768
|
+
this.debug(`${sessionName} - Screenshot is saving to ${outputFile}`);
|
|
2769
|
+
|
|
2770
|
+
if (activeSessionPage) {
|
|
2771
|
+
await activeSessionPage.screenshot({
|
|
2772
|
+
path: outputFile,
|
|
2773
|
+
fullPage: fullPageOption,
|
|
2774
|
+
type: 'png',
|
|
2775
|
+
});
|
|
2776
|
+
}
|
|
2696
2777
|
}
|
|
2697
2778
|
}
|
|
2698
|
-
|
|
2699
|
-
return this.page.screenshot({ path: outputFile, fullPage: fullPageOption, type: 'png' });
|
|
2700
2779
|
}
|
|
2701
2780
|
|
|
2702
2781
|
async _failed() {
|
|
@@ -3358,12 +3437,16 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
3358
3437
|
} else {
|
|
3359
3438
|
assertElementExists(els, locator, 'Clickable element');
|
|
3360
3439
|
}
|
|
3440
|
+
|
|
3441
|
+
highlightActiveElement.call(this, els[0], this.page);
|
|
3442
|
+
|
|
3361
3443
|
await els[0].click(options);
|
|
3362
3444
|
const promises = [];
|
|
3363
3445
|
if (options.waitForNavigation) {
|
|
3364
3446
|
promises.push(this.waitForNavigation());
|
|
3365
3447
|
}
|
|
3366
3448
|
promises.push(this._waitForAction());
|
|
3449
|
+
|
|
3367
3450
|
return Promise.all(promises);
|
|
3368
3451
|
}
|
|
3369
3452
|
|
|
@@ -3708,3 +3791,9 @@ function getNormalizedKey(key) {
|
|
|
3708
3791
|
}
|
|
3709
3792
|
return normalizedKey;
|
|
3710
3793
|
}
|
|
3794
|
+
|
|
3795
|
+
function highlightActiveElement(element, context) {
|
|
3796
|
+
if (!this.options.enableHighlight && !store.debugMode) return;
|
|
3797
|
+
|
|
3798
|
+
highlightElement(element, context);
|
|
3799
|
+
}
|
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
|
@@ -362,6 +362,63 @@ class TestCafe extends Helper {
|
|
|
362
362
|
return this.t.resizeWindow(width, height).catch(mapError);
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
/**
|
|
366
|
+
* Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the matching element.
|
|
367
|
+
*
|
|
368
|
+
* Examples:
|
|
369
|
+
*
|
|
370
|
+
* ```js
|
|
371
|
+
* I.dontSee('#add-to-cart-btn');
|
|
372
|
+
* I.focus('#product-tile')
|
|
373
|
+
* I.see('#add-to-cart-bnt');
|
|
374
|
+
* ```
|
|
375
|
+
*
|
|
376
|
+
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
|
|
377
|
+
* @param {any} [options] Playwright only: [Additional options](https://playwright.dev/docs/api/class-locator#locator-focus) for available options object as 2nd argument.
|
|
378
|
+
*
|
|
379
|
+
*
|
|
380
|
+
*/
|
|
381
|
+
async focus(locator) {
|
|
382
|
+
const els = await this._locate(locator);
|
|
383
|
+
await assertElementExists(els, locator, 'Element to focus');
|
|
384
|
+
const element = await els.nth(0);
|
|
385
|
+
|
|
386
|
+
const focusElement = ClientFunction(() => element().focus(), { boundTestRun: this.t, dependencies: { element } });
|
|
387
|
+
|
|
388
|
+
return focusElement();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Remove focus from a text input, button, etc.
|
|
393
|
+
* Calls [blur](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.
|
|
394
|
+
*
|
|
395
|
+
* Examples:
|
|
396
|
+
*
|
|
397
|
+
* ```js
|
|
398
|
+
* I.blur('.text-area')
|
|
399
|
+
* ```
|
|
400
|
+
* ```js
|
|
401
|
+
* //element `#product-tile` is focused
|
|
402
|
+
* I.see('#add-to-cart-btn');
|
|
403
|
+
* I.blur('#product-tile')
|
|
404
|
+
* I.dontSee('#add-to-cart-btn');
|
|
405
|
+
* ```
|
|
406
|
+
*
|
|
407
|
+
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
|
|
408
|
+
* @param {any} [options] Playwright only: [Additional options](https://playwright.dev/docs/api/class-locator#locator-blur) for available options object as 2nd argument.
|
|
409
|
+
*
|
|
410
|
+
*
|
|
411
|
+
*/
|
|
412
|
+
async blur(locator) {
|
|
413
|
+
const els = await this._locate(locator);
|
|
414
|
+
await assertElementExists(els, locator, 'Element to blur');
|
|
415
|
+
const element = await els.nth(0);
|
|
416
|
+
|
|
417
|
+
const blurElement = ClientFunction(() => element().blur(), { boundTestRun: this.t, dependencies: { element } });
|
|
418
|
+
|
|
419
|
+
return blurElement();
|
|
420
|
+
}
|
|
421
|
+
|
|
365
422
|
/**
|
|
366
423
|
* Perform a click on a link or a button, given by a locator.
|
|
367
424
|
* If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string.
|
|
@@ -488,6 +545,8 @@ class TestCafe extends Helper {
|
|
|
488
545
|
*
|
|
489
546
|
* ```js
|
|
490
547
|
* I.appendField('#myTextField', 'appended');
|
|
548
|
+
* // typing secret
|
|
549
|
+
* I.appendField('password', secret('123456'));
|
|
491
550
|
* ```
|
|
492
551
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator
|
|
493
552
|
* @param {string} value text value to append.
|
|
@@ -501,7 +560,7 @@ class TestCafe extends Helper {
|
|
|
501
560
|
const el = await els.nth(0);
|
|
502
561
|
|
|
503
562
|
return this.t
|
|
504
|
-
.typeText(el, value, { replace: false })
|
|
563
|
+
.typeText(el, value.toString(), { replace: false })
|
|
505
564
|
.catch(mapError);
|
|
506
565
|
}
|
|
507
566
|
|
|
@@ -1212,7 +1271,7 @@ class TestCafe extends Helper {
|
|
|
1212
1271
|
* â ī¸ returns a _promise_ which is synchronized internally by recorder
|
|
1213
1272
|
*
|
|
1214
1273
|
*
|
|
1215
|
-
* If a function returns a Promise It will wait for
|
|
1274
|
+
* If a function returns a Promise It will wait for its resolution.
|
|
1216
1275
|
*/
|
|
1217
1276
|
async executeScript(fn, ...args) {
|
|
1218
1277
|
const browserFn = createClientFunction(fn, args).with({ boundTestRun: this.t });
|
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,10 @@ 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');
|
|
33
|
+
const { focusElement } = require('./scripts/focusElement');
|
|
34
|
+
const { blurElement } = require('./scripts/blurElement');
|
|
30
35
|
|
|
31
36
|
const SHADOW = 'shadow';
|
|
32
37
|
const webRoot = 'body';
|
|
@@ -39,7 +44,7 @@ const webRoot = 'body';
|
|
|
39
44
|
* @typedef WebDriverConfig
|
|
40
45
|
* @type {object}
|
|
41
46
|
* @prop {string} url - base url of website to be tested.
|
|
42
|
-
* @prop {string} browser
|
|
47
|
+
* @prop {string} browser - Browser in which to perform testing.
|
|
43
48
|
* @prop {string} [basicAuth] - (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
44
49
|
* @prop {string} [host=localhost] - WebDriver host to connect.
|
|
45
50
|
* @prop {number} [port=4444] - WebDriver port to connect.
|
|
@@ -57,6 +62,7 @@ const webRoot = 'body';
|
|
|
57
62
|
* @prop {object} [desiredCapabilities] Selenium's [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities).
|
|
58
63
|
* @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["WebDriver"]._startBrowser()`.
|
|
59
64
|
* @prop {object} [timeouts] [WebDriver timeouts](http://webdriver.io/docs/timeouts.html) defined as hash.
|
|
65
|
+
* @prop {boolean} [highlightElement] - highlight the interacting elements
|
|
60
66
|
*/
|
|
61
67
|
const config = {};
|
|
62
68
|
|
|
@@ -822,7 +828,7 @@ class WebDriver extends Helper {
|
|
|
822
828
|
}
|
|
823
829
|
|
|
824
830
|
/**
|
|
825
|
-
* Find a checkbox by providing human
|
|
831
|
+
* Find a checkbox by providing human-readable text:
|
|
826
832
|
*
|
|
827
833
|
* ```js
|
|
828
834
|
* this.helpers['WebDriver']._locateCheckable('I agree with terms and conditions').then // ...
|
|
@@ -835,7 +841,7 @@ class WebDriver extends Helper {
|
|
|
835
841
|
}
|
|
836
842
|
|
|
837
843
|
/**
|
|
838
|
-
* Find a clickable element by providing human
|
|
844
|
+
* Find a clickable element by providing human-readable text:
|
|
839
845
|
*
|
|
840
846
|
* ```js
|
|
841
847
|
* const els = await this.helpers.WebDriver._locateClickable('Next page');
|
|
@@ -850,7 +856,7 @@ class WebDriver extends Helper {
|
|
|
850
856
|
}
|
|
851
857
|
|
|
852
858
|
/**
|
|
853
|
-
* Find field elements by providing human
|
|
859
|
+
* Find field elements by providing human-readable text:
|
|
854
860
|
*
|
|
855
861
|
* ```js
|
|
856
862
|
* this.helpers['WebDriver']._locateFields('Your email').then // ...
|
|
@@ -949,6 +955,7 @@ class WebDriver extends Helper {
|
|
|
949
955
|
assertElementExists(res, locator, 'Clickable element');
|
|
950
956
|
}
|
|
951
957
|
const elem = usingFirstElement(res);
|
|
958
|
+
highlightActiveElement.call(this, elem);
|
|
952
959
|
return this.browser[clickMethod](getElementId(elem));
|
|
953
960
|
}
|
|
954
961
|
|
|
@@ -995,6 +1002,7 @@ class WebDriver extends Helper {
|
|
|
995
1002
|
assertElementExists(res, locator, 'Clickable element');
|
|
996
1003
|
}
|
|
997
1004
|
const elem = usingFirstElement(res);
|
|
1005
|
+
highlightActiveElement.call(this, elem);
|
|
998
1006
|
|
|
999
1007
|
return this.executeScript((el) => {
|
|
1000
1008
|
if (document.activeElement instanceof HTMLElement) {
|
|
@@ -1035,6 +1043,7 @@ class WebDriver extends Helper {
|
|
|
1035
1043
|
}
|
|
1036
1044
|
|
|
1037
1045
|
const elem = usingFirstElement(res);
|
|
1046
|
+
highlightActiveElement.call(this, elem);
|
|
1038
1047
|
return elem.doubleClick();
|
|
1039
1048
|
}
|
|
1040
1049
|
|
|
@@ -1148,6 +1157,7 @@ class WebDriver extends Helper {
|
|
|
1148
1157
|
const res = await findFields.call(this, field);
|
|
1149
1158
|
assertElementExists(res, field, 'Field');
|
|
1150
1159
|
const elem = usingFirstElement(res);
|
|
1160
|
+
highlightActiveElement.call(this, elem);
|
|
1151
1161
|
return elem.setValue(value.toString());
|
|
1152
1162
|
}
|
|
1153
1163
|
|
|
@@ -1157,6 +1167,8 @@ class WebDriver extends Helper {
|
|
|
1157
1167
|
*
|
|
1158
1168
|
* ```js
|
|
1159
1169
|
* I.appendField('#myTextField', 'appended');
|
|
1170
|
+
* // typing secret
|
|
1171
|
+
* I.appendField('password', secret('123456'));
|
|
1160
1172
|
* ```
|
|
1161
1173
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator
|
|
1162
1174
|
* @param {string} value text value to append.
|
|
@@ -1168,7 +1180,8 @@ class WebDriver extends Helper {
|
|
|
1168
1180
|
const res = await findFields.call(this, field);
|
|
1169
1181
|
assertElementExists(res, field, 'Field');
|
|
1170
1182
|
const elem = usingFirstElement(res);
|
|
1171
|
-
|
|
1183
|
+
highlightActiveElement.call(this, elem);
|
|
1184
|
+
return elem.addValue(value.toString());
|
|
1172
1185
|
}
|
|
1173
1186
|
|
|
1174
1187
|
/**
|
|
@@ -1188,6 +1201,7 @@ class WebDriver extends Helper {
|
|
|
1188
1201
|
const res = await findFields.call(this, field);
|
|
1189
1202
|
assertElementExists(res, field, 'Field');
|
|
1190
1203
|
const elem = usingFirstElement(res);
|
|
1204
|
+
highlightActiveElement.call(this, elem);
|
|
1191
1205
|
return elem.clearValue(getElementId(elem));
|
|
1192
1206
|
}
|
|
1193
1207
|
|
|
@@ -1219,6 +1233,7 @@ class WebDriver extends Helper {
|
|
|
1219
1233
|
const res = await findFields.call(this, select);
|
|
1220
1234
|
assertElementExists(res, select, 'Selectable field');
|
|
1221
1235
|
const elem = usingFirstElement(res);
|
|
1236
|
+
highlightActiveElement.call(this, elem);
|
|
1222
1237
|
|
|
1223
1238
|
if (!Array.isArray(option)) {
|
|
1224
1239
|
option = [option];
|
|
@@ -1310,6 +1325,7 @@ class WebDriver extends Helper {
|
|
|
1310
1325
|
assertElementExists(res, field, 'Checkable');
|
|
1311
1326
|
const elem = usingFirstElement(res);
|
|
1312
1327
|
const elementId = getElementId(elem);
|
|
1328
|
+
highlightActiveElement.call(this, elem);
|
|
1313
1329
|
|
|
1314
1330
|
const isSelected = await this.browser.isElementSelected(elementId);
|
|
1315
1331
|
if (isSelected) return Promise.resolve(true);
|
|
@@ -1342,6 +1358,7 @@ class WebDriver extends Helper {
|
|
|
1342
1358
|
assertElementExists(res, field, 'Checkable');
|
|
1343
1359
|
const elem = usingFirstElement(res);
|
|
1344
1360
|
const elementId = getElementId(elem);
|
|
1361
|
+
highlightActiveElement.call(this, elem);
|
|
1345
1362
|
|
|
1346
1363
|
const isSelected = await this.browser.isElementSelected(elementId);
|
|
1347
1364
|
if (!isSelected) return Promise.resolve(true);
|
|
@@ -2689,6 +2706,9 @@ class WebDriver extends Helper {
|
|
|
2689
2706
|
*
|
|
2690
2707
|
* // passing in an array
|
|
2691
2708
|
* I.type(['T', 'E', 'X', 'T']);
|
|
2709
|
+
*
|
|
2710
|
+
* // passing a secret
|
|
2711
|
+
* I.type(secret('123456'));
|
|
2692
2712
|
* ```
|
|
2693
2713
|
*
|
|
2694
2714
|
* @param {string|string[]} key or array of keys to type.
|
|
@@ -2698,6 +2718,7 @@ class WebDriver extends Helper {
|
|
|
2698
2718
|
*/
|
|
2699
2719
|
async type(keys, delay = null) {
|
|
2700
2720
|
if (!Array.isArray(keys)) {
|
|
2721
|
+
keys = keys.toString();
|
|
2701
2722
|
keys = keys.split('');
|
|
2702
2723
|
}
|
|
2703
2724
|
if (delay) {
|
|
@@ -2745,6 +2766,59 @@ class WebDriver extends Helper {
|
|
|
2745
2766
|
}
|
|
2746
2767
|
}
|
|
2747
2768
|
|
|
2769
|
+
/**
|
|
2770
|
+
* Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the matching element.
|
|
2771
|
+
*
|
|
2772
|
+
* Examples:
|
|
2773
|
+
*
|
|
2774
|
+
* ```js
|
|
2775
|
+
* I.dontSee('#add-to-cart-btn');
|
|
2776
|
+
* I.focus('#product-tile')
|
|
2777
|
+
* I.see('#add-to-cart-bnt');
|
|
2778
|
+
* ```
|
|
2779
|
+
*
|
|
2780
|
+
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
|
|
2781
|
+
* @param {any} [options] Playwright only: [Additional options](https://playwright.dev/docs/api/class-locator#locator-focus) for available options object as 2nd argument.
|
|
2782
|
+
*
|
|
2783
|
+
*
|
|
2784
|
+
*/
|
|
2785
|
+
async focus(locator) {
|
|
2786
|
+
const els = await this._locate(locator);
|
|
2787
|
+
assertElementExists(els, locator, 'Element to focus');
|
|
2788
|
+
const el = usingFirstElement(els);
|
|
2789
|
+
|
|
2790
|
+
await focusElement(el, this.browser);
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
/**
|
|
2794
|
+
* Remove focus from a text input, button, etc.
|
|
2795
|
+
* Calls [blur](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.
|
|
2796
|
+
*
|
|
2797
|
+
* Examples:
|
|
2798
|
+
*
|
|
2799
|
+
* ```js
|
|
2800
|
+
* I.blur('.text-area')
|
|
2801
|
+
* ```
|
|
2802
|
+
* ```js
|
|
2803
|
+
* //element `#product-tile` is focused
|
|
2804
|
+
* I.see('#add-to-cart-btn');
|
|
2805
|
+
* I.blur('#product-tile')
|
|
2806
|
+
* I.dontSee('#add-to-cart-btn');
|
|
2807
|
+
* ```
|
|
2808
|
+
*
|
|
2809
|
+
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
|
|
2810
|
+
* @param {any} [options] Playwright only: [Additional options](https://playwright.dev/docs/api/class-locator#locator-blur) for available options object as 2nd argument.
|
|
2811
|
+
*
|
|
2812
|
+
*
|
|
2813
|
+
*/
|
|
2814
|
+
async blur(locator) {
|
|
2815
|
+
const els = await this._locate(locator);
|
|
2816
|
+
assertElementExists(els, locator, 'Element to blur');
|
|
2817
|
+
const el = usingFirstElement(els);
|
|
2818
|
+
|
|
2819
|
+
await blurElement(el, this.browser);
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2748
2822
|
/**
|
|
2749
2823
|
* Drag an item to a destination element.
|
|
2750
2824
|
*
|
|
@@ -4000,6 +4074,12 @@ function isModifierKey(key) {
|
|
|
4000
4074
|
return unicodeModifierKeys.includes(key);
|
|
4001
4075
|
}
|
|
4002
4076
|
|
|
4077
|
+
function highlightActiveElement(element) {
|
|
4078
|
+
if (!this.options.enableHighlight && !store.debugMode) return;
|
|
4079
|
+
|
|
4080
|
+
highlightElement(element, this.browser);
|
|
4081
|
+
}
|
|
4082
|
+
|
|
4003
4083
|
function prepareLocateFn(context) {
|
|
4004
4084
|
if (!context) return this._locate.bind(this);
|
|
4005
4085
|
return (l) => {
|
package/docs/changelog.md
CHANGED
|
@@ -7,6 +7,91 @@ layout: Section
|
|
|
7
7
|
|
|
8
8
|
# Releases
|
|
9
9
|
|
|
10
|
+
## 3.5.1
|
|
11
|
+
|
|
12
|
+
đŠī¸ Features
|
|
13
|
+
|
|
14
|
+
* [Puppeteer][WebDriver][TestCafe] Added methods by **[KobeNguyenT](https://github.com/KobeNguyenT)** in [#3737](https://github.com/codeceptjs/CodeceptJS/issues/3737)
|
|
15
|
+
* `blur`
|
|
16
|
+
* `focus`
|
|
17
|
+
* Improved BDD output to print steps without `I.` commands` by **[davertmik](https://github.com/davertmik)** [#3739](https://github.com/codeceptjs/CodeceptJS/issues/3739)
|
|
18
|
+
* Improved `codecept init` setup for Electron tests by **[KobeNguyenT](https://github.com/KobeNguyenT)**. See [#3733](https://github.com/codeceptjs/CodeceptJS/issues/3733)
|
|
19
|
+
|
|
20
|
+
đ Bug Fixes
|
|
21
|
+
|
|
22
|
+
* Fixed serializing of custom errors making tests stuck. Fix [#3739](https://github.com/codeceptjs/CodeceptJS/issues/3739) by **[davertmik](https://github.com/davertmik)**.
|
|
23
|
+
|
|
24
|
+
đ Documentation
|
|
25
|
+
|
|
26
|
+
* Fixed Playwright docs by **[Horsty80](https://github.com/Horsty80)**
|
|
27
|
+
* Fixed ai docs by **[ngraf](https://github.com/ngraf)**
|
|
28
|
+
* Various fixes by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
29
|
+
|
|
30
|
+
## 3.5.0
|
|
31
|
+
|
|
32
|
+
đŠī¸ Features
|
|
33
|
+
|
|
34
|
+
- **đĒ [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)**
|
|
35
|
+

|
|
36
|
+
* [AI guide](/ai) added
|
|
37
|
+
* added support for OpenAI in `pause()`
|
|
38
|
+
* added [`heal` plugin](/plugins#heal) for self-healing tests
|
|
39
|
+
* added [`OpenAI`](/helpers/openai) helper
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
- [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)**
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
- **[Playwright]** Support for APIs in Playwright ([#3665](https://github.com/codeceptjs/CodeceptJS/issues/3665)) - by Egor Bodnar
|
|
47
|
+
* `clearField` replaced to use new Playwright API
|
|
48
|
+
* `blur` added
|
|
49
|
+
* `focus` added
|
|
50
|
+
|
|
51
|
+
- **[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)** :
|
|
52
|
+
|
|
53
|
+
Multiple browsers configured as profiles:
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
exports.config = {
|
|
57
|
+
helpers: {
|
|
58
|
+
WebDriver: {
|
|
59
|
+
url: 'http://localhost:3000',
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
multiple: {
|
|
63
|
+
profile1: {
|
|
64
|
+
browsers: [
|
|
65
|
+
{
|
|
66
|
+
browser: "firefox",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
browser: "chrome",
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
```
|
|
74
|
+
And executed via `run-workers` with `all` argument
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
npx codeceptjs run-workers 2 all
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- **[Appium]** Add Appium v2 support ([#3622](https://github.com/codeceptjs/CodeceptJS/issues/3622)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
81
|
+
- 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)**
|
|
82
|
+
- Added `emptyOutputFolder` config to clean up output before running tests ([#3604](https://github.com/codeceptjs/CodeceptJS/issues/3604)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
83
|
+
- Add `secret()` function support to `append()` and `type()` ([#3615](https://github.com/codeceptjs/CodeceptJS/issues/3615)) - by **[anils92](https://github.com/anils92)**
|
|
84
|
+
- **[Playwright]** Add `bypassCSP` option to helper's config ([#3641](https://github.com/codeceptjs/CodeceptJS/issues/3641)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
85
|
+
- Print number of tests for each suite in dryRun ([#3620](https://github.com/codeceptjs/CodeceptJS/issues/3620)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
86
|
+
|
|
87
|
+
đ Bug Fixes
|
|
88
|
+
|
|
89
|
+
- Support `--grep` in dry-run command ([#3673](https://github.com/codeceptjs/CodeceptJS/issues/3673)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
90
|
+
- Fix typings improvements in playwright ([#3650](https://github.com/codeceptjs/CodeceptJS/issues/3650)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
91
|
+
- Fixed global retry [#3667](https://github.com/codeceptjs/CodeceptJS/issues/3667) by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
92
|
+
- Fixed creating JavaScript test using "codeceptjs gt" ([#3611](https://github.com/codeceptjs/CodeceptJS/issues/3611)) - by Jaromir Obr
|
|
93
|
+
|
|
94
|
+
|
|
10
95
|
## 3.4.1
|
|
11
96
|
|
|
12
97
|
* Updated mocha to v 10.2. Fixes [#3591](https://github.com/codeceptjs/CodeceptJS/issues/3591)
|