├── CHANGELOG.md ├── LICENSE ├── LICENSE.aventstack ├── README.md ├── exceldb.png ├── extentreports-cucumber6-adapter ├── .gitignore ├── pom-nexus.xml ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── aventstack │ │ └── extentreports │ │ ├── cucumber │ │ └── adapter │ │ │ ├── ExtentCucumberAdapter.java │ │ │ ├── TestSourcesModel.java │ │ │ └── URLOutputStream.java │ │ └── service │ │ └── ExtentService.java │ └── test │ ├── java │ └── cucumber │ │ └── examples │ │ └── java │ │ └── calculator │ │ ├── Configurer.java │ │ ├── DateCalculator.java │ │ ├── DateStepdefs.java │ │ ├── Example.java │ │ ├── RpnCalculator.java │ │ ├── RpnCalculatorStepdefs.java │ │ ├── RunCukesTest.java │ │ ├── ScreenShotStepDefinition.java │ │ └── ShoppingStepdefs.java │ └── resources │ ├── com │ └── aventstack │ │ └── adapter │ │ ├── extent.properties │ │ ├── html-config.xml │ │ ├── klov.properties │ │ └── spark-config.xml │ ├── cucumber │ └── examples │ │ └── java │ │ └── calculator │ │ ├── basic_arithmetic.feature │ │ ├── date_calculator.feature │ │ ├── screen.feature │ │ ├── shopping.feature │ │ └── skipdef.feature │ └── logo.png └── summary.png /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | # 2.18.0 (2023-8-10) 5 | 6 | * Updated extent report version 7 | * Updated java version to 11 8 | * Updated dependencies version 9 | 10 | # 2.17.0 (2023-05-12) 11 | 12 | * Update pdf report version 13 | 14 | # 2.16.0 (2023-02-01) 15 | 16 | * Added excel reporter 17 | 18 | # 2.15.0 (2022-11-14) 19 | 20 | * Add support for status filter for html report 21 | * Updated pdf report data collector version to 2.9.1 22 | 23 | # 2.14.3 (2022-11-11) 24 | 25 | * Add support for status filter for PDF report [Issue 55](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/55) 26 | 27 | # 2.14.2 (2022-11-10) 28 | 29 | * Add support for status filter for Spark report [Issue 54](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/54) 30 | 31 | # 2.14.1 (2022-11-07) 32 | 33 | * Updated pdf report data collector version to 2.8.0 34 | 35 | # 2.14.0 (2022-11-05) 36 | 37 | * Add device and author information as tags [Issue 52](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/52) 38 | * Base folder report name custom delimiter setting [Issue 53](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/53) 39 | 40 | # 2.13.1 (2022-11-02) 41 | 42 | * Fixed tag count & test display for scenario outline [Issue 51](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/51) 43 | 44 | # 2.13.0 (2022-6-19) 45 | 46 | * Updated pdf report data collector version to 2.7.0 47 | 48 | # 2.12.0 (2022-6-11) 49 | 50 | * Updated pdf report data collector version to 2.6.0 51 | 52 | # 2.11.1 (2022-05-11) 53 | 54 | * Updated pdf report data collector version to 2.5.1 55 | 56 | # 2.11.0 (2022-05-09) 57 | 58 | * Updated pdf report data collector version to 2.5.0 59 | 60 | # 2.10.4 (2022-04-28) 61 | 62 | * Updated pdf report data collector version to 2.4.1 63 | 64 | # 2.10.3 (2022-04-23) 65 | 66 | * Updated pdf report data collector version to 2.4.0 67 | 68 | # 2.10.2 (2022-04-12) 69 | 70 | * Updated pdf report data collector version to 2.3.0 71 | 72 | # 2.10.1 (2022-04-09) 73 | * Updated HTML extent report version 74 | 75 | # 2.10.0 (2022-03-06) 76 | 77 | * Updated pdf report data collector version to 2.1.0 78 | 79 | # 2.9.0 (2022-01-16) 80 | 81 | * Add HTMLExtentReporter to adapter [Issue 48](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/48) 82 | * Scenario Outline with non default dialect display issue [Issue 47](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/47) 83 | 84 | # 2.8.4 (2021-09-14) 85 | 86 | * Updated pdf report data collector version to 1.7.4 87 | 88 | # 2.8.3 (2021-08-3) 89 | 90 | * Remove temporary base64 images for pdf [Issue 40](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/40)] 91 | * Updated pdf reporter version to 2.2.4 92 | 93 | # 2.8.2 (2021-06-21) 94 | 95 | * Updated pdf reporter version to 2.2.3 96 | 97 | # 2.8.1 (2021-04-20) 98 | 99 | * Updated pdf reporter version to 2.2.1 100 | 101 | # 2.8.0 (2021-04-16) 102 | 103 | * Store step pending status exception stacktrace [Issue 37](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/37) 104 | * Updated extentreport version to 5.0.6 105 | * Updated pdf reporter version to 2.2.0 106 | 107 | # 2.7.0 (2021-03-09) 108 | 109 | * Incorrect scenario count for tags\categories [Issue 33](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/33) 110 | 111 | # 2.6.0 (2020-12-13) 112 | 113 | * Support to attach base64 images [Issue 11](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/11) 114 | 115 | # 2.5.0 (2020-11-19) 116 | 117 | * Support dialect in adapter [Issue 29](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/29) 118 | * Upgrade extentreport version 119 | * Upgrade pdf reporter version 120 | 121 | # 2.4.0 (2020-11-05) 122 | 123 | * Klov reporter initialization fails with NPE [Issue 26](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/26) 124 | * Parallel execution does not work for junit runner [Issue 27](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/27) 125 | * Fail pending step in strict mode [Issue 25](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/25) 126 | * Upgrade pdf reporter version 127 | 128 | ## 2.3.0 (2020-10-24) 129 | 130 | * "Passed" Step duration not correct when multiple logs are attached [Issue 24](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/24) 131 | 132 | ## 2.2.0 (2020-10-21) 133 | 134 | * Option to default your report to Dashboard [Issue 16](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/16) 135 | * Add support for pdf reporter [Issue 20](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/20) 136 | * Ability to determine hook type [Issue 21](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/21) 137 | 138 | ## 2.1.0 (2020-10-14) 139 | 140 | * Fixed missing parameterized values in scenario outline names [Issue 15](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/15) 141 | 142 | ## 2.0.0 (2020-09-06) 143 | 144 | * Upgrade adapter to support extent version 5 [Issue 5](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/5) 145 | * Remove StrictAware interface as deprecated [Issue 4](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/4) 146 | 147 | ## 1.2.0 (2020-08-11) 148 | 149 | * Add date and time to report folders [Issue 8](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/8) 150 | * Fix report folder name issue [Issue 9](https://github.com/grasshopper7/extentreports-cucumber5-adapter/issues/9) 151 | 152 | ## 1.1.0 (2020-07-21) 153 | 154 | * Support for Json formatter [Issue 3](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/3) 155 | * Fixed docstring display [Issue 2](https://github.com/grasshopper7/extentreports-cucumber6-adapter/issues/2) 156 | 157 | ## 1.0.0 (2020-06-13) 158 | 159 | * Released based on extentreports-cucumber5-adapter 160 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 grasshopper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.aventstack: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 aventstack 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This deals with generating **Extent reports for Cucumber-JVM version 6** using the ExtentReports Cucumber **Adapter Plugin**. For more details refer to this [article](http://ghchirp.site/2098/). A sample usage of this adapter can be found [here](https://github.com/grasshopper7/cuke6-extent-adapter-report). 2 | 3 | To **build** from source use ```install -Dmaven.test.failure.ignore=true``` or ```install -Dmaven.test.skip=true```. This ignores intentional test failures from stopping the build. 4 | 5 | **Dependency** - To work with the latest [ExtentReports version 5](https://github.com/extent-framework/extentreports-java/wiki), which includes support for latest **Spark, PDF *(New Feature, check 'Extent PDF Reporter' section below)*, Ported HTML *(New Feature, check 'HTML Reporter' section)*, Klov and Json** reporters, add the below dependency. The HTML and Logger reporters, among others, have been deprecated. For more details refer to the **POM Dependencies** section of the [article](http://ghchirp.site/2098/). 6 | 7 | ``` 8 | 9 | tech.grasshopper 10 | extentreports-cucumber6-adapter 11 | 2.18.0 12 | 13 | ``` 14 | 15 | To work with the older ExtentReports version 4, add the below dependency. 16 | 17 | ``` 18 | 19 | tech.grasshopper 20 | extentreports-cucumber6-adapter 21 | 1.2.0 22 | 23 | ``` 24 | 25 | **Report Attachments** - To add attachments, like screen images, two settings need to be added to the extent.properties. First property, named **screenshot.dir**, is the directory where the attachments are stored. Second is **screenshot.rel.path**, which is the relative path from the report file to the screenshot directory. For more details refer to the **Report Attachments** section of the [article](http://ghchirp.site/2098/). 26 | 27 | ``` 28 | extent.reporter.spark.out=test-output/SparkReport/Spark.html 29 | 30 | screenshot.dir=test-output/ 31 | screenshot.rel.path=../ 32 | ``` 33 | 34 | 35 | **Extent Excel Reporter** *(NEW FEATURE)* - The Excel reporter summarizes the test run results in a **dashboard** and other worksheets with **feature, scenario, exception, tags, authors, devices details**. A complete sample report can be found [here](https://github.com/grasshopper7/cuke6-extent-adapter-report/tree/master/cuke6-extent-adapter-report/excelreport). 36 | 37 | 38 | ![sample](https://raw.githubusercontent.com/grasshopper7/extentreports-cucumber6-adapter/master/exceldb.png) 39 | 40 | 41 | The Excel report needs to be enabled in the extent.properties file. 42 | ``` 43 | extent.reporter.excel.start=true 44 | extent.reporter.excel.out=test output/ExcelReport/ExtentExcel.xlsx 45 | ``` 46 | 47 | 48 | **Extent PDF Reporter** *(NEW FEATURE)* - The PDF reporter summarizes the test run results in a **dashboard** and other sections with **feature, scenario and step details**. 49 | 50 | 51 | ![sample](https://raw.githubusercontent.com/grasshopper7/extentreports-cucumber6-adapter/master/summary.png) 52 | 53 | 54 | The PDF report is needs to be enabled in the extent.properties file. 55 | ``` 56 | extent.reporter.pdf.start=true 57 | extent.reporter.pdf.out=test output/PdfReport/ExtentPdf.pdf 58 | ``` 59 | The default color settings can be modified by using a YAML config file, named pdf-config.yaml in the project src/test/resource folder. The detailed documentation for this feature is available in this [article](http://ghchirp.site/2224/). 60 | 61 | 62 | **Ported HTML Reporter** *(NEW FEATURE)* - The original HTML Extent Reporter was deprecated in 4.1.3 and removed in 5.0.0. The HTML report available in the adapter is based on the same code base and is similar in appearance. The major changes are in the Freemarker template code which have been modified to work with the Extent Reports version 5. 63 | 64 | The HTML report needs to be enabled in the extent.properties file. 65 | ``` 66 | extent.reporter.html.start=true 67 | extent.reporter.html.out=test-output/HtmlReport/ExtentHtml.html 68 | ``` 69 | 70 | **Customized Report Folder Name** *(NEW FEATURE)* - To enable report folder name with date and\or time details, two settings need to be added to the extent.properties. These are **basefolder.name** and **basefolder.datetimepattern**. These will be merged to create the base folder name, inside which the reports will be generated. The basefolder.datetimepattern value should be a **valid date time formatter pattern**. For more details refer to the **Customized Report Folder Name** section of the [article](http://ghchirp.site/2098/). 71 | 72 | ``` 73 | extent.reporter.spark.out=test-output/SparkReport/ 74 | 75 | screenshot.dir=test-output/ 76 | screenshot.rel.path=../ 77 | 78 | basefolder.name=reports 79 | basefolder.datetimepattern=d-MMM-YY HH-mm-ss 80 | ``` 81 | 82 | With the above settings, a base folder with the name '**reports 10-Aug-20 10-25-50**' will contain the reports. Screenshots if any, will be located inside the '**reports 10-Aug-20 10-25-50/test-output**' folder structure. Similarly the report will be created in the '**reports 10-Aug-20 10-25-50/test-output/SparkReport**' folder structure. 83 | 84 | **Attach Image as Base64 String** *(NEW FEATURE)* - This feature can be used to attach images to the Spark report by setting the **src attribute of the img tag to a Base64 encoded string** of the image. When this feature is used, no physical file is created. There is no need to modify any step definition code to use this. To enable this, use the below settings in extent.properties, which is false by default. 85 | 86 | ``` 87 | extent.reporter.spark.base64imagesrc=true 88 | ``` 89 | The Spark report file size will be **pretty large and there could be memory issues** if a substantial number of images are present. A generic thumbnail is created and on clicking the image is displayed. 90 | 91 | **Environment or System Info Properties** *(NEW FEATURE)* - It is now possible to add environment or system info properties in the extent.properties or pass them in the maven command line. The key string should begin with the prefix - 'systeminfo.'. **Be careful of the dot at the end**. For more details refer to the **Environment or System Info Properties** section of the [article](http://ghchirp.site/2098/). 92 | 93 | ``` 94 | systeminfo.os=windows 95 | ``` 96 | -------------------------------------------------------------------------------- /exceldb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grasshopper7/extentreports-cucumber6-adapter/83dad648d859c244e33fce99238c378df05987fa/exceldb.png -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | 3 | /test-output/ 4 | 5 | 6 | /.settings/ 7 | 8 | 9 | 10 | /.classpath 11 | 12 | 13 | 14 | /.project -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/pom-nexus.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | tech.grasshopper 7 | extentreports-cucumber6-adapter 8 | 2.18.0 9 | extentreports-cucumber6-adapter 10 | https://ghchirp.site/2098/ 11 | Cucumber-JVM 6 adapter for Extent Framework 12 | 13 | 14 | 15 | MIT License 16 | http://www.opensource.org/licenses/mit-license.php 17 | 18 | 19 | 20 | 21 | 22 | Grass Hopper 23 | grass.hopper.moc@gmail.com 24 | Grasshopper 25 | http://ghchirp.site/ 26 | 27 | 28 | 29 | 30 | UTF-8 31 | UTF-8 32 | 6.10.4 33 | 5.0.9 34 | 2.12.0 35 | 1.1.0 36 | 1.4.0 37 | 38 | 39 | 40 | scm:git:git://github.com/grasshopper7/extentreports-cucumber6-adapter.git 41 | scm:git:ssh://github.com:grasshopper7/extentreports-cucumber6-adapter.git 42 | https://github.com/grasshopper7/extentreports-cucumber6-adapter 43 | 44 | 45 | 46 | 47 | com.aventstack 48 | extentreports 49 | ${extent.version} 50 | 51 | 52 | tech.grasshopper 53 | extent-pdf-report 54 | ${pdfreporter.version} 55 | 56 | 57 | com.aventstack 58 | klov-reporter 59 | ${extent.version} 60 | 61 | 62 | tech.grasshopper 63 | htmlextentreporter 64 | ${htmlreporter.version} 65 | 66 | 67 | tech.grasshopper 68 | extent-excel-report 69 | ${excelreporter.version} 70 | 71 | 72 | io.cucumber 73 | cucumber-java 74 | ${cucumber.version} 75 | provided 76 | 77 | 78 | io.cucumber 79 | cucumber-testng 80 | ${cucumber.version} 81 | test 82 | 83 | 84 | org.mongodb 85 | mongodb-driver 86 | 3.3.0 87 | 88 | 89 | com.fasterxml.jackson.core 90 | jackson-databind 91 | 2.14.0 92 | test 93 | 94 | 95 | com.fasterxml.jackson.datatype 96 | jackson-datatype-jsr310 97 | 2.14.0 98 | test 99 | 100 | 101 | io.github.bonigarcia 102 | webdrivermanager 103 | 3.8.1 104 | test 105 | 106 | 107 | org.seleniumhq.selenium 108 | selenium-java 109 | 3.141.59 110 | test 111 | 112 | 113 | 114 | 115 | 116 | ossrh 117 | https://oss.sonatype.org/content/repositories/snapshots 118 | 119 | 120 | ossrh 121 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 122 | 123 | 124 | 125 | 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-compiler-plugin 130 | 3.8.1 131 | 132 | 11 133 | 11 134 | 135 | 136 | 137 | org.apache.maven.plugins 138 | maven-resources-plugin 139 | 3.2.0 140 | 141 | 142 | org.sonatype.plugins 143 | nexus-staging-maven-plugin 144 | 1.6.8 145 | true 146 | 147 | ossrh 148 | https://oss.sonatype.org/ 149 | true 150 | 151 | 152 | 153 | org.apache.maven.plugins 154 | maven-source-plugin 155 | 3.2.0 156 | 157 | 158 | attach-sources 159 | 160 | jar-no-fork 161 | 162 | 163 | 164 | 165 | 166 | org.apache.maven.plugins 167 | maven-javadoc-plugin 168 | 3.2.0 169 | 170 | 171 | attach-javadocs 172 | 173 | jar 174 | 175 | 176 | 177 | 178 | 179 | org.apache.maven.plugins 180 | maven-gpg-plugin 181 | 1.6 182 | 183 | 184 | sign-artifacts 185 | verify 186 | 187 | sign 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | tech.grasshopper 7 | extentreports-cucumber6-adapter 8 | 2.18.0 9 | extentreports-cucumber6-adapter 10 | https://ghchirp.site/2098/ 11 | Cucumber-JVM 6 adapter for Extent Framework 12 | 13 | 14 | 15 | MIT License 16 | http://www.opensource.org/licenses/mit-license.php 17 | 18 | 19 | 20 | 21 | 22 | Grass Hopper 23 | grass.hopper.moc@gmail.com 24 | Grasshopper 25 | http://ghchirp.site/ 26 | 27 | 28 | 29 | 30 | UTF-8 31 | UTF-8 32 | 6.10.4 33 | 5.1.0 34 | 2.12.0 35 | 1.1.0 36 | 1.4.0 37 | 38 | 39 | 40 | 41 | com.aventstack 42 | extentreports 43 | ${extent.version} 44 | 45 | 46 | tech.grasshopper 47 | extent-pdf-report 48 | ${pdfreporter.version} 49 | 50 | 51 | com.aventstack 52 | klov-reporter 53 | ${extent.version} 54 | 55 | 56 | tech.grasshopper 57 | htmlextentreporter 58 | ${htmlreporter.version} 59 | 60 | 61 | tech.grasshopper 62 | extent-excel-report 63 | ${excelreporter.version} 64 | 65 | 66 | io.cucumber 67 | cucumber-java 68 | ${cucumber.version} 69 | provided 70 | 71 | 72 | io.cucumber 73 | cucumber-testng 74 | ${cucumber.version} 75 | test 76 | 77 | 78 | org.mongodb 79 | mongodb-driver 80 | 3.3.0 81 | 82 | 83 | com.fasterxml.jackson.core 84 | jackson-databind 85 | 2.14.0 86 | test 87 | 88 | 89 | com.fasterxml.jackson.datatype 90 | jackson-datatype-jsr310 91 | 2.14.0 92 | test 93 | 94 | 95 | io.github.bonigarcia 96 | webdrivermanager 97 | 3.8.1 98 | test 99 | 100 | 101 | org.seleniumhq.selenium 102 | selenium-java 103 | 3.141.59 104 | test 105 | 106 | 107 | 108 | 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-compiler-plugin 113 | 3.8.1 114 | 115 | 11 116 | 11 117 | 118 | 119 | 120 | org.apache.maven.plugins 121 | maven-resources-plugin 122 | 3.2.0 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/main/java/com/aventstack/extentreports/cucumber/adapter/ExtentCucumberAdapter.java: -------------------------------------------------------------------------------- 1 | package com.aventstack.extentreports.cucumber.adapter; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | import java.io.UnsupportedEncodingException; 7 | import java.net.URI; 8 | import java.net.URISyntaxException; 9 | import java.net.URL; 10 | import java.nio.file.Paths; 11 | import java.util.ArrayList; 12 | import java.util.Base64; 13 | import java.util.Collection; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.Set; 18 | import java.util.concurrent.ConcurrentHashMap; 19 | import java.util.concurrent.atomic.AtomicInteger; 20 | import java.util.stream.Collectors; 21 | 22 | import com.aventstack.extentreports.ExtentTest; 23 | import com.aventstack.extentreports.GherkinKeyword; 24 | import com.aventstack.extentreports.MediaEntityBuilder; 25 | import com.aventstack.extentreports.Status; 26 | import com.aventstack.extentreports.gherkin.model.Asterisk; 27 | import com.aventstack.extentreports.gherkin.model.ScenarioOutline; 28 | import com.aventstack.extentreports.markuputils.MarkupHelper; 29 | import com.aventstack.extentreports.model.Test; 30 | import com.aventstack.extentreports.service.ExtentService; 31 | 32 | import io.cucumber.core.exception.CucumberException; 33 | import io.cucumber.messages.Messages.GherkinDocument.Feature; 34 | import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario; 35 | import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario.Examples; 36 | import io.cucumber.messages.Messages.GherkinDocument.Feature.Step; 37 | import io.cucumber.messages.Messages.GherkinDocument.Feature.TableRow; 38 | import io.cucumber.messages.Messages.GherkinDocument.Feature.TableRow.TableCell; 39 | import io.cucumber.plugin.ConcurrentEventListener; 40 | import io.cucumber.plugin.event.DataTableArgument; 41 | import io.cucumber.plugin.event.DocStringArgument; 42 | import io.cucumber.plugin.event.EmbedEvent; 43 | import io.cucumber.plugin.event.EventHandler; 44 | import io.cucumber.plugin.event.EventPublisher; 45 | import io.cucumber.plugin.event.HookTestStep; 46 | import io.cucumber.plugin.event.PickleStepTestStep; 47 | import io.cucumber.plugin.event.Result; 48 | import io.cucumber.plugin.event.StepArgument; 49 | import io.cucumber.plugin.event.TestCase; 50 | import io.cucumber.plugin.event.TestCaseStarted; 51 | import io.cucumber.plugin.event.TestRunFinished; 52 | import io.cucumber.plugin.event.TestSourceRead; 53 | import io.cucumber.plugin.event.TestStepFinished; 54 | import io.cucumber.plugin.event.TestStepStarted; 55 | import io.cucumber.plugin.event.WriteEvent; 56 | 57 | /** 58 | * A port of Cucumber-JVM (MIT licensed) HtmlFormatter for Extent Framework 59 | * Original source: 60 | * https://github.com/cucumber/cucumber-jvm/blob/master/core/src/main/java/cucumber/runtime/formatter/HTMLFormatter.java 61 | * 62 | */ 63 | public class ExtentCucumberAdapter implements ConcurrentEventListener { 64 | 65 | private static Map featureMap = new ConcurrentHashMap<>(); 66 | private static ThreadLocal featureTestThreadLocal = new InheritableThreadLocal<>(); 67 | private static Map scenarioOutlineMap = new ConcurrentHashMap<>(); 68 | private static ThreadLocal scenarioOutlineThreadLocal = new InheritableThreadLocal<>(); 69 | private static ThreadLocal scenarioThreadLocal = new InheritableThreadLocal<>(); 70 | private static ThreadLocal isHookThreadLocal = new InheritableThreadLocal<>(); 71 | private static ThreadLocal stepTestThreadLocal = new InheritableThreadLocal<>(); 72 | private static ThreadLocal> featureTagsThreadLocal = new InheritableThreadLocal<>(); 73 | private static ThreadLocal> scenarioOutlineTagsThreadLocal = new InheritableThreadLocal<>(); 74 | 75 | @SuppressWarnings("serial") 76 | private static final Map MIME_TYPES_EXTENSIONS = new HashMap() { 77 | { 78 | put("image/bmp", "bmp"); 79 | put("image/gif", "gif"); 80 | put("image/jpeg", "jpeg"); 81 | put("image/jpg", "jpg"); 82 | put("image/png", "png"); 83 | put("image/svg+xml", "svg"); 84 | // TODO Video, txt, html, pdf etc. 85 | // put("video/ogg", "ogg"); 86 | // put("video/mp4", "mp4"); 87 | } 88 | }; 89 | 90 | private static final AtomicInteger EMBEDDED_INT = new AtomicInteger(0); 91 | 92 | private final TestSourcesModel testSources = new TestSourcesModel(); 93 | 94 | private ThreadLocal currentFeatureFile = new ThreadLocal<>(); 95 | private ThreadLocal currentScenarioOutline = new InheritableThreadLocal<>(); 96 | private ThreadLocal currentExamples = new InheritableThreadLocal<>(); 97 | 98 | private EventHandler testSourceReadHandler = new EventHandler() { 99 | @Override 100 | public void receive(TestSourceRead event) { 101 | handleTestSourceRead(event); 102 | } 103 | }; 104 | private EventHandler caseStartedHandler = new EventHandler() { 105 | @Override 106 | public void receive(TestCaseStarted event) { 107 | handleTestCaseStarted(event); 108 | } 109 | }; 110 | private EventHandler stepStartedHandler = new EventHandler() { 111 | @Override 112 | public void receive(TestStepStarted event) { 113 | handleTestStepStarted(event); 114 | } 115 | }; 116 | private EventHandler stepFinishedHandler = new EventHandler() { 117 | @Override 118 | public void receive(TestStepFinished event) { 119 | handleTestStepFinished(event); 120 | } 121 | }; 122 | private EventHandler embedEventhandler = new EventHandler() { 123 | @Override 124 | public void receive(EmbedEvent event) { 125 | handleEmbed(event); 126 | } 127 | }; 128 | private EventHandler writeEventhandler = new EventHandler() { 129 | @Override 130 | public void receive(WriteEvent event) { 131 | handleWrite(event); 132 | } 133 | }; 134 | private EventHandler runFinishedHandler = new EventHandler() { 135 | @Override 136 | public void receive(TestRunFinished event) { 137 | finishReport(); 138 | } 139 | }; 140 | 141 | public ExtentCucumberAdapter(String arg) { 142 | ExtentService.getInstance(); 143 | } 144 | 145 | @Override 146 | public void setEventPublisher(EventPublisher publisher) { 147 | publisher.registerHandlerFor(TestSourceRead.class, testSourceReadHandler); 148 | publisher.registerHandlerFor(TestCaseStarted.class, caseStartedHandler); 149 | publisher.registerHandlerFor(TestStepStarted.class, stepStartedHandler); 150 | publisher.registerHandlerFor(TestStepFinished.class, stepFinishedHandler); 151 | publisher.registerHandlerFor(EmbedEvent.class, embedEventhandler); 152 | publisher.registerHandlerFor(WriteEvent.class, writeEventhandler); 153 | publisher.registerHandlerFor(TestRunFinished.class, runFinishedHandler); 154 | } 155 | 156 | private void handleTestSourceRead(TestSourceRead event) { 157 | testSources.addTestSourceReadEvent(event.getUri(), event); 158 | } 159 | 160 | private synchronized void handleTestCaseStarted(TestCaseStarted event) { 161 | handleStartOfFeature(event.getTestCase()); 162 | handleScenarioOutline(event.getTestCase()); 163 | createTestCase(event.getTestCase()); 164 | } 165 | 166 | private synchronized void handleTestStepStarted(TestStepStarted event) { 167 | isHookThreadLocal.set(false); 168 | 169 | if (event.getTestStep() instanceof HookTestStep) { 170 | ExtentTest t = scenarioThreadLocal.get().createNode(Asterisk.class, event.getTestStep().getCodeLocation(), 171 | (((HookTestStep) event.getTestStep()).getHookType()).toString().toUpperCase()); 172 | stepTestThreadLocal.set(t); 173 | isHookThreadLocal.set(true); 174 | } 175 | 176 | if (event.getTestStep() instanceof PickleStepTestStep) { 177 | PickleStepTestStep testStep = (PickleStepTestStep) event.getTestStep(); 178 | createTestStep(testStep); 179 | } 180 | } 181 | 182 | private synchronized void handleTestStepFinished(TestStepFinished event) { 183 | updateResult(event.getResult()); 184 | } 185 | 186 | private synchronized void updateResult(Result result) { 187 | Test test = stepTestThreadLocal.get().getModel(); 188 | switch (result.getStatus().name().toLowerCase()) { 189 | case "failed": 190 | stepTestThreadLocal.get().fail(result.getError()); 191 | break; 192 | case "undefined": 193 | stepTestThreadLocal.get().fail("Step undefined"); 194 | break; 195 | case "pending": 196 | stepTestThreadLocal.get().fail(result.getError()); 197 | break; 198 | case "skipped": 199 | if (isHookThreadLocal.get()) { 200 | ExtentService.getInstance().removeTest(stepTestThreadLocal.get()); 201 | break; 202 | } 203 | boolean currentEndingEventSkipped = test.hasLog() 204 | ? test.getLogs().get(test.getLogs().size() - 1).getStatus() == Status.SKIP 205 | : false; 206 | if (result.getError() != null) { 207 | stepTestThreadLocal.get().skip(result.getError()); 208 | } else if (!currentEndingEventSkipped) { 209 | String details = result.getError() == null ? "Step skipped" : result.getError().getMessage(); 210 | stepTestThreadLocal.get().skip(details); 211 | } 212 | break; 213 | case "passed": 214 | if (stepTestThreadLocal.get() != null) { 215 | if (isHookThreadLocal.get()) { 216 | boolean mediaLogs = !test.getLogs().stream().filter(l -> l.getMedia() != null) 217 | .collect(Collectors.toList()).isEmpty(); 218 | if (!test.hasLog() && !mediaLogs) 219 | ExtentService.getInstance().removeTest(stepTestThreadLocal.get()); 220 | } 221 | stepTestThreadLocal.get().pass(""); 222 | } 223 | break; 224 | default: 225 | break; 226 | } 227 | } 228 | 229 | private synchronized void handleEmbed(EmbedEvent event) { 230 | 231 | String mimeType = event.getMediaType(); 232 | String extension = MIME_TYPES_EXTENSIONS.get(mimeType); 233 | if (extension != null) { 234 | if (stepTestThreadLocal.get() == null) { 235 | ExtentTest t = scenarioThreadLocal.get().createNode(Asterisk.class, "Embed"); 236 | stepTestThreadLocal.set(t); 237 | } 238 | 239 | String title = event.getName() == null ? "" : event.getName(); 240 | if (ExtentService.isBase64ImageSrcEnabled() && mimeType.startsWith("image/")) { 241 | stepTestThreadLocal.get().info(title, MediaEntityBuilder 242 | .createScreenCaptureFromBase64String(Base64.getEncoder().encodeToString(event.getData())) 243 | .build()); 244 | } else { 245 | StringBuilder fileName = new StringBuilder("embedded").append(EMBEDDED_INT.incrementAndGet()) 246 | .append(".").append(extension); 247 | try { 248 | URL url = toUrl(fileName.toString()); 249 | writeBytesToURL(event.getData(), url); 250 | try { 251 | File file = new File(url.toURI()); 252 | stepTestThreadLocal.get().info(title, 253 | MediaEntityBuilder 254 | .createScreenCaptureFromPath( 255 | ExtentService.getScreenshotReportRelatvePath() + file.getName()) 256 | .build()); 257 | } catch (URISyntaxException e) { 258 | e.printStackTrace(); 259 | } 260 | } catch (IOException e) { 261 | e.printStackTrace(); 262 | } 263 | } 264 | } 265 | } 266 | 267 | private static void writeBytesToURL(byte[] buf, URL url) throws IOException { 268 | OutputStream out = createReportFileOutputStream(url); 269 | try { 270 | out.write(buf); 271 | } catch (IOException e) { 272 | throw new IOException("Unable to write to report file item: ", e); 273 | } 274 | } 275 | 276 | private static OutputStream createReportFileOutputStream(URL url) { 277 | try { 278 | return new URLOutputStream(url); 279 | } catch (IOException | URISyntaxException e) { 280 | throw new CucumberException(e); 281 | } 282 | } 283 | 284 | private URL toUrl(String fileName) { 285 | try { 286 | URL url = Paths.get(ExtentService.getScreenshotFolderName(), fileName).toUri().toURL(); 287 | return url; 288 | } catch (IOException e) { 289 | throw new CucumberException(e); 290 | } 291 | } 292 | 293 | private synchronized void handleWrite(WriteEvent event) { 294 | String text = event.getText(); 295 | if (text != null && !text.isEmpty()) { 296 | stepTestThreadLocal.get().info(text); 297 | } 298 | } 299 | 300 | private void finishReport() { 301 | ExtentService.getInstance().flush(); 302 | } 303 | 304 | private synchronized void handleStartOfFeature(TestCase testCase) { 305 | if (currentFeatureFile == null || !currentFeatureFile.equals(testCase.getUri())) { 306 | currentFeatureFile.set(testCase.getUri()); 307 | createFeature(testCase); 308 | } 309 | } 310 | 311 | private synchronized void createFeature(TestCase testCase) { 312 | Feature feature = testSources.getFeature(testCase.getUri()); 313 | try { 314 | ExtentService.getInstance().setGherkinDialect(feature.getLanguage()); 315 | } catch (UnsupportedEncodingException e) { 316 | e.printStackTrace(); 317 | } 318 | 319 | if (feature != null) { 320 | if (featureMap.containsKey(feature.getName())) { 321 | featureTestThreadLocal.set(featureMap.get(feature.getName())); 322 | return; 323 | } 324 | if (featureTestThreadLocal.get() != null 325 | && featureTestThreadLocal.get().getModel().getName().equals(feature.getName())) { 326 | return; 327 | } 328 | ExtentTest t = ExtentService.getInstance().createTest( 329 | com.aventstack.extentreports.gherkin.model.Feature.class, feature.getName(), 330 | feature.getDescription()); 331 | featureTestThreadLocal.set(t); 332 | featureMap.put(feature.getName(), t); 333 | 334 | Set tagList = feature.getTagsList().stream().map(tag -> tag.getName()).collect(Collectors.toSet()); 335 | featureTagsThreadLocal.set(tagList); 336 | } 337 | } 338 | 339 | private synchronized void handleScenarioOutline(TestCase testCase) { 340 | TestSourcesModel.AstNode astNode = testSources.getAstNode(currentFeatureFile.get(), testCase.getLine()); 341 | Scenario scenarioDefinition = TestSourcesModel.getScenarioDefinition(astNode); 342 | 343 | // if (scenarioDefinition.getKeyword().equals("Scenario Outline")) { 344 | if (scenarioDefinition.getExamplesCount() > 0) { 345 | if (currentScenarioOutline.get() == null 346 | || !currentScenarioOutline.get().getName().equals(scenarioDefinition.getName())) { 347 | scenarioOutlineThreadLocal.set(null); 348 | createScenarioOutline(scenarioDefinition); 349 | currentScenarioOutline.set(scenarioDefinition); 350 | } 351 | Examples examples = (Examples) astNode.parent.node; 352 | if (currentExamples.get() == null || !currentExamples.get().equals(examples)) { 353 | currentExamples.set(examples); 354 | createExamples(examples); 355 | } 356 | } else { 357 | scenarioOutlineThreadLocal.set(null); 358 | currentScenarioOutline.set(null); 359 | currentExamples.set(null); 360 | } 361 | } 362 | 363 | private synchronized void createScenarioOutline(Scenario scenarioOutline) { 364 | if (scenarioOutlineMap.containsKey(scenarioOutline.getName())) { 365 | scenarioOutlineThreadLocal.set(scenarioOutlineMap.get(scenarioOutline.getName())); 366 | return; 367 | } 368 | if (scenarioOutlineThreadLocal.get() == null) { 369 | ExtentTest t = featureTestThreadLocal.get().createNode( 370 | com.aventstack.extentreports.gherkin.model.ScenarioOutline.class, scenarioOutline.getName(), 371 | scenarioOutline.getDescription()); 372 | scenarioOutlineThreadLocal.set(t); 373 | scenarioOutlineMap.put(scenarioOutline.getName(), t); 374 | 375 | /* 376 | * if (featureTagsThreadLocal.get() != null) { 377 | * featureTagsThreadLocal.get().forEach(x -> t.assignCategory(x)); } 378 | * scenarioOutline.getTagsList().stream().map(tag -> tag.getName()).forEach(c -> 379 | * t.assignCategory(c)); 380 | */ 381 | 382 | Set tagList = scenarioOutline.getTagsList().stream().map(tag -> tag.getName()) 383 | .collect(Collectors.toSet()); 384 | scenarioOutlineTagsThreadLocal.set(tagList); 385 | } 386 | } 387 | 388 | private void createExamples(Examples examples) { 389 | List rows = new ArrayList<>(); 390 | rows.add(examples.getTableHeader()); 391 | rows.addAll(examples.getTableBodyList()); 392 | String[][] data = getTable(rows); 393 | String markup = MarkupHelper.createTable(data).getMarkup(); 394 | if (examples.getName() != null && !examples.getName().isEmpty()) { 395 | markup = examples.getName() + markup; 396 | } 397 | markup = (scenarioOutlineThreadLocal.get().getModel().getDescription() == null ? "" 398 | : scenarioOutlineThreadLocal.get().getModel().getDescription() + "

