Skip to content

[Core] Add name to embedding #1698

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions core/src/main/java/io/cucumber/core/api/Scenario.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,21 @@ public interface Scenario {
*
* @param data what to embed, for example an image.
* @param mimeType what is the data?
*
* @deprecated use {@link Scenario#embed(byte[], String, String)} instead.
*/
@Deprecated
void embed(byte[] data, String mimeType);

/**
* Like {@link Scenario#embed(byte[], String)}, but with name for the embedding.
*
* @param data what to embed, for example an image.
* @param mimeType what is the data?
* @param name embedding name
*/
void embed(byte[] data, String mimeType, String name);

/**
* Outputs some text into the report.
*
Expand Down
13 changes: 13 additions & 0 deletions core/src/main/java/io/cucumber/core/event/EmbedEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,20 @@
public final class EmbedEvent extends TestCaseEvent {
private final byte[] data;
private final String mimeType;
public final String name;

public EmbedEvent(Instant timeInstant, TestCase testCase, byte[] data, String mimeType) {
super(timeInstant, testCase);
this.data = Objects.requireNonNull(data);
this.mimeType = Objects.requireNonNull(mimeType);
this.name = null;
}

public EmbedEvent(Instant timeInstant, TestCase testCase, byte[] data, String mimeType, String name) {
super(timeInstant, testCase);
this.data = data;
this.mimeType = mimeType;
this.name = name;
}

public byte[] getData() {
Expand All @@ -23,4 +32,8 @@ public byte[] getData() {
public String getMimeType() {
return mimeType;
}

public String getName() {
return name;
}
}
4 changes: 2 additions & 2 deletions core/src/main/java/io/cucumber/core/plugin/HTMLFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,14 @@ private void handleEmbed(EmbedEvent event) {
String mimeType = event.getMimeType();
if (mimeType.startsWith("text/")) {
// just pass straight to the plugin to output in the html
jsFunctionCall("embedding", mimeType, new String(event.getData()));
jsFunctionCall("embedding", mimeType, new String(event.getData()), event.getName());
} else {
// Creating a file instead of using data urls to not clutter the js file
String extension = MIME_TYPES_EXTENSIONS.get(mimeType);
if (extension != null) {
StringBuilder fileName = new StringBuilder("embedded").append(embeddedIndex++).append(".").append(extension);
writeBytesToURL(event.getData(), toUrl(fileName.toString()));
jsFunctionCall("embedding", mimeType, fileName);
jsFunctionCall("embedding", mimeType, fileName, event.getName());
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions core/src/main/java/io/cucumber/core/plugin/JSONFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ private void handleWrite(WriteEvent event) {
}

private void handleEmbed(EmbedEvent event) {
addEmbeddingToHookMap(event.getData(), event.getMimeType());
addEmbeddingToHookMap(event.getData(), event.getMimeType(), event.getName());
}

private void handleTestStepFinished(TestStepFinished event) {
Expand Down Expand Up @@ -339,18 +339,21 @@ private void addOutputToHookMap(String text) {
((List<String>)currentStepOrHookMap.get("output")).add(text);
}

private void addEmbeddingToHookMap(byte[] data, String mimeType) {
private void addEmbeddingToHookMap(byte[] data, String mimeType, String name) {
if (!currentStepOrHookMap.containsKey("embeddings")) {
currentStepOrHookMap.put("embeddings", new ArrayList<Map<String, Object>>());
}
Map<String, Object> embedMap = createEmbeddingMap(data, mimeType);
Map<String, Object> embedMap = createEmbeddingMap(data, mimeType, name);
((List<Map<String, Object>>)currentStepOrHookMap.get("embeddings")).add(embedMap);
}

private Map<String, Object> createEmbeddingMap(byte[] data, String mimeType) {
private Map<String, Object> createEmbeddingMap(byte[] data, String mimeType, String name) {
Map<String, Object> embedMap = new HashMap<>();
embedMap.put("mime_type", mimeType);
embedMap.put("data", Base64.encodeBytes(data));
if (name != null) {
embedMap.put("name", name);
}
return embedMap;
}

Expand Down
13 changes: 7 additions & 6 deletions core/src/main/java/io/cucumber/core/runner/Scenario.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,17 @@ public boolean isFailed() {

@Override
public void embed(byte[] data, String mimeType) {
if (bus != null) {
bus.send(new EmbedEvent(bus.getInstant(), testCase, data, mimeType));
}
bus.send(new EmbedEvent(bus.getInstant(), testCase, data, mimeType));
}

@Override
public void embed(byte[] data, String mimeType, String name) {
bus.send(new EmbedEvent(bus.getInstant(), testCase, data, mimeType, name));
}

@Override
public void write(String text) {
if (bus != null) {
bus.send(new WriteEvent(bus.getInstant(), testCase, text));
}
bus.send(new WriteEvent(bus.getInstant(), testCase, text));
}

@Override
Expand Down
14 changes: 10 additions & 4 deletions core/src/main/resources/io/cucumber/core/plugin/html/formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,21 +93,27 @@ CucumberHTML.DOMFormatter = function(rootNode) {
}
};

this.embedding = function(mimeType, data) {
this.embedding = function(mimeType, data, name) {
var nameHtml;
if (!name) {
nameHtml = "";
} else {
nameHtml = "<h4>" + name + "</h4>";
}
if (currentStepIndex == 1) {
this.dummyStep();
}
if (mimeType.match(/^image\//))
{
currentStep.append('<img src="' + data + '">');
currentStep.append(nameHtml + '<img src="' + data + '">');
}
else if (mimeType.match(/^video\//))
{
currentStep.append('<video src="' + data + '" type="' + mimeType + '" autobuffer controls>Your browser doesn\'t support video.</video>');
currentStep.append(nameHtml + '<video src="' + data + '" type="' + mimeType + '" autobuffer controls>Your browser doesn\'t support video.</video>');
}
else if (mimeType.match(/^text\//))
{
this.write(data);
this.write(nameHtml + data);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ public void writes_valid_report_js() throws Throwable {
"formatter.result({\n" +
" \"status\": \"passed\"\n" +
"});\n",
"formatter.embedding(\"image/png\", \"embedded0.png\");\n",
"formatter.embedding(\"image/png\", \"embedded0.png\", \"Fake image\");\n",
"formatter.after({\n" +
" \"status\": \"passed\"\n" +
"});\n",
"formatter.embedding(\"text/plain\", \"dodgy stack trace here\");\n",
"formatter.embedding(\"text/plain\", \"dodgy stack trace here\", null);\n",
"formatter.after({\n" +
" \"status\": \"passed\"\n" +
"});\n"),
Expand All @@ -114,8 +114,8 @@ public void includes_uri() throws Throwable {
public void included_embedding() throws Throwable {
writeReport();
String reportJs = readReportJs();
assertContains("formatter.embedding(\"image/png\", \"embedded0.png\");", reportJs);
assertContains("formatter.embedding(\"text/plain\", \"dodgy stack trace here\");", reportJs);
assertContains("formatter.embedding(\"image/png\", \"embedded0.png\", \"Fake image\");", reportJs);
assertContains("formatter.embedding(\"text/plain\", \"dodgy stack trace here\", null);", reportJs);
}

@Test
Expand Down Expand Up @@ -654,7 +654,7 @@ public void should_handle_text_embeddings_from_before_hooks() {
" \"keyword\": \"Scenario\",\n" +
" \"name\": \"scenario name\"\n" +
"});\n", "" +
"formatter.embedding(\"text/ascii\", \"embedded from hook\");\n", "" +
"formatter.embedding(\"text/ascii\", \"embedded from hook\", null);\n", "" +
"formatter.before({\n" +
" \"status\": \"passed\"\n" +
"});\n", "" +
Expand Down Expand Up @@ -748,7 +748,7 @@ private void runFeaturesWithFormatter(URL outputDir) {
stepsToLocation.put("first step", "path/step_definitions.java:3");
hooks.add(TestHelper.hookEntry("after", result("passed")));
hooks.add(TestHelper.hookEntry("after", result("passed")));
hookActions.add(createEmbedHookAction("fakedata".getBytes(US_ASCII), "image/png"));
hookActions.add(createEmbedHookAction("fakedata".getBytes(US_ASCII), "image/png", "Fake image"));
hookActions.add(createEmbedHookAction("dodgy stack trace here".getBytes(US_ASCII), "text/plain"));
stepDuration = ofMillis(1L);

Expand Down
75 changes: 75 additions & 0 deletions core/src/test/java/io/cucumber/core/plugin/JSONFormatterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,81 @@ public void should_handle_embed_from_a_hook() {
assertThat(formatterOutput, sameJSONAs(expected));
}

@Test
public void should_handle_embed_with_name_from_a_hook() {
CucumberFeature feature = TestHelper.feature("path/test.feature", "" +
"Feature: Banana party\n" +
"\n" +
" Scenario: Monkey eats bananas\n" +
" Given there are bananas\n");
features.add(feature);
stepsToResult.put("there are bananas", result("passed"));
stepsToLocation.put("there are bananas", "StepDefs.there_are_bananas()");
hooks.add(TestHelper.hookEntry("before", result("passed")));
hookLocations.add("Hooks.before_hook_1()");
hookActions.add(createEmbedHookAction(new byte[]{1, 2, 3}, "mime-type;base64", "someEmbedding"));
stepDuration = ofMillis(1L);

String formatterOutput = runFeaturesWithFormatter();

String expected = "" +
"[\n" +
" {\n" +
" \"id\": \"banana-party\",\n" +
" \"uri\": \"file:path/test.feature\",\n" +
" \"keyword\": \"Feature\",\n" +
" \"name\": \"Banana party\",\n" +
" \"line\": 1,\n" +
" \"description\": \"\",\n" +
" \"elements\": [\n" +
" {\n" +
" \"id\": \"banana-party;monkey-eats-bananas\",\n" +
" \"keyword\": \"Scenario\",\n" +
" \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" +
" \"name\": \"Monkey eats bananas\",\n" +
" \"line\": 3,\n" +
" \"description\": \"\",\n" +
" \"type\": \"scenario\",\n" +
" \"before\": [\n" +
" {\n" +
" \"match\": {\n" +
" \"location\": \"Hooks.before_hook_1()\"\n" +
" },\n" +
" \"embeddings\": [\n" +
" {\n" +
" \"mime_type\": \"mime-type;base64\",\n" +
" \"data\": \"AQID\",\n" +
" \"name\": \"someEmbedding\"\n" +
" }\n" +
" ],\n" +
" \"result\": {\n" +
" \"status\": \"passed\",\n" +
" \"duration\": 1000000\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"steps\": [\n" +
" {\n" +
" \"keyword\": \"Given \",\n" +
" \"name\": \"there are bananas\",\n" +
" \"line\": 4,\n" +
" \"match\": {\n" +
" \"location\": \"StepDefs.there_are_bananas()\"\n" +
" },\n" +
" \"result\": {\n" +
" \"status\": \"passed\",\n" +
" \"duration\": 1000000\n" +
" }\n" +
" }\n" +
" ]\n" +
" }\n" +
" ],\n" +
" \"tags\": []\n" +
" }\n" +
"]";
assertThat(formatterOutput, sameJSONAs(expected));
}

@Test
public void should_format_scenario_with_a_step_with_a_doc_string() {
CucumberFeature feature = TestHelper.feature("path/test.feature", "" +
Expand Down
11 changes: 9 additions & 2 deletions core/src/test/java/io/cucumber/core/runner/TestHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -497,17 +497,24 @@ public Object answer(InvocationOnMock invocation) {
}

public static Answer<Object> createEmbedHookAction(final byte[] data, final String mimeType) {
return createEmbedHookAction(data, mimeType, null);
}

public static Answer<Object> createEmbedHookAction(final byte[] data, final String mimeType, final String name) {
return new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) {
Scenario scenario = (Scenario) invocation.getArguments()[0];
scenario.embed(data, mimeType);
if (name != null) {
scenario.embed(data, mimeType, name);
} else {
scenario.embed(data, mimeType);
}
return null;
}
};
}


private static AssertionFailedError mockAssertionFailedError() {
AssertionFailedError error = mock(AssertionFailedError.class);
Answer<Object> printStackTraceHandler = new Answer<Object>() {
Expand Down