diff --git a/.gitignore b/.gitignore index 175dd8e6..199c2a5c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ .classpath .factorypath .DS_Store -package-lock.json \ No newline at end of file +package-lock.json +.flattened-pom.xml \ No newline at end of file diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 42a98cc1..8943ebbd 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -16,4 +16,4 @@ # under the License. wrapperVersion=3.3.2 distributionType=only-script -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.1/apache-maven-3.9.1-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/4.0.0-beta-3/apache-maven-4.0.0-beta-3-bin.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d111931..f00cfb15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. Back to [Readme](README.md). +## [3.8.0] - 2024-09-06 + +### Fixed + +* Better detection of embedded URLs in attachments, docstrings and outputs +* Fixed table alignment for duration column +* All graphs and totals now show the correct numbers based on the current rerun settings +* Aligned hover colors of overview and detail pages + +### Added + +* Logged report location now includes `file://` prefix +* Added `Rerun Scenarios` page in case of `groupPreviousScenarioRuns` mode +* Added a dedicated `Reruns` button to each scenario with previous runs + +### Changed + +* Updated JUnit Jupiter to `5.11.0` +* Updated Mockito to `5.13.0` +* Updated Dagger to `5.52` +* Build and test process uses Maven 4 wrapper + ## [3.7.1] - 2024-07-12 ### Fixed @@ -889,7 +911,10 @@ the core component is now the reporting engine that is the base for other forms Initial project version on GitHub and Maven Central. +[3.8.0]: https://github.com/trivago/cluecumber-report-plugin/tree/v3.8.0 + [3.7.1]: https://github.com/trivago/cluecumber-report-plugin/tree/v3.7.1 + [3.7.0]: https://github.com/trivago/cluecumber-report-plugin/tree/v3.7.0 [3.6.3]: https://github.com/trivago/cluecumber-report-plugin/tree/v3.6.3 diff --git a/README.md b/README.md index a532a51c..3684e35c 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ A fully generated example report can be [viewed here](https://softwaretester.blo Cluecumber generates the following report pages: * __All Scenarios__: all scenarios grouped by their status `passed`, `failed` or `skipped`. +* __Rerun Scenarios__: all scenarios that had previous runs if the respective option is turned on. * __Scenario Sequence__: all scenarios in running order including their individual status information * __Scenario Details__: all steps, hooks, stack traces and attachments of a single scenario * __All Features__: an overview of all features @@ -47,7 +48,7 @@ All changes are documented in the [full changelog](CHANGELOG.md). ## Building -Cluecumber requires Java >= 11 and Maven >= 3.3.9. +Cluecumber requires Java >= 11 and Maven >= 3.6.3. It is available in [Maven central](https://central.sonatype.com/search?q=g%3Acom.trivago.rta++a%3Acluecumber-core+a%3Acluecumber-maven&smo=true). ## Star History diff --git a/core/pom.xml b/core/pom.xml index f9044015..90bed580 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,13 +6,14 @@ 4.0.0 cluecumber-core - 3.7.1 + ${revision} jar cluecumber-parent com.trivago.rta - 3.7.1 + ${revision} + ../pom.xml Cluecumber Core diff --git a/engine/pom.xml b/engine/pom.xml index 239c78a6..22019070 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -8,11 +8,12 @@ cluecumber-parent com.trivago.rta - 3.7.1 + ${revision} + ../pom.xml cluecumber-engine - 3.7.1 + ${revision} jar Cluecumber Engine @@ -28,16 +29,16 @@ 11 UTF-8 - 3.10.1 + 3.13.0 2.8.0-M1 2.3.33 1.9.0 2.11.0 - 2.51.1 + 2.52 1.5.0 1.1.0 0.9.1 - 3.2.2 + 3.4.2 @@ -157,4 +158,11 @@ + + + maven_central + Maven Central + https://repo.maven.apache.org/maven2/ + + \ No newline at end of file diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/CluecumberEngine.java b/engine/src/main/java/com/trivago/cluecumber/engine/CluecumberEngine.java index c80b1061..3a1f7226 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/CluecumberEngine.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/CluecumberEngine.java @@ -139,7 +139,7 @@ public void build( } reportGenerator.generateReport(allScenariosPageCollection); logger.info( - "=> Cluecumber Report: " + propertyManager.getGeneratedHtmlReportDirectory() + "/" + + "=> Cluecumber Report: file:///" + propertyManager.getGeneratedHtmlReportDirectory() + "/" + Settings.START_PAGE + Settings.HTML_FILE_EXTENSION, DEFAULT, COMPACT, diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/constants/Navigation.java b/engine/src/main/java/com/trivago/cluecumber/engine/constants/Navigation.java index 502c4c55..b8ac5f54 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/constants/Navigation.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/constants/Navigation.java @@ -30,6 +30,7 @@ public class Navigation { */ public static final List internalLinks = Arrays.asList( new Link("scenario_summary", "pages/scenario-summary.html", LinkType.INTERNAL), + new Link("rerun_scenarios", "pages/rerun-scenarios.html", LinkType.INTERNAL), new Link("scenario_sequence", "pages/scenario-sequence.html", LinkType.INTERNAL), new Link("tag_summary", "pages/tag-summary.html", LinkType.INTERNAL), new Link("step_summary", "pages/step-summary.html", LinkType.INTERNAL), diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/constants/Settings.java b/engine/src/main/java/com/trivago/cluecumber/engine/constants/Settings.java index bd07b50b..52c5e234 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/constants/Settings.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/constants/Settings.java @@ -60,6 +60,14 @@ public class Settings { * The first part of the name of scenario detail pages. */ public static final String SCENARIO_DETAIL_PAGE_FRAGMENT = "/" + SCENARIO_DETAIL_PAGE_PATH + "/scenario_"; + /** + * The folder of the scenario rerun pages. + */ + public final static String SCENARIO_RERUN_PAGE_PATH = "scenario-detail"; + /** + * The first part of the name of scenario rerun pages. + */ + public static final String SCENARIO_RERUN_PAGE_FRAGMENT = "/" + SCENARIO_RERUN_PAGE_PATH + "/scenario_"; /** * The name of the tag summary page. */ @@ -100,6 +108,11 @@ public class Settings { * The name of the tree view page. */ public static final String TREE_VIEW_PAGE = "tree-view"; + /** + * The name of the tree view page. + */ + public static final String RERUN_SCENARIOS_PAGE = "rerun-scenarios"; + /** * Defines all possible start pages. @@ -128,7 +141,11 @@ public enum StartPage { /** * The tree view page. */ - TREE_VIEW(TREE_VIEW_PAGE); + TREE_VIEW(TREE_VIEW_PAGE), + /** + * The rerun scenarios page. + */ + RERUN_SCENARIOS(RERUN_SCENARIOS_PAGE); private final String pageName; diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/filesystem/FileSystemManager.java b/engine/src/main/java/com/trivago/cluecumber/engine/filesystem/FileSystemManager.java index 8f75fa4a..5b634222 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/filesystem/FileSystemManager.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/filesystem/FileSystemManager.java @@ -33,6 +33,7 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Manages access to the file system. @@ -60,13 +61,11 @@ public FileSystemManager(final CluecumberLogger logger) { */ public List getJsonFilePaths(final String sourcePath) { List jsonFilePaths = new ArrayList<>(); - try { - jsonFilePaths = - Files.walk(Paths.get(sourcePath)) - .filter(Files::isRegularFile) - .filter(p -> p.toString().toLowerCase().endsWith(".json")) - .collect(Collectors.toList()); - + try (Stream paths = Files.walk(Paths.get(sourcePath))) { + jsonFilePaths = paths + .filter(Files::isRegularFile) + .filter(p -> p.toString().toLowerCase().endsWith(".json")) + .collect(Collectors.toList()); } catch (IOException e) { logger.warn("Unable to traverse JSON files in " + sourcePath); } diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/json/pojo/Element.java b/engine/src/main/java/com/trivago/cluecumber/engine/json/pojo/Element.java index 96d10de8..e5ee098e 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/json/pojo/Element.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/json/pojo/Element.java @@ -32,8 +32,8 @@ public class Element { private List before = new ArrayList<>(); private int line; - private boolean isLastOfMultipleScenarioRuns = false; - private boolean isNotLastOfMultipleScenarioRuns = false; + private boolean isMultiRunParent = false; + private boolean isMultiRunChild = false; private String featureName = ""; private String featureUri = ""; private String name = ""; @@ -41,15 +41,13 @@ public class Element { private String id = ""; private List after = new ArrayList<>(); private String type = ""; - private String keyword = ""; + private final String keyword = ""; private List backgroundSteps = new ArrayList<>(); private List steps = new ArrayList<>(); private List tags = new ArrayList<>(); @SerializedName("start_timestamp") private String startTimestamp = ""; - private List childrenElements = new ArrayList<>(); - ; - + private List multiRunChildren = new ArrayList<>(); private transient int featureIndex = 0; private transient int scenarioIndex = 0; private transient boolean failOnPendingOrUndefined = false; @@ -535,11 +533,10 @@ private int getNumberOfStepsWithStatus(final Status status) { * @return the duration in nanoseconds. */ public long getTotalDuration() { - long totalDurationNanoseconds = before.stream().mapToLong(beforeStep -> beforeStep.getResult().getDuration()).sum(); - totalDurationNanoseconds += backgroundSteps.stream().mapToLong(Step::getTotalDuration).sum(); - totalDurationNanoseconds += steps.stream().mapToLong(Step::getTotalDuration).sum(); - totalDurationNanoseconds += after.stream().mapToLong(afterStep -> afterStep.getResult().getDuration()).sum(); - return totalDurationNanoseconds; + return before.stream().mapToLong(beforeStep -> beforeStep.getResult().getDuration()).sum() + + backgroundSteps.stream().mapToLong(Step::getTotalDuration).sum() + + steps.stream().mapToLong(Step::getTotalDuration).sum() + + after.stream().mapToLong(afterStep -> afterStep.getResult().getDuration()).sum(); } /** @@ -662,9 +659,9 @@ public boolean hasStepHooks() { } /** - * Check if this scenario contains sub-sections. + * Check if this scenario contains subsections. * - * @return true if there are sub-sections. + * @return true if there are subsections. */ public boolean hasSubSections() { for (Step step : backgroundSteps) { @@ -789,17 +786,17 @@ public void setId(final String id) { * * @return true if this scenario is the last of multiple runs. */ - public boolean getIsLastOfMultipleScenarioRuns() { - return isLastOfMultipleScenarioRuns; + public boolean isMultiRunParent() { + return isMultiRunParent; } /** * Set to true if this scenario is the last of multiple runs. * - * @param isLastOfMultipleScenarioRuns true if this scenario is the last of multiple runs. + * @param isMultiRunParent true if this scenario is the last of multiple runs. */ - public void setIsLastOfMultipleScenarioRuns(final boolean isLastOfMultipleScenarioRuns) { - this.isLastOfMultipleScenarioRuns = isLastOfMultipleScenarioRuns; + public void setMultiRunParent(final boolean isMultiRunParent) { + this.isMultiRunParent = isMultiRunParent; } /** @@ -807,17 +804,17 @@ public void setIsLastOfMultipleScenarioRuns(final boolean isLastOfMultipleScenar * * @return true if this scenario was run multiple times and it's not the last run. */ - public boolean getIsNotLastOfMultipleScenarioRuns() { - return isNotLastOfMultipleScenarioRuns; + public boolean isMultiRunChild() { + return isMultiRunChild; } /** - * Set to true if this scenario was run multiple times and it's not the last run. + * Set to true if this scenario was run multiple times, but it's not the last run. * - * @param isNotLastOfMultipleScenarioRuns true if this scenario was run multiple times and it's not the last run. + * @param isMultiRunChild true if this scenario was run multiple times and it's not the last run. */ - public void setIsNotLastOfMultipleScenarioRuns(final boolean isNotLastOfMultipleScenarioRuns) { - this.isNotLastOfMultipleScenarioRuns = isNotLastOfMultipleScenarioRuns; + public void isMultiRunChild(final boolean isMultiRunChild) { + this.isMultiRunChild = isMultiRunChild; } /** @@ -825,17 +822,26 @@ public void setIsNotLastOfMultipleScenarioRuns(final boolean isNotLastOfMultiple * * @return The children elements. */ - public List getChildrenElements() { - return childrenElements; + public List getMultiRunChildren() { + return multiRunChildren; } /** * Set the children elements of this scenario. * - * @param childrenElements The children elements. + * @param multiRunChildren The children elements. + */ + public void setMultiRunChildren(final List multiRunChildren) { + this.multiRunChildren = multiRunChildren; + } + + /** + * Check if this scenario is part of a multi-run. + * + * @return true if this scenario is part of a multi-run. */ - public void setChildrenElements(final List childrenElements) { - this.childrenElements = childrenElements; + public boolean isPartOfMultiRun() { + return isMultiRunParent || isMultiRunChild; } /** diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/json/pojo/Report.java b/engine/src/main/java/com/trivago/cluecumber/engine/json/pojo/Report.java index a32fbdb8..a5035b75 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/json/pojo/Report.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/json/pojo/Report.java @@ -27,7 +27,6 @@ public class Report implements Cloneable { private String name = ""; private String description = ""; private String id = ""; - private String keyword = ""; private String uri = ""; private List tags = new ArrayList<>(); diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ElementIndexPreProcessor.java b/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ElementIndexPreProcessor.java index 9cfb7e28..935161bc 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ElementIndexPreProcessor.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ElementIndexPreProcessor.java @@ -64,47 +64,36 @@ public void process(final List reports) { int stepIndex = 0; Step oldStep = null; for (Step step : element.getBackgroundSteps()) { - int count = 0; - step.setIndex(stepIndex); - for (int i = 0; i < step.getKeyword().length(); i++) { - if (step.getKeyword().charAt(i) == '>') { - count++; - } else { - break; - } - } - if (count > 0) { - step.setCollapseLevel(count); - step.setKeyword(step.getKeyword().substring(count).trim()); - } - if (oldStep != null && oldStep.getCollapseLevel() < step.getCollapseLevel()) { - oldStep.setHasSubSections(true); - } + processStep(step, stepIndex, oldStep); oldStep = step; stepIndex++; } oldStep = null; for (Step step : element.getSteps()) { - int count = 0; - step.setIndex(stepIndex); - for (int i = 0; i < step.getKeyword().length(); i++) { - if (step.getKeyword().charAt(i) == '>') { - count++; - } else { - break; - } - } - if (count > 0) { - step.setCollapseLevel(count); - step.setKeyword(step.getKeyword().substring(count).trim()); - } - if (oldStep != null && oldStep.getCollapseLevel() < step.getCollapseLevel()) { - oldStep.setHasSubSections(true); - } + processStep(step, stepIndex, oldStep); oldStep = step; stepIndex++; } } } } + + private void processStep(Step step, int stepIndex, Step oldStep) { + int count = 0; + step.setIndex(stepIndex); + for (int i = 0; i < step.getKeyword().length(); i++) { + if (step.getKeyword().charAt(i) == '>') { + count++; + } else { + break; + } + } + if (count > 0) { + step.setCollapseLevel(count); + step.setKeyword(step.getKeyword().substring(count).trim()); + } + if (oldStep != null && oldStep.getCollapseLevel() < step.getCollapseLevel()) { + oldStep.setHasSubSections(true); + } + } } diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ElementMultipleRunsPreProcessor.java b/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ElementMultipleRunsPreProcessor.java index 60ba796b..fc35981b 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ElementMultipleRunsPreProcessor.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ElementMultipleRunsPreProcessor.java @@ -60,19 +60,26 @@ public void addMultipleRunsInformationToScenarios(final List reports) { )); // set flags based on start time and add children element to last run element - groupedElements.values().forEach(idGroup -> idGroup.values().forEach(lineGroup -> { - if (lineGroup.size() < 2) { + groupedElements.values().forEach(idGroup -> idGroup.values().forEach(group -> { + if (group.size() < 2) { return; } - lineGroup.sort(Comparator.comparing( + group.sort(Comparator.comparing( Element::getStartDateTime, Comparator.nullsLast(Comparator.naturalOrder())).reversed()); - Element lastRunElement = lineGroup.get(0); - lastRunElement.setIsLastOfMultipleScenarioRuns(true); - List childrenElements = new ArrayList<>(lineGroup.subList(1, lineGroup.size())); - for (Element element : childrenElements) { - element.setIsNotLastOfMultipleScenarioRuns(true); + Element lastRunElement = group.get(0); + lastRunElement.setMultiRunChildren(group); + lastRunElement.setMultiRunParent(true); + // remove first of line group as it is the last run element + group.remove(0); + for (Element element : group) { + element.isMultiRunChild(true); } - lastRunElement.setChildrenElements(childrenElements); + lastRunElement.setMultiRunChildren(group); })); + + // Remove elements marked as isMultiRunChild from each report + for (Report report : reports) { + report.getElements().removeIf(Element::isMultiRunChild); + } } } diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ReportJsonPostProcessor.java b/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ReportJsonPostProcessor.java index 81e395ed..75e96070 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ReportJsonPostProcessor.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/json/processors/ReportJsonPostProcessor.java @@ -71,7 +71,7 @@ private void addFeatureInformationToScenarios(final Report report) { element.setFeatureUri(featureUri); element.setFeatureName(featureName); element.setFeatureIndex(featureIndex); - if (reportTags.size() > 0) { + if (!reportTags.isEmpty()) { List mergedTags = Stream.concat(element.getTags().stream(), reportTags.stream()) .distinct() .collect(Collectors.toList()); diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/properties/PropertyManager.java b/engine/src/main/java/com/trivago/cluecumber/engine/properties/PropertyManager.java index 09f6ce4e..eb53dd34 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/properties/PropertyManager.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/properties/PropertyManager.java @@ -229,12 +229,13 @@ public void setCustomNavigationLinks(final Map customNavigationL */ public List getNavigationLinks() { List links = new LinkedList<>(Navigation.internalLinks); - customNavigationLinks.forEach((key, value) -> { String linkName = key.replace("_", " "); links.add(new Link(linkName, value, LinkType.EXTERNAL)); }); + links.removeIf(link -> !groupPreviousScenarioRuns && link.getName().equals("rerun_scenarios")); + return links; } diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/pojos/pagecollections/AllScenariosPageCollection.java b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/pojos/pagecollections/AllScenariosPageCollection.java index 3292b91d..222c95df 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/pojos/pagecollections/AllScenariosPageCollection.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/pojos/pagecollections/AllScenariosPageCollection.java @@ -174,8 +174,8 @@ public boolean hasSkippedScenarios() { * * @return true if there are scenarios run multiple times. */ - public boolean hasNotLastRunScenarios() { - return getTotalNumberOfNotLastScenariosRuns() > 0; + public boolean hasMultiRunChildren() { + return getTotalNumberOfMultiRunChildren() > 0; } /** @@ -204,7 +204,7 @@ public int getTotalNumberOfFailedScenarios() { public int getTotalNumberOfFailedScenarioWithoutLaterRuns() { return reports.stream().mapToInt( report -> (int) report.getElements().stream().filter( - element -> element.getStatus().equals(Status.FAILED) && !element.getIsNotLastOfMultipleScenarioRuns() + element -> element.getStatus().equals(Status.FAILED) && !element.isMultiRunChild() ).count()).sum(); } @@ -222,11 +222,14 @@ public int getTotalNumberOfSkippedScenarios() { * * @return The scenario count. */ - public int getTotalNumberOfNotLastScenariosRuns() { - return reports.stream().mapToInt( - report -> (int) report.getElements().stream().filter( - element -> element.getIsNotLastOfMultipleScenarioRuns() - ).count()).sum(); + public int getTotalNumberOfMultiRunChildren() { + int sum = 0; + for (Report report : reports) { + for (Element element : report.getElements()) { + sum += element.getMultiRunChildren().size(); + } + } + return sum; } private int getNumberOfScenariosWithStatus(final Status status) { @@ -266,7 +269,7 @@ private ZonedDateTime getEarliestStartDateTime() { for (Element element : report.getElements()) { ZonedDateTime currentStartDateTime = element.getStartDateTime(); if (currentStartDateTime != null && - (earliestStartDateTime == null || currentStartDateTime.isBefore(earliestStartDateTime))) { + (earliestStartDateTime == null || currentStartDateTime.isBefore(earliestStartDateTime))) { earliestStartDateTime = currentStartDateTime; } } @@ -280,7 +283,7 @@ private ZonedDateTime getLatestEndDateTime() { for (Element element : report.getElements()) { ZonedDateTime currentEndDateTime = element.getEndDateTime(); if (currentEndDateTime != null && - (latestEndDateTime == null || currentEndDateTime.isAfter(latestEndDateTime))) { + (latestEndDateTime == null || currentEndDateTime.isAfter(latestEndDateTime))) { latestEndDateTime = currentEndDateTime; } } @@ -297,7 +300,7 @@ public String returnStartDateTimeString() { ZonedDateTime earliestStartDateTime = getEarliestStartDateTime(); if (earliestStartDateTime != null) { return RenderingUtils.convertZonedDateTimeToDateString(earliestStartDateTime) + " " + - RenderingUtils.convertZonedDateTimeToTimeString(earliestStartDateTime); + RenderingUtils.convertZonedDateTimeToTimeString(earliestStartDateTime); } return ""; } @@ -311,7 +314,7 @@ public String returnEndDateTimeString() { ZonedDateTime latestEndDateTime = getLatestEndDateTime(); if (latestEndDateTime != null) { return RenderingUtils.convertZonedDateTimeToDateString(latestEndDateTime) + " " + - RenderingUtils.convertZonedDateTimeToTimeString(latestEndDateTime); + RenderingUtils.convertZonedDateTimeToTimeString(latestEndDateTime); } return ""; } diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/renderering/AllScenariosPageRenderer.java b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/renderering/AllScenariosPageRenderer.java index f3046dcf..9986dbd2 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/renderering/AllScenariosPageRenderer.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/renderering/AllScenariosPageRenderer.java @@ -74,9 +74,29 @@ public String getRenderedContent( throws CluecumberException { AllScenariosPageCollection allScenariosPageCollectionClone = getAllScenariosPageCollectionClone(allScenariosPageCollection); + addChartJsonToReportDetails(allScenariosPageCollectionClone); + return processedContent(template, allScenariosPageCollectionClone, propertyManager.getNavigationLinks()); + } + + /** + * Get the rendered HTML content for the reruns page. + * @param allScenariosPageCollection The {@link AllScenariosPageCollection} instance. + * @param template The {@link Template} instance. + * @return The HTML content as a string. + * @throws CluecumberException Thrown on any error. + */ + public String getRendererContentByReruns(final AllScenariosPageCollection allScenariosPageCollection, + final Template template) throws CluecumberException { + AllScenariosPageCollection allScenariosPageCollectionClone = getAllScenariosPageCollectionClone(allScenariosPageCollection); + + for (Report report : allScenariosPageCollectionClone.getReports()) { + List elements = report.getElements() + .stream() + .filter(Element::isPartOfMultiRun) + .collect(Collectors.toList()); + report.setElements(elements); + } - allScenariosPageCollectionClone.setGroupPreviousScenarioRuns(propertyManager.isGroupPreviousScenarioRuns()); - allScenariosPageCollectionClone.setExpandPreviousScenarioRuns(propertyManager.isExpandPreviousScenarioRuns()); addChartJsonToReportDetails(allScenariosPageCollectionClone); return processedContent(template, allScenariosPageCollectionClone, propertyManager.getNavigationLinks()); } @@ -97,6 +117,7 @@ public String getRenderedContentByTagFilter( AllScenariosPageCollection allScenariosPageCollectionClone = getAllScenariosPageCollectionClone(allScenariosPageCollection); allScenariosPageCollectionClone.setTagFilter(tag); + allScenariosPageCollectionClone.getReports().forEach(report -> { List elements = report.getElements() .stream() @@ -182,6 +203,8 @@ private AllScenariosPageCollection getAllScenariosPageCollectionClone( throw new CluecumberException("Clone of AllScenariosPageCollection not supported: " + e.getMessage()); } addCustomParametersToReportDetails(clone, propertyManager.getCustomParameters()); + clone.setGroupPreviousScenarioRuns(propertyManager.isGroupPreviousScenarioRuns()); + clone.setExpandPreviousScenarioRuns(propertyManager.isExpandPreviousScenarioRuns()); return clone; } } diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/renderering/RenderingUtils.java b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/renderering/RenderingUtils.java index 863885df..4877403f 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/renderering/RenderingUtils.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/renderering/RenderingUtils.java @@ -32,7 +32,8 @@ */ public class RenderingUtils { private static final int MICROSECOND_FACTOR = 1000000; - private static final Pattern URL_PATTERN = Pattern.compile("(file.*)|((ftp|http|https)://(\\w+:?\\w*@)?(\\S+)(:[0-9]+)?(/|/([\\w#!:.?+=&%@\\-/]))?)"); + private static final Pattern URL_PATTERN = Pattern.compile( + "\\b((ftp|https?|file)://\\S+)|\\b(file:\\\\\\\\S+)"); /** * Convert nanoseconds to a human-readable time string. diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/templates/TemplateEngine.java b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/templates/TemplateEngine.java index 505c43b9..d1cb49ab 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/templates/TemplateEngine.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/templates/TemplateEngine.java @@ -70,6 +70,10 @@ public enum Template { * Scenario sequence template. */ SCENARIO_SEQUENCE("scenario-sequence"), + /** + * Rerun scenarios template. + */ + RERUN_SCENARIOS("rerun-scenarios"), /** * Step overview template. */ diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/visitors/ScenarioVisitor.java b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/visitors/ScenarioVisitor.java index 90318183..c1ebd0fe 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/visitors/ScenarioVisitor.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/visitors/ScenarioVisitor.java @@ -29,8 +29,10 @@ import javax.inject.Inject; import javax.inject.Singleton; +import java.util.List; import static com.trivago.cluecumber.engine.rendering.pages.templates.TemplateEngine.Template.ALL_SCENARIOS; +import static com.trivago.cluecumber.engine.rendering.pages.templates.TemplateEngine.Template.RERUN_SCENARIOS; import static com.trivago.cluecumber.engine.rendering.pages.templates.TemplateEngine.Template.SCENARIO_DETAILS; import static com.trivago.cluecumber.engine.rendering.pages.templates.TemplateEngine.Template.SCENARIO_SEQUENCE; @@ -85,7 +87,7 @@ public void visit(final AllScenariosPageCollection allScenariosPageCollection) t templateEngine.getTemplate(ALL_SCENARIOS) ), propertyManager.getGeneratedHtmlReportDirectory() + "/" + Settings.PAGES_DIRECTORY + "/" + - Settings.SCENARIO_SUMMARY_PAGE_PATH + Settings.HTML_FILE_EXTENSION); + Settings.SCENARIO_SUMMARY_PAGE_PATH + Settings.HTML_FILE_EXTENSION); // Scenario sequence page fileIO.writeContentToFile( @@ -94,22 +96,39 @@ public void visit(final AllScenariosPageCollection allScenariosPageCollection) t templateEngine.getTemplate(SCENARIO_SEQUENCE) ), propertyManager.getGeneratedHtmlReportDirectory() + "/" + Settings.PAGES_DIRECTORY + "/" + - Settings.SCENARIO_SEQUENCE_PAGE_PATH + Settings.HTML_FILE_EXTENSION); + Settings.SCENARIO_SEQUENCE_PAGE_PATH + Settings.HTML_FILE_EXTENSION); + + // Scenario reruns page + fileIO.writeContentToFile( + allScenariosPageRenderer.getRendererContentByReruns( + allScenariosPageCollection, + templateEngine.getTemplate(RERUN_SCENARIOS) + ), + propertyManager.getGeneratedHtmlReportDirectory() + "/" + Settings.PAGES_DIRECTORY + "/" + + Settings.RERUN_SCENARIOS_PAGE + Settings.HTML_FILE_EXTENSION); // Scenario detail pages ScenarioDetailsPageCollection scenarioDetailsPageCollection; for (Report report : allScenariosPageCollection.getReports()) { for (Element element : report.getElements()) { - scenarioDetailsPageCollection = new ScenarioDetailsPageCollection(element, propertyManager.getCustomPageTitle()); - fileIO.writeContentToFile( - scenarioDetailsPageRenderer.getRenderedContent( - scenarioDetailsPageCollection, - templateEngine.getTemplate(SCENARIO_DETAILS) - ), - propertyManager.getGeneratedHtmlReportDirectory() + "/" + - Settings.PAGES_DIRECTORY + Settings.SCENARIO_DETAIL_PAGE_FRAGMENT + - element.getScenarioIndex() + Settings.HTML_FILE_EXTENSION); + writeScenarioDetails(element); + // Scenario rerun detail pages + for (Element child : element.getMultiRunChildren()) { + writeScenarioDetails(child); + } } } } + + private void writeScenarioDetails(final Element element) throws CluecumberException { + ScenarioDetailsPageCollection scenarioDetailsPageCollection = new ScenarioDetailsPageCollection(element, propertyManager.getCustomPageTitle()); + fileIO.writeContentToFile( + scenarioDetailsPageRenderer.getRenderedContent( + scenarioDetailsPageCollection, + templateEngine.getTemplate(SCENARIO_DETAILS) + ), + propertyManager.getGeneratedHtmlReportDirectory() + "/" + + Settings.PAGES_DIRECTORY + Settings.SCENARIO_DETAIL_PAGE_FRAGMENT + + element.getScenarioIndex() + Settings.HTML_FILE_EXTENSION); + } } diff --git a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/visitors/StepVisitor.java b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/visitors/StepVisitor.java index 63895828..b5ae7565 100644 --- a/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/visitors/StepVisitor.java +++ b/engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/visitors/StepVisitor.java @@ -90,7 +90,6 @@ public void visit(final AllScenariosPageCollection allScenariosPageCollection) t Settings.STEP_SUMMARY_PAGE + Settings.HTML_FILE_EXTENSION); // Scenarios by step pages - for (Step step : allStepsPageCollection.getSteps()) { fileIO.writeContentToFile( allScenariosPageRenderer.getRenderedContentByStepFilter( diff --git a/engine/src/main/resources/template/css/cluecumber.css b/engine/src/main/resources/template/css/cluecumber.css index accb5942..e9b0284e 100644 --- a/engine/src/main/resources/template/css/cluecumber.css +++ b/engine/src/main/resources/template/css/cluecumber.css @@ -115,7 +115,7 @@ pre { } .list-group-item .row:hover { - background-color: lightgray; + background-color: rgba(0, 0, 0, .075); } .list-group-flush .list-group-item:last-child { @@ -293,4 +293,8 @@ tr.even td, tr.odd td { .dataTables_length { text-align: left; +} + +.multiRunChildren { + margin-top: 1rem; } \ No newline at end of file diff --git a/engine/src/main/resources/template/macros/navigation.ftl b/engine/src/main/resources/template/macros/navigation.ftl index f4f20c9c..ae464a4d 100644 --- a/engine/src/main/resources/template/macros/navigation.ftl +++ b/engine/src/main/resources/template/macros/navigation.ftl @@ -33,6 +33,9 @@ limitations under the License. <#case "scenario_summary"> <#assign linkName="All Scenarios"> <#break> + <#case "rerun_scenarios"> + <#assign linkName="Rerun Scenarios"> + <#break> <#case "scenario_sequence"> <#assign linkName="Scenario Sequence"> <#break> diff --git a/engine/src/main/resources/template/macros/page.ftl b/engine/src/main/resources/template/macros/page.ftl index f9f2d24d..70e6ee83 100644 --- a/engine/src/main/resources/template/macros/page.ftl +++ b/engine/src/main/resources/template/macros/page.ftl @@ -88,7 +88,7 @@ limitations under the License. <#macro graph> -
+
\ No newline at end of file diff --git a/engine/src/main/resources/template/macros/scenario.ftl b/engine/src/main/resources/template/macros/scenario.ftl index 63bc348b..64659d31 100644 --- a/engine/src/main/resources/template/macros/scenario.ftl +++ b/engine/src/main/resources/template/macros/scenario.ftl @@ -22,17 +22,10 @@ limitations under the License. <#assign passedRequested = status == "passed"> <#assign allRequested = status == "all"> - <#if isGroupPreviousScenarioRuns()> - <#assign failuresCondition = (failedRequested && hasFailedScenariosNotPassedOnLastRun())> - <#else> - <#assign failuresCondition = (failedRequested && hasFailedScenarios())> - - <#if - (skippedRequested && hasSkippedScenarios()) || - failuresCondition || + <#if (skippedRequested && hasSkippedScenarios()) || + (failedRequested && hasFailedScenarios()) || (passedRequested && hasPassedScenarios()) || - allRequested - > + allRequested>
@@ -76,14 +69,15 @@ limitations under the License. Feature Scenario - Started - Duration + Started + Duration <#if allRequested> - Status + Status + <#assign counter = 0> <#list reports as report> <#assign tooltipText = ""> <#if report.description?has_content> @@ -92,53 +86,66 @@ limitations under the License. <#assign tooltipText = "${tooltipText}${report.uri}"> <#list report.elements as element> + <#assign counter = counter + 1> <#if (skippedRequested && element.skipped) || (failedRequested && element.failed) || (passedRequested && element.passed) || allRequested> - <#assign isLastOfMultipleScenarioRuns = element.getIsLastOfMultipleScenarioRuns()> - <#assign isNotLastOfMultipleScenarioRuns = element.getIsNotLastOfMultipleScenarioRuns()> - <#if !isGroupPreviousScenarioRuns() || (isGroupPreviousScenarioRuns() && !isNotLastOfMultipleScenarioRuns) || allRequested> - - <#if allRequested> - ${element.scenarioIndex} + + <#if allRequested> + ${counter} + + + + ${report.name?html} + + + + ${element.name?html} + + <#if element.isMultiRunParent()> + - - - ${report.name?html} - - - - ${element.name?html} - <#if element.firstExceptionClass != ""> -

