codeceptjs 3.3.7-beta.1 → 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 -41
  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
@@ -575,7 +575,7 @@ class TestCafe extends Helper {
575
575
  */
576
576
  async moveCursorTo(locator, offsetX = 0, offsetY = 0) {
577
577
  const els = (await findElements.call(this, this.context, locator)).filterVisible();
578
- await assertElementExists(els);
578
+ await assertElementExists(els, locator);
579
579
 
580
580
  return this.t
581
581
  .hover(els.nth(0), { offsetX, offsetY })
@@ -639,7 +639,7 @@ class TestCafe extends Helper {
639
639
  matcher = await els.nth(0);
640
640
  }
641
641
  const els = (await findClickable.call(this, matcher, locator)).filterVisible();
642
- assertElementExists(els);
642
+ assertElementExists(els, locator);
643
643
  return this.t
644
644
  .rightClick(els.nth(0))
645
645
  .catch(mapError);
@@ -926,7 +926,7 @@ class TestCafe extends Helper {
926
926
  async seeElement(locator) {
927
927
  const exists = (await findElements.call(this, this.context, locator)).filterVisible().exists;
928
928
  return this.t
929
- .expect(exists).ok(`No element "${locator}" found`)
929
+ .expect(exists).ok(`No element "${(new Locator(locator))}" found`)
930
930
  .catch(mapError);
931
931
  }
932
932
 
@@ -944,7 +944,7 @@ class TestCafe extends Helper {
944
944
  async dontSeeElement(locator) {
945
945
  const exists = (await findElements.call(this, this.context, locator)).filterVisible().exists;
946
946
  return this.t
947
- .expect(exists).notOk(`Element "${locator}" is still visible`)
947
+ .expect(exists).notOk(`Element "${(new Locator(locator))}" is still visible`)
948
948
  .catch(mapError);
949
949
  }
950
950
 
@@ -962,7 +962,7 @@ class TestCafe extends Helper {
962
962
  async seeElementInDOM(locator) {
963
963
  const exists = (await findElements.call(this, this.context, locator)).exists;
964
964
  return this.t
965
- .expect(exists).ok(`No element "${locator}" found in DOM`)
965
+ .expect(exists).ok(`No element "${(new Locator(locator))}" found in DOM`)
966
966
  .catch(mapError);
967
967
  }
968
968
 
@@ -980,7 +980,7 @@ class TestCafe extends Helper {
980
980
  async dontSeeElementInDOM(locator) {
981
981
  const exists = (await findElements.call(this, this.context, locator)).exists;
982
982
  return this.t
983
- .expect(exists).notOk(`Element "${locator}" is still in DOM`)
983
+ .expect(exists).notOk(`Element "${(new Locator(locator))}" is still in DOM`)
984
984
  .catch(mapError);
985
985
  }
986
986
 
@@ -1135,10 +1135,10 @@ class TestCafe extends Helper {
1135
1135
  const outputFile = path.join(global.output_dir, fileName);
1136
1136
 
1137
1137
  const sel = await findElements.call(this, this.context, locator);
1138
- assertElementExists(sel);
1138
+ assertElementExists(sel, locator);
1139
1139
  const firstElement = await sel.filterVisible().nth(0);
1140
1140
 
1141
- this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
1141
+ this.debug(`Screenshot of ${(new Locator(locator))} element has been saved to ${outputFile}`);
1142
1142
  return this.t.takeElementScreenshot(firstElement, fileName);
1143
1143
  }
1144
1144
 
@@ -1257,7 +1257,7 @@ class TestCafe extends Helper {
1257
1257
  */
1258
1258
  async grabTextFrom(locator) {
1259
1259
  const sel = await findElements.call(this, this.context, locator);
1260
- assertElementExists(sel);
1260
+ assertElementExists(sel, locator);
1261
1261
  const texts = await this.grabTextFromAll(locator);
1262
1262
  if (texts.length > 1) {
1263
1263
  this.debugSection('GrabText', `Using first element out of ${texts.length}`);
@@ -1305,7 +1305,7 @@ class TestCafe extends Helper {
1305
1305
  */
1306
1306
  async grabAttributeFrom(locator, attr) {
1307
1307
  const sel = await findElements.call(this, this.context, locator);
1308
- assertElementExists(sel);
1308
+ assertElementExists(sel, locator);
1309
1309
  const attrs = await this.grabAttributeFromAll(locator, attr);
1310
1310
  if (attrs.length > 1) {
1311
1311
  this.debugSection('GrabAttribute', `Using first element out of ${attrs.length}`);
@@ -1350,7 +1350,7 @@ class TestCafe extends Helper {
1350
1350
  */
1351
1351
  async grabValueFrom(locator) {
1352
1352
  const sel = await findElements.call(this, this.context, locator);
1353
- assertElementExists(sel);
1353
+ assertElementExists(sel, locator);
1354
1354
  const values = await this.grabValueFromAll(locator);
1355
1355
  if (values.length > 1) {
1356
1356
  this.debugSection('GrabValue', `Using first element out of ${values.length}`);
@@ -1761,7 +1761,7 @@ class TestCafe extends Helper {
1761
1761
 
1762
1762
  return this.t
1763
1763
  .expect(createSelector(locator).with({ boundTestRun: this.t }).filterVisible().count)
1764
- .eql(num, `The number of elements (${locator}) is not ${num} after ${sec} sec`, { timeout: waitTimeout })
1764
+ .eql(num, `The number of elements (${(new Locator(locator))}) is not ${num} after ${sec} sec`, { timeout: waitTimeout })
1765
1765
  .catch(mapError);
1766
1766
  }
1767
1767
 
@@ -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(`
@@ -1440,7 +1440,7 @@ class WebDriver extends Helper {
1440
1440
  */
1441
1441
  async grabHTMLFrom(locator) {
1442
1442
  const html = await this.grabHTMLFromAll(locator);
1443
- assertElementExists(html);
1443
+ assertElementExists(html, locator);
1444
1444
  if (html.length > 1) {
1445
1445
  this.debugSection('GrabHTML', `Using first element out of ${html.length}`);
1446
1446
  }
@@ -1789,7 +1789,7 @@ class WebDriver extends Helper {
1789
1789
  const res = await this._locate(locator, true);
1790
1790
  assertElementExists(res, locator);
1791
1791
  const selected = await forEachAsync(res, async el => el.isDisplayed());
1792
- return truth(`elements of ${locator}`, 'to be seen').assert(selected);
1792
+ return truth(`elements of ${(new Locator(locator))}`, 'to be seen').assert(selected);
1793
1793
  }
1794
1794
 
1795
1795
  /**
@@ -1807,10 +1807,10 @@ class WebDriver extends Helper {
1807
1807
  async dontSeeElement(locator) {
1808
1808
  const res = await this._locate(locator, false);
1809
1809
  if (!res || res.length === 0) {
1810
- return truth(`elements of ${locator}`, 'to be seen').negate(false);
1810
+ return truth(`elements of ${(new Locator(locator))}`, 'to be seen').negate(false);
1811
1811
  }
1812
1812
  const selected = await forEachAsync(res, async el => el.isDisplayed());
1813
- return truth(`elements of ${locator}`, 'to be seen').negate(selected);
1813
+ return truth(`elements of ${(new Locator(locator))}`, 'to be seen').negate(selected);
1814
1814
  }
1815
1815
 
1816
1816
  /**
@@ -1948,7 +1948,7 @@ class WebDriver extends Helper {
1948
1948
  */
1949
1949
  async seeNumberOfElements(locator, num) {
1950
1950
  const res = await this._locate(locator);
1951
- return assert.equal(res.length, num, `expected number of elements (${locator}) is ${num}, but found ${res.length}`);
1951
+ return assert.equal(res.length, num, `expected number of elements (${(new Locator(locator))}) is ${num}, but found ${res.length}`);
1952
1952
  }
1953
1953
 
1954
1954
  /**
@@ -1967,7 +1967,7 @@ class WebDriver extends Helper {
1967
1967
  */
1968
1968
  async seeNumberOfVisibleElements(locator, num) {
1969
1969
  const res = await this.grabNumberOfVisibleElements(locator);
1970
- return assert.equal(res, num, `expected number of visible elements (${locator}) is ${num}, but found ${res}`);
1970
+ return assert.equal(res, num, `expected number of visible elements (${(new Locator(locator))}) is ${num}, but found ${res}`);
1971
1971
  }
1972
1972
 
1973
1973
  /**
@@ -2010,7 +2010,7 @@ class WebDriver extends Helper {
2010
2010
  });
2011
2011
  return assert.ok(
2012
2012
  chunked.length === elemAmount,
2013
- `expected all elements (${locator}) to have CSS property ${JSON.stringify(cssProperties)}`,
2013
+ `expected all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`,
2014
2014
  );
2015
2015
  }
2016
2016
 
@@ -2046,7 +2046,7 @@ class WebDriver extends Helper {
2046
2046
  });
2047
2047
  return assert.ok(
2048
2048
  chunked.length === elemAmount,
2049
- `expected all elements (${locator}) to have attributes ${JSON.stringify(attributes)}`,
2049
+ `expected all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`,
2050
2050
  );
2051
2051
  }
2052
2052
 
@@ -2224,7 +2224,7 @@ class WebDriver extends Helper {
2224
2224
  */
2225
2225
  async scrollIntoView(locator, scrollIntoViewOptions) {
2226
2226
  const res = await this._locate(withStrictLocator(locator), true);
2227
- assertElementExists(res);
2227
+ assertElementExists(res, locator);
2228
2228
  const elem = usingFirstElement(res);
2229
2229
  return elem.scrollIntoView(scrollIntoViewOptions);
2230
2230
  }
@@ -2254,12 +2254,12 @@ class WebDriver extends Helper {
2254
2254
 
2255
2255
  if (locator) {
2256
2256
  const res = await this._locate(withStrictLocator(locator), true);
2257
- assertElementExists(res);
2257
+ assertElementExists(res, locator);
2258
2258
  const elem = usingFirstElement(res);
2259
2259
  const elementId = getElementId(elem);
2260
2260
  if (this.browser.isMobile && !this.browser.isW3C) return this.browser.touchScroll(offsetX, offsetY, elementId);
2261
2261
  const location = await elem.getLocation();
2262
- assertElementExists(location, 'Failed to receive', 'location');
2262
+ assertElementExists(location, locator, 'Failed to receive', 'location');
2263
2263
  /* eslint-disable prefer-arrow-callback */
2264
2264
  return this.browser.execute(function (x, y) { return window.scrollTo(x, y); }, location.x + offsetX, location.y + offsetY);
2265
2265
  /* eslint-enable */
@@ -2316,7 +2316,7 @@ class WebDriver extends Helper {
2316
2316
  assertElementExists(res, locator);
2317
2317
  const elem = usingFirstElement(res);
2318
2318
 
2319
- this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
2319
+ this.debug(`Screenshot of ${(new Locator(locator))} element has been saved to ${outputFile}`);
2320
2320
  return elem.saveScreenshot(outputFile);
2321
2321
  }
2322
2322
 
@@ -2773,11 +2773,11 @@ class WebDriver extends Helper {
2773
2773
  */
2774
2774
  async dragAndDrop(srcElement, destElement) {
2775
2775
  let sourceEl = await this._locate(srcElement);
2776
- assertElementExists(sourceEl);
2776
+ assertElementExists(sourceEl, srcElement);
2777
2777
  sourceEl = usingFirstElement(sourceEl);
2778
2778
 
2779
2779
  let destEl = await this._locate(destElement);
2780
- assertElementExists(destEl);
2780
+ assertElementExists(destEl, destElement);
2781
2781
  destEl = usingFirstElement(destEl);
2782
2782
 
2783
2783
  return sourceEl.dragAndDrop(destEl);
@@ -2922,7 +2922,7 @@ class WebDriver extends Helper {
2922
2922
  *
2923
2923
  */
2924
2924
  async waitForEnabled(locator, sec = null) {
2925
- const aSec = sec || this.options.waitForTimeout;
2925
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2926
2926
  if (isWebDriver5()) {
2927
2927
  return this.browser.waitUntil(async () => {
2928
2928
  const res = await this.$$(withStrictLocator(locator));
@@ -2967,17 +2967,17 @@ class WebDriver extends Helper {
2967
2967
  *
2968
2968
  */
2969
2969
  async waitForElement(locator, sec = null) {
2970
- const aSec = sec || this.options.waitForTimeout;
2970
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
2971
2971
  if (isWebDriver5()) {
2972
2972
  return this.browser.waitUntil(async () => {
2973
2973
  const res = await this.$$(withStrictLocator(locator));
2974
2974
  return res && res.length;
2975
- }, aSec * 1000, `element (${locator}) still not present on page after ${aSec} sec`);
2975
+ }, aSec * 1000, `element (${(new Locator(locator))}) still not present on page after ${aSec} sec`);
2976
2976
  }
2977
2977
  return this.browser.waitUntil(async () => {
2978
2978
  const res = await this._res(locator);
2979
2979
  return res && res.length;
2980
- }, { timeout: aSec * 1000, timeoutMsg: `element (${locator}) still not present on page after ${aSec} sec` });
2980
+ }, { timeout: aSec * 1000, timeoutMsg: `element (${(new Locator(locator))}) still not present on page after ${aSec} sec` });
2981
2981
  }
2982
2982
 
2983
2983
  /**
@@ -2995,7 +2995,7 @@ class WebDriver extends Helper {
2995
2995
  *
2996
2996
  */
2997
2997
  async waitForClickable(locator, waitTimeout) {
2998
- waitTimeout = waitTimeout || this.options.waitForTimeout;
2998
+ waitTimeout = waitTimeout || this.options.waitForTimeoutInSeconds;
2999
2999
  let res = await this._locate(locator);
3000
3000
  res = usingFirstElement(res);
3001
3001
  assertElementExists(res, locator);
@@ -3020,7 +3020,7 @@ class WebDriver extends Helper {
3020
3020
  */
3021
3021
  async waitInUrl(urlPart, sec = null) {
3022
3022
  const client = this.browser;
3023
- const aSec = sec || this.options.waitForTimeout;
3023
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
3024
3024
  let currUrl = '';
3025
3025
  if (isWebDriver5()) {
3026
3026
  return client
@@ -3064,7 +3064,7 @@ class WebDriver extends Helper {
3064
3064
  *
3065
3065
  */
3066
3066
  async waitUrlEquals(urlPart, sec = null) {
3067
- const aSec = sec || this.options.waitForTimeout;
3067
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
3068
3068
  const baseUrl = this.options.url;
3069
3069
  if (urlPart.indexOf('http') < 0) {
3070
3070
  urlPart = baseUrl + urlPart;
@@ -3101,7 +3101,7 @@ class WebDriver extends Helper {
3101
3101
  *
3102
3102
  */
3103
3103
  async waitForText(text, sec = null, context = null) {
3104
- const aSec = sec || this.options.waitForTimeout;
3104
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
3105
3105
  const _context = context || this.root;
3106
3106
  if (isWebDriver5()) {
3107
3107
  return this.browser.waitUntil(
@@ -3149,7 +3149,7 @@ class WebDriver extends Helper {
3149
3149
  */
3150
3150
  async waitForValue(field, value, sec = null) {
3151
3151
  const client = this.browser;
3152
- const aSec = sec || this.options.waitForTimeout;
3152
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
3153
3153
  if (isWebDriver5()) {
3154
3154
  return client.waitUntil(
3155
3155
  async () => {
@@ -3195,7 +3195,7 @@ class WebDriver extends Helper {
3195
3195
  *
3196
3196
  */
3197
3197
  async waitForVisible(locator, sec = null) {
3198
- const aSec = sec || this.options.waitForTimeout;
3198
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
3199
3199
  if (isWebDriver5()) {
3200
3200
  return this.browser.waitUntil(async () => {
3201
3201
  const res = await this.$$(withStrictLocator(locator));
@@ -3232,7 +3232,7 @@ class WebDriver extends Helper {
3232
3232
  *
3233
3233
  */
3234
3234
  async waitNumberOfVisibleElements(locator, num, sec = null) {
3235
- const aSec = sec || this.options.waitForTimeout;
3235
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
3236
3236
  if (isWebDriver5()) {
3237
3237
  return this.browser.waitUntil(async () => {
3238
3238
  const res = await this.$$(withStrictLocator(locator));
@@ -3269,7 +3269,7 @@ class WebDriver extends Helper {
3269
3269
  *
3270
3270
  */
3271
3271
  async waitForInvisible(locator, sec = null) {
3272
- const aSec = sec || this.options.waitForTimeout;
3272
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
3273
3273
  if (isWebDriver5()) {
3274
3274
  return this.browser.waitUntil(async () => {
3275
3275
  const res = await this.$$(withStrictLocator(locator));
@@ -3317,7 +3317,7 @@ class WebDriver extends Helper {
3317
3317
  *
3318
3318
  */
3319
3319
  async waitForDetached(locator, sec = null) {
3320
- const aSec = sec || this.options.waitForTimeout;
3320
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
3321
3321
  if (isWebDriver5()) {
3322
3322
  return this.browser.waitUntil(async () => {
3323
3323
  const res = await this._res(locator);
@@ -3366,7 +3366,7 @@ class WebDriver extends Helper {
3366
3366
  }
3367
3367
  }
3368
3368
 
3369
- const aSec = sec || this.options.waitForTimeout;
3369
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
3370
3370
  if (isWebDriver5()) {
3371
3371
  return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), aSec * 1000, '');
3372
3372
  }
@@ -3414,7 +3414,7 @@ class WebDriver extends Helper {
3414
3414
  *
3415
3415
  */
3416
3416
  async switchToNextTab(num = 1, sec = null) {
3417
- const aSec = sec || this.options.waitForTimeout;
3417
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
3418
3418
  let target;
3419
3419
  const current = await this.browser.getWindowHandle();
3420
3420
 
@@ -3455,7 +3455,7 @@ class WebDriver extends Helper {
3455
3455
  *
3456
3456
  */
3457
3457
  async switchToPreviousTab(num = 1, sec = null) {
3458
- const aSec = sec || this.options.waitForTimeout;
3458
+ const aSec = sec || this.options.waitForTimeoutInSeconds;
3459
3459
  const current = await this.browser.getWindowHandle();
3460
3460
  let target;
3461
3461
 
package/docs/changelog.md CHANGED
@@ -7,13 +7,48 @@ layout: Section
7
7
 
8
8
  # Releases
9
9
 
10
+ ## 3.3.7
11
+
12
+ đŸ›Šī¸ Features
13
+
14
+ * **Promise-based typings** for TypeScript definitions in [#3465](https://github.com/codeceptjs/CodeceptJS/issues/3465) by **[nlespiaucq](https://github.com/nlespiaucq)**. If you use TypeScript or use linters [check how it may be useful to you](https://bit.ly/3XIMq6n).
15
+ * **Translation** improved to use [custom vocabulary](https://codecept.io/translation/).
16
+ * **[Playwright]** Added methods in [#3398](https://github.com/codeceptjs/CodeceptJS/issues/3398) by **[mirao](https://github.com/mirao)**
17
+ * `restartBrowser` - to restart a browser (with different config)
18
+ * `_createContextPage` - to create a new browser context with a page from a helper
19
+ * Added [Cucumber custom types](/bdd#custom-types) for BDD in [#3435](https://github.com/codeceptjs/CodeceptJS/issues/3435) by **[Likstern](https://github.com/Likstern)**
20
+ * Propose using JSONResponse helper when initializing project for API testing. [#3455](https://github.com/codeceptjs/CodeceptJS/issues/3455) by **[PeterNgTr](https://github.com/PeterNgTr)**
21
+ * When translation enabled, generate tests using localized aliases. By **[davertmik](https://github.com/davertmik)**
22
+ * **[Appium]** Added `checkIfAppIsInstalled` in [#3507](https://github.com/codeceptjs/CodeceptJS/issues/3507) by **[PeterNgTr](https://github.com/PeterNgTr)**
23
+
24
+ 🐛 Bugfixes
25
+
26
+ * Fixed [#3462](https://github.com/codeceptjs/CodeceptJS/issues/3462) `TypeError: Cannot read properties of undefined (reading 'setStatus')` by **[dwentland24](https://github.com/dwentland24)** in [#3438](https://github.com/codeceptjs/CodeceptJS/issues/3438)
27
+ * Fixed creating steps file for TypeScript setup [#3459](https://github.com/codeceptjs/CodeceptJS/issues/3459) by **[PeterNgTr](https://github.com/PeterNgTr)**
28
+ * Fixed issue of after all event in `run-rerun` command after complete execution [#3464](https://github.com/codeceptjs/CodeceptJS/issues/3464) by **[jain-neeeraj](https://github.com/jain-neeeraj)**
29
+ * [Playwright][WebDriver][Appium] Do not change `waitForTimeout` value on validation. See [#3478](https://github.com/codeceptjs/CodeceptJS/issues/3478) by **[pmajewski24](https://github.com/pmajewski24)**. Fixes [#2589](https://github.com/codeceptjs/CodeceptJS/issues/2589)
30
+ * [Playwright][WebDriver][Protractor][Puppeteer][TestCafe] Fixes `Element "{null: undefined}" was not found` and `element ([object Object]) still not present` messages when using object locators. See [#3501](https://github.com/codeceptjs/CodeceptJS/issues/3501) and [#3502](https://github.com/codeceptjs/CodeceptJS/issues/3502) by **[pmajewski24](https://github.com/pmajewski24)**
31
+ * **[Playwright]** Improved file names when downloading file in [#3449](https://github.com/codeceptjs/CodeceptJS/issues/3449) by **[PeterNgTr](https://github.com/PeterNgTr)**. Fixes [#3412](https://github.com/codeceptjs/CodeceptJS/issues/3412) and [#3409](https://github.com/codeceptjs/CodeceptJS/issues/3409)
32
+ * Add default value to `profile` env variable. See [#3443](https://github.com/codeceptjs/CodeceptJS/issues/3443) by **[dwentland24](https://github.com/dwentland24)**. Resolves [#3339](https://github.com/codeceptjs/CodeceptJS/issues/3339)
33
+ * **[Playwright]** Using system-native path separator when saving artifacts in [#3460](https://github.com/codeceptjs/CodeceptJS/issues/3460) by **[PeterNgTr](https://github.com/PeterNgTr)**
34
+ * **[Playwright]** Saving videos and traces from multiple sessions in [#3505](https://github.com/codeceptjs/CodeceptJS/issues/3505) by **[davertmik](https://github.com/davertmik)**
35
+ * **[Playwright]** Fixed `amOnPage` to navigate to `about:blank` by **[zaxoavoki](https://github.com/zaxoavoki)** in [#3470](https://github.com/codeceptjs/CodeceptJS/issues/3470) Fixes [#2311](https://github.com/codeceptjs/CodeceptJS/issues/2311)
36
+ * Various typing improvements by **[AWolf81](https://github.com/AWolf81)** **[PeterNgTr](https://github.com/PeterNgTr)** **[mirao](https://github.com/mirao)**
37
+
38
+ 📖 Documentation
39
+
40
+ * Updated [Auickstart](https://codecept.io/quickstart/) with detailed explanation of questions in init
41
+ * Added [Translation](/translations/) guide
42
+ * Updated [TypeScript](https://bit.ly/3XIMq6n) guide for promise-based typings
43
+ * Reordered guides list on a website
44
+
10
45
  ## 3.3.6
11
46
 
12
47
  * [`run-rerun`](https://codecept.io/commands/#run-rerun) command was re-introduced by **[dwentland24](https://github.com/dwentland24)** in [#3436](https://github.com/codeceptjs/CodeceptJS/issues/3436). Use it to perform run multiple times and detect flaky tests
13
48
  * Enabled `retryFailedStep` by default in `@codeceptjs/configure` v 0.10. See https://github.com/codeceptjs/configure/pull/26
14
49
  * **[Playwright]** Fixed properties types "waitForNavigation" and "firefox" by **[mirao](https://github.com/mirao)** in [#3401](https://github.com/codeceptjs/CodeceptJS/issues/3401)
15
50
  * **[REST]** Changed "endpoint" to optional by **[mirao](https://github.com/mirao)** in [#3404](https://github.com/codeceptjs/CodeceptJS/issues/3404)
16
- * **[REST]** Use [`secret`]() for form encoded string by **[PeterNgTr](https://github.com/PeterNgTr)**:
51
+ * **[REST]** Use [`secret`](/secrets) for form encoded string by **[PeterNgTr](https://github.com/PeterNgTr)**:
17
52
 
18
53
  ```js
19
54
  const secretData = secret('name=john&password=123456');