diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/HtmlTocWriter.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/HtmlTocWriter.java index 76680e9b80..751520e55d 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/HtmlTocWriter.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/HtmlTocWriter.java @@ -16,6 +16,7 @@ import com.google.common.collect.Ordering; import com.tngtech.jgiven.report.html.PackageTocBuilder.PackageToc; import com.tngtech.jgiven.report.html.StaticHtmlReportGenerator.ModelFile; +import com.tngtech.jgiven.report.model.ReportStatistics; import com.tngtech.jgiven.report.model.ScenarioModel; import com.tngtech.jgiven.report.model.Tag; @@ -24,10 +25,12 @@ public class HtmlTocWriter { private final Map> tagMap; private final PackageToc packageToc; private final ImmutableListMultimap groupedTags; + private final ReportStatistics totalStatistics; - public HtmlTocWriter( Map> tagMap, PackageToc packageToc ) { + public HtmlTocWriter( Map> tagMap, PackageToc packageToc, ReportStatistics totalStatistics ) { this.tagMap = tagMap; this.packageToc = packageToc; + this.totalStatistics = totalStatistics; groupedTags = getGroupedTags(); } @@ -48,11 +51,19 @@ public void writeToc( PrintWriter writer ) { private void writeSummary() { writer.println( "

Summary

" ); writer.println( "
    " ); - writer.println( "
  • All Scenarios" ); - writer.println( "
  • Failed Scenarios" ); + writeLink( totalStatistics.numScenarios, "all.html", "All Scenarios" ); + writeLink( totalStatistics.numPendingScenarios, "pending.html", "Pending Scenarios" ); + writeLink( totalStatistics.numFailedScenarios, "failed.html", "Failed Scenarios" ); writer.println( "
" ); } + private void writeLink( int count, String fileName, String title ) { + if( count > 0 ) { + writer.print( "
  • " + title ); + writer.println( " " + count + "" ); + } + } + private void writeSearchInput() { writer.println( "" ); diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/ReportModelHtmlWriter.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/ReportModelHtmlWriter.java index b571df05c3..1d708d53f1 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/ReportModelHtmlWriter.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/ReportModelHtmlWriter.java @@ -164,9 +164,7 @@ void writeHeader( ReportModel reportModel ) { className = Files.getFileExtension( reportModel.getClassName() ); } - if( !Strings.isNullOrEmpty( packageName ) ) { - writer.println( format( "
    %s
    ", packageName ) ); - } + writer.println( format( "
    %s
    ", packageName ) ); writer.println( format( "

    %s

    ", className ) ); writer.println( " " ); diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/StaticHtmlReportGenerator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/StaticHtmlReportGenerator.java index 84116eee1a..61929a0a05 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/StaticHtmlReportGenerator.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/StaticHtmlReportGenerator.java @@ -31,6 +31,7 @@ public class StaticHtmlReportGenerator implements ReportModelFileHandler { static class ModelFile { ReportModel model; File file; + ReportStatistics statistics; } private final List models = Lists.newArrayList(); @@ -93,28 +94,40 @@ public void handleReportModel( ReportModel model, File file ) { } public void writeEnd() { + ReportStatistics totalStatistics = calulcateStatistics(); + PackageToc packageToc = new PackageTocBuilder( models ).getRootPackageToc(); - HtmlTocWriter tocWriter = new HtmlTocWriter( tagMap, packageToc ); - ReportStatistics totalStatistics = new ReportStatistics(); + HtmlTocWriter tocWriter = new HtmlTocWriter( tagMap, packageToc, totalStatistics ); List failedScenarios = Lists.newArrayList(); + List pendingScenarios = Lists.newArrayList(); List allScenarios = Lists.newArrayList(); for( ModelFile modelFile : models ) { ReportModelHtmlWriter modelWriter = ReportModelHtmlWriter.writeModelToFile( modelFile.model, tocWriter, modelFile.file ); - totalStatistics = totalStatistics.add( modelWriter.getStatistics() ); failedScenarios.addAll( modelFile.model.getFailedScenarios() ); + pendingScenarios.addAll( modelFile.model.getPendingScenarios() ); allScenarios.addAll( modelFile.model.getScenarios() ); } writeTagFiles( tocWriter ); - writeScenarios( tocWriter, failedScenarios, ".Failed Scenarios", "failed.html" ); - writeScenarios( tocWriter, allScenarios, ".All Scenarios", "all.html" ); + writeScenarios( tocWriter, failedScenarios, "Failed Scenarios", "failed.html" ); + writeScenarios( tocWriter, pendingScenarios, "Pending Scenarios", "pending.html" ); + writeScenarios( tocWriter, allScenarios, "All Scenarios", "all.html" ); StatisticsPageHtmlWriter statisticsPageHtmlWriter = new StatisticsPageHtmlWriter( tocWriter, totalStatistics ); statisticsPageHtmlWriter.write( toDir ); } + private ReportStatistics calulcateStatistics() { + ReportStatistics totalStatistics = new ReportStatistics(); + for( ModelFile modelFile : models ) { + modelFile.statistics = new StatisticsCalculator().getStatistics( modelFile.model ); + totalStatistics = totalStatistics.add( modelFile.statistics ); + } + return totalStatistics; + } + private void writeScenarios( HtmlTocWriter tocWriter, List failedScenarios, String name, String fileName ) { ReportModel reportModel = new ReportModel(); reportModel.setScenarios( failedScenarios ); diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/StatisticsPageHtmlWriter.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/StatisticsPageHtmlWriter.java index a5ef6d54f7..f8f8dff49a 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/StatisticsPageHtmlWriter.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/StatisticsPageHtmlWriter.java @@ -52,15 +52,16 @@ private void writeStatistics() { printWriter.write( "
    " ); writeStatisticNumber( statistics.numClasses, "classes" ); - printWriter.write( "" ); + writeStatisticsNumberWithLink( statistics.numScenarios, "scenarios", "all.html" ); writeStatisticNumber( statistics.numCases, "cases" ); writeStatisticNumber( statistics.numSteps, "steps" ); - String failedClass = statistics.numFailedCases > 0 ? "failed" : ""; + writeStatisticsNumberWithLink( statistics.numPendingScenarios, "pending scenarios", "pending.html" ); + + String failedClass = statistics.numFailedScenarios > 0 ? "failed" : ""; printWriter.write( "" ); + + statistics.numFailedScenarios + "
    failed scenarios
    " ); writeStatisticNumber( DurationFormatter.format( statistics.durationInNanos ), "total time" ); long averageNanos = statistics.numCases != 0 ? statistics.durationInNanos / statistics.numCases : 0; @@ -68,6 +69,11 @@ private void writeStatistics() { printWriter.println( "" ); } + private void writeStatisticsNumberWithLink( int number, String title, String fileName ) { + printWriter.write( "" ); + } + private void writeStatisticNumber( int number, String name ) { writeStatisticNumber( number + "", name ); } diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ExecutionStatusCalculator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ExecutionStatusCalculator.java index 0ef1c7a5d8..9830503326 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ExecutionStatusCalculator.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ExecutionStatusCalculator.java @@ -13,6 +13,13 @@ public void visit( ScenarioModel scenarioModel ) { } } + @Override + public void visit( ScenarioCaseModel scenarioCase ) { + if( !scenarioCase.success ) { + status = ExecutionStatus.FAILED; + } + } + @Override public void visit( StepModel stepModel ) { if( stepModel.isFailed() ) { diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModel.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModel.java index 419e6861d5..52246fd0c5 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModel.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModel.java @@ -2,6 +2,7 @@ import java.util.Collections; import java.util.Comparator; +import java.util.EnumSet; import java.util.List; import com.google.common.base.Optional; @@ -101,13 +102,22 @@ public String getPackageName() { } public List getFailedScenarios() { + return getScenariosWithStatus( ExecutionStatus.FAILED ); + } + + public List getPendingScenarios() { + return getScenariosWithStatus( ExecutionStatus.NONE_IMPLEMENTED, ExecutionStatus.PARTIALLY_IMPLEMENTED ); + } + + public List getScenariosWithStatus( ExecutionStatus first, ExecutionStatus... rest ) { + EnumSet stati = EnumSet.of( first, rest ); List result = Lists.newArrayList(); for( ScenarioModel m : scenarios ) { - if( m.getExecutionStatus() == ExecutionStatus.FAILED ) { + ExecutionStatus executionStatus = m.getExecutionStatus(); + if( stati.contains( executionStatus ) ) { result.add( m ); } } return result; } - } diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportStatistics.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportStatistics.java index 194c99c6d6..896ff462df 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportStatistics.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportStatistics.java @@ -3,10 +3,13 @@ public class ReportStatistics { public int numClasses; public int numScenarios; + public int numFailedScenarios; public int numCases; public int numFailedCases; public int numSteps; public long durationInNanos; + public int numPendingScenarios; + public int numSuccessfulScenarios; public ReportStatistics add( ReportStatistics statistics ) { ReportStatistics copy = new ReportStatistics(); @@ -18,6 +21,9 @@ public ReportStatistics add( ReportStatistics statistics ) { private void addModifying( ReportStatistics statistics ) { this.numClasses += statistics.numClasses; this.numScenarios += statistics.numScenarios; + this.numFailedScenarios += statistics.numFailedScenarios; + this.numPendingScenarios += statistics.numPendingScenarios; + this.numSuccessfulScenarios += statistics.numSuccessfulScenarios; this.numCases += statistics.numCases; this.numFailedCases += statistics.numFailedCases; this.numSteps += statistics.numSteps; diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ScenarioModel.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ScenarioModel.java index 36c6a9782c..4ca5d2cf02 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ScenarioModel.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ScenarioModel.java @@ -19,6 +19,7 @@ public class ScenarioModel { private boolean casesAsTable; private final List scenarioCases = Lists.newArrayList(); private long durationInNanos; + private ExecutionStatus executionStatus; public void accept( ReportModelVisitor visitor ) { visitor.visit( this ); @@ -34,9 +35,12 @@ public void addCase( ScenarioCaseModel scenarioCase ) { } public ExecutionStatus getExecutionStatus() { - ExecutionStatusCalculator executionStatusCalculator = new ExecutionStatusCalculator(); - this.accept( executionStatusCalculator ); - return executionStatusCalculator.executionStatus(); + if( executionStatus == null ) { + ExecutionStatusCalculator executionStatusCalculator = new ExecutionStatusCalculator(); + this.accept( executionStatusCalculator ); + executionStatus = executionStatusCalculator.executionStatus(); + } + return executionStatus; } public ScenarioCaseModel getCase( int i ) { diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/StatisticsCalculator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/StatisticsCalculator.java index e071bcce1a..e1427639b4 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/StatisticsCalculator.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/StatisticsCalculator.java @@ -34,6 +34,14 @@ public void visit( ReportModel reportModel ) { public void visit( ScenarioModel scenarioModel ) { statistics.numScenarios++; statistics.durationInNanos += scenarioModel.getDurationInNanos(); + ExecutionStatus executionStatus = scenarioModel.getExecutionStatus(); + if( executionStatus == ExecutionStatus.FAILED ) { + statistics.numFailedScenarios += 1; + } else if( executionStatus == ExecutionStatus.NONE_IMPLEMENTED || executionStatus == ExecutionStatus.PARTIALLY_IMPLEMENTED ) { + statistics.numPendingScenarios += 1; + } else { + statistics.numSuccessfulScenarios += 1; + } } @Override diff --git a/jgiven-examples/src/test/java/com/tngtech/jgiven/examples/notimplementedyet/NotImplementYetExampleTest.java b/jgiven-examples/src/test/java/com/tngtech/jgiven/examples/notimplementedyet/NotImplementYetExampleTest.java index ec64a9e756..eb6c54c0e2 100644 --- a/jgiven-examples/src/test/java/com/tngtech/jgiven/examples/notimplementedyet/NotImplementYetExampleTest.java +++ b/jgiven-examples/src/test/java/com/tngtech/jgiven/examples/notimplementedyet/NotImplementYetExampleTest.java @@ -1,5 +1,6 @@ package com.tngtech.jgiven.examples.notimplementedyet; +import org.assertj.core.api.Assertions; import org.junit.Test; import com.tngtech.jgiven.annotation.Description; @@ -14,20 +15,37 @@ public class NotImplementYetExampleTest extends SimpleScenarioTest> extends Stage { @@ -21,4 +28,24 @@ public class ThenReportGenerator> extends St assertThat( new File( targetReportDir, name ) ).exists(); return self(); } + + public SELF file_$_contains_scenario_$( String fileName, int scenarioNr ) throws IOException { + final ScenarioModel scenarioModel = reportModels.get( 0 ).getScenarios().get( 0 ); + final String regex = ""; + return file_$_contains( fileName, regex ); + } + + public SELF file_$_contains( String fileName, final String regex ) throws IOException { + ImmutableList content = Files.asCharSource( new File( targetReportDir, fileName ), Charset.forName( "utf8" ) ).readLines(); + + boolean match = FluentIterable.from( content ).anyMatch( new Predicate() { + @Override + public boolean apply( String input ) { + return input.matches( regex ); + } + } ); + + assertThat( match ).as( "file " + fileName + " does not contain " + regex ).isTrue(); + return self(); + } } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html/StaticHtmlReportGeneratorTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html/StaticHtmlReportGeneratorTest.java index d625452072..92d0a5c9ca 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html/StaticHtmlReportGeneratorTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html/StaticHtmlReportGeneratorTest.java @@ -10,6 +10,7 @@ import com.tngtech.jgiven.JGivenScenarioTest; import com.tngtech.jgiven.report.WhenReportGenerator; import com.tngtech.jgiven.report.json.GivenJsonReports; +import com.tngtech.jgiven.report.model.StepStatus; import com.tngtech.jgiven.tags.FeatureHtmlReport; import com.tngtech.jgiven.tags.FeatureTags; import com.tngtech.jgiven.tags.Issue; @@ -68,4 +69,33 @@ public void the_static_HTML_report_generates_one_file_for_all_failed_scenarios() when().the_static_HTML_reporter_is_executed(); then().a_file_with_name_$_exists( "failed.html" ); } + + @Test + @Issue( "#33" ) + public void the_failed_file_generated_by_the_static_HTML_report_generator_contains_scenarios_where_some_cases_are_failed() + throws IOException { + given().a_report_model() + .and().the_report_has_$_scenarios( 1 ) + .and().the_scenario_has_$_default_cases( 2 ) + .and().case_$_of_scenario_$_has_failed( 1, 1 ) + .and().step_$_of_case_$_has_status( 1, 1, StepStatus.FAILED ) + .and().the_report_exist_as_JSON_file(); + when().the_static_HTML_reporter_is_executed(); + then().a_file_with_name_$_exists( "failed.html" ) + .and().file_$_contains_scenario_$( "failed.html", 1 ); + } + + @Test + @Issue( "#33" ) + public void the_failed_file_generated_by_the_static_HTML_report_generator_contains_failed_scenarios_where_all_steps_are_successful() + throws IOException { + given().a_report_model() + .and().the_report_has_$_scenarios( 1 ) + .and().case_$_of_scenario_$_has_failed( 1, 1 ) + .and().the_report_exist_as_JSON_file(); + when().the_static_HTML_reporter_is_executed(); + then().a_file_with_name_$_exists( "failed.html" ) + .and().file_$_contains_scenario_$( "failed.html", 1 ); + } + } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/model/GivenReportModel.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/model/GivenReportModel.java index 6280506df1..30a62094c5 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/model/GivenReportModel.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/model/GivenReportModel.java @@ -161,11 +161,15 @@ private ScenarioCaseModel getCase( int ncase ) { return self(); } - public SELF step_$_has_status( int i, StepStatus status ) { - getCase( 1 ).getStep( i - 1 ).setStatus( status ); + public SELF step_$_of_case_$_has_status( int stepNr, int caseNr, StepStatus status ) { + getCase( caseNr ).getStep( stepNr - 1 ).setStatus( status ); return self(); } + public SELF step_$_has_status( int stepNr, StepStatus status ) { + return step_$_of_case_$_has_status( stepNr, 1, status ); + } + public SELF step_$_has_a_duration_of_$_nano_seconds( int i, long durationInNanos ) { getCase( 1 ).getStep( i - 1 ).setDurationInNanos( durationInNanos ); return self(); diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/model/GivenReportModels.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/model/GivenReportModels.java index 2a33b803da..29f4774e2e 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/model/GivenReportModels.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/model/GivenReportModels.java @@ -51,4 +51,14 @@ public SELF the_first_scenario_has_tag( String name ) { givenReportModel.case_$_of_scenario_$_has_failed( caseNr, scenarioNr ); return self(); } + + public SELF the_scenario_has_$_default_cases( int ncases ) { + givenReportModel.the_scenario_has_$_default_cases( ncases ); + return self(); + } + + public SELF step_$_of_case_$_has_status( int stepNr, int caseNr, StepStatus status ) { + givenReportModel.step_$_of_case_$_has_status( stepNr, caseNr, status ); + return self(); + } }