codeceptjs 3.5.9-beta.2 → 3.5.10
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/README.md +14 -16
- package/docs/build/Appium.js +7 -7
- package/docs/build/Playwright.js +53 -13
- package/docs/build/Puppeteer.js +36 -20
- package/docs/build/WebDriver.js +23 -2
- package/docs/helpers/Appium.md +1 -1
- package/docs/helpers/Playwright.md +30 -0
- package/docs/helpers/Puppeteer.md +15 -0
- package/docs/helpers/WebDriver.md +15 -0
- package/docs/internal-api.md +0 -110
- package/docs/parallel.md +114 -2
- package/docs/webapi/grabWebElement.mustache +9 -0
- package/docs/webapi/grabWebElements.mustache +9 -0
- package/lib/ai.js +12 -3
- package/lib/colorUtils.js +10 -0
- package/lib/helper/Appium.js +3 -3
- package/lib/helper/Playwright.js +25 -12
- package/lib/helper/Puppeteer.js +25 -22
- package/lib/helper/WebDriver.js +8 -0
- package/lib/html.js +3 -3
- package/lib/pause.js +6 -3
- package/lib/plugin/heal.js +40 -7
- package/lib/recorder.js +12 -5
- package/package.json +14 -11
- package/typings/promiseBasedTypes.d.ts +45 -1
- package/typings/types.d.ts +46 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Reference: [Helpers API](https://github.com/codeceptjs/CodeceptJS/tree/master/do
|
|
|
24
24
|
## Supercharged E2E Testing
|
|
25
25
|
|
|
26
26
|
CodeceptJS is a new testing framework for end-to-end testing with WebDriver (or others).
|
|
27
|
-
It abstracts browser interaction to simple steps that are written from a user perspective.
|
|
27
|
+
It abstracts browser interaction to simple steps that are written from a user's perspective.
|
|
28
28
|
A simple test that verifies the "Welcome" text is present on a main page of a site will look like:
|
|
29
29
|
|
|
30
30
|
```js
|
|
@@ -57,27 +57,27 @@ And more to come...
|
|
|
57
57
|
|
|
58
58
|
CodeceptJS is a successor of [Codeception](http://codeception.com), a popular full-stack testing framework for PHP.
|
|
59
59
|
With CodeceptJS your scenario-driven functional and acceptance tests will be as simple and clean as they can be.
|
|
60
|
-
You don't need to worry about asynchronous nature of NodeJS or about various APIs of Selenium, Puppeteer, TestCafe, etc. as CodeceptJS unifies them and makes them work as they are synchronous.
|
|
60
|
+
You don't need to worry about asynchronous nature of NodeJS or about various APIs of Playwright, Selenium, Puppeteer, TestCafe, etc. as CodeceptJS unifies them and makes them work as they are synchronous.
|
|
61
61
|
|
|
62
62
|
|
|
63
63
|
## Features
|
|
64
64
|
|
|
65
|
-
* 🪄 **AI-powered** with GPT features to assist and heal failing tests
|
|
66
|
-
* Based on [Mocha](https://mochajs.org/) testing framework.
|
|
67
|
-
* Designed for scenario driven acceptance testing in BDD-style
|
|
68
|
-
* Uses ES6 natively without transpiler.
|
|
65
|
+
* 🪄 **AI-powered** with GPT features to assist and heal failing tests.
|
|
66
|
+
* ☕ Based on [Mocha](https://mochajs.org/) testing framework.
|
|
67
|
+
* 💼 Designed for scenario driven acceptance testing in BDD-style.
|
|
68
|
+
* 💻 Uses ES6 natively without transpiler.
|
|
69
69
|
* Also plays nice with TypeScript.
|
|
70
|
-
* Smart locators: use names, labels, matching text, CSS or XPath to locate elements.
|
|
71
|
-
* Interactive debugging shell: pause test at any point and try different commands in a browser.
|
|
70
|
+
* </> Smart locators: use names, labels, matching text, CSS or XPath to locate elements.
|
|
71
|
+
* 🌐 Interactive debugging shell: pause test at any point and try different commands in a browser.
|
|
72
72
|
* Easily create tests, pageobjects, stepobjects with CLI generators.
|
|
73
73
|
|
|
74
|
-
##
|
|
74
|
+
## Installation
|
|
75
75
|
|
|
76
76
|
```sh
|
|
77
77
|
npm i codeceptjs --save
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
Move to directory where you'd like to have your tests (and
|
|
80
|
+
Move to directory where you'd like to have your tests (and CodeceptJS config) stored, and execute:
|
|
81
81
|
|
|
82
82
|
```sh
|
|
83
83
|
npx codeceptjs init
|
|
@@ -131,8 +131,8 @@ Scenario('test some forms', ({ I }) => {
|
|
|
131
131
|
});
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
All actions are performed by I object; assertions functions start with `see` function.
|
|
135
|
-
In
|
|
134
|
+
All actions are performed by `I` object; assertions functions start with `see` function.
|
|
135
|
+
In these examples all methods of `I` are taken from WebDriver helper, see [reference](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/WebDriver.md) to learn how to use them.
|
|
136
136
|
|
|
137
137
|
Let's execute this test with `run` command. Additional option `--steps` will show us the running process. We recommend use `--steps` or `--debug` during development.
|
|
138
138
|
|
|
@@ -198,17 +198,15 @@ In this case 'User is valid' string will be searched only inside elements locate
|
|
|
198
198
|
### Grabbers
|
|
199
199
|
|
|
200
200
|
In case you need to return a value from a webpage and use it directly in test, you should use methods with `grab` prefix.
|
|
201
|
-
They are expected to be used inside async/await functions, and their results will be available in test:
|
|
201
|
+
They are expected to be used inside `async/await` functions, and their results will be available in test:
|
|
202
202
|
|
|
203
203
|
```js
|
|
204
|
-
const assert = require('assert');
|
|
205
|
-
|
|
206
204
|
Feature('CodeceptJS Demonstration');
|
|
207
205
|
|
|
208
206
|
Scenario('test page title', async ({ I }) => {
|
|
209
207
|
I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
|
|
210
208
|
const title = await I.grabTitle();
|
|
211
|
-
|
|
209
|
+
I.expectEqual(title, 'Example application with SimpleForm and Twitter Bootstrap'); // Avaiable with Expect helper. -> https://codecept.io/helpers/Expect/
|
|
212
210
|
});
|
|
213
211
|
```
|
|
214
212
|
|
package/docs/build/Appium.js
CHANGED
|
@@ -276,7 +276,7 @@ class Appium extends Webdriver {
|
|
|
276
276
|
const _convertedCaps = {};
|
|
277
277
|
for (const [key, value] of Object.entries(capabilities)) {
|
|
278
278
|
if (!key.startsWith(vendorPrefix.appium)) {
|
|
279
|
-
if (key !== 'platformName') {
|
|
279
|
+
if (key !== 'platformName' && key !== 'bstack:options') {
|
|
280
280
|
_convertedCaps[`${vendorPrefix.appium}:${key}`] = value;
|
|
281
281
|
} else {
|
|
282
282
|
_convertedCaps[`${key}`] = value;
|
|
@@ -423,8 +423,8 @@ class Appium extends Webdriver {
|
|
|
423
423
|
async runOnIOS(caps, fn) {
|
|
424
424
|
if (this.platform !== 'ios') return;
|
|
425
425
|
recorder.session.start('iOS-only actions');
|
|
426
|
-
this._runWithCaps(caps, fn);
|
|
427
|
-
recorder.add('restore from iOS session', () => recorder.session.restore());
|
|
426
|
+
await this._runWithCaps(caps, fn);
|
|
427
|
+
await recorder.add('restore from iOS session', () => recorder.session.restore());
|
|
428
428
|
return recorder.promise();
|
|
429
429
|
}
|
|
430
430
|
|
|
@@ -465,8 +465,8 @@ class Appium extends Webdriver {
|
|
|
465
465
|
async runOnAndroid(caps, fn) {
|
|
466
466
|
if (this.platform !== 'android') return;
|
|
467
467
|
recorder.session.start('Android-only actions');
|
|
468
|
-
this._runWithCaps(caps, fn);
|
|
469
|
-
recorder.add('restore from Android session', () => recorder.session.restore());
|
|
468
|
+
await this._runWithCaps(caps, fn);
|
|
469
|
+
await recorder.add('restore from Android session', () => recorder.session.restore());
|
|
470
470
|
return recorder.promise();
|
|
471
471
|
}
|
|
472
472
|
|
|
@@ -1424,10 +1424,10 @@ class Appium extends Webdriver {
|
|
|
1424
1424
|
*
|
|
1425
1425
|
* @return {Promise<void>}
|
|
1426
1426
|
*
|
|
1427
|
-
* Appium: support
|
|
1427
|
+
* Appium: support both Android and iOS
|
|
1428
1428
|
*/
|
|
1429
1429
|
async closeApp() {
|
|
1430
|
-
onlyForApps.call(this
|
|
1430
|
+
onlyForApps.call(this);
|
|
1431
1431
|
return this.browser.closeApp();
|
|
1432
1432
|
}
|
|
1433
1433
|
|
package/docs/build/Playwright.js
CHANGED
|
@@ -362,7 +362,7 @@ class Playwright extends Helper {
|
|
|
362
362
|
ignoreLog: ['warning', 'log'],
|
|
363
363
|
uniqueScreenshotNames: false,
|
|
364
364
|
manualStart: false,
|
|
365
|
-
getPageTimeout:
|
|
365
|
+
getPageTimeout: 30000,
|
|
366
366
|
waitForNavigation: 'load',
|
|
367
367
|
restart: false,
|
|
368
368
|
keepCookies: false,
|
|
@@ -474,7 +474,7 @@ class Playwright extends Helper {
|
|
|
474
474
|
async _before(test) {
|
|
475
475
|
this.currentRunningTest = test;
|
|
476
476
|
recorder.retry({
|
|
477
|
-
retries:
|
|
477
|
+
retries: process.env.FAILED_STEP_RETRIES || 3,
|
|
478
478
|
when: err => {
|
|
479
479
|
if (!err || typeof (err.message) !== 'string') {
|
|
480
480
|
return false;
|
|
@@ -1450,6 +1450,40 @@ class Playwright extends Helper {
|
|
|
1450
1450
|
return findFields.call(this, locator);
|
|
1451
1451
|
}
|
|
1452
1452
|
|
|
1453
|
+
/**
|
|
1454
|
+
* Grab WebElements for given locator
|
|
1455
|
+
* Resumes test execution, so **should be used inside an async function with `await`** operator.
|
|
1456
|
+
*
|
|
1457
|
+
* ```js
|
|
1458
|
+
* const webElements = await I.grabWebElements('#button');
|
|
1459
|
+
* ```
|
|
1460
|
+
*
|
|
1461
|
+
* @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
1462
|
+
* @returns {Promise<*>} WebElement of being used Web helper
|
|
1463
|
+
*
|
|
1464
|
+
*
|
|
1465
|
+
*/
|
|
1466
|
+
async grabWebElements(locator) {
|
|
1467
|
+
return this._locate(locator);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/**
|
|
1471
|
+
* Grab WebElement for given locator
|
|
1472
|
+
* Resumes test execution, so **should be used inside an async function with `await`** operator.
|
|
1473
|
+
*
|
|
1474
|
+
* ```js
|
|
1475
|
+
* const webElement = await I.grabWebElement('#button');
|
|
1476
|
+
* ```
|
|
1477
|
+
*
|
|
1478
|
+
* @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
1479
|
+
* @returns {Promise<*>} WebElement of being used Web helper
|
|
1480
|
+
*
|
|
1481
|
+
*
|
|
1482
|
+
*/
|
|
1483
|
+
async grabWebElement(locator) {
|
|
1484
|
+
return this._locateElement(locator);
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1453
1487
|
/**
|
|
1454
1488
|
* Switch focus to a particular tab by its number. It waits tabs loading and then switch tab
|
|
1455
1489
|
*
|
|
@@ -2265,8 +2299,15 @@ class Playwright extends Helper {
|
|
|
2265
2299
|
const el = els[0];
|
|
2266
2300
|
|
|
2267
2301
|
await highlightActiveElement.call(this, el);
|
|
2302
|
+
let optionToSelect = '';
|
|
2268
2303
|
|
|
2269
|
-
|
|
2304
|
+
try {
|
|
2305
|
+
optionToSelect = await el.locator('option', { hasText: option }).textContent();
|
|
2306
|
+
} catch (e) {
|
|
2307
|
+
optionToSelect = option;
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
if (!Array.isArray(option)) option = [optionToSelect];
|
|
2270
2311
|
|
|
2271
2312
|
await el.selectOption(option);
|
|
2272
2313
|
return this._waitForAction();
|
|
@@ -2856,19 +2897,17 @@ class Playwright extends Helper {
|
|
|
2856
2897
|
|
|
2857
2898
|
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
2858
2899
|
const elemAmount = res.length;
|
|
2859
|
-
const commands = [];
|
|
2860
2900
|
let props = [];
|
|
2861
2901
|
|
|
2862
2902
|
for (const element of res) {
|
|
2863
|
-
const
|
|
2864
|
-
|
|
2865
|
-
Object.keys(cssPropertiesCamelCase).forEach(prop => {
|
|
2903
|
+
for (const prop of Object.keys(cssProperties)) {
|
|
2904
|
+
const cssProp = await this.grabCssPropertyFrom(locator, prop);
|
|
2866
2905
|
if (isColorProperty(prop)) {
|
|
2867
|
-
props.push(convertColorToRGBA(
|
|
2906
|
+
props.push(convertColorToRGBA(cssProp));
|
|
2868
2907
|
} else {
|
|
2869
|
-
props.push(
|
|
2908
|
+
props.push(cssProp);
|
|
2870
2909
|
}
|
|
2871
|
-
}
|
|
2910
|
+
}
|
|
2872
2911
|
}
|
|
2873
2912
|
|
|
2874
2913
|
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
|
|
@@ -2876,7 +2915,8 @@ class Playwright extends Helper {
|
|
|
2876
2915
|
let chunked = chunkArray(props, values.length);
|
|
2877
2916
|
chunked = chunked.filter((val) => {
|
|
2878
2917
|
for (let i = 0; i < val.length; ++i) {
|
|
2879
|
-
|
|
2918
|
+
// eslint-disable-next-line eqeqeq
|
|
2919
|
+
if (val[i] != values[i]) return false;
|
|
2880
2920
|
}
|
|
2881
2921
|
return true;
|
|
2882
2922
|
});
|
|
@@ -2914,7 +2954,7 @@ class Playwright extends Helper {
|
|
|
2914
2954
|
let chunked = chunkArray(attrs, values.length);
|
|
2915
2955
|
chunked = chunked.filter((val) => {
|
|
2916
2956
|
for (let i = 0; i < val.length; ++i) {
|
|
2917
|
-
if (val[i]
|
|
2957
|
+
if (!val[i].includes(values[i])) return false;
|
|
2918
2958
|
}
|
|
2919
2959
|
return true;
|
|
2920
2960
|
});
|
|
@@ -3582,7 +3622,7 @@ class Playwright extends Helper {
|
|
|
3582
3622
|
const _contextObject = this.frame ? this.frame : contextObject;
|
|
3583
3623
|
let count = 0;
|
|
3584
3624
|
do {
|
|
3585
|
-
waiter = await _contextObject.locator(`:has-text(
|
|
3625
|
+
waiter = await _contextObject.locator(`:has-text("${text}")`).first().isVisible();
|
|
3586
3626
|
if (waiter) break;
|
|
3587
3627
|
await this.wait(1);
|
|
3588
3628
|
count += 1000;
|
package/docs/build/Puppeteer.js
CHANGED
|
@@ -297,7 +297,7 @@ class Puppeteer extends Helper {
|
|
|
297
297
|
this.sessionPages = {};
|
|
298
298
|
this.currentRunningTest = test;
|
|
299
299
|
recorder.retry({
|
|
300
|
-
retries: 3,
|
|
300
|
+
retries: process.env.FAILED_STEP_RETRIES || 3,
|
|
301
301
|
when: err => {
|
|
302
302
|
if (!err || typeof (err.message) !== 'string') {
|
|
303
303
|
return false;
|
|
@@ -1063,6 +1063,23 @@ class Puppeteer extends Helper {
|
|
|
1063
1063
|
return findFields.call(this, locator);
|
|
1064
1064
|
}
|
|
1065
1065
|
|
|
1066
|
+
/**
|
|
1067
|
+
* Grab WebElements for given locator
|
|
1068
|
+
* Resumes test execution, so **should be used inside an async function with `await`** operator.
|
|
1069
|
+
*
|
|
1070
|
+
* ```js
|
|
1071
|
+
* const webElements = await I.grabWebElements('#button');
|
|
1072
|
+
* ```
|
|
1073
|
+
*
|
|
1074
|
+
* @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
1075
|
+
* @returns {Promise<*>} WebElement of being used Web helper
|
|
1076
|
+
*
|
|
1077
|
+
*
|
|
1078
|
+
*/
|
|
1079
|
+
async grabWebElements(locator) {
|
|
1080
|
+
return this._locate(locator);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1066
1083
|
/**
|
|
1067
1084
|
* Switch focus to a particular tab by its number. It waits tabs loading and then switch tab
|
|
1068
1085
|
*
|
|
@@ -2589,29 +2606,26 @@ class Puppeteer extends Helper {
|
|
|
2589
2606
|
|
|
2590
2607
|
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
2591
2608
|
const elemAmount = res.length;
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
.
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
}));
|
|
2606
|
-
});
|
|
2607
|
-
});
|
|
2608
|
-
let props = await Promise.all(commands);
|
|
2609
|
+
let props = [];
|
|
2610
|
+
|
|
2611
|
+
for (const element of res) {
|
|
2612
|
+
for (const prop of Object.keys(cssProperties)) {
|
|
2613
|
+
const cssProp = await this.grabCssPropertyFrom(locator, prop);
|
|
2614
|
+
if (isColorProperty(prop)) {
|
|
2615
|
+
props.push(convertColorToRGBA(cssProp));
|
|
2616
|
+
} else {
|
|
2617
|
+
props.push(cssProp);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2609
2622
|
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
|
|
2610
2623
|
if (!Array.isArray(props)) props = [props];
|
|
2611
2624
|
let chunked = chunkArray(props, values.length);
|
|
2612
2625
|
chunked = chunked.filter((val) => {
|
|
2613
2626
|
for (let i = 0; i < val.length; ++i) {
|
|
2614
|
-
|
|
2627
|
+
// eslint-disable-next-line eqeqeq
|
|
2628
|
+
if (val[i] != values[i]) return false;
|
|
2615
2629
|
}
|
|
2616
2630
|
return true;
|
|
2617
2631
|
});
|
|
@@ -2651,7 +2665,9 @@ class Puppeteer extends Helper {
|
|
|
2651
2665
|
let chunked = chunkArray(attrs, values.length);
|
|
2652
2666
|
chunked = chunked.filter((val) => {
|
|
2653
2667
|
for (let i = 0; i < val.length; ++i) {
|
|
2654
|
-
|
|
2668
|
+
const _actual = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(values[i], 10);
|
|
2669
|
+
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
|
|
2670
|
+
if (!_actual.includes(_expected)) return false;
|
|
2655
2671
|
}
|
|
2656
2672
|
return true;
|
|
2657
2673
|
});
|
package/docs/build/WebDriver.js
CHANGED
|
@@ -869,6 +869,23 @@ class WebDriver extends Helper {
|
|
|
869
869
|
return findFields.call(this, locator).then(res => res);
|
|
870
870
|
}
|
|
871
871
|
|
|
872
|
+
/**
|
|
873
|
+
* Grab WebElements for given locator
|
|
874
|
+
* Resumes test execution, so **should be used inside an async function with `await`** operator.
|
|
875
|
+
*
|
|
876
|
+
* ```js
|
|
877
|
+
* const webElements = await I.grabWebElements('#button');
|
|
878
|
+
* ```
|
|
879
|
+
*
|
|
880
|
+
* @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
881
|
+
* @returns {Promise<*>} WebElement of being used Web helper
|
|
882
|
+
*
|
|
883
|
+
*
|
|
884
|
+
*/
|
|
885
|
+
async grabWebElements(locator) {
|
|
886
|
+
return this._locate(locator);
|
|
887
|
+
}
|
|
888
|
+
|
|
872
889
|
/**
|
|
873
890
|
* Set [WebDriver timeouts](https://webdriver.io/docs/timeouts.html) in realtime.
|
|
874
891
|
*
|
|
@@ -2013,7 +2030,9 @@ class WebDriver extends Helper {
|
|
|
2013
2030
|
let chunked = chunkArray(props, values.length);
|
|
2014
2031
|
chunked = chunked.filter((val) => {
|
|
2015
2032
|
for (let i = 0; i < val.length; ++i) {
|
|
2016
|
-
|
|
2033
|
+
const _acutal = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(val[i], 10);
|
|
2034
|
+
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
|
|
2035
|
+
if (_acutal !== _expected) return false;
|
|
2017
2036
|
}
|
|
2018
2037
|
return true;
|
|
2019
2038
|
});
|
|
@@ -2049,7 +2068,9 @@ class WebDriver extends Helper {
|
|
|
2049
2068
|
let chunked = chunkArray(attrs, values.length);
|
|
2050
2069
|
chunked = chunked.filter((val) => {
|
|
2051
2070
|
for (let i = 0; i < val.length; ++i) {
|
|
2052
|
-
|
|
2071
|
+
const _acutal = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(val[i], 10);
|
|
2072
|
+
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
|
|
2073
|
+
if (_acutal !== _expected) return false;
|
|
2053
2074
|
}
|
|
2054
2075
|
return true;
|
|
2055
2076
|
});
|
package/docs/helpers/Appium.md
CHANGED
|
@@ -1389,6 +1389,36 @@ let inputs = await I.grabValueFromAll('//form/input');
|
|
|
1389
1389
|
|
|
1390
1390
|
Returns **[Promise][22]<[Array][10]<[string][9]>>** attribute value
|
|
1391
1391
|
|
|
1392
|
+
### grabWebElement
|
|
1393
|
+
|
|
1394
|
+
Grab WebElement for given locator
|
|
1395
|
+
Resumes test execution, so **should be used inside an async function with `await`** operator.
|
|
1396
|
+
|
|
1397
|
+
```js
|
|
1398
|
+
const webElement = await I.grabWebElement('#button');
|
|
1399
|
+
```
|
|
1400
|
+
|
|
1401
|
+
#### Parameters
|
|
1402
|
+
|
|
1403
|
+
- `locator` **([string][9] | [object][6])** element located by CSS|XPath|strict locator.
|
|
1404
|
+
|
|
1405
|
+
Returns **[Promise][22]<any>** WebElement of being used Web helper
|
|
1406
|
+
|
|
1407
|
+
### grabWebElements
|
|
1408
|
+
|
|
1409
|
+
Grab WebElements for given locator
|
|
1410
|
+
Resumes test execution, so **should be used inside an async function with `await`** operator.
|
|
1411
|
+
|
|
1412
|
+
```js
|
|
1413
|
+
const webElements = await I.grabWebElements('#button');
|
|
1414
|
+
```
|
|
1415
|
+
|
|
1416
|
+
#### Parameters
|
|
1417
|
+
|
|
1418
|
+
- `locator` **([string][9] | [object][6])** element located by CSS|XPath|strict locator.
|
|
1419
|
+
|
|
1420
|
+
Returns **[Promise][22]<any>** WebElement of being used Web helper
|
|
1421
|
+
|
|
1392
1422
|
### grabWebSocketMessages
|
|
1393
1423
|
|
|
1394
1424
|
Grab the recording WS messages
|
|
@@ -1216,6 +1216,21 @@ let inputs = await I.grabValueFromAll('//form/input');
|
|
|
1216
1216
|
|
|
1217
1217
|
Returns **[Promise][13]<[Array][15]<[string][6]>>** attribute value
|
|
1218
1218
|
|
|
1219
|
+
### grabWebElements
|
|
1220
|
+
|
|
1221
|
+
Grab WebElements for given locator
|
|
1222
|
+
Resumes test execution, so **should be used inside an async function with `await`** operator.
|
|
1223
|
+
|
|
1224
|
+
```js
|
|
1225
|
+
const webElements = await I.grabWebElements('#button');
|
|
1226
|
+
```
|
|
1227
|
+
|
|
1228
|
+
#### Parameters
|
|
1229
|
+
|
|
1230
|
+
- `locator` **([string][6] | [object][4])** element located by CSS|XPath|strict locator.
|
|
1231
|
+
|
|
1232
|
+
Returns **[Promise][13]<any>** WebElement of being used Web helper
|
|
1233
|
+
|
|
1219
1234
|
### handleDownloads
|
|
1220
1235
|
|
|
1221
1236
|
Sets a directory to where save files. Allows to test file downloads.
|
|
@@ -1367,6 +1367,21 @@ let inputs = await I.grabValueFromAll('//form/input');
|
|
|
1367
1367
|
|
|
1368
1368
|
Returns **[Promise][25]<[Array][28]<[string][17]>>** attribute value
|
|
1369
1369
|
|
|
1370
|
+
### grabWebElements
|
|
1371
|
+
|
|
1372
|
+
Grab WebElements for given locator
|
|
1373
|
+
Resumes test execution, so **should be used inside an async function with `await`** operator.
|
|
1374
|
+
|
|
1375
|
+
```js
|
|
1376
|
+
const webElements = await I.grabWebElements('#button');
|
|
1377
|
+
```
|
|
1378
|
+
|
|
1379
|
+
#### Parameters
|
|
1380
|
+
|
|
1381
|
+
- `locator` **([string][17] | [object][16])** element located by CSS|XPath|strict locator.
|
|
1382
|
+
|
|
1383
|
+
Returns **[Promise][25]<any>** WebElement of being used Web helper
|
|
1384
|
+
|
|
1370
1385
|
### moveCursorTo
|
|
1371
1386
|
|
|
1372
1387
|
Moves cursor to element matched by locator.
|
package/docs/internal-api.md
CHANGED
|
@@ -96,117 +96,7 @@ module.exports = function() {
|
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
```
|
|
99
|
-
You could get test stats when running with workers
|
|
100
99
|
|
|
101
|
-
```js
|
|
102
|
-
const { event } = require('codeceptjs');
|
|
103
|
-
|
|
104
|
-
module.exports = function() {
|
|
105
|
-
|
|
106
|
-
event.dispatcher.on(event.workers.result, function (result) {
|
|
107
|
-
|
|
108
|
-
console.log(result);
|
|
109
|
-
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// in console log
|
|
114
|
-
FAIL | 7 passed, 1 failed, 1 skipped // 2s
|
|
115
|
-
{
|
|
116
|
-
"tests": {
|
|
117
|
-
"passed": [
|
|
118
|
-
{
|
|
119
|
-
"type": "test",
|
|
120
|
-
"title": "Assert @C3",
|
|
121
|
-
"body": "() => { }",
|
|
122
|
-
"async": 0,
|
|
123
|
-
"sync": true,
|
|
124
|
-
"_timeout": 2000,
|
|
125
|
-
"_slow": 75,
|
|
126
|
-
"_retries": -1,
|
|
127
|
-
"timedOut": false,
|
|
128
|
-
"_currentRetry": 0,
|
|
129
|
-
"pending": false,
|
|
130
|
-
"opts": {},
|
|
131
|
-
"tags": [
|
|
132
|
-
"@C3"
|
|
133
|
-
],
|
|
134
|
-
"uid": "xe4q1HdqpRrZG5dPe0JG+A",
|
|
135
|
-
"workerIndex": 3,
|
|
136
|
-
"retries": -1,
|
|
137
|
-
"duration": 493,
|
|
138
|
-
"err": null,
|
|
139
|
-
"parent": {
|
|
140
|
-
"title": "My",
|
|
141
|
-
"ctx": {},
|
|
142
|
-
"suites": [],
|
|
143
|
-
"tests": [],
|
|
144
|
-
"root": false,
|
|
145
|
-
"pending": false,
|
|
146
|
-
"_retries": -1,
|
|
147
|
-
"_beforeEach": [],
|
|
148
|
-
"_beforeAll": [],
|
|
149
|
-
"_afterEach": [],
|
|
150
|
-
"_afterAll": [],
|
|
151
|
-
"_timeout": 2000,
|
|
152
|
-
"_slow": 75,
|
|
153
|
-
"_bail": false,
|
|
154
|
-
"_onlyTests": [],
|
|
155
|
-
"_onlySuites": [],
|
|
156
|
-
"delayed": false
|
|
157
|
-
},
|
|
158
|
-
"steps": [
|
|
159
|
-
{
|
|
160
|
-
"actor": "I",
|
|
161
|
-
"name": "amOnPage",
|
|
162
|
-
"status": "success",
|
|
163
|
-
"agrs": [
|
|
164
|
-
"https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST"
|
|
165
|
-
],
|
|
166
|
-
"startedAt": 1698760652610,
|
|
167
|
-
"startTime": 1698760652611,
|
|
168
|
-
"endTime": 1698760653098,
|
|
169
|
-
"finishedAt": 1698760653098,
|
|
170
|
-
"duration": 488
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
"actor": "I",
|
|
174
|
-
"name": "grabCurrentUrl",
|
|
175
|
-
"status": "success",
|
|
176
|
-
"agrs": [],
|
|
177
|
-
"startedAt": 1698760653098,
|
|
178
|
-
"startTime": 1698760653098,
|
|
179
|
-
"endTime": 1698760653099,
|
|
180
|
-
"finishedAt": 1698760653099,
|
|
181
|
-
"duration": 1
|
|
182
|
-
}
|
|
183
|
-
]
|
|
184
|
-
}
|
|
185
|
-
],
|
|
186
|
-
"failed": [],
|
|
187
|
-
"skipped": []
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
CodeceptJS also exposes the env var `process.env.RUNS_WITH_WORKERS` when running tests with `run-workers` command so that you could handle the events better in your plugins/helpers
|
|
193
|
-
|
|
194
|
-
```js
|
|
195
|
-
const { event } = require('codeceptjs');
|
|
196
|
-
|
|
197
|
-
module.exports = function() {
|
|
198
|
-
// this event would trigger the `_publishResultsToTestrail` when running `run-workers` command
|
|
199
|
-
event.dispatcher.on(event.workers.result, async () => {
|
|
200
|
-
await _publishResultsToTestrail();
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
// this event would not trigger the `_publishResultsToTestrail` multiple times when running `run-workers` command
|
|
204
|
-
event.dispatcher.on(event.all.result, async () => {
|
|
205
|
-
// when running `run` command, this env var is undefined
|
|
206
|
-
if (!process.env.RUNS_WITH_WORKERS) await _publishResultsToTestrail();
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
100
|
Available events:
|
|
211
101
|
|
|
212
102
|
* `event.test.before(test)` - *async* when `Before` hooks from helpers and from test is executed
|