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
@@ -1,7 +1,6 @@
1
1
  const requireg = require('requireg');
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
- const fsExtra = require('fs-extra');
5
4
 
6
5
  const Helper = require('../helper');
7
6
  const Locator = require('../locator');
@@ -11,7 +10,7 @@ const { urlEquals } = require('../assert/equal');
11
10
  const { equals } = require('../assert/equal');
12
11
  const { empty } = require('../assert/empty');
13
12
  const { truth } = require('../assert/truth');
14
- const isElementClickable = require('./scripts/isElementClickable');
13
+
15
14
  const {
16
15
  xpathLocator,
17
16
  ucfirst,
@@ -160,7 +159,7 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
160
159
  * }
161
160
  * ```
162
161
  *
163
- * #### Example #6: Lunach tests emulating iPhone 6
162
+ * #### Example #6: Launch tests emulating iPhone 6
164
163
  *
165
164
  *
166
165
  *
@@ -234,6 +233,7 @@ class Playwright extends Helper {
234
233
  keepBrowserState: false,
235
234
  show: false,
236
235
  defaultPopupAction: 'accept',
236
+ ignoreHTTPSErrors: false, // Adding it here o that context can be set up to ignore the SSL errors
237
237
  };
238
238
 
239
239
  config = Object.assign(defaults, config);
@@ -348,6 +348,9 @@ class Playwright extends Helper {
348
348
  await this.executeScript('localStorage.clear();').catch((err) => {
349
349
  if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
350
350
  });
351
+ await this.executeScript('sessionStorage.clear();').catch((err) => {
352
+ if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
353
+ });
351
354
  }
352
355
  // await this.closeOtherTabs();
353
356
  return this.browser;
@@ -539,7 +542,7 @@ class Playwright extends Helper {
539
542
  this.browser.on('targetchanged', (target) => {
540
543
  this.debugSection('Url', target.url());
541
544
  });
542
- this.browserContext = await this.browser.newContext({ acceptDownloads: true, ...this.options.emulate });
545
+ this.browserContext = await this.browser.newContext({ ignoreHTTPSErrors: this.options.ignoreHTTPSErrors, acceptDownloads: true, ...this.options.emulate });// Adding the HTTPSError ignore in the context so that we can ignore those errors
543
546
 
544
547
  const existingPages = await this.browserContext.pages();
545
548
 
@@ -691,7 +694,7 @@ class Playwright extends Helper {
691
694
  assertElementExists(els);
692
695
 
693
696
  // Use manual mouse.move instead of .hover() so the offset can be added to the coordinates
694
- const { x, y } = await els[0]._clickablePoint();
697
+ const { x, y } = await clickablePoint(els[0]);
695
698
  await this.page.mouse.move(x + offsetX, y + offsetY);
696
699
  return this._waitForAction();
697
700
  }
@@ -747,7 +750,7 @@ class Playwright extends Helper {
747
750
  const els = await this._locate(locator);
748
751
  assertElementExists(els, locator, 'Element');
749
752
  await els[0].scrollIntoViewIfNeeded();
750
- const elementCoordinates = await els[0]._clickablePoint();
753
+ const elementCoordinates = await clickablePoint(els[0]);
751
754
  await this.executeScript((offsetX, offsetY) => window.scrollBy(offsetX, offsetY), { offsetX: elementCoordinates.x + offsetX, offsetY: elementCoordinates.y + offsetY });
752
755
  } else {
753
756
  await this.executeScript(({ offsetX, offsetY }) => window.scrollTo(offsetX, offsetY), { offsetX, offsetY });
@@ -1141,7 +1144,7 @@ class Playwright extends Helper {
1141
1144
  /**
1142
1145
  * {{> pressKeyWithKeyNormalization }}
1143
1146
  *
1144
- * _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/Playwright#1313](https://github.com/GoogleChrome/Playwright/issues/1313)).
1147
+ * _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/Puppeteer#1313](https://github.com/GoogleChrome/puppeteer/issues/1313)).
1145
1148
  */
1146
1149
  async pressKey(key) {
1147
1150
  const modifiers = [];
@@ -1168,6 +1171,20 @@ class Playwright extends Helper {
1168
1171
  return this._waitForAction();
1169
1172
  }
1170
1173
 
1174
+ /**
1175
+ * {{> type }}
1176
+ */
1177
+ async type(keys, delay = null) {
1178
+ if (!Array.isArray(keys)) {
1179
+ keys = keys.split('');
1180
+ }
1181
+
1182
+ for (const key of keys) {
1183
+ await this.page.keyboard.press(key);
1184
+ if (delay) await this.wait(delay / 1000);
1185
+ }
1186
+ }
1187
+
1171
1188
  /**
1172
1189
  * {{> fillField }}
1173
1190
  *
@@ -1600,8 +1617,8 @@ class Playwright extends Helper {
1600
1617
  const src = await this._locate(locator);
1601
1618
  assertElementExists(src, locator, 'Slider Element');
1602
1619
 
1603
- // Note: Using private api ._clickablePoint because the .BoundingBox does not take into account iframe offsets!
1604
- const sliderSource = await src[0]._clickablePoint();
1620
+ // Note: Using clickablePoint private api because the .BoundingBox does not take into account iframe offsets!
1621
+ const sliderSource = await clickablePoint(src[0]);
1605
1622
 
1606
1623
  // Drag start point
1607
1624
  await this.page.mouse.move(sliderSource.x, sliderSource.y, { steps: 5 });
@@ -1631,6 +1648,21 @@ class Playwright extends Helper {
1631
1648
  return array.length === 1 ? array[0] : array;
1632
1649
  }
1633
1650
 
1651
+ /**
1652
+ * {{> saveElementScreenshot }}
1653
+ *
1654
+ */
1655
+ async saveElementScreenshot(locator, fileName) {
1656
+ const outputFile = screenshotOutputFolder(fileName);
1657
+
1658
+ const res = await this._locate(locator);
1659
+ assertElementExists(res, locator);
1660
+ if (res.length > 1) this.debug(`[Elements] Using first element out of ${res.length}`);
1661
+ const elem = res[0];
1662
+ this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
1663
+ return elem.screenshot({ path: outputFile, type: 'png' });
1664
+ }
1665
+
1634
1666
  /**
1635
1667
  * {{> saveScreenshot }}
1636
1668
  */
@@ -1962,7 +1994,7 @@ class Playwright extends Helper {
1962
1994
  /**
1963
1995
  * Waits for navigation to finish. By default takes configured `waitForNavigation` option.
1964
1996
  *
1965
- * See [Pupeteer's reference](https://github.com/GoogleChrome/Playwright/blob/master/docs/api.md#pagewaitfornavigationoptions)
1997
+ * See [Pupeteer's reference](https://github.com/microsoft/Playwright/blob/master/docs/api.md#pagewaitfornavigationoptions)
1966
1998
  *
1967
1999
  * @param {*} opts
1968
2000
  */
@@ -2068,7 +2100,14 @@ async function proceedClick(locator, context = null, options = {}) {
2068
2100
  } else {
2069
2101
  assertElementExists(els, locator, 'Clickable element');
2070
2102
  }
2071
- await els[0].click(options);
2103
+ /*
2104
+ using the force true options itself but instead dispatching a click
2105
+ */
2106
+ if (options.force) {
2107
+ await els[0].dispatchEvent('click');
2108
+ } else {
2109
+ await els[0].click(options);
2110
+ }
2072
2111
  const promises = [];
2073
2112
  if (options.waitForNavigation) {
2074
2113
  promises.push(this.waitForNavigation());
@@ -2190,9 +2229,9 @@ async function proceedDragAndDrop(sourceLocator, destinationLocator, options = {
2190
2229
  const dst = await this._locate(destinationLocator);
2191
2230
  assertElementExists(dst, destinationLocator, 'Destination Element');
2192
2231
 
2193
- // Note: Using private api ._clickablePoint becaues the .BoundingBox does not take into account iframe offsets!
2194
- const dragSource = await src[0]._clickablePoint();
2195
- const dragDestination = await dst[0]._clickablePoint();
2232
+ // Note: Using clickablePoint private api becaues the .BoundingBox does not take into account iframe offsets!
2233
+ const dragSource = await clickablePoint(src[0]);
2234
+ const dragDestination = await clickablePoint(dst[0]);
2196
2235
 
2197
2236
  // Drag start point
2198
2237
  await this.page.mouse.move(dragSource.x, dragSource.y, { steps: 5 });
@@ -2317,10 +2356,20 @@ function $XPath(element, selector) {
2317
2356
  async function targetCreatedHandler(page) {
2318
2357
  if (!page) return;
2319
2358
  this.withinLocator = null;
2320
- page.on('load', (frame) => {
2359
+ page.on('load', () => {
2321
2360
  page.$('body')
2322
2361
  .catch(() => null)
2323
- .then(context => this.context = context);
2362
+ .then(async context => {
2363
+ if (this.context._type === 'Frame') {
2364
+ // we are inside iframe?
2365
+ const frameEl = await this.context.frameElement();
2366
+ this.context = await frameEl.contentFrame();
2367
+ return;
2368
+ }
2369
+ // if context element was in iframe - keep it
2370
+ // if (await this.context.ownerFrame()) return;
2371
+ this.context = context;
2372
+ });
2324
2373
  });
2325
2374
  page.on('console', (msg) => {
2326
2375
  this.debugSection(`Browser:${ucfirst(msg.type())}`, (msg._text || '') + msg.args().join(' '));
@@ -2339,7 +2388,7 @@ async function targetCreatedHandler(page) {
2339
2388
  }
2340
2389
 
2341
2390
  // List of key values to key definitions
2342
- // https://github.com/GoogleChrome/Playwright/blob/v1.20.0/lib/USKeyboardLayout.js
2391
+ // https://github.com/puppeteer/puppeteer/blob/v1.20.0/lib/USKeyboardLayout.js
2343
2392
  const keyDefinitionMap = {
2344
2393
  /* eslint-disable quote-props */
2345
2394
  '0': 'Digit0',
@@ -2403,3 +2452,12 @@ function getNormalizedKey(key) {
2403
2452
  }
2404
2453
  return normalizedKey;
2405
2454
  }
2455
+
2456
+ async function clickablePoint(el) {
2457
+ const rect = await el.boundingBox();
2458
+ if (!rect) throw new ElementNotFound(el);
2459
+ const {
2460
+ x, y, width, height,
2461
+ } = rect;
2462
+ return { x: x + width / 2, y: y + height / 2 };
2463
+ }
@@ -10,8 +10,8 @@ const requireg = require('requireg');
10
10
  const Helper = require('../helper');
11
11
  const stringIncludes = require('../assert/include').includes;
12
12
  const { urlEquals, equals } = require('../assert/equal');
13
- const empty = require('../assert/empty').empty;
14
- const truth = require('../assert/truth').truth;
13
+ const { empty } = require('../assert/empty');
14
+ const { truth } = require('../assert/truth');
15
15
  const {
16
16
  xpathLocator,
17
17
  fileExists,
@@ -967,6 +967,30 @@ class Protractor extends Helper {
967
967
  return this.browser.getCurrentUrl().then(currentUrl => urlEquals(this.options.url).negate(url, currentUrl));
968
968
  }
969
969
 
970
+ /**
971
+ * {{> saveElementScreenshot }}
972
+ *
973
+ */
974
+ async saveElementScreenshot(locator, fileName) {
975
+ const outputFile = screenshotOutputFolder(fileName);
976
+
977
+ const writeFile = (png, outputFile) => {
978
+ const fs = require('fs');
979
+ const stream = fs.createWriteStream(outputFile);
980
+ stream.write(Buffer.from(png, 'base64'));
981
+ stream.end();
982
+ return new Promise(resolve => stream.on('finish', resolve));
983
+ };
984
+
985
+ const res = await this._locate(locator);
986
+ assertElementExists(res, locator);
987
+ if (res.length > 1) this.debug(`[Elements] Using first element out of ${res.length}`);
988
+ const elem = res[0];
989
+ this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
990
+ const png = await elem.takeScreenshot();
991
+ return writeFile(png, outputFile);
992
+ }
993
+
970
994
  /**
971
995
  * {{> saveScreenshot }}
972
996
  */
@@ -64,7 +64,7 @@ const consoleLogStore = new Console();
64
64
  * * `waitForAction`: (optional) how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
65
65
  * * `waitForNavigation`: (optional, default: 'load'). When to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle0`, `networkidle2`. See [Puppeteer API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions). Array values are accepted as well.
