codeceptjs 3.1.1 → 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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,30 @@
1
+ ## 3.1.2
2
+
3
+ đŸ›Šī¸ Features:
4
+
5
+ * Added `coverage` plugin to generate code coverage for Playwright & Puppeteer. By @anirudh-modi
6
+ * Added `subtitle` plugin to generate subtitles for videos recorded with Playwright. By @anirudh-modi
7
+ * Configuration: `config.tests` to accept array of file patterns. See #2994 by @monsteramba
8
+
9
+ ```js
10
+ exports.config = {
11
+ tests: ['./*_test.js','./sampleTest.js'],
12
+ // ...
13
+ }
14
+ ```
15
+ * Notification is shown for test files without `Feature()`. See #3011 by @PeterNgTr
16
+
17
+ 🐛 Bugfixes:
18
+
19
+ * [Playwright] Fixed #2986 error is thrown when deleting a missing video. Fix by @hatufacci
20
+ * Fixed false positive result when invalid function is called in a helper. See #2997 by @abhimanyupandian
21
+ * [Appium] Removed full page mode for `saveScreenshot`. See #3002 by @nlespiaucq
22
+ * [Playwright] Fixed #3003 saving trace for a test with a long name. Fix by @hatufacci
23
+
24
+ 🎱 Other:
25
+
26
+ * Deprecated `puppeteerCoverage` plugin in favor of `coverage` plugin.
27
+
1
28
  ## 3.1.1
2
29
 
3
30
  * [Appium] Fixed #2759
@@ -1551,6 +1551,20 @@ class Appium extends Webdriver {
1551
1551
  return super.grabValueFrom(parseLocator.call(this, locator));
1552
1552
  }
1553
1553
 
