codeceptjs 2.6.7 → 2.6.11

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 (97) hide show
  1. package/CHANGELOG.md +33 -6
  2. package/README.md +11 -11
  3. package/docs/advanced.md +21 -0
  4. package/docs/basics.md +6 -5
  5. package/docs/bdd.md +1 -2
  6. package/docs/books.md +1 -1
  7. package/docs/build/Appium.js +1 -2
  8. package/docs/build/FileSystem.js +3 -3
  9. package/docs/build/Mochawesome.js +1 -1
  10. package/docs/build/Nightmare.js +81 -5
  11. package/docs/build/Playwright.js +100 -17
  12. package/docs/build/Protractor.js +34 -2
  13. package/docs/build/Puppeteer.js +59 -2
  14. package/docs/build/TestCafe.js +23 -0
  15. package/docs/build/WebDriver.js +62 -16
  16. package/docs/changelog.md +152 -125
  17. package/docs/community-helpers.md +7 -3
  18. package/docs/configuration.md +1 -1
  19. package/docs/custom-helpers.md +2 -2
  20. package/docs/data.md +1 -1
  21. package/docs/detox.md +2 -2
  22. package/docs/email.md +1 -1
  23. package/docs/examples.md +12 -2
  24. package/docs/helpers/Appium.md +24 -5
  25. package/docs/helpers/Nightmare.md +42 -0
  26. package/docs/helpers/Playwright.md +41 -4
  27. package/docs/helpers/Protractor.md +14 -0
  28. package/docs/helpers/Puppeteer.md +38 -1
  29. package/docs/helpers/TestCafe.md +14 -0
  30. package/docs/helpers/WebDriver.md +24 -5
  31. package/docs/hooks.md +14 -14
  32. package/docs/locators.md +1 -1
  33. package/docs/playwright.md +13 -0
  34. package/docs/translation.md +21 -1
  35. package/docs/ui.md +2 -2
  36. package/docs/videos.md +4 -4
  37. package/docs/webapi/saveElementScreenshot.mustache +9 -0
  38. package/docs/webapi/type.mustache +11 -6
  39. package/docs/wiki/{Community-Helpers.md → Community-Helpers-&-Plugins.md} +6 -2
  40. package/docs/wiki/Examples.md +11 -1
  41. package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +68 -0
  42. package/docs/wiki/Home.md +9 -4
  43. package/docs/wiki/Release-Process.md +24 -0
  44. package/docs/wiki/Tests.md +1391 -0
  45. package/docs/wiki/Upgrading-to-CodeceptJS-3.md +153 -0
  46. package/docs/wiki/Videos.md +3 -3
  47. package/lib/actor.js +1 -1
  48. package/lib/assert/empty.js +1 -1
  49. package/lib/assert/equal.js +1 -1
  50. package/lib/assert/include.js +1 -1
  51. package/lib/assert/truth.js +1 -1
  52. package/lib/codecept.js +2 -3
  53. package/lib/command/configMigrate.js +3 -5
  54. package/lib/command/definitions.js +1 -2
  55. package/lib/command/dryRun.js +1 -2
  56. package/lib/command/gherkin/init.js +1 -1
  57. package/lib/command/gherkin/snippets.js +3 -3
  58. package/lib/command/gherkin/steps.js +2 -3
  59. package/lib/command/info.js +1 -2
  60. package/lib/command/init.js +2 -2
  61. package/lib/command/interactive.js +1 -2
  62. package/lib/command/list.js +3 -4
  63. package/lib/command/run-multiple.js +2 -3
  64. package/lib/command/run-rerun.js +2 -4
  65. package/lib/command/run.js +1 -2
  66. package/lib/container.js +2 -2
  67. package/lib/data/context.js +1 -1
  68. package/lib/event.js +1 -1
  69. package/lib/helper/Appium.js +1 -2
  70. package/lib/helper/FileSystem.js +3 -3
  71. package/lib/helper/Mochawesome.js +1 -1
  72. package/lib/helper/Nightmare.js +54 -5
  73. package/lib/helper/Playwright.js +75 -17
  74. package/lib/helper/Protractor.js +26 -2
  75. package/lib/helper/Puppeteer.js +34 -2
  76. package/lib/helper/TestCafe.js +15 -0
  77. package/lib/helper/WebDriver.js +43 -11
  78. package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
  79. package/lib/hooks.js +1 -2
  80. package/lib/interfaces/gherkin.js +0 -1
  81. package/lib/listener/helpers.js +1 -2
  82. package/lib/listener/mocha.js +0 -1
  83. package/lib/locator.js +2 -2
  84. package/lib/pause.js +1 -1
  85. package/lib/plugin/allure.js +1 -1
  86. package/lib/plugin/autoDelay.js +3 -3
  87. package/lib/plugin/autoLogin.js +1 -1
  88. package/lib/plugin/screenshotOnFail.js +2 -1
  89. package/lib/plugin/standardActingHelpers.js +0 -3
  90. package/lib/recorder.js +1 -1
  91. package/lib/step.js +3 -0
  92. package/lib/ui.js +1 -0
  93. package/package.json +3 -2
  94. package/translations/fr-FR.js +63 -0
  95. package/translations/index.js +5 -4
  96. package/typings/types.d.ts +140 -8
  97. package/docs/wiki/Release-process.md +0 -25
