codeceptjs 3.3.6 → 3.3.7

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/docs/basics.md +2 -3
  2. package/docs/bdd.md +33 -0
  3. package/docs/best.md +8 -8
  4. package/docs/build/Appium.js +60 -22
  5. package/docs/build/GraphQL.js +6 -6
  6. package/docs/build/Nightmare.js +4 -4
  7. package/docs/build/Playwright.js +102 -39
  8. package/docs/build/Polly.js +0 -0
  9. package/docs/build/Protractor.js +25 -25
  10. package/docs/build/Puppeteer.js +7 -7
  11. package/docs/build/SeleniumWebdriver.js +0 -0
  12. package/docs/build/TestCafe.js +12 -12
  13. package/docs/build/WebDriver.js +32 -32
  14. package/docs/changelog.md +36 -1
  15. package/docs/helpers/Appium.md +103 -79
  16. package/docs/helpers/GraphQL.md +6 -6
  17. package/docs/helpers/Playwright.md +280 -245
  18. package/docs/helpers/Puppeteer.md +1 -1
  19. package/docs/helpers/REST.md +1 -1
  20. package/docs/helpers/WebDriver.md +2 -2
  21. package/docs/playwright.md +14 -0
  22. package/docs/quickstart.md +40 -13
  23. package/docs/translation.md +83 -56
  24. package/docs/typescript.md +49 -3
  25. package/docs/wiki/Books-&-Posts.md +0 -0
  26. package/docs/wiki/Community-Helpers-&-Plugins.md +0 -0
  27. package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +0 -0
  28. package/docs/wiki/Examples.md +0 -0
  29. package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +0 -0
  30. package/docs/wiki/Home.md +0 -0
  31. package/docs/wiki/Release-Process.md +0 -0
  32. package/docs/wiki/Roadmap.md +0 -0
  33. package/docs/wiki/Tests.md +0 -0
  34. package/docs/wiki/Upgrading-to-CodeceptJS-3.md +0 -0
  35. package/docs/wiki/Videos.md +0 -0
  36. package/lib/codecept.js +3 -1
  37. package/lib/command/definitions.js +26 -2
  38. package/lib/command/generate.js +23 -9
  39. package/lib/command/init.js +32 -7
  40. package/lib/command/run.js +5 -1
  41. package/lib/container.js +15 -15
  42. package/lib/helper/Appium.js +60 -22
  43. package/lib/helper/GraphQL.js +6 -6
  44. package/lib/helper/Nightmare.js +4 -4
  45. package/lib/helper/Playwright.js +102 -39
  46. package/lib/helper/Protractor.js +25 -25
  47. package/lib/helper/Puppeteer.js +7 -7
  48. package/lib/helper/TestCafe.js +12 -12
  49. package/lib/helper/WebDriver.js +32 -32
  50. package/lib/helper/errors/ElementNotFound.js +1 -1
  51. package/lib/helper/extras/PlaywrightRestartOpts.js +0 -2
  52. package/lib/interfaces/bdd.js +26 -1
  53. package/lib/listener/artifacts.js +19 -0
  54. package/lib/rerun.js +12 -13
  55. package/lib/step.js +5 -5
  56. package/lib/translation.js +32 -0
  57. package/package.json +11 -17
  58. package/translations/ru-RU.js +1 -0
  59. package/typings/index.d.ts +29 -1
  60. package/typings/promiseBasedTypes.d.ts +10466 -0
  61. package/typings/types.d.ts +62 -12
  62. package/CHANGELOG.md +0 -2340
