codeceptjs 2.4.3 → 2.6.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 (76) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/README.md +32 -7
  3. package/bin/codecept.js +3 -0
  4. package/docs/basics.md +11 -5
  5. package/docs/bdd.md +4 -4
  6. package/docs/build/MockRequest.js +3 -0
  7. package/docs/build/Nightmare.js +10 -2
  8. package/docs/build/Playwright.js +3187 -0
  9. package/docs/build/Protractor.js +16 -2
  10. package/docs/build/Puppeteer.js +126 -19
  11. package/docs/build/REST.js +3 -1
  12. package/docs/build/TestCafe.js +11 -3
  13. package/docs/build/WebDriver.js +361 -28
  14. package/docs/changelog.md +116 -0
  15. package/docs/configuration.md +2 -2
  16. package/docs/custom-helpers.md +55 -10
  17. package/docs/helpers/Appium.md +81 -1
  18. package/docs/helpers/MockRequest.md +281 -38
  19. package/docs/helpers/Nightmare.md +10 -2
  20. package/docs/helpers/Playwright.md +1770 -0
  21. package/docs/helpers/Protractor.md +15 -3
  22. package/docs/helpers/Puppeteer-firefox.md +32 -1
  23. package/docs/helpers/Puppeteer.md +126 -76
  24. package/docs/helpers/TestCafe.md +10 -2
  25. package/docs/helpers/WebDriver.md +208 -118
  26. package/docs/locators.md +2 -0
  27. package/docs/playwright.md +306 -0
  28. package/docs/plugins.md +103 -0
  29. package/docs/reports.md +12 -0
  30. package/docs/shadow.md +68 -0
  31. package/docs/visual.md +0 -73
  32. package/docs/webapi/forceClick.mustache +27 -0
  33. package/docs/webapi/seeInPopup.mustache +7 -0
  34. package/docs/webapi/setCookie.mustache +10 -2
  35. package/docs/webapi/type.mustache +12 -0
  36. package/docs/webdriver.md +7 -3
  37. package/lib/codecept.js +1 -1
  38. package/lib/command/definitions.js +2 -2
  39. package/lib/command/generate.js +4 -4
  40. package/lib/command/gherkin/snippets.js +4 -4
  41. package/lib/command/init.js +1 -1
  42. package/lib/command/interactive.js +3 -0
  43. package/lib/command/run-multiple.js +2 -2
  44. package/lib/command/run-rerun.js +2 -0
  45. package/lib/command/run-workers.js +22 -8
  46. package/lib/command/run.js +2 -0
  47. package/lib/command/workers/runTests.js +1 -0
  48. package/lib/container.js +1 -1
  49. package/lib/event.js +2 -0
  50. package/lib/helper/MockRequest.js +3 -0
  51. package/lib/helper/Playwright.js +2422 -0
  52. package/lib/helper/Protractor.js +1 -2
  53. package/lib/helper/Puppeteer.js +84 -19
  54. package/lib/helper/REST.js +3 -1
  55. package/lib/helper/TestCafe.js +1 -1
  56. package/lib/helper/WebDriver.js +313 -26
  57. package/lib/helper/extras/PlaywrightPropEngine.js +53 -0
  58. package/lib/helper/scripts/isElementClickable.js +54 -14
  59. package/lib/interfaces/gherkin.js +1 -1
  60. package/lib/listener/helpers.js +3 -0
  61. package/lib/locator.js +5 -0
  62. package/lib/mochaFactory.js +12 -10
  63. package/lib/plugin/allure.js +8 -1
  64. package/lib/plugin/autoDelay.js +1 -8
  65. package/lib/plugin/commentStep.js +133 -0
  66. package/lib/plugin/screenshotOnFail.js +3 -10
  67. package/lib/plugin/selenoid.js +2 -2
  68. package/lib/plugin/standardActingHelpers.js +13 -0
  69. package/lib/plugin/stepByStepReport.js +1 -8
  70. package/lib/plugin/wdio.js +10 -1
  71. package/lib/reporter/cli.js +30 -1
  72. package/lib/session.js +7 -4
  73. package/package.json +13 -10
  74. package/typings/Mocha.d.ts +567 -16
  75. package/typings/index.d.ts +9 -5
  76. package/typings/types.d.ts +1634 -74
@@ -1054,8 +1054,7 @@ class Protractor extends Helper {
1054
1054
  }
1055
1055
 
1056
1056
  /**
1057
- * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the
1058
- * given string.
1057
+ * {{> seeInPopup }}
1059
1058
  */