@@ -0,0 +1,153 @@
1
+ 🚀 CodeceptJS 3 is in beta now. Install it:
2
+
3
+ ```
4
+ npm i codeceptjs@3.0.0-beta.3
5
+ ```
6
+
7
+ * [COMPLETE CHANGELOG](https://github.com/Codeception/CodeceptJS/blob/codeceptjs-v3.0/CHANGELOG.md#300-beta)
8
+ * [Documentation](https://github.com/Codeception/CodeceptJS/tree/codeceptjs-v3.0/docs)
9
+
10
+ After installing a new version, run `codecept3-upgrade` which is described below:
11
+
12
+ ---
13
+
14
+ CodeceptJS 3.0 is a new version of CodeceptJS with some breaking changes included.
15
+ You should update your project following this guide to ensure that everything works correctly.
16
+
17
+ ### 1️⃣ Syntax Change
18
+
19
+ CodeceptJS 3.0 introduced a new syntax for declaring tests, instead of:
20
+
21
+ ```js
22
+ Scenario('title', (I, loginPage) => {})
23
+ ```
24
+ we use a new way of passing arguments into a test, via destruction:
25
+
26
+ ```js
27
+ Scenario('title', ({ I, loginPage }) => {})
28
+ ```
29
+
30
+ This works similarly to `inject()` command. This change was done to unify accessing [dependency injection container](https://codecept.io/pageobjects/#dependency-injection), and to provide better TypeScript support.
31
+
32
+ > If you use BDD-style project with Gherkin, no changes needed for this step.
33
+
34
+ To upgrade your project, you don't need to change manually all your tests.
35
+
36
+ 💪 **Use [codecept3-upgrade tool](https://www.npmjs.com/package/codecept3-upgrade) to perform the migration**. Use it carefully, as it updates your current code.
37
+
38
+ To upgrade your codebase, commit all your changes, and switch to a new branch.
39
+ Run upgrade script (even without installing) for a directory where you have your tests:
40
+
41
+ ```
42
+ npx codecept3-upgrade tests
43
+ ```
44
+ Review the changes in Git Diff and try to execute tests using CodeceptJS 3.0
45
+
46
+
47
+ ### 2️⃣ Grabbers signature change
48
+
49
+ There were confusion and inconsistency across grab* methods. What they will return if multiple elements are found? A single element or an array? This was the problem as the format was dependent on a page content.
50
+
51
+ In 3.0 we decided to make all current grab* methods to return a single value only. While we add new methods grab**FromAll that return an array.
52
+
53
+ For instance, `grabTextFrom` will always return a single text value, while `grabTextFromAll` will return an array of values:
54
+
55
+ ```js
56
+ await I.grabTextFrom('.username'); => 'john'
57
+ await I.grabTextFromAll('.username'); => ['john', 'claudia', 'bill']
58
+ ```
59
+
60
+ Please update parts in your project where you rely on grab* methods to return an array.
61
+
62
+
63
+ | Single Value | Multiple Values |
64
+ | -- | -- |
65
+ | ✋`grabTextFrom` | 🙌 `grabTextFromAll` |
66
+ | ✋`grabValueFrom` | 🙌 `grabValueFromAll` |
67
+ | ✋ `grabAttributeFrom` | 🙌`grabAttributeFromAll` |
68
+ | ✋ `grabHTMLFrom` | 🙌 `grabHTMLFromAll` |
69
+ | ✋ `grabCssPropertyFrom` | 🙌 `grabCssPropertyFromAll` |
70
+
71
+ > Single-value `grab*From` will throw error when no data was matched, while `grab*FromAll` will return array.
72
+
73
+ ### 3️⃣ Bootstrap / Teardown Changed
74
+
75
+ `async/await` paradigm changed the way we write asynchronous code in NodeJS.
76
+ However, bootstrap functions were created to use old-style methods of passing `done` callback inside.
77
+
78
+ In 3.0 we decided to completely change the way async bootstrap is performed and replace all it with async/await functions:
79
+
80
+ ```js
81
+ // before
82
+ bootstrap: (done) => {
83
+ server.start().then(done);
84
+ },
85
+
86
+ // after
87
+ bootstrap: async () => {
88
+ await server.start();
89
+ }
90
+ ```
91
+ Passing a string as bootstrap function (to require a function from external file) is also not supported:
92
+
93
+ ```js
94
+ // before
95
+ bootstrap: './server_start.js',
96
+
97
+ // after
98
+ bootstrap: require('./server_start.js'),
99
+ ```
100
+
101
+ The same rules are applied to teardown, bootstrapAll, teardownAll.
102
+
103
+ ### 4️⃣ Locator Detection Heuristic Change
104
+
105
+ In 3.0 we added a new rule to auto-detect CSS locator. If a locator starts with `[` parser will use it as a CSS locator, without trying to match value by text.
106
+
107
+ * Previous behavior: I.click('[user]') - will try to search for a link with `[user]` text, if no found - take `[user]` as CSS locator
108
+ * Current behavior: I.click('[user]') - will check only for CSS locator `[user]`
109
+
110
+ ### 5️⃣ Improved Parallel Execution with Workers
111
+
112
+ CodeceptJS has two parallel execution modes:
113
+
114
+ * classical `run-multiple` which spawns independent CodeceptJS processes on system level.
115
+ * threaded `run-workers` which uses NodeJS workers instead of subprocesses.
116
+
117
+ Workers are faster, they can communicate with parent thread. Unfortunately, `run-workers` was not as efficient as `run-multiple` because there was no way of declaring sophisticated configs for parallel execution. For instance, running tests in 4 threads in 2 browsers could not be set.
118
+
119
+ We updated workers API to make them as flexible as possible. You don't even need to put complex logic into config! You can create your own parallel runner and customize it to match your expectations:
120
+
121
+ ```js
122
+ // don't initialize workers in constructor
123
+ const workers = new Workers(null, workerConfig);
124
+ // split tests by suites in 2 groups
125
+ const testGroups = workers.createGroupsOfSuites(2);
126
+
127
+ const browsers = ['firefox', 'chrome'];
128
+
129
+ const configs = browsers.map(browser => {
130
+ return helpers: {
131
+ WebDriver: { browser }
132
+ }
133
+ });
134
+
135
+ for (const config of configs) {
136
+ for (group of groupOfTests) {
137
+ const worker = workers.spawn();
138
+ worker.addTests(group);
139
+ worker.addConfig(config);
140
+ }
141
+ }
142
+
143
+ workers.run();
144
+ ```
145
+ [Learn more about how you can create a parallel runner](https://github.com/Codeception/CodeceptJS/blob/codeceptjs-v3.0/docs/parallel.md#custom-parallel-execution).
146
+
147
+ `run-multiple` will still work but it is considered deprecated and won't be supported.
148
+
149
+ ### WebDriverIO helper removed
150
+
151
+ WebDriverIO helper supported webdriverio v4 library, which is not maintained anymore. It should be easy to switch to WebDriver helper which supports webdriverio v6.
152
+
153
+
@@ -1,10 +1,10 @@
1
1
  [![](http://i3.ytimg.com/vi/BRMWstiOTks/maxresdefault.jpg)](https://www.youtube.com/watch?v=BRMWstiOTks)
2
2
 
3
- * ## [An Introduction, Getting started and working with CodeceptJS & Puppeteer (EAWeekend)](https://www.youtube.com/watch?v=BRMWstiOTks)
3
+ ## [An Introduction, Getting started and working with CodeceptJS & Puppeteer (EAWeekend)](https://www.youtube.com/watch?v=BRMWstiOTks)
4
4
 
5
- * ## [CodeceptJS Official YouTube Channel](https://www.youtube.com/channel/UCEs4030bmtonyDhTHEXa_2g)
5
+ ## [CodeceptJS Official YouTube Channel](https://www.youtube.com/channel/UCEs4030bmtonyDhTHEXa_2g)
6
6
 
7
- * ## [Introductory Videos](https://www.youtube.com/watch?v=FPFG1rBNJ64&list=PLcFXthgti9Lt4SjSvL1ALDg6dOeTC0TvT)
7
+ ## [Introductory Videos](https://www.youtube.com/watch?v=FPFG1rBNJ64&list=PLcFXthgti9Lt4SjSvL1ALDg6dOeTC0TvT)
8
8
 
9
9
  Free educational videos provided by our community member **[@ontytoom](http://github.com/ontytoom)**.
10
10
 
package/lib/actor.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const Step = require('./step');
2
2
  const container = require('./container');
3
- const methodsOfObject = require('./utils').methodsOfObject;
3
+ const { methodsOfObject } = require('./utils');
4
4
  const recorder = require('./recorder');
5
5
  const event = require('./event');
6
6
  const output = require('./output');
@@ -1,6 +1,6 @@
1
1
  const Assertion = require('../assert');
2
2
  const AssertionFailedError = require('./error');
3
- const template = require('../utils').template;
3
+ const { template } = require('../utils');
4
4
  const output = require('../output');
5
5
 
6
6
  class EmptinessAssertion extends Assertion {
@@ -1,6 +1,6 @@
1
1
  const Assertion = require('../assert');
2
2
  const AssertionFailedError = require('./error');
3
- const template = require('../utils').template;
3
+ const { template } = require('../utils');
4
4
  const output = require('../output');
5
5
 
6
6
  class EqualityAssertion extends Assertion {
@@ -1,6 +1,6 @@
1
1
  const Assertion = require('../assert');
2
2
  const AssertionFailedError = require('./error');
3
- const template = require('../utils').template;
3
+ const { template } = require('../utils');
4
4
  const output = require('../output');
5
5
 
6
6
  const MAX_LINES = 10;
@@ -1,6 +1,6 @@
1
1
  const Assertion = require('../assert');
2
2
  const AssertionFailedError = require('./error');
3
- const template = require('../utils').template;
3
+ const { template } = require('../utils');
4
4
  const output = require('../output');
5
5
 
6
6
  class TruthAssertion extends Assertion {
package/lib/codecept.js CHANGED
@@ -1,8 +1,7 @@
1
- const existsSync = require('fs').existsSync;
2
- const readFileSync = require('fs').readFileSync;
1
+ const { existsSync, readFileSync } = require('fs');
3
2
  const glob = require('glob');
4
3
  const fsPath = require('path');
5
- const resolve = require('path').resolve;
4
+ const { resolve } = require('path');
6
5
 
7
6
  const container = require('./container');
8
7
  const Config = require('./config');
@@ -5,11 +5,9 @@ const mkdirp = require('mkdirp');
5
5
  const path = require('path');
6
6
  const util = require('util');
7
7
 
8
- const print = require('../output').print;
9
- const success = require('../output').success;
10
- const error = require('../output').error;
11
- const fileExists = require('../utils').fileExists;
12
- const getTestRoot = require('./utils').getTestRoot;
8
+ const { print, success, error } = require('../output');
9
+ const { fileExists } = require('../utils');
10
+ const { getTestRoot } = require('./utils');
13
11
 
14
12
  module.exports = function (initPath) {
15
13
  const testsPath = getTestRoot(initPath);
@@ -1,8 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
 
4
- const getConfig = require('./utils').getConfig;
5
- const getTestRoot = require('./utils').getTestRoot;
4
+ const { getConfig, getTestRoot } = require('./utils');
6
5
  const Codecept = require('../codecept');
7
6
  const container = require('../container');
8
7
  const output = require('../output');
@@ -1,5 +1,4 @@
1
- const getConfig = require('./utils').getConfig;
2
- const getTestRoot = require('./utils').getTestRoot;
1
+ const { getConfig, getTestRoot } = require('./utils');
3
2
  const Config = require('../config');
4
3
  const Codecept = require('../codecept');
5
4
  const output = require('../output');
@@ -4,7 +4,7 @@ const path = require('path');
4
4
  const mkdirp = require('mkdirp');
5
5
 
6
6
  const output = require('../../output');
7
- const { fileExists, ucfirst, lcfirst } = require('../../utils');
7
+ const { fileExists } = require('../../utils');
8
8
  const {
9
9
  getConfig, getTestRoot, updateConfig, safeFileWrite,
10
10
  } = require('../utils');
@@ -4,8 +4,8 @@ const { Parser } = require('gherkin');
4
4
  const glob = require('glob');
5
5
  const fsPath = require('path');
6
6
 
7
- const getConfig = require('../utils').getConfig;
8
- const getTestRoot = require('../utils').getTestRoot;
7
+ const { getConfig } = require('../utils');
8
+ const { getTestRoot } = require('../utils');
9
9
  const Codecept = require('../../codecept');
10
10
  const output = require('../../output');
11
11
  const { matchStep } = require('../../interfaces/bdd');
@@ -97,7 +97,7 @@ module.exports = function (genPath, options) {
97
97
 
98
98
  let stepFile = options.path || config.gherkin.steps[0];
99
99
  if (!fs.existsSync(stepFile)) {
100
- output.error('Please enter a valid step file path ', stepFile);
100
+ output.error(`Please enter a valid step file path ${stepFile}`);
101
101
  process.exit(1);
102
102
  }
103
103
 
@@ -1,7 +1,6 @@
1
- const getConfig = require('../utils').getConfig;
2
- const getTestRoot = require('../utils').getTestRoot;
1
+ const { getConfig } = require('../utils');
2
+ const { getTestRoot } = require('../utils');
3
3
  const Codecept = require('../../codecept');
4
- const container = require('../../container');
5
4
  const output = require('../../output');
6
5
  const { getSteps } = require('../../interfaces/bdd');
7
6
 
@@ -1,7 +1,6 @@
1
1
  const envinfo = require('envinfo');
2
2
 
3
- const getConfig = require('./utils').getConfig;
4
- const getTestRoot = require('./utils').getTestRoot;
3
+ const { getConfig, getTestRoot } = require('./utils');
5
4
  const Codecept = require('../codecept');
6
5
  const output = require('../output');
7
6
 
@@ -21,7 +21,7 @@ const defaultConfig = {
21
21
  mocha: {},
22
22
  };
23
23
 
24
- const helpers = ['WebDriver', 'Puppeteer', 'TestCafe', 'Protractor', 'Nightmare', 'Appium', 'Playwright'];
24
+ const helpers = require('../plugin/standardActingHelpers').slice();
25
25
  const translations = Object.keys(require('../../translations'));
26
26
 
27
27
  const noTranslation = 'English (no localization)';
@@ -32,7 +32,7 @@ let packages;
32
32
  const configHeader = `const { setHeadlessWhen } = require('@codeceptjs/configure');
33
33
 
34
34
  // turn on headless mode when running with HEADLESS=true environment variable
35
- // HEADLESS=true npx codecept run
35
+ // export HEADLESS=true && npx codeceptjs run
36
36
  setHeadlessWhen(process.env.HEADLESS);
37
37
 
38
38
  `;
@@ -1,5 +1,4 @@
1
- const getConfig = require('./utils').getConfig;
2
- const getTestRoot = require('./utils').getTestRoot;
1
+ const { getConfig, getTestRoot } = require('./utils');
3
2
  const recorder = require('../recorder');
4
3
  const Codecept = require('../codecept');
5
4
  const event = require('../event');
@@ -1,9 +1,8 @@
1
- const getConfig = require('./utils').getConfig;
2
- const getTestRoot = require('./utils').getTestRoot;
1
+ const { getConfig, getTestRoot } = require('./utils');
3
2
  const Codecept = require('../codecept');
4
3
  const container = require('../container');
5
- const getParamsToString = require('../parser').getParamsToString;
6
- const methodsOfObject = require('../utils').methodsOfObject;
4
+ const { getParamsToString } = require('../parser');
5
+ const { methodsOfObject } = require('../utils');
7
6
  const output = require('../output');
8
7
 
9
8
  module.exports = function (path) {
@@ -1,12 +1,11 @@
1
- const fork = require('child_process').fork;
1
+ const { fork } = require('child_process');
2
2
  const path = require('path');
3
3
  const crypto = require('crypto');
4
4
 
5
5
  const runHook = require('../hooks');
6
6
  const event = require('../event');
7
7
  const collection = require('./run-multiple/collection');
8
- const clearString = require('../utils').clearString;
9
- const replaceValueDeep = require('../utils').replaceValueDeep;
8
+ const { clearString, replaceValueDeep } = require('../utils');
10
9
  const {
11
10
  getConfig, getTestRoot, fail,
12
11
  } = require('./utils');
@@ -1,7 +1,5 @@
1
- const getConfig = require('./utils').getConfig;
2
- const getTestRoot = require('./utils').getTestRoot;
3
- const printError = require('./utils').printError;
4
- const createOutputDir = require('./utils').createOutputDir;
1
+ const { getConfig, getTestRoot } = require('./utils');
2
+ const { printError, createOutputDir } = require('./utils');
5
3
  const Config = require('../config');
6
4
  const Codecept = require('../rerun');
7
5
 
@@ -1,8 +1,7 @@
1
1
  const mkdirp = require('mkdirp');
2
2
  const path = require('path');
3
3
 
4
- const getConfig = require('./utils').getConfig;
5
- const getTestRoot = require('./utils').getTestRoot;
4
+ const { getConfig, getTestRoot } = require('./utils');
6
5
  const printError = require('./utils').printError;
7
6
  const createOutputDir = require('./utils').createOutputDir;
8
7
  const Config = require('../config');
package/lib/container.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const glob = require('glob');
2
2
  const path = require('path');
3
3
 
4
- const fileExists = require('./utils').fileExists;
4
+ const { fileExists } = require('./utils');
5
5
  const Translation = require('./translation');
6
6
  const MochaFactory = require('./mochaFactory');
7
7
  const recorder = require('./recorder');
@@ -163,7 +163,7 @@ function createHelpers(config) {
163
163
  }
164
164
  helpers[helperName] = new HelperClass(config[helperName]);
165
165
  } catch (err) {
166
- throw new Error(`Could not load helper ${helperName} from module '${moduleName}':\n${err.message}`);
166
+ throw new Error(`Could not load helper ${helperName} from module '${moduleName}':\n${err.message}\n${err.stack}`);
167
167
  }
168
168
  }
169
169
 
@@ -1,4 +1,4 @@
1
- const isGenerator = require('../utils').isGenerator;
1
+ const { isGenerator } = require('../utils');
2
2
  const DataTable = require('./table');
3
3
  const DataScenarioConfig = require('./dataScenarioConfig');
4
4
 
package/lib/event.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const events = require('events');
2
2
 
3
3
  const dispatcher = new events.EventEmitter();
4
- const log = require('./output').log;
4
+ const { log } = require('./output');
5
5
 
6
6
  /**
7
7
  * @namespace
@@ -1,13 +1,12 @@
1
1
  let webdriverio;
2
2
  let wdioV4;
3
- let SCREEN_SIZE;
4
3
 
5
4
  const fs = require('fs');
6
5
  const requireg = require('requireg');
7
6
 
8
7
  const Webdriver = require('./WebDriver');
9
8
  const AssertionFailedError = require('../assert/error');
10
- const truth = require('../assert/truth').truth;
9
+ const { truth } = require('../assert/truth');
11
10
  const recorder = require('../recorder');
12
11
  const Locator = require('../locator');
13
12
  const ConnectionRefused = require('./errors/ConnectionRefused');
@@ -3,9 +3,9 @@ const path = require('path');
3
3
  const fs = require('fs');
4
4
 
5
5
  const Helper = require('../helper');
6
- const fileExists = require('../utils').fileExists;
7
- const fileIncludes = require('../assert/include').fileIncludes;
8
- const fileEquals = require('../assert/equal').fileEquals;
6
+ const { fileExists } = require('../utils');
7
+ const { fileIncludes } = require('../assert/include');
8
+ const { fileEquals } = require('../assert/equal');
9
9
 
10
10
  /**
11
11
  * Helper for testing filesystem.
@@ -5,7 +5,7 @@ let currentSuite;
5
5
  const requireg = require('requireg');
6
6
 
7
7
  const Helper = require('../helper');
8
- const clearString = require('../utils').clearString;
8
+ const { clearString } = require('../utils');
9
9
 
10
10
  class Mochawesome extends Helper {
11
11
  constructor(config) {
@@ -3,11 +3,11 @@ const requireg = require('requireg');
3
3
  const urlResolve = require('url').resolve;
4
4
 
5
5
  const Helper = require('../helper');
6
- const stringIncludes = require('../assert/include').includes;
7
- const urlEquals = require('../assert/equal').urlEquals;
8
- const equals = require('../assert/equal').equals;
9
- const empty = require('../assert/empty').empty;
10
- const truth = require('../assert/truth').truth;
6
+ const { includes: stringIncludes } = require('../assert/include');
7
+ const { urlEquals } = require('../assert/equal');
8
+ const { equals } = require('../assert/equal');
9
+ const { empty } = require('../assert/empty');
10
+ const { truth } = require('../assert/truth');
11
11
  const Locator = require('../locator');
12
12
  const ElementNotFound = require('./errors/ElementNotFound');
13
13
  const {
@@ -1066,6 +1066,55 @@ class Nightmare extends Helper {
1066
1066
  return this.browser.refresh();
1067
1067
  }
1068
1068
 
1069
+ /**
1070
+ * {{> saveElementScreenshot }}
1071
+ *
1072
+ */
1073
+ async saveElementScreenshot(locator, fileName) {
1074
+ const outputFile = screenshotOutputFolder(fileName);
1075
+
1076
+ const rect = await this.grabElementBoundingRect(locator);
1077
+
1078
+ const button_clip = {
1079
+ x: Math.floor(rect.x),
1080
+ y: Math.floor(rect.y),
1081
+ width: Math.floor(rect.width),
1082
+ height: Math.floor(rect.height),
1083
+ };
1084
+
1085
+ this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
1086
+ // take the screenshot
1087
+ await this.browser.screenshot(outputFile, button_clip);
1088
+ }
1089
+
1090
+ /**
1091
+ * {{> grabElementBoundingRect }}
1092
+ */
1093
+ async grabElementBoundingRect(locator, prop) {
1094
+ locator = new Locator(locator, 'css');
1095
+
1096
+ const rect = await this.browser.evaluate(async (by, locator) => {
1097
+ // store the button in a variable
1098
+
1099
+ const build_cluster_btn = await window.codeceptjs.findElement(by, locator);
1100
+
1101
+ // use the getClientRects() function on the button to determine
1102
+ // the size and location
1103
+ const rect = build_cluster_btn.getBoundingClientRect();
1104
+
1105
+ // convert the rectangle to a clip object and return it
1106
+ return {
1107
+ x: rect.left,
1108
+ y: rect.top,
1109
+ width: rect.width,
1110
+ height: rect.height,
1111
+ };
1112
+ }, locator.type, locator.value);
1113
+
1114
+ if (prop) return rect[prop];
1115
+ return rect;
1116
+ }
1117
+
1069
1118
  /**
1070
1119
  * {{> saveScreenshot }}
1071
1120
  */