diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/StepAccess.java b/jgiven-core/src/main/java/com/tngtech/jgiven/CurrentStep.java similarity index 67% rename from jgiven-core/src/main/java/com/tngtech/jgiven/StepAccess.java rename to jgiven-core/src/main/java/com/tngtech/jgiven/CurrentStep.java index e20f4a12b7..b3bf09d4a2 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/StepAccess.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/CurrentStep.java @@ -1,13 +1,12 @@ package com.tngtech.jgiven; - import com.tngtech.jgiven.attachment.Attachment; /** - * This interface can be injected into a stage by using the {@link com.tngtech.jgiven.annotation.ExpectedScenarioState} + * This interface can be injected into a stage by using the {@link com.tngtech.jgiven.annotation.ScenarioState} * annotation. It provides programmatic access to the current executed step. */ -public interface StepAccess { +public interface CurrentStep { /** * Adds an attachment to the current step @@ -16,14 +15,6 @@ public interface StepAccess { */ void addAttachment( Attachment attachment ); - /** - * Set the description of the current step - * - * @param description the description to set - * @see com.tngtech.jgiven.annotation.Description - */ - void setDescription( String description ); - /** * Set the extended description of the current step * diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/Attachment.java b/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/Attachment.java index 317407ae34..39c1dd656a 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/Attachment.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/Attachment.java @@ -1,10 +1,182 @@ package com.tngtech.jgiven.attachment; +import java.io.*; +import java.nio.charset.Charset; + +import javax.xml.bind.DatatypeConverter; + +import com.google.common.io.ByteStreams; +import com.google.common.io.CharStreams; +import com.google.common.io.Files; +import com.tngtech.jgiven.impl.util.ResourceUtil; + /** - * Represents an attachment of a step + * Represents an attachment of a step. + * Attachments must be representable as a String so that it can be stored as JSON. + * For binary attachments this means that they have to be encoded with Base64. + * In addition, attachments must have a media type so that reporters know + * how to present an attachment. + * */ -public interface Attachment { - String asString(); +public class Attachment { + + /** + * An optional title. + * Can be {@code null}. + */ + private String title; + + /** + * The content of the attachment. + * In case the media type is binary, this is a Base64 encoded string + */ + private final String content; + + private final MediaType mediaType; + + /** + * Convenience constructor, where title is set to {@code null} + */ + protected Attachment( String content, MediaType mediaType ) { + this( content, mediaType, null ); + } + + /** + * Creates a new instance of this Attachment + * @param content the content of this attachment. In case of a binary attachment, this must be + * Base64 encoded. Must not be {@code null} + * @param mediaType the mediaType. Must not be {@code null} + * @param title an optional title, may be {@code null} + */ + protected Attachment( String content, MediaType mediaType, String title ) { + if( mediaType == null ) { + throw new IllegalArgumentException( "MediaType must not be null" ); + } + + if( content == null ) { + throw new IllegalArgumentException( "Content must not be null" ); + } + + this.content = content; + this.mediaType = mediaType; + this.title = title; + } + + /** + * An optional title of the attachment. + * The title can be used by reporters, e.g. as a tooltip. + * Can be {@code null}. + */ + public String title() { + return title; + } + + /** + * The content of the attachment represented as a string. + * Binary attachments must be encoded in Base64 format. + */ + public String content() { + return content; + } + + /** + * The type of the attachment. + * It depends on the reporter how this information is used. + */ + public MediaType getMediaType() { + return mediaType; + } + + /** + * An optional title + */ + public String getTitle() { + return title; + } + + /** + * Sets the title and returns {@code this} + */ + public Attachment withTitle( String title ) { + this.title = title; + return this; + } + + /** + * Creates an attachment from a given array of bytes. + * The bytes will be Base64 encoded. + * @throws java.lang.IllegalArgumentException if mediaType is not binary + */ + public static Attachment fromBinaryBytes( byte[] bytes, MediaType mediaType ) { + if( !mediaType.isBinary() ) { + throw new IllegalArgumentException( "MediaType must be binary" ); + } + return new Attachment( DatatypeConverter.printBase64Binary( bytes ), mediaType, null ); + } + + /** + * Creates an attachment from a binary input stream. + * The content of the stream will be transformed into a Base64 encoded string + * @throws IOException if an I/O error occurs + * @throws java.lang.IllegalArgumentException if mediaType is not binary + */ + public static Attachment fromBinaryInputStream( InputStream inputStream, MediaType mediaType ) throws IOException { + return fromBinaryBytes( ByteStreams.toByteArray( inputStream ), mediaType ); + } + + /** + * Creates an attachment from the given binary file {@code file}. + * The content of the file will be transformed into a Base64 encoded string. + * @throws IOException if an I/O error occurs + * @throws java.lang.IllegalArgumentException if mediaType is not binary + */ + public static Attachment fromBinaryFile( File file, MediaType mediaType ) throws IOException { + FileInputStream stream = new FileInputStream( file ); + try { + return fromBinaryInputStream( stream, mediaType ); + } finally { + ResourceUtil.close( stream ); + } + } + + /** + * Creates a non-binary attachment from the given file. + * @throws IOException if an I/O error occurs + * @throws java.lang.IllegalArgumentException if mediaType is binary + */ + public static Attachment fromTextFile( File file, MediaType mediaType, Charset charSet ) throws IOException { + return fromText( Files.toString( file, charSet ), mediaType ); + } + + /** + * Creates a non-binary attachment from the given file. + * @throws IOException if an I/O error occurs + * @throws java.lang.IllegalArgumentException if mediaType is binary + */ + public static Attachment fromTextInputStream( InputStream inputStream, MediaType mediaType, Charset charset ) throws IOException { + return fromText( CharStreams.toString( new InputStreamReader( inputStream, charset ) ), mediaType ); + } + + /** + * Equivalent to {@link com.tngtech.jgiven.attachment.Attachment#Attachment(String, MediaType)} + * @throws java.lang.IllegalArgumentException if mediaType is binary + */ + public static Attachment fromText( String content, MediaType mediaType ) { + if( mediaType.isBinary() ) { + throw new IllegalArgumentException( "MediaType must not be binary" ); + } + return new Attachment( content, mediaType ); + } + + /** + * Equivalent to {@link com.tngtech.jgiven.attachment.Attachment#Attachment(String, MediaType)} + * @throws java.lang.IllegalArgumentException if mediaType is not binary + */ + public static Attachment fromBase64( String base64encodedContent, MediaType mediaType ) { + if( !mediaType.isBinary() ) { + throw new IllegalArgumentException( "MediaType must be binary" ); + } + return new Attachment( base64encodedContent, mediaType ); + } - String getMimeType(); } diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/BinaryAttachment.java b/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/BinaryAttachment.java deleted file mode 100644 index 422709eb3a..0000000000 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/BinaryAttachment.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.tngtech.jgiven.attachment; - -import javax.xml.bind.DatatypeConverter; - -/** - * Represents an binary attachment - */ -public class BinaryAttachment implements Attachment { - - private final String base64; - private final String mimeType; - - private BinaryAttachment( String base64, String mimeType ) { - this.base64 = base64; - this.mimeType = mimeType; - } - - public static BinaryAttachment fromBase64PngImage( String base64 ) { - return fromBase64( base64, "image/png" ); - } - - public static BinaryAttachment fromBase64( String base64, String mimeType ) { - return new BinaryAttachment( base64, mimeType ); - } - - public static BinaryAttachment fromBytes( byte[] bytes, String mimeType ) { - return new BinaryAttachment( DatatypeConverter.printBase64Binary( bytes ), mimeType ); - } - - @Override - public String asString() { - return base64; - } - - @Override - public String getMimeType() { - return mimeType; - } -} diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/MediaType.java b/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/MediaType.java new file mode 100644 index 0000000000..b8436d397d --- /dev/null +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/MediaType.java @@ -0,0 +1,118 @@ +package com.tngtech.jgiven.attachment; + +import static com.tngtech.jgiven.attachment.MediaType.Type.*; + +/** + * Represents a Media Type. + */ +public class MediaType { + + /** + * Represents the type of a Media Type + */ + public static enum Type { + APPLICATION( "application" ), + AUDIO( "audio" ), + IMAGE( "image" ), + TEXT( "text" ), + VIDEO( "video" ); + + private final String value; + + Type( String value ) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + + /** + * Get the type from a given string + */ + public Type fromString( String string ) { + for( Type type : values() ) { + if( type.value.equalsIgnoreCase( string ) ) { + return type; + } + } + throw new IllegalArgumentException( "Unknown type " + string ); + } + + } + + public static final MediaType PNG = image( "png" ); + public static final MediaType PLAIN_TEXT = text( "plain" ); + + private final Type type; + private final String subType; + private final boolean binary; + + /** + * Creates a new MediaType + * @param type the type + * @param subType the subtype + * @param binary whether or not content of this media type is binary. If {@code true}, the + * content will be encoded as Base64 when stored in the JSON model. + */ + public MediaType( Type type, String subType, boolean binary ) { + this.type = type; + this.subType = subType; + this.binary = binary; + } + + /** + * The type of the Media Type. + */ + public Type getType() { + return type; + } + + /** + * The subtype of the Media Type. + */ + public String getSubType() { + return subType; + } + + /** + * Whether this media type is binary or not. + */ + public boolean isBinary() { + return binary; + } + + public String asString() { + return type.value + "/" + subType; + } + + /** + * Creates a binary image media type with the given subtype. + */ + public static MediaType image( String subType ) { + return new MediaType( IMAGE, subType, true ); + } + + /** + * Creates a binary video media type with the given subtype. + */ + public static MediaType video( String subType ) { + return new MediaType( VIDEO, subType, true ); + } + + /** + * Creates a binary audio media type with the given subtype. + */ + public static MediaType audio( String subType ) { + return new MediaType( AUDIO, subType, true ); + } + + /** + * Creates a non-binary text media type with the given subtype. + */ + public static MediaType text( String subType ) { + return new MediaType( TEXT, subType, false ); + } + +} diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/TextAttachment.java b/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/TextAttachment.java deleted file mode 100644 index f334182f5c..0000000000 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/attachment/TextAttachment.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.tngtech.jgiven.attachment; - -/** - * Represents a text attachment - */ -public class TextAttachment implements Attachment { - - private final String content; - - public TextAttachment( String content ) { - this.content = content; - } - - @Override - public String asString() { - return content; - } - - @Override - public String getMimeType() { - return "text/plain"; - } -} 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 eea769abc7..356daf59eb 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 @@ -20,7 +20,7 @@ import com.google.common.base.Optional; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.tngtech.jgiven.StepAccess; +import com.tngtech.jgiven.CurrentStep; import com.tngtech.jgiven.annotation.*; import com.tngtech.jgiven.attachment.Attachment; import com.tngtech.jgiven.exception.FailIfPassedException; @@ -78,7 +78,7 @@ public enum State { public ScenarioExecutor() { injector.injectValueByType( ScenarioExecutor.class, this ); - injector.injectValueByType( StepAccess.class, new StepAccessImpl() ); + injector.injectValueByType( CurrentStep.class, new StepAccessImpl() ); } static class StageState { @@ -91,21 +91,16 @@ static class StageState { } } - class StepAccessImpl implements StepAccess { + class StepAccessImpl implements CurrentStep { @Override public void addAttachment( Attachment attachment ) { listener.attachmentAdded( attachment ); } - @Override - public void setDescription( String description ) { - // listener.setDescription(description); - } - @Override public void setExtendedDescription( String extendedDescription ) { - // listener.setExtendedDescription(extendedDescription); + listener.extendedDescriptionUpdated( extendedDescription ); } } 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 bb626674c1..b29434680f 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 @@ -34,4 +34,7 @@ public void scenarioFinished() {} @Override public void attachmentAdded( Attachment attachment ) {} + + @Override + public void extendedDescriptionUpdated( String extendedDescription ) {} } 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 0418f078e0..b86bd8328e 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 @@ -25,4 +25,6 @@ public interface ScenarioListener { void scenarioFinished(); void attachmentAdded( Attachment attachment ); + + void extendedDescriptionUpdated( String extendedDescription ); } diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/AttachmentModel.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/AttachmentModel.java index ac6c1d7cb2..6392f81b64 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/AttachmentModel.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/AttachmentModel.java @@ -1,8 +1,10 @@ package com.tngtech.jgiven.report.model; public class AttachmentModel { + private String title; private String value; - private String mimeType; + private String mediaType; + private boolean binary; public String getValue() { return value; @@ -12,11 +14,27 @@ public void setValue( String value ) { this.value = value; } - public String getMimeType() { - return mimeType; + public String getMediaType() { + return mediaType; } - public void setMimeType( String mimeType ) { - this.mimeType = mimeType; + public void setMediaType( String mimeType ) { + this.mediaType = mimeType; + } + + public void setTitle( String title ) { + this.title = title; + } + + public String getTitle() { + return title; + } + + public void setIsBinary( boolean isBinary ) { + this.binary = isBinary; + } + + public boolean isBinary() { + return binary; } } 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 08599405cd..21e08431ce 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 @@ -413,6 +413,11 @@ public void attachmentAdded( Attachment attachment ) { currentStep.setAttachment( attachment ); } + @Override + public void extendedDescriptionUpdated( String extendedDescription ) { + currentStep.setExtendedDescription( extendedDescription ); + } + public void setTestClass( Class testClass ) { setClassName( testClass.getName() ); if( testClass.isAnnotationPresent( Description.class ) ) { diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/StepModel.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/StepModel.java index aef5c66108..c184f77f6d 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/StepModel.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/StepModel.java @@ -114,8 +114,10 @@ public Word getLastWord() { public void setAttachment( Attachment attachment ) { this.attachment = new AttachmentModel(); - this.attachment.setValue( attachment.asString() ); - this.attachment.setMimeType( attachment.getMimeType() ); + this.attachment.setTitle(attachment.getTitle()); + this.attachment.setValue( attachment.content() ); + this.attachment.setMediaType(attachment.getMediaType().asString()); + this.attachment.setIsBinary( attachment.getMediaType().isBinary()); } public AttachmentModel getAttachment() { diff --git a/jgiven-html5-report/src/app/index.html b/jgiven-html5-report/src/app/index.html index 231a4c2ae6..d35142bab5 100644 --- a/jgiven-html5-report/src/app/index.html +++ b/jgiven-html5-report/src/app/index.html @@ -259,7 +259,12 @@
- + + + diff --git a/jgiven-html5-report/src/main/java/com/tngtech/jgiven/report/html5/Html5AttachmentGenerator.java b/jgiven-html5-report/src/main/java/com/tngtech/jgiven/report/html5/Html5AttachmentGenerator.java index 2bb0d8ce8b..800bff0add 100644 --- a/jgiven-html5-report/src/main/java/com/tngtech/jgiven/report/html5/Html5AttachmentGenerator.java +++ b/jgiven-html5-report/src/main/java/com/tngtech/jgiven/report/html5/Html5AttachmentGenerator.java @@ -39,7 +39,7 @@ public void visit( StepModel stepModel ) { return; } - String mimeType = attachment.getMimeType(); + String mimeType = attachment.getMediaType(); MediaType mediaType = MediaType.parse( mimeType ); File targetFile = null; if( mediaType.is( MediaType.ANY_TEXT_TYPE ) ) { diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureAttachments.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureAttachments.java new file mode 100644 index 0000000000..c39aaec6e7 --- /dev/null +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureAttachments.java @@ -0,0 +1,13 @@ +package com.tngtech.jgiven.tags; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import com.tngtech.jgiven.annotation.IsTag; + +@IsTag( type = "Feature", value = "Attachments", + description = "In order to get additional information about a step, like screenshots, for example
" + + "As a JGiven user,
" + + "I want that steps can have attachments" ) +@Retention( RetentionPolicy.RUNTIME ) +public @interface FeatureAttachments {} diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureCaseDiffs.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureCaseDiffs.java index a2cd67b82a..20f7857b0d 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureCaseDiffs.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureCaseDiffs.java @@ -7,7 +7,7 @@ @IsTag( type = "Feature", value = "Case Diffs", description = "In order to get a better overview over structurally different cases of a scenario
" - + "As a human,
" + + "As a human,
" + "I want the differences highlighted in the generated report" ) @Retention( RetentionPolicy.RUNTIME ) public @interface FeatureCaseDiffs { diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureDataTables.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureDataTables.java index f7e2dd2d73..a8c61f7902 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureDataTables.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureDataTables.java @@ -7,7 +7,7 @@ @IsTag( type = "Feature", value = "Data Tables", description = "In order to get a better overview over the different cases of a scenario
" - + "As a human,
" + + "As a human,
" + "I want to have different cases represented as a data table" ) @Retention( RetentionPolicy.RUNTIME ) public @interface FeatureDataTables { diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureDerivedParameters.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureDerivedParameters.java index 2c5749e014..b711e81d3e 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureDerivedParameters.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureDerivedParameters.java @@ -7,7 +7,7 @@ @IsTag( type = "Feature", value = "Derived Parameters", description = "In order to not have to specify easily derivable parameters explicitly
" - + "As a developer,
" + + "As a developer,
" + "I want that step arguments derived from parameters appear in a data table" ) @Retention( RetentionPolicy.RUNTIME ) public @interface FeatureDerivedParameters { diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/Html5GeneratorTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/Html5GeneratorTest.java index b18078b070..13c5f48d73 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/Html5GeneratorTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/Html5GeneratorTest.java @@ -10,6 +10,7 @@ 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.FeatureAttachments; import com.tngtech.jgiven.tags.FeatureHtml5Report; import com.tngtech.jgiven.tags.Issue; @@ -67,4 +68,23 @@ public void clicking_on_tag_labels_opens_the_tag_page( boolean prependType, Stri then().the_page_title_is( tagName ); } + + @Test + @FeatureAttachments + public void attachments_appear_in_the_HTML5_report() throws Exception { + String content = "Some Example Attachment\nwith some example content"; + given().a_report_model() + .and().step_$_of_scenario_$_has_a_text_attachment_with_content( 1, 1, content ) + .and().the_report_exist_as_JSON_file(); + + whenReport + .and().the_HTML5_report_has_been_generated(); + + when().the_page_of_scenario_$_is_opened( 1 ) + .and().scenario_$_is_expanded( 1 ); + + then().an_attachment_icon_exists() + .and().the_content_of_the_referenced_attachment_is( content ); + + } } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/Html5ReportStage.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/Html5ReportStage.java new file mode 100644 index 0000000000..96bea2912f --- /dev/null +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/Html5ReportStage.java @@ -0,0 +1,30 @@ +package com.tngtech.jgiven.report.html5; + +import java.io.File; + +import org.openqa.selenium.OutputType; +import org.openqa.selenium.TakesScreenshot; +import org.openqa.selenium.WebDriver; + +import com.tngtech.jgiven.CurrentStep; +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.ExpectedScenarioState; +import com.tngtech.jgiven.attachment.Attachment; +import com.tngtech.jgiven.attachment.MediaType; + +public class Html5ReportStage> extends Stage { + @ExpectedScenarioState + protected CurrentStep currentStep; + + @ExpectedScenarioState + protected WebDriver webDriver; + + @ExpectedScenarioState + protected File targetReportDir; + + protected void takeScreenshot() { + String base64 = ( (TakesScreenshot) webDriver ).getScreenshotAs( OutputType.BASE64 ); + currentStep.addAttachment( Attachment.fromBase64( base64, MediaType.PNG ).withTitle( "Screenshot" ) ); + } + +} diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/ThenHtml5Report.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/ThenHtml5Report.java index 5d016282b0..57224155b9 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/ThenHtml5Report.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/ThenHtml5Report.java @@ -2,25 +2,18 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.File; import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Arrays; +import java.util.List; import org.openqa.selenium.By; -import org.openqa.selenium.OutputType; -import org.openqa.selenium.TakesScreenshot; -import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.testng.reporters.Files; -import com.tngtech.jgiven.Stage; -import com.tngtech.jgiven.StepAccess; -import com.tngtech.jgiven.annotation.ExpectedScenarioState; -import com.tngtech.jgiven.attachment.BinaryAttachment; - -public class ThenHtml5Report> extends Stage { - - @ExpectedScenarioState - StepAccess stepAccess; - - @ExpectedScenarioState - protected WebDriver webDriver; +public class ThenHtml5Report> extends Html5ReportStage { public SELF the_page_title_is( String pageTitle ) { assertThat( webDriver.findElement( By.id( "page-title" ) ).getText() ).isEqualTo( pageTitle ); @@ -29,8 +22,24 @@ public SELF the_page_title_is( String pageTitle ) { public SELF the_page_statistics_line_contains_text( String text ) throws IOException { assertThat( webDriver.findElement( By.className( "page-statistics" ) ).getText() ).contains( text ); - String base64 = ( (TakesScreenshot) webDriver ).getScreenshotAs( OutputType.BASE64 ); - stepAccess.addAttachment( BinaryAttachment.fromBase64PngImage( base64 ) ); + return self(); + } + + public SELF an_attachment_icon_exists() { + assertThat( findAttachmentIcon() ).isNotEmpty(); + return self(); + } + + private List findAttachmentIcon() { + return webDriver.findElements( By.className( "fa-paperclip" ) ); + } + + public SELF the_content_of_the_referenced_attachment_is( String content ) throws IOException, URISyntaxException { + String href = findAttachmentIcon().get( 0 ).findElement( By.xpath( ".." ) ).getAttribute( "href" ); + System.out.println( "==========" + href ); + System.out.println( Arrays.toString( targetReportDir.listFiles() ) ); + String foundContent = Files.readFile( new File( new URL( href ).toURI() ) ).trim(); + assertThat( content ).isEqualTo( foundContent ); return self(); } } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/WhenHtml5Report.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/WhenHtml5Report.java index 2b88f89fe8..864ac04b07 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/WhenHtml5Report.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/report/html5/WhenHtml5Report.java @@ -5,27 +5,34 @@ import java.util.List; import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; +import org.openqa.selenium.Dimension; import org.openqa.selenium.WebElement; import org.openqa.selenium.phantomjs.PhantomJSDriver; -import com.tngtech.jgiven.Stage; import com.tngtech.jgiven.annotation.AfterScenario; -import com.tngtech.jgiven.annotation.ProvidedScenarioState; +import com.tngtech.jgiven.annotation.AfterStage; +import com.tngtech.jgiven.annotation.BeforeScenario; +import com.tngtech.jgiven.annotation.ExpectedScenarioState; +import com.tngtech.jgiven.impl.util.WordUtil; +import com.tngtech.jgiven.report.model.ReportModel; +import com.tngtech.jgiven.report.model.ScenarioModel; -public class WhenHtml5Report> extends Stage { +public class WhenHtml5Report> extends Html5ReportStage { - @ProvidedScenarioState - protected WebDriver webDriver = new PhantomJSDriver(); - - @ProvidedScenarioState - protected File targetReportDir; + @ExpectedScenarioState + protected List reportModels; public SELF the_index_page_is_opened() throws MalformedURLException { url_$_is_opened( "" ); return self(); } + @BeforeScenario + protected void setupWebDriver() { + webDriver = new PhantomJSDriver(); + webDriver.manage().window().setSize( new Dimension( 1280, 768 ) ); + } + @AfterScenario protected void closeWebDriver() { webDriver.close(); @@ -55,4 +62,31 @@ public SELF the_All_Scenarios_page_is_opened() throws MalformedURLException { } return self(); } + + public SELF scenario_$_is_expanded( int scenarioNr ) { + ScenarioModel scenarioModel = getScenarioModel( scenarioNr ); + webDriver.findElement( By.xpath( "//h4[contains(text(),'" + + WordUtil.capitalize( scenarioModel.getDescription() ) + "')]" ) ) + .click(); + return self(); + } + + private ScenarioModel getScenarioModel( int scenarioNr ) { + return reportModels.get( 0 ).getScenarios().get( scenarioNr - 1 ); + } + + public SELF the_page_of_scenario_$_is_opened( int scenarioNr ) throws MalformedURLException { + + ScenarioModel scenarioModel = getScenarioModel( scenarioNr ); + url_$_is_opened( "#/scenario/" + + scenarioModel.getClassName() + + "/" + scenarioModel.getTestMethodName() ); + return self(); + } + + @AfterStage + public void takeScreenshotAfterStage() { + takeScreenshot(); + + } } 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 81fc852035..402b1375e2 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 @@ -11,6 +11,8 @@ import com.tngtech.jgiven.annotation.ExtendedDescription; import com.tngtech.jgiven.annotation.ProvidedScenarioState; import com.tngtech.jgiven.annotation.Table; +import com.tngtech.jgiven.attachment.Attachment; +import com.tngtech.jgiven.attachment.MediaType; import com.tngtech.jgiven.report.analysis.CaseArgumentAnalyser; public class GivenReportModel> extends Stage { @@ -207,8 +209,8 @@ private DataTable toDataTable( String[][] dataTable ) { public SELF case_$_has_a_when_step_$_with_argument_$_and_argument_name_$( int ncase, String name, String arg, String argName ) { getCase( ncase ) .addStep( - new StepModel(name, - Arrays.asList(Word.introWord("when"), new Word(name), Word.argWord(argName, arg, (String) null)))); + new StepModel( name, + Arrays.asList( Word.introWord( "when" ), new Word( name ), Word.argWord( argName, arg, (String) null ) ) ) ); return self(); } @@ -239,4 +241,10 @@ public SELF header_type_set_to( Table.HeaderType headerType ) { latestWord.getArgumentInfo().getDataTable().setHeaderType( headerType ); return self(); } + + public SELF step_$_of_scenario_$_has_an_attachment_with_content( int stepNr, int scenarioNr, String content ) { + StepModel step = reportModel.getScenarios().get( scenarioNr - 1 ).getScenarioCases().get( 0 ).getStep( stepNr - 1 ); + step.setAttachment( Attachment.fromText( content, MediaType.PLAIN_TEXT ) ); + 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 7ef377524d..e98ddb9d42 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 @@ -66,4 +66,9 @@ public SELF the_tag_has_prependType_set_to( boolean prependType ) { givenReportModel.step_$_of_case_$_has_status( stepNr, caseNr, status ); return self(); } + + public SELF step_$_of_scenario_$_has_a_text_attachment_with_content(int stepNr, int scenarioNr, String content) { + givenReportModel.step_$_of_scenario_$_has_an_attachment_with_content(stepNr, scenarioNr,content); + return self(); + } }