codeceptjs 3.0.6 → 3.1.2

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +92 -8
  2. package/README.md +9 -1
  3. package/bin/codecept.js +28 -17
  4. package/docs/build/Appium.js +69 -0
  5. package/docs/build/GraphQL.js +9 -10
  6. package/docs/build/Playwright.js +271 -63
  7. package/docs/build/Protractor.js +2 -0
  8. package/docs/build/Puppeteer.js +56 -18
  9. package/docs/build/REST.js +16 -3
  10. package/docs/build/WebDriver.js +82 -16
  11. package/docs/changelog.md +93 -9
  12. package/docs/configuration.md +15 -2
  13. package/docs/email.md +8 -8
  14. package/docs/examples.md +3 -3
  15. package/docs/helpers/Appium.md +66 -68
  16. package/docs/helpers/MockRequest.md +3 -3
  17. package/docs/helpers/Playwright.md +269 -203
  18. package/docs/helpers/Puppeteer.md +17 -1
  19. package/docs/helpers/REST.md +23 -9
  20. package/docs/helpers/WebDriver.md +3 -2
  21. package/docs/locators.md +27 -0
  22. package/docs/mobile.md +2 -1
  23. package/docs/nightmare.md +0 -5
  24. package/docs/parallel.md +14 -7
  25. package/docs/playwright.md +178 -11
  26. package/docs/plugins.md +61 -69
  27. package/docs/react.md +1 -1
  28. package/docs/reports.md +5 -4
  29. package/lib/actor.js +1 -2
  30. package/lib/codecept.js +13 -2
  31. package/lib/command/definitions.js +8 -1
  32. package/lib/command/interactive.js +4 -2
  33. package/lib/command/run-multiple/collection.js +4 -0
  34. package/lib/container.js +3 -3
  35. package/lib/helper/Appium.js +41 -0
  36. package/lib/helper/GraphQL.js +9 -10
  37. package/lib/helper/Playwright.js +218 -70
  38. package/lib/helper/Protractor.js +2 -0
  39. package/lib/helper/Puppeteer.js +56 -18
  40. package/lib/helper/REST.js +12 -0
  41. package/lib/helper/WebDriver.js +82 -16
  42. package/lib/helper/errors/ConnectionRefused.js +1 -1
  43. package/lib/helper/extras/Popup.js +1 -1
  44. package/lib/helper/extras/React.js +44 -32
  45. package/lib/interfaces/gherkin.js +1 -0
  46. package/lib/listener/exit.js +2 -4
  47. package/lib/listener/helpers.js +3 -4
  48. package/lib/locator.js +7 -0
  49. package/lib/mochaFactory.js +11 -6
  50. package/lib/output.js +5 -2
  51. package/lib/plugin/allure.js +7 -18
  52. package/lib/plugin/commentStep.js +1 -1
  53. package/lib/plugin/{puppeteerCoverage.js → coverage.js} +10 -22
  54. package/lib/plugin/customLocator.js +2 -2
  55. package/lib/plugin/screenshotOnFail.js +5 -0
  56. package/lib/plugin/subtitles.js +88 -0
  57. package/lib/plugin/tryTo.js +1 -1
  58. package/lib/step.js +4 -2
  59. package/lib/ui.js +6 -2
  60. package/package.json +5 -4
  61. package/typings/index.d.ts +44 -21
  62. package/typings/types.d.ts +137 -16