66
66
  * * `pressKeyDelay`: (optional, default: '10'). Delay between key presses in ms. Used when calling Puppeteers page.type(...) in fillField/appendField
67
- * * `getPageTimeout` (optional, default: '0') config option to set maximum navigation time in milliseconds.
67
+ * * `getPageTimeout` (optional, default: '30000') config option to set maximum navigation time in milliseconds. If the timeout is set to 0, then timeout will be disabled.
68
68
  * * `waitForTimeout`: (optional) default wait* timeout in ms. Default: 1000.
69
69
  * * `windowSize`: (optional) default window size. Set a dimension like `640x480`.
70
70
  * * `userAgent`: (optional) user-agent string.
@@ -190,7 +190,7 @@ class Puppeteer extends Helper {
190
190
  disableScreenshots: false,
191
191
  uniqueScreenshotNames: false,
192
192
  manualStart: false,
193
- getPageTimeout: 0,
193
+ getPageTimeout: 30000,
194
194
  waitForNavigation: 'load',
195
195
  restart: true,
196
196
  keepCookies: false,
@@ -295,6 +295,9 @@ class Puppeteer extends Helper {
295
295
  await this.executeScript('localStorage.clear();').catch((err) => {
296
296
  if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
297
297
  });
298
+ await this.executeScript('sessionStorage.clear();').catch((err) => {
299
+ if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
300
+ });
298
301
  }
299
302
  await this.closeOtherTabs();
300
303
  return this.browser;
@@ -1195,6 +1198,20 @@ class Puppeteer extends Helper {
1195
1198
  return this._waitForAction();
1196
1199
  }
1197
1200
 
1201
+ /**
1202
+ * {{> type }}
1203
+ */
1204
+ async type(keys, delay = null) {
1205
+ if (!Array.isArray(keys)) {
1206
+ keys = keys.split('');
1207
+ }
1208
+
1209
+ for (const key of keys) {
1210
+ await this.page.keyboard.press(key);
1211
+ if (delay) await this.wait(delay / 1000);
1212
+ }
1213
+ }
1214
+
1198
1215
  /**
1199
1216
  * {{> fillField }}
1200
1217
  * {{ react }}
@@ -1671,6 +1688,21 @@ class Puppeteer extends Helper {
1671
1688
  return array.length === 1 ? array[0] : array;
1672
1689
  }
1673
1690
 
1691
+ /**
1692
+ * {{> saveElementScreenshot }}
1693
+ *
1694
+ */
1695
+ async saveElementScreenshot(locator, fileName) {
1696
+ const outputFile = screenshotOutputFolder(fileName);
1697
+
1698
+ const res = await this._locate(locator);
1699
+ assertElementExists(res, locator);
1700
+ if (res.length > 1) this.debug(`[Elements] Using first element out of ${res.length}`);
1701
+ const elem = res[0];
1702
+ this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
1703
+ return elem.screenshot({ path: outputFile, type: 'png' });
1704
+ }
1705
+
1674
1706
  /**
1675
1707
  * {{> saveScreenshot }}
1676
1708
  */
@@ -731,6 +731,21 @@ class TestCafe extends Helper {
731
731
  stringIncludes('HTML source of a page').negate(text, source);
732
732
  }
733
733
 
734
+ /**
735
+ * {{> saveElementScreenshot }}
736
+ *
737
+ */
738
+ async saveElementScreenshot(locator, fileName) {
739
+ const outputFile = path.join(global.output_dir, fileName);
740
+
741
+ const sel = await findElements.call(this, this.context, locator);
742
+ assertElementExists(sel);
743
+ const firstElement = await sel.filterVisible().nth(0);
744
+
745
+ this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
746
+ return this.t.takeElementScreenshot(firstElement, fileName);
747
+ }
748
+
734
749
  /**
735
750
  * {{> saveScreenshot }}
736
751
  */
@@ -1530,6 +1530,21 @@ class WebDriver extends Helper {
1530
1530
  return elem.moveTo({ xOffset, yOffset });
1531
1531
  }
1532
1532
 
1533
+ /**
1534
+ * {{> saveElementScreenshot }}
1535
+ *
1536
+ */
1537
+ async saveElementScreenshot(locator, fileName) {
1538
+ const outputFile = screenshotOutputFolder(fileName);
1539
+
1540
+ const res = await this._locate(withStrictLocator(locator), true);
1541
+ assertElementExists(res, locator);
1542
+ const elem = usingFirstElement(res);
1543
+
1544
+ this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
1545
+ return elem.saveScreenshot(outputFile);
1546
+ }
1547
+
1533
1548
  /**
1534
1549
  * {{> saveScreenshot }}
1535
1550
  *
@@ -1755,14 +1770,19 @@ class WebDriver extends Helper {
1755
1770
 
1756
1771
  /**
1757
1772
  * {{> type }}
1758
- * Type out given array of keys or a string of text
1759
1773
  */
1760
- async type(keys) {
1761
- if (Array.isArray(keys)) {
1762
- await this.browser.keys(keys);
1774
+ async type(keys, delay = null) {
1775
+ if (!Array.isArray(keys)) {
1776
+ keys = keys.split('');
1777
+ }
1778
+ if (delay) {
1779
+ for (const key of keys) {
1780
+ await this.browser.keys(key);
1781
+ await this.wait(delay / 1000);
1782
+ }
1763
1783
  return;
1764
1784
  }
1765
- await this.browser.keys(keys.split(''));
1785
+ await this.browser.keys(keys);
1766
1786
  }
1767
1787
 
1768
1788
  /**
@@ -1819,12 +1839,24 @@ class WebDriver extends Helper {
1819
1839
 
1820
1840
  // for chrome
1821
1841
  if (browser.isW3C) {
1822
- return browser.performActions([
1823
- { type: 'pointerDown', button: 0 },
1824
- {
1825
- type: 'pointerMove', origin: 'pointer', duration: 1000, x: offsetX, y: 0,
1826
- },
1827
- { type: 'pointerUp', button: 0 },
1842
+ const xOffset = await this.grabElementBoundingRect(locator, 'x');
1843
+ const yOffset = await this.grabElementBoundingRect(locator, 'y');
1844
+
1845
+ return browser.performActions([{
1846
+ type: 'pointer',
1847
+ id: 'pointer1',
1848
+ parameters: { pointerType: 'mouse' },
1849
+ actions: [
1850
+ {
1851
+ type: 'pointerMove', origin: 'pointer', duration: 1000, x: xOffset, y: yOffset,
1852
+ },
1853
+ { type: 'pointerDown', button: 0 },
1854
+ {
1855
+ type: 'pointerMove', origin: 'pointer', duration: 1000, x: offsetX, y: 0,
1856
+ },
1857
+ { type: 'pointerUp', button: 0 },
1858
+ ],
1859
+ },
1828
1860
  ]);
1829
1861
  }
1830
1862
 
@@ -55,7 +55,7 @@ const pollyWebDriver = {
55
55
  throw new Error(`Invalid value for baseUrl: ${baseUrl}`);
56
56
  }
57
57
  if (!(typeof oneOrMoreUrls === 'string' || Array.isArray(oneOrMoreUrls))) {
58
- throw new Error(`Expected type of Urls is 'string' or 'array', Found '${typeof oneOrMoreUrls}'.`);
58
+ throw new Error(`Expected type of URLs is 'string' or 'array', found '${typeof oneOrMoreUrls}'.`);
59
59
  }
60
60
  // Remove '/' if it's at the end of baseUrl
61
61
  const lastChar = baseUrl.substr(-1);
package/lib/hooks.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const fsPath = require('path');
2
2
 
3
- const getParamNames = require('./utils').getParamNames;
4
- const fileExists = require('./utils').fileExists;
3
+ const { getParamNames, fileExists } = require('./utils');
5
4
  const output = require('./output');
6
5
 
7
6
  module.exports = function (hook, done, stage) {
@@ -2,7 +2,6 @@ const { Parser } = require('gherkin');
2
2
  const { Context, Suite, Test } = require('mocha');
3
3
 
4
4
  const { matchStep } = require('./bdd');
5
- const { isAsyncFunction } = require('../utils');
6
5
  const event = require('../event');
7
6
  const scenario = require('../scenario');
8
7
  const Step = require('../step');
@@ -2,8 +2,7 @@ const event = require('../event');
2
2
  const container = require('../container');
3
3
  const recorder = require('../recorder');
4
4
  const store = require('../store');
5
- const error = require('../output').error;
6
- const { deepMerge } = require('../utils');
5
+ const { error } = require('../output');
7
6
  /**
8
7
  * Enable Helpers to listen to test events
9
8
  */
@@ -1,7 +1,6 @@
1
1
  const event = require('../event');
2
2
  const container = require('../container');
3
3
  const recorder = require('../recorder');
4
- const error = require('../output').error;
5
4
 
6
5
  module.exports = function () {
7
6
  let mocha;
package/lib/locator.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const cssToXPath = require('css-to-xpath');
2
- const sprintf = require('sprintf-js').sprintf;
2
+ const { sprintf } = require('sprintf-js');
3
3
 
4
- const xpathLocator = require('./utils').xpathLocator;
4
+ const { xpathLocator } = require('./utils');
5
5
 
6
6
  const locatorTypes = ['css', 'by', 'xpath', 'id', 'name', 'fuzzy', 'frame'];
7
7
  /** @class */
package/lib/pause.js CHANGED
@@ -7,7 +7,7 @@ const store = require('./store');
7
7
  const recorder = require('./recorder');
8
8
  const event = require('./event');
9
9
  const output = require('./output');
10
- const methodsOfObject = require('./utils').methodsOfObject;
10
+ const { methodsOfObject } = require('./utils');
11
11
 
12
12
  // npm install colors
13
13
  let rl;
@@ -2,7 +2,7 @@ const Allure = require('allure-js-commons');
2
2
 
3
3
  const event = require('../event');
4
4
  const logger = require('../output');
5
- const ansiRegExp = require('../utils').ansiRegExp;
5
+ const { ansiRegExp } = require('../utils');
6
6
 
7
7
  const defaultConfig = {
8
8
  outputDir: global.output_dir,
@@ -2,9 +2,8 @@ const Container = require('../container');
2
2
  const store = require('../store');
3
3
  const recorder = require('../recorder');
4
4
  const event = require('../event');
5
- const log = require('../output').log;
6
-
7
- const supportedHelpers = require('./standardActingHelpers');
5
+ const { log } = require('../output');
6
+ const supportedHelpers = require('./standardActingHelpers').slice();
8
7
 
9
8
  const methodsToDelay = [
10
9
  'click',
@@ -60,6 +59,7 @@ const defaultConfig = {
60
59
  *
61
60
  */
62
61
  module.exports = function (config) {
62
+ supportedHelpers.push('REST');
63
63
  const helpers = Container.helpers();
64
64
  let helper;
65
65
 
@@ -4,7 +4,7 @@ const { fileExists } = require('../utils');
4
4
  const container = require('../container');
5
5
  const store = require('../store');
6
6
  const recorder = require('../recorder');
7
- const debug = require('../output').debug;
7
+ const { debug } = require('../output');
8
8
  const isAsyncFunction = require('../utils').isAsyncFunction;
9
9
 
10
10
  const defaultUser = {
@@ -86,7 +86,7 @@ module.exports = function (config) {
86
86
  } else {
87
87
  fileName += '.failed.png';
88
88
  }
89
- output.plugin('screenshotOnFail', 'Test failed, saving screenshot');
89
+ output.plugin('screenshotOnFail', 'Test failed, try to save a screenshot');
90
90
 
91
91
  try {
92
92
  if (options.reportDir) {
@@ -107,6 +107,7 @@ module.exports = function (config) {
107
107
  allureReporter.addAttachment('Last Seen Screenshot', fs.readFileSync(path.join(global.output_dir, fileName)), 'image/png');
108
108
  }
109
109
  } catch (err) {
110
+ output.plugin(err);
110
111
  if (
111
112
  err
112
113
  && err.type
@@ -1,14 +1,11 @@
1
1
  const standardActingHelpers = [
2
- 'Mochawesome',
3
2
  'WebDriver',
4
- 'WebDriverIO',
5
3
  'Protractor',
6
4
  'Appium',
7
5
  'Nightmare',
8
6
  'Puppeteer',
9
7
  'Playwright',
10
8
  'TestCafe',
11
- 'REST',
12
9
  ];
13
10
 
14
11
  module.exports = standardActingHelpers;
package/lib/recorder.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const debug = require('debug')('codeceptjs');
2
2
  const promiseRetry = require('promise-retry');
3
3
 
4
- const log = require('./output').log;
4
+ const { log } = require('./output');
5
5
 
6
6
  let promise;
7
7
  let running = false;
package/lib/step.js CHANGED
@@ -96,6 +96,9 @@ class Step {
96
96
  /** @return {string} */
97
97
  humanizeArgs() {
98
98
  return this.args.map((arg) => {
99
+ if (!arg) {
100
+ return '';
101
+ }
99
102
  if (typeof arg === 'string') {
100
103
  return `"${arg}"`;
101
104
  }
package/lib/ui.js CHANGED
@@ -99,6 +99,7 @@ module.exports = function (suite) {
99
99
 
100
100
  const suite = Suite.create(suites[0], title);
101
101
  if (!opts) opts = {};
102
+ suite.opts = opts;
102
103
  suite.timeout(0);
103
104
 
104
105
  if (opts.retries) suite.retries(opts.retries);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeceptjs",
3
- "version": "2.6.7",
3
+ "version": "2.6.11",
4
4
  "description": "Supercharged End 2 End Testing Framework for NodeJS",
5
5
  "keywords": [
6
6
  "acceptance",
@@ -9,6 +9,7 @@
9
9
  "puppeteer",
10
10
  "webdriver",
11
11
  "testcafe",
12
+ "playwright",
12
13
  "protractor",
13
14
  "bdd",
14
15
  "tdd",
@@ -110,7 +111,7 @@
110
111
  "mocha-parallel-tests": "^2.3.0",
111
112
  "nightmare": "^3.0.2",
112
113
  "nodemon": "^1.19.4",
113
- "playwright": "^1.1.1",
114
+ "playwright": "^1.4.0",
114
115
  "protractor": "^5.4.4",
115
116
  "puppeteer": "^4.0.0",
116
117
  "qrcode-terminal": "^0.12.0",