codeceptjs 3.5.3 â 3.5.4-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +0 -44
- package/README.md +1 -1
- package/docs/changelog.md +0 -51
- package/docs/helpers/Playwright.md +16 -3
- package/lib/helper/Playwright.js +101 -136
- package/lib/helper/extras/PlaywrightReact.js +9 -0
- package/package.json +2 -2
- package/docs/helpers/Nightmare.md +0 -1258
- package/docs/helpers/Protractor.md +0 -1709
- package/lib/helper/Nightmare.js +0 -1410
- package/lib/helper/Protractor.js +0 -1832
- package/lib/helper/clientscripts/nightmare.js +0 -213
package/CHANGELOG.md
CHANGED
|
@@ -1,47 +1,3 @@
|
|
|
1
|
-
## 3.5.3
|
|
2
|
-
|
|
3
|
-
đŠī¸ Features
|
|
4
|
-
|
|
5
|
-
* [Playwright] Provide new steps to check network traffic #3748 - by @ngraf @KobeNguyenT
|
|
6
|
-
|
|
7
|
-
```js
|
|
8
|
-
// recording traffics and verify the traffic
|
|
9
|
-
await I.startRecordingTraffic();
|
|
10
|
-
I.amOnPage('https://codecept.io/');
|
|
11
|
-
await I.seeTraffic({ name: 'traffics', url: 'https://codecept.io/img/companies/BC_LogoScreen_C.jpg' });
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
```js
|
|
15
|
-
// block the traffic
|
|
16
|
-
I.blockTraffic('https://reqres.in/api/comments/*');
|
|
17
|
-
await I.amOnPage('/form/fetch_call');
|
|
18
|
-
await I.startRecordingTraffic();
|
|
19
|
-
await I.click('GET COMMENTS');
|
|
20
|
-
await I.see('Can not load data!');
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
```js
|
|
24
|
-
// check the traffic with advanced params
|
|
25
|
-
I.amOnPage('https://openai.com/blog/chatgpt');
|
|
26
|
-
await I.startRecordingTraffic();
|
|
27
|
-
await I.seeTraffic({
|
|
28
|
-
name: 'sentry event',
|
|
29
|
-
url: 'https://images.openai.com/blob/cf717bdb-0c8c-428a-b82b-3c3add87a600',
|
|
30
|
-
parameters: {
|
|
31
|
-
width: '1919',
|
|
32
|
-
height: '1138',
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
đ Bugfix
|
|
38
|
-
|
|
39
|
-
* [retryStepPlugin] Fix retry step when using global retry #3768 - by @KobeNguyenT
|
|
40
|
-
|
|
41
|
-
đ Deprecated
|
|
42
|
-
|
|
43
|
-
* Nightmare and Protractor helpers have been deprecated
|
|
44
|
-
|
|
45
1
|
## 3.5.2
|
|
46
2
|
|
|
47
3
|
đ Bug Fixes
|
package/README.md
CHANGED
|
@@ -304,8 +304,8 @@ Thanks all to those who are and will have contributing to this awesome project!
|
|
|
304
304
|
<a href="https://github.com/pablopaul"><img src="https://avatars.githubusercontent.com/u/635526?v=4" title="pablopaul" width="80" height="80"></a>
|
|
305
305
|
<a href="https://github.com/Georgegriff"><img src="https://avatars.githubusercontent.com/u/9056958?v=4" title="Georgegriff" width="80" height="80"></a>
|
|
306
306
|
<a href="https://github.com/mirao"><img src="https://avatars.githubusercontent.com/u/12584138?v=4" title="mirao" width="80" height="80"></a>
|
|
307
|
-
<a href="https://github.com/actions-user"><img src="https://avatars.githubusercontent.com/u/65916846?v=4" title="actions-user" width="80" height="80"></a>
|
|
308
307
|
<a href="https://github.com/KMKoushik"><img src="https://avatars.githubusercontent.com/u/24666922?v=4" title="KMKoushik" width="80" height="80"></a>
|
|
308
|
+
<a href="https://github.com/actions-user"><img src="https://avatars.githubusercontent.com/u/65916846?v=4" title="actions-user" width="80" height="80"></a>
|
|
309
309
|
<a href="https://github.com/apps/dependabot"><img src="https://avatars.githubusercontent.com/in/29110?v=4" title="dependabot[bot]" width="80" height="80"></a>
|
|
310
310
|
<a href="https://github.com/nikocanvacom"><img src="https://avatars.githubusercontent.com/u/83254493?v=4" title="nikocanvacom" width="80" height="80"></a>
|
|
311
311
|
<a href="https://github.com/elukoyanov"><img src="https://avatars.githubusercontent.com/u/11647141?v=4" title="elukoyanov" width="80" height="80"></a>
|
package/docs/changelog.md
CHANGED
|
@@ -7,57 +7,6 @@ layout: Section
|
|
|
7
7
|
|
|
8
8
|
# Releases
|
|
9
9
|
|
|
10
|
-
## 3.5.3
|
|
11
|
-
|
|
12
|
-
đŠī¸ Features
|
|
13
|
-
|
|
14
|
-
* **[Playwright]** Provide new steps to check network traffic [#3748](https://github.com/codeceptjs/CodeceptJS/issues/3748) - by **[ngraf](https://github.com/ngraf)** **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
15
|
-
|
|
16
|
-
```js
|
|
17
|
-
// recording traffics and verify the traffic
|
|
18
|
-
await I.startRecordingTraffic();
|
|
19
|
-
I.amOnPage('https://codecept.io/');
|
|
20
|
-
await I.seeTraffic({ name: 'traffics', url: 'https://codecept.io/img/companies/BC_LogoScreen_C.jpg' });
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
```js
|
|
24
|
-
// block the traffic
|
|
25
|
-
I.blockTraffic('https://reqres.in/api/comments/*');
|
|
26
|
-
await I.amOnPage('/form/fetch_call');
|
|
27
|
-
await I.startRecordingTraffic();
|
|
28
|
-
await I.click('GET COMMENTS');
|
|
29
|
-
await I.see('Can not load data!');
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
```js
|
|
33
|
-
// check the traffic with advanced params
|
|
34
|
-
I.amOnPage('https://openai.com/blog/chatgpt');
|
|
35
|
-
await I.startRecordingTraffic();
|
|
36
|
-
await I.seeTraffic({
|
|
37
|
-
name: 'sentry event',
|
|
38
|
-
url: 'https://images.openai.com/blob/cf717bdb-0c8c-428a-b82b-3c3add87a600',
|
|
39
|
-
parameters: {
|
|
40
|
-
width: '1919',
|
|
41
|
-
height: '1138',
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
đ Bugfix
|
|
47
|
-
|
|
48
|
-
* **[retryStepPlugin]** Fix retry step when using global retry [#3768](https://github.com/codeceptjs/CodeceptJS/issues/3768) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
49
|
-
|
|
50
|
-
đ Deprecated
|
|
51
|
-
|
|
52
|
-
* Nightmare and Protractor helpers have been deprecated
|
|
53
|
-
|
|
54
|
-
## 3.5.2
|
|
55
|
-
|
|
56
|
-
đ Bug Fixes
|
|
57
|
-
|
|
58
|
-
* **[Playwright]** reverted `clearField` to previous implementation
|
|
59
|
-
* **[OpenAI]** fixed running helper in pause mode. [#3755](https://github.com/codeceptjs/CodeceptJS/issues/3755) by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
60
|
-
|
|
61
10
|
## 3.5.1
|
|
62
11
|
|
|
63
12
|
đŠī¸ Features
|
|
@@ -299,7 +299,7 @@ const elements = await this.helpers['Playwright']._locate({name: 'password'});
|
|
|
299
299
|
|
|
300
300
|
### _locateCheckable
|
|
301
301
|
|
|
302
|
-
Find a checkbox by providing human
|
|
302
|
+
Find a checkbox by providing human-readable text:
|
|
303
303
|
NOTE: Assumes the checkable element exists
|
|
304
304
|
|
|
305
305
|
```js
|
|
@@ -313,7 +313,7 @@ this.helpers['Playwright']._locateCheckable('I agree with terms and conditions')
|
|
|
313
313
|
|
|
314
314
|
### _locateClickable
|
|
315
315
|
|
|
316
|
-
Find a clickable element by providing human
|
|
316
|
+
Find a clickable element by providing human-readable text:
|
|
317
317
|
|
|
318
318
|
```js
|
|
319
319
|
this.helpers['Playwright']._locateClickable('Next page').then // ...
|
|
@@ -323,9 +323,22 @@ this.helpers['Playwright']._locateClickable('Next page').then // ...
|
|
|
323
323
|
|
|
324
324
|
- `locator`
|
|
325
325
|
|
|
326
|
+
### _locateElement
|
|
327
|
+
|
|
328
|
+
Get the first element by different locator types, including strict locator
|
|
329
|
+
Should be used in custom helpers:
|
|
330
|
+
|
|
331
|
+
```js
|
|
332
|
+
const element = await this.helpers['Playwright']._locateElement({name: 'password'});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
#### Parameters
|
|
336
|
+
|
|
337
|
+
- `locator`
|
|
338
|
+
|
|
326
339
|
### _locateFields
|
|
327
340
|
|
|
328
|
-
Find field elements by providing human
|
|
341
|
+
Find field elements by providing human-readable text:
|
|
329
342
|
|
|
330
343
|
```js
|
|
331
344
|
this.helpers['Playwright']._locateFields('Your email').then // ...
|
package/lib/helper/Playwright.js
CHANGED
|
@@ -824,9 +824,9 @@ class Playwright extends Helper {
|
|
|
824
824
|
return;
|
|
825
825
|
}
|
|
826
826
|
|
|
827
|
-
const
|
|
828
|
-
assertElementExists(
|
|
829
|
-
this.context =
|
|
827
|
+
const el = await this._locateElement(locator);
|
|
828
|
+
assertElementExists(el, locator);
|
|
829
|
+
this.context = el;
|
|
830
830
|
this.contextLocator = locator;
|
|
831
831
|
|
|
832
832
|
this.withinLocator = new Locator(locator);
|
|
@@ -929,11 +929,11 @@ class Playwright extends Helper {
|
|
|
929
929
|
*
|
|
930
930
|
*/
|
|
931
931
|
async moveCursorTo(locator, offsetX = 0, offsetY = 0) {
|
|
932
|
-
const
|
|
933
|
-
assertElementExists(
|
|
932
|
+
const el = await this._locateElement(locator);
|
|
933
|
+
assertElementExists(el, locator);
|
|
934
934
|
|
|
935
935
|
// Use manual mouse.move instead of .hover() so the offset can be added to the coordinates
|
|
936
|
-
const { x, y } = await clickablePoint(
|
|
936
|
+
const { x, y } = await clickablePoint(el);
|
|
937
937
|
await this.page.mouse.move(x + offsetX, y + offsetY);
|
|
938
938
|
return this._waitForAction();
|
|
939
939
|
}
|
|
@@ -943,9 +943,8 @@ class Playwright extends Helper {
|
|
|
943
943
|
*
|
|
944
944
|
*/
|
|
945
945
|
async focus(locator, options = {}) {
|
|
946
|
-
const
|
|
947
|
-
assertElementExists(
|
|
948
|
-
const el = els[0];
|
|
946
|
+
const el = await this._locateElement(locator);
|
|
947
|
+
assertElementExists(el, locator, 'Element to focus');
|
|
949
948
|
|
|
950
949
|
await el.focus(options);
|
|
951
950
|
return this._waitForAction();
|
|
@@ -956,12 +955,10 @@ class Playwright extends Helper {
|
|
|
956
955
|
*
|
|
957
956
|
*/
|
|
958
957
|
async blur(locator, options = {}) {
|
|
959
|
-
const
|
|
960
|
-
assertElementExists(
|
|
961
|
-
// TODO: locator change required after #3677 implementation
|
|
962
|
-
const elXpath = await getXPathForElement(els[0]);
|
|
958
|
+
const el = await this._locateElement(locator);
|
|
959
|
+
assertElementExists(el, locator, 'Element to blur');
|
|
963
960
|
|
|
964
|
-
await
|
|
961
|
+
await el.blur(options);
|
|
965
962
|
return this._waitForAction();
|
|
966
963
|
}
|
|
967
964
|
|
|
@@ -1058,10 +1055,10 @@ class Playwright extends Helper {
|
|
|
1058
1055
|
}
|
|
1059
1056
|
|
|
1060
1057
|
if (locator) {
|
|
1061
|
-
const
|
|
1062
|
-
assertElementExists(
|
|
1063
|
-
await
|
|
1064
|
-
const elementCoordinates = await clickablePoint(
|
|
1058
|
+
const el = await this._locateElement(locator);
|
|
1059
|
+
assertElementExists(el, locator, 'Element');
|
|
1060
|
+
await el.scrollIntoViewIfNeeded();
|
|
1061
|
+
const elementCoordinates = await clickablePoint(el);
|
|
1065
1062
|
await this.executeScript((offsetX, offsetY) => window.scrollBy(offsetX, offsetY), { offsetX: elementCoordinates.x + offsetX, offsetY: elementCoordinates.y + offsetY });
|
|
1066
1063
|
} else {
|
|
1067
1064
|
await this.executeScript(({ offsetX, offsetY }) => window.scrollTo(offsetX, offsetY), { offsetX, offsetY });
|
|
@@ -1129,7 +1126,20 @@ class Playwright extends Helper {
|
|
|
1129
1126
|
}
|
|
1130
1127
|
|
|
1131
1128
|
/**
|
|
1132
|
-
*
|
|
1129
|
+
* Get the first element by different locator types, including strict locator
|
|
1130
|
+
* Should be used in custom helpers:
|
|
1131
|
+
*
|
|
1132
|
+
* ```js
|
|
1133
|
+
* const element = await this.helpers['Playwright']._locateElement({name: 'password'});
|
|
1134
|
+
* ```
|
|
1135
|
+
*/
|
|
1136
|
+
async _locateElement(locator) {
|
|
1137
|
+
const context = await this.context || await this._getContext();
|
|
1138
|
+
return findElement(context, locator);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/**
|
|
1142
|
+
* Find a checkbox by providing human-readable text:
|
|
1133
1143
|
* NOTE: Assumes the checkable element exists
|
|
1134
1144
|
*
|
|
1135
1145
|
* ```js
|
|
@@ -1144,7 +1154,7 @@ class Playwright extends Helper {
|
|
|
1144
1154
|
}
|
|
1145
1155
|
|
|
1146
1156
|
/**
|
|
1147
|
-
* Find a clickable element by providing human
|
|
1157
|
+
* Find a clickable element by providing human-readable text:
|
|
1148
1158
|
*
|
|
1149
1159
|
* ```js
|
|
1150
1160
|
* this.helpers['Playwright']._locateClickable('Next page').then // ...
|
|
@@ -1156,7 +1166,7 @@ class Playwright extends Helper {
|
|
|
1156
1166
|
}
|
|
1157
1167
|
|
|
1158
1168
|
/**
|
|
1159
|
-
* Find field elements by providing human
|
|
1169
|
+
* Find field elements by providing human-readable text:
|
|
1160
1170
|
*
|
|
1161
1171
|
* ```js
|
|
1162
1172
|
* this.helpers['Playwright']._locateFields('Your email').then // ...
|
|
@@ -1532,13 +1542,8 @@ class Playwright extends Helper {
|
|
|
1532
1542
|
const els = await findFields.call(this, field);
|
|
1533
1543
|
assertElementExists(els, field, 'Field');
|
|
1534
1544
|
const el = els[0];
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
if (tag === 'INPUT' || tag === 'TEXTAREA') {
|
|
1538
|
-
await this._evaluateHandeInContext(el => el.value = '', el);
|
|
1539
|
-
} else if (editable) {
|
|
1540
|
-
await this._evaluateHandeInContext(el => el.innerHTML = '', el);
|
|
1541
|
-
}
|
|
1545
|
+
|
|
1546
|
+
await el.clear();
|
|
1542
1547
|
|
|
1543
1548
|
highlightActiveElement.call(this, el, this.page);
|
|
1544
1549
|
|
|
@@ -1565,21 +1570,16 @@ class Playwright extends Helper {
|
|
|
1565
1570
|
* @param {any} [options] [Additional options](https://playwright.dev/docs/api/class-locator#locator-clear) for available options object as 2nd argument.
|
|
1566
1571
|
*/
|
|
1567
1572
|
async clearField(locator, options = {}) {
|
|
1568
|
-
|
|
1569
|
-
|
|
1573
|
+
const els = await findFields.call(this, locator);
|
|
1574
|
+
assertElementExists(els, locator, 'Field to clear');
|
|
1570
1575
|
|
|
1571
|
-
|
|
1572
|
-
const els = await findFields.call(this, locator);
|
|
1573
|
-
assertElementExists(els, locator, 'Field to clear');
|
|
1574
|
-
// TODO: locator change required after #3677 implementation
|
|
1575
|
-
const elXpath = await getXPathForElement(els[0]);
|
|
1576
|
+
const el = els[0];
|
|
1576
1577
|
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
return result;
|
|
1578
|
+
highlightActiveElement.call(this, el, this.page);
|
|
1579
|
+
|
|
1580
|
+
await el.clear();
|
|
1581
|
+
|
|
1582
|
+
return this._waitForAction();
|
|
1583
1583
|
}
|
|
1584
1584
|
|
|
1585
1585
|
/**
|
|
@@ -1633,29 +1633,11 @@ class Playwright extends Helper {
|
|
|
1633
1633
|
const els = await findFields.call(this, select);
|
|
1634
1634
|
assertElementExists(els, select, 'Selectable field');
|
|
1635
1635
|
const el = els[0];
|
|
1636
|
-
|
|
1637
|
-
throw new Error('Element is not <select>');
|
|
1638
|
-
}
|
|
1636
|
+
|
|
1639
1637
|
highlightActiveElement.call(this, el, this.page);
|
|
1640
1638
|
if (!Array.isArray(option)) option = [option];
|
|
1641
1639
|
|
|
1642
|
-
|
|
1643
|
-
const opt = xpathLocator.literal(option[key]);
|
|
1644
|
-
let optEl = await findElements.call(this, el, { xpath: Locator.select.byVisibleText(opt) });
|
|
1645
|
-
if (optEl.length) {
|
|
1646
|
-
this._evaluateHandeInContext(el => el.selected = true, optEl[0]);
|
|
1647
|
-
continue;
|
|
1648
|
-
}
|
|
1649
|
-
optEl = await findElements.call(this, el, { xpath: Locator.select.byValue(opt) });
|
|
1650
|
-
if (optEl.length) {
|
|
1651
|
-
this._evaluateHandeInContext(el => el.selected = true, optEl[0]);
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
await this._evaluateHandeInContext((element) => {
|
|
1655
|
-
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
1656
|
-
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1657
|
-
}, el);
|
|
1658
|
-
|
|
1640
|
+
await el.selectOption(option);
|
|
1659
1641
|
return this._waitForAction();
|
|
1660
1642
|
}
|
|
1661
1643
|
|
|
@@ -1906,7 +1888,7 @@ class Playwright extends Helper {
|
|
|
1906
1888
|
const els = await this._locate(locator);
|
|
1907
1889
|
const texts = [];
|
|
1908
1890
|
for (const el of els) {
|
|
1909
|
-
texts.push(await (await el.
|
|
1891
|
+
texts.push(await (await el.innerText()));
|
|
1910
1892
|
}
|
|
1911
1893
|
this.debug(`Matched ${els.length} elements`);
|
|
1912
1894
|
return texts;
|
|
@@ -1928,7 +1910,7 @@ class Playwright extends Helper {
|
|
|
1928
1910
|
async grabValueFromAll(locator) {
|
|
1929
1911
|
const els = await findFields.call(this, locator);
|
|
1930
1912
|
this.debug(`Matched ${els.length} elements`);
|
|
1931
|
-
return Promise.all(els.map(el => el.
|
|
1913
|
+
return Promise.all(els.map(el => el.inputValue()));
|
|
1932
1914
|
}
|
|
1933
1915
|
|
|
1934
1916
|
/**
|
|
@@ -1947,7 +1929,7 @@ class Playwright extends Helper {
|
|
|
1947
1929
|
async grabHTMLFromAll(locator) {
|
|
1948
1930
|
const els = await this._locate(locator);
|
|
1949
1931
|
this.debug(`Matched ${els.length} elements`);
|
|
1950
|
-
return Promise.all(els.map(el => el
|
|
1932
|
+
return Promise.all(els.map(el => el.innerHTML()));
|
|
1951
1933
|
}
|
|
1952
1934
|
|
|
1953
1935
|
/**
|
|
@@ -1968,7 +1950,7 @@ class Playwright extends Helper {
|
|
|
1968
1950
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
1969
1951
|
const els = await this._locate(locator);
|
|
1970
1952
|
this.debug(`Matched ${els.length} elements`);
|
|
1971
|
-
const cssValues = await Promise.all(els.map(el => el
|
|
1953
|
+
const cssValues = await Promise.all(els.map(el => el.evaluate((el, cssProperty) => getComputedStyle(el).getPropertyValue(cssProperty), cssProperty)));
|
|
1972
1954
|
|
|
1973
1955
|
return cssValues;
|
|
1974
1956
|
}
|
|
@@ -1984,21 +1966,20 @@ class Playwright extends Helper {
|
|
|
1984
1966
|
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
1985
1967
|
const elemAmount = res.length;
|
|
1986
1968
|
const commands = [];
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
}));
|
|
1969
|
+
let props = [];
|
|
1970
|
+
|
|
1971
|
+
for (const element of res) {
|
|
1972
|
+
const cssProperties = await element.evaluate((el) => getComputedStyle(el));
|
|
1973
|
+
|
|
1974
|
+
Object.keys(cssPropertiesCamelCase).forEach(prop => {
|
|
1975
|
+
if (isColorProperty(prop)) {
|
|
1976
|
+
props.push(convertColorToRGBA(cssProperties[prop]));
|
|
1977
|
+
} else {
|
|
1978
|
+
props.push(cssProperties[prop]);
|
|
1979
|
+
}
|
|
1999
1980
|
});
|
|
2000
|
-
}
|
|
2001
|
-
|
|
1981
|
+
}
|
|
1982
|
+
|
|
2002
1983
|
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
|
|
2003
1984
|
if (!Array.isArray(props)) props = [props];
|
|
2004
1985
|
let chunked = chunkArray(props, values.length);
|
|
@@ -2024,7 +2005,7 @@ class Playwright extends Helper {
|
|
|
2024
2005
|
res.forEach((el) => {
|
|
2025
2006
|
Object.keys(attributes).forEach((prop) => {
|
|
2026
2007
|
commands.push(el
|
|
2027
|
-
|
|
2008
|
+
.evaluate((el, attr) => el[attr] || el.getAttribute(attr), prop));
|
|
2028
2009
|
});
|
|
2029
2010
|
});
|
|
2030
2011
|
let attrs = await Promise.all(commands);
|
|
@@ -2045,11 +2026,11 @@ class Playwright extends Helper {
|
|
|
2045
2026
|
*
|
|
2046
2027
|
*/
|
|
2047
2028
|
async dragSlider(locator, offsetX = 0) {
|
|
2048
|
-
const src = await this.
|
|
2029
|
+
const src = await this._locateElement(locator);
|
|
2049
2030
|
assertElementExists(src, locator, 'Slider Element');
|
|
2050
2031
|
|
|
2051
2032
|
// Note: Using clickablePoint private api because the .BoundingBox does not take into account iframe offsets!
|
|
2052
|
-
const sliderSource = await clickablePoint(src
|
|
2033
|
+
const sliderSource = await clickablePoint(src);
|
|
2053
2034
|
|
|
2054
2035
|
// Drag start point
|
|
2055
2036
|
await this.page.mouse.move(sliderSource.x, sliderSource.y, { steps: 5 });
|
|
@@ -2083,8 +2064,7 @@ class Playwright extends Helper {
|
|
|
2083
2064
|
const array = [];
|
|
2084
2065
|
|
|
2085
2066
|
for (let index = 0; index < els.length; index++) {
|
|
2086
|
-
|
|
2087
|
-
array.push(await a.jsonValue());
|
|
2067
|
+
array.push(await els[index].getAttribute(attr));
|
|
2088
2068
|
}
|
|
2089
2069
|
|
|
2090
2070
|
return array;
|
|
@@ -2097,10 +2077,9 @@ class Playwright extends Helper {
|
|
|
2097
2077
|
async saveElementScreenshot(locator, fileName) {
|
|
2098
2078
|
const outputFile = screenshotOutputFolder(fileName);
|
|
2099
2079
|
|
|
2100
|
-
const res = await this.
|
|
2080
|
+
const res = await this._locateElement(locator);
|
|
2101
2081
|
assertElementExists(res, locator);
|
|
2102
|
-
|
|
2103
|
-
const elem = res[0];
|
|
2082
|
+
const elem = res;
|
|
2104
2083
|
this.debug(`Screenshot of ${(new Locator(locator))} element has been saved to ${outputFile}`);
|
|
2105
2084
|
return elem.screenshot({ path: outputFile, type: 'png' });
|
|
2106
2085
|
}
|
|
@@ -2526,16 +2505,26 @@ class Playwright extends Helper {
|
|
|
2526
2505
|
}
|
|
2527
2506
|
return;
|
|
2528
2507
|
}
|
|
2508
|
+
let contentFrame;
|
|
2509
|
+
|
|
2529
2510
|
if (!locator) {
|
|
2530
|
-
this.context = this.page;
|
|
2511
|
+
this.context = await this.page.frames()[0];
|
|
2531
2512
|
this.contextLocator = null;
|
|
2532
2513
|
return;
|
|
2533
2514
|
}
|
|
2534
2515
|
|
|
2535
2516
|
// iframe by selector
|
|
2536
2517
|
const els = await this._locate(locator);
|
|
2537
|
-
assertElementExists(els, locator);
|
|
2538
|
-
|
|
2518
|
+
// assertElementExists(els, locator);
|
|
2519
|
+
|
|
2520
|
+
// get content of the first iframe
|
|
2521
|
+
if ((locator.frame && locator.frame === 'iframe') || locator.toLowerCase() === 'iframe') {
|
|
2522
|
+
contentFrame = await this.page.frames()[1];
|
|
2523
|
+
// get content of the iframe using its name
|
|
2524
|
+
} else if (locator.toLowerCase().includes('name=')) {
|
|
2525
|
+
const frameName = locator.split('=')[1].replace(/"/g, '').replaceAll(/]/g, '');
|
|
2526
|
+
contentFrame = await this.page.frame(frameName);
|
|
2527
|
+
}
|
|
2539
2528
|
|
|
2540
2529
|
if (contentFrame) {
|
|
2541
2530
|
this.context = contentFrame;
|
|
@@ -2624,9 +2613,9 @@ class Playwright extends Helper {
|
|
|
2624
2613
|
* {{> grabElementBoundingRect }}
|
|
2625
2614
|
*/
|
|
2626
2615
|
async grabElementBoundingRect(locator, prop) {
|
|
2627
|
-
const
|
|
2628
|
-
assertElementExists(
|
|
2629
|
-
const rect = await
|
|
2616
|
+
const el = await this._locateElement(locator);
|
|
2617
|
+
assertElementExists(el, locator);
|
|
2618
|
+
const rect = await el.boundingBox();
|
|
2630
2619
|
if (prop) return rect[prop];
|
|
2631
2620
|
return rect;
|
|
2632
2621
|
}
|
|
@@ -2997,42 +2986,19 @@ function buildLocatorString(locator) {
|
|
|
2997
2986
|
}
|
|
2998
2987
|
return locator.simplify();
|
|
2999
2988
|
}
|
|
3000
|
-
// TODO: locator change required after #3677 implementation. Temporary solution before migration. Should be deleted after #3677 implementation
|
|
3001
|
-
async function getXPathForElement(elementHandle) {
|
|
3002
|
-
function calculateIndex(node) {
|
|
3003
|
-
let index = 1;
|
|
3004
|
-
let sibling = node.previousElementSibling;
|
|
3005
|
-
while (sibling) {
|
|
3006
|
-
if (sibling.tagName === node.tagName) {
|
|
3007
|
-
index++;
|
|
3008
|
-
}
|
|
3009
|
-
sibling = sibling.previousElementSibling;
|
|
3010
|
-
}
|
|
3011
|
-
return index;
|
|
3012
|
-
}
|
|
3013
2989
|
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
if (node.hasAttribute('id')) {
|
|
3018
|
-
segments.unshift(`*[@id="${node.getAttribute('id')}"]`);
|
|
3019
|
-
break;
|
|
3020
|
-
} else {
|
|
3021
|
-
const index = calculateIndex(node);
|
|
3022
|
-
segments.unshift(`${node.localName}[${index}]`);
|
|
3023
|
-
node = node.parentNode;
|
|
3024
|
-
}
|
|
3025
|
-
}
|
|
3026
|
-
return `//${segments.join('/')}`;
|
|
3027
|
-
}
|
|
2990
|
+
async function findElements(matcher, locator) {
|
|
2991
|
+
if (locator.react) return findReact(matcher, locator);
|
|
2992
|
+
locator = new Locator(locator, 'css');
|
|
3028
2993
|
|
|
3029
|
-
return
|
|
2994
|
+
return matcher.locator(buildLocatorString(locator)).all();
|
|
3030
2995
|
}
|
|
3031
2996
|
|
|
3032
|
-
async function
|
|
2997
|
+
async function findElement(matcher, locator) {
|
|
3033
2998
|
if (locator.react) return findReact(matcher, locator);
|
|
3034
2999
|
locator = new Locator(locator, 'css');
|
|
3035
|
-
|
|
3000
|
+
|
|
3001
|
+
return matcher.locator(buildLocatorString(locator));
|
|
3036
3002
|
}
|
|
3037
3003
|
|
|
3038
3004
|
async function getVisibleElements(elements) {
|
|
@@ -3062,7 +3028,6 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
3062
3028
|
assertElementExists(els, locator, 'Clickable element');
|
|
3063
3029
|
}
|
|
3064
3030
|
|
|
3065
|
-
const element = els[0];
|
|
3066
3031
|
highlightActiveElement.call(this, els[0], this.page);
|
|
3067
3032
|
|
|
3068
3033
|
/*
|
|
@@ -3111,22 +3076,22 @@ async function findClickable(matcher, locator) {
|
|
|
3111
3076
|
async function proceedSee(assertType, text, context, strict = false) {
|
|
3112
3077
|
let description;
|
|
3113
3078
|
let allText;
|
|
3079
|
+
|
|
3114
3080
|
if (!context) {
|
|
3115
3081
|
let el = await this.context;
|
|
3116
|
-
|
|
3117
3082
|
if (el && !el.getProperty) {
|
|
3118
3083
|
// Fallback to body
|
|
3119
|
-
el = await this.
|
|
3084
|
+
el = await this.page.$('body');
|
|
3120
3085
|
}
|
|
3121
3086
|
|
|
3122
|
-
allText = [await el.
|
|
3087
|
+
allText = [await el.innerText()];
|
|
3123
3088
|
description = 'web application';
|
|
3124
3089
|
} else {
|
|
3125
3090
|
const locator = new Locator(context, 'css');
|
|
3126
3091
|
description = `element ${locator.toString()}`;
|
|
3127
3092
|
const els = await this._locate(locator);
|
|
3128
3093
|
assertElementExists(els, locator.toString());
|
|
3129
|
-
allText = await Promise.all(els.map(el => el.
|
|
3094
|
+
allText = await Promise.all(els.map(el => el.innerText()));
|
|
3130
3095
|
}
|
|
3131
3096
|
|
|
3132
3097
|
if (strict) {
|
|
@@ -3194,15 +3159,15 @@ async function proceedSeeInField(assertType, field, value) {
|
|
|
3194
3159
|
const els = await findFields.call(this, field);
|
|
3195
3160
|
assertElementExists(els, field, 'Field');
|
|
3196
3161
|
const el = els[0];
|
|
3197
|
-
const tag = await el.
|
|
3198
|
-
const fieldType = await el.
|
|
3162
|
+
const tag = await el.evaluate(e => e.tagName);
|
|
3163
|
+
const fieldType = await el.getAttribute('type');
|
|
3199
3164
|
|
|
3200
3165
|
const proceedMultiple = async (elements) => {
|
|
3201
3166
|
const fields = Array.isArray(elements) ? elements : [elements];
|
|
3202
3167
|
|
|
3203
3168
|
const elementValues = [];
|
|
3204
3169
|
for (const element of fields) {
|
|
3205
|
-
elementValues.push(await element.
|
|
3170
|
+
elementValues.push(await element.inputValue());
|
|
3206
3171
|
}
|
|
3207
3172
|
|
|
3208
3173
|
if (typeof value === 'boolean') {
|
|
@@ -3216,8 +3181,8 @@ async function proceedSeeInField(assertType, field, value) {
|
|
|
3216
3181
|
};
|
|
3217
3182
|
|
|
3218
3183
|
if (tag === 'SELECT') {
|
|
3219
|
-
if (await el.
|
|
3220
|
-
const selectedOptions = await el
|
|
3184
|
+
if (await el.getAttribute('multiple')) {
|
|
3185
|
+
const selectedOptions = await el.all('option:checked');
|
|
3221
3186
|
if (!selectedOptions.length) return null;
|
|
3222
3187
|
|
|
3223
3188
|
const options = await filterFieldsByValue(selectedOptions, value, true);
|
|
@@ -3248,7 +3213,7 @@ async function proceedSeeInField(assertType, field, value) {
|
|
|
3248
3213
|
async function filterFieldsByValue(elements, value, onlySelected) {
|
|
3249
3214
|
const matches = [];
|
|
3250
3215
|
for (const element of elements) {
|
|
3251
|
-
const val = await element.
|
|
3216
|
+
const val = await element.getAttribute('value');
|
|
3252
3217
|
let isSelected = true;
|
|
3253
3218
|
if (onlySelected) {
|
|
3254
3219
|
isSelected = await elementSelected(element);
|
|
@@ -3272,12 +3237,12 @@ async function filterFieldsBySelectionState(elements, state) {
|
|
|
3272
3237
|
}
|
|
3273
3238
|
|
|
3274
3239
|
async function elementSelected(element) {
|
|
3275
|
-
const type = await element.
|
|
3240
|
+
const type = await element.getAttribute('type');
|
|
3276
3241
|
|
|
3277
3242
|
if (type === 'checkbox' || type === 'radio') {
|
|
3278
3243
|
return element.isChecked();
|
|
3279
3244
|
}
|
|
3280
|
-
return element.
|
|
3245
|
+
return element.getAttribute('selected');
|
|
3281
3246
|
}
|
|
3282
3247
|
|
|
3283
3248
|
function isFrameLocator(locator) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeceptjs",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.4-beta.1",
|
|
4
4
|
"description": "Supercharged End 2 End Testing Framework for NodeJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"acceptance",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"lint": "eslint bin/ examples/ lib/ test/ translations/ runok.js",
|
|
41
41
|
"lint-fix": "eslint bin/ examples/ lib/ test/ translations/ runok.js --fix",
|
|
42
42
|
"docs": "./runok.js docs",
|
|
43
|
-
"test:unit": "mocha test/unit --recursive",
|
|
43
|
+
"test:unit": "mocha test/unit --recursive --timeout 5000",
|
|
44
44
|
"test:runner": "mocha test/runner --recursive --timeout 5000",
|
|
45
45
|
"test": "npm run test:unit && npm run test:runner",
|
|
46
46
|
"test:appium-quick": "mocha test/helper/Appium_test.js --grep 'quick'",
|