codeceptjs 3.3.8-beta.1 → 3.4.0-beta.1

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 (63) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +31 -32
  3. package/docs/advanced.md +48 -24
  4. package/docs/basics.md +115 -40
  5. package/docs/best.md +2 -2
  6. package/docs/build/ApiDataFactory.js +6 -6
  7. package/docs/build/Appium.js +2 -19
  8. package/docs/build/FileSystem.js +2 -2
  9. package/docs/build/GraphQLDataFactory.js +2 -2
  10. package/docs/build/Playwright.js +3 -2
  11. package/docs/build/TestCafe.js +4 -4
  12. package/docs/build/WebDriver.js +29 -164
  13. package/docs/helpers/ApiDataFactory.md +6 -6
  14. package/docs/helpers/FileSystem.md +2 -2
  15. package/docs/helpers/GraphQLDataFactory.md +2 -2
  16. package/docs/helpers/Playwright.md +2 -1
  17. package/docs/index.md +1 -1
  18. package/docs/plugins.md +73 -48
  19. package/docs/reports.md +0 -56
  20. package/docs/typescript.md +2 -8
  21. package/lib/actor.js +2 -1
  22. package/lib/cli.js +3 -3
  23. package/lib/codecept.js +2 -1
  24. package/lib/command/generate.js +3 -1
  25. package/lib/command/gherkin/snippets.js +8 -4
  26. package/lib/command/init.js +0 -8
  27. package/lib/command/run-workers.js +3 -6
  28. package/lib/command/utils.js +0 -10
  29. package/lib/command/workers/runTests.js +2 -2
  30. package/lib/config.js +5 -1
  31. package/lib/helper/ApiDataFactory.js +7 -7
  32. package/lib/helper/Appium.js +2 -19
  33. package/lib/helper/FileSystem.js +3 -3
  34. package/lib/helper/GraphQL.js +1 -1
  35. package/lib/helper/GraphQLDataFactory.js +3 -3
  36. package/lib/helper/JSONResponse.js +1 -1
  37. package/lib/helper/Mochawesome.js +1 -1
  38. package/lib/helper/Nightmare.js +1 -1
  39. package/lib/helper/Playwright.js +4 -3
  40. package/lib/helper/Protractor.js +1 -1
  41. package/lib/helper/Puppeteer.js +1 -1
  42. package/lib/helper/REST.js +1 -1
  43. package/lib/helper/TestCafe.js +5 -5
  44. package/lib/helper/WebDriver.js +30 -165
  45. package/lib/helper.js +0 -2
  46. package/lib/interfaces/bdd.js +1 -1
  47. package/lib/interfaces/featureConfig.js +1 -0
  48. package/lib/interfaces/gherkin.js +38 -25
  49. package/lib/listener/exit.js +2 -2
  50. package/lib/listener/retry.js +67 -0
  51. package/lib/listener/steps.js +1 -1
  52. package/lib/listener/timeout.js +47 -10
  53. package/lib/mochaFactory.js +3 -3
  54. package/lib/plugin/allure.js +14 -323
  55. package/lib/plugin/fakerTransform.js +2 -2
  56. package/lib/recorder.js +1 -1
  57. package/lib/scenario.js +25 -18
  58. package/lib/utils.js +6 -0
  59. package/lib/workers.js +4 -7
  60. package/package.json +13 -17
  61. package/typings/index.d.ts +66 -1
  62. package/typings/promiseBasedTypes.d.ts +12 -12
  63. package/typings/types.d.ts +95 -262
