diff --git a/CHANGELOG.md b/CHANGELOG.md index d45bb948..3c967e79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,19 +7,35 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. Back to [Readme](README.md). +## [0.8.0] - 2018-06-10 + +### Fixed + +* Scenario.write outputs with null values lead to rendering exceptions +* Scenario.write outputs are not shown in before and after steps + +### Added + +* Support for Before and After hock attachments +* Updated example JSON files in example project + +### Removed + +* Capitalization of scenario names + ## [0.7.1] - 2018-05-08 -# Added +### Added * Feature description is now shown in the feature tool tip on hover -# Fixed +### Fixed * Chart was not rendered when a scenario contained step data tables ## [0.7.0] - 2018-04-18 -# Changed +### Changed * Unified report design * Updated all dependencies @@ -27,69 +43,69 @@ Back to [Readme](README.md). ## [0.6.0] - 2018-04-12 -# Added +### Added * Example project -# Changed +### Changed * Cluecumber is now a monorepo -# Fixed +### Fixed * Table header error on tab overview page ## [0.5.0] - 2018-03-19 -# Added +### Added * Tag summary page -# Fixed +### Fixed * Background Scenario steps are now rendered correctly * Various small bug fixes ## [0.3.0] - 2018-02-19 -# Added +### Added * Scenario.output is now displayed in the scenario details -# Fixed +### Fixed * Scenarios with pending and skipped steps are also considered skipped. * Background scenarios are now merged to the following scenarios. -# Changed +### Changed * Before and after steps have a lower opacity to focus on test steps. * Internal organization of page types allows easier extension. ## [0.2.0] - 2018-01-16 -# Added +### Added - Support for data tables within steps - Cleaner report headers -# Removed +### Removed - Javascript back method is replaced with simple links on the detail pages -# Fixed +### Fixed - Report generation is now much more resilient if information is missing in the JSON sources ## [0.1.1] - 2017-12-12 -# Removed +### Removed - Unnecessary log outputs for attachments ## [0.1.0] - 2017-12-12 -# Added +### Added - Support for Cucumber 2 attachments @@ -142,6 +158,8 @@ Back to [Readme](README.md). Initial project version on GitHub and Maven Central. +[0.8.0]: https://github.com/trivago/cluecumber-report-plugin/tree/0.8.0 +[0.7.1]: https://github.com/trivago/cluecumber-report-plugin/tree/0.7.1 [0.6.0]: https://github.com/trivago/cluecumber-report-plugin/tree/0.6.0 [0.5.0]: https://github.com/trivago/cluecumber-report-plugin/tree/0.5.0 [0.3.0]: https://github.com/trivago/cluecumber-report-plugin/tree/0.3.0 diff --git a/documentation/img/browserstack.png b/documentation/img/browserstack.png index d98853b5..6107acbd 100644 Binary files a/documentation/img/browserstack.png and b/documentation/img/browserstack.png differ diff --git a/example-project/json/attachments.json b/example-project/json/attachments.json new file mode 100755 index 00000000..51c4518d --- /dev/null +++ b/example-project/json/attachments.json @@ -0,0 +1,72 @@ +[ + { + "description": "This is an attachment test", + "elements": [ + { + "description": "Attachment in every step", + "id": ";", + "keyword": "Scenario", + "line": 2, + "name": "This scenario passes", + "steps": [ + { + "keyword": "Given ", + "line": 3, + "match": { + "location": "someMethod()" + }, + "name": "this step has an attachment", + "result": { + "duration": 244280571, + "status": "passed" + }, + "embeddings": [ + { + "mime_type": "image/png", + "data": "Zm9v" + } + ] + }, + { + "keyword": "And ", + "line": 3, + "match": { + "location": "someMethod2()" + }, + "name": "this step has also an attachment", + "output": [ + "Output 1", + null, + "Output 3" + ], + "result": { + "duration": 3923468231, + "status": "passed" + }, + "embeddings": [ + { + "mime_type": "image/png", + "data": "Zm9v" + }, + { + "mime_type": "image/png", + "data": "Zm9v" + } + ] + } + ], + "tags": [ + { + "name": "@test" + } + ], + "type": "scenario" + } + ], + "id": "", + "keyword": "Feature", + "line": 1, + "name": "Feature 1", + "uri": "features/attachments.feature" + } +] diff --git a/example-project/json/skip.json b/example-project/json/skip.json index 50cd8304..7aed6cdd 100755 --- a/example-project/json/skip.json +++ b/example-project/json/skip.json @@ -8,6 +8,10 @@ "match": { "location": "Steps.java:6" }, + "output": [ + "Output 1", + "Output 2" + ], "result": { "duration": 1452264732, "error_message": "java.lang.RuntimeException: failing before hook\n\tat Steps.lambda$new$0(Steps.java:7)\n\tat cucumber.runtime.java8.Java8HookDefinition.lambda$execute$0(Java8HookDefinition.java:51)", @@ -31,7 +35,27 @@ "result": { "duration": 2552264732, "status": "skipped" - } + }, + "output": [ + "Output 5", + "Output 6" + ] + } + ], + "after": [ + { + "match": { + "location": "After.java:6" + }, + "result": { + "duration": 1452264732, + "error_message": "Some errors", + "status": "failed" + }, + "output": [ + "Output 3", + "Output 4" + ] } ], "tags": [ diff --git a/example-project/pom.xml b/example-project/pom.xml index 778f37eb..85aacb03 100644 --- a/example-project/pom.xml +++ b/example-project/pom.xml @@ -6,7 +6,7 @@ de.benjamin-bischoff cluecumber-test-project - 0.7.1 + 0.8.0 UTF-8 diff --git a/plugin-code/pom.xml b/plugin-code/pom.xml index d9e564fc..c4290fbc 100644 --- a/plugin-code/pom.xml +++ b/plugin-code/pom.xml @@ -6,7 +6,7 @@ com.trivago.rta cluecumber-report-plugin - 0.7.1 + 0.8.0 https://github.com/trivago/cluecumber-report-plugin Cluecumber Maven Plugin for Cucumber Reports @@ -78,14 +78,16 @@ 1.7.1 3.5 2.4 - + 3.1.0 2.9.0 4.12 + 0.8.6 2.8.2 1.8.3 2.3.1 2.3.28 + 1.11.3 @@ -247,7 +249,13 @@ org.codehaus.plexus plexus-utils - 3.1.0 + ${plexus.utilities.version} + + + + org.jsoup + jsoup + ${jsoup.version} @@ -267,7 +275,7 @@ com.openpojo openpojo - 0.8.6 + ${openpojo.version} test diff --git a/plugin-code/src/main/java/com/trivago/rta/json/pojo/After.java b/plugin-code/src/main/java/com/trivago/rta/json/pojo/After.java index 913a7a81..ce664d94 100644 --- a/plugin-code/src/main/java/com/trivago/rta/json/pojo/After.java +++ b/plugin-code/src/main/java/com/trivago/rta/json/pojo/After.java @@ -16,18 +16,5 @@ package com.trivago.rta.json.pojo; -import java.util.ArrayList; -import java.util.List; - public class After extends ResultMatch { - // Cucumber 2 - private List embeddings = new ArrayList<>(); - - public List getEmbeddings() { - return embeddings; - } - - public void setEmbeddings(final List embeddings) { - this.embeddings = embeddings; - } } diff --git a/plugin-code/src/main/java/com/trivago/rta/json/pojo/ResultMatch.java b/plugin-code/src/main/java/com/trivago/rta/json/pojo/ResultMatch.java index 33bc1bab..c4bbf181 100644 --- a/plugin-code/src/main/java/com/trivago/rta/json/pojo/ResultMatch.java +++ b/plugin-code/src/main/java/com/trivago/rta/json/pojo/ResultMatch.java @@ -18,10 +18,16 @@ import com.trivago.rta.constants.Status; +import java.util.ArrayList; +import java.util.List; + class ResultMatch { private Result result; private Match match; + private List output = new ArrayList<>(); + private List embeddings = new ArrayList<>(); + public Result getResult() { return result != null ? result : new Result(); } @@ -38,6 +44,22 @@ public void setMatch(final Match match) { this.match = match; } + public List getEmbeddings() { + return embeddings; + } + + public void setEmbeddings(final List embeddings) { + this.embeddings = embeddings; + } + + public List getOutput() { + return output; + } + + public void setOutput(final List output) { + this.output = output; + } + public String getGlueMethodName() { return getMatch().getLocation(); } @@ -66,6 +88,8 @@ public String toString() { return "ResultMatch{" + "result=" + result + ", match=" + match + + ", output=" + output + + ", embeddings=" + embeddings + '}'; } } diff --git a/plugin-code/src/main/java/com/trivago/rta/json/pojo/Step.java b/plugin-code/src/main/java/com/trivago/rta/json/pojo/Step.java index 86488552..a41c4074 100644 --- a/plugin-code/src/main/java/com/trivago/rta/json/pojo/Step.java +++ b/plugin-code/src/main/java/com/trivago/rta/json/pojo/Step.java @@ -23,9 +23,7 @@ public class Step extends ResultMatch { private int line; private String name = ""; private String keyword = ""; - private List output = new ArrayList<>(); private List rows = new ArrayList<>(); - private List embeddings = new ArrayList<>(); public int getLine() { return line; @@ -51,13 +49,6 @@ public void setKeyword(final String keyword) { this.keyword = keyword; } - public List getEmbeddings() { - return embeddings; - } - - public void setEmbeddings(final List embeddings) { - this.embeddings = embeddings; - } public List getRows() { return rows; @@ -67,23 +58,13 @@ public void setRows(final List rows) { this.rows = rows; } - public List getOutput() { - return output; - } - - public void setOutput(final List output) { - this.output = output; - } - @Override public String toString() { return "Step{" + "line=" + line + ", name='" + name + '\'' + ", keyword='" + keyword + '\'' + - ", output=" + output + ", rows=" + rows + - ", embeddings=" + embeddings + '}'; } } diff --git a/plugin-code/src/main/java/com/trivago/rta/rendering/RenderingUtils.java b/plugin-code/src/main/java/com/trivago/rta/rendering/RenderingUtils.java index 5710e739..cbda14a5 100644 --- a/plugin-code/src/main/java/com/trivago/rta/rendering/RenderingUtils.java +++ b/plugin-code/src/main/java/com/trivago/rta/rendering/RenderingUtils.java @@ -16,6 +16,8 @@ package com.trivago.rta.rendering; +import org.jsoup.Jsoup; + import java.time.Duration; public class RenderingUtils { @@ -45,6 +47,16 @@ public static long convertMicrosecondsToMilliseconds(final long microseconds) { return Duration.ofMillis(microseconds / MICROSECOND_FACTOR).toMillis(); } + /** + * Returns prettified HTML + * + * @param html The source html. + * @return The prettified HTML. + */ + static String prettifyHtml(String html) { + return Jsoup.parse(html).toString().trim(); + } + /** * Return the current Cluecumber version. * diff --git a/plugin-code/src/main/java/com/trivago/rta/rendering/ReportGenerator.java b/plugin-code/src/main/java/com/trivago/rta/rendering/ReportGenerator.java index 9eeaa655..510b2454 100644 --- a/plugin-code/src/main/java/com/trivago/rta/rendering/ReportGenerator.java +++ b/plugin-code/src/main/java/com/trivago/rta/rendering/ReportGenerator.java @@ -99,7 +99,6 @@ private void copyReportAssets() throws CluecumberPluginException { String reportDirectory = propertyManager.getGeneratedHtmlReportDirectory(); fileSystemManager.createDirectory(reportDirectory); fileSystemManager.createDirectory(reportDirectory + "/js"); - fileSystemManager.createDirectory(reportDirectory + "/img"); fileSystemManager.createDirectory(reportDirectory + "/css"); // Copy CSS resources diff --git a/plugin-code/src/main/java/com/trivago/rta/rendering/TemplateEngine.java b/plugin-code/src/main/java/com/trivago/rta/rendering/TemplateEngine.java index a3a2280f..c9edf6ff 100644 --- a/plugin-code/src/main/java/com/trivago/rta/rendering/TemplateEngine.java +++ b/plugin-code/src/main/java/com/trivago/rta/rendering/TemplateEngine.java @@ -51,23 +51,23 @@ public TemplateEngine( } String getRenderedStartPageContent(final StartPageCollection startPageCollection) throws CluecumberPluginException { - return startPageRenderer.getRenderedContent( + return RenderingUtils.prettifyHtml(startPageRenderer.getRenderedContent( startPageCollection, templateConfiguration.getTemplate(PluginSettings.SUITE_OVERVIEW_PAGE_PATH) - ); + )); } String getRenderedDetailPageContent(final DetailPageCollection detailPageCollection) throws CluecumberPluginException { - return scenarioDetailPageRenderer.getRenderedContent( + return RenderingUtils.prettifyHtml(scenarioDetailPageRenderer.getRenderedContent( detailPageCollection, templateConfiguration.getTemplate(PluginSettings.SCENATIO_DETAIL_PAGE_PATH) - ); + )); } String getRenderedTagSummaryPageContent(final TagSummaryPageCollection tagSummaryPageCollection) throws CluecumberPluginException { - return tagSummaryPageRenderer.getRenderedContent( + return RenderingUtils.prettifyHtml(tagSummaryPageRenderer.getRenderedContent( tagSummaryPageCollection, templateConfiguration.getTemplate(PluginSettings.TAG_SUMMARY_PAGE_PATH) - ); + )); } } diff --git a/plugin-code/src/main/resources/template/css/cluecumber.css b/plugin-code/src/main/resources/template/css/cluecumber.css index 4eba2748..d92fa269 100644 --- a/plugin-code/src/main/resources/template/css/cluecumber.css +++ b/plugin-code/src/main/resources/template/css/cluecumber.css @@ -56,8 +56,8 @@ code { padding: 1rem; } -.list-group-item .row:hover{ - background-color: rgba(0,0,0,.075); +.list-group-item .row:hover { + background-color: rgba(0, 0, 0, .075); } div.tooltip-inner { @@ -65,6 +65,11 @@ div.tooltip-inner { white-space: nowrap; } -table.compact{ +table.compact { width: min-content; +} + +iframe { + border: 1px solid #ccc; + padding: 5px 5px 0 5px; } \ No newline at end of file diff --git a/plugin-code/src/main/resources/template/macros/page.ftl b/plugin-code/src/main/resources/template/macros/page.ftl index ebfaf774..d04b2b0d 100644 --- a/plugin-code/src/main/resources/template/macros/page.ftl +++ b/plugin-code/src/main/resources/template/macros/page.ftl @@ -25,7 +25,7 @@
${title}
<#if subtitle != ""> -
${subtitle}
+
${subtitle}
<#nested> diff --git a/plugin-code/src/main/resources/template/macros/scenario.ftl b/plugin-code/src/main/resources/template/macros/scenario.ftl index 0e1187ea..103c72e1 100644 --- a/plugin-code/src/main/resources/template/macros/scenario.ftl +++ b/plugin-code/src/main/resources/template/macros/scenario.ftl @@ -41,13 +41,13 @@ <#list report.elements as element> <#if (skippedRequested && element.skipped) || (failedRequested && element.failed) || (passedRequested && element.passed)> - ${report.name?html} + ${report.name?html} - + ${element.name?html} - ${element.returnTotalDurationString()} @@ -62,4 +62,66 @@
+ + +<#macro attachments step> + <#if step.embeddings??> + <#list step.embeddings as attachment> +
+
+
+ <#if attachment.image> + + + + <#else> + ${attachment.data?html} + +
+
+
+ + + + +<#macro status step> + <#if step.failed> + <#assign class = "text-danger" /> + <#elseif step.skipped> + <#assign class = "text-warning" /> + <#else> + <#assign class = "text-success" /> + + ${step.status.statusString} + + +<#macro errorMessage step> + <#if step.result.hasErrorMessage()> +
+
+
+ ${step.result.errorMessage?html} +
+
+
+ + + +<#macro output step> + <#if step.output??> + <#list step.output as output> + <#if output?has_content> +
+
+
+ +
+
+
+ + + \ No newline at end of file diff --git a/plugin-code/src/main/resources/template/scenario-detail.ftl b/plugin-code/src/main/resources/template/scenario-detail.ftl index 7622830f..4d635c6a 100644 --- a/plugin-code/src/main/resources/template/scenario-detail.ftl +++ b/plugin-code/src/main/resources/template/scenario-detail.ftl @@ -1,5 +1,5 @@ <#import "macros/page.ftl"as page> -<#import "macros/scenario.ftl" as scenarioMacros> +<#import "macros/scenario.ftl" as scenario> <#import "macros/navigation.ftl" as navigation> <@page.page base="../.." links=["tag_summary", "suite_overview"]> @@ -48,25 +48,12 @@ ${before.result.returnDurationString()}
- <#if before.failed> - <#assign class = "text-danger" /> - <#elseif before.skipped> - <#assign class = "text-warning" /> - <#else> - <#assign class = "text-success" /> - - ${before.status.statusString} + <@scenario.status step=before/>
- <#if before.result.hasErrorMessage()> -
-
-
- ${before.result.errorMessage?html} -
-
-
- + <@scenario.errorMessage step=before/> + <@scenario.output step=before/> + <@scenario.attachments step=before/> @@ -97,52 +84,12 @@ ${step.result.returnDurationString()}
- <#if step.failed> - <#assign class = "text-danger" /> - <#elseif step.skipped> - <#assign class = "text-warning" /> - <#else> - <#assign class = "text-success" /> - - ${step.status.statusString} + <@scenario.status step=step/>
- <#if step.result.hasErrorMessage()> -
-
-
- ${step.result.errorMessage?html} -
-
-
- - <#list step.output as output> -
-
-
- -
-
-
- - <#list step.embeddings as embedding> -
-
-
- <#if embedding.image> - - - - <#else> - ${embedding.data?html} - -
-
-
- + <@scenario.errorMessage step=step/> + <@scenario.output step=step/> + <@scenario.attachments step=step/> @@ -161,42 +108,12 @@ ${after.result.returnDurationString()}
- <#if after.failed> - <#assign class = "text-danger" /> - <#elseif after.skipped> - <#assign class = "text-warning" /> - <#else> - <#assign class = "text-success" /> - - ${after.status.statusString} + <@scenario.status step=after/>
- <#if after.result.hasErrorMessage()> -
-
-
- ${after.result.errorMessage?html} -
-
-
- - <#list after.embeddings as embedding> -
-
-
- <#if embedding.image> - - - - <#else> - ${embedding.data?html} - -
-
-
- + <@scenario.errorMessage step=after/> + <@scenario.output step=after/> + <@scenario.attachments step=after/> diff --git a/plugin-code/src/test/java/com/trivago/rta/json/JsonPojoConverterTest.java b/plugin-code/src/test/java/com/trivago/rta/json/JsonPojoConverterTest.java index 5d3b7f89..93a89ce7 100644 --- a/plugin-code/src/test/java/com/trivago/rta/json/JsonPojoConverterTest.java +++ b/plugin-code/src/test/java/com/trivago/rta/json/JsonPojoConverterTest.java @@ -114,7 +114,7 @@ public void convertJsonToReportPojosTest() throws CluecumberPluginException { Report report = reports[0]; assertThat(report.getName(), is("Test")); assertThat(report.getId(), is("test")); - assertThat(report.toString(), is("Report{line=1, elements=[Element{before=[ResultMatch{result=Result{duration=5554929, status='passed', errorMessage=''}, match=Match{location='BeforeAfterScenario.before(Scenario)', arguments=[]}}], line=5, name='Test feature', description='', id='test;id', after=[ResultMatch{result=Result{duration=153270, status='passed', errorMessage=''}, match=Match{location='BeforeAfterScenario.after(Scenario)', arguments=[]}}], type='scenario', keyword='Scenario', steps=[Step{line=7, name='the start page is opened', keyword='Given ', output=[], rows=[], embeddings=[]}, Step{line=8, name='I see something', keyword='Then ', output=[], rows=[], embeddings=[]}], tags=[Tag{name='@sometag'}, Tag{name='@someothertag'}], scenarioIndex=-1}], name='Test', description='', id='test', keyword='Feature', uri='parallel/features/Test.feature'}")); + assertThat(report.toString(), is("Report{line=1, elements=[Element{before=[ResultMatch{result=Result{duration=5554929, status='passed', errorMessage=''}, match=Match{location='BeforeAfterScenario.before(Scenario)', arguments=[]}, output=[], embeddings=[]}], line=5, name='Test feature', description='', id='test;id', after=[ResultMatch{result=Result{duration=153270, status='passed', errorMessage=''}, match=Match{location='BeforeAfterScenario.after(Scenario)', arguments=[]}, output=[], embeddings=[]}], type='scenario', keyword='Scenario', steps=[Step{line=7, name='the start page is opened', keyword='Given ', rows=[]}, Step{line=8, name='I see something', keyword='Then ', rows=[]}], tags=[Tag{name='@sometag'}, Tag{name='@someothertag'}], scenarioIndex=-1}], name='Test', description='', id='test', keyword='Feature', uri='parallel/features/Test.feature'}")); } @Test(expected = CluecumberPluginException.class) diff --git a/plugin-code/src/test/java/com/trivago/rta/rendering/ReportGeneratorTest.java b/plugin-code/src/test/java/com/trivago/rta/rendering/ReportGeneratorTest.java index cf51ee47..de74382b 100644 --- a/plugin-code/src/test/java/com/trivago/rta/rendering/ReportGeneratorTest.java +++ b/plugin-code/src/test/java/com/trivago/rta/rendering/ReportGeneratorTest.java @@ -68,7 +68,7 @@ public void fileOperationsTest() throws Exception { reportGenerator.generateReport(startPageCollection); - verify(fileSystemManager, times(4)).createDirectory(anyString()); + verify(fileSystemManager, times(3)).createDirectory(anyString()); verify(fileSystemManager, times(11)).exportResource(any(Class.class), anyString(), anyString()); verify(fileIO, times(1)).writeContentToFile(eq("RENDERED_START_PAGE_CONTENT"), anyString()); verify(fileIO, times(2)).writeContentToFile(eq("RENDERED_DETAIL_PAGE_CONTENT"), anyString()); diff --git a/plugin-code/src/test/java/com/trivago/rta/rendering/TemplateEngineTest.java b/plugin-code/src/test/java/com/trivago/rta/rendering/TemplateEngineTest.java index 81f6857c..3d333484 100644 --- a/plugin-code/src/test/java/com/trivago/rta/rendering/TemplateEngineTest.java +++ b/plugin-code/src/test/java/com/trivago/rta/rendering/TemplateEngineTest.java @@ -43,7 +43,7 @@ public void getRenderedStartPageTest() throws CluecumberPluginException { when(templateConfiguration.getTemplate("index")).thenReturn(template); when(startPageRenderer.getRenderedContent(startPageCollection, template)).thenReturn("START_PAGE_CONTENT"); String renderedStartPage = templateEngine.getRenderedStartPageContent(startPageCollection); - assertThat(renderedStartPage, is("START_PAGE_CONTENT")); + assertThat(renderedStartPage, is("\n \n \n START_PAGE_CONTENT\n \n")); } @Test @@ -53,7 +53,7 @@ public void getRenderedDetailPageTest() throws CluecumberPluginException { when(templateConfiguration.getTemplate("scenario-detail")).thenReturn(template); when(scenarioDetailPageRenderer.getRenderedContent(detailPageCollection, template)).thenReturn("DETAIL_PAGE_CONTENT"); String renderedDetailPage = templateEngine.getRenderedDetailPageContent(detailPageCollection); - assertThat(renderedDetailPage, is("DETAIL_PAGE_CONTENT")); + assertThat(renderedDetailPage, is("\n \n \n DETAIL_PAGE_CONTENT\n \n")); } @Test @@ -63,6 +63,6 @@ public void getRenderedTagPageTest() throws CluecumberPluginException { when(templateConfiguration.getTemplate("tag-summary")).thenReturn(template); when(tagSummaryPageRenderer.getRenderedContent(tagSummaryPageCollection, template)).thenReturn("TAG_PAGE_CONTENT"); String renderedTagSummaryPage = templateEngine.getRenderedTagSummaryPageContent(tagSummaryPageCollection); - assertThat(renderedTagSummaryPage, is("TAG_PAGE_CONTENT")); + assertThat(renderedTagSummaryPage, is("\n \n \n TAG_PAGE_CONTENT\n \n")); } }