1060
1059
  async seeInPopup(text) {
1061
1060
  const popupAlert = await this.browser.switchTo().alert();
@@ -36,7 +36,6 @@ const findReact = require('./extras/React');
36
36
 
37
37
  let puppeteer;
38
38
  let perfTiming;
39
- let isAuthenticated = false;
40
39
  const popupStore = new Popup();
41
40
  const consoleLogStore = new Console();
42
41
 
@@ -168,11 +167,14 @@ class Puppeteer extends Helper {
168
167
  constructor(config) {
169
168
  super(config);
170
169
 
171
- puppeteer = requireg(config.browser === 'firefox' ? 'puppeteer-firefox' : 'puppeteer');
170
+ puppeteer = requireg('puppeteer');
172
171
 
173
172
  // set defaults
174
173
  this.isRemoteBrowser = false;
175
174
  this.isRunning = false;
175
+ this.isAuthenticated = false;
176
+ this.sessionPages = {};
177
+ this.activeSessionName = '';
176
178
 
177
179
  // override defaults with config
178
180
  this._setConfig(config);
@@ -201,7 +203,7 @@ class Puppeteer extends Helper {
201
203
  }
202
204
 
203
205
  _getOptions(config) {
204
- return config.browser === 'firefox' ? this.options.firefox : this.options.chrome;
206
+ return config.browser === 'firefox' ? Object.assign(this.options.firefox, { product: 'firefox' }) : this.options.chrome;
205
207
  }
206
208
 
207
209
  _setConfig(config) {
@@ -220,6 +222,9 @@ class Puppeteer extends Helper {
220
222
  {
221
223
  name: 'show', message: 'Show browser window', default: true, type: 'confirm',
222
224
  },
225
+ {
226
+ name: 'windowSize', message: 'Browser viewport size', default: '1200x900',
227
+ },
223
228
  ];
224
229
  }
225
230
 
@@ -227,7 +232,7 @@ class Puppeteer extends Helper {
227
232
  try {
228
233
  requireg('puppeteer');
229
234
  } catch (e) {
230
- return ['puppeteer@^1.6.0'];
235
+ return ['puppeteer@^3.0.1'];
231
236
  }
232
237
  }
233
238
 
@@ -243,6 +248,7 @@ class Puppeteer extends Helper {
243
248
 
244
249
 
245
250
  async _before() {
251
+ this.sessionPages = {};
246
252
  recorder.retry({
247
253
  retries: 5,
248
254
  when: err => err.message.indexOf('context') > -1, // ignore context errors
@@ -257,7 +263,8 @@ class Puppeteer extends Helper {
257
263
 
258
264
  // close other sessions
259
265
  const contexts = this.browser.browserContexts();
260
- contexts.shift();
266
+ const defaultCtx = contexts.shift();
267
+
261
268
  await Promise.all(contexts.map(c => c.close()));
262
269
 
263
270
  if (this.options.restart) {
@@ -265,6 +272,12 @@ class Puppeteer extends Helper {
265
272
  return this._stopBrowser();
266
273
  }
267
274
 
275
+ // ensure this.page is from default context
276
+ if (this.page) {
277
+ const existingPages = defaultCtx.targets().filter(t => t.type() === 'page');
278
+ await this._setPage(await existingPages[0].page());
279
+ }
280
+
268
281
  if (this.options.keepBrowserState) return;
269
282
 
270
283
  if (!this.options.keepCookies) {
@@ -291,11 +304,13 @@ class Puppeteer extends Helper {
291
304
 
292
305
  _session() {
293
306
  return {
294
- start: async () => {
307
+ start: async (name = '', config) => {
295
308
  this.debugSection('Incognito Tab', 'opened');
309
+ this.activeSessionName = name;
296
310
 
297
311
  const bc = await this.browser.createIncognitoBrowserContext();
298
- await bc.newPage();
312
+ const page = await bc.newPage();
313
+
299
314
  // Create a new page inside context.
300
315
  return bc;
301
316
  },
@@ -304,12 +319,21 @@ class Puppeteer extends Helper {
304
319
  },
305
320
  loadVars: async (context) => {
306
321
  const existingPages = context.targets().filter(t => t.type() === 'page');
307
- return this._setPage(await existingPages[0].page());
322
+ this.sessionPages[this.activeSessionName] = await existingPages[0].page();
323
+ return this._setPage(this.sessionPages[this.activeSessionName]);
308
324
  },
309
- restoreVars: async () => {
325
+ restoreVars: async (session) => {
310
326
  this.withinLocator = null;
311
- const existingPages = await this.browser.targets().filter(t => t.type() === 'page');
327
+
328
+ if (!session) {
329
+ this.activeSessionName = '';
330
+ } else {
331
+ this.activeSessionName = session;
332
+ }
333
+ const defaultCtx = this.browser.defaultBrowserContext();
334
+ const existingPages = defaultCtx.targets().filter(t => t.type() === 'page');
312
335
  await this._setPage(await existingPages[0].page());
336
+
313
337
  return this._waitForAction();
314
338
  },
315
339
  };
@@ -361,8 +385,7 @@ class Puppeteer extends Helper {
361
385
  }
362
386
 
363
387
  /**
364
- * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the
365
- * given string.
388
+ * {{> seeInPopup }}
366
389
  */
367
390
  async seeInPopup(text) {
368
391
  popupStore.assertPopupVisible();
@@ -475,6 +498,7 @@ class Puppeteer extends Helper {
475
498
  this._setPage(null);
476
499
  this.context = null;
477
500
  popupStore.clear();
501
+ this.isAuthenticated = false;
478
502
  if (this.isRemoteBrowser) {
479
503
  await this.browser.disconnect();
480
504
  } else {
@@ -543,10 +567,10 @@ class Puppeteer extends Helper {
543
567
  url = this.options.url + url;
544
568
  }
545
569
 
546
- if (this.config.basicAuth && (isAuthenticated !== true)) {
570
+ if (this.config.basicAuth && (this.isAuthenticated !== true)) {
547
571
  if (url.includes(this.options.url)) {
548
- this.page.authenticate(this.config.basicAuth);
549
- isAuthenticated = true;
572
+ await this.page.authenticate(this.config.basicAuth);
573
+ this.isAuthenticated = true;
550
574
  }
551
575
  }
552
576
 
@@ -919,6 +943,36 @@ class Puppeteer extends Helper {
919
943
  return proceedClick.call(this, locator, context);
920
944
  }
921
945
 
946
+ /**
947
+ * {{> forceClick }}
948
+ *
949
+ * {{ react }}
950
+ */
951
+ async forceClick(locator, context = null) {
952
+ let matcher = await this.context;
953
+ if (context) {
954
+ const els = await this._locate(context);
955
+ assertElementExists(els, context);
956
+ matcher = els[0];
957
+ }
958
+
959
+ const els = await findClickable.call(this, matcher, locator);
960
+ if (context) {
961
+ assertElementExists(els, locator, 'Clickable element', `was not found inside element ${new Locator(context).toString()}`);
962
+ } else {
963
+ assertElementExists(els, locator, 'Clickable element');
964
+ }
965
+ const elem = els[0];
966
+ return this.executeScript((el) => {
967
+ if (document.activeElement instanceof HTMLElement) {
968
+ document.activeElement.blur();
969
+ }
970
+ const event = document.createEvent('MouseEvent');
971
+ event.initEvent('click', true, true);
972
+ return el.dispatchEvent(event);
973
+ }, elem);
974
+ }
975
+
922
976
  /**
923
977
  * {{> clickLink }}
924
978
  *
@@ -1185,6 +1239,8 @@ class Puppeteer extends Helper {
1185
1239
 
1186
1240
  /**
1187
1241
  * {{> attachFile }}
1242
+ *
1243
+ * > ⚠ There is an [issue with file upload in Puppeteer 2.1.0 & 2.1.1](https://github.com/puppeteer/puppeteer/issues/5420), downgrade to 2.0.0 if you face it.
1188
1244
  */
1189
1245
  async attachFile(locator, pathToFile) {
1190
1246
  const file = path.join(global.codecept_dir, pathToFile);
@@ -1614,10 +1670,19 @@ class Puppeteer extends Helper {
1614
1670
  const outputFile = screenshotOutputFolder(fileName);
1615
1671
 
1616
1672
  this.debug(`Screenshot is saving to ${outputFile}`);
1617
- const openSessions = await this.browser.pages();
1618
- if (openSessions.length > 1) {
1619
- this.page = await this.browser.targets()[this.browser.targets().length - 1].page();
1673
+
1674
+ if (this.activeSessionName) {
1675
+ const activeSessionPage = this.sessionPages[this.activeSessionName];
1676
+
1677
+ if (activeSessionPage) {
1678
+ return activeSessionPage.screenshot({
1679
+ path: outputFile,
1680
+ fullPage: fullPageOption,
1681
+ type: 'png',
1682
+ });
1683
+ }
1620
1684
  }
1685
+
1621
1686
  return this.page.screenshot({ path: outputFile, fullPage: fullPageOption, type: 'png' });
1622
1687
  }
1623
1688
 
@@ -1898,7 +1963,7 @@ class Puppeteer extends Helper {
1898
1963
  }, { timeout: waitTimeout }, locator.value, text, $XPath.toString());
1899
1964
  }
1900
1965
  } else {
1901
- waiter = contextObject.waitForFunction(text => document.body.innerText.indexOf(text) > -1, { timeout: waitTimeout }, text);
1966
+ waiter = contextObject.waitForFunction(text => document.body && document.body.innerText.indexOf(text) > -1, { timeout: waitTimeout }, text);
1902
1967
  }
1903
1968
 
1904
1969
  return waiter.catch((err) => {
@@ -75,7 +75,9 @@ class REST extends Helper {
75
75
  }
76
76
 
77
77
  if ((typeof request.data) === 'string') {
78
- request.headers = Object.assign(request.headers, { 'Content-Type': 'application/x-www-form-urlencoded' });
78
+ if (!request.headers || !request.headers['Content-Type']) {
79
+ request.headers = Object.assign(request.headers, { 'Content-Type': 'application/x-www-form-urlencoded' });
80
+ }
79
81
  }
80
82
 
81
83
  if (this.config.onRequest) {
@@ -1208,7 +1208,7 @@ async function proceedClick(locator, context = null) {
1208
1208
  await assertElementExists(els, locator, 'Clickable element');
1209
1209
  }
1210
1210
 
1211
- const firstElement = await els.nth(0);
1211
+ const firstElement = await els.filterVisible().nth(0);
1212
1212
 
1213
1213
  return this.t
1214
1214
  .click(firstElement)