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 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 readable text:
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 readable text:
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 readable text:
341
+ Find field elements by providing human-readable text:
329
342
 
330
343
  ```js
331
344
  this.helpers['Playwright']._locateFields('Your email').then // ...
@@ -824,9 +824,9 @@ class Playwright extends Helper {
824
824
  return;
825
825
  }
826
826
 
827
- const els = await this._locate(locator);
828
- assertElementExists(els, locator);
829
- this.context = els[0];
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 els = await this._locate(locator);
933
- assertElementExists(els, locator);
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(els[0]);
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 els = await this._locate(locator);
947
- assertElementExists(els, locator, 'Element to focus');
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 els = await this._locate(locator);
960
- assertElementExists(els, locator, 'Element to blur');
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 this.page.locator(elXpath).blur(options);
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 els = await this._locate(locator);
1062
- assertElementExists(els, locator, 'Element');
1063
- await els[0].scrollIntoViewIfNeeded();
1064
- const elementCoordinates = await clickablePoint(els[0]);
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
- * Find a checkbox by providing human readable text:
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 readable text:
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 readable text:
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
- const tag = await el.getProperty('tagName').then(el => el.jsonValue());
1536
- const editable = await el.getProperty('contenteditable').then(el => el.jsonValue());
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
- let result;
1569
- const isNewClearMethodPresent = false; // not works, disabled for now. Prev: typeof this.page.locator().clear === 'function';
1573
+ const els = await findFields.call(this, locator);
1574
+ assertElementExists(els, locator, 'Field to clear');
1570
1575
 
1571
- if (isNewClearMethodPresent) {
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
- await this.page.locator(elXpath).clear(options);
1578
- result = await this._waitForAction();
1579
- } else {
1580
- result = await this.fillField(locator, '');
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
- if (await el.getProperty('tagName').then(t => t.jsonValue()) !== 'SELECT') {
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
- for (const key in option) {
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.getProperty('innerText')).jsonValue());
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.getProperty('value').then(t => t.jsonValue())));
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.$eval('xpath=.', element => element.innerHTML, 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.$eval('xpath=.', (el, cssProperty) => getComputedStyle(el).getPropertyValue(cssProperty), cssProperty)));
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
- res.forEach((el) => {
1988
- Object.keys(cssPropertiesCamelCase).forEach((prop) => {
1989
- commands.push(el.$eval('xpath=.', (el) => {
1990
- const style = window.getComputedStyle ? getComputedStyle(el) : el.currentStyle;
1991
- return JSON.parse(JSON.stringify(style));
1992
- }, el)
1993
- .then((props) => {
1994
- if (isColorProperty(prop)) {
1995
- return convertColorToRGBA(props[prop]);
1996
- }
1997
- return props[prop];
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
- let props = await Promise.all(commands);
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
- .$eval('xpath=.', (el, attr) => el[attr] || el.getAttribute(attr), prop));
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._locate(locator);
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[0]);
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
- const a = await this._evaluateHandeInContext(([el, attr]) => el[attr] || el.getAttribute(attr), [els[index], attr]);
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._locate(locator);
2080
+ const res = await this._locateElement(locator);
2101
2081
  assertElementExists(res, locator);
2102
- if (res.length > 1) this.debug(`[Elements] Using first element out of ${res.length}`);
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
- const contentFrame = await els[0].contentFrame();
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 els = await this._locate(locator);
2628
- assertElementExists(els, locator);
2629
- const rect = await els[0].boundingBox();
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
- function generateXPath(node) {
3015
- const segments = [];
3016
- while (node && node.nodeType === Node.ELEMENT_NODE) {
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 elementHandle.evaluate(generateXPath);
2994
+ return matcher.locator(buildLocatorString(locator)).all();
3030
2995
  }
3031
2996
 
3032
- async function findElements(matcher, locator) {
2997
+ async function findElement(matcher, locator) {
3033
2998
  if (locator.react) return findReact(matcher, locator);
3034
2999
  locator = new Locator(locator, 'css');
3035
- return matcher.$$(buildLocatorString(locator));
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.context.$('body');
3084
+ el = await this.page.$('body');
3120
3085
  }
3121
3086
 
3122
- allText = [await el.getProperty('innerText').then(p => p.jsonValue())];
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.getProperty('innerText').then(p => p.jsonValue())));
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.getProperty('tagName').then(el => el.jsonValue());
3198
- const fieldType = await el.getProperty('type').then(el => el.jsonValue());
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.getProperty('value').then(el => el.jsonValue()));
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.getProperty('multiple')) {
3220
- const selectedOptions = await el.$$('option:checked');
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.getProperty('value').then(el => el.jsonValue());
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.getProperty('type').then(el => !!el && el.jsonValue());
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.getProperty('selected').then(el => el.jsonValue());
3245
+ return element.getAttribute('selected');
3281
3246
  }
3282
3247
 
3283
3248
  function isFrameLocator(locator) {
@@ -0,0 +1,9 @@
1
+ module.exports = async function findReact(matcher, locator) {
2
+ let _locator = `_react=${locator.react}`;
3
+
4
+ if (locator.props && locator.props.name) {
5
+ _locator += `[name = "${locator.props.name}"]`;
6
+ }
7
+
8
+ return matcher.locator(_locator).all();
9
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeceptjs",
3
- "version": "3.5.3",
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'",