1554
+ /**
1555
+ * Saves a screenshot to ouput folder (set in codecept.json or codecept.conf.js).
1556
+ * Filename is relative to output folder.
1557
+ *
1558
+ * ```js
1559
+ * I.saveScreenshot('debug.png');
1560
+ * ```
1561
+ *
1562
+ * @param {string} fileName file name to save.
1563
+ */
1564
+ async saveScreenshot(fileName) {
1565
+ return super.saveScreenshot(fileName, false);
1566
+ }
1567
+
1554
1568
  /**
1555
1569
  * Scroll element into viewport.
1556
1570
  *
@@ -2570,7 +2570,7 @@ class Playwright extends Helper {
2570
2570
  }
2571
2571
 
2572
2572
  if (this.options.trace) {
2573
- const path = `${global.output_dir}/trace/${clearString(test.title)}.zip`;
2573
+ const path = `${global.output_dir}/trace/${clearString(test.title).slice(0, 255)}.zip`;
2574
2574
  await this.browserContext.tracing.stop({ path });
2575
2575
  test.artifacts.trace = path;
2576
2576
  }
@@ -2581,7 +2581,7 @@ class Playwright extends Helper {
2581
2581
  if (this.options.keepVideoForPassedTests) {
2582
2582
  test.artifacts.video = await this.page.video().path();
2583
2583
  } else {
2584
- this.page.video().delete();
2584
+ this.page.video().delete().catch(e => {});
2585
2585
  }
2586
2586
  }
2587
2587
 
@@ -129,8 +129,9 @@ const consoleLogStore = new Console();
129
129
  * }
130
130
  * }
131
131
  * ```
132
+ * > Note: When connecting to remote browser `show` and specific `chrome` options (e.g. `headless` or `devtools`) are ignored.
132
133
  *
133
- * #### Example #5: Target URL with provided basic authentication
134
+ * #### Example #5: Target URL with provided basic authentication
134
135
  *
135
136
  * ```js
136
137
  * {
@@ -143,10 +144,25 @@ const consoleLogStore = new Console();
143
144
  * }
144
145
  * }
145
146
  * ```
147
+ * #### Troubleshooting
146
148
  *
149
+ * Error Message: `No usable sandbox!`
150
+ *
151
+ * When running Puppeteer on CI try to disable sandbox if you see that message
152
+ *
153
+ * ```
154
+ * helpers: {
155
+ * Puppeteer: {
156
+ * url: 'http://localhost',
157
+ * show: false,
158
+ * chrome: {
159
+ * args: ['--no-sandbox', '--disable-setuid-sandbox']
160
+ * }
161
+ * },
162
+ * }
163
+ * ```
147
164
  *
148
165
  *
149
- * Note: When connecting to remote browser `show` and specific `chrome` options (e.g. `headless` or `devtools`) are ignored.
150
166
  *
151
167
  * ## Access From Helpers
152
168
  *
package/docs/changelog.md CHANGED
@@ -7,6 +7,33 @@ layout: Section
7
7
 
8
8
  # Releases
9
9
 
10
+ ## 3.1.2
11
+
12
+ đŸ›Šī¸ Features:
13
+
14
+ * Added `coverage` plugin to generate code coverage for Playwright & Puppeteer. By **[anirudh-modi](https://github.com/anirudh-modi)**
15
+ * Added `subtitle` plugin to generate subtitles for videos recorded with Playwright. By **[anirudh-modi](https://github.com/anirudh-modi)**
16
+ * Configuration: `config.tests` to accept array of file patterns. See [#2994](https://github.com/codeceptjs/CodeceptJS/issues/2994) by **[monsteramba](https://github.com/monsteramba)**
17
+
18
+ ```js
19
+ exports.config = {
20
+ tests: ['./*_test.js','./sampleTest.js'],
21
+ // ...
22
+ }
23
+ ```
24
+ * Notification is shown for test files without `Feature()`. See [#3011](https://github.com/codeceptjs/CodeceptJS/issues/3011) by **[PeterNgTr](https://github.com/PeterNgTr)**
25
+
26
+ 🐛 Bugfixes:
27
+
28
+ * **[Playwright]** Fixed [#2986](https://github.com/codeceptjs/CodeceptJS/issues/2986) error is thrown when deleting a missing video. Fix by **[hatufacci](https://github.com/hatufacci)**
29
+ * Fixed false positive result when invalid function is called in a helper. See [#2997](https://github.com/codeceptjs/CodeceptJS/issues/2997) by **[abhimanyupandian](https://github.com/abhimanyupandian)**
30
+ * **[Appium]** Removed full page mode for `saveScreenshot`. See [#3002](https://github.com/codeceptjs/CodeceptJS/issues/3002) by **[nlespiaucq](https://github.com/nlespiaucq)**
31
+ * **[Playwright]** Fixed [#3003](https://github.com/codeceptjs/CodeceptJS/issues/3003) saving trace for a test with a long name. Fix by **[hatufacci](https://github.com/hatufacci)**
32
+
33
+ 🎱 Other:
34
+
35
+ * Deprecated `puppeteerCoverage` plugin in favor of `coverage` plugin.
36
+
10
37
  ## 3.1.1
11
38
 
12
39
  * **[Appium]** Fixed [#2759](https://github.com/codeceptjs/CodeceptJS/issues/2759)
@@ -11,7 +11,7 @@ After running `codeceptjs init` it should be saved in test root.
11
11
 
12
12
  Here is an overview of available options with their defaults:
13
13
 
14
- * **tests**: `"./*_test.js"` - pattern to locate tests. Allows to enter [glob pattern](https://github.com/isaacs/node-glob).
14
+ * **tests**: `"./*_test.js"` - pattern to locate tests. Allows to enter [glob pattern](https://github.com/isaacs/node-glob), Can either be a pattern to locate tests or an array of patterns to locate tests / test file names.
15
15
  * **grep**: - pattern to filter tests by name
16
16
  * **include**: `{}` - actors and page objects to be registered in DI container and included in tests. Accepts objects and module `require` paths
17
17
  * **timeout**: `10000` - default tests timeout
@@ -47,7 +47,20 @@ exports.config = {
47
47
  require: ["ts-node/register", "should"]
48
48
  }
49
49
  ```
50
-
50
+ For array of test pattern
51
+ ```js
52
+ exports.config = {
53
+ tests: ['./*_test.js','./sampleTest.js'],
54
+ timeout: 10000,
55
+ output: '',
56
+ helpers: {},
57
+ include: {},
58
+ bootstrap: false,
59
+ mocha: {},
60
+ // require modules
61
+ require: ["ts-node/register", "should"]
62
+ }
63
+ ```
51
64
  ## Dynamic Configuration
52
65
 
53
66
  By default `codecept.json` is used for configuration. You can override its values in runtime by using `--override` or `-o` option in command line, passing valid JSON as a value:
@@ -1027,6 +1027,19 @@ let email = await I.grabValueFrom('input[name=email]');
1027
1027
 
1028
1028
  Returns **[Promise][13]<[string][4]>** attribute value
1029
1029
 
1030
+ ### saveScreenshot
1031
+
1032
+ Saves a screenshot to ouput folder (set in codecept.json or codecept.conf.js).
1033
+ Filename is relative to output folder.
1034
+
1035
+ ```js
1036
+ I.saveScreenshot('debug.png');
1037
+ ```
1038
+
1039
+ #### Parameters
1040
+
1041
+ - `fileName` **[string][4]** file name to save.
1042
+
1030
1043
  ### scrollIntoView
1031
1044
 
1032
1045
  Scroll element into viewport.
@@ -1663,27 +1676,11 @@ I.saveElementScreenshot(`#submit`,'debug.png');
1663
1676
  - `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator.
1664
1677
  - `fileName` **[string][4]** file name to save.
1665
1678
 
1666
- ### saveScreenshot
1667
-
1668
- Saves a screenshot to ouput folder (set in codecept.json or codecept.conf.js).
1669
- Filename is relative to output folder.
1670
- Optionally resize the window to the full available page `scrollHeight` and `scrollWidth` to capture the entire page by passing `true` in as the second argument.
1671
-
1672
- ```js
1673
- I.saveScreenshot('debug.png');
1674
- I.saveScreenshot('debug.png', true) //resizes to available scrollHeight and scrollWidth before taking screenshot
1675
- ```
1676
-
1677
- #### Parameters
1678
-
1679
- - `fileName` **[string][4]** file name to save.
1680
- - `fullPage` **[boolean][20]** (optional, `false` by default) flag to enable fullscreen screenshot mode. (optional, default `false`)
1681
-
1682
1679
  ### type
1683
1680
 
1684
1681
  Types out the given text into an active field.
1685
1682
  To slow down typing use a second parameter, to set interval between key presses.
1686
- _Note:_ Should be used when [`fillField`][21] is not an option.
1683
+ _Note:_ Should be used when [`fillField`][20] is not an option.
1687
1684
 
1688
1685
  ```js
1689
1686
  // passing in a string
@@ -1921,6 +1918,4 @@ Returns **([Promise][13]<DOMRect> | [Promise][13]<[number][8]>)** Element
1921
1918
 
1922
1919
  [19]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined
1923
1920
 
1924
- [20]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
1925
-
1926
- [21]: #fillfield
1921
+ [20]: #fillfield
@@ -101,6 +101,8 @@ This helper should be configured in codecept.json or codecept.conf.js
101
101
  }
102
102
  ```
103
103
 
104
+ > Note: When connecting to remote browser `show` and specific `chrome` options (e.g. `headless` or `devtools`) are ignored.
105
+
104
106
  #### Example #5: Target URL with provided basic authentication
105
107
 
106
108
  ```js
@@ -115,7 +117,21 @@ This helper should be configured in codecept.json or codecept.conf.js
115
117
  }
116
118
  ```
117
119
 
118
- Note: When connecting to remote browser `show` and specific `chrome` options (e.g. `headless` or `devtools`) are ignored.
120
+ #### Troubleshooting
121
+
122
+ Error Message: `No usable sandbox!`
123
+
124
+ When running Puppeteer on CI try to disable sandbox if you see that message
125
+
126
+ helpers: {
127
+ Puppeteer: {
128
+ url: 'http://localhost',
129
+ show: false,
130
+ chrome: {
131
+ args: ['--no-sandbox', '--disable-setuid-sandbox']
132
+ }
133
+ },
134
+ }
119
135
 
120
136
  ## Access From Helpers
121
137
 
@@ -456,6 +456,63 @@ For instance, this is how you can read a trace for a failed test from an example
456
456
  npx playwright show-trace /home/davert/projects/codeceptjs/examples/output/trace/open.zip
457
457
  ```
458
458
 
459
+ ## Capturing code coverage
460
+
461
+ Code coverage can be captured, by enabling the `coverage` plugin in `codecept.config.js`.
462
+
463
+ ```js
464
+ {
465
+ plugins: {
466
+ coverage: {
467
+ enabled: true
468
+ }
469
+ }
470
+ }
471
+ ```
472
+
473
+ Once all the tests are completed, `codecept` will create and store coverage in `output/coverage` folder, as shown below.
474
+
475
+ ![](https://user-images.githubusercontent.com/16587779/131362352-30ee9c51-705f-4098-b665-53035ea9275f.png)
476
+
477
+ ### Converting `playwright` coverage to `istanbul` coverage
478
+
479
+ To convert coverage generated from `playwright` to `istanbul` coverage, you first need to install
480
+ - [`v8-to-istanbul`](https://www.npmjs.com/package/v8-to-istanbul)
481
+
482
+ Once installed, convert the coverage to a format which `istanbul` can recognize, by writing a script as shown below.
483
+
484
+ ```js
485
+ const v8toIstanbul = require('v8-to-istanbul');
486
+ // read all the coverage file from output/coverage folder
487
+ const coverage = require('./output/coverage/Visit_Home_1630335005.coverage.json');
488
+ const fs = require('fs/promises');
489
+
490
+ (async () => {
491
+ for (const entry of coverage) {
492
+ // Used to get file name
493
+ const file = entry.url.match(/(?:http(s)*:\/\/.*\/)(?<file>.*)/);
494
+ const converter = new v8toIstanbul(file.groups.file, 0, {
495
+ source: entry.source
496
+ });
497
+
498
+ await converter.load();
499
+ converter.applyCoverage(entry.functions);
500
+
501
+ // Store converted coverage file which can later be used to generate report
502
+ await fs.writeFile('./coverage/final.json', JSON.stringify(converter.toIstanbul(), null, 2));
503
+ }
504
+ })();
505
+ ```
506
+
507
+ Once the istanbul compatible coverage is generated, use [`nyc`](https://www.npmjs.com/package/nyc) to generate your coverage report in your desired format.
508
+
509
+ ```
510
+ npx nyc report --reporter text -t coverage
511
+ ```
512
+
513
+ The above command will generate a text report like shown below.
514
+
515
+ ![](https://user-images.githubusercontent.com/16587779/131363170-b03b4398-5e9a-4142-bc32-764a5f4a5e11.png)
459
516
  ## Extending Helper
460
517
 
461
518
  To create custom `I.*` commands using Playwright API you need to create a custom helper.
package/docs/plugins.md CHANGED
@@ -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/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
  }
@@ -1397,6 +1397,20 @@ class Appium extends Webdriver {
1397
1397
  return super.grabValueFrom(parseLocator.call(this, locator));
1398
1398
  }
1399
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
+
1400
1414
  /**
1401
1415
  * {{> scrollIntoView }}
1402
1416
  *
@@ -1856,7 +1856,7 @@ class Playwright extends Helper {
1856
1856
  }
1857
1857
 
1858
1858
  if (this.options.trace) {
1859
- const path = `${global.output_dir}/trace/${clearString(test.title)}.zip`;
1859
+ const path = `${global.output_dir}/trace/${clearString(test.title).slice(0, 255)}.zip`;
1860
1860
  await this.browserContext.tracing.stop({ path });
1861
1861
  test.artifacts.trace = path;
1862
1862
  }
@@ -1867,7 +1867,7 @@ class Playwright extends Helper {
1867
1867
  if (this.options.keepVideoForPassedTests) {
1868
1868
  test.artifacts.video = await this.page.video().path();
1869
1869
  } else {
1870
- this.page.video().delete();
1870
+ this.page.video().delete().catch(e => {});
1871
1871
  }
1872
1872
  }
1873
1873
 
@@ -129,8 +129,9 @@ const consoleLogStore = new Console();
129
129
  * }
130
130
  * }
131
131
  * ```
132
+ * > Note: When connecting to remote browser `show` and specific `chrome` options (e.g. `headless` or `devtools`) are ignored.
132
133
  *
133
- * #### Example #5: Target URL with provided basic authentication
134
+ * #### Example #5: Target URL with provided basic authentication
134
135
  *
135
136
  * ```js
136
137
  * {
@@ -143,10 +144,25 @@ const consoleLogStore = new Console();
143
144
  * }
144
145
  * }
145
146
  * ```
147
+ * #### Troubleshooting
146
148
  *
149
+ * Error Message: `No usable sandbox!`
150
+ *
151
+ * When running Puppeteer on CI try to disable sandbox if you see that message
152
+ *
153
+ * ```
154
+ * helpers: {
155
+ * Puppeteer: {
156
+ * url: 'http://localhost',
157
+ * show: false,
158
+ * chrome: {
159
+ * args: ['--no-sandbox', '--disable-setuid-sandbox']
160
+ * }
161
+ * },
162
+ * }
163
+ * ```
147
164
  *
148
165
  *
149
- * Note: When connecting to remote browser `show` and specific `chrome` options (e.g. `headless` or `devtools`) are ignored.
150
166
  *
151
167
  * ## Access From Helpers
152
168
  *
@@ -17,12 +17,6 @@ class MochaFactory {
17
17
  output.process(opts.child);
18
18
  mocha.ui(scenarioUi);
19
19
 
20
- // process.on('unhandledRejection', (reason) => {
21
- // output.error('Unhandled rejection');
22
- // console.log(Error.captureStackTrace(reason));
23
- // output.error(reason);
24
- // });
25
-
26
20
  Mocha.Runner.prototype.uncaught = function (err) {
27
21
  if (err) {
28
22
  if (err.toString().indexOf('ECONNREFUSED') >= 0) {
@@ -51,19 +45,30 @@ class MochaFactory {
51
45
 
52
46
  // add ids for each test and check uniqueness
53
47
  const dupes = [];
48
+ let missingFeatureInFile = [];
54
49
  const seenTests = [];
55
50
  mocha.suite.eachTest(test => {
56
51
  test.id = genTestId(test);
52
+
57
53
  const name = test.fullTitle();
58
54
  if (seenTests.includes(test.id)) {
59
55
  dupes.push(name);
60
56
  }
61
57
  seenTests.push(test.id);
58
+
59
+ if (name.slice(0, name.indexOf(':')) === '') {
60
+ missingFeatureInFile.push(test.file);
61
+ }
62
62
  });
63
63
  if (dupes.length) {
64
64
  // ideally this should be no-op and throw (breaking change)...
65
65
  output.error(`Duplicate test names detected - Feature + Scenario name should be unique:\n${dupes.join('\n')}`);
66
66
  }
67
+
68
+ if (missingFeatureInFile.length) {
69
+ missingFeatureInFile = [...new Set(missingFeatureInFile)];
70
+ output.error(`Missing Feature section in:\n${missingFeatureInFile.join('\n')}`);
71
+ }
67
72
  }
68
73
  };
69
74
 
@@ -1,4 +1,4 @@
1
- const debug = require('debug')('codeceptjs:plugin:puppeteerCoverage');
1
+ const debugModule = require('debug');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
 
@@ -13,7 +13,7 @@ const defaultConfig = {
13
13
  uniqueFileName: true,
14
14
  };
15
15
 
16
- const supportedHelpers = ['Puppeteer'];
16
+ const supportedHelpers = ['Puppeteer', 'Playwright'];
17
17
 
18
18
  function buildFileName(test, uniqueFileName) {
19
19
  let fileName = clearString(test.title);
@@ -41,15 +41,14 @@ function buildFileName(test, uniqueFileName) {
41
41
  }
42
42
 
43
43
  /**
44
- * Dumps puppeteers code coverage after every test.
44
+ * Dumps code coverage from Playwright/Puppeteer after every test.
45
45
  *
46
46
  * #### Configuration
47
47
  *
48
- * Configuration can either be taken from a corresponding helper (deprecated) or a from plugin config (recommended).
49
48
  *
50
49
  * ```js
51
50
  * plugins: {
52
- * puppeteerCoverage: {
51
+ * playwrightCoverage: {
53
52
  * enabled: true
54
53
  * }
55
54
  * }
@@ -59,33 +58,22 @@ function buildFileName(test, uniqueFileName) {
59
58
  *
60
59
  * * `coverageDir`: directory to dump coverage files
61
60
  * * `uniqueFileName`: generate a unique filename by adding uuid
62
- *
63
- * First of all, your mileage may vary!
64
- *
65
- * To work, you need the client javascript code to be NOT uglified. They need to be built in "development" mode.
66
- * And the end of your tests, you'll get a directory full of coverage per test run. Now what?
67
- * You'll need to convert the coverage code to something istanbul can read. Good news is someone wrote the code
68
- * for you (see puppeteer-to-istanbul link below). Then using istanbul you need to combine the converted
69
- * coverage and create a report. Good luck!
70
- *
71
- * Links:
72
- * * https://github.com/GoogleChrome/puppeteer/blob/v1.12.2/docs/api.md#class-coverage
73
- * * https://github.com/istanbuljs/puppeteer-to-istanbul
74
- * * https://github.com/gotwarlost/istanbul
75
61
  */
