codeceptjs 3.1.2 → 3.1.3

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,3 +1,39 @@
1
+ ## 3.1.3
2
+
3
+ 🛩️ Features:
4
+
5
+ * BDD Improvement. Added `DataTableArgument` class to work with table data structures.
6
+
7
+ ```js
8
+ const { DataTableArgument } = require('codeceptjs');
9
+ //...
10
+ Given('I have an employee card', (table) => {
11
+ const dataTableArgument = new DataTableArgument(table);
12
+ const hashes = dataTableArgument.hashes();
13
+ // hashes = [{ name: 'Harry', surname: 'Potter', position: 'Seeker' }];
14
+ const rows = dataTableArgument.rows();
15
+ // rows = [['Harry', 'Potter', Seeker]];
16
+ }
17
+ ```
18
+ See updated [BDD section](https://codecept.io/bdd/) for more API options.
19
+
20
+ * Support `cjs` file extensions for config file: `codecept.conf.cjs`. See #3052 by @kalvenschraut
21
+ * API updates: Added `test.file` and `suite.file` properties to `test` and `suite` objects to use in helpers and plugins.
22
+
23
+ 🐛 Bugfixes:
24
+
25
+ * [Playwright] Fixed resetting `test.artifacts` for failing tests. See #3033 by @jancorvus. Fixes #3032
26
+ * [Playwright] Apply `basicAuth` credentials to all opened browser contexts. See #3036 by @nikocanvacom. Fixes #3035
27
+ * [WebDriver] Updated `webdriverio` default version to `^6.12.1`. See #3043 by @sridhareaswaran
28
+ * [Playwright] `I.haveRequestHeaders` affects all tabs. See #3049 by @jancorvus
29
+ * BDD: Fixed unhandled empty feature files. Fix #3046 by @abhimanyupandian
30
+ * Fixed `RangeError: Invalid string length` in `recorder.js` when running huge amount of tests.
31
+
32
+ 📖 Documentation:
33
+
34
+ * Added Testrail reporter [Reports Docs](https://codecept.io/reports/#testrail)
35
+
36
+
1
37
  ## 3.1.2
2
38
 
3
39
  🛩️ Features:
package/docs/bdd.md CHANGED
@@ -264,8 +264,10 @@ You can also use the `parse()` method to obtain an object that allow you to get
264
264
  - `raw()` - returns the table as a 2-D array
265
265
  - `rows()` - returns the table as a 2-D array, without the first row
266
266
  - `hashes()` - returns an array of objects where each row is converted to an object (column header is the key)
267
+ - `rowsHash()` - returns an object where each row corresponds to an entry(first column is the key, second column is the value)
268
+ - `transpose()` - transpose the data, returns nothing. To work with the transposed table use the methods above.
267
269
 
268
- If we use hashes() with the previous exemple :
270
+ If we use hashes() with the previous example :
269
271
 
270
272
  ```js
271
273
  Given('I have products in my cart', (table) => { // eslint-disable-line
@@ -281,7 +283,59 @@ Given('I have products in my cart', (table) => { // eslint-disable-line
281
283
  }
282
284
  });
283
285
  ```
286
+ Examples of tables using:
284
287
 
288
+ ```gherkin
289
+ Given I have a short employees card
290
+ | Harry | Potter |
291
+ | Chuck | Norris |
292
+ ```
293
+ ```js
294
+ const { DataTableArgument } = require('codeceptjs');
295
+ //...
296
+ Given('I have a short employees card', (table) => {
297
+ const dataTableArgument = new DataTableArgument(table);
298
+ const raw = dataTableArgument.raw();
299
+ // row = [['Harry', 'Potter'], ['Chuck', 'Norris']]
300
+ dataTableArgument.transpose();
301
+ const transposedRaw = dataTableArgument.raw();
302
+ // transposedRaw = [['Harry', 'Chuck'], ['Potter', 'Norris']];
303
+ }
304
+ );
305
+ ```
306
+ ```gherkin
307
+ Given I have an employee card
308
+ | name | surname | position |
309
+ | Harry | Potter | Seeker |
310
+ ```
311
+ ```js
312
+ const { DataTableArgument } = require('codeceptjs');
313
+ //...
314
+ Given('I have an employee card', (table) => {
315
+ const dataTableArgument = new DataTableArgument(table);
316
+ const hashes = dataTableArgument.hashes();
317
+ // hashes = [{ name: 'Harry', surname: 'Potter', position: 'Seeker' }];
318
+ const rows = dataTableArgument.rows();
319
+ // rows = [['Harry', 'Potter', Seeker]];
320
+ }
321
+ );
322
+ ```
323
+ ```gherkin
324
+ Given I have a formatted employee card
325
+ | name | Harry |
326
+ | surname | Potter |
327
+ | position | Seeker |
328
+ ```
329
+ ```js
330
+ const { DataTableArgument } = require('codeceptjs');
331
+ //...
332
+ Given('I have a formatted employee card', (table) => {
333
+ const dataTableArgument = new DataTableArgument(table);
334
+ const rawHash = dataTableArgument.rowsHash();
335
+ // rawHash = { name: 'Harry', surname: 'Potter', position: 'Seeker' };
336
+ }
337
+ );
338
+ ```
285
339
  ### Examples
286
340
 
287
341
  In case scenarios represent the same logic but differ on data, we can use *Scenario Outline* to provide different examples for the same behavior. Scenario outline is just like a basic scenario with some values replaced with placeholders, which are filled from a table. Each set of values is executed as a different test.
@@ -481,10 +481,11 @@ class Appium extends Webdriver {
481
481
  * ```js
482
482
  * I.removeApp('appName', 'com.example.android.apis');
483
483
  * ```
484
- * @param {string} appId
485
- * @param {string} bundleId String ID of bundle
486
484
  *
487
485
  * Appium: support only Android
486
+ *
487
+ * @param {string} appId
488
+ * @param {string} [bundleId] ID of bundle
488
489
  */
489
490
  async removeApp(appId, bundleId) {
490
491
  onlyForApps.call(this, 'Android');
@@ -820,9 +821,10 @@ class Appium extends Webdriver {
820
821
  * I.hideDeviceKeyboard('pressKey', 'Done');
821
822
  * ```
822
823
  *
823
- * @param {'tapOutside' | 'pressKey'} strategy desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)
824
- *
825
824
  * Appium: support Android and iOS
825
+ *
826
+ * @param {'tapOutside' | 'pressKey'} [strategy] Desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)
827
+ * @param {string} [key] Optional key
826
828
  */
827
829
  async hideDeviceKeyboard(strategy, key) {
828
830
  onlyForApps.call(this);
@@ -1162,6 +1164,8 @@ class Appium extends Webdriver {
1162
1164
  * ```
1163
1165
  *
1164
1166
  * Appium: support Android and iOS
1167
+ *
1168
+ * @param {Array} actions Array of touch actions
1165
1169
  */
1166
1170
  async touchPerform(actions) {
1167
1171
  onlyForApps.call(this);
@@ -354,6 +354,7 @@ class Playwright extends Helper {
354
354
  if (this.options.restart && !this.options.manualStart) await this._startBrowser();
355
355
  if (!this.isRunning && !this.options.manualStart) await this._startBrowser();
356
356
 
357
+ this.isAuthenticated = false;
357
358
  if (this.isElectron) {
358
359
  this.browserContext = this.browser.context();
359
360
  } else if (this.userDataDir) {
@@ -364,6 +365,10 @@ class Playwright extends Helper {
364
365
  acceptDownloads: true,
365
366
  ...this.options.emulate,
366
367
  };
368
+ if (this.options.basicAuth) {
369
+ contextOptions.httpCredentials = this.options.basicAuth;
370
+ this.isAuthenticated = true;
371
+ }
367
372
  if (this.options.recordVideo) contextOptions.recordVideo = this.options.recordVideo;
368
373
  if (this.storageState) contextOptions.storageState = this.storageState;
369
374
  this.browserContext = await this.browser.newContext(contextOptions); // Adding the HTTPSError ignore in the context so that we can ignore those errors
@@ -566,7 +571,7 @@ class Playwright extends Helper {
566
571
  page.setDefaultNavigationTimeout(this.options.getPageTimeout);
567
572
  this.context = await this.page;
568
573
  this.contextLocator = null;
569
- if (this.config.browser === 'chrome') {
574
+ if (this.options.browser === 'chrome') {
570
575
  await page.bringToFront();
571
576
  }
572
577
  }
@@ -730,9 +735,9 @@ class Playwright extends Helper {
730
735
  url = this.options.url + url;
731
736
  }
732
737
 
733
- if (this.config.basicAuth && (this.isAuthenticated !== true)) {
738
+ if (this.options.basicAuth && (this.isAuthenticated !== true)) {
734
739
  if (url.includes(this.options.url)) {
735
- await this.browserContext.setHTTPCredentials(this.config.basicAuth);
740
+ await this.browserContext.setHTTPCredentials(this.options.basicAuth);
736
741
  this.isAuthenticated = true;
737
742
  }
738
743
  }
@@ -795,7 +800,7 @@ class Playwright extends Helper {
795
800
  if (!customHeaders) {
796
801
  throw new Error('Cannot send empty headers.');
797
802
  }
798
- return this.page.setExtraHTTPHeaders(customHeaders);
803
+ return this.browserContext.setExtraHTTPHeaders(customHeaders);
799
804
  }
800
805
 
801
806
  /**
@@ -2565,6 +2570,11 @@ class Playwright extends Helper {
2565
2570
 
2566
2571
  async _failed(test) {
2567
2572
  await this._withinEnd();
2573
+
2574
+ if (!test.artifacts) {
2575
+ test.artifacts = {};
2576
+ }
2577
+
2568
2578
  if (this.options.recordVideo && this.page.video()) {
2569
2579
  test.artifacts.video = await this.page.video().path();
2570
2580
  }
@@ -481,7 +481,7 @@ class WebDriver extends Helper {
481
481
  try {
482
482
  require('webdriverio');
483
483
  } catch (e) {
484
- return ['webdriverio@^5.2.2'];
484
+ return ['webdriverio@^6.12.1'];
485
485
  }
486
486
  }
487
487
 
package/docs/changelog.md CHANGED
@@ -7,6 +7,42 @@ layout: Section
7
7
 
8
8
  # Releases
9
9
 
10
+ ## 3.1.3
11
+
12
+ 🛩️ Features:
13
+
14
+ * BDD Improvement. Added `DataTableArgument` class to work with table data structures.
15
+
16
+ ```js
17
+ const { DataTableArgument } = require('codeceptjs');
18
+ //...
19
+ Given('I have an employee card', (table) => {
20
+ const dataTableArgument = new DataTableArgument(table);
21
+ const hashes = dataTableArgument.hashes();
22
+ // hashes = [{ name: 'Harry', surname: 'Potter', position: 'Seeker' }];
23
+ const rows = dataTableArgument.rows();
24
+ // rows = [['Harry', 'Potter', Seeker]];
25
+ }
26
+ ```
27
+ See updated [BDD section](https://codecept.io/bdd/) for more API options.
28
+
29
+ * Support `cjs` file extensions for config file: `codecept.conf.cjs`. See [#3052](https://github.com/codeceptjs/CodeceptJS/issues/3052) by **[kalvenschraut](https://github.com/kalvenschraut)**
30
+ * API updates: Added `test.file` and `suite.file` properties to `test` and `suite` objects to use in helpers and plugins.
31
+
32
+ 🐛 Bugfixes:
33
+
34
+ * **[Playwright]** Fixed resetting `test.artifacts` for failing tests. See [#3033](https://github.com/codeceptjs/CodeceptJS/issues/3033) by **[jancorvus](https://github.com/jancorvus)**. Fixes [#3032](https://github.com/codeceptjs/CodeceptJS/issues/3032)
35
+ * **[Playwright]** Apply `basicAuth` credentials to all opened browser contexts. See [#3036](https://github.com/codeceptjs/CodeceptJS/issues/3036) by **[nikocanvacom](https://github.com/nikocanvacom)**. Fixes [#3035](https://github.com/codeceptjs/CodeceptJS/issues/3035)
36
+ * **[WebDriver]** Updated `webdriverio` default version to `^6.12.1`. See [#3043](https://github.com/codeceptjs/CodeceptJS/issues/3043) by **[sridhareaswaran](https://github.com/sridhareaswaran)**
37
+ * **[Playwright]** `I.haveRequestHeaders` affects all tabs. See [#3049](https://github.com/codeceptjs/CodeceptJS/issues/3049) by **[jancorvus](https://github.com/jancorvus)**
38
+ * BDD: Fixed unhandled empty feature files. Fix [#3046](https://github.com/codeceptjs/CodeceptJS/issues/3046) by **[abhimanyupandian](https://github.com/abhimanyupandian)**
39
+ * Fixed `RangeError: Invalid string length` in `recorder.js` when running huge amount of tests.
40
+
41
+ 📖 Documentation:
42
+
43
+ * Added Testrail reporter [Reports Docs](https://codecept.io/reports/#testrail)
44
+
45
+
10
46
  ## 3.1.2
11
47
 
12
48
  🛩️ Features:
package/docs/commands.md CHANGED
@@ -47,18 +47,12 @@ Run single test with steps printed
47
47
  npx codeceptjs run github_test.js --steps
48
48
  ```
49
49
 
50
- Run single test in debug mode
50
+ Run single test in debug mode (see more in [debugging](#Debugging) section)
51
51
 
52
52
  ```sh
53
53
  npx codeceptjs run github_test.js --debug
54
54
  ```
55
55
 
56
- Run test with internal logs printed (global promises, and events).
57
-
58
- ```sh
59
- npx codeceptjs run github_test.js --verbose
60
- ```
61
-
62
56
  Select config file manually (`-c` or `--config` option)
63
57
 
64
58
  ```sh
@@ -80,6 +74,26 @@ npx codeceptjs run --reporter xunit
80
74
 
81
75
  Use any of [Mocha reporters](https://github.com/mochajs/mocha/tree/master/lib/reporters) used.
82
76
 
77
+ #### Debugging
78
+
79
+ Run single test in debug mode
80
+
81
+ ```sh
82
+ npx codeceptjs run --debug
83
+ ```
84
+
85
+ Run test with internal logs printed.
86
+
87
+ ```sh
88
+ npx codeceptjs run --verbose
89
+ ```
90
+
91
+ Display complete debug output including scheduled promises
92
+
93
+ ```
94
+ DEBUG=codeceptjs:* npx codeceptjs run
95
+ ```
96
+
83
97
  ## Run Workers
84
98
 
85
99
  Run tests in parallel threads.
@@ -245,10 +245,12 @@ Remove an app from the device.
245
245
  I.removeApp('appName', 'com.example.android.apis');
246
246
  ```
247
247
 
248
+ Appium: support only Android
249
+
248
250
  #### Parameters
249
251
 
250
252
  - `appId` **[string][4]**
251
- - `bundleId` **[string][4]** String ID of bundleAppium: support only Android
253
+ - `bundleId` **[string][4]?** ID of bundle
252
254
 
253
255
  ### seeCurrentActivityIs
254
256
 
@@ -473,10 +475,12 @@ I.hideDeviceKeyboard('tapOutside');
473
475
  I.hideDeviceKeyboard('pressKey', 'Done');
474
476
  ```
475
477
 
478
+ Appium: support Android and iOS
479
+
476
480
  #### Parameters
477
481
 
478
- - `strategy` **(`"tapOutside"` \| `"pressKey"`)** desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)Appium: support Android and iOS
479
- - `key`
482
+ - `strategy` **(`"tapOutside"` \| `"pressKey"`)?** Desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)
483
+ - `key` **[string][4]?** Optional key
480
484
 
481
485
  ### sendDeviceKeyEvent
482
486
 
@@ -685,7 +689,7 @@ Appium: support Android and iOS
685
689
 
686
690
  #### Parameters
687
691
 
688
- - `actions`
692
+ - `actions` **[Array][11]** Array of touch actions
689
693
 
690
694
  ### pullFile
691
695
 
@@ -722,7 +726,7 @@ Perform a rotation gesture centered on the specified element.
722
726
  I.rotate(120, 120)
723
727
  ```
724
728
 
725
- See corresponding [webdriverio reference][11].
729
+ See corresponding [webdriverio reference][12].
726
730
 
727
731
  Appium: support only iOS
728
732
 
@@ -739,7 +743,7 @@ Appium: support only iOS
739
743
 
740
744
  Set immediate value in app.
741
745
 
742
- See corresponding [webdriverio reference][12].
746
+ See corresponding [webdriverio reference][13].
743
747
 
744
748
  Appium: support only iOS
745
749
 
@@ -926,7 +930,7 @@ let pins = await I.grabTextFromAll('#pin li');
926
930
 
927
931
  - `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator.
928
932
 
929
- Returns **[Promise][13]<[Array][14]<[string][4]>>** attribute value
933
+ Returns **[Promise][14]<[Array][11]<[string][4]>>** attribute value
930
934
 
931
935
  ### grabTextFrom
932
936
 
@@ -943,7 +947,7 @@ If multiple elements found returns first element.
943
947
 
944
948
  - `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator.
945
949
 
946
- Returns **[Promise][13]<[string][4]>** attribute value
950
+ Returns **[Promise][14]<[string][4]>** attribute value
947
951
 
948
952
  ### grabNumberOfVisibleElements
949
953
 
@@ -958,7 +962,7 @@ let numOfElements = await I.grabNumberOfVisibleElements('p');
958
962
 
959
963
  - `locator` **([string][4] \| [object][6])** located by CSS|XPath|strict locator.
960
964
 
961
- Returns **[Promise][13]<[number][8]>** number of visible elements
965
+ Returns **[Promise][14]<[number][8]>** number of visible elements
962
966
 
963
967
  ### grabAttributeFrom
964
968
 
@@ -977,7 +981,7 @@ let hint = await I.grabAttributeFrom('#tooltip', 'title');
977
981
  - `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator.
978
982
  - `attr` **[string][4]** attribute name.
979
983
 
980
- Returns **[Promise][13]<[string][4]>** attribute value
984
+ Returns **[Promise][14]<[string][4]>** attribute value
981
985
 
982
986
  ### grabAttributeFromAll
983
987
 
@@ -994,7 +998,7 @@ let hints = await I.grabAttributeFromAll('.tooltip', 'title');
994
998
  - `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator.
995
999
  - `attr` **[string][4]** attribute name.
996
1000
 
997
- Returns **[Promise][13]<[Array][14]<[string][4]>>** attribute value
1001
+ Returns **[Promise][14]<[Array][11]<[string][4]>>** attribute value
998
1002
 
999
1003
  ### grabValueFromAll
1000
1004
 
@@ -1009,7 +1013,7 @@ let inputs = await I.grabValueFromAll('//form/input');
1009
1013
 
1010
1014
  - `locator` **([string][4] \| [object][6])** field located by label|name|CSS|XPath|strict locator.
1011
1015
 
1012
- Returns **[Promise][13]<[Array][14]<[string][4]>>** attribute value
1016
+ Returns **[Promise][14]<[Array][11]<[string][4]>>** attribute value
1013
1017
 
1014
1018
  ### grabValueFrom
1015
1019
 
@@ -1025,7 +1029,7 @@ let email = await I.grabValueFrom('input[name=email]');
1025
1029
 
1026
1030
  - `locator` **([string][4] \| [object][6])** field located by label|name|CSS|XPath|strict locator.
1027
1031
 
1028
- Returns **[Promise][13]<[string][4]>** attribute value
1032
+ Returns **[Promise][14]<[string][4]>** attribute value
1029
1033
 
1030
1034
  ### saveScreenshot
1031
1035
 
@@ -1139,7 +1143,7 @@ I.selectOption('Which OS do you use?', ['Android', 'iOS']);
1139
1143
  #### Parameters
1140
1144
 
1141
1145
  - `select` **([string][4] \| [object][6])** field located by label|name|CSS|XPath|strict locator.
1142
- - `option` **([string][4] \| [Array][14]<any>)** visible text or value of option.Supported only for web testing
1146
+ - `option` **([string][4] \| [Array][11]<any>)** visible text or value of option.Supported only for web testing
1143
1147
 
1144
1148
  ### waitForElement
1145
1149
 
@@ -1482,7 +1486,7 @@ let postHTMLs = await I.grabHTMLFromAll('.post');
1482
1486
  - `locator`
1483
1487
  - `element` **([string][4] \| [object][6])** located by CSS|XPath|strict locator.
1484
1488
 
1485
- Returns **[Promise][13]<[Array][14]<[string][4]>>** HTML code for an element
1489
+ Returns **[Promise][14]<[Array][11]<[string][4]>>** HTML code for an element
1486
1490
 
1487
1491
  ### grabHTMLFrom
1488
1492
 
@@ -1499,7 +1503,7 @@ let postHTML = await I.grabHTMLFrom('#post');
1499
1503
  - `locator`
1500
1504
  - `element` **([string][4] \| [object][6])** located by CSS|XPath|strict locator.
1501
1505
 
1502
- Returns **[Promise][13]<[string][4]>** HTML code for an element
1506
+ Returns **[Promise][14]<[string][4]>** HTML code for an element
1503
1507
 
1504
1508
  ### seeTextEquals
1505
1509
 
@@ -1560,7 +1564,7 @@ Resumes test execution, so **should be used inside async function with `await`**
1560
1564
  let pageSource = await I.grabSource();
1561
1565
  ```
1562
1566
 
1563
- Returns **[Promise][13]<[string][4]>** source code
1567
+ Returns **[Promise][14]<[string][4]>** source code
1564
1568
 
1565
1569
  ### grabBrowserLogs
1566
1570
 
@@ -1572,7 +1576,7 @@ let logs = await I.grabBrowserLogs();
1572
1576
  console.log(JSON.stringify(logs))
1573
1577
  ```
1574
1578
 
1575
- Returns **([Promise][13]<[Array][14]<[object][6]>> | [undefined][19])** all browser logs
1579
+ Returns **([Promise][14]<[Array][11]<[object][6]>> | [undefined][19])** all browser logs
1576
1580
 
1577
1581
  ### dontSeeInSource
1578
1582
 
@@ -1697,7 +1701,7 @@ I.type(['T', 'E', 'X', 'T']);
1697
1701
 
1698
1702
  - `keys`
1699
1703
  - `delay` **[number][8]?** (optional) delay in ms between key presses (optional, default `null`)
1700
- - `key` **([string][4] \| [Array][14]<[string][4]>)** or array of keys to type.
1704
+ - `key` **([string][4] \| [Array][11]<[string][4]>)** or array of keys to type.
1701
1705
 
1702
1706
  ### dragAndDrop
1703
1707
 
@@ -1736,7 +1740,7 @@ Useful for referencing a specific handle when calling `I.switchToWindow(handle)`
1736
1740
  const windows = await I.grabAllWindowHandles();
1737
1741
  ```
1738
1742
 
1739
- Returns **[Promise][13]<[Array][14]<[string][4]>>**
1743
+ Returns **[Promise][14]<[Array][11]<[string][4]>>**
1740
1744
 
1741
1745
  ### grabCurrentWindowHandle
1742
1746
 
@@ -1747,7 +1751,7 @@ Useful for referencing it when calling `I.switchToWindow(handle)`
1747
1751
  const window = await I.grabCurrentWindowHandle();
1748
1752
  ```
1749
1753
 
1750
- Returns **[Promise][13]<[string][4]>**
1754
+ Returns **[Promise][14]<[string][4]>**
1751
1755
 
1752
1756
  ### switchToWindow
1753
1757
 
@@ -1797,7 +1801,7 @@ Resumes test execution, so **should be used inside async function with `await`**
1797
1801
  let tabs = await I.grabNumberOfOpenTabs();
1798
1802
  ```
1799
1803
 
1800
- Returns **[Promise][13]<[number][8]>** number of open tabs
1804
+ Returns **[Promise][14]<[number][8]>** number of open tabs
1801
1805
 
1802
1806
  ### scrollPageToTop
1803
1807
 
@@ -1824,7 +1828,7 @@ Resumes test execution, so **should be used inside an async function with `await
1824
1828
  let { x, y } = await I.grabPageScrollPosition();
1825
1829
  ```
1826
1830
 
1827
- Returns **[Promise][13]<PageScrollPosition>** scroll position
1831
+ Returns **[Promise][14]<PageScrollPosition>** scroll position
1828
1832
 
1829
1833
  ### setGeoLocation
1830
1834
 
@@ -1850,7 +1854,7 @@ Resumes test execution, so **should be used inside async function with `await`**
1850
1854
  let geoLocation = await I.grabGeoLocation();
1851
1855
  ```
1852
1856
 
1853
- Returns **[Promise][13]<{latitude: [number][8], longitude: [number][8], altitude: [number][8]}>**
1857
+ Returns **[Promise][14]<{latitude: [number][8], longitude: [number][8], altitude: [number][8]}>**
1854
1858
 
1855
1859
  ### grabElementBoundingRect
1856
1860
 
@@ -1878,7 +1882,7 @@ const width = await I.grabElementBoundingRect('h3', 'width');
1878
1882
  - `prop`
1879
1883
  - `elementSize` **[string][4]?** x, y, width or height of the given element.
1880
1884
 
1881
- Returns **([Promise][13]<DOMRect> | [Promise][13]<[number][8]>)** Element bounding rectangle
1885
+ Returns **([Promise][14]<DOMRect> | [Promise][14]<[number][8]>)** Element bounding rectangle
1882
1886
 
1883
1887
  [1]: http://codecept.io/helpers/WebDriver/
1884
1888
 
@@ -1900,13 +1904,13 @@ Returns **([Promise][13]<DOMRect> | [Promise][13]<[number][8]>)** Element
1900
1904
 
1901
1905
  [10]: http://webdriver.io/api/mobile/swipe.html
1902
1906
 
1903
- [11]: http://webdriver.io/api/mobile/rotate.html
1907
+ [11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
1904
1908
 
1905
- [12]: http://webdriver.io/api/mobile/setImmediateValue.html
1909
+ [12]: http://webdriver.io/api/mobile/rotate.html
1906
1910
 
1907
- [13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
1911
+ [13]: http://webdriver.io/api/mobile/setImmediateValue.html
1908
1912
 
1909
- [14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
1913
+ [14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
1910
1914
 
1911
1915
  [15]: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
1912
1916
 
@@ -406,6 +406,11 @@ When a test fails and video was enabled a video file is shown under the `artifac
406
406
 
407
407
  Open video and use it to debug a failed test case. Video helps when running tests on CI. Configure your CI system to enable artifacts storage for `output/video` and review videos of failed test case to understand failures.
408
408
 
409
+ It is recommended to enable [subtitles](https://codecept.io/plugins/#subtitles) plugin which will generate subtitles from steps in `.srt` format. Subtitles file will be saved into after a video file so video player (like VLC) would load them automatically:
410
+
411
+ ![](https://user-images.githubusercontent.com/220264/131644090-38d1ca55-1ba1-41fa-8fd1-7dea2b7ae995.png)
412
+
413
+
409
414
  ## Trace <Badge text="Since 3.1" type="warning"/>
410
415
 
411
416
  If video is not enough to descover why a test failed a [trace](https://playwright.dev/docs/trace-viewer/) can be recorded.
@@ -456,7 +461,7 @@ For instance, this is how you can read a trace for a failed test from an example
456
461
  npx playwright show-trace /home/davert/projects/codeceptjs/examples/output/trace/open.zip
457
462
  ```
458
463
 
459
- ## Capturing code coverage
464
+ ## Capturing Code Coverage
460
465
 
461
466
  Code coverage can be captured, by enabling the `coverage` plugin in `codecept.config.js`.
462
467
 
@@ -474,45 +479,23 @@ Once all the tests are completed, `codecept` will create and store coverage in `
474
479
 
475
480
  ![](https://user-images.githubusercontent.com/16587779/131362352-30ee9c51-705f-4098-b665-53035ea9275f.png)
476
481
 
477
- ### Converting `playwright` coverage to `istanbul` coverage
478
-
479
- To convert coverage generated from `playwright` to `istanbul` coverage, you first need to install
480
- - [`v8-to-istanbul`](https://www.npmjs.com/package/v8-to-istanbul)
481
-
482
- Once installed, convert the coverage to a format which `istanbul` can recognize, by writing a script as shown below.
483
-
484
- ```js
485
- const v8toIstanbul = require('v8-to-istanbul');
486
- // read all the coverage file from output/coverage folder
487
- const coverage = require('./output/coverage/Visit_Home_1630335005.coverage.json');
488
- const fs = require('fs/promises');
489
-
490
- (async () => {
491
- for (const entry of coverage) {
492
- // Used to get file name
493
- const file = entry.url.match(/(?:http(s)*:\/\/.*\/)(?<file>.*)/);
494
- const converter = new v8toIstanbul(file.groups.file, 0, {
495
- source: entry.source
496
- });
497
-
498
- await converter.load();
499
- converter.applyCoverage(entry.functions);
500
-
501
- // Store converted coverage file which can later be used to generate report
502
- await fs.writeFile('./coverage/final.json', JSON.stringify(converter.toIstanbul(), null, 2));
503
- }
504
- })();
505
- ```
482
+ Then you need to [convert code coverage from Playwright's format into Istanbul format](https://github.com/codeceptjs/CodeceptJS/wiki/Converting-Playwright-to-Istanbul-Coverage).
506
483
 
507
484
  Once the istanbul compatible coverage is generated, use [`nyc`](https://www.npmjs.com/package/nyc) to generate your coverage report in your desired format.
508
485
 
509
486
  ```
510
- npx nyc report --reporter text -t coverage
487
+ npx nyc report --reporter html -t coverage
511
488
  ```
512
489
 
513
- The above command will generate a text report like shown below.
490
+ The above command will generate will generate coverage in an interactive html format. It should generate `html` files in the directory where your code coverage is present, something like shown below.
491
+
492
+ ![](https://user-images.githubusercontent.com/16587779/131858419-cbc7df7d-0851-47b9-b086-b5e3b9165674.png)
493
+
494
+ Open `index.html` in your browser to view the full interactive coverage report.
495
+
496
+ ![](https://user-images.githubusercontent.com/16587779/131858993-87d1aafc-8ef1-4a82-867d-e64a13e36106.png)
514
497
 
515
- ![](https://user-images.githubusercontent.com/16587779/131363170-b03b4398-5e9a-4142-bc32-764a5f4a5e11.png)
498
+ ![](https://user-images.githubusercontent.com/16587779/131859006-c6f17d18-c603-44a5-9d59-0670177276cf.png)
516
499
  ## Extending Helper
517
500
 
518
501
  To create custom `I.*` commands using Playwright API you need to create a custom helper.
package/docs/plugins.md CHANGED
@@ -417,7 +417,7 @@ Dumps code coverage from Playwright/Puppeteer after every test.
417
417
 
418
418
  ```js
419
419
  plugins: {
420
- playwrightCoverage: {
420
+ coverage: {
421
421
  enabled: true
422
422
  }
423
423
  }
package/docs/reports.md CHANGED
@@ -7,8 +7,8 @@ title: Reporters
7
7
 
8
8
  ## Cli
9
9
 
10
- By default CodeceptJS provides cli reporter with console output.
11
- Test names and failures will be printed to screen.
10
+ By default, CodeceptJS provides cli reporter with console output.
11
+ Test names and failures will be printed out on screen.
12
12
 
13
13
  ```sh
14
14
  GitHub --
@@ -201,7 +201,7 @@ npx codeceptjs dry-run --debug -p allure
201
201
 
202
202
  ## ReportPortal
203
203
 
204
- Allure is a great reportin tool, however, if you are running tests on different machines it is hard to merge its XML result files to build a proper report. So, for enterprise grade reporting we recommend using [ReportPortal](https://reportportal.io).
204
+ Allure is a great reporting tool, however, if you are running tests on different machines it is hard to merge its XML result files to build a proper report. So, for enterprise grade reporting we recommend using [ReportPortal](https://reportportal.io).
205
205
 
206
206
  ![](https://camo.githubusercontent.com/6550c0365f1d0ce1e29c53f1860b12957d1fc529/68747470733a2f2f692e6962622e636f2f516d353247306e2f53637265656e73686f742d323031392d30342d31312d61742d31352d35372d34302e706e67)
207
207
 
@@ -376,3 +376,21 @@ npx codeceptjs run --reporter mocha-multi
376
376
  ```
377
377
 
378
378
  This will give you cli with steps in console and HTML report in `output` directory.
379
+
380
+
381
+ ## Testrail
382
+
383
+ Testrail integration with CodeceptJS is now so seamless. The test run is created automatically afterwards. The screenshots of failed tests are also attached to test results.
384
+
385
+ Try to use [codeceptjs-testrail](https://www.npmjs.com/package/codeceptjs-testrail) plugin
386
+
387
+ Install it via NPM:
388
+
389
+ ```sh
390
+ npm i codeceptjs-testrail --save
391
+ ```
392
+
393
+ ![Attachemnt for failed case](http://g.recordit.co/ajaa2QRlnW.gif)
394
+
395
+ Now there is new feature, add the configuration to test run of test plan
396
+ ![Attachemnt for failed case](http://g.recordit.co/uQLvQUq7cT.gif)
package/lib/actor.js CHANGED
@@ -28,7 +28,7 @@ class Actor {
28
28
 
29
29
  /**
30
30
  * @function
31
- * @param {*} opts
31
+ * @param {*} [opts]
32
32
  * @return {this}
33
33
  * @inner
34
34
  */
package/lib/config.js CHANGED
@@ -128,7 +128,7 @@ function loadConfigFile(configFile) {
128
128
  const extensionName = path.extname(configFile);
129
129
 
130
130
  // .conf.js config file
131
- if (extensionName === '.js' || extensionName === '.ts') {
131
+ if (extensionName === '.js' || extensionName === '.ts' || extensionName === '.cjs') {
132
132
  return Config.create(require(configFile).config);
133
133
  }
134
134
 
@@ -1,4 +1,9 @@
1
+ /**
2
+ * DataTableArgument class to store the Cucumber data table from
3
+ * a step as an object with methods that can be used to access the data.
4
+ */
1
5
  class DataTableArgument {
6
+ /** @param {*} gherkinDataTable */
2
7
  constructor(gherkinDataTable) {
3
8
  this.rawData = gherkinDataTable.rows.map((row) => {
4
9
  return row.cells.map((cell) => {
@@ -7,16 +12,25 @@ class DataTableArgument {
7
12
  });
8
13
  }
9
14
 
15
+ /** Returns the table as a 2-D array
16
+ * @returns {string[][]}
17
+ */
10
18
  raw() {
11
19
  return this.rawData.slice(0);
12
20
  }
13
21
 
22
+ /** Returns the table as a 2-D array, without the first row
23
+ * @returns {string[][]}
24
+ */
14
25
  rows() {
15
26
  const copy = this.raw();
16
27
  copy.shift();
17
28
  return copy;
18
29
  }
19
30
 
31
+ /** Returns an array of objects where each row is converted to an object (column header is the key)
32
+ * @returns {any[]}
33
+ */
20
34
  hashes() {
21
35
  const copy = this.raw();
22
36
  const header = copy.shift();
@@ -26,6 +40,27 @@ class DataTableArgument {
26
40
  return r;
27
41
  });
28
42
  }
43
+
44
+ /** Returns an object where each row corresponds to an entry
45
+ * (first column is the key, second column is the value)
46
+ * @returns {Record<string, string>}
47
+ */
48
+ rowsHash() {
49
+ const rows = this.raw();
50
+ const everyRowHasTwoColumns = rows.every((row) => row.length === 2);
51
+ if (!everyRowHasTwoColumns) {
52
+ throw new Error('rowsHash can only be called on a data table where all rows have exactly two columns');
53
+ }
54
+ /** @type {Record<string, string>} */
55
+ const result = {};
56
+ rows.forEach((x) => (result[x[0]] = x[1]));
57
+ return result;
58
+ }
59
+
60
+ /** Transposed the data */
61
+ transpose() {
62
+ this.rawData = this.rawData[0].map((x, i) => this.rawData.map((y) => y[i]));
63
+ }
29
64
  }
30
65
 
31
66
  module.exports = DataTableArgument;
@@ -481,10 +481,11 @@ class Appium extends Webdriver {
481
481
  * ```js
482
482
  * I.removeApp('appName', 'com.example.android.apis');
483
483
  * ```
484
- * @param {string} appId
485
- * @param {string} bundleId String ID of bundle
486
484
  *
487
485
  * Appium: support only Android
486
+ *
487
+ * @param {string} appId
488
+ * @param {string} [bundleId] ID of bundle
488
489
  */
489
490
  async removeApp(appId, bundleId) {
490
491
  onlyForApps.call(this, 'Android');
@@ -820,9 +821,10 @@ class Appium extends Webdriver {
820
821
  * I.hideDeviceKeyboard('pressKey', 'Done');
821
822
  * ```
822
823
  *
823
- * @param {'tapOutside' | 'pressKey'} strategy desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)
824
- *
825
824
  * Appium: support Android and iOS
825
+ *
826
+ * @param {'tapOutside' | 'pressKey'} [strategy] Desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)
827
+ * @param {string} [key] Optional key
826
828
  */
827
829
  async hideDeviceKeyboard(strategy, key) {
828
830
  onlyForApps.call(this);
@@ -1162,6 +1164,8 @@ class Appium extends Webdriver {
1162
1164
  * ```
1163
1165
  *
1164
1166
  * Appium: support Android and iOS
1167
+ *
1168
+ * @param {Array} actions Array of touch actions
1165
1169
  */
1166
1170
  async touchPerform(actions) {
1167
1171
  onlyForApps.call(this);
@@ -354,6 +354,7 @@ class Playwright extends Helper {
354
354
  if (this.options.restart && !this.options.manualStart) await this._startBrowser();
355
355
  if (!this.isRunning && !this.options.manualStart) await this._startBrowser();
356
356
 
357
+ this.isAuthenticated = false;
357
358
  if (this.isElectron) {
358
359
  this.browserContext = this.browser.context();
359
360
  } else if (this.userDataDir) {
@@ -364,6 +365,10 @@ class Playwright extends Helper {
364
365
  acceptDownloads: true,
365
366
  ...this.options.emulate,
366
367
  };
368
+ if (this.options.basicAuth) {
369
+ contextOptions.httpCredentials = this.options.basicAuth;
370
+ this.isAuthenticated = true;
371
+ }
367
372
  if (this.options.recordVideo) contextOptions.recordVideo = this.options.recordVideo;
368
373
  if (this.storageState) contextOptions.storageState = this.storageState;
369
374
  this.browserContext = await this.browser.newContext(contextOptions); // Adding the HTTPSError ignore in the context so that we can ignore those errors
@@ -559,7 +564,7 @@ class Playwright extends Helper {
559
564
  page.setDefaultNavigationTimeout(this.options.getPageTimeout);
560
565
  this.context = await this.page;
561
566
  this.contextLocator = null;
562
- if (this.config.browser === 'chrome') {
567
+ if (this.options.browser === 'chrome') {
563
568
  await page.bringToFront();
564
569
  }
565
570
  }
@@ -714,9 +719,9 @@ class Playwright extends Helper {
714
719
  url = this.options.url + url;
715
720
  }
716
721
 
717
- if (this.config.basicAuth && (this.isAuthenticated !== true)) {
722
+ if (this.options.basicAuth && (this.isAuthenticated !== true)) {
718
723
  if (url.includes(this.options.url)) {
719
- await this.browserContext.setHTTPCredentials(this.config.basicAuth);
724
+ await this.browserContext.setHTTPCredentials(this.options.basicAuth);
720
725
  this.isAuthenticated = true;
721
726
  }
722
727
  }
@@ -775,7 +780,7 @@ class Playwright extends Helper {
775
780
  if (!customHeaders) {
776
781
  throw new Error('Cannot send empty headers.');
777
782
  }
778
- return this.page.setExtraHTTPHeaders(customHeaders);
783
+ return this.browserContext.setExtraHTTPHeaders(customHeaders);
779
784
  }
780
785
 
781
786
  /**
@@ -1851,6 +1856,11 @@ class Playwright extends Helper {
1851
1856
 
1852
1857
  async _failed(test) {
1853
1858
  await this._withinEnd();
1859
+
1860
+ if (!test.artifacts) {
1861
+ test.artifacts = {};
1862
+ }
1863
+
1854
1864
  if (this.options.recordVideo && this.page.video()) {
1855
1865
  test.artifacts.video = await this.page.video().path();
1856
1866
  }
@@ -481,7 +481,7 @@ class WebDriver extends Helper {
481
481
  try {
482
482
  require('webdriverio');
483
483
  } catch (e) {
484
- return ['webdriverio@^5.2.2'];
484
+ return ['webdriverio@^6.12.1'];
485
485
  }
486
486
  }
487
487
 
package/lib/index.js CHANGED
@@ -32,6 +32,8 @@ module.exports = {
32
32
  within: require('./within'),
33
33
  /** @type {typeof CodeceptJS.DataTable} */
34
34
  dataTable: require('./data/table'),
35
+ /** @type {typeof CodeceptJS.DataTableArgument} */
36
+ dataTableArgument: require('./data/dataTableArgument'),
35
37
  /** @type {typeof CodeceptJS.store} */
36
38
  store: require('./store'),
37
39
  /** @type {typeof CodeceptJS.Locator} */
@@ -11,15 +11,19 @@ const transform = require('../transform');
11
11
  const parser = new Parser();
12
12
  parser.stopAtFirstError = false;
13
13
 
14
- module.exports = (text) => {
14
+ module.exports = (text, file) => {
15
15
  const ast = parser.parse(text);
16
16
 
17
+ if (!ast.feature) {
18
+ throw new Error(`No 'Features' available in Gherkin '${file}' provided!`);
19
+ }
17
20
  const suite = new Suite(ast.feature.name, new Context());
18
21
  const tags = ast.feature.tags.map(t => t.name);
19
22
  suite.title = `${suite.title} ${tags.join(' ')}`.trim();
20
23
  suite.tags = tags || [];
21
24
  suite.comment = ast.feature.description;
22
25
  suite.feature = ast.feature;
26
+ suite.file = file;
23
27
  suite.timeout(0);
24
28
 
25
29
  suite.beforeEach('codeceptjs.before', () => scenario.setup(suite));
@@ -95,6 +99,7 @@ module.exports = (text) => {
95
99
  const title = `${child.name} ${JSON.stringify(current)} ${tags.join(' ')}`.trim();
96
100
  const test = new Test(title, async () => runSteps(addExampleInTable(exampleSteps, current)));
97
101
  test.tags = suite.tags.concat(tags);
102
+ test.file = file;
98
103
  suite.addTest(scenario.test(test));
99
104
  }
100
105
  }
@@ -104,6 +109,7 @@ module.exports = (text) => {
104
109
  const title = `${child.name} ${tags.join(' ')}`.trim();
105
110
  const test = new Test(title, async () => runSteps(child.steps));
106
111
  test.tags = suite.tags.concat(tags);
112
+ test.file = file;
107
113
  suite.addTest(scenario.test(test));
108
114
  }
109
115
 
@@ -1,3 +1,4 @@
1
+ const path = require('path');
1
2
  const event = require('../event');
2
3
  const container = require('../container');
3
4
  const recorder = require('../recorder');
@@ -2,7 +2,7 @@ const Mocha = require('mocha');
2
2
  const fsPath = require('path');
3
3
  const fs = require('fs');
4
4
  const reporter = require('./cli');
5
- const gherkinParser = require('./interfaces/gherkin.js');
5
+ const gherkinParser = require('./interfaces/gherkin');
6
6
  const output = require('./output');
7
7
  const { genTestId } = require('./utils');
8
8
  const ConnectionRefused = require('./helper/errors/ConnectionRefused');
@@ -35,8 +35,7 @@ class MochaFactory {
35
35
  if (mocha.suite.suites.length === 0) {
36
36
  mocha.files
37
37
  .filter(file => file.match(/\.feature$/))
38
- .map(file => fs.readFileSync(file, 'utf8'))
39
- .forEach(content => mocha.suite.addSuite(gherkinParser(content)));
38
+ .forEach(file => mocha.suite.addSuite(gherkinParser(fs.readFileSync(file, 'utf8'), file)));
40
39
 
41
40
  // remove feature files
42
41
  mocha.files = mocha.files.filter(file => !file.match(/\.feature$/));
@@ -48,7 +48,7 @@ function buildFileName(test, uniqueFileName) {
48
48
  *
49
49
  * ```js
50
50
  * plugins: {
51
- * playwrightCoverage: {
51
+ * coverage: {
52
52
  * enabled: true
53
53
  * }
54
54
  * }
package/lib/recorder.js CHANGED
@@ -3,6 +3,8 @@ const promiseRetry = require('promise-retry');
3
3
 
4
4
  const { log } = require('./output');
5
5
 
6
+ const MAX_TASKS = 100;
7
+
6
8
  let promise;
7
9
  let running = false;
8
10
  let errFn;
@@ -172,7 +174,7 @@ module.exports = {
172
174
  return;
173
175
  }
174
176
  tasks.push(taskName);
175
- debug(`${currentQueue()}Queued | ${taskName}`);
177
+ if (process.env.DEBUG) debug(`${currentQueue()}Queued | ${taskName}`);
176
178
 
177
179
  return promise = Promise.resolve(promise).then((res) => {
178
180
  const retryOpts = this.retries.slice(-1).pop();
@@ -296,7 +298,7 @@ module.exports = {
296
298
  * @inner
297
299
  */
298
300
  stop() {
299
- debug(this.toString());
301
+ if (process.env.DEBUG) debug(this.toString());
300
302
  log(`${currentQueue()}Stopping recording promises`);
301
303
  running = false;
302
304
  },
@@ -318,7 +320,7 @@ module.exports = {
318
320
  * @inner
319
321
  */
320
322
  scheduled() {
321
- return tasks.join('\n');
323
+ return tasks.slice(-MAX_TASKS).join('\n');
322
324
  },
323
325
 
324
326
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeceptjs",
3
- "version": "3.1.2",
3
+ "version": "3.1.3",
4
4
  "description": "Supercharged End 2 End Testing Framework for NodeJS",
5
5
  "keywords": [
6
6
  "acceptance",
@@ -111,6 +111,7 @@ declare const pause: typeof CodeceptJS.pause;
111
111
  declare const within: typeof CodeceptJS.within;
112
112
  declare const session: typeof CodeceptJS.session;
113
113
  declare const DataTable: typeof CodeceptJS.DataTable;
114
+ declare const DataTableArgument: typeof CodeceptJS.DataTableArgument;
114
115
  declare const codeceptjs: typeof CodeceptJS.index;
115
116
  declare const locate: typeof CodeceptJS.Locator.build;
116
117
  declare function inject(): CodeceptJS.SupportObject;
@@ -160,6 +161,7 @@ declare namespace NodeJS {
160
161
  within: typeof within;
161
162
  session: typeof session;
162
163
  DataTable: typeof DataTable;
164
+ DataTableArgument: typeof DataTableArgument;
163
165
  locate: typeof locate;
164
166
  inject: typeof inject;
165
167
  secret: typeof secret;
@@ -321,11 +321,11 @@ declare namespace CodeceptJS {
321
321
  * ```js
322
322
  * I.removeApp('appName', 'com.example.android.apis');
323
323
  * ```
324
- * @param bundleId - String ID of bundle
325
324
  *
326
325
  * Appium: support only Android
326
+ * @param [bundleId] - ID of bundle
327
327
  */
328
- removeApp(appId: string, bundleId: string): void;
328
+ removeApp(appId: string, bundleId?: string): void;
329
329
  /**
330
330
  * Check current activity on an Android device.
331
331
  *
@@ -522,11 +522,12 @@ declare namespace CodeceptJS {
522
522
  * // or by pressing key
523
523
  * I.hideDeviceKeyboard('pressKey', 'Done');
524
524
  * ```
525
- * @param strategy - desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)
526
525
  *
527
526
  * Appium: support Android and iOS
527
+ * @param [strategy] - Desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)
528
+ * @param [key] - Optional key
528
529
  */
529
- hideDeviceKeyboard(strategy: 'tapOutside' | 'pressKey'): void;
530
+ hideDeviceKeyboard(strategy?: 'tapOutside' | 'pressKey', key?: string): void;
530
531
  /**
531
532
  * Send a key event to the device.
532
533
  * List of keys: https://developer.android.com/reference/android/view/KeyEvent.html
@@ -682,8 +683,9 @@ declare namespace CodeceptJS {
682
683
  * ```
683
684
  *
684
685
  * Appium: support Android and iOS
686
+ * @param actions - Array of touch actions
685
687
  */
686
- touchPerform(): void;
688
+ touchPerform(actions: any[]): void;
687
689
  /**
688
690
  * Pulls a file from the device.
689
691
  *
@@ -9803,7 +9805,7 @@ declare namespace CodeceptJS {
9803
9805
  * add print comment method`
9804
9806
  */
9805
9807
  say(msg: string, color?: string): Promise<any> | undefined;
9806
- retry(opts: any): this;
9808
+ retry(opts?: any): this;
9807
9809
  }
9808
9810
  /**
9809
9811
  * Create CodeceptJS runner.
@@ -9947,6 +9949,34 @@ declare namespace CodeceptJS {
9947
9949
  xadd(array: any[]): void;
9948
9950
  filter(func: (...params: any[]) => any): void;
9949
9951
  }
9952
+ /**
9953
+ * DataTableArgument class to store the Cucumber data table from
9954
+ * a step as an object with methods that can be used to access the data.
9955
+ */
9956
+ class DataTableArgument {
9957
+ constructor(gherkinDataTable: any);
9958
+ /**
9959
+ * Returns the table as a 2-D array
9960
+ */
9961
+ raw(): string[][];
9962
+ /**
9963
+ * Returns the table as a 2-D array, without the first row
9964
+ */
9965
+ rows(): string[][];
9966
+ /**
9967
+ * Returns an array of objects where each row is converted to an object (column header is the key)
9968
+ */
9969
+ hashes(): any[];
9970
+ /**
9971
+ * Returns an object where each row corresponds to an entry
9972
+ * (first column is the key, second column is the value)
9973
+ */
9974
+ rowsHash(): Record<string, string>;
9975
+ /**
9976
+ * Transposed the data
9977
+ */
9978
+ transpose(): void;
9979
+ }
9950
9980
  namespace event {
9951
9981
  const dispatcher: NodeJS.EventEmitter;
9952
9982
  const test: {
@@ -10043,6 +10073,7 @@ declare namespace CodeceptJS {
10043
10073
  var pause: typeof CodeceptJS.pause;
10044
10074
  var within: typeof CodeceptJS.within;
10045
10075
  var dataTable: typeof CodeceptJS.DataTable;
10076
+ var dataTableArgument: typeof CodeceptJS.DataTableArgument;
10046
10077
  var store: typeof CodeceptJS.store;
10047
10078
  var locator: typeof CodeceptJS.Locator;
10048
10079
  }