codeceptjs 3.0.6 → 3.1.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 (62) hide show
  1. package/CHANGELOG.md +92 -8
  2. package/README.md +9 -1
  3. package/bin/codecept.js +28 -17
  4. package/docs/build/Appium.js +69 -0
  5. package/docs/build/GraphQL.js +9 -10
  6. package/docs/build/Playwright.js +271 -63
  7. package/docs/build/Protractor.js +2 -0
  8. package/docs/build/Puppeteer.js +56 -18
  9. package/docs/build/REST.js +16 -3
  10. package/docs/build/WebDriver.js +82 -16
  11. package/docs/changelog.md +93 -9
  12. package/docs/configuration.md +15 -2
  13. package/docs/email.md +8 -8
  14. package/docs/examples.md +3 -3
  15. package/docs/helpers/Appium.md +66 -68
  16. package/docs/helpers/MockRequest.md +3 -3
  17. package/docs/helpers/Playwright.md +269 -203
  18. package/docs/helpers/Puppeteer.md +17 -1
  19. package/docs/helpers/REST.md +23 -9
  20. package/docs/helpers/WebDriver.md +3 -2
  21. package/docs/locators.md +27 -0
  22. package/docs/mobile.md +2 -1
  23. package/docs/nightmare.md +0 -5
  24. package/docs/parallel.md +14 -7
  25. package/docs/playwright.md +178 -11
  26. package/docs/plugins.md +61 -69
  27. package/docs/react.md +1 -1
  28. package/docs/reports.md +5 -4
  29. package/lib/actor.js +1 -2
  30. package/lib/codecept.js +13 -2
  31. package/lib/command/definitions.js +8 -1
  32. package/lib/command/interactive.js +4 -2
  33. package/lib/command/run-multiple/collection.js +4 -0
  34. package/lib/container.js +3 -3
  35. package/lib/helper/Appium.js +41 -0
  36. package/lib/helper/GraphQL.js +9 -10
  37. package/lib/helper/Playwright.js +218 -70
  38. package/lib/helper/Protractor.js +2 -0
  39. package/lib/helper/Puppeteer.js +56 -18
  40. package/lib/helper/REST.js +12 -0
  41. package/lib/helper/WebDriver.js +82 -16
  42. package/lib/helper/errors/ConnectionRefused.js +1 -1
  43. package/lib/helper/extras/Popup.js +1 -1
  44. package/lib/helper/extras/React.js +44 -32
  45. package/lib/interfaces/gherkin.js +1 -0
  46. package/lib/listener/exit.js +2 -4
  47. package/lib/listener/helpers.js +3 -4
  48. package/lib/locator.js +7 -0
  49. package/lib/mochaFactory.js +11 -6
  50. package/lib/output.js +5 -2
  51. package/lib/plugin/allure.js +7 -18
  52. package/lib/plugin/commentStep.js +1 -1
  53. package/lib/plugin/{puppeteerCoverage.js → coverage.js} +10 -22
  54. package/lib/plugin/customLocator.js +2 -2
  55. package/lib/plugin/screenshotOnFail.js +5 -0
  56. package/lib/plugin/subtitles.js +88 -0
  57. package/lib/plugin/tryTo.js +1 -1
  58. package/lib/step.js +4 -2
  59. package/lib/ui.js +6 -2
  60. package/package.json +5 -4
  61. package/typings/index.d.ts +44 -21
  62. package/typings/types.d.ts +137 -16
package/docs/plugins.md CHANGED
@@ -341,7 +341,7 @@ This plugin can be used
341
341
  ### Config
342
342
 
343
343
  - `enabled` - (default: false) enable a plugin
344
- - `regusterGlobal` - (default: false) register `__` template literal function globally. You can override function global name by providing a name as a value.
344
+ - `registerGlobal` - (default: false) register `__` template literal function globally. You can override function global name by providing a name as a value.
345
345
 
346
346
  ### Examples
347
347
 
