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 +36 -0
- package/docs/bdd.md +55 -1
- package/docs/build/Appium.js +8 -4
- package/docs/build/Playwright.js +14 -4
- package/docs/build/WebDriver.js +1 -1
- package/docs/changelog.md +36 -0
- package/docs/commands.md +21 -7
- package/docs/helpers/Appium.md +33 -29
- package/docs/playwright.md +16 -33
- package/docs/plugins.md +1 -1
- package/docs/reports.md +21 -3
- package/lib/actor.js +1 -1
- package/lib/config.js +1 -1
- package/lib/data/dataTableArgument.js +35 -0
- package/lib/helper/Appium.js +8 -4
- package/lib/helper/Playwright.js +14 -4
- package/lib/helper/WebDriver.js +1 -1
- package/lib/index.js +2 -0
- package/lib/interfaces/gherkin.js +7 -1
- package/lib/listener/helpers.js +1 -0
- package/lib/mochaFactory.js +2 -3
- package/lib/plugin/coverage.js +1 -1
- package/lib/recorder.js +5 -3
- package/package.json +1 -1
- package/typings/index.d.ts +2 -0
- package/typings/types.d.ts +37 -6
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
|
|
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.
|
package/docs/build/Appium.js
CHANGED
|
@@ -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);
|
package/docs/build/Playwright.js
CHANGED
|
@@ -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.
|
|
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.
|
|
738
|
+
if (this.options.basicAuth && (this.isAuthenticated !== true)) {
|
|
734
739
|
if (url.includes(this.options.url)) {
|
|
735
|
-
await this.browserContext.setHTTPCredentials(this.
|
|
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.
|
|
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
|
}
|
package/docs/build/WebDriver.js
CHANGED
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.
|
package/docs/helpers/Appium.md
CHANGED
|
@@ -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]
|
|
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"`)
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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][
|
|
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]:
|
|
1907
|
+
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
|
|
1904
1908
|
|
|
1905
|
-
[12]: http://webdriver.io/api/mobile/
|
|
1909
|
+
[12]: http://webdriver.io/api/mobile/rotate.html
|
|
1906
1910
|
|
|
1907
|
-
[13]:
|
|
1911
|
+
[13]: http://webdriver.io/api/mobile/setImmediateValue.html
|
|
1908
1912
|
|
|
1909
|
-
[14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/
|
|
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
|
|
package/docs/playwright.md
CHANGED
|
@@ -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
|
+

|
|
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
|
|
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
|

|
|
476
481
|
|
|
477
|
-
|
|
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
|
|
487
|
+
npx nyc report --reporter html -t coverage
|
|
511
488
|
```
|
|
512
489
|
|
|
513
|
-
The above command will generate
|
|
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
|
+

|
|
493
|
+
|
|
494
|
+
Open `index.html` in your browser to view the full interactive coverage report.
|
|
495
|
+
|
|
496
|
+

|
|
514
497
|
|
|
515
|
-

|
|
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
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
|
|
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
|
|
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
|

|
|
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
|
+

|
|
394
|
+
|
|
395
|
+
Now there is new feature, add the configuration to test run of test plan
|
|
396
|
+

|
package/lib/actor.js
CHANGED
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;
|
package/lib/helper/Appium.js
CHANGED
|
@@ -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);
|
package/lib/helper/Playwright.js
CHANGED
|
@@ -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.
|
|
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.
|
|
722
|
+
if (this.options.basicAuth && (this.isAuthenticated !== true)) {
|
|
718
723
|
if (url.includes(this.options.url)) {
|
|
719
|
-
await this.browserContext.setHTTPCredentials(this.
|
|
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.
|
|
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
|
}
|
package/lib/helper/WebDriver.js
CHANGED
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
|
|
package/lib/listener/helpers.js
CHANGED
package/lib/mochaFactory.js
CHANGED
|
@@ -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
|
|
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
|
-
.
|
|
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$/));
|
package/lib/plugin/coverage.js
CHANGED
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
package/typings/index.d.ts
CHANGED
|
@@ -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;
|
package/typings/types.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
}
|