@@ -766,8 +766,8 @@ class TestCafe extends Helper {
766
766
 
767
767
  // TODO As far as I understand the testcafe docs this should do a multi-select
768
768
  // but it does not work
769
- const clickOpts = { ctrl: option.length > 1 };
770
- await this.t.click(el, clickOpts).catch(mapError);
769
+ // const clickOpts = { ctrl: option.length > 1 };
770
+ await this.t.click(el).catch(mapError);
771
771
 
772
772
  for (const key of option) {
773
773
  const opt = key;
@@ -776,7 +776,7 @@ class TestCafe extends Helper {
776
776
  try {
777
777
  optEl = el.child('option').withText(opt);
778
778
  if (await optEl.count) {
779
- await this.t.click(optEl, clickOpts).catch(mapError);
779
+ await this.t.click(optEl).catch(mapError);
780
780
  continue;
781
781
  }
782
782
  // eslint-disable-next-line no-empty
@@ -787,7 +787,7 @@ class TestCafe extends Helper {
787
787
  const sel = `[value="${opt}"]`;
788
788
  optEl = el.find(sel);
789
789
  if (await optEl.count) {
790
- await this.t.click(optEl, clickOpts).catch(mapError);
790
+ await this.t.click(optEl).catch(mapError);
791
791
  }
792
792
  // eslint-disable-next-line no-empty
793
793
  } catch (err) {
@@ -8,8 +8,8 @@ const Helper = require('../helper');
8
8
  const stringIncludes = require('../assert/include').includes;
9
9
  const { urlEquals, equals } = require('../assert/equal');
10
10
  const { debug } = require('../output');
11
- const empty = require('../assert/empty').empty;
12
- const truth = require('../assert/truth').truth;
11
+ const { empty } = require('../assert/empty');
12
+ const { truth } = require('../assert/truth');
13
13
  const {
14
14
  xpathLocator,
15
15
  fileExists,
@@ -31,8 +31,6 @@ const Locator = require('../locator');
31
31
  const SHADOW = 'shadow';
32
32
  const webRoot = 'body';
33
33
 
34
- let version;
35
-
36
34
  /**
37
35
  * ## Configuration
38
36
  *
@@ -266,7 +264,7 @@ const config = {};
266
264
  * ```js
267
265
  * plugins: {
268
266
  * wdio: {
269
- * enabled: true,
267
+ * enabled: true,
270
268
  * services: ['sauce'],
271
269
  * user: ... ,// saucelabs username
272
270
  * key: ... // saucelabs api key
@@ -294,7 +292,7 @@ const config = {};
294
292
  * ```js
295
293
  * plugins: {
296
294
  * wdio: {
297
- * enabled: true,
295
+ * enabled: true,
298
296
  * services: ['browserstack'],
299
297
  * user: ... ,// browserstack username
300
298
  * key: ... // browserstack api key
@@ -386,16 +384,6 @@ class WebDriver extends Helper {
386
384
  super(config);
387
385
  webdriverio = require('webdriverio');
388
386
 
389
- try {
390
- version = JSON.parse(fs.readFileSync(path.join(require.resolve('webdriverio'), '/../../', 'package.json')).toString()).version;
391
- } catch (err) {
392
- this.debug('Can\'t detect webdriverio version, assuming webdriverio v6 is used');
393
- }
394
-
395
- if (isWebDriver5()) {
396
- console.log('DEPRECATION NOTICE:');
397
- console.log('You are using webdriverio v5. It is recommended to update to webdriverio@6.\nSupport of webdriverio v5 is deprecated and will be removed in CodeceptJS 3.0\n');
398
- }
399
387
  // set defaults
400
388
  this.root = webRoot;
401
389
  this.isWeb = true;
@@ -661,23 +649,23 @@ class WebDriver extends Helper {
661
649
  }
662
650
 
663
651
  /**
664
- * Use [webdriverio](https://webdriver.io/docs/api.html) API inside a test.
665
- *
666
- * First argument is a description of an action.
667
- * Second argument is async function that gets this helper as parameter.
668
- *
669
- * { [`browser`](https://webdriver.io/docs/api.html)) } object from WebDriver API is available.
670
- *
671
- * ```js
672
- * I.useWebDriverTo('open multiple windows', async ({ browser }) {
673
- * // create new window
674
- * await browser.newWindow('https://webdriver.io');
675
- * });
676
- * ```
677
- *
678
- * @param {string} description used to show in logs.
679
- * @param {function} fn async functuion that executed with WebDriver helper as argument
680
- */
652
+ * Use [webdriverio](https://webdriver.io/docs/api.html) API inside a test.
653
+ *
654
+ * First argument is a description of an action.
655
+ * Second argument is async function that gets this helper as parameter.
656
+ *
657
+ * { [`browser`](https://webdriver.io/docs/api.html)) } object from WebDriver API is available.
658
+ *
659
+ * ```js
660
+ * I.useWebDriverTo('open multiple windows', async ({ browser }) {
661
+ * // create new window
662
+ * await browser.newWindow('https://webdriver.io');
663
+ * });
664
+ * ```
665
+ *
666
+ * @param {string} description used to show in logs.
667
+ * @param {function} fn async functuion that executed with WebDriver helper as argument
668
+ */
681
669
  useWebDriverTo(description, fn) {
682
670
  return this._useTo(...arguments);
683
671
  }
@@ -2291,7 +2279,6 @@ class WebDriver extends Helper {
2291
2279
  const res = await this._locate(withStrictLocator(locator), true);
2292
2280
  assertElementExists(res, locator);
2293
2281
  const elem = usingFirstElement(res);
2294
- if (isWebDriver5()) return elem.moveTo(xOffset, yOffset);
2295
2282
  return elem.moveTo({ xOffset, yOffset });
2296
2283
  }
2297
2284
 
@@ -2923,19 +2910,7 @@ class WebDriver extends Helper {
2923
2910
  */
2924
2911
  async waitForEnabled(locator, sec = null) {
2925
2912
  const aSec = sec || this.options.waitForTimeoutInSeconds;
2926
- if (isWebDriver5()) {
2927
- return this.browser.waitUntil(async () => {
2928
- const res = await this.$$(withStrictLocator(locator));
2929
- if (!res || res.length === 0) {
2930
- return false;
2931
- }
2932
- const selected = await forEachAsync(res, async el => this.browser.isElementEnabled(getElementId(el)));
2933
- if (Array.isArray(selected)) {
2934
- return selected.filter(val => val === true).length > 0;
2935
- }
2936
- return selected;
2937
- }, aSec * 1000, `element (${new Locator(locator)}) still not enabled after ${aSec} sec`);
2938
- }
2913
+
2939
2914
  return this.browser.waitUntil(async () => {
2940
2915
  const res = await this._res(locator);
2941
2916
  if (!res || res.length === 0) {
@@ -2968,12 +2943,7 @@ class WebDriver extends Helper {
2968
2943
  */
2969
2944
  async waitForElement(locator, sec = null) {
2970
2945
  const aSec = sec || this.options.waitForTimeoutInSeconds;
2971
- if (isWebDriver5()) {
2972
- return this.browser.waitUntil(async () => {
2973
- const res = await this.$$(withStrictLocator(locator));
2974
- return res && res.length;
2975
- }, aSec * 1000, `element (${(new Locator(locator))}) still not present on page after ${aSec} sec`);
2976
- }
2946
+
2977
2947
  return this.browser.waitUntil(async () => {
2978
2948
  const res = await this._res(locator);
2979
2949
  return res && res.length;
@@ -3022,20 +2992,7 @@ class WebDriver extends Helper {
3022
2992
  const client = this.browser;
3023
2993
  const aSec = sec || this.options.waitForTimeoutInSeconds;
3024
2994
  let currUrl = '';
3025
- if (isWebDriver5()) {
3026
- return client
3027
- .waitUntil(function () {
3028
- return this.getUrl().then((res) => {
3029
- currUrl = decodeUrl(res);
3030
- return currUrl.indexOf(urlPart) > -1;
3031
- });
3032
- }, aSec * 1000).catch((e) => {
3033
- if (e.message.indexOf('timeout')) {
3034
- throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`);
3035
- }
3036
- throw e;
3037
- });
3038
- }
2995
+
3039
2996
  return client
3040
2997
  .waitUntil(function () {
3041
2998
  return this.getUrl().then((res) => {
@@ -3103,20 +3060,6 @@ class WebDriver extends Helper {
3103
3060
  async waitForText(text, sec = null, context = null) {
3104
3061
  const aSec = sec || this.options.waitForTimeoutInSeconds;
3105
3062
  const _context = context || this.root;
3106
- if (isWebDriver5()) {
3107
- return this.browser.waitUntil(
3108
- async () => {
3109
- const res = await this.$$(withStrictLocator.call(this, _context));
3110
- if (!res || res.length === 0) return false;
3111
- const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)));
3112
- if (Array.isArray(selected)) {
3113
- return selected.filter(part => part.indexOf(text) >= 0).length > 0;
3114
- }
3115
- return selected.indexOf(text) >= 0;
3116
- }, aSec * 1000,
3117
- `element (${_context}) is not in DOM or there is no element(${_context}) with text "${text}" after ${aSec} sec`,
3118
- );
3119
- }
3120
3063
 
3121
3064
  return this.browser.waitUntil(
3122
3065
  async () => {
@@ -3150,20 +3093,7 @@ class WebDriver extends Helper {
3150
3093
  async waitForValue(field, value, sec = null) {
3151
3094
  const client = this.browser;
3152
3095
  const aSec = sec || this.options.waitForTimeoutInSeconds;
3153
- if (isWebDriver5()) {
3154
- return client.waitUntil(
3155
- async () => {
3156
- const res = await findFields.call(this, field);
3157
- if (!res || res.length === 0) return false;
3158
- const selected = await forEachAsync(res, async el => el.getValue());
3159
- if (Array.isArray(selected)) {
3160
- return selected.filter(part => part.indexOf(value) >= 0).length > 0;
3161
- }
3162
- return selected.indexOf(value) >= 0;
3163
- }, aSec * 1000,
3164
- `element (${field}) is not in DOM or there is no element(${field}) with value "${value}" after ${aSec} sec`,
3165
- );
3166
- }
3096
+
3167
3097
  return client.waitUntil(
3168
3098
  async () => {
3169
3099
  const res = await findFields.call(this, field);
@@ -3196,17 +3126,7 @@ class WebDriver extends Helper {
3196
3126
  */
3197
3127
  async waitForVisible(locator, sec = null) {
3198
3128
  const aSec = sec || this.options.waitForTimeoutInSeconds;
3199
- if (isWebDriver5()) {
3200
- return this.browser.waitUntil(async () => {
3201
- const res = await this.$$(withStrictLocator(locator));
3202
- if (!res || res.length === 0) return false;
3203
- const selected = await forEachAsync(res, async el => el.isDisplayed());
3204
- if (Array.isArray(selected)) {
3205
- return selected.filter(val => val === true).length > 0;
3206
- }
3207
- return selected;
3208
- }, aSec * 1000, `element (${new Locator(locator)}) still not visible after ${aSec} sec`);
3209
- }
3129
+
3210
3130
  return this.browser.waitUntil(async () => {
3211
3131
  const res = await this._res(locator);
3212
3132
  if (!res || res.length === 0) return false;
@@ -3233,17 +3153,7 @@ class WebDriver extends Helper {
3233
3153
  */
3234
3154
  async waitNumberOfVisibleElements(locator, num, sec = null) {
3235
3155
  const aSec = sec || this.options.waitForTimeoutInSeconds;
3236
- if (isWebDriver5()) {
3237
- return this.browser.waitUntil(async () => {
3238
- const res = await this.$$(withStrictLocator(locator));
3239
- if (!res || res.length === 0) return false;
3240
- let selected = await forEachAsync(res, async el => el.isDisplayed());
3241
3156
 
3242
- if (!Array.isArray(selected)) selected = [selected];
3243
- selected = selected.filter(val => val === true);
3244
- return selected.length === num;
3245
- }, aSec * 1000, `The number of elements (${new Locator(locator)}) is not ${num} after ${aSec} sec`);
3246
- }
3247
3157
  return this.browser.waitUntil(async () => {
3248
3158
  const res = await this._res(locator);
3249
3159
  if (!res || res.length === 0) return false;
@@ -3270,14 +3180,7 @@ class WebDriver extends Helper {
3270
3180
  */
3271
3181
  async waitForInvisible(locator, sec = null) {
3272
3182
  const aSec = sec || this.options.waitForTimeoutInSeconds;
3273
- if (isWebDriver5()) {
3274
- return this.browser.waitUntil(async () => {
3275
- const res = await this.$$(withStrictLocator(locator));
3276
- if (!res || res.length === 0) return true;
3277
- const selected = await forEachAsync(res, async el => el.isDisplayed());
3278
- return !selected.length;
3279
- }, aSec * 1000, `element (${new Locator(locator)}) still visible after ${aSec} sec`);
3280
- }
3183
+
3281
3184
  return this.browser.waitUntil(async () => {
3282
3185
  const res = await this._res(locator);
3283
3186
  if (!res || res.length === 0) return true;
@@ -3318,15 +3221,7 @@ class WebDriver extends Helper {
3318
3221
  */
3319
3222
  async waitForDetached(locator, sec = null) {
3320
3223
  const aSec = sec || this.options.waitForTimeoutInSeconds;
3321
- if (isWebDriver5()) {
3322
- return this.browser.waitUntil(async () => {
3323
- const res = await this._res(locator);
3324
- if (!res || res.length === 0) {
3325
- return true;
3326
- }
3327
- return false;
3328
- }, aSec * 1000, `element (${new Locator(locator)}) still on page after ${aSec} sec`);
3329
- }
3224
+
3330
3225
  return this.browser.waitUntil(async () => {
3331
3226
  const res = await this._res(locator);
3332
3227
  if (!res || res.length === 0) {
@@ -3367,9 +3262,7 @@ class WebDriver extends Helper {
3367
3262
  }
3368
3263
 
3369
3264
  const aSec = sec || this.options.waitForTimeoutInSeconds;
3370
- if (isWebDriver5()) {
3371
- return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), aSec * 1000, '');
3372
- }
3265
+
3373
3266
  return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), { timeout: aSec * 1000, timeoutMsg: '' });
3374
3267
  }
3375
3268
 
@@ -3418,18 +3311,6 @@ class WebDriver extends Helper {
3418
3311
  let target;
3419
3312
  const current = await this.browser.getWindowHandle();
3420
3313
 
3421
- if (isWebDriver5()) {
3422
- await this.browser.waitUntil(async () => {
3423
- await this.browser.getWindowHandles().then((handles) => {
3424
- if (handles.indexOf(current) + num + 1 <= handles.length) {
3425
- target = handles[handles.indexOf(current) + num];
3426
- }
3427
- });
3428
- return target;
3429
- }, aSec * 1000, `There is no ability to switch to next tab with offset ${num}`);
3430
- return this.browser.switchToWindow(target);
3431
- }
3432
-
3433
3314
  await this.browser.waitUntil(async () => {
3434
3315
  await this.browser.getWindowHandles().then((handles) => {
3435
3316
  if (handles.indexOf(current) + num + 1 <= handles.length) {
@@ -3459,18 +3340,6 @@ class WebDriver extends Helper {
3459
3340
  const current = await this.browser.getWindowHandle();
3460
3341
  let target;
3461
3342
 
3462
- if (isWebDriver5()) {
3463
- await this.browser.waitUntil(async () => {
3464
- await this.browser.getWindowHandles().then((handles) => {
3465
- if (handles.indexOf(current) - num > -1) {
3466
- target = handles[handles.indexOf(current) - num];
3467
- }
3468
- });
3469
- return target;
3470
- }, aSec * 1000, `There is no ability to switch to previous tab with offset ${num}`);
3471
- return this.browser.switchToWindow(target);
3472
- }
3473
-
3474
3343
  await this.browser.waitUntil(async () => {
3475
3344
  await this.browser.getWindowHandles().then((handles) => {
3476
3345
  if (handles.indexOf(current) - num > -1) {
@@ -4145,8 +4014,4 @@ function prepareLocateFn(context) {
4145
4014
  };
4146
4015
  }
4147
4016
 
4148
- function isWebDriver5() {
4149
- return version && version.indexOf('5') === 0;
4150
- }
4151
-
4152
4017
  module.exports = WebDriver;
@@ -53,8 +53,8 @@ See the example for Posts factories:
53
53
  ```js
54
54
  // tests/factories/posts.js
55
55
 
56
- const Factory = require('rosie').Factory;
57
- const faker = require('@faker-js/faker');
56
+ const { Factory } = require('rosie');
57
+ const { faker } = require('@faker-js/faker');
58
58
 
59
59
  module.exports = new Factory()
60
60
  // no need to set id, it will be set by REST API
@@ -228,8 +228,8 @@ I.have('user', { }, { age: 33, height: 55 })
228
228
  #### Parameters
229
229
 
230
230
  - `factory` **any** factory to use
231
- - `params` **any** predefined parameters
232
- - `options` **any** options for programmatically generate the attributes
231
+ - `params` **any?** predefined parameters
232
+ - `options` **any?** options for programmatically generate the attributes
233
233
 
234
234
  Returns **[Promise][5]&lt;any>**
235
235
 
@@ -252,8 +252,8 @@ I.haveMultiple('post', 3, { author: 'davert' }, { publish_date: '01.01.1997' });
252
252
 
253
253
  - `factory` **any**
254
254
  - `times` **any**
255
- - `params` **any**
256
- - `options` **any**
255
+ - `params` **any?**
256
+ - `options` **any?**
257
257
 
258
258
  [1]: https://github.com/rosiejs/rosie
259
259
 
@@ -124,10 +124,10 @@ Checks that file found by `seeFile` includes a text.
124
124
 
125
125
  ### waitForFile
126
126
 
127
- Waits for file to be present in current directory.
127
+ Waits for the file to be present in the current directory.
128
128
 
129
129
  ```js
130
- I.handleDownloads();
130
+ I.handleDownloads('downloads/largeFilesName.txt');
131
131
  I.click('Download large File');
132
132
  I.amInPath('output/downloads');
133
133
  I.waitForFile('largeFilesName.txt', 10); // wait 10 seconds for file
@@ -53,8 +53,8 @@ See the example for Users factories:
53
53
  ```js
54
54
  // tests/factories/users.js
55
55
 
56
- const Factory = require('rosie').Factory;
57
- const faker = require('@faker-js/faker');
56
+ const { Factory } = require('rosie').Factory;
57
+ const { faker } = require('@faker-js/faker');
58
58
 
59
59
  // Used with a constructor function passed to Factory, so that the final build
60
60
  // object matches the necessary pattern to be sent as the variables object.
@@ -51,6 +51,7 @@ Type: [object][5]
51
51
  - `video` **[boolean][26]?** enables video recording for failed tests; videos are saved into `output/videos` folder
52
52
  - `keepVideoForPassedTests` **[boolean][26]?** save videos for passed tests; videos are saved into `output/videos` folder
53
53
  - `trace` **[boolean][26]?** record [tracing information][35] with screenshots and snapshots.
54
+ - `keepTraceForPassedTests` **[boolean][26]?** save trace for passed tests.
54
55
  - `fullPageScreenshots` **[boolean][26]?** make full page screenshots on failure.
55
56
  - `uniqueScreenshotNames` **[boolean][26]?** option to prevent screenshot override if you have scenarios with the same name in different suites.
56
57
  - `keepBrowserState` **[boolean][26]?** keep browser state between tests when `restart` is set to 'session'.
@@ -1138,7 +1139,7 @@ I.waitForFile('avatar.jpg', 5);
1138
1139
 
1139
1140
  #### Parameters
1140
1141
 
1141
- - `fileName` **[string][8]?** set filename for downloaded file
1142
+ - `fileName` **[string][8]** set filename for downloaded file
1142
1143
 
1143
1144
  Returns **[Promise][14]&lt;void>**
1144
1145
 
package/docs/index.md CHANGED
@@ -86,7 +86,7 @@ Scenario('Checkout test', ({ I }) => {
86
86
  Can we use it for long scenarios? Sure!
87
87
 
88
88
  ```js
89
- const faker = require('@faker-js/faker'); // Use 3rd-party JS code
89
+ const { faker } = require('@faker-js/faker'); // Use 3rd-party JS code
90
90
 
91
91
  Feature('Store');
92
92