76
62
  module.exports = function (config) {
77
63
  const helpers = Container.helpers();
78
64
  let coverageRunning = false;
79
65
  let helper;
80
66
 
67
+ let debug;
81
68
  for (const helperName of supportedHelpers) {
82
69
  if (Object.keys(helpers).indexOf(helperName) > -1) {
83
70
  helper = helpers[helperName];
71
+ debug = debugModule(`codeceptjs:plugin:${helperName.toLowerCase()}Coverage`);
84
72
  }
85
73
  }
86
74
 
87
75
  if (!helper) {
88
- console.error('Coverage is only supported in Puppeteer');
76
+ console.error('Coverage is only supported in Puppeteer, Playwright');
89
77
  return; // no helpers for screenshot
90
78
  }
91
79
 
@@ -102,7 +90,7 @@ module.exports = function (config) {
102
90
  'starting coverage',
103
91
  async () => {
104
92
  try {
105
- if (!coverageRunning) {
93
+ if (!coverageRunning && helper.page && helper.page.coverage) {
106
94
  debug('--> starting coverage <--');
107
95
  coverageRunning = true;
108
96
  await helper.page.coverage.startJSCoverage();
@@ -115,13 +103,13 @@ module.exports = function (config) {
115
103
  );
116
104
  });
117
105
 
118
- // Save puppeteer coverage data after every test run
106
+ // Save coverage data after every test run
119
107
  event.dispatcher.on(event.test.after, async (test) => {
120
108
  recorder.add(
121
109
  'saving coverage',
122
110
  async () => {
123
111
  try {
124
- if (coverageRunning) {
112
+ if (coverageRunning && helper.page && helper.page.coverage) {
125
113
  debug('--> stopping coverage <--');
126
114
  coverageRunning = false;
127
115
  const coverage = await helper.page.coverage.stopJSCoverage();
@@ -38,7 +38,7 @@ const defaultConfig = {
38
38
  * // in codecept.conf.js
39
39
  * plugins: {
40
40
  * customLocator: {
41
- * enabled: true
41
+ * enabled: true,
42
42
  * attribute: 'data-test'
43
43
  * }
44
44
  * }
@@ -57,7 +57,7 @@ const defaultConfig = {
57
57
  * // in codecept.conf.js
58
58
  * plugins: {
59
59
  * customLocator: {
60
- * enabled: true
60
+ * enabled: true,
61
61
  * prefix: '=',
62
62
  * attribute: 'data-qa'
63
63
  * }
@@ -0,0 +1,88 @@
1
+ const { v4: uuidv4 } = require('uuid');
2
+ const fsPromise = require('fs').promises;
3
+ const path = require('path');
4
+ const event = require('../event');
5
+
6
+ // This will convert a given timestamp in milliseconds to
7
+ // an SRT recognized timestamp, ie HH:mm:ss,SSS
8
+ function formatTimestamp(timestampInMs) {
9
+ const date = new Date(0, 0, 0, 0, 0, 0, timestampInMs);
10
+ const hours = date.getHours();
11
+ const minutes = date.getMinutes();
12
+ const seconds = date.getSeconds();
13
+ const ms = timestampInMs - (hours * 3600000 + minutes * 60000 + seconds * 1000);
14
+ return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')},${ms.toString().padStart(3, '0')}`;
15
+ }
16
+
17
+ let steps = {};
18
+ let testStartedAt;
19
+ /**
20
+ * Automatically captures steps as subtitle, and saves it as an artifact when a video is found for a failed test
21
+ *
22
+ * #### Configuration
23
+ * ```js
24
+ * plugins: {
25
+ * subtitles: {
26
+ * enabled: true
27
+ * }
28
+ * }
29
+ * ```
30
+ */
31
+ module.exports = function () {
32
+ event.dispatcher.on(event.test.before, (_) => {
33
+ testStartedAt = Date.now();
34
+ steps = {};
35
+ });
36
+
37
+ event.dispatcher.on(event.step.started, (step) => {
38
+ const stepStartedAt = Date.now();
39
+ step.id = uuidv4();
40
+
41
+ let title = `${step.actor}.${step.name}(${step.args ? step.args.join(',') : ''})`;
42
+ if (title.length > 100) {
43
+ title = `${title.substring(0, 100)}...`;
44
+ }
45
+
46
+ steps[step.id] = {
47
+ start: formatTimestamp(stepStartedAt - testStartedAt),
48
+ startedAt: stepStartedAt,
49
+ title,
50
+ };
51
+ });
52
+
53
+ event.dispatcher.on(event.step.finished, (step) => {
54
+ if (step && step.id && steps[step.id]) {
55
+ steps[step.id].end = formatTimestamp(Date.now() - testStartedAt);
56
+ }
57
+ });
58
+
59
+ event.dispatcher.on(event.test.after, async (test) => {
60
+ if (test && test.artifacts && test.artifacts.video) {
61
+ const stepsSortedByStartTime = Object.values(steps);
62
+ stepsSortedByStartTime.sort((stepA, stepB) => {
63
+ return stepA.startedAt - stepB.startedAt;
64
+ });
65
+
66
+ let subtitle = '';
67
+
68
+ // For an SRT file, every subtitle has to be in the format as mentioned below:
69
+ //
70
+ // 1
71
+ // HH:mm:ss,SSS --> HH:mm:ss,SSS
72
+ // [title]
73
+ stepsSortedByStartTime.forEach((step, index) => {
74
+ if (step.end) {
75
+ subtitle = `${subtitle}${index + 1}
76
+ ${step.start} --> ${step.end}
77
+ ${step.title}
78
+
79
+ `;
80
+ }
81
+ });
82
+
83
+ const { dir: artifactsDirectory, name: fileName } = path.parse(test.artifacts.video);
84
+ await fsPromise.writeFile(`${artifactsDirectory}/${fileName}.srt`, subtitle);
85
+ test.artifacts.subtitle = `${artifactsDirectory}/${fileName}.srt`;
86
+ }
87
+ });
88
+ };
@@ -89,7 +89,7 @@ function tryTo(callback) {
89
89
  recorder.session.catch((err) => {
90
90
  result = false;
91
91
  const msg = err.inspect ? err.inspect() : err.toString();
92
- debug(`Unsuccesful try > ${msg}`);
92
+ debug(`Unsuccessful try > ${msg}`);
93
93
  recorder.session.restore('tryTo');
94
94
  return result;
95
95
  });
package/lib/step.js CHANGED
@@ -212,16 +212,18 @@ class MetaStep extends Step {
212
212
  step.metaStep = this;
213
213
  };
214
214
  event.dispatcher.prependListener(event.step.before, registerStep);
215
+ let rethrownError = null;
215
216
  try {
216
217
  this.startTime = Date.now();
217
218
  result = fn.apply(this.context, this.args);
218
219
  } catch (error) {
219
- this.status = 'failed';
220
+ this.setStatus('failed');
221
+ rethrownError = error;
220
222
  } finally {
221
223
  this.endTime = Date.now();
222
-
223
224
  event.dispatcher.removeListener(event.step.before, registerStep);
224
225
  }
226
+ if (rethrownError) { throw rethrownError; }
225
227
  return result;
226
228
  }
227
229
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeceptjs",
3
- "version": "3.1.1",
3
+ "version": "3.1.2",
4
4
  "description": "Supercharged End 2 End Testing Framework for NodeJS",
5
5
  "keywords": [
6
6
  "acceptance",
@@ -84,7 +84,8 @@
84
84
  "promise-retry": "^1.1.1",
85
85
  "requireg": "^0.2.2",
86
86
  "resq": "^1.10.0",
87
- "sprintf-js": "^1.1.1"
87
+ "sprintf-js": "^1.1.1",
88
+ "uuid": "^8.3.2"
88
89
  },
89
90
  "devDependencies": {
90
91
  "@codeceptjs/detox-helper": "^1.0.2",
@@ -948,6 +948,16 @@ declare namespace CodeceptJS {
948
948
  * @returns attribute value
949
949
  */
950
950
  grabValueFrom(locator: CodeceptJS.LocatorOrString): Promise<string>;
951
+ /**
952
+ * Saves a screenshot to ouput folder (set in codecept.json or codecept.conf.js).
953
+ * Filename is relative to output folder.
954
+ *
955
+ * ```js
956
+ * I.saveScreenshot('debug.png');
957
+ * ```
958
+ * @param fileName - file name to save.
959
+ */
960
+ saveScreenshot(fileName: string): void;
951
961
  /**
952
962
  * Scroll element into viewport.
953
963
  *
@@ -5374,6 +5384,7 @@ declare namespace CodeceptJS {
5374
5384
  * }
5375
5385
  * }
5376
5386
  * ```
5387
+ * > Note: When connecting to remote browser `show` and specific `chrome` options (e.g. `headless` or `devtools`) are ignored.
5377
5388
  *
5378
5389
  * #### Example #5: Target URL with provided basic authentication
5379
5390
  *
@@ -5388,10 +5399,25 @@ declare namespace CodeceptJS {
5388
5399
  * }
5389
5400
  * }
5390
5401
  * ```
5402
+ * #### Troubleshooting
5391
5403
  *
5404
+ * Error Message: `No usable sandbox!`
5405
+ *
5406
+ * When running Puppeteer on CI try to disable sandbox if you see that message
5407
+ *
5408
+ * ```
5409
+ * helpers: {
5410
+ * Puppeteer: {
5411
+ * url: 'http://localhost',
5412
+ * show: false,
5413
+ * chrome: {
5414
+ * args: ['--no-sandbox', '--disable-setuid-sandbox']
5415
+ * }
5416
+ * },
5417
+ * }
5418
+ * ```
5392
5419
  *
5393
5420
  *
5394
- * Note: When connecting to remote browser `show` and specific `chrome` options (e.g. `headless` or `devtools`) are ignored.
5395
5421
  *
5396
5422
  * ## Access From Helpers
5397
5423
  *