") + markup; 399 | scenarioOutlineThreadLocal.get().getModel().setDescription(markup); 400 | } 401 | 402 | private String[][] getTable(List rows) { 403 | String data[][] = null; 404 | int rowSize = rows.size(); 405 | for (int i = 0; i < rowSize; i++) { 406 | TableRow row = rows.get(i); 407 | List cells = row.getCellsList(); 408 | int cellSize = cells.size(); 409 | if (data == null) { 410 | data = new String[rowSize][cellSize]; 411 | } 412 | for (int j = 0; j < cellSize; j++) { 413 | data[i][j] = cells.get(j).getValue(); 414 | } 415 | } 416 | return data; 417 | } 418 | 419 | private synchronized void createTestCase(TestCase testCase) { 420 | TestSourcesModel.AstNode astNode = testSources.getAstNode(currentFeatureFile.get(), testCase.getLine()); 421 | if (astNode != null) { 422 | Scenario scenarioDefinition = TestSourcesModel.getScenarioDefinition(astNode); 423 | ExtentTest parent = scenarioOutlineThreadLocal.get() != null ? scenarioOutlineThreadLocal.get() 424 | : featureTestThreadLocal.get(); 425 | ExtentTest t = parent.createNode(com.aventstack.extentreports.gherkin.model.Scenario.class, 426 | testCase.getName(), scenarioDefinition.getDescription()); 427 | scenarioThreadLocal.set(t); 428 | } 429 | if (!testCase.getTags().isEmpty()) { 430 | // testCase.getTags().forEach(x -> scenarioThreadLocal.get().assignCategory(x)); 431 | updateCategoryAndDeviceAndAuthor(testCase.getTags()); 432 | } 433 | if (featureTagsThreadLocal.get() != null) { 434 | // featureTagsThreadLocal.get().forEach(x -> 435 | // scenarioThreadLocal.get().assignCategory(x)); 436 | updateCategoryAndDeviceAndAuthor(featureTagsThreadLocal.get()); 437 | } 438 | 439 | Test parent = scenarioThreadLocal.get().getModel().getParent(); 440 | if (parent.getBddType() == ScenarioOutline.class && scenarioOutlineTagsThreadLocal.get() != null) { 441 | // scenarioOutlineTagsThreadLocal.get().forEach(x -> 442 | // scenarioThreadLocal.get().assignCategory(x)); 443 | updateCategoryAndDeviceAndAuthor(scenarioOutlineTagsThreadLocal.get()); 444 | } 445 | } 446 | 447 | private void updateCategoryAndDeviceAndAuthor(Collection tags) { 448 | tags.forEach(t -> { 449 | if (ExtentService.isDeviceEnabled() && isValidDeviceTag(t)) { 450 | 451 | scenarioThreadLocal.get().assignDevice(t.substring(ExtentService.getDevicePrefix().length())); 452 | } else if (ExtentService.isAuthorEnabled() && isValidAuthoTag(t)) { 453 | 454 | scenarioThreadLocal.get().assignAuthor(t.substring(ExtentService.getAuthorPrefix().length())); 455 | } else 456 | scenarioThreadLocal.get().assignCategory(t); 457 | }); 458 | } 459 | 460 | private boolean isValidDeviceTag(String tag) { 461 | if (tag.startsWith(ExtentService.getDevicePrefix()) && tag.length() > ExtentService.getDevicePrefix().length()) 462 | return true; 463 | return false; 464 | } 465 | 466 | private boolean isValidAuthoTag(String tag) { 467 | if (tag.startsWith(ExtentService.getAuthorPrefix()) && tag.length() > ExtentService.getAuthorPrefix().length()) 468 | return true; 469 | return false; 470 | } 471 | 472 | private synchronized void createTestStep(PickleStepTestStep testStep) { 473 | String stepName = testStep.getStep().getText(); 474 | TestSourcesModel.AstNode astNode = testSources.getAstNode(currentFeatureFile.get(), 475 | testStep.getStep().getLine()); 476 | if (astNode != null) { 477 | Step step = (Step) astNode.node; 478 | try { 479 | String name = stepName == null || stepName.isEmpty() 480 | ? step.getText().replace("<", "<").replace(">", ">") 481 | : stepName; 482 | ExtentTest t = scenarioThreadLocal.get().createNode(new GherkinKeyword(step.getKeyword().trim()), 483 | step.getKeyword() + name, testStep.getCodeLocation()); 484 | stepTestThreadLocal.set(t); 485 | } catch (ClassNotFoundException e) { 486 | e.printStackTrace(); 487 | } 488 | } 489 | StepArgument argument = testStep.getStep().getArgument(); 490 | if (argument != null) { 491 | if (argument instanceof DocStringArgument) { 492 | stepTestThreadLocal.get() 493 | .pass(MarkupHelper.createCodeBlock(((DocStringArgument) argument).getContent())); 494 | } else if (argument instanceof DataTableArgument) { 495 | stepTestThreadLocal.get() 496 | .pass(MarkupHelper.createTable(createDataTableList((DataTableArgument) argument))); 497 | } 498 | } 499 | } 500 | 501 | private String[][] createDataTableList(DataTableArgument dataTable) { 502 | List> cells = dataTable.cells(); 503 | int rowSize = cells.size(); 504 | int cellSize = cells.get(0).size(); 505 | String[][] data = new String[rowSize][cellSize]; 506 | 507 | for (int i = 0; i < rowSize; i++) { 508 | for (int j = 0; j < cellSize; j++) 509 | data[i][j] = cells.get(i).get(j); 510 | } 511 | return data; 512 | } 513 | 514 | // the below additions are from PR #33 515 | // https://github.com/extent-framework/extentreports-cucumber4-adapter/pull/33 516 | public static synchronized void addTestStepLog(String message) { 517 | stepTestThreadLocal.get().info(message); 518 | } 519 | 520 | public static synchronized void addTestStepScreenCaptureFromPath(String imagePath) throws IOException { 521 | stepTestThreadLocal.get().addScreenCaptureFromPath(imagePath); 522 | } 523 | 524 | public static synchronized void addTestStepScreenCaptureFromPath(String imagePath, String title) 525 | throws IOException { 526 | stepTestThreadLocal.get().addScreenCaptureFromPath(imagePath, title); 527 | } 528 | 529 | public static ExtentTest getCurrentStep() { 530 | return stepTestThreadLocal.get(); 531 | } 532 | 533 | } 534 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/main/java/com/aventstack/extentreports/cucumber/adapter/TestSourcesModel.java: -------------------------------------------------------------------------------- 1 | package com.aventstack.extentreports.cucumber.adapter; 2 | 3 | import io.cucumber.gherkin.Gherkin; 4 | import io.cucumber.messages.Messages; 5 | import io.cucumber.messages.Messages.GherkinDocument; 6 | import io.cucumber.messages.Messages.GherkinDocument.Feature; 7 | import io.cucumber.messages.Messages.GherkinDocument.Feature.Background; 8 | import io.cucumber.messages.Messages.GherkinDocument.Feature.FeatureChild; 9 | import io.cucumber.messages.Messages.GherkinDocument.Feature.FeatureChild.RuleChild; 10 | import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario; 11 | import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario.Examples; 12 | import io.cucumber.messages.Messages.GherkinDocument.Feature.Step; 13 | import io.cucumber.messages.Messages.GherkinDocument.Feature.TableRow; 14 | import io.cucumber.messages.internal.com.google.protobuf.GeneratedMessageV3; 15 | import io.cucumber.messages.internal.com.google.protobuf.Message; 16 | import io.cucumber.plugin.event.TestSourceRead; 17 | 18 | import java.io.File; 19 | import java.net.URI; 20 | import java.net.URISyntaxException; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.UUID; 25 | 26 | import static io.cucumber.gherkin.Gherkin.makeSourceEnvelope; 27 | import static java.util.Collections.singletonList; 28 | import static java.util.stream.Collectors.toList; 29 | 30 | final class TestSourcesModel { 31 | 32 | private final Map pathToReadEventMap = new HashMap<>(); 33 | private final Map pathToAstMap = new HashMap<>(); 34 | private final Map> pathToNodeMap = new HashMap<>(); 35 | 36 | static Scenario getScenarioDefinition(AstNode astNode) { 37 | AstNode candidate = astNode; 38 | while (candidate != null && !(candidate.node instanceof Scenario)) { 39 | candidate = candidate.parent; 40 | } 41 | return candidate == null ? null : (Scenario) candidate.node; 42 | } 43 | 44 | static boolean isBackgroundStep(AstNode astNode) { 45 | return astNode.parent.node instanceof Background; 46 | } 47 | 48 | static String calculateId(AstNode astNode) { 49 | GeneratedMessageV3 node = astNode.node; 50 | if (node instanceof Scenario) { 51 | return calculateId(astNode.parent) + ";" + convertToId(((Scenario) node).getName()); 52 | } 53 | if (node instanceof ExamplesRowWrapperNode) { 54 | return calculateId(astNode.parent) + ";" + (((ExamplesRowWrapperNode) node).bodyRowIndex + 2); 55 | } 56 | if (node instanceof TableRow) { 57 | return calculateId(astNode.parent) + ";" + 1; 58 | } 59 | if (node instanceof Examples) { 60 | return calculateId(astNode.parent) + ";" + convertToId(((Examples) node).getName()); 61 | } 62 | if (node instanceof Feature) { 63 | return convertToId(((Feature) node).getName()); 64 | } 65 | return ""; 66 | } 67 | 68 | static String convertToId(String name) { 69 | return name.replaceAll("[\\s'_,!]", "-").toLowerCase(); 70 | } 71 | 72 | static URI relativize(URI uri) { 73 | if (!"file".equals(uri.getScheme())) { 74 | return uri; 75 | } 76 | if (!uri.isAbsolute()) { 77 | return uri; 78 | } 79 | 80 | try { 81 | URI root = new File("").toURI(); 82 | URI relative = root.relativize(uri); 83 | // Scheme is lost by relativize 84 | return new URI("file", relative.getSchemeSpecificPart(), relative.getFragment()); 85 | } catch (URISyntaxException e) { 86 | throw new IllegalArgumentException(e.getMessage(), e); 87 | } 88 | } 89 | 90 | void addTestSourceReadEvent(URI path, TestSourceRead event) { 91 | pathToReadEventMap.put(path, event); 92 | } 93 | 94 | Feature getFeature(URI path) { 95 | if (!pathToAstMap.containsKey(path)) { 96 | parseGherkinSource(path); 97 | } 98 | if (pathToAstMap.containsKey(path)) { 99 | return pathToAstMap.get(path).getFeature(); 100 | } 101 | return null; 102 | } 103 | 104 | private void parseGherkinSource(URI path) { 105 | if (!pathToReadEventMap.containsKey(path)) { 106 | return; 107 | } 108 | String source = pathToReadEventMap.get(path).getSource(); 109 | 110 | List sources = singletonList(makeSourceEnvelope(source, path.toString())); 111 | 112 | List envelopes = Gherkin 113 | .fromSources(sources, true, true, true, () -> String.valueOf(UUID.randomUUID())).collect(toList()); 114 | 115 | GherkinDocument gherkinDocument = envelopes.stream().filter(Messages.Envelope::hasGherkinDocument) 116 | .map(Messages.Envelope::getGherkinDocument).findFirst().orElse(null); 117 | 118 | pathToAstMap.put(path, gherkinDocument); 119 | Map nodeMap = new HashMap<>(); 120 | AstNode currentParent = new AstNode(gherkinDocument.getFeature(), null); 121 | for (FeatureChild child : gherkinDocument.getFeature().getChildrenList()) { 122 | processFeatureDefinition(nodeMap, child, currentParent); 123 | } 124 | pathToNodeMap.put(path, nodeMap); 125 | 126 | } 127 | 128 | private void processFeatureDefinition(Map nodeMap, FeatureChild child, AstNode currentParent) { 129 | if (child.hasBackground()) { 130 | processBackgroundDefinition(nodeMap, child.getBackground(), currentParent); 131 | } else if (child.hasScenario()) { 132 | processScenarioDefinition(nodeMap, child.getScenario(), currentParent); 133 | } else if (child.hasRule()) { 134 | AstNode childNode = new AstNode(child.getRule(), currentParent); 135 | nodeMap.put(child.getRule().getLocation().getLine(), childNode); 136 | for (RuleChild ruleChild : child.getRule().getChildrenList()) { 137 | processRuleDefinition(nodeMap, ruleChild, childNode); 138 | } 139 | } 140 | } 141 | 142 | private void processBackgroundDefinition(Map nodeMap, Background background, 143 | AstNode currentParent) { 144 | AstNode childNode = new AstNode(background, currentParent); 145 | nodeMap.put(background.getLocation().getLine(), childNode); 146 | for (Step step : background.getStepsList()) { 147 | nodeMap.put(step.getLocation().getLine(), new AstNode(step, childNode)); 148 | } 149 | } 150 | 151 | private void processScenarioDefinition(Map nodeMap, Scenario child, AstNode currentParent) { 152 | AstNode childNode = new AstNode(child, currentParent); 153 | nodeMap.put(child.getLocation().getLine(), childNode); 154 | for (Step step : child.getStepsList()) { 155 | nodeMap.put(step.getLocation().getLine(), new AstNode(step, childNode)); 156 | } 157 | if (child.getExamplesCount() > 0) { 158 | processScenarioOutlineExamples(nodeMap, child, childNode); 159 | } 160 | } 161 | 162 | private void processRuleDefinition(Map nodeMap, RuleChild child, AstNode currentParent) { 163 | if (child.hasBackground()) { 164 | processBackgroundDefinition(nodeMap, child.getBackground(), currentParent); 165 | } else if (child.hasScenario()) { 166 | processScenarioDefinition(nodeMap, child.getScenario(), currentParent); 167 | } 168 | } 169 | 170 | private void processScenarioOutlineExamples(Map nodeMap, Scenario scenarioOutline, 171 | AstNode parent) { 172 | for (Examples examples : scenarioOutline.getExamplesList()) { 173 | AstNode examplesNode = new AstNode(examples, parent); 174 | TableRow headerRow = examples.getTableHeader(); 175 | AstNode headerNode = new AstNode(headerRow, examplesNode); 176 | nodeMap.put(headerRow.getLocation().getLine(), headerNode); 177 | for (int i = 0; i < examples.getTableBodyCount(); ++i) { 178 | TableRow examplesRow = examples.getTableBody(i); 179 | GeneratedMessageV3 rowNode = new ExamplesRowWrapperNode(examplesRow, i); 180 | AstNode expandedScenarioNode = new AstNode(rowNode, examplesNode); 181 | nodeMap.put(examplesRow.getLocation().getLine(), expandedScenarioNode); 182 | } 183 | } 184 | } 185 | 186 | AstNode getAstNode(URI path, int line) { 187 | if (!pathToNodeMap.containsKey(path)) { 188 | parseGherkinSource(path); 189 | } 190 | if (pathToNodeMap.containsKey(path)) { 191 | return pathToNodeMap.get(path).get(line); 192 | } 193 | return null; 194 | } 195 | 196 | boolean hasBackground(URI path, int line) { 197 | if (!pathToNodeMap.containsKey(path)) { 198 | parseGherkinSource(path); 199 | } 200 | if (pathToNodeMap.containsKey(path)) { 201 | AstNode astNode = pathToNodeMap.get(path).get(line); 202 | return getBackgroundForTestCase(astNode) != null; 203 | } 204 | return false; 205 | } 206 | 207 | static Background getBackgroundForTestCase(AstNode astNode) { 208 | Feature feature = getFeatureForTestCase(astNode); 209 | return feature.getChildrenList().stream().filter(FeatureChild::hasBackground).map(FeatureChild::getBackground) 210 | .findFirst().orElse(null); 211 | } 212 | 213 | private static Feature getFeatureForTestCase(AstNode astNode) { 214 | while (astNode.parent != null) { 215 | astNode = astNode.parent; 216 | } 217 | return (Feature) astNode.node; 218 | } 219 | 220 | static class ExamplesRowWrapperNode extends GeneratedMessageV3 { 221 | 222 | final int bodyRowIndex; 223 | 224 | ExamplesRowWrapperNode(GeneratedMessageV3 examplesRow, int bodyRowIndex) { 225 | this.bodyRowIndex = bodyRowIndex; 226 | } 227 | 228 | @Override 229 | protected FieldAccessorTable internalGetFieldAccessorTable() { 230 | throw new UnsupportedOperationException("not implemented"); 231 | } 232 | 233 | @Override 234 | protected Message.Builder newBuilderForType(BuilderParent builderParent) { 235 | throw new UnsupportedOperationException("not implemented"); 236 | } 237 | 238 | @Override 239 | public Message.Builder newBuilderForType() { 240 | throw new UnsupportedOperationException("not implemented"); 241 | } 242 | 243 | @Override 244 | public Message.Builder toBuilder() { 245 | throw new UnsupportedOperationException("not implemented"); 246 | } 247 | 248 | @Override 249 | public Message getDefaultInstanceForType() { 250 | throw new UnsupportedOperationException("not implemented"); 251 | } 252 | 253 | } 254 | 255 | static class AstNode { 256 | 257 | final GeneratedMessageV3 node; 258 | final AstNode parent; 259 | 260 | AstNode(GeneratedMessageV3 node, AstNode parent) { 261 | this.node = node; 262 | this.parent = parent; 263 | } 264 | 265 | } 266 | 267 | } -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/main/java/com/aventstack/extentreports/cucumber/adapter/URLOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.aventstack.extentreports.cucumber.adapter; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | import java.io.OutputStream; 10 | import java.net.HttpURLConnection; 11 | import java.net.URISyntaxException; 12 | import java.net.URL; 13 | import java.util.Collections; 14 | import java.util.Map; 15 | import java.util.stream.Collectors; 16 | 17 | import static java.nio.charset.StandardCharsets.UTF_8; 18 | import io.cucumber.messages.internal.com.google.gson.Gson; 19 | 20 | /** 21 | * A stream that can write to both file and http URLs. If it's a file URL, 22 | * writes with a {@link java.io.FileOutputStream}, if it's a http or https URL, 23 | * writes with a HTTP PUT (by default) or with the specified method. 24 | */ 25 | class URLOutputStream extends OutputStream { 26 | private final URL url; 27 | private final String method; 28 | private final int expectedResponseCode; 29 | private final OutputStream out; 30 | private final HttpURLConnection urlConnection; 31 | 32 | URLOutputStream(URL url) throws IOException, URISyntaxException { 33 | this(url, "PUT", Collections.emptyMap(), 200); 34 | } 35 | 36 | private URLOutputStream(URL url, String method, Map headers, int expectedResponseCode) 37 | throws IOException, URISyntaxException { 38 | this.url = url; 39 | this.method = method; 40 | this.expectedResponseCode = expectedResponseCode; 41 | if (url.getProtocol().equals("file")) { 42 | // File file = new File(url.getFile()); 43 | File file = new File(url.toURI().getPath()); 44 | ensureParentDirExists(file); 45 | out = new FileOutputStream(file); 46 | urlConnection = null; 47 | } else if (url.getProtocol().startsWith("http")) { 48 | urlConnection = (HttpURLConnection) url.openConnection(); 49 | urlConnection.setRequestMethod(method); 50 | urlConnection.setDoOutput(true); 51 | for (Map.Entry header : headers.entrySet()) { 52 | urlConnection.setRequestProperty(header.getKey(), header.getValue()); 53 | } 54 | out = urlConnection.getOutputStream(); 55 | } else { 56 | throw new IllegalArgumentException("URL Scheme must be one of file,http,https. " + url.toExternalForm()); 57 | } 58 | } 59 | 60 | private void ensureParentDirExists(File file) throws IOException { 61 | if (file.getParentFile() != null && !file.getParentFile().isDirectory()) { 62 | boolean ok = file.getParentFile().mkdirs() || file.getParentFile().isDirectory(); 63 | if (!ok) { 64 | throw new IOException("Failed to create directory " + file.getParentFile().getAbsolutePath()); 65 | } 66 | } 67 | } 68 | 69 | @Override 70 | public void write(byte[] buffer, int offset, int count) throws IOException { 71 | out.write(buffer, offset, count); 72 | } 73 | 74 | @Override 75 | public void write(byte[] buffer) throws IOException { 76 | out.write(buffer); 77 | } 78 | 79 | @Override 80 | public void write(int b) throws IOException { 81 | out.write(b); 82 | } 83 | 84 | @Override 85 | public void flush() throws IOException { 86 | out.flush(); 87 | } 88 | 89 | @Override 90 | public void close() throws IOException { 91 | try { 92 | if (urlConnection == null) { 93 | return; 94 | } 95 | 96 | int responseCode = urlConnection.getResponseCode(); 97 | if (responseCode == expectedResponseCode) { 98 | return; 99 | } 100 | 101 | try { 102 | urlConnection.getInputStream().close(); 103 | throw new IOException( 104 | String.format("Expected response code: %d. Got: %d", expectedResponseCode, responseCode)); 105 | } catch (IOException expected) { 106 | InputStream errorStream = urlConnection.getErrorStream(); 107 | if (errorStream != null) { 108 | throw createResponseException(responseCode, expected, errorStream); 109 | } else { 110 | throw expected; 111 | } 112 | } 113 | } finally { 114 | out.close(); 115 | } 116 | } 117 | 118 | private ResponseException createResponseException(int responseCode, IOException expected, InputStream errorStream) 119 | throws IOException { 120 | try (BufferedReader br = new BufferedReader(new InputStreamReader(errorStream, UTF_8))) { 121 | String responseBody = br.lines().collect(Collectors.joining(System.lineSeparator())); 122 | String contentType = urlConnection.getHeaderField("Content-Type"); 123 | if (contentType == null) { 124 | contentType = "text/plain"; 125 | } 126 | return new ResponseException(responseBody, expected, responseCode, contentType); 127 | } 128 | } 129 | 130 | class ResponseException extends IOException { 131 | private static final long serialVersionUID = -1643050449805097598L; 132 | private final Gson gson = new Gson(); 133 | private final int responseCode; 134 | private final String contentType; 135 | 136 | public ResponseException(String responseBody, IOException cause, int responseCode, String contentType) { 137 | super(responseBody, cause); 138 | this.responseCode = responseCode; 139 | this.contentType = contentType; 140 | } 141 | 142 | @Override 143 | public String getMessage() { 144 | if (contentType.equals("application/json")) { 145 | Map map = gson.fromJson(super.getMessage(), Map.class); 146 | if (map.containsKey("error")) { 147 | return getMessage0(map.get("error").toString()); 148 | } else { 149 | return getMessage0(super.getMessage()); 150 | } 151 | } else { 152 | return getMessage0(super.getMessage()); 153 | } 154 | } 155 | 156 | private String getMessage0(String message) { 157 | return String.format("%s %s\nHTTP %d\n%s", method, url, responseCode, message); 158 | } 159 | } 160 | } -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/main/java/com/aventstack/extentreports/service/ExtentService.java: -------------------------------------------------------------------------------- 1 | package com.aventstack.extentreports.service; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.Serializable; 8 | import java.nio.file.Paths; 9 | import java.time.LocalDateTime; 10 | import java.time.format.DateTimeFormatter; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.Optional; 14 | import java.util.Properties; 15 | import java.util.stream.Collectors; 16 | 17 | import com.aventstack.extentreports.ExtentReports; 18 | import com.aventstack.extentreports.Status; 19 | import com.aventstack.extentreports.observer.ExtentObserver; 20 | import com.aventstack.extentreports.reporter.ExtentHtmlReporter; 21 | import com.aventstack.extentreports.reporter.ExtentKlovReporter; 22 | import com.aventstack.extentreports.reporter.ExtentSparkReporter; 23 | import com.aventstack.extentreports.reporter.JsonFormatter; 24 | import com.aventstack.extentreports.reporter.ReporterConfigurable; 25 | import com.aventstack.extentreports.reporter.ReporterFilterable; 26 | import com.aventstack.extentreports.reporter.configuration.ViewName; 27 | 28 | import tech.grasshopper.pdf.extent.ExtentPDFCucumberReporter; 29 | import tech.grasshopper.pdf.extent.processor.MediaProcessor; 30 | import tech.grasshopper.pdf.section.details.executable.MediaCleanup.CleanupType; 31 | import tech.grasshopper.pdf.section.details.executable.MediaCleanup.MediaCleanupOption; 32 | import tech.grasshopper.reporter.ExtentExcelCucumberReporter; 33 | 34 | public class ExtentService implements Serializable { 35 | 36 | private static final long serialVersionUID = -5008231199972325650L; 37 | 38 | private static Properties properties; 39 | 40 | public static synchronized ExtentReports getInstance() { 41 | return ExtentReportsLoader.INSTANCE; 42 | } 43 | 44 | public static Object getProperty(String key) { 45 | String sys = System.getProperty(key); 46 | return sys == null ? (properties == null ? null : properties.get(key)) : sys; 47 | } 48 | 49 | public static Object getPropertyOrDefault(String key, Object defaultValue) { 50 | Object value = getProperty(key); 51 | return value == null ? defaultValue : value; 52 | } 53 | 54 | public static String getScreenshotFolderName() { 55 | return ExtentReportsLoader.SCREENSHOT_FOLDER_NAME; 56 | } 57 | 58 | public static String getScreenshotReportRelatvePath() { 59 | return ExtentReportsLoader.SCREENSHOT_FOLDER_REPORT_RELATIVE_PATH; 60 | } 61 | 62 | public static boolean isBase64ImageSrcEnabled() { 63 | return ExtentReportsLoader.ENABLE_BASE64_IMAGE_SRC; 64 | } 65 | 66 | public static boolean isDeviceEnabled() { 67 | return ExtentReportsLoader.IS_DEVICE_ENABLED; 68 | } 69 | 70 | public static boolean isAuthorEnabled() { 71 | return ExtentReportsLoader.IS_AUTHOR_ENABLED; 72 | } 73 | 74 | public static String getDevicePrefix() { 75 | return ExtentReportsLoader.DEVICE_NAME_PREFIX; 76 | } 77 | 78 | public static String getAuthorPrefix() { 79 | return ExtentReportsLoader.AUTHOR_NAME_PREFIX; 80 | } 81 | 82 | @SuppressWarnings("unused") 83 | private ExtentReports readResolve() { 84 | return ExtentReportsLoader.INSTANCE; 85 | } 86 | 87 | private static class ExtentReportsLoader { 88 | 89 | private static final ExtentReports INSTANCE = new ExtentReports(); 90 | private static final String[] DEFAULT_SETUP_PATH = new String[] { "extent.properties", 91 | "com/aventstack/adapter/extent.properties" }; 92 | 93 | private static final String SYS_INFO_MARKER = "systeminfo."; 94 | private static final String OUTPUT_PATH = "test-output/"; 95 | private static final String EXTENT_REPORTER = "extent.reporter"; 96 | private static final String START = "start"; 97 | private static final String CONFIG = "config"; 98 | private static final String OUT = "out"; 99 | private static final String VIEW_ORDER = "vieworder"; 100 | private static final String STATUS_FILTER = "statusfilter"; 101 | private static final String BASE64_IMAGE_SRC = "base64imagesrc"; 102 | private static final String ENABLE_DEVICE = "enable.device"; 103 | private static final String ENABLE_AUTHOR = "enable.author"; 104 | private static final String PREFIX_DEVICE = "prefix.device"; 105 | private static final String PREFIX_AUTHOR = "prefix.author"; 106 | private static final String DELIM = "."; 107 | 108 | private static final String KLOV = "klov"; 109 | private static final String SPARK = "spark"; 110 | private static final String JSONF = "json"; 111 | private static final String PDF = "pdf"; 112 | private static final String HTML = "html"; 113 | private static final String EXCEL = "excel"; 114 | 115 | private static final String INIT_KLOV_KEY = EXTENT_REPORTER + DELIM + KLOV + DELIM + START; 116 | private static final String INIT_SPARK_KEY = EXTENT_REPORTER + DELIM + SPARK + DELIM + START; 117 | private static final String INIT_JSONF_KEY = EXTENT_REPORTER + DELIM + JSONF + DELIM + START; 118 | private static final String INIT_PDF_KEY = EXTENT_REPORTER + DELIM + PDF + DELIM + START; 119 | private static final String INIT_HTML_KEY = EXTENT_REPORTER + DELIM + HTML + DELIM + START; 120 | private static final String INIT_EXCEL_KEY = EXTENT_REPORTER + DELIM + EXCEL + DELIM + START; 121 | 122 | private static final String CONFIG_KLOV_KEY = EXTENT_REPORTER + DELIM + KLOV + DELIM + CONFIG; 123 | private static final String CONFIG_SPARK_KEY = EXTENT_REPORTER + DELIM + SPARK + DELIM + CONFIG; 124 | private static final String CONFIG_HTML_KEY = EXTENT_REPORTER + DELIM + HTML + DELIM + CONFIG; 125 | 126 | private static final String OUT_SPARK_KEY = EXTENT_REPORTER + DELIM + SPARK + DELIM + OUT; 127 | private static final String OUT_JSONF_KEY = EXTENT_REPORTER + DELIM + JSONF + DELIM + OUT; 128 | private static final String OUT_PDF_KEY = EXTENT_REPORTER + DELIM + PDF + DELIM + OUT; 129 | private static final String OUT_HTML_KEY = EXTENT_REPORTER + DELIM + HTML + DELIM + OUT; 130 | private static final String OUT_EXCEL_KEY = EXTENT_REPORTER + DELIM + EXCEL + DELIM + OUT; 131 | 132 | private static final String VIEW_ORDER_SPARK_KEY = EXTENT_REPORTER + DELIM + SPARK + DELIM + VIEW_ORDER; 133 | private static final String BASE64_IMAGE_SRC_SPARK_KEY = EXTENT_REPORTER + DELIM + SPARK + DELIM 134 | + BASE64_IMAGE_SRC; 135 | 136 | private static final String DEVICE_ENABLE_SPARK_KEY = EXTENT_REPORTER + DELIM + SPARK + DELIM + ENABLE_DEVICE; 137 | private static final String AUTHOR_ENABLE_SPARK_KEY = EXTENT_REPORTER + DELIM + SPARK + DELIM + ENABLE_AUTHOR; 138 | private static final String DEVICE_PREFIX_SPARK_KEY = EXTENT_REPORTER + DELIM + SPARK + DELIM + PREFIX_DEVICE; 139 | private static final String AUTHOR_PREFIX_SPARK_KEY = EXTENT_REPORTER + DELIM + SPARK + DELIM + PREFIX_AUTHOR; 140 | 141 | private static boolean ENABLE_BASE64_IMAGE_SRC = false; 142 | 143 | // Use below for both Spark & Pdf reporters 144 | private static final String STATUS_FILTER_KEY = EXTENT_REPORTER + DELIM + STATUS_FILTER; 145 | 146 | private static String SCREENSHOT_FOLDER_NAME; 147 | private static String SCREENSHOT_FOLDER_REPORT_RELATIVE_PATH; 148 | private static final String DEFAULT_SCREENSHOT_FOLDER_NAME = "test-output/"; 149 | 150 | private static final String SCREENSHOT_DIR_PROPERTY = "screenshot.dir"; 151 | private static final String SCREENSHOT_REL_PATH_PROPERTY = "screenshot.rel.path"; 152 | 153 | private static final String REPORTS_BASEFOLDER = "basefolder"; 154 | private static final String REPORTS_BASEFOLDER_NAME = REPORTS_BASEFOLDER + DELIM + "name"; 155 | private static final String REPORTS_BASEFOLDER_DATETIMEPATTERN = REPORTS_BASEFOLDER + DELIM + "datetimepattern"; 156 | private static final String REPORTS_BASEFOLDER_ENABLEDELIMITER = REPORTS_BASEFOLDER + DELIM 157 | + "enable.delimiter"; 158 | private static final String REPORTS_BASEFOLDER_DELIMITER = REPORTS_BASEFOLDER + DELIM + "delimiter"; 159 | private static final LocalDateTime FOLDER_CURRENT_TIMESTAMP = LocalDateTime.now(); 160 | 161 | private static boolean IS_DEVICE_ENABLED = false; 162 | private static boolean IS_AUTHOR_ENABLED = false; 163 | private static String DEVICE_NAME_PREFIX; 164 | private static String AUTHOR_NAME_PREFIX; 165 | 166 | private static final String DEFAULT_DEVICE_PREFIX = "@dev_"; 167 | private static final String DEFAULT_AUTHOR_PREFIX = "@aut_"; 168 | 169 | static { 170 | createViaProperties(); 171 | createViaSystem(); 172 | configureScreenshotProperties(); 173 | configureDeviceAndAuthorProperties(); 174 | } 175 | 176 | private static void createViaProperties() { 177 | 178 | ClassLoader loader = ExtentReportsLoader.class.getClassLoader(); 179 | Optional is = Arrays.stream(DEFAULT_SETUP_PATH).map(x -> loader.getResourceAsStream(x)) 180 | .filter(x -> x != null).findFirst(); 181 | if (is.isPresent()) { 182 | Properties properties = new Properties(); 183 | try { 184 | properties.load(is.get()); 185 | ExtentService.properties = properties; 186 | 187 | if (properties.containsKey(INIT_KLOV_KEY) 188 | && "true".equals(String.valueOf(properties.get(INIT_KLOV_KEY)))) 189 | initKlov(properties); 190 | 191 | if (properties.containsKey(INIT_SPARK_KEY) 192 | && "true".equals(String.valueOf(properties.get(INIT_SPARK_KEY)))) 193 | initSpark(properties); 194 | 195 | if (properties.containsKey(INIT_JSONF_KEY) 196 | && "true".equals(String.valueOf(properties.get(INIT_JSONF_KEY)))) 197 | initJsonf(properties); 198 | 199 | if (properties.containsKey(INIT_PDF_KEY) 200 | && "true".equals(String.valueOf(properties.get(INIT_PDF_KEY)))) 201 | initPdf(properties); 202 | 203 | if (properties.containsKey(INIT_HTML_KEY) 204 | && "true".equals(String.valueOf(properties.get(INIT_HTML_KEY)))) 205 | initHtml(properties); 206 | 207 | if (properties.containsKey(INIT_EXCEL_KEY) 208 | && "true".equals(String.valueOf(properties.get(INIT_EXCEL_KEY)))) 209 | initExcel(properties); 210 | 211 | addSystemInfo(properties); 212 | } catch (Exception e) { 213 | e.printStackTrace(); 214 | } 215 | } 216 | } 217 | 218 | private static void createViaSystem() { 219 | 220 | if ("true".equals(System.getProperty(INIT_KLOV_KEY))) 221 | initKlov(null); 222 | 223 | if ("true".equals(System.getProperty(INIT_SPARK_KEY))) 224 | initSpark(null); 225 | 226 | if ("true".equals(System.getProperty(INIT_JSONF_KEY))) 227 | initJsonf(null); 228 | 229 | if ("true".equals(System.getProperty(INIT_PDF_KEY))) 230 | initPdf(null); 231 | 232 | if ("true".equals(System.getProperty(INIT_HTML_KEY))) 233 | initHtml(null); 234 | 235 | if ("true".equals(System.getProperty(INIT_EXCEL_KEY))) 236 | initExcel(null); 237 | 238 | addSystemInfo(System.getProperties()); 239 | } 240 | 241 | private static String getBaseFolderName() { 242 | String folderpattern = ""; 243 | Object baseFolderPrefix = getProperty(REPORTS_BASEFOLDER_NAME); 244 | Object baseFolderPatternSuffix = getProperty(REPORTS_BASEFOLDER_DATETIMEPATTERN); 245 | String enableDelimiter = String.valueOf(getPropertyOrDefault(REPORTS_BASEFOLDER_ENABLEDELIMITER, "true")); 246 | String delimiter = String.valueOf(getPropertyOrDefault(REPORTS_BASEFOLDER_DELIMITER, " ")); 247 | 248 | if (enableDelimiter.equalsIgnoreCase("false")) 249 | delimiter = ""; 250 | 251 | if (baseFolderPrefix != null && !String.valueOf(baseFolderPrefix).isEmpty() 252 | && baseFolderPatternSuffix != null && !String.valueOf(baseFolderPatternSuffix).isEmpty()) { 253 | DateTimeFormatter folderSuffix = DateTimeFormatter.ofPattern(String.valueOf(baseFolderPatternSuffix)); 254 | folderpattern = baseFolderPrefix + delimiter + folderSuffix.format(FOLDER_CURRENT_TIMESTAMP) + "/"; 255 | } 256 | return folderpattern; 257 | } 258 | 259 | private static String getOutputPath(Properties properties, String key) { 260 | String out; 261 | if (properties != null && properties.get(key) != null) 262 | out = String.valueOf(properties.get(key)); 263 | else 264 | out = System.getProperty(key); 265 | out = out == null || out.equals("null") || out.isEmpty() ? OUTPUT_PATH + key.split("\\.")[2] + "/" : out; 266 | return getBaseFolderName() + out; 267 | } 268 | 269 | private static void configureScreenshotProperties() { 270 | Object property = getProperty(SCREENSHOT_DIR_PROPERTY); 271 | SCREENSHOT_FOLDER_NAME = property == null || String.valueOf(property).isEmpty() 272 | ? DEFAULT_SCREENSHOT_FOLDER_NAME 273 | : String.valueOf(property); 274 | SCREENSHOT_FOLDER_NAME = getBaseFolderName() + SCREENSHOT_FOLDER_NAME; 275 | 276 | property = getProperty(SCREENSHOT_REL_PATH_PROPERTY); 277 | SCREENSHOT_FOLDER_REPORT_RELATIVE_PATH = property == null || String.valueOf(property).isEmpty() 278 | ? SCREENSHOT_FOLDER_NAME 279 | : String.valueOf(property); 280 | } 281 | 282 | private static void configureDeviceAndAuthorProperties() { 283 | if ("true".equals(String.valueOf(getPropertyOrDefault(DEVICE_ENABLE_SPARK_KEY, "false")))) 284 | IS_DEVICE_ENABLED = true; 285 | if ("true".equals(String.valueOf(getPropertyOrDefault(AUTHOR_ENABLE_SPARK_KEY, "false")))) 286 | IS_AUTHOR_ENABLED = true; 287 | 288 | if (IS_DEVICE_ENABLED) { 289 | String property = String.valueOf(getPropertyOrDefault(DEVICE_PREFIX_SPARK_KEY, DEFAULT_DEVICE_PREFIX)); 290 | if (!property.isEmpty()) 291 | DEVICE_NAME_PREFIX = property; 292 | } 293 | 294 | if (IS_AUTHOR_ENABLED) { 295 | String property = String.valueOf(getPropertyOrDefault(AUTHOR_PREFIX_SPARK_KEY, DEFAULT_AUTHOR_PREFIX)); 296 | if (!property.isEmpty()) 297 | AUTHOR_NAME_PREFIX = property; 298 | } 299 | } 300 | 301 | private static void initKlov(Properties properties) { 302 | ExtentKlovReporter klov = new ExtentKlovReporter("Default"); 303 | String configPath = properties == null ? System.getProperty(CONFIG_KLOV_KEY) 304 | : String.valueOf(properties.get(CONFIG_KLOV_KEY)); 305 | File f = new File(configPath); 306 | if (configPath != null && !configPath.isEmpty() && f.exists()) { 307 | // Object prop = ExtentService.getProperty("screenshot.dir"); 308 | // String screenshotDir = prop == null ? "test-output/" : String.valueOf(prop); 309 | configureScreenshotProperties(); 310 | String url = Paths.get(SCREENSHOT_FOLDER_NAME).toString(); 311 | ExtentService.getInstance().tryResolveMediaPath(new String[] { url }); 312 | try { 313 | InputStream is = new FileInputStream(f); 314 | klov.loadInitializationParams(is); 315 | INSTANCE.attachReporter(klov); 316 | } catch (IOException e) { 317 | e.printStackTrace(); 318 | } 319 | } 320 | } 321 | 322 | private static void initSpark(Properties properties) { 323 | String out = getOutputPath(properties, OUT_SPARK_KEY); 324 | ExtentSparkReporter spark = new ExtentSparkReporter(out); 325 | sparkReportViewOrder(spark); 326 | filterReportStatus(spark); 327 | base64PngImageStyle(); 328 | attach(spark, properties, CONFIG_SPARK_KEY); 329 | } 330 | 331 | private static void initHtml(Properties properties) { 332 | String out = getOutputPath(properties, OUT_HTML_KEY); 333 | ExtentHtmlReporter html = new ExtentHtmlReporter(out); 334 | filterReportStatus(html); 335 | base64PngImageStyle(); 336 | attach(html, properties, CONFIG_HTML_KEY); 337 | } 338 | 339 | private static void initExcel(Properties properties) { 340 | String out = getOutputPath(properties, OUT_EXCEL_KEY); 341 | ExtentExcelCucumberReporter excel = new ExtentExcelCucumberReporter(out); 342 | INSTANCE.attachReporter(excel); 343 | } 344 | 345 | private static void filterReportStatus(ReporterFilterable reporter) { 346 | try { 347 | if (getProperty(STATUS_FILTER_KEY) == null) 348 | return; 349 | 350 | List statuses = Arrays.stream(String.valueOf(getProperty(STATUS_FILTER_KEY)).split(",")) 351 | .map(s -> convertToStatus(s)).collect(Collectors.toList()); 352 | reporter.filter().statusFilter().as(statuses); 353 | } catch (Exception e) { 354 | // Do nothing. Uses no filter. 355 | } 356 | } 357 | 358 | private static void sparkReportViewOrder(ExtentSparkReporter spark) { 359 | try { 360 | List viewOrder = Arrays.stream(String.valueOf(getProperty(VIEW_ORDER_SPARK_KEY)).split(",")) 361 | .map(v -> ViewName.valueOf(v.toUpperCase())).collect(Collectors.toList()); 362 | spark.viewConfigurer().viewOrder().as(viewOrder).apply(); 363 | } catch (Exception e) { 364 | // Do nothing. Use default order. 365 | } 366 | } 367 | 368 | private static void base64PngImageStyle() { 369 | if ("true".equals(String.valueOf(properties.getOrDefault(BASE64_IMAGE_SRC_SPARK_KEY, "false")))) { 370 | ENABLE_BASE64_IMAGE_SRC = true; 371 | } 372 | } 373 | 374 | private static void initJsonf(Properties properties) { 375 | String out = getOutputPath(properties, OUT_JSONF_KEY); 376 | JsonFormatter jsonf = new JsonFormatter(out); 377 | INSTANCE.attachReporter(jsonf); 378 | } 379 | 380 | private static void initPdf(Properties properties) { 381 | String out = getOutputPath(properties, OUT_PDF_KEY); 382 | configureScreenshotProperties(); 383 | MediaCleanupOption mediaCleanup = MediaCleanupOption.builder().cleanUpType(CleanupType.PATTERN) 384 | .pattern(MediaProcessor.EMBEDDED_PREFIX + ".*").build(); 385 | ExtentPDFCucumberReporter pdf = new ExtentPDFCucumberReporter(out, SCREENSHOT_FOLDER_NAME, mediaCleanup); 386 | filterReportStatus(pdf); 387 | INSTANCE.attachReporter(pdf); 388 | } 389 | 390 | private static void attach(ReporterConfigurable r, Properties properties, String configKey) { 391 | Object configPath = properties == null ? System.getProperty(configKey) : properties.get(configKey); 392 | if (configPath != null && !String.valueOf(configPath).isEmpty()) 393 | try { 394 | r.loadXMLConfig(String.valueOf(configPath)); 395 | } catch (IOException e) { 396 | e.printStackTrace(); 397 | } 398 | INSTANCE.attachReporter((ExtentObserver) r); 399 | } 400 | 401 | private static void addSystemInfo(Properties properties) { 402 | properties.forEach((k, v) -> { 403 | String key = String.valueOf(k); 404 | if (key.startsWith(SYS_INFO_MARKER)) { 405 | key = key.substring(key.indexOf('.') + 1); 406 | INSTANCE.setSystemInfo(key, String.valueOf(v)); 407 | } 408 | }); 409 | } 410 | 411 | private static Status convertToStatus(String status) { 412 | String lowerStatus = status.toLowerCase(); 413 | 414 | switch (lowerStatus) { 415 | case "info": 416 | return Status.INFO; 417 | case "pass": 418 | return Status.PASS; 419 | case "Warning": 420 | return Status.WARNING; 421 | case "skip": 422 | return Status.SKIP; 423 | case "fail": 424 | return Status.FAIL; 425 | default: 426 | throw new IllegalArgumentException(); 427 | } 428 | } 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/java/cucumber/examples/java/calculator/Configurer.java: -------------------------------------------------------------------------------- 1 | package cucumber.examples.java.calculator; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | import com.fasterxml.jackson.databind.JavaType; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 8 | 9 | import io.cucumber.java.DefaultDataTableCellTransformer; 10 | import io.cucumber.java.DefaultDataTableEntryTransformer; 11 | import io.cucumber.java.DefaultParameterTransformer; 12 | 13 | public class Configurer { 14 | 15 | private final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()); 16 | 17 | @DefaultParameterTransformer 18 | @DefaultDataTableEntryTransformer 19 | @DefaultDataTableCellTransformer 20 | public Object defaultTransformer(Object fromValue, Type toValueType) { 21 | JavaType javaType = objectMapper.constructType(toValueType); 22 | return objectMapper.convertValue(fromValue, javaType); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/java/cucumber/examples/java/calculator/DateCalculator.java: -------------------------------------------------------------------------------- 1 | package cucumber.examples.java.calculator; 2 | 3 | import java.util.Date; 4 | 5 | public class DateCalculator { 6 | private Date now; 7 | 8 | public DateCalculator(Date now) { 9 | this.now = now; 10 | } 11 | 12 | public String isDateInThePast(Date date) { 13 | return (date.before(now)) ? "yes" : "no"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/java/cucumber/examples/java/calculator/DateStepdefs.java: -------------------------------------------------------------------------------- 1 | package cucumber.examples.java.calculator; 2 | 3 | import static java.text.DateFormat.MEDIUM; 4 | import static java.text.DateFormat.getDateInstance; 5 | import static java.util.Locale.ENGLISH; 6 | import static org.testng.Assert.assertEquals; 7 | 8 | import java.text.ParseException; 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | 12 | import io.cucumber.java.ParameterType; 13 | import io.cucumber.java.en.Given; 14 | import io.cucumber.java.en.Then; 15 | import io.cucumber.java.en.When; 16 | 17 | 18 | public class DateStepdefs { 19 | private String result; 20 | private DateCalculator calculator; 21 | 22 | @ParameterType(".*?") 23 | public Date isodate(String date) throws ParseException { 24 | return new SimpleDateFormat("yyyy-mm-dd").parse(date); 25 | } 26 | 27 | @Given("today is {isodate}") 28 | public void today_is(Date date) { 29 | calculator = new DateCalculator(date); 30 | } 31 | 32 | @ParameterType(".*?") 33 | public Date date(String date) throws ParseException { 34 | return getDateInstance(MEDIUM, ENGLISH).parse(date); 35 | } 36 | 37 | @When("I ask if {date} is in the past") 38 | public void I_ask_if_date_is_in_the_past(Date date) { 39 | result = calculator.isDateInThePast(date); 40 | } 41 | 42 | @Then("the result should be {string}") 43 | public void the_result_should_be(String expectedResult) { 44 | assertEquals(expectedResult, result); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/java/cucumber/examples/java/calculator/Example.java: -------------------------------------------------------------------------------- 1 | package cucumber.examples.java.calculator; 2 | 3 | public interface Example { 4 | } 5 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/java/cucumber/examples/java/calculator/RpnCalculator.java: -------------------------------------------------------------------------------- 1 | package cucumber.examples.java.calculator; 2 | 3 | import java.util.Deque; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | import static java.util.Arrays.asList; 8 | 9 | public class RpnCalculator { 10 | private final Deque stack = new LinkedList(); 11 | private static final List OPS = asList("-", "+", "*", "/"); 12 | 13 | public void push(Object arg) { 14 | if (OPS.contains(arg)) { 15 | Number y = stack.removeLast(); 16 | Number x = stack.isEmpty() ? 0 : stack.removeLast(); 17 | Double val = null; 18 | if (arg.equals("-")) { 19 | val = x.doubleValue() - y.doubleValue(); 20 | } else if (arg.equals("+")) { 21 | val = x.doubleValue() + y.doubleValue(); 22 | } else if (arg.equals("*")) { 23 | val = x.doubleValue() * y.doubleValue(); 24 | } else if (arg.equals("/")) { 25 | val = x.doubleValue() / y.doubleValue(); 26 | } 27 | push(val); 28 | } else { 29 | stack.add((Number) arg); 30 | } 31 | } 32 | 33 | public void PI() { 34 | push(Math.PI); 35 | } 36 | 37 | public Number value() { 38 | return stack.getLast(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/java/cucumber/examples/java/calculator/RpnCalculatorStepdefs.java: -------------------------------------------------------------------------------- 1 | package cucumber.examples.java.calculator; 2 | 3 | import static org.testng.Assert.assertEquals; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import io.cucumber.java.After; 9 | import io.cucumber.java.Before; 10 | import io.cucumber.java.DataTableType; 11 | import io.cucumber.java.Scenario; 12 | import io.cucumber.java.en.Given; 13 | import io.cucumber.java.en.Then; 14 | import io.cucumber.java.en.When; 15 | 16 | public class RpnCalculatorStepdefs { 17 | private RpnCalculator calc; 18 | 19 | @Given("a calculator I just turned on") 20 | public void a_calculator_I_just_turned_on() { 21 | calc = new RpnCalculator(); 22 | } 23 | 24 | @When("I add {int} and {int}") 25 | public void adding(int arg1, int arg2) { 26 | calc.push(arg1); 27 | calc.push(arg2); 28 | calc.push("+"); 29 | } 30 | 31 | @Given("I press {string}") 32 | public void I_press(String what) { 33 | calc.push(what); 34 | } 35 | 36 | @Then("the result is {int}") 37 | public void the_result_is(double expected) { 38 | assertEquals(expected, calc.value()); 39 | } 40 | 41 | @Before("not @foo") 42 | public void before(Scenario scenario) { 43 | scenario.log("Runs BEFORE scenarios *not* tagged with @foo"); 44 | } 45 | 46 | @After("not @foo") 47 | public void after(Scenario scenario) { 48 | scenario.log("Runs AFTER scenarios *not* tagged with @foo"); 49 | } 50 | 51 | @DataTableType 52 | public Entry getEntries(Map entry) { 53 | return new Entry(Integer.valueOf(entry.get("first")), 54 | Integer.valueOf(entry.get("second")), 55 | entry.get("operation")); 56 | } 57 | 58 | @Given("the previous entries:") 59 | public void thePreviousEntries(List entries) { 60 | for (Entry entry : entries) { 61 | calc.push(entry.first); 62 | calc.push(entry.second); 63 | calc.push(entry.operation); 64 | } 65 | } 66 | 67 | static final class Entry { 68 | private final Integer first; 69 | private final Integer second; 70 | private final String operation; 71 | 72 | Entry(Integer first, Integer second, String operation) { 73 | this.first = first; 74 | this.second = second; 75 | this.operation = operation; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/java/cucumber/examples/java/calculator/RunCukesTest.java: -------------------------------------------------------------------------------- 1 | package cucumber.examples.java.calculator; 2 | 3 | import io.cucumber.testng.AbstractTestNGCucumberTests; 4 | import io.cucumber.testng.CucumberOptions; 5 | 6 | @CucumberOptions(plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"}) 7 | public class RunCukesTest extends AbstractTestNGCucumberTests { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/java/cucumber/examples/java/calculator/ScreenShotStepDefinition.java: -------------------------------------------------------------------------------- 1 | package cucumber.examples.java.calculator; 2 | 3 | import org.openqa.selenium.OutputType; 4 | import org.openqa.selenium.TakesScreenshot; 5 | import org.openqa.selenium.WebDriver; 6 | import org.openqa.selenium.chrome.ChromeDriver; 7 | 8 | import io.cucumber.java.AfterStep; 9 | import io.cucumber.java.BeforeStep; 10 | import io.cucumber.java.Scenario; 11 | import io.cucumber.java.en.And; 12 | import io.github.bonigarcia.wdm.WebDriverManager; 13 | 14 | public class ScreenShotStepDefinition { 15 | 16 | private WebDriver driver; 17 | 18 | @And("Go to {word}") 19 | public void visitweb(String site) throws Exception { 20 | System.out.println(site); 21 | driver.get(site); 22 | Thread.sleep(5000); 23 | } 24 | 25 | @BeforeStep(value = "@website") 26 | public void beforeSite() { 27 | System.out.println("BEFORE SITE"); 28 | WebDriverManager.chromedriver().setup(); 29 | driver = new ChromeDriver(); 30 | driver.manage().window().maximize(); 31 | } 32 | 33 | @AfterStep(value = "@website") 34 | public void afterSite(Scenario scenario) { 35 | System.out.println("AFTER SITE"); 36 | 37 | TakesScreenshot ts = (TakesScreenshot) driver; 38 | byte[] screenshot = ts.getScreenshotAs(OutputType.BYTES); 39 | scenario.log("this is my failure message………."); 40 | scenario.attach(screenshot, "image/png",""); 41 | driver.quit(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/java/cucumber/examples/java/calculator/ShoppingStepdefs.java: -------------------------------------------------------------------------------- 1 | package cucumber.examples.java.calculator; 2 | 3 | import static org.testng.Assert.assertEquals; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import io.cucumber.java.DataTableType; 9 | import io.cucumber.java.DocStringType; 10 | import io.cucumber.java.en.Given; 11 | import io.cucumber.java.en.Then; 12 | import io.cucumber.java.en.When; 13 | 14 | public class ShoppingStepdefs { 15 | private RpnCalculator calc = new RpnCalculator(); 16 | 17 | @DataTableType 18 | public Grocery getGroceries(Map entry) { 19 | return new Grocery(entry.get("name"), new Price(Integer.parseInt(entry.get("price")))); 20 | } 21 | 22 | @Given("the following groceries:") 23 | public void the_following_groceries(List groceries) { 24 | for (Grocery grocery : groceries) { 25 | calc.push(grocery.price.value); 26 | calc.push("+"); 27 | } 28 | } 29 | 30 | @When("I pay {int}") 31 | public void i_pay(int amount) { 32 | calc.push(amount); 33 | calc.push("-"); 34 | } 35 | 36 | @Then("my change should be {int}") 37 | public void my_change_should_be_(int change) { 38 | assertEquals(-calc.value().intValue(), change); 39 | } 40 | 41 | @DocStringType 42 | public Speech getSpeech(String text) { 43 | return new Speech(text); 44 | } 45 | 46 | @Given("the doc string is") 47 | public void the_doc_string_is(Speech speech) { 48 | System.out.println(speech); 49 | } 50 | 51 | public static class Speech { 52 | private String text; 53 | private int lines; 54 | private int words; 55 | 56 | public Speech(String text) { 57 | this.text = text; 58 | this.lines = text.split("[\\n\\r]+").length; 59 | this.words = text.split("[\\s]+").length; 60 | } 61 | } 62 | 63 | public static class Grocery { 64 | @SuppressWarnings("unused") 65 | private String name; 66 | private Price price; 67 | 68 | Grocery(String name, Price price) { 69 | this.name = name; 70 | this.price = price; 71 | } 72 | } 73 | 74 | public static final class Price { 75 | private int value; 76 | 77 | Price(int value) { 78 | this.value = value; 79 | } 80 | 81 | static Price fromString(String value) { 82 | return new Price(Integer.parseInt(value)); 83 | } 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/resources/com/aventstack/adapter/extent.properties: -------------------------------------------------------------------------------- 1 | extent.reporter.klov.start=false 2 | extent.reporter.spark.start=true 3 | extent.reporter.json.start=true 4 | extent.reporter.pdf.start=true 5 | extent.reporter.html.start=true 6 | 7 | extent.reporter.klov.config= 8 | extent.reporter.spark.config= 9 | 10 | extent.reporter.spark.out=test-output/SparkReport/ExtentSpark.html 11 | extent.reporter.json.out=test-output/JsonReport/ExtentJson.json 12 | extent.reporter.pdf.out=test output/PdfReport/ExtentPdf.pdf 13 | extent.reporter.html.out=test-output/HtmlReport/ExtentHtml.html 14 | 15 | screenshot.dir=test-output/ 16 | screenshot.rel.path=../ 17 | 18 | systeminfo.os=Windows 19 | systeminfo.version=10 -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/resources/com/aventstack/adapter/html-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | dark 7 | 8 | 9 | 10 | UTF-8 11 | 12 | 13 | 14 | https 15 | 16 | 17 | HTML Extent 18 | 19 | 20 | GhChirp Report 21 | 22 | 23 | false 24 | 25 | 26 | top 27 | true 28 | true 29 | true 30 | 31 | 32 | 33 | true 34 | true 35 | true 36 | true 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/resources/com/aventstack/adapter/klov.properties: -------------------------------------------------------------------------------- 1 | mongodb.host=localhost 2 | mongodb.port=27017 3 | mongodb.uri= 4 | klov.host=localhost 5 | klov.port=80 6 | klov.project.name=cucumber4 7 | klov.report.name=examples 8 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/resources/com/aventstack/adapter/spark-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | dark 7 | 8 | 9 | 10 | UTF-8 11 | 12 | 13 | 14 | https 15 | 16 | 17 | Spark Extent 18 | 19 | 20 | Grasshopper Report 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/resources/cucumber/examples/java/calculator/basic_arithmetic.feature: -------------------------------------------------------------------------------- 1 | @foo 2 | Feature: Basic Arithmetic 3 | 4 | Background: A Calculator 5 | Given a calculator I just turned on 6 | 7 | Scenario: Addition Scenario 8 | # Try to change one of the values below to provoke a failure 9 | When I add 4 and 5 10 | Then the result is 9 11 | 12 | Scenario: Another Addition 13 | # Try to change one of the values below to provoke a failure 14 | When I add 4 and 7 15 | Then the result is 11 16 | 17 | Scenario Outline: Many additions 18 | Given the previous entries: 19 | | first | second | operation | 20 | | 1 | 1 | + | 21 | | 2 | 1 | + | 22 | When I press "+" 23 | And I add and 24 | And I press "+" 25 | Then the result is 26 | 27 | Examples: Single digits 28 | | a | b | c | 29 | | 1 | 2 | 9 | 30 | | 2 | 3 | 10 | 31 | 32 | Examples: Double digits 33 | | a | b | c | 34 | | 10 | 20 | 35 | 35 | | 20 | 30 | 55 | 36 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/resources/cucumber/examples/java/calculator/date_calculator.feature: -------------------------------------------------------------------------------- 1 | @calc 2 | Feature: Dates with different date formats 3 | This feature shows you can have different date formats, as long as you annotate the 4 | corresponding step definition method accordingly. 5 | 6 | Scenario: Determine past date 7 | Given today is 2011-01-20 8 | When I ask if Jan 19, 2011 is in the past 9 | Then the result should be "yes" -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/resources/cucumber/examples/java/calculator/screen.feature: -------------------------------------------------------------------------------- 1 | Feature: Scenarios feature file for screenshot 2 | 3 | @website 4 | Scenario: Scenario screenshot 5 | Given Go to https://stackoverflow.com/ 6 | Given Go to https://github.com/ 7 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/resources/cucumber/examples/java/calculator/shopping.feature: -------------------------------------------------------------------------------- 1 | @shop 2 | Feature: Shopping 3 | 4 | Scenario: Give correct change 5 | Given the following groceries: 6 | | name | price | 7 | | milk | 9 | 8 | | bread | 7 | 9 | | soap | 5 | 10 | When I pay 25 11 | Then my change should be 4 12 | 13 | Scenario: Doc String to Custom Object 14 | Given the doc string is 15 | """ 16 | Hello there how r u? 17 | 18 | Doing great. 19 | Whats new? 20 | """ 21 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/resources/cucumber/examples/java/calculator/skipdef.feature: -------------------------------------------------------------------------------- 1 | Feature: Scenarios With No Step Definitions 2 | 3 | @skipscenario 4 | Scenario: No Step defined 5 | Given No step definition methods 6 | -------------------------------------------------------------------------------- /extentreports-cucumber6-adapter/src/test/resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grasshopper7/extentreports-cucumber6-adapter/83dad648d859c244e33fce99238c378df05987fa/extentreports-cucumber6-adapter/src/test/resources/logo.png -------------------------------------------------------------------------------- /summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grasshopper7/extentreports-cucumber6-adapter/83dad648d859c244e33fce99238c378df05987fa/summary.png --------------------------------------------------------------------------------