@@ -448,7 +448,7 @@ class TestCafe extends Helper {
448
448
  */
449
449
  async moveCursorTo(locator, offsetX = 0, offsetY = 0) {
450
450
  const els = (await findElements.call(this, this.context, locator)).filterVisible();
451
- await assertElementExists(els);
451
+ await assertElementExists(els, locator);
452
452
 
453
453
  return this.t
454
454
  .hover(els.nth(0), { offsetX, offsetY })
@@ -485,7 +485,7 @@ class TestCafe extends Helper {
485
485
  matcher = await els.nth(0);
486
486
  }
487
487
  const els = (await findClickable.call(this, matcher, locator)).filterVisible();
488
- assertElementExists(els);
488
+ assertElementExists(els, locator);
489
489
  return this.t
490
490
  .rightClick(els.nth(0))
491
491
  .catch(mapError);
@@ -642,7 +642,7 @@ class TestCafe extends Helper {
642
642
  async seeElement(locator) {
643
643
  const exists = (await findElements.call(this, this.context, locator)).filterVisible().exists;
644
644
  return this.t
645
- .expect(exists).ok(`No element "${locator}" found`)
645
+ .expect(exists).ok(`No element "${(new Locator(locator))}" found`)
646
646
  .catch(mapError);
647
647
  }
648
648
 
@@ -652,7 +652,7 @@ class TestCafe extends Helper {
652
652
  async dontSeeElement(locator) {
653
653
  const exists = (await findElements.call(this, this.context, locator)).filterVisible().exists;
654
654
  return this.t
655
- .expect(exists).notOk(`Element "${locator}" is still visible`)
655
+ .expect(exists).notOk(`Element "${(new Locator(locator))}" is still visible`)
656
656
  .catch(mapError);
657
657
  }
658
658
 
@@ -662,7 +662,7 @@ class TestCafe extends Helper {
662
662
  async seeElementInDOM(locator) {
663
663
  const exists = (await findElements.call(this, this.context, locator)).exists;
664
664
  return this.t
665
- .expect(exists).ok(`No element "${locator}" found in DOM`)
665
+ .expect(exists).ok(`No element "${(new Locator(locator))}" found in DOM`)
666
666
  .catch(mapError);
667
667
  }
668
668
 
@@ -672,7 +672,7 @@ class TestCafe extends Helper {
672
672
  async dontSeeElementInDOM(locator) {
673
673
  const exists = (await findElements.call(this, this.context, locator)).exists;
674
674
  return this.t
675
- .expect(exists).notOk(`Element "${locator}" is still in DOM`)
675
+ .expect(exists).notOk(`Element "${(new Locator(locator))}" is still in DOM`)
676
676
  .catch(mapError);
677
677
  }
678
678
 
@@ -761,10 +761,10 @@ class TestCafe extends Helper {
761
761
  const outputFile = path.join(global.output_dir, fileName);
762
762
 
763
763
  const sel = await findElements.call(this, this.context, locator);
764
- assertElementExists(sel);
764
+ assertElementExists(sel, locator);
765
765
  const firstElement = await sel.filterVisible().nth(0);
766
766
 
767
- this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
767
+ this.debug(`Screenshot of ${(new Locator(locator))} element has been saved to ${outputFile}`);
768
768
  return this.t.takeElementScreenshot(firstElement, fileName);
769
769
  }
770
770
 
@@ -818,7 +818,7 @@ class TestCafe extends Helper {
818
818
  */
819
819
  async grabTextFrom(locator) {
820
820
  const sel = await findElements.call(this, this.context, locator);
821
- assertElementExists(sel);
821
+ assertElementExists(sel, locator);
822
822
  const texts = await this.grabTextFromAll(locator);
823
823
  if (texts.length > 1) {
824
824
  this.debugSection('GrabText', `Using first element out of ${texts.length}`);
@@ -846,7 +846,7 @@ class TestCafe extends Helper {
846
846
  */
847
847
  async grabAttributeFrom(locator, attr) {
848
848
  const sel = await findElements.call(this, this.context, locator);
849
- assertElementExists(sel);
849
+ assertElementExists(sel, locator);
850
850
  const attrs = await this.grabAttributeFromAll(locator, attr);
851
851
  if (attrs.length > 1) {
852
852
  this.debugSection('GrabAttribute', `Using first element out of ${attrs.length}`);
@@ -874,7 +874,7 @@ class TestCafe extends Helper {
874
874
  */
875
875
  async grabValueFrom(locator) {
876
876
  const sel = await findElements.call(this, this.context, locator);
877
- assertElementExists(sel);
877
+ assertElementExists(sel, locator);
878
878
  const values = await this.grabValueFromAll(locator);
879
879
  if (values.length > 1) {
880
880
  this.debugSection('GrabValue', `Using first element out of ${values.length}`);
@@ -1130,7 +1130,7 @@ class TestCafe extends Helper {
1130
1130
 
1131
1131
  return this.t
1132
1132
  .expect(createSelector(locator).with({ boundTestRun: this.t }).filterVisible().count)
1133
- .eql(num, `The number of elements (${locator}) is not ${num} after ${sec} sec`, { timeout: waitTimeout })
1133
+ .eql(num, `The number of elements (${(new Locator(locator))}) is not ${num} after ${sec} sec`, { timeout: waitTimeout })
1134
1134
  .catch(mapError);
1135
1135
  }
1136
1136
 
@@ -46,7 +46,7 @@ let version;
46
46
  * @prop {string} [host=localhost] - WebDriver host to connect.
47
47
  * @prop {number} [port=4444] - WebDriver port to connect.
48
48
  * @prop {string} [protocol=http] - protocol for WebDriver server.
49
- * @prop {string} [path=/wd/hub] - path to WebDriver server,
49
+ * @prop {string} [path=/wd/hub] - path to WebDriver server.
50
50
  * @prop {boolean} [restart=true] - restart browser between tests.
51
51
  * @prop {boolean|number} [smartWait=false] - **enables [SmartWait](http://codecept.io/acceptance/#smartwait)**; wait for additional milliseconds for element to appear. Enable for 5 secs: "smartWait": 5000.
52
52
  * @prop {boolean} [disableScreenshots=false] - don't save screenshots on failure.
@@ -463,7 +463,7 @@ class WebDriver extends Helper {
463
463
  delete config.capabilities.selenoidOptions;
464
464
  }
465
465
 
466
- config.waitForTimeout /= 1000; // convert to seconds
466
+ config.waitForTimeoutInSeconds = config.waitForTimeout / 1000; // convert to seconds
467
467
 
468
468
  if (!config.capabilities.platformName && (!config.url || !config.browser)) {
469
469
  throw new Error(`
@@ -1201,7 +1201,7 @@ class WebDriver extends Helper {
1201
1201
  */
1202
1202
  async grabHTMLFrom(locator) {
1203
1203
  const html = await this.grabHTMLFromAll(locator);
1204
- assertElementExists(html);
1204
+ assertElementExists(html, locator);
1205
1205
  if (html.length > 1) {
1206
1206
  this.debugSection('GrabHTML', `Using first element out of ${html.length}`);
1207
1207
  }
@@ -1380,7 +1380,7 @@ class WebDriver extends Helper {
1380
1380
  const res = await this._locate(locator, true);
1381
1381
  assertElementExists(res, locator);
1382
1382
  const selected = await forEachAsync(res, async el => el.isDisplayed());
1383
- return truth(`elements of ${locator}`, 'to be seen').assert(selected);
1383
+ return truth(`elements of ${(new Locator(locator))}`, 'to be seen').assert(selected);
1384
1384
  }
1385
1385
 
1386
1386
  /**
@@ -1390,10 +1390,10 @@ class WebDriver extends Helper {
1390
1390
  async dontSeeElement(locator) {
1391
1391
  const res = await this._locate(locator, false);
1392
1392
  if (!res || res.length === 0) {
1393
- return truth(`elements of ${locator}`, 'to be seen').negate(false);
1393
+ return truth(`elements of ${(new Locator(locator))}`, 'to be seen').negate(false);
1394
1394
  }
1395
1395
  const selected = await forEachAsync(res, async el => el.isDisplayed());
1396
- return truth(`elements of ${locator}`, 'to be seen').negate(selected);
1396
+ return truth(`elements of ${(new Locator(locator))}`, 'to be seen').negate(selected);
1397
1397
  }
1398
1398
 
1399
1399
  /**
@@ -1465,7 +1465,7 @@ class WebDriver extends Helper {
1465
1465
  */
1466
1466
  async seeNumberOfElements(locator, num) {
1467
1467
  const res = await this._locate(locator);
1468
- return assert.equal(res.length, num, `expected number of elements (${locator}) is ${num}, but found ${res.length}`);
1468
+ return assert.equal(res.length, num, `expected number of elements (${(new Locator(locator))}) is ${num}, but found ${res.length}`);
1469
1469
  }
1470
1470
 
1471
1471
  /**
@@ -1474,7 +1474,7 @@ class WebDriver extends Helper {
1474
1474
  */
1475
1475
  async seeNumberOfVisibleElements(locator, num) {
1476
1476
  const res = await this.grabNumberOfVisibleElements(locator);
1477
- return assert.equal(res, num, `expected number of visible elements (${locator}) is ${num}, but found ${res}`);
1477
+ return assert.equal(res, num, `expected number of visible elements (${(new Locator(locator))}) is ${num}, but found ${res}`);
1478
1478
  }
1479
1479
 
1480
1480
  /**
@@ -1508,7 +1508,7 @@ class WebDriver extends Helper {
1508
1508
  });
1509
1509
  return assert.ok(
1510
1510
  chunked.length === elemAmount,
1511
- `expected all elements (${locator}) to have CSS property ${JSON.stringify(cssProperties)}`,
1511
+ `expected all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`,
1512
1512
  );
1513
1513
  }
1514
1514
 
@@ -1535,7 +1535,7 @@ class WebDriver extends Helper {
1535
1535
  });
1536
1536
  return assert.ok(
1537
1537
  chunked.length === elemAmount,
1538
- `expected all elements (${locator}) to have attributes ${JSON.stringify(attributes)}`,
1538
+ `expected all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`,
1539
1539
  );
1540
1540
  }
1541
1541
 
@@ -1611,7 +1611,7 @@ class WebDriver extends Helper {
1611
1611
  */
1612
1612
  async scrollIntoView(locator, scrollIntoViewOptions) {
1613
1613
  const res = await this._locate(withStrictLocator(locator), true);
1614
- assertElementExists(res);
1614
+ assertElementExists(res, locator);
1615
1615
  const elem = usingFirstElement(res);
1616
1616
  return elem.scrollIntoView(scrollIntoViewOptions);
1617
1617
  }
@@ -1629,12 +1629,12 @@ class WebDriver extends Helper {
1629
1629
 
1630
1630
  if (locator) {
1631
1631
  const res = await this._locate(withStrictLocator(locator), true);
1632
- assertElementExists(res);
1632
+ assertElementExists(res, locator);
1633
1633
  const elem = usingFirstElement(res);
1634
1634
  const elementId = getElementId(elem);
1635
1635
  if (this.browser.isMobile && !this.browser.isW3C) return this.browser.touchScroll(offsetX, offsetY, elementId);
1636
1636
  const location = await elem.getLocation();
1637
- assertElementExists(location, 'Failed to receive', 'location');
1637
+ assertElementExists(location, locator, 'Failed to receive', 'location');
1638
1638
  /* eslint-disable prefer-arrow-callback */
1639
1639
  return this.browser.execute(function (x, y) { return window.scrollTo(x, y); }, location.x + offsetX, location.y + offsetY);
1640
1640
  /* eslint-enable */
@@ -1669,7 +1669,7 @@ class WebDriver extends Helper {
1669
1669
  assertElementExists(res, locator);
1670
1670
  const elem = usingFirstElement(res);
1671
1671
 
1672
- this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
1672
+ this.debug(`Screenshot of ${(new Locator(locator))} element has been saved to ${outputFile}`);
1673
1673
  return elem.saveScreenshot(outputFile);
1674
1674
  }
1675
1675
 
@@ -1939,11 +1939,11 @@ class WebDriver extends Helper {
1939
1939
  */
1940
1940
  async dragAndDrop(srcElement, destElement) {
1941
1941
  let sourceEl = await this._locate(srcElement);
1942
- assertElementExists(sourceEl);
1942
+ assertElementExists(sourceEl, srcElement);
1943
1943
  sourceEl = usingFirstElement(sourceEl);
1944
1944
 
1945
1945
  let destEl = await this._locate(destElement);
1946
- assertElementExists(destEl);
1946
+ assertElementExists(destEl, destElement);
1947
1947
  destEl = usingFirstElement(destEl);
1948
1948
 
1949
1949
  return sourceEl.dragAndDrop(destEl);
@@ -2042,7 +2042,7 @@ class WebDriver extends Helper {
2042
2042
  * {{> waitForEnabled }}
2043
2043
  */
2044
2044
  async waitForEnabled(locator, sec = null) {
2045
- const aSec = sec || this.options.waitForTimeout;
2045
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2046
2046
  if (isWebDriver5()) {
2047
2047
  return this.browser.waitUntil(async () => {
2048
2048
  const res = await this.$$(withStrictLocator(locator));
@@ -2076,24 +2076,24 @@ class WebDriver extends Helper {
2076
2076
  * {{> waitForElement }}
2077
2077
  */
2078
2078
  async waitForElement(locator, sec = null) {
2079
- const aSec = sec || this.options.waitForTimeout;
2079
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2080
2080
  if (isWebDriver5()) {
2081
2081
  return this.browser.waitUntil(async () => {
2082
2082
  const res = await this.$$(withStrictLocator(locator));
2083
2083
  return res && res.length;
2084
- }, aSec * 1000, `element (${locator}) still not present on page after ${aSec} sec`);
2084
+ }, aSec * 1000, `element (${(new Locator(locator))}) still not present on page after ${aSec} sec`);
2085
2085
  }
2086
2086
  return this.browser.waitUntil(async () => {
2087
2087
  const res = await this._res(locator);
2088
2088
  return res && res.length;
2089
- }, { timeout: aSec * 1000, timeoutMsg: `element (${locator}) still not present on page after ${aSec} sec` });
2089
+ }, { timeout: aSec * 1000, timeoutMsg: `element (${(new Locator(locator))}) still not present on page after ${aSec} sec` });
2090
2090
  }
2091
2091
 
2092
2092
  /**
2093
2093
  * {{> waitForClickable }}
2094
2094
  */
2095
2095
  async waitForClickable(locator, waitTimeout) {
2096
- waitTimeout = waitTimeout || this.options.waitForTimeout;
2096
+ waitTimeout = waitTimeout || this.options.waitForTimeoutInSeconds;
2097
2097
  let res = await this._locate(locator);
2098
2098
  res = usingFirstElement(res);
2099
2099
  assertElementExists(res, locator);
@@ -2109,7 +2109,7 @@ class WebDriver extends Helper {
2109
2109
  */
2110
2110
  async waitInUrl(urlPart, sec = null) {
2111
2111
  const client = this.browser;
2112
- const aSec = sec || this.options.waitForTimeout;
2112
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2113
2113
  let currUrl = '';
2114
2114
  if (isWebDriver5()) {
2115
2115
  return client
@@ -2143,7 +2143,7 @@ class WebDriver extends Helper {
2143
2143
  * {{> waitUrlEquals }}
2144
2144
  */
2145
2145
  async waitUrlEquals(urlPart, sec = null) {
2146
- const aSec = sec || this.options.waitForTimeout;
2146
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2147
2147
  const baseUrl = this.options.url;
2148
2148
  if (urlPart.indexOf('http') < 0) {
2149
2149
  urlPart = baseUrl + urlPart;
@@ -2167,7 +2167,7 @@ class WebDriver extends Helper {
2167
2167
  *
2168
2168
  */
2169
2169
  async waitForText(text, sec = null, context = null) {
2170
- const aSec = sec || this.options.waitForTimeout;
2170
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2171
2171
  const _context = context || this.root;
2172
2172
  if (isWebDriver5()) {
2173
2173
  return this.browser.waitUntil(
@@ -2205,7 +2205,7 @@ class WebDriver extends Helper {
2205
2205
  */
2206
2206
  async waitForValue(field, value, sec = null) {
2207
2207
  const client = this.browser;
2208
- const aSec = sec || this.options.waitForTimeout;
2208
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2209
2209
  if (isWebDriver5()) {
2210
2210
  return client.waitUntil(
2211
2211
  async () => {
@@ -2241,7 +2241,7 @@ class WebDriver extends Helper {
2241
2241
  *
2242
2242
  */
2243
2243
  async waitForVisible(locator, sec = null) {
2244
- const aSec = sec || this.options.waitForTimeout;
2244
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2245
2245
  if (isWebDriver5()) {
2246
2246
  return this.browser.waitUntil(async () => {
2247
2247
  const res = await this.$$(withStrictLocator(locator));
@@ -2268,7 +2268,7 @@ class WebDriver extends Helper {
2268
2268
  * {{> waitNumberOfVisibleElements }}
2269
2269
  */
2270
2270
  async waitNumberOfVisibleElements(locator, num, sec = null) {
2271
- const aSec = sec || this.options.waitForTimeout;
2271
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2272
2272
  if (isWebDriver5()) {
2273
2273
  return this.browser.waitUntil(async () => {
2274
2274
  const res = await this.$$(withStrictLocator(locator));
@@ -2295,7 +2295,7 @@ class WebDriver extends Helper {
2295
2295
  * {{> waitForInvisible }}
2296
2296
  */
2297
2297
  async waitForInvisible(locator, sec = null) {
2298
- const aSec = sec || this.options.waitForTimeout;
2298
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2299
2299
  if (isWebDriver5()) {
2300
2300
  return this.browser.waitUntil(async () => {
2301
2301
  const res = await this.$$(withStrictLocator(locator));
@@ -2323,7 +2323,7 @@ class WebDriver extends Helper {
2323
2323
  * {{> waitForDetached }}
2324
2324
  */
2325
2325
  async waitForDetached(locator, sec = null) {
2326
- const aSec = sec || this.options.waitForTimeout;
2326
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2327
2327
  if (isWebDriver5()) {
2328
2328
  return this.browser.waitUntil(async () => {
2329
2329
  const res = await this._res(locator);
@@ -2355,7 +2355,7 @@ class WebDriver extends Helper {
2355
2355
  }
2356
2356
  }
2357
2357
 
2358
- const aSec = sec || this.options.waitForTimeout;
2358
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2359
2359
  if (isWebDriver5()) {
2360
2360
  return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), aSec * 1000, '');
2361
2361
  }
@@ -2384,7 +2384,7 @@ class WebDriver extends Helper {
2384
2384
  * {{> switchToNextTab }}
2385
2385
  */
2386
2386
  async switchToNextTab(num = 1, sec = null) {
2387
- const aSec = sec || this.options.waitForTimeout;
2387
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2388
2388
  let target;
2389
2389
  const current = await this.browser.getWindowHandle();
2390
2390
 
@@ -2415,7 +2415,7 @@ class WebDriver extends Helper {
2415
2415
  * {{> switchToPreviousTab }}
2416
2416
  */
2417
2417
  async switchToPreviousTab(num = 1, sec = null) {
2418
- const aSec = sec || this.options.waitForTimeout;
2418
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2419
2419
  const current = await this.browser.getWindowHandle();
2420
2420
  let target;
2421
2421
 
@@ -11,7 +11,7 @@ class ElementNotFound {
11
11
  if (typeof locator === 'object') {
12
12
  locator = JSON.stringify(locator);
13
13
  }
14
- throw new Error(`${prefixMessage} "${(new Locator(locator)).toString()}" ${postfixMessage}`);
14
+ throw new Error(`${prefixMessage} "${(new Locator(locator))}" ${postfixMessage}`);
15
15
  }
16
16
  }
17
17
 
@@ -18,8 +18,6 @@ module.exports = {
18
18
 
19
19
  restarts = Object.keys(RESTART_OPTS).find(key => RESTART_OPTS[key] === restart);
20
20
 
21
- console.log(restarts);
22
-
23
21
  if (restarts === null || restarts === undefined) throw new Error('No restart strategy set, use the following values for restart: session, context, browser');
24
22
  },
25
23
 
@@ -1,4 +1,4 @@
1
- const { CucumberExpression, ParameterTypeRegistry } = require('cucumber-expressions');
1
+ const { CucumberExpression, ParameterTypeRegistry, ParameterType } = require('cucumber-expressions');
2
2
  const Config = require('../config');
3
3
 
4
4
  let steps = {};
@@ -58,6 +58,30 @@ const getSteps = () => {
58
58
  return steps;
59
59
  };
60
60
 
61
+ const defineParameterType = (options) => {
62
+ const parameterType = buildParameterType(options);
63
+ parameterTypeRegistry.defineParameterType(parameterType);
64
+ };
65
+
66
+ const buildParameterType = ({
67
+ name,
68
+ regexp,
69
+ transformer,
70
+ useForSnippets,
71
+ preferForRegexpMatch,
72
+ }) => {
73
+ if (typeof useForSnippets !== 'boolean') useForSnippets = true;
74
+ if (typeof preferForRegexpMatch !== 'boolean') preferForRegexpMatch = false;
75
+ return new ParameterType(
76
+ name,
77
+ regexp,
78
+ null,
79
+ transformer,
80
+ useForSnippets,
81
+ preferForRegexpMatch,
82
+ );
83
+ };
84
+
61
85
  module.exports = {
62
86
  Given: addStep,
63
87
  When: addStep,
@@ -66,4 +90,5 @@ module.exports = {
66
90
  matchStep,
67
91
  getSteps,
68
92
  clearSteps,
93
+ defineParameterType,
69
94
  };
@@ -0,0 +1,19 @@
1
+ const event = require('../event');
2
+ const recorder = require('../recorder');
3
+
4
+ /**
5
+ * Create and clean up empty artifacts
6
+ */
7
+ module.exports = function () {
8
+ event.dispatcher.on(event.test.before, (test) => {
9
+ test.artifacts = {};
10
+ });
11
+
12
+ event.dispatcher.on(event.test.after, (test) => {
13
+ recorder.add('clean up empty artifacts', () => {
14
+ for (const key in (test.artifacts || {})) {
15
+ if (!test.artifacts[key]) delete test.artifacts[key];
16
+ }
17
+ });
18
+ });
19
+ };
package/lib/rerun.js CHANGED
@@ -38,7 +38,10 @@ class CodeceptRerunner extends BaseCodecept {
38
38
  const configRerun = this.config.rerun || {};
39
39
  const minSuccess = configRerun.minSuccess || 1;
40
40
  const maxReruns = configRerun.maxReruns || 1;
41
- if (minSuccess > maxReruns) throw new Error('minSuccess must be less than maxReruns');
41
+ if (minSuccess > maxReruns) {
42
+ process.exitCode = 1;
43
+ throw new Error(`run-rerun Configuration Error: minSuccess must be less than maxReruns. Current values: minSuccess=${minSuccess} maxReruns=${maxReruns}`);
44
+ }
42
45
  if (maxReruns === 1) {
43
46
  await this.runOnce(test);
44
47
  return;
@@ -61,20 +64,16 @@ class CodeceptRerunner extends BaseCodecept {
61
64
  }
62
65
  }
63
66
 
64
- run(test) {
65
- const done = () => {
67
+ async run(test) {
68
+ event.emit(event.all.before, this);
69
+ try {
70
+ await this.runTests(test);
71
+ } catch (e) {
72
+ output.error(e.stack);
73
+ } finally {
66
74
  event.emit(event.all.result, this);
67
75
  event.emit(event.all.after, this);
68
- };
69
- event.emit(event.all.before, this);
70
- return this.runTests(test)
71
- .then(() => {
72
- this.teardown(done);
73
- })
74
- .catch((e) => {
75
- this.teardown(done);
76
- throw e;
77
- });
76
+ }
78
77
  }
79
78
  }
80
79
 
package/lib/step.js CHANGED
@@ -268,13 +268,13 @@ class MetaStep extends Step {
268
268
  step.metaStep = this;
269
269
  };
270
270
  event.dispatcher.prependListener(event.step.before, registerStep);
271
- let rethrownError = null;
271
+ // Handle async and sync methods.
272
272
  if (fn.constructor.name === 'AsyncFunction') {
273
273
  result = fn.apply(this.context, this.args).then((result) => {
274
274
  return result;
275
- }).catch(function (error) {
275
+ }).catch((error) => {
276
276
  this.setStatus('failed');
277
- rethrownError = error;
277
+ throw error;
278
278
  }).finally(() => {
279
279
  this.endTime = Date.now();
280
280
  event.dispatcher.removeListener(event.step.before, registerStep);
@@ -285,13 +285,13 @@ class MetaStep extends Step {
285
285
  result = fn.apply(this.context, this.args);
286
286
  } catch (error) {
287
287
  this.setStatus('failed');
288
- rethrownError = error;
288
+ throw error;
289
289
  } finally {
290
290
  this.endTime = Date.now();
291
291
  event.dispatcher.removeListener(event.step.before, registerStep);
292
292
  }
293
293
  }
294
- if (rethrownError) { throw rethrownError; }
294
+
295
295
  return result;
296
296
  }
297
297
  }
@@ -1,9 +1,29 @@
1
+ const merge = require('lodash.merge');
2
+ const path = require('path');
3
+
4
+ const defaultVocabulary = {
5
+ I: 'I',
6
+ actions: {},
7
+ };
8
+
1
9
  class Translation {
2
10
  constructor(vocabulary, loaded) {
3
11
  this.vocabulary = vocabulary;
4
12
  this.loaded = loaded !== false;
5
13
  }
6
14
 
15
+ loadVocabulary(vocabularyFile) {
16
+ if (!vocabularyFile) return;
17
+ const filePath = path.join(global.codecept_dir, vocabularyFile);
18
+
19
+ try {
20
+ const vocabulary = require(filePath);
21
+ this.vocabulary = merge(this.vocabulary, vocabulary);
22
+ } catch (err) {
23
+ throw new Error(`Can't load vocabulary from ${filePath}; ${err}`);
24
+ }
25
+ }
26
+
7
27
  value(val) {
8
28
  return this.vocabulary[val];
9
29
  }
@@ -18,6 +38,18 @@ class Translation {
18
38
  get I() {
19
39
  return this.vocabulary.I;
20
40
  }
41
+
42
+ static get langs() {
43
+ return require('../translations');
44
+ }
45
+
46
+ static createDefault() {
47
+ return new Translation(defaultVocabulary, true);
48
+ }
49
+
50
+ static createEmpty() {
51
+ return new Translation(defaultVocabulary, false);
52
+ }
21
53
  }
22
54
 
23
55
  module.exports = Translation;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeceptjs",
3
- "version": "3.3.6",
3
+ "version": "3.3.7",
4
4
  "description": "Supercharged End 2 End Testing Framework for NodeJS",
5
5
  "keywords": [
6
6
  "acceptance",
@@ -42,7 +42,7 @@
42
42
  "lint-fix": "eslint bin/ examples/ lib/ test/ translations/ runok.js --fix",
43
43
  "docs": "./runok.js docs",
44
44
  "test:unit": "mocha test/unit --recursive",
45
- "test:runner": "mocha test/runner --recursive",
45
+ "test:runner": "mocha test/runner --recursive --timeout 5000",
46
46
  "test": "npm run test:unit && npm run test:runner",
47
47
  "test:appium-quick": "mocha test/helper/Appium_test.js --grep 'quick'",
48
48
  "test:appium-other": "mocha test/helper/Appium_test.js --grep 'second'",
@@ -50,7 +50,8 @@
50
50
  "dev:graphql": "nodemon test/data/graphql/index.js",
51
51
  "publish:site": "./runok.js publish:site",
52
52
  "update-contributor-faces": "contributor-faces .",
53
- "dtslint": "dtslint typings --localTs './node_modules/typescript/lib'"
53
+ "dtslint": "dtslint typings --localTs './node_modules/typescript/lib'",
54
+ "prepare": "husky install"
54
55
  },
55
56
  "dependencies": {
56
57
  "@codeceptjs/configure": "^0.8.0",
@@ -86,8 +87,8 @@
86
87
  "promise-retry": "^1.1.1",
87
88
  "requireg": "^0.2.2",
88
89
  "resq": "^1.10.2",
89
- "sprintf-js": "^1.1.1",
90
90
  "semver": "^6.3.0",
91
+ "sprintf-js": "^1.1.1",
91
92
  "uuid": "^8.3.2"
92
93
  },
93
94
  "devDependencies": {
@@ -116,7 +117,7 @@
116
117
  "express": "^4.17.2",
117
118
  "form-data": "^3.0.1",
118
119
  "graphql": "^14.6.0",
119
- "husky": "^4.3.8",
120
+ "husky": "^8.0.1",
120
121
  "jsdoc": "^3.6.10",
121
122
  "jsdoc-typeof-plugin": "^1.0.0",
122
123
  "json-server": "^0.10.1",
@@ -132,28 +133,21 @@
132
133
  "sinon-chai": "^3.7.0",
133
134
  "testcafe": "^1.18.3",
134
135
  "ts-morph": "^3.1.3",
136
+ "ts-node": "^10.9.1",
135
137
  "tsd-jsdoc": "https://github.com/englercj/tsd-jsdoc.git",
136
138
  "typedoc": "^0.23.10",
137
139
  "typedoc-plugin-markdown": "^3.13.4",
138
- "typescript": "^4.4.3",
140
+ "typescript": "^4.8.4",
139
141
  "wdio-docker-service": "^1.5.0",
140
142
  "webdriverio": "^7.16.14",
141
143
  "xml2js": "^0.4.23",
142
144
  "xmldom": "^0.1.31",
143
- "xpath": "0.0.27"
145
+ "xpath": "0.0.27",
146
+ "inquirer-test": "^2.0.1"
144
147
  },
145
148
  "engines": {
146
149
  "node": ">=8.9.1",
147
150
  "npm": ">=5.6.0"
148
151
  },
149
- "peerDependencies": {
150
- "@faker-js/faker": "^5.5.3"
151
- },
152
- "es6": true,
153
- "husky": {
154
- "hooks": {
155
- "pre-commit": "npm run lint",
156
- "pre-push": "npm run test:unit"
157
- }
158
- }
152
+ "es6": true
159
153
  }
@@ -9,6 +9,7 @@ module.exports = {
9
9
  AfterSuite: 'После_всего',
10
10
  },
11
11
  actions: {
12
+ say: 'сообщаю',
12
13
  waitForElement: 'ожидаю_элемент',
13
14
  waitForVisible: 'ожидаю_увидеть',
14
15
  waitForText: 'ожидаю_текст',