${element.firstExceptionClass}

- - <#if isGroupPreviousScenarioRuns() && isLastOfMultipleScenarioRuns && !allRequested> - <#list element.getChildrenElements() as childElement> -
- └── - Previous run - started - at: ${childElement.startDateString} - - ${childElement.startTimeString} <@common.status status=childElement.status.statusString/> - <#if childElement.firstExceptionClass != ""> -

${childElement.firstExceptionClass}

- -
- - - - - ${element.startDateString}
${element.startTimeString} - - - ${element.returnTotalDurationString()} - - <#if allRequested> - <@common.status status=element.status.statusString/> + + <#if element.firstExceptionClass != ""> +

${element.firstExceptionClass}

+ + + <#if element.isMultiRunParent()> +
+
    + <#list element.getMultiRunChildren() as childElement> +
  1. + Previous run from + ${childElement.startDateString}, ${childElement.startTimeString} + <@common.status status=childElement.status.statusString/> + + <#if childElement.firstExceptionClass != ""> +

    ${childElement.firstExceptionClass}

    + +
  2. + +
+
- - + + + ${element.startDateString}
${element.startTimeString} + + + ${element.returnTotalDurationString()} + + <#if allRequested> + <@common.status status=element.status.statusString/> + + @@ -213,7 +220,8 @@ limitations under the License. <#if step.hasOutputs()>
- | Step Output
diff --git a/engine/src/main/resources/template/rerun-scenarios.ftl b/engine/src/main/resources/template/rerun-scenarios.ftl new file mode 100644 index 00000000..cbb03a19 --- /dev/null +++ b/engine/src/main/resources/template/rerun-scenarios.ftl @@ -0,0 +1,18 @@ +<#-- +Copyright 2024 trivago N.V. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<#assign reruns = 1 /> +<#include "scenario-summary.ftl"> \ No newline at end of file diff --git a/engine/src/main/resources/template/scenario-detail.ftl b/engine/src/main/resources/template/scenario-detail.ftl index 39a01e0c..d1861e45 100644 --- a/engine/src/main/resources/template/scenario-detail.ftl +++ b/engine/src/main/resources/template/scenario-detail.ftl @@ -71,20 +71,25 @@ preheadlineLink="pages/feature-scenarios/feature_${element.featureIndex?c}.html" Total
${element.returnTotalDurationString()} - <#if groupPreviousScenarioRuns && element.getIsLastOfMultipleScenarioRuns()> + <#assign numberOfChildren = element.getMultiRunChildren()?size> + <#if groupPreviousScenarioRuns && element.multiRunParent>
- <#if groupPreviousScenarioRuns && element.getIsNotLastOfMultipleScenarioRuns()> + <#if groupPreviousScenarioRuns && element.multiRunChild>
- <#if (element.hasHooks() && element.hasHooksWithContent()) || element.hasSubSections() || (element.hasStepHooks() && element.hasStepHooksWithContent()) || element.hasDocStrings()> + <#if (element.hasHooks() && element.hasHooksWithContent()) || + element.hasSubSections() || + (element.hasStepHooks() && element.hasStepHooksWithContent()) || + element.hasDocStrings()>
diff --git a/engine/src/main/resources/template/scenario-summary.ftl b/engine/src/main/resources/template/scenario-summary.ftl index c03a264e..59cf4456 100644 --- a/engine/src/main/resources/template/scenario-summary.ftl +++ b/engine/src/main/resources/template/scenario-summary.ftl @@ -56,6 +56,15 @@ limitations under the License. <#assign subsubheadline = ""> <#assign preheadline = ""> <#assign preheadlineLink = ""> +<#elseif (reruns??)> + <#assign base = "./.."> + <#assign headline = "Rerun Scenarios"> + <#assign pageName = "Rerun Scenarios"> + <#assign highlight = "rerun_scenarios"> + <#assign subheadline = ""> + <#assign subsubheadline = ""> + <#assign preheadline = ""> + <#assign preheadlineLink = ""> <#else> <#assign base = "./.."> <#assign headline = "All Scenarios"> @@ -112,11 +121,12 @@ preheadlineLink=preheadlineLink> - <#if isGroupPreviousScenarioRuns() && hasNotLastRunScenarios() && !(scenarioSequence??)> + + <#if hasMultiRunChildren()>
- @@ -144,4 +154,27 @@ preheadlineLink=preheadlineLink> <@scenario.table status="skipped" numberOfScenarios=totalNumberOfSkippedScenarios /> <@scenario.table status="passed" numberOfScenarios=totalNumberOfPassedScenarios /> + + diff --git a/engine/src/main/resources/template/snippets/js.ftl b/engine/src/main/resources/template/snippets/js.ftl index 38554dfe..7b7c0d98 100644 --- a/engine/src/main/resources/template/snippets/js.ftl +++ b/engine/src/main/resources/template/snippets/js.ftl @@ -123,13 +123,13 @@ limitations under the License. $(".scenarioDocstring").collapse("show"); } if (${expandPreviousScenarioRuns?c}) { - $(".btn-outline-secondary[data-cluecumber-item='show-not-last-runs-button']").click(); + $("[data-cluecumber-item='multi-run-button']").click(); } } ); function resizeIframe(iframe) { - setInterval(function() { + setInterval(function () { try { iframe.height = iframe.contentWindow.document.body.scrollHeight + 25 + "px"; } catch (e) { diff --git a/engine/src/main/resources/template/tree-view.ftl b/engine/src/main/resources/template/tree-view.ftl index 4360bf75..0e78e4b8 100644 --- a/engine/src/main/resources/template/tree-view.ftl +++ b/engine/src/main/resources/template/tree-view.ftl @@ -44,7 +44,7 @@ preheadlineLink="">
    <#list scenarios as scenario> - <#if ((!scenario.getIsLastOfMultipleScenarioRuns() && !scenario.getIsNotLastOfMultipleScenarioRuns()) || scenario.getIsLastOfMultipleScenarioRuns()) > + <#if ((!scenario.isMultiRunParent() && !scenario.isMultiRunChild()) || scenario.isMultiRunParent()) >
  1. ${scenario.name?html} diff --git a/engine/src/test/java/com/trivago/cluecumber/engine/json/pojo/DocStringTest.java b/engine/src/test/java/com/trivago/cluecumber/engine/json/pojo/DocStringTest.java index 1b5074ec..0e6b452e 100644 --- a/engine/src/test/java/com/trivago/cluecumber/engine/json/pojo/DocStringTest.java +++ b/engine/src/test/java/com/trivago/cluecumber/engine/json/pojo/DocStringTest.java @@ -25,10 +25,16 @@ public void returnWithClickableLinksTest() { assertEquals(docString.returnWithClickableLinks(), "This should be a https://www.google.de link"); } + @Test + public void returnWithClickableLinksFalseAlarmTest() { + docString.setValue("This should be a profile link with some profile stuff."); + assertEquals(docString.returnWithClickableLinks(), "This should be a profile link with some profile stuff."); + } + @Test public void returnWithClickableLocalLinksTest() { - docString.setValue("The shared location is file:\\MACHINE\\Folder\\Some Folder"); - assertEquals(docString.returnWithClickableLinks(), "The shared location is file:\\MACHINE\\Folder\\Some Folder"); + docString.setValue("The shared location is file:\\MACHINE\\Folder\\Some Folder"); + assertEquals(docString.returnWithClickableLinks(), "The shared location is <a href='file:\\MACHINE\\Folder\\Some Folder' target='_blank'>file:\\MACHINE\\Folder\\Some Folder</a>"); } @Test diff --git a/engine/src/test/java/com/trivago/cluecumber/engine/json/processors/ElementMultipleRunsPreProcessorTest.java b/engine/src/test/java/com/trivago/cluecumber/engine/json/processors/ElementMultipleRunsPreProcessorTest.java index dcd3f465..5d261d86 100644 --- a/engine/src/test/java/com/trivago/cluecumber/engine/json/processors/ElementMultipleRunsPreProcessorTest.java +++ b/engine/src/test/java/com/trivago/cluecumber/engine/json/processors/ElementMultipleRunsPreProcessorTest.java @@ -26,6 +26,7 @@ public void addMultipleRunsInformationToScenariosTest() { Report report = new Report(); List elements = new ArrayList<>(); + // First run of scenario 1 Element element = new Element(); element.setName("Scenario 1"); element.setType("scenario"); @@ -34,6 +35,7 @@ public void addMultipleRunsInformationToScenariosTest() { element.setStartTimestamp("2019-04-11T08:00:21.123Z"); elements.add(element); + // Scenario 2 without reruns element = new Element(); element.setName("Scenario 2"); element.setType("scenario"); @@ -42,6 +44,7 @@ public void addMultipleRunsInformationToScenariosTest() { element.setStartTimestamp("2019-04-11T08:00:21.123Z"); elements.add(element); + // Last run of scenario 1 element = new Element(); element.setName("Scenario 1"); element.setType("scenario"); @@ -56,15 +59,16 @@ public void addMultipleRunsInformationToScenariosTest() { List e = reports.get(0).getElements(); elementMultipleRunsPreProcessor.addMultipleRunsInformationToScenarios(reports); + assertEquals(2, e.size()); - assertTrue(e.get(0).getIsNotLastOfMultipleScenarioRuns()); - assertFalse(e.get(1).getIsNotLastOfMultipleScenarioRuns()); - assertFalse(e.get(2).getIsNotLastOfMultipleScenarioRuns()); - assertFalse(e.get(0).getIsLastOfMultipleScenarioRuns()); - assertFalse(e.get(1).getIsLastOfMultipleScenarioRuns()); - assertTrue(e.get(2).getIsLastOfMultipleScenarioRuns()); - assertEquals(e.get(0).getChildrenElements().size(), 0); - assertEquals(e.get(1).getChildrenElements().size(), 0); - assertEquals(e.get(2).getChildrenElements().size(), 1); + assertFalse(e.get(0).isPartOfMultiRun()); + assertFalse(e.get(0).isMultiRunParent()); + assertFalse(e.get(0).isMultiRunChild()); + assertEquals(e.get(0).getMultiRunChildren().size(), 0); + + assertTrue(e.get(1).isPartOfMultiRun()); + assertTrue(e.get(1).isMultiRunParent()); + assertFalse(e.get(1).isMultiRunChild()); + assertEquals(e.get(1).getMultiRunChildren().size(), 1); } } diff --git a/engine/src/test/java/com/trivago/cluecumber/engine/rendering/pages/pojos/pagecollections/AllScenariosPageCollectionTest.java b/engine/src/test/java/com/trivago/cluecumber/engine/rendering/pages/pojos/pagecollections/AllScenariosPageCollectionTest.java index afaa6f32..3bbc674e 100644 --- a/engine/src/test/java/com/trivago/cluecumber/engine/rendering/pages/pojos/pagecollections/AllScenariosPageCollectionTest.java +++ b/engine/src/test/java/com/trivago/cluecumber/engine/rendering/pages/pojos/pagecollections/AllScenariosPageCollectionTest.java @@ -4,6 +4,7 @@ import com.trivago.cluecumber.engine.json.pojo.Report; import com.trivago.cluecumber.engine.json.pojo.Result; import com.trivago.cluecumber.engine.json.pojo.Step; +import com.trivago.cluecumber.engine.json.processors.ElementMultipleRunsPreProcessor; import com.trivago.cluecumber.engine.rendering.pages.pojos.CustomParameter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -168,18 +169,34 @@ public void hasSkippedScenariosTest() { } @Test - public void hasNotLastRunScenariosTest() { - assertFalse(allScenariosPageCollection.hasNotLastRunScenarios()); - Report[] reportList = new Report[1]; + public void hasMultiRunChildrenTest() { + List reports = new ArrayList<>(); Report report = new Report(); List elements = new ArrayList<>(); + Element element = new Element(); - element.setIsNotLastOfMultipleScenarioRuns(true); + element.setName("Scenario 1"); + element.setType("scenario"); + element.setId("feature1-scenario1"); + element.setLine(3); + element.setStartTimestamp("2019-04-11T08:00:21.123Z"); + elements.add(element); + + element = new Element(); + element.setName("Scenario 1"); + element.setType("scenario"); + element.setId("feature1-scenario1"); + element.setLine(3); + element.setStartTimestamp("2019-04-11T08:00:25.829Z"); elements.add(element); + report.setElements(elements); - reportList[0] = report; - allScenariosPageCollection.addReports(reportList); - assertTrue(allScenariosPageCollection.hasNotLastRunScenarios()); + reports.add(report); + + new ElementMultipleRunsPreProcessor().addMultipleRunsInformationToScenarios(reports); + assertFalse(allScenariosPageCollection.hasMultiRunChildren()); + allScenariosPageCollection.addReports(reports.toArray(new Report[0])); + assertTrue(allScenariosPageCollection.hasMultiRunChildren()); } @Test diff --git a/examples/core-example/pom.xml b/examples/core-example/pom.xml index 5209a35b..b3b00696 100644 --- a/examples/core-example/pom.xml +++ b/examples/core-example/pom.xml @@ -6,8 +6,8 @@ blog.softwaretester core-example - 3.4.0 - pom + 3.8.0 + jar 11 diff --git a/examples/maven-example/json/rerun/rerun.json b/examples/maven-example/json/rerun/rerun.json new file mode 100755 index 00000000..af573897 --- /dev/null +++ b/examples/maven-example/json/rerun/rerun.json @@ -0,0 +1,154 @@ +[ + { + "description": "This is a rerun feature", + "elements": [ + { + "start_timestamp": "2024-09-03T09:23:51.166Z", + "description": "", + "id": ";", + "keyword": "Scenario", + "line": 2, + "name": "This is an interesting scenario", + "steps": [ + { + "keyword": "Given ", + "line": 3, + "match": { + "location": "SomeClass.java:-2" + }, + "name": "this is the first step", + "result": { + "duration": 18244280571, + "status": "passed" + } + }, + { + "keyword": "And ", + "line": 4, + "match": { + "location": "SomeClass.java:-2" + }, + "name": "this is the second step", + "result": { + "duration": 6155261782, + "error_message": "This is the error message of step 2", + "status": "failed" + } + } + ], + "tags": [ + { + "name": "@rerun" + } + ], + "type": "scenario" + } + ], + "id": "", + "keyword": "Feature", + "line": 1, + "name": "Rerun", + "uri": "features/rerun/rerun.feature" + }, + { + "description": "This is a rerun feature", + "elements": [ + { + "start_timestamp": "2024-09-03T09:25:45.606Z", + "description": "", + "id": ";", + "keyword": "Scenario", + "line": 2, + "name": "This is an interesting scenario", + "steps": [ + { + "keyword": "Given ", + "line": 3, + "match": { + "location": "SomeClass.java:-2" + }, + "name": "this is the first step", + "result": { + "duration": 13244280571, + "status": "passed" + } + }, + { + "keyword": "And ", + "line": 4, + "match": { + "location": "SomeClass.java:-2" + }, + "name": "this is the second step", + "result": { + "duration": 5255261782, + "error_message": "This is the error message of step 2", + "status": "failed" + } + } + ], + "tags": [ + { + "name": "@rerun" + } + ], + "type": "scenario" + } + ], + "id": "", + "keyword": "Feature", + "line": 1, + "name": "Rerun", + "uri": "features/rerun/rerun.feature" + }, + { + "description": "This is a rerun feature", + "elements": [ + { + "start_timestamp": "2024-09-03T09:26:13.625Z", + "description": "", + "id": ";", + "keyword": "Scenario", + "line": 2, + "name": "This is an interesting scenario", + "steps": [ + { + "keyword": "Given ", + "line": 3, + "match": { + "location": "SomeClass.java:-2" + }, + "name": "this is the first step", + "result": { + "duration": 12244280571, + "status": "passed" + } + }, + { + "keyword": "And ", + "line": 4, + "match": { + "location": "SomeClass.java:-2" + }, + "name": "this is the second step", + "result": { + "duration": 25155261782, + "status": "passed" + } + } + ], + "tags": [ + { + "name": "@rerun" + } + ], + "type": "scenario" + } + ], + "id": "", + "keyword": "Feature", + "line": 1, + "name": "Rerun", + "uri": "features/rerun/rerun.feature" + } +] diff --git a/examples/maven-example/json/rerun/single.json b/examples/maven-example/json/rerun/single.json new file mode 100644 index 00000000..40c95ea1 --- /dev/null +++ b/examples/maven-example/json/rerun/single.json @@ -0,0 +1,60 @@ +[ + { + "line": 1, + "elements": [ + { + "start_timestamp": "2024-09-03T09:00:23.668Z", + "line": 6, + "name": "Single Scenario", + "description": "", + "id": "shopping", + "type": "scenario", + "keyword": "Scenario", + "steps": [ + { + "result": { + "duration": 467889821, + "status": "passed" + }, + "line": 7, + "name": "I search for book \"Something\"", + "match": { + "arguments": [ + { + "val": "Something", + "offset": 19 + } + ], + "location": "MySteps.I_search(String)" + }, + "keyword": "When " + }, + { + "result": { + "duration": 1691629452, + "status": "passed" + }, + "line": 8, + "name": "the results include \"Something\"", + "match": { + "arguments": [ + { + "val": "Something", + "offset": 21 + } + ], + "location": "MySteps.search_results_include(String)" + }, + "keyword": "Then " + } + ] + } + ], + "name": "Vendor Shopping Cart", + "description": "I\u0027d like to be able to search for my favourite book", + "id": "vendor-shopping-cart", + "keyword": "Feature", + "uri": "classpath:org/ShoppingCart.feature", + "tags": [] + } +] \ No newline at end of file diff --git a/examples/maven-example/pom.xml b/examples/maven-example/pom.xml index 04197140..57a563d7 100644 --- a/examples/maven-example/pom.xml +++ b/examples/maven-example/pom.xml @@ -6,7 +6,7 @@ blog.softwaretester maven-example - 3.7.1 + 3.8.0 pom @@ -37,7 +37,7 @@ - ${cucumber.report.json.location} + ${cucumber.report.json.location}/rerun ${generated.report.location} @@ -63,7 +63,7 @@ - ALL_SCENARIOS + ALL_SCENARIOS @@ -86,8 +86,8 @@ false - true - false + true + false default diff --git a/makefile b/makefile index f59ab55a..4e982415 100644 --- a/makefile +++ b/makefile @@ -1,9 +1,16 @@ -build-and-test: - ./mvnw clean install - (cd examples/maven-example && ../../mvnw verify -e) +build-and-test: build test +.Phony: build-and-test +build: + ./mvnw clean install -U -ntp +.Phony: build + +test: + ./mvnw clean verify -U -ntp -f examples/maven-example/pom.xml open examples/maven-example/target/cluecumber-report/index.html +.Phony: test show-versions: ./mvnw versions:display-dependency-updates -U -ntp - ./mvnw versions:display-plugin-updates -U -ntp \ No newline at end of file + ./mvnw versions:display-plugin-updates -U -ntp +.Phony: show-versions \ No newline at end of file diff --git a/maven/pom.xml b/maven/pom.xml index d8d512bb..cd32ea88 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -7,12 +7,13 @@ cluecumber-maven maven-plugin - 3.7.1 + ${revision} cluecumber-parent com.trivago.rta - 3.7.1 + ${revision} + ../pom.xml Cluecumber Maven @@ -44,9 +45,9 @@ 3.9.6 3.9.6 3.10.1 - 3.9.6 + 3.9.9 1 - 3.10.1 + 3.13.0 3.10.2 1.1.0 diff --git a/pom.xml b/pom.xml index 88089c0c..817c8663 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.trivago.rta cluecumber-parent - 3.7.1 + ${revision} pom Cluecumber Parent @@ -16,11 +16,12 @@ engine - maven core + maven + 3.8.0 11 11 11 @@ -30,14 +31,17 @@ UTF-8 UTF-8 3.11.0 - 3.1.0 - 1.6.13 - 5.11.0-M2 - 5.12.0 - 3.1.0 - 3.2.1 - 3.5.0 - 3.11.0 + 3.2.5 + 1.7.0 + 5.11.0 + 5.13.0 + 3.5.0 + 3.3.1 + 3.10.0 + 3.1.1 + 3.3.1 + 3.1.3 + 1.6.0 2017 @@ -130,8 +134,65 @@ maven-release-plugin ${maven-release-plugin.version} + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + + org.apache.maven.plugins + maven-install-plugin + ${maven-install-plugin.version} + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.5.0 + + + enforce-maven + + enforce + + + + + 3.6.3 + + + + + + + + org.codehaus.mojo + flatten-maven-plugin + ${flatten-maven-plugin.version} + + true + resolveCiFriendliesOnly + + + + flatten + process-resources + + flatten + + + + flatten.clean + clean + + clean + + + + +