diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ScenarioExecutor.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ScenarioExecutor.java index e2ca5222db..ba80a90ac7 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ScenarioExecutor.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ScenarioExecutor.java @@ -62,7 +62,7 @@ public enum State { /** * Measures the stack depth of methods called on the step definition object. - * Only the top-level method calls are used for reporting. + * Only the top-level method calls are used for reporting. */ private final AtomicInteger stackDepth = new AtomicInteger(); @@ -95,15 +95,17 @@ static class StageState { class MethodHandler implements StepMethodHandler { @Override public void handleMethod( Object stageInstance, Method paramMethod, Object[] arguments, InvocationMode mode ) throws Throwable { - if( paramMethod.isSynthetic() ) + if( paramMethod.isSynthetic() ) { return; + } if( paramMethod.isAnnotationPresent( AfterStage.class ) || paramMethod.isAnnotationPresent( BeforeStage.class ) || paramMethod.isAnnotationPresent( BeforeScenario.class ) || paramMethod.isAnnotationPresent( AfterScenario.class ) || - paramMethod.isAnnotationPresent( Hidden.class ) ) + paramMethod.isAnnotationPresent( Hidden.class ) ) { return; + } update( stageInstance ); @@ -116,6 +118,7 @@ public void handleMethod( Object stageInstance, Method paramMethod, Object[] arg @Override public void handleThrowable( Throwable t ) throws Throwable { + listener.stepMethodFailed( t ); failed( t ); } @@ -123,8 +126,9 @@ public void handleThrowable( Throwable t ) throws Throwable { @SuppressWarnings( "unchecked" ) public T addStage( Class stepsClass ) { - if( stages.containsKey( stepsClass ) ) + if( stages.containsKey( stepsClass ) ) { return (T) stages.get( stepsClass ).instance; + } T result = setupCglibProxy( stepsClass ); @@ -186,8 +190,9 @@ private T update( T t ) throws Throwable { private void executeAfterStageMethods( Object stage ) throws Throwable { StageState stageState = getStageState( stage ); - if( stageState.afterStageCalled ) + if( stageState.afterStageCalled ) { return; + } stageState.afterStageCalled = true; executeAnnotatedMethods( stage, AfterStage.class ); } @@ -197,8 +202,9 @@ StageState getStageState( Object stage ) { } private void ensureBeforeStepsAreExecuted() throws Throwable { - if( state != State.INIT ) + if( state != State.INIT ) { return; + } state = State.STARTED; methodInterceptor.enableMethodHandling( false ); @@ -274,10 +280,12 @@ public void wireSteps( CanWire canWire ) { * Has to be called when the scenario is finished in order to execute after methods */ public void finished() throws Throwable { - if( state == FINISHED ) + if( state == FINISHED ) { return; - if( state != STARTED ) + } + if( state != STARTED ) { throw new IllegalStateException( "The Scenario must be in state STARTED in order to finish it, but it is in state " + state ); + } state = FINISHED; methodInterceptor.enableMethodHandling( false ); @@ -350,7 +358,7 @@ public void failed( Throwable e ) { /** * Starts a scenario with the given description. - * + * * @param description the description of the scenario */ public void startScenario( String description ) { diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/NoOpScenarioListener.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/NoOpScenarioListener.java index 79e919f1b9..ed57ae33f4 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/NoOpScenarioListener.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/NoOpScenarioListener.java @@ -21,4 +21,7 @@ public void stepMethodInvoked( Method paramMethod, List arguments, Invoc @Override public void introWordAdded( String word ) {} + + @Override + public void stepMethodFailed( Throwable t ) {} } diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/ScenarioListener.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/ScenarioListener.java index a37bae7ff0..aceb55fa6f 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/ScenarioListener.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/intercept/ScenarioListener.java @@ -17,4 +17,6 @@ public interface ScenarioListener { void introWordAdded( String word ); + void stepMethodFailed( Throwable t ); + } diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/lang/de/Schritte.java b/jgiven-core/src/main/java/com/tngtech/jgiven/lang/de/Schritte.java index 3e01f6219a..9bf5c4d556 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/lang/de/Schritte.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/lang/de/Schritte.java @@ -1,41 +1,7 @@ package com.tngtech.jgiven.lang.de; -import com.tngtech.jgiven.annotation.IntroWord; -import com.tngtech.jgiven.base.StageBase; - /** - * A German version for step definitions + * @deprecated will be removed with version 0.3.0. Use {@link Stufe} instead */ -public class Schritte> extends StageBase { - - @IntroWord - public SELF gegeben() { - return self(); - } - - @IntroWord - public SELF wenn() { - return self(); - } - - @IntroWord - public SELF dann() { - return self(); - } - - @IntroWord - public SELF und() { - return self(); - } - - @IntroWord - public SELF aber() { - return self(); - } - - @IntroWord - public SELF mit() { - return self(); - } - -} +@Deprecated +public class Schritte> extends Stufe {} diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/lang/de/Stufe.java b/jgiven-core/src/main/java/com/tngtech/jgiven/lang/de/Stufe.java new file mode 100644 index 0000000000..6f8ecf3370 --- /dev/null +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/lang/de/Stufe.java @@ -0,0 +1,43 @@ +package com.tngtech.jgiven.lang.de; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.IntroWord; +import com.tngtech.jgiven.base.StageBase; + +/** + * Eine deutsche Version der {@link Stage}-Klasse + * + */ +public class Stufe> extends StageBase { + + @IntroWord + public SELF gegeben() { + return self(); + } + + @IntroWord + public SELF wenn() { + return self(); + } + + @IntroWord + public SELF dann() { + return self(); + } + + @IntroWord + public SELF und() { + return self(); + } + + @IntroWord + public SELF aber() { + return self(); + } + + @IntroWord + public SELF mit() { + return self(); + } + +} diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/ScenarioHtmlWriter.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/ScenarioHtmlWriter.java index edf90af667..43c84d104b 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/ScenarioHtmlWriter.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/html/ScenarioHtmlWriter.java @@ -4,6 +4,7 @@ import java.io.PrintWriter; +import com.google.common.html.HtmlEscapers; import com.tngtech.jgiven.impl.util.WordUtil; import com.tngtech.jgiven.report.model.ReportModelVisitor; import com.tngtech.jgiven.report.model.ScenarioCaseModel; @@ -108,7 +109,7 @@ public void visit( StepModel stepModel ) { if( !firstWord ) { writer.print( ' ' ); } - String text = word.value; + String text = HtmlEscapers.htmlEscaper().escape( word.value ); if( firstWord && word.isIntroWord ) { writer.print( format( "%s", WordUtil.capitalize( text ) ) ); @@ -128,7 +129,7 @@ public void visit( StepModel stepModel ) { } private void printArg( Word word ) { - String value = word.getArgumentInfo().isCaseArg() ? formatCaseArgument( word ) : word.value; + String value = word.getArgumentInfo().isCaseArg() ? formatCaseArgument( word ) : HtmlEscapers.htmlEscaper().escape( word.value ); value = escapeToHtml( value ); String multiLine = value.contains( "
" ) ? "multiline" : ""; String caseClass = word.getArgumentInfo().isCaseArg() ? "caseArgument" : "argument"; @@ -140,6 +141,6 @@ private String escapeToHtml( String value ) { } String formatCaseArgument( Word word ) { - return word.value; + return HtmlEscapers.htmlEscaper().escape( word.value ); } } diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModelBuilder.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModelBuilder.java index 7f5a124b9d..3c514f5e58 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModelBuilder.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModelBuilder.java @@ -160,14 +160,16 @@ private ScenarioCaseModel getCurrentScenarioCase() { @Override public void stepMethodInvoked( Method paramMethod, List arguments, InvocationMode mode ) { - if( !isStepMethod( paramMethod ) ) + if( !isStepMethod( paramMethod ) ) { return; + } addStepMethod( paramMethod, arguments, mode ); } public boolean isStepMethod( Method paramMethod ) { - if( !Modifier.isPublic( paramMethod.getModifiers() ) ) + if( !Modifier.isPublic( paramMethod.getModifiers() ) ) { return false; + } return true; } @@ -205,11 +207,15 @@ public ReportModel getScenarioCollectionModel() { } @Override - public void scenarioFailed( Throwable e ) { + public void stepMethodFailed( Throwable t ) { if( !currentScenarioCase.steps.isEmpty() ) { currentScenarioCase.steps.get( currentScenarioCase.steps.size() - 1 ) .setStatus( StepStatus.FAILED ); } + } + + @Override + public void scenarioFailed( Throwable e ) { setSuccess( false ); setErrorMessage( e.getMessage() ); } @@ -357,4 +363,5 @@ private static List getExplodedTags( Tag originalTag, String[] stringArray } return result; } + } diff --git a/jgiven-junit/src/main/java/com/tngtech/jgiven/junit/ScenarioExecutionRule.java b/jgiven-junit/src/main/java/com/tngtech/jgiven/junit/ScenarioExecutionRule.java index a724a3f846..182095b971 100644 --- a/jgiven-junit/src/main/java/com/tngtech/jgiven/junit/ScenarioExecutionRule.java +++ b/jgiven-junit/src/main/java/com/tngtech/jgiven/junit/ScenarioExecutionRule.java @@ -63,6 +63,7 @@ public void evaluate() throws Throwable { throw e; } catch( Throwable t ) { failed( t ); + throw t; } } }; @@ -126,11 +127,11 @@ private static List getArgumentsFrom( Object object, String fieldName ) } catch( NoSuchFieldException e ) { log.warn( format( "Could not find field containing test method arguments in '%s'. " - + "Probably the internal representation has changed. Consider writing a bug report.", + + "Probably the internal representation has changed. Consider writing a bug report.", methodClass.getSimpleName() ), e ); } catch( IllegalAccessException e ) { log.warn( format( "Not able to access field containing test method arguments in '%s'. " - + "Probably the internal representation has changed. Consider writing a bug report.", + + "Probably the internal representation has changed. Consider writing a bug report.", methodClass.getSimpleName() ), e ); } return Collections.emptyList(); diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarioRepository.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarioRepository.java index 30e60c9d31..99305aef47 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarioRepository.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarioRepository.java @@ -128,6 +128,10 @@ private static List setupTestScenarios() { .numberOfSteps( 2 ) .failingStep( 1 ); + addTestScenario( result, "failing_test_with_three_steps" ) + .numberOfSteps( 3 ) + .failingStep( 1 ); + addTestScenario( result, "failing_test_with_two_steps_and_second_step_fails" ) .numberOfSteps( 2 ) .failingStep( 2 ); diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarios.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarios.java index b82368bd5b..399328ee26 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarios.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarios.java @@ -17,6 +17,14 @@ public void failing_test_with_two_steps() { when().something_happens(); } + @Test + @org.testng.annotations.Test + public void failing_test_with_three_steps() { + given().an_exception_is_thrown(); + when().something_happens(); + then().something_happened(); + } + @Test @org.testng.annotations.Test public void failing_test_with_two_steps_and_second_step_fails() { diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/ThenTestStage.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/ThenTestStage.java index c3712aa6cd..59d61ec05b 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/ThenTestStage.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/ThenTestStage.java @@ -4,4 +4,6 @@ public class ThenTestStage extends Stage { + public void something_happened() {} + } \ No newline at end of file diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutionTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutionTest.java index ebfff16174..a8a95d0d90 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutionTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutionTest.java @@ -66,11 +66,12 @@ public void failing_tests_annotated_with_NotImplementedYet_with_executeSteps_set @Test public void steps_following_failing_steps_are_reported_as_skipped() { - given().a_failing_test_with_$_steps( 2 ) + given().a_failing_test_with_$_steps( 3 ) .and().step_$_fails( 1 ); when().the_test_is_executed_with_JUnit(); then().step_$_is_reported_as_failed( 1 ) - .and().step_$_is_reported_as_skipped( 2 ); + .and().step_$_is_reported_as_skipped( 2 ) + .and().step_$_is_reported_as_skipped( 3 ); } @Test diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/de/DeSzenarioTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/de/DeSzenarioTest.java index d7cc8afa6b..904417e6ce 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/de/DeSzenarioTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/de/DeSzenarioTest.java @@ -2,12 +2,13 @@ import org.junit.Test; -import com.tngtech.jgiven.junit.de.DeSzenarioTest.DeutscheTestSchritte; +import com.tngtech.jgiven.junit.de.DeSzenarioTest.DeutscheTestStufe; import com.tngtech.jgiven.lang.de.Schritte; +import com.tngtech.jgiven.lang.de.Stufe; import com.tngtech.jgiven.tags.FeatureGerman; @FeatureGerman -public class DeSzenarioTest extends SzenarioTest { +public class DeSzenarioTest extends SzenarioTest { @Test public void Szenarien_können_in_deutsch_geschrieben_werden() { @@ -17,21 +18,21 @@ public class DeSzenarioTest extends SzenarioTest { + static class DeutscheTestStufe extends Stufe { - public DeutscheTestSchritte ein_deutsches_Projekt() { + public DeutscheTestStufe ein_deutsches_Projekt() { return self(); } - public DeutscheTestSchritte generiert_JGiven_deutsche_Berichte() { + public DeutscheTestStufe generiert_JGiven_deutsche_Berichte() { return self(); } - public DeutscheTestSchritte die_Szenarien_in_deutsch_geschrieben_werden() { + public DeutscheTestStufe die_Szenarien_in_deutsch_geschrieben_werden() { return self(); } - public DeutscheTestSchritte JGiven_verwendet_wird() { + public DeutscheTestStufe JGiven_verwendet_wird() { return self(); } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html/HtmlWriterScenarioTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html/HtmlWriterScenarioTest.java index 2422d23583..d28e9f7eb6 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html/HtmlWriterScenarioTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html/HtmlWriterScenarioTest.java @@ -11,6 +11,7 @@ import com.tngtech.jgiven.report.model.StepStatus; import com.tngtech.jgiven.tags.FeatureDataTables; import com.tngtech.jgiven.tags.FeatureHtmlReport; +import com.tngtech.jgiven.tags.Issue; @FeatureHtmlReport @RunWith( DataProviderRunner.class ) @@ -36,6 +37,16 @@ public void step_status_appears_correctly_in_HTML_reports( StepStatus status, St then().the_HTML_report_contains_text( expectedString ); } + @Test + @FeatureDataTables + @Issue( "#9" ) + public void HTML_in_arguments_is_escaped_in_HTML_reports() { + given().a_report_model_with_one_scenario() + .and().case_$_has_a_when_step_$_with_argument( 1, "test", "" ); + when().the_HTML_report_is_generated(); + then().the_HTML_report_contains_text( "<someHtmlTag>" ); + } + @Test @FeatureDataTables public void data_tables_are_generated_in_HTML_reports() { 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 bb7f5c4ee3..5977b5e6ee 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 @@ -49,9 +49,9 @@ private void addCase( ScenarioModel scenarioModel ) { } scenarioCaseModel.addStep( "something_happens", Arrays.asList( Word.introWord( "given" ), new Word( "something" ) ), InvocationMode.NORMAL ); - if( !scenarioCaseModel.arguments.isEmpty() ) { + for( String arg : scenarioCaseModel.arguments ) { scenarioCaseModel.addStep( "something_happens", asList( Word.introWord( "when" ), - Word.argWord( scenarioCaseModel.arguments.get( 0 ) ) ), InvocationMode.NORMAL ); + Word.argWord( arg ) ), InvocationMode.NORMAL ); } } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/text/PlainTextScenarioWriterTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/text/PlainTextScenarioWriterTest.java index e2564917aa..77b72fda38 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/text/PlainTextScenarioWriterTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/text/PlainTextScenarioWriterTest.java @@ -68,11 +68,13 @@ public void data_tables_are_generated_in_text_reports() throws UnsupportedEncodi when().the_plain_text_report_is_generated(); - then().the_report_contains_text( "\n" + - " | param1 | param2 |\n" + - " +--------+--------+\n" + - " | arg10 | arg11 |\n" + - " | arg20 | arg21 |\n" + - " | arg30 | arg31 |\n" ); + then().the_report_contains_text( "" ) + .and().the_report_contains_text( "" ) + .and().the_report_contains_text( "\n" + + " | param1 | param2 |\n" + + " +--------+--------+\n" + + " | arg10 | arg11 |\n" + + " | arg20 | arg21 |\n" + + " | arg30 | arg31 |\n" ); } }