@@ -409,6 +409,29 @@ Scenario('project update test', async (I) => {
409
409
 
410
410
  - `config`
411
411
 
412
+ ## coverage
413
+
414
+ Dumps code coverage from Playwright/Puppeteer after every test.
415
+
416
+ #### Configuration
417
+
418
+ ```js
419
+ plugins: {
420
+ playwrightCoverage: {
421
+ enabled: true
422
+ }
423
+ }
424
+ ```
425
+
426
+ Possible config options:
427
+
428
+ - `coverageDir`: directory to dump coverage files
429
+ - `uniqueFileName`: generate a unique filename by adding uuid
430
+
431
+ ### Parameters
432
+
433
+ - `config`
434
+
412
435
  ## customLocator
413
436
 
414
437
  Creates a [custom locator][3] by using special attributes in HTML.
@@ -441,7 +464,7 @@ Using `data-test` attribute with `$` prefix:
441
464
  // in codecept.conf.js
442
465
  plugins: {
443
466
  customLocator: {
444
- enabled: true
467
+ enabled: true,
445
468
  attribute: 'data-test'
446
469
  }
447
470
  }
@@ -460,7 +483,7 @@ Using `data-qa` attribute with `=` prefix:
460
483
  // in codecept.conf.js
461
484
  plugins: {
462
485
  customLocator: {
463
- enabled: true
486
+ enabled: true,
464
487
  prefix: '=',
465
488
  attribute: 'data-qa'
466
489
  }
@@ -536,45 +559,6 @@ Enable it manually on each run via `-p` option:
536
559
 
537
560
  npx codeceptjs run -p pauseOnFail
538
561
 
539
- ## puppeteerCoverage
540
-
541
- Dumps puppeteers code coverage after every test.
542
-
543
- #### Configuration
544
-
545
- Configuration can either be taken from a corresponding helper (deprecated) or a from plugin config (recommended).
546
-
547
- ```js
548
- plugins: {
549
- puppeteerCoverage: {
550
- enabled: true
551
- }
552
- }
553
- ```
554
-
555
- Possible config options:
556
-
557
- - `coverageDir`: directory to dump coverage files
558
- - `uniqueFileName`: generate a unique filename by adding uuid
559
-
560
- First of all, your mileage may vary!
561
-
562
- To work, you need the client javascript code to be NOT uglified. They need to be built in "development" mode.
563
- And the end of your tests, you'll get a directory full of coverage per test run. Now what?
564
- You'll need to convert the coverage code to something istanbul can read. Good news is someone wrote the code
565
- for you (see puppeteer-to-istanbul link below). Then using istanbul you need to combine the converted
566
- coverage and create a report. Good luck!
567
-
568
- Links:
569
-
570
- - [https://github.com/GoogleChrome/puppeteer/blob/v1.12.2/docs/api.md#class-coverage][7]
571
- - [https://github.com/istanbuljs/puppeteer-to-istanbul][8]
572
- - [https://github.com/gotwarlost/istanbul][9]
573
-
574
- ### Parameters
575
-
576
- - `config`
577
-
578
562
  ## retryFailedStep
579
563
 
580
564
  Retries each failed step in a test.
@@ -673,14 +657,14 @@ Possible config options:
673
657
 
674
658
  ## selenoid
675
659
 
676
- [Selenoid][10] plugin automatically starts browsers and video recording.
660
+ [Selenoid][7] plugin automatically starts browsers and video recording.
677
661
  Works with WebDriver helper.
678
662
 
679
663
  ### Prerequisite
680
664
 
681
665
  This plugin **requires Docker** to be installed.
682
666
 
683
- > If you have issues starting Selenoid with this plugin consider using the official [Configuration Manager][11] tool from Selenoid
667
+ > If you have issues starting Selenoid with this plugin consider using the official [Configuration Manager][8] tool from Selenoid
684
668
 
685
669
  ### Usage
686
670
 
@@ -709,7 +693,7 @@ plugins: {
709
693
  }
710
694
  ```
711
695
 
712
- When `autoCreate` is enabled it will pull the [latest Selenoid from DockerHub][12] and start Selenoid automatically.
696
+ When `autoCreate` is enabled it will pull the [latest Selenoid from DockerHub][9] and start Selenoid automatically.
713
697
  It will also create `browsers.json` file required by Selenoid.
714
698
 
715
699
  In automatic mode the latest version of browser will be used for tests. It is recommended to specify exact version of each browser inside `browsers.json` file.
@@ -721,10 +705,10 @@ In automatic mode the latest version of browser will be used for tests. It is re
721
705
  While this plugin can create containers for you for better control it is recommended to create and launch containers manually.
722
706
  This is especially useful for Continous Integration server as you can configure scaling for Selenoid containers.
723
707
 
724
- > Use [Selenoid Configuration Manager][11] to create and start containers semi-automatically.
708
+ > Use [Selenoid Configuration Manager][8] to create and start containers semi-automatically.
725
709
 
726
710
  1. Create `browsers.json` file in the same directory `codecept.conf.js` is located
727
- [Refer to Selenoid documentation][13] to know more about browsers.json.
711
+ [Refer to Selenoid documentation][10] to know more about browsers.json.
728
712
 
729
713
  _Sample browsers.json_
730
714
 
@@ -749,7 +733,7 @@ _Sample browsers.json_
749
733
 
750
734
  2. Create Selenoid container
751
735
 
752
- Run the following command to create a container. To know more [refer here][14]
736
+ Run the following command to create a container. To know more [refer here][11]
753
737
 
754
738
  ```bash
755
739
  docker create \
@@ -782,7 +766,7 @@ When `allure` plugin is enabled a video is attached to report automatically.
782
766
  | enableVideo | Enable video recording and use `video` folder of output (default: false) |
783
767
  | enableLog | Enable log recording and use `logs` folder of output (default: false) |
784
768
  | deletePassed | Delete video and logs of passed tests (default : true) |
785
- | additionalParams | example: `additionalParams: '--env TEST=test'` [Refer here][15] to know more |
769
+ | additionalParams | example: `additionalParams: '--env TEST=test'` [Refer here][12] to know more |
786
770
 
787
771
  ### Parameters
788
772
 
@@ -790,7 +774,7 @@ When `allure` plugin is enabled a video is attached to report automatically.
790
774
 
791
775
  ## stepByStepReport
792
776
 
793
- ![step-by-step-report][16]
777
+ ![step-by-step-report][13]
794
778
 
795
779
  Generates step by step report for a test.
796
780
  After each step in a test a screenshot is created. After test executed screenshots are combined into slideshow.
@@ -823,6 +807,20 @@ Possible config options:
823
807
 
824
808
  - `config` **any**
825
809
 
810
+ ## subtitles
811
+
812
+ Automatically captures steps as subtitle, and saves it as an artifact when a video is found for a failed test
813
+
814
+ #### Configuration
815
+
816
+ ```js
817
+ plugins: {
818
+ subtitles: {
819
+ enabled: true
820
+ }
821
+ }
822
+ ```
823
+
826
824
  ## tryTo
827
825
 
828
826
  Adds global `tryTo` function inside of which all failed steps won't fail a test but will return true/false.
@@ -897,7 +895,7 @@ This plugin allows to run webdriverio services like:
897
895
  - browserstack
898
896
  - appium
899
897
 
900
- A complete list of all available services can be found on [webdriverio website][17].
898
+ A complete list of all available services can be found on [webdriverio website][14].
901
899
 
902
900
  #### Setup
903
901
 
@@ -909,7 +907,7 @@ See examples below:
909
907
 
910
908
  #### Selenium Standalone Service
911
909
 
912
- Install `@wdio/selenium-standalone-service` package, as [described here][18].
910
+ Install `@wdio/selenium-standalone-service` package, as [described here][15].
913
911
  It is important to make sure it is compatible with current webdriverio version.
914
912
 
915
913
  Enable `wdio` plugin in plugins list and add `selenium-standalone` service:
@@ -928,7 +926,7 @@ Please note, this service can be used with Protractor helper as well!
928
926
 
929
927
  #### Sauce Service
930
928
 
931
- Install `@wdio/sauce-service` package, as [described here][19].
929
+ Install `@wdio/sauce-service` package, as [described here][16].
932
930
  It is important to make sure it is compatible with current webdriverio version.
933
931
 
934
932
  Enable `wdio` plugin in plugins list and add `sauce` service:
@@ -970,28 +968,22 @@ In the same manner additional services from webdriverio can be installed, enable
970
968
 
971
969
  [6]: /basics/#pause
972
970
 
973
- [7]: https://github.com/GoogleChrome/puppeteer/blob/v1.12.2/docs/api.md#class-coverage
974
-
975
- [8]: https://github.com/istanbuljs/puppeteer-to-istanbul
976
-
977
- [9]: https://github.com/gotwarlost/istanbul
978
-
979
- [10]: https://aerokube.com/selenoid/
971
+ [7]: https://aerokube.com/selenoid/
980
972
 
981
- [11]: https://aerokube.com/cm/latest/
973
+ [8]: https://aerokube.com/cm/latest/
982
974
 
983
- [12]: https://hub.docker.com/u/selenoid
975
+ [9]: https://hub.docker.com/u/selenoid
984
976
 
985
- [13]: https://aerokube.com/selenoid/latest/#_prepare_configuration
977
+ [10]: https://aerokube.com/selenoid/latest/#_prepare_configuration
986
978
 
987
- [14]: https://aerokube.com/selenoid/latest/#_option_2_start_selenoid_container
979
+ [11]: https://aerokube.com/selenoid/latest/#_option_2_start_selenoid_container
988
980
 
989
- [15]: https://docs.docker.com/engine/reference/commandline/create/
981
+ [12]: https://docs.docker.com/engine/reference/commandline/create/
990
982
 
991
- [16]: https://codecept.io/img/codeceptjs-slideshow.gif
983
+ [13]: https://codecept.io/img/codeceptjs-slideshow.gif
992
984
 
993
- [17]: https://webdriver.io
985
+ [14]: https://webdriver.io
994
986
 
995
- [18]: https://webdriver.io/docs/selenium-standalone-service.html
987
+ [15]: https://webdriver.io/docs/selenium-standalone-service.html
996
988
 
997
- [19]: https://webdriver.io/docs/sauce-service.html
989
+ [16]: https://webdriver.io/docs/sauce-service.html
package/docs/react.md CHANGED
@@ -55,7 +55,7 @@ For React apps a special `react` locator is available. It allows to select an el
55
55
  { react: 'Input', state: 'valid'}
56
56
  ```
57
57
 
58
- In WebDriver and Puppeteer you can use React locators in any method where locator is required:
58
+ In WebDriver, Puppeteer, and Playwright you can use React locators in any method where locator is required:
59
59
 
60
60
  ```js
61
61
  I.click({ react: 'Tab', props: { title: 'Click Me!' }});
package/docs/reports.md CHANGED
@@ -352,10 +352,11 @@ Configure mocha-multi with reports that you want:
352
352
  }
353
353
  },
354
354
  "mochawesome": {
355
- "stdout": "./output/console.log",
356
- "options": {
357
- "reportDir": "./output",
358
- "reportFilename": "report"
355
+ "stdout": "./output/console.log",
356
+ "options": {
357
+ "reportDir": "./output",
358
+ "reportFilename": "report"
359
+ }
359
360
  },
360
361
  "mocha-junit-reporter": {
361
362
  "stdout": "./output/console.log",
package/lib/actor.js CHANGED
@@ -74,8 +74,7 @@ module.exports = function (obj = {}) {
74
74
  const helpers = container.helpers();
75
75
 
76
76
  // add methods from enabled helpers
77
- Object.keys(helpers)
78
- .map(key => helpers[key])
77
+ Object.values(helpers)
79
78
  .forEach((helper) => {
80
79
  methodsOfObject(helper, 'Helper')
81
80
  .filter(method => method !== 'constructor' && method[0] !== '_')
package/lib/codecept.js CHANGED
@@ -130,7 +130,16 @@ class Codecept {
130
130
  let patterns = [pattern];
131
131
  if (!pattern) {
132
132
  patterns = [];
133
- if (this.config.tests && !this.opts.features) patterns.push(this.config.tests);
133
+
134
+ // If the user wants to test a specific set of test files as an array or string.
135
+ if (this.config.tests && !this.opts.features) {
136
+ if (Array.isArray(this.config.tests)) {
137
+ patterns.push(...this.config.tests);
138
+ } else {
139
+ patterns.push(this.config.tests);
140
+ }
141
+ }
142
+
134
143
  if (this.config.gherkin.features && !this.opts.tests) {
135
144
  if (Array.isArray(this.config.gherkin.features)) {
136
145
  this.config.gherkin.features.forEach(feature => {
@@ -147,7 +156,9 @@ class Codecept {
147
156
  if (!fsPath.isAbsolute(file)) {
148
157
  file = fsPath.join(global.codecept_dir, file);
149
158
  }
150
- this.testFiles.push(fsPath.resolve(file));
159
+ if (!this.testFiles.includes(fsPath.resolve(file))) {
160
+ this.testFiles.push(fsPath.resolve(file));
161
+ }
151
162
  });
152
163
  }
153
164
  }
@@ -194,7 +194,14 @@ function getPath(originalPath, targetFolderPath, testsPath) {
194
194
  else if (parsedPath.ext === '.ts') parsedPath.base = parsedPath.name;
195
195
 
196
196
  if (!parsedPath.dir.startsWith('.')) return path.posix.join(parsedPath.dir, parsedPath.base);
197
- const relativePath = path.posix.relative(targetFolderPath, path.posix.join(testsPath, parsedPath.dir, parsedPath.base));
197
+ const relativePath = path.posix.relative(
198
+ targetFolderPath.split(path.sep).join(path.posix.sep),
199
+ path.posix.join(
200
+ testsPath.split(path.sep).join(path.posix.sep),
201
+ parsedPath.dir.split(path.sep).join(path.posix.sep),
202
+ parsedPath.base.split(path.sep).join(path.posix.sep),
203
+ ),
204
+ );
198
205
 
199
206
  return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;
200
207
  }
@@ -8,9 +8,11 @@ module.exports = async function (path, options) {
8
8
  // Backward compatibility for --profile
9
9
  process.profile = options.profile;
10
10
  process.env.profile = options.profile;
11
+ const configFile = options.config;
12
+
13
+ const config = getConfig(configFile);
14
+ const testsPath = getTestRoot(configFile);
11
15
 
12
- const testsPath = getTestRoot(path);
13
- const config = getConfig(testsPath);
14
16
  const codecept = new Codecept(config, options);
15
17
  codecept.init(testsPath);
16
18
 
@@ -80,6 +80,10 @@ class Collection {
80
80
  return;
81
81
  }
82
82
 
83
+ if (runConfig.gherkin && config.gherkin.features) {
84
+ patterns.push(runConfig.gherkin.features);
85
+ }
86
+
83
87
  createChunks(runConfig, patterns).forEach((runChunkConfig, index) => {
84
88
  const run = createRun(`${runName}:chunk${index + 1}`, runChunkConfig);
85
89
  run.setOriginalName(runName);
package/lib/container.js CHANGED
@@ -215,7 +215,7 @@ function createSupportObjects(config) {
215
215
  newObj._init();
216
216
  }
217
217
  } catch (err) {
218
- throw new Error(`Initialization failed for ${name}: ${newObj}\n${err.message}`);
218
+ throw new Error(`Initialization failed for ${name}: ${newObj}\n${err.message}\n${err.stack}`);
219
219
  }
220
220
  return newObj;
221
221
  }
@@ -288,7 +288,7 @@ function createPlugins(config, options = {}) {
288
288
  }
289
289
  plugins[pluginName] = require(module)(config[pluginName]);
290
290
  } catch (err) {
291
- throw new Error(`Could not load plugin ${pluginName} from module '${module}':\n${err.message}`);
291
+ throw new Error(`Could not load plugin ${pluginName} from module '${module}':\n${err.message}\n${err.stack}`);
292
292
  }
293
293
  }
294
294
  return plugins;
@@ -380,7 +380,7 @@ function loadSupportObject(modulePath, supportObjectName) {
380
380
 
381
381
  return obj;
382
382
  } catch (err) {
383
- throw new Error(`Could not include object ${supportObjectName} from module '${modulePath}'\n${err.message}`);
383
+ throw new Error(`Could not include object ${supportObjectName} from module '${modulePath}'\n${err.message}\n${err.stack}`);
384
384
  }
385
385
  }
386
386
 
@@ -1352,6 +1352,33 @@ class Appium extends Webdriver {
1352
1352
  return super.grabTextFrom(parseLocator.call(this, locator));
1353
1353
  }
1354
1354
 
1355
+ /**
1356
+ * {{> grabNumberOfVisibleElements }}
1357
+ */
1358
+ async grabNumberOfVisibleElements(locator) {
1359
+ if (this.isWeb) return super.grabNumberOfVisibleElements(locator);
1360
+ return super.grabNumberOfVisibleElements(parseLocator.call(this, locator));
1361
+ }
1362
+
1363
+ /**
1364
+ * Can be used for apps only with several values ("contentDescription", "text", "className", "resourceId")
1365
+ *
1366
+ * {{> grabAttributeFrom }}
1367
+ */
1368
+ async grabAttributeFrom(locator, attr) {
1369
+ if (this.isWeb) return super.grabAttributeFrom(locator, attr);
1370
+ return super.grabAttributeFrom(parseLocator.call(this, locator), attr);
1371
+ }
1372
+
1373
+ /**
1374
+ * Can be used for apps only with several values ("contentDescription", "text", "className", "resourceId")
1375
+ * {{> grabAttributeFromAll }}
1376
+ */
1377
+ async grabAttributeFromAll(locator, attr) {
1378
+ if (this.isWeb) return super.grabAttributeFromAll(locator, attr);
1379
+ return super.grabAttributeFromAll(parseLocator.call(this, locator), attr);
1380
+ }
1381
+
1355
1382
  /**
1356
1383
  * {{> grabValueFromAll }}
1357
1384
  *
@@ -1370,6 +1397,20 @@ class Appium extends Webdriver {
1370
1397
  return super.grabValueFrom(parseLocator.call(this, locator));
1371
1398
  }
1372
1399
 
1400
+ /**
1401
+ * Saves a screenshot to ouput folder (set in codecept.json or codecept.conf.js).
1402
+ * Filename is relative to output folder.
1403
+ *
1404
+ * ```js
1405
+ * I.saveScreenshot('debug.png');
1406
+ * ```
1407
+ *
1408
+ * @param {string} fileName file name to save.
1409
+ */
1410
+ async saveScreenshot(fileName) {
1411
+ return super.saveScreenshot(fileName, false);
1412
+ }
1413
+
1373
1414
  /**
1374
1415
  * {{> scrollIntoView }}
1375
1416
  *
@@ -1,8 +1,6 @@
1
- let axios = require('axios');
1
+ const axios = require('axios').default;
2
2
  const Helper = require('../helper');
3
3
 
4
- let headers = {};
5
-
6
4
  /**
7
5
  * GraphQL helper allows to send additional requests to a GraphQl endpoint during acceptance tests.
8
6
  * [Axios](https://github.com/axios/axios) library is used to perform requests.
@@ -41,15 +39,16 @@ let headers = {};
41
39
  class GraphQL extends Helper {
42
40
  constructor(config) {
43
41
  super(config);
44
- axios = require('axios');
42
+ this.axios = axios.create();
43
+ this.headers = {};
45
44
  this.options = {
46
45
  timeout: 10000,
47
46
  defaultHeaders: {},
48
47
  endpoint: '',
49
48
  };
50
49
  this.options = Object.assign(this.options, config);
51
- headers = { ...this.options.defaultHeaders };
52
- axios.defaults.headers = this.options.defaultHeaders;
50
+ this.headers = { ...this.options.defaultHeaders };
51
+ this.axios.defaults.headers = this.options.defaultHeaders;
53
52
  }
54
53
 
55
54
  static _checkRequirements() {
@@ -66,10 +65,10 @@ class GraphQL extends Helper {
66
65
  * @param {object} request
67
66
  */
68
67
  async _executeQuery(request) {
69
- axios.defaults.timeout = request.timeout || this.options.timeout;
68
+ this.axios.defaults.timeout = request.timeout || this.options.timeout;
70
69
 
71
- if (headers && headers.auth) {
72
- request.auth = headers.auth;
70
+ if (this.headers && this.headers.auth) {
71
+ request.auth = this.headers.auth;
73
72
  }
74
73
 
75
74
  request.headers = Object.assign(request.headers, {
@@ -84,7 +83,7 @@ class GraphQL extends Helper {
84
83
 
85
84
  let response;
86
85
  try {
87
- response = await axios(request);
86
+ response = await this.axios(request);
88
87
  } catch (err) {
89
88
  if (!err.response) throw err;
90
89
  this.debugSection(