package/CHANGELOG.md CHANGED
@@ -1,10 +1,94 @@
1
+ ## 3.1.2
2
+
3
+ đŸ›Šī¸ Features:
4
+
5
+ * Added `coverage` plugin to generate code coverage for Playwright & Puppeteer. By @anirudh-modi
6
+ * Added `subtitle` plugin to generate subtitles for videos recorded with Playwright. By @anirudh-modi
7
+ * Configuration: `config.tests` to accept array of file patterns. See #2994 by @monsteramba
8
+
9
+ ```js
10
+ exports.config = {
11
+ tests: ['./*_test.js','./sampleTest.js'],
12
+ // ...
13
+ }
14
+ ```
15
+ * Notification is shown for test files without `Feature()`. See #3011 by @PeterNgTr
16
+
17
+ 🐛 Bugfixes:
18
+
19
+ * [Playwright] Fixed #2986 error is thrown when deleting a missing video. Fix by @hatufacci
20
+ * Fixed false positive result when invalid function is called in a helper. See #2997 by @abhimanyupandian
21
+ * [Appium] Removed full page mode for `saveScreenshot`. See #3002 by @nlespiaucq
22
+ * [Playwright] Fixed #3003 saving trace for a test with a long name. Fix by @hatufacci
23
+
24
+ 🎱 Other:
25
+
26
+ * Deprecated `puppeteerCoverage` plugin in favor of `coverage` plugin.
27
+
28
+ ## 3.1.1
29
+
30
+ * [Appium] Fixed #2759
31
+ `grabNumberOfVisibleElements`, `grabAttributeFrom`, `grabAttributeFromAll` to allow id locators.
32
+
33
+ ## 3.1.0
34
+
35
+ * [Plawyright] Updated to Playwright 1.13
36
+ * [Playwright] **Possible breaking change**: `BrowserContext` is initialized before each test and closed after. This behavior matches recommendation from Playwright team to use different contexts for tests.
37
+ * [Puppeteer] Updated to Puppeteer 10.2.
38
+ * [Protractor] Helper deprecated
39
+
40
+ đŸ›Šī¸ Features:
41
+
42
+ * [Playwright] Added recording of [video](https://codecept.io/playwright/#video) and [traces](https://codecept.io/playwright/#trace) by @davertmik
43
+ * [Playwritght] [Mocking requests](https://codecept.io/playwright/#mocking-network-requests) implemented via `route` API of Playwright by @davertmik
44
+ * [Playwright] Added **support for [React locators](https://codecept.io/react/#locators)** in #2912 by @AAAstorga
45
+
46
+ 🐛 Bugfixes:
47
+
48
+ * [Puppeteer] Fixed #2244 `els[0]._clickablePoint is not a function` by @karunandrii.
49
+ * [Puppeteer] Fixed `fillField` to check for invisible elements. See #2916 by @anne-open-xchange
50
+ * [Playwright] Reset of dialog event listener before registration of new one. #2946 by @nikocanvacom
51
+ * Fixed running Gherkin features with `run-multiple` using chunks. See #2900 by @andrenoberto
52
+ * Fixed #2937 broken typings for subfolders on Windows by @jancorvus
53
+ * Fixed issue where cucumberJsonReporter not working with fakerTransform plugin. See #2942 by @ilangv
54
+ * Fixed #2952 finished job with status code 0 when playwright cannot connect to remote wss url. By @davertmik
55
+
56
+
57
+ ## 3.0.7
58
+
59
+ 📖 Documentation fixes:
60
+
61
+ * Remove broken link from `Nightmare helper`. See #2860 by @Arhell
62
+ * Fixed broken links in `playwright.md`. See #2848 by @johnhoodjr
63
+ * Fix mocha-multi config example. See #2881 by @rimesc
64
+ * Fix small errors in email documentation file. See #2884 by @mkrtchian
65
+ * Improve documentation for `Sharing Data Between Workers` section. See #2891 by @ngraf
66
+
67
+ đŸ›Šī¸ Features:
68
+
69
+ * [WebDriver] Shadow DOM Support for `Webdriver`. See #2741 by @gkushang
70
+ * [Release management] Introduce the versioning automatically, it follows the semantics versioning. See #2883 by @PeterNgTr
71
+ * Adding opts into `Scenario.skip` that it would be useful for building reports. See #2867 by @AlexKo4
72
+ * Added support for attaching screenshots to [cucumberJsonReporter](https://github.com/ktryniszewski-mdsol/codeceptjs-cucumber-json-reporter) See #2888 by @fijijavis
73
+ * Supported config file for `codeceptjs shell` command. See #2895 by @PeterNgTr:
74
+
75
+ ```
76
+ npx codeceptjs shell -c foo.conf.js
77
+ ```
78
+
79
+ Bug fixes:
80
+ * [GraphQL] Use a helper-specific instance of Axios to avoid contaminating global defaults. See #2868 by @vanvoljg
81
+ * A default system color is used when passing non supported system color when using I.say(). See #2874 by @PeterNgTr
82
+ * [Playwright] Avoid the timout due to calling the click on invisible elements. See #2875 by cbayer97
83
+
84
+
1
85
  ## 3.0.6
2
86
 
3
- * [Playwright] Added `electron` as a browser to config. See #2834 by @cbayer97
87
+ * [Playwright] Added `electron` as a browser to config. See #2834 by @cbayer97
4
88
  * [Playwright] Implemented `launchPersistentContext` to be able to launch persistent remote browsers. See #2817 by @brunoqueiros. Fixes #2376.
5
89
  * Fixed printing logs and stack traces for `run-workers`. See #2857 by @haveac1gar. Fixes #2621, #2852
6
- * Emit custom messages from worker to the main thread. See #2824 by @jccguimaraes
7
- * Improved workers processes output. See #2804 by @drfiresign
90
+ * Emit custom messages from worker to the main thread. See #2824 by @jccguimaraes
91
+ * Improved workers processes output. See #2804 by @drfiresign
8
92
  * BDD. Added ability to use an array of feature files inside config in `gherkin.features`. See #2814 by @jbergeronjr
9
93
 
10
94
  ```js
@@ -13,8 +97,8 @@
13
97
  "./features/api_features/*.feature"
14
98
  ],
15
99
  ```
16
- * Added `getQueueId` to reporter to rerun a specific promise. See #2837 by @jonatask
17
- * **Added `fakerTransform` plugin** to use faker data in Gherkin scenarios. See #2854 by @adrielcodeco
100
+ * Added `getQueueId` to reporter to rerun a specific promise. See #2837 by @jonatask
101
+ * **Added `fakerTransform` plugin** to use faker data in Gherkin scenarios. See #2854 by @adrielcodeco
18
102
 
19
103
  ```feature
20
104
  Scenario Outline: ...
@@ -26,7 +110,7 @@ Scenario Outline: ...
26
110
  | productName | customer | email | anythingMore |
27
111
  | {{commerce.product}} | Dr. {{name.findName}} | {{internet.email}} | staticData |
28
112
  ```
29
- * [REST] Use class instance of axios, not the global instance, to avoid contaminating global configuration. #2846 by @vanvoljg
113
+ * [REST] Use class instance of axios, not the global instance, to avoid contaminating global configuration. #2846 by @vanvoljg
30
114
  * [Appium] Added `tunnelIdentifier` config option to provide tunnel for SauceLabs. See #2832 by @gurjeetbains
31
115
 
32
116
  ## 3.0.5
@@ -34,9 +118,9 @@ Scenario Outline: ...
34
118
 
35
119
  Features:
36
120
 
37
- * **[Official Docker image for CodeceptJS v3](https://hub.docker.com/r/codeceptjs/codeceptjs)**. New Docker image is based on official Playwright image and supports Playwright, Puppeteer, WebDriver engines. Thanks @VikentyShevyrin
121
+ * **[Official Docker image for CodeceptJS v3](https://hub.docker.com/r/codeceptjs/codeceptjs)**. New Docker image is based on official Playwright image and supports Playwright, Puppeteer, WebDriver engines. Thanks @VikentyShevyrin
38
122
  * Better support for Typescript `codecept.conf.ts` configuration files. See #2750 by @elaichenkov
39
- * Propagate more events for custom parallel script. See #2796 by @jccguimaraes
123
+ * Propagate more events for custom parallel script. See #2796 by @jccguimaraes
40
124
  * [mocha-junit-reporter] Now supports attachments, see documentation for details. See #2675 by @Shard
41
125
  * CustomLocators interface for TypeScript to extend from LocatorOrString. See #2798 by @danielrentz
42
126
  * [REST] Mask sensitive data from log messages.
package/README.md CHANGED
@@ -1,4 +1,12 @@
1
- [<img src="https://img.shields.io/badge/slack-@codeceptjs-purple.svg?logo=slack">](https://join.slack.com/t/codeceptjs/shared_invite/enQtMzA5OTM4NDM2MzA4LWE4MThhN2NmYTgxNTU5MTc4YzAyYWMwY2JkMmZlYWI5MWQ2MDM5MmRmYzZmYmNiNmY5NTAzM2EwMGIwOTNhOGQ) [<img src="https://img.shields.io/badge/discourse-codeceptjs-purple">](https://codecept.discourse.group) [![NPM version][npm-image]][npm-url] [![Build Status](https://travis-ci.org/Codeception/CodeceptJS.svg?branch=master)](https://travis-ci.org/Codeception/CodeceptJS) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/1823c38c74e44724b5555e3641f72621)](https://www.codacy.com/app/DavertMik/CodeceptJS?utm_source=github.com&utm_medium=referral&utm_content=Codeception/CodeceptJS&utm_campaign=badger) [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)
1
+ [<img src="https://img.shields.io/badge/slack-@codeceptjs-purple.svg?logo=slack">](https://join.slack.com/t/codeceptjs/shared_invite/enQtMzA5OTM4NDM2MzA4LWE4MThhN2NmYTgxNTU5MTc4YzAyYWMwY2JkMmZlYWI5MWQ2MDM5MmRmYzZmYmNiNmY5NTAzM2EwMGIwOTNhOGQ) [<img src="https://img.shields.io/badge/discourse-codeceptjs-purple">](https://codecept.discourse.group) [![NPM version][npm-image]][npm-url]
2
+
3
+ Build Status:
4
+
5
+ [![Playwright Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/playwright.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/playwright.yml)
6
+ [![Puppeteer Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/puppeteer.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/puppeteer.yml)
7
+ [![WebDriver Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/webdriver.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/webdriver.yml)
8
+ [![Appium Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appium.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appium.yml)
9
+ [![TestCafe Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/testcafe.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/testcafe.yml)
2
10
 
3
11
  # CodeceptJS
4
12
 
package/bin/codecept.js CHANGED
@@ -2,6 +2,16 @@
2
2
  const program = require('commander');
3
3
  const Codecept = require('../lib/codecept');
4
4
  const { print, error } = require('../lib/output');
5
+ const { printError } = require('../lib/command/utils');
6
+
7
+ const errorHandler = (fn) => async (...args) => {
8
+ try {
9
+ await fn(...args);
10
+ } catch (e) {
11
+ printError(e);
12
+ process.exitCode = 1;
13
+ }
14
+ };
5
15
 
6
16
  if (process.versions.node && process.versions.node.split('.') && process.versions.node.split('.')[0] < 8) {
7
17
  error('NodeJS >= 8 is required to run.');
@@ -16,41 +26,42 @@ program.version(Codecept.version());
16
26
 
17
27
  program.command('init [path]')
18
28
  .description('Creates dummy config in current dir or [path]')
19
- .action(require('../lib/command/init'));
29
+ .action(errorHandler(require('../lib/command/init')));
20
30
 
21
31
  program.command('migrate [path]')
22
32
  .description('Migrate json config to js config in current dir or [path]')
23
- .action(require('../lib/command/configMigrate'));
33
+ .action(errorHandler(require('../lib/command/configMigrate')));
24
34
 
25
35
  program.command('shell [path]')
26
36
  .alias('sh')
27
37
  .description('Interactive shell')
28
38
  .option('--verbose', 'output internal logging information')
29
39
  .option('--profile [value]', 'configuration profile to be used')
30
- .action(require('../lib/command/interactive'));
40
+ .option('-c, --config [file]', 'configuration file to be used')
41
+ .action(errorHandler(require('../lib/command/interactive')));
31
42
 
32
43
  program.command('list [path]')
33
44
  .alias('l')
34
45
  .description('List all actions for I.')
35
- .action(require('../lib/command/list'));
46
+ .action(errorHandler(require('../lib/command/list')));
36
47
 
37
48
  program.command('def [path]')
38
49
  .description('Generates TypeScript definitions for all I actions.')
39
50
  .option('-c, --config [file]', 'configuration file to be used')
40
51
  .option('-o, --output [folder]', 'target folder to paste definitions')
41
- .action(require('../lib/command/definitions'));
52
+ .action(errorHandler(require('../lib/command/definitions')));
42
53
 
43
54
  program.command('gherkin:init [path]')
44
55
  .alias('bdd:init')
45
56
  .description('Prepare CodeceptJS to run feature files.')
46
57
  .option('-c, --config [file]', 'configuration file to be used')
47
- .action(require('../lib/command/gherkin/init'));
58
+ .action(errorHandler(require('../lib/command/gherkin/init')));
48
59
 
49
60
  program.command('gherkin:steps [path]')
50
61
  .alias('bdd:steps')
51
62
  .description('Prints all defined gherkin steps.')
52
63
  .option('-c, --config [file]', 'configuration file to be used')
53
- .action(require('../lib/command/gherkin/steps'));
64
+ .action(errorHandler(require('../lib/command/gherkin/steps')));
54
65
 
55
66
  program.command('gherkin:snippets [path]')
56
67
  .alias('bdd:snippets')
@@ -59,28 +70,28 @@ program.command('gherkin:snippets [path]')
59
70
  .option('-c, --config [file]', 'configuration file to be used')
60
71
  .option('--feature [file]', 'feature files(s) to scan')
61
72
  .option('--path [file]', 'file in which to place the new snippets')
62
- .action(require('../lib/command/gherkin/snippets'));
73
+ .action(errorHandler(require('../lib/command/gherkin/snippets')));
63
74
 
64
75
  program.command('generate:test [path]')
65
76
  .alias('gt')
66
77
  .description('Generates an empty test')
67
- .action(require('../lib/command/generate').test);
78
+ .action(errorHandler(require('../lib/command/generate').test));
68
79
 
69
80
  program.command('generate:pageobject [path]')
70
81
  .alias('gpo')
71
82
  .description('Generates an empty page object')
72
- .action(require('../lib/command/generate').pageObject);
83
+ .action(errorHandler(require('../lib/command/generate').pageObject));
73
84
 
74
85
  program.command('generate:object [path]')
75
86
  .alias('go')
76
87
  .option('--type, -t [kind]', 'type of object to be created')
77
88
  .description('Generates an empty support object (page/step/fragment)')
78
- .action(require('../lib/command/generate').pageObject);
89
+ .action(errorHandler(require('../lib/command/generate').pageObject));
79
90
 
80
91
  program.command('generate:helper [path]')
81
92
  .alias('gh')
82
93
  .description('Generates a new helper')
83
- .action(require('../lib/command/generate').helper);
94
+ .action(errorHandler(require('../lib/command/generate').helper));
84
95
 
85
96
  program.command('run [test]')
86
97
  .description('Executes tests')
@@ -116,8 +127,8 @@ program.command('run [test]')
116
127
  .option('--recursive', 'include sub directories')
117
128
  .option('--trace', 'trace function calls')
118
129
  .option('--child <string>', 'option for child processes')
130
+ .action(errorHandler(require('../lib/command/run')));
119
131
 
120
- .action(require('../lib/command/run'));
121
132
  program.command('run-workers <workers>')
122
133
  .description('Executes tests in workers')
123
134
  .option('-c, --config [file]', 'configuration file to be used')
@@ -133,7 +144,7 @@ program.command('run-workers <workers>')
133
144
  .option('-p, --plugins <k=v,k2=v2,...>', 'enable plugins, comma-separated')
134
145
  .option('-O, --reporter-options <k=v,k2=v2,...>', 'reporter-specific options')
135
146
  .option('-R, --reporter <name>', 'specify the reporter to use')
136
- .action(require('../lib/command/run-workers'));
147
+ .action(errorHandler(require('../lib/command/run-workers')));
137
148
 
138
149
  program.command('run-multiple [suites...]')
139
150
  .description('Executes tests multiple')
@@ -157,12 +168,12 @@ program.command('run-multiple [suites...]')
157
168
  // mocha options
158
169
  .option('--colors', 'force enabling of colors')
159
170
 
160
- .action(require('../lib/command/run-multiple'));
171
+ .action(errorHandler(require('../lib/command/run-multiple')));
161
172
 
162
173
  program.command('info [path]')
163
174
  .description('Print debugging information concerning the local environment')
164
175
  .option('-c, --config', 'your config file path')
165
- .action(require('../lib/command/info'));
176
+ .action(errorHandler(require('../lib/command/info')));
166
177
 
167
178
  program.command('dry-run [test]')
168
179
  .description('Prints step-by-step scenario for a test without actually running it')
@@ -178,7 +189,7 @@ program.command('dry-run [test]')
178
189
  .option('--steps', 'show step-by-step execution')
179
190
  .option('--verbose', 'output internal logging information')
180
191
  .option('--debug', 'output additional information')
181
- .action(require('../lib/command/dryRun'));
192
+ .action(errorHandler(require('../lib/command/dryRun')));
182
193
 
183
194
  program.on('command:*', (cmd) => {
184
195
  console.log(`\nUnknown command ${cmd}\n`);
@@ -183,6 +183,7 @@ class Appium extends Webdriver {
183
183
  config.capabilities.browserName = config.browser || config.capabilities.browserName;
184
184
  config.capabilities.app = config.app || config.capabilities.app;
185
185
  config.capabilities.platformName = config.platform || config.capabilities.platformName;
186
+ config.capabilities.tunnelIdentifier = config.tunnelIdentifier || config.capabilities.tunnelIdentifier; // Adding the code to connect to sauce labs via sauce tunnel
186
187
  config.waitForTimeout /= 1000; // convert to seconds
187
188
 
188
189
  // [CodeceptJS compatible] transform host to hostname
@@ -1461,6 +1462,60 @@ class Appium extends Webdriver {
1461
1462
  return super.grabTextFrom(parseLocator.call(this, locator));
1462
1463
  }
1463
1464
 
1465
+ /**
1466
+ * Grab number of visible elements by locator.
1467
+ * Resumes test execution, so **should be used inside async function with `await`** operator.
1468
+ *
1469
+ * ```js
1470
+ * let numOfElements = await I.grabNumberOfVisibleElements('p');
1471
+ * ```
1472
+ *
1473
+ * @param {CodeceptJS.LocatorOrString} locator located by CSS|XPath|strict locator.
1474
+ * @returns {Promise<number>} number of visible elements
1475
+ */
1476
+ async grabNumberOfVisibleElements(locator) {
1477
+ if (this.isWeb) return super.grabNumberOfVisibleElements(locator);
1478
+ return super.grabNumberOfVisibleElements(parseLocator.call(this, locator));
1479
+ }
1480
+
1481
+ /**
1482
+ * Can be used for apps only with several values ("contentDescription", "text", "className", "resourceId")
1483
+ *
1484
+ * Retrieves an attribute from an element located by CSS or XPath and returns it to test.
1485
+ * Resumes test execution, so **should be used inside async with `await`** operator.
1486
+ * If more than one element is found - attribute of first element is returned.
1487
+ *
1488
+ * ```js
1489
+ * let hint = await I.grabAttributeFrom('#tooltip', 'title');
1490
+ * ```
1491
+ * @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
1492
+ * @param {string} attr attribute name.
1493
+ * @returns {Promise<string>} attribute value
1494
+ *
1495
+ */
1496
+ async grabAttributeFrom(locator, attr) {
1497
+ if (this.isWeb) return super.grabAttributeFrom(locator, attr);
1498
+ return super.grabAttributeFrom(parseLocator.call(this, locator), attr);
1499
+ }
1500
+
1501
+ /**
1502
+ * Can be used for apps only with several values ("contentDescription", "text", "className", "resourceId")
1503
+ * Retrieves an array of attributes from elements located by CSS or XPath and returns it to test.
1504
+ * Resumes test execution, so **should be used inside async with `await`** operator.
1505
+ *
1506
+ * ```js
1507
+ * let hints = await I.grabAttributeFromAll('.tooltip', 'title');
1508
+ * ```
1509
+ * @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
1510
+ * @param {string} attr attribute name.
1511
+ * @returns {Promise<string[]>} attribute value
1512
+ *
1513
+ */
1514
+ async grabAttributeFromAll(locator, attr) {
1515
+ if (this.isWeb) return super.grabAttributeFromAll(locator, attr);
1516
+ return super.grabAttributeFromAll(parseLocator.call(this, locator), attr);
1517
+ }
1518
+
1464
1519
  /**
1465
1520
  * Retrieves an array of value from a form located by CSS or XPath and returns it to test.
1466
1521
  * Resumes test execution, so **should be used inside async function with `await`** operator.
@@ -1496,6 +1551,20 @@ class Appium extends Webdriver {
1496
1551
  return super.grabValueFrom(parseLocator.call(this, locator));
1497
1552
  }
1498
1553
 
1554
+ /**
1555
+ * Saves a screenshot to ouput folder (set in codecept.json or codecept.conf.js).
1556
+ * Filename is relative to output folder.
1557
+ *
1558
+ * ```js
1559
+ * I.saveScreenshot('debug.png');
1560
+ * ```
1561
+ *
1562
+ * @param {string} fileName file name to save.
1563
+ */
1564
+ async saveScreenshot(fileName) {
1565
+ return super.saveScreenshot(fileName, false);
1566
+ }
1567
+
1499
1568
  /**
1500
1569
  * Scroll element into viewport.
1501
1570
  *
@@ -1,8 +1,6 @@
1
- let axios = require('axios');
1
+ const axios = require('axios').default;
2
2
  const Helper = require('../helper');
3
3
 
4
- let headers = {};
5
-
6
4
  /**
7
5
  * GraphQL helper allows to send additional requests to a GraphQl endpoint during acceptance tests.
8
6
  * [Axios](https://github.com/axios/axios) library is used to perform requests.
@@ -41,15 +39,16 @@ let headers = {};
41
39
  class GraphQL extends Helper {
42
40
  constructor(config) {
43
41
  super(config);
44
- axios = require('axios');
42
+ this.axios = axios.create();
43
+ this.headers = {};
45
44
  this.options = {
46
45
  timeout: 10000,
47
46
  defaultHeaders: {},
48
47
  endpoint: '',
49
48
  };
50
49
  this.options = Object.assign(this.options, config);
51
- headers = { ...this.options.defaultHeaders };
52
- axios.defaults.headers = this.options.defaultHeaders;
50
+ this.headers = { ...this.options.defaultHeaders };
51
+ this.axios.defaults.headers = this.options.defaultHeaders;
53
52
  }
54
53
 
55
54
  static _checkRequirements() {
@@ -66,10 +65,10 @@ class GraphQL extends Helper {
66
65
  * @param {object} request
67
66
  */
68
67
  async _executeQuery(request) {
69
- axios.defaults.timeout = request.timeout || this.options.timeout;
68
+ this.axios.defaults.timeout = request.timeout || this.options.timeout;
70
69
 
71
- if (headers && headers.auth) {
72
- request.auth = headers.auth;
70
+ if (this.headers && this.headers.auth) {
71
+ request.auth = this.headers.auth;
73
72
  }
74
73
 
75
74
  request.headers = Object.assign(request.headers, {
@@ -84,7 +83,7 @@ class GraphQL extends Helper {
84
83
 
85
84
  let response;
86
85
  try {
87
- response = await axios(request);
86
+ response = await this.axios(request);
88
87
  } catch (err) {
89
88
  if (!err.response) throw err;
90
89
  this.debugSection(