Skip to content
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

Enable generation of Reports from a List of Features #959

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
136 changes: 93 additions & 43 deletions src/main/java/net/masterthought/cucumber/ReportBuilder.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,26 @@
package net.masterthought.cucumber;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import net.masterthought.cucumber.generators.ErrorPage;
import net.masterthought.cucumber.generators.FailuresOverviewPage;
import net.masterthought.cucumber.generators.FeatureReportPage;
import net.masterthought.cucumber.generators.FeaturesOverviewPage;
import net.masterthought.cucumber.generators.StepsOverviewPage;
import net.masterthought.cucumber.generators.TagReportPage;
import net.masterthought.cucumber.generators.TagsOverviewPage;
import net.masterthought.cucumber.generators.TrendsOverviewPage;
import net.masterthought.cucumber.json.Feature;
import net.masterthought.cucumber.generators.*;
import net.masterthought.cucumber.json.*;
import net.masterthought.cucumber.json.support.TagObject;
import org.apache.commons.io.FileUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import static java.nio.charset.StandardCharsets.UTF_8;

public class ReportBuilder {

private static final Logger LOG = Logger.getLogger(ReportBuilder.class.getName());
Expand All @@ -54,7 +46,7 @@ public class ReportBuilder {
private ReportResult reportResult;
private final ReportParser reportParser;

private Configuration configuration;
private final Configuration configuration;
private List<String> jsonFiles;

/**
Expand All @@ -64,45 +56,46 @@ public class ReportBuilder {
*/
private boolean wasTrendsFileSaved = false;

public ReportBuilder(List<String> jsonFiles, Configuration configuration) {
this.jsonFiles = jsonFiles;
public ReportBuilder(Configuration configuration) {
this.configuration = configuration;
reportParser = new ReportParser(configuration);
}

@Deprecated
public ReportBuilder(List<String> jsonFiles, Configuration configuration) {
this(configuration);
this.jsonFiles = jsonFiles;
}

/**
* Parses provided files and generates the report. When generating process fails
* report with information about error is provided.
*
* @return stats for the generated report
* @deprecated use {@link #generateReportsFromFiles(List)} instead
*/
@Deprecated
public Reportable generateReports() {
Trends trends = null;
return generateReportsFromFiles(jsonFiles);
}

/**
* Parses provided files and generates the report. When generating process fails
* report with information about error is provided.
*
* @return stats for the generated report
*/
public Reportable generateReportsFromFiles(List<String> pathsTojsonFiles) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this changes the API - will you update also maven and jenkins plugin which use this library as dependency?

try {
// first copy static resources so ErrorPage is displayed properly
copyStaticResources();

// create directory for embeddings before files are generated
createEmbeddingsDirectory();

// add metadata info sourced from files
reportParser.parseClassificationsFiles(configuration.getClassificationFiles());

// parse json files for results
List<Feature> features = reportParser.parseJsonFiles(jsonFiles);
reportResult = new ReportResult(features, configuration);
Reportable reportable = reportResult.getFeatureReport();

if (configuration.isTrendsAvailable()) {
// prepare data required by generators, collect generators and generate pages
trends = updateAndSaveTrends(reportable);
}

// Collect and generate pages in a single pass
generatePages(trends);

return reportable;
List<Feature> features = reportParser.parseJsonFiles(pathsTojsonFiles);
return generateReportsFromFeatures(features);

// whatever happens we want to provide at least error page instead of incomplete report or exception
} catch (Exception e) {
Expand All @@ -121,6 +114,62 @@ public Reportable generateReports() {
}
}

/**
* Parses provided features and generates the report. When generating process fails
* report with information about error is provided.
*
* @return stats for the generated report
*/
public Reportable generateReportsFromFeatures(List<Feature> features) {
reportResult = new ReportResult(features, configuration);
Reportable reportable = reportResult.getFeatureReport();

Trends trends = null;
if (configuration.isTrendsAvailable()) {
// prepare data required by generators, collect generators and generate pages
trends = updateAndSaveTrends(reportable);
}

generateEmbeddings();

// Collect and generate pages in a single pass
generatePages(trends);

return reportable;
}

private void generateEmbeddings() {
// create directory for embeddings before files are generated
createEmbeddingsDirectory();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have already removed this method by second PR?


reportResult.getAllFeatures().forEach(feature -> {
for (Element element : feature.getElements()) {
for (Hook hook : element.getAfter()) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after....

storeEmbedding(hook.getEmbeddings(), configuration.getEmbeddingDirectory());
}
for (Hook hook : element.getBefore()) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... should be after before :)

storeEmbedding(hook.getEmbeddings(), configuration.getEmbeddingDirectory());
}
for (Step step : element.getSteps()) {
storeEmbedding(step.getEmbeddings(), configuration.getEmbeddingDirectory());
}
}
});
}

private void storeEmbedding(Embedding[] embeddings, File embeddingDirectory) {
for (Embedding embedding : embeddings) {
Path file = FileSystems.getDefault().getPath(embeddingDirectory.getAbsolutePath(),
embedding.getFileId() + "." + embedding.getExtension());
byte[] decodedData = Base64.getDecoder().decode(embedding.getData().getBytes(UTF_8));
try {
Files.write(file, decodedData);
} catch (IOException e) {
throw new ValidationException(e);
}
}
}

private void copyStaticResources() {
copyResources("css", "cucumber.css", "bootstrap.min.css", "font-awesome.min.css");
copyResources("js", "jquery.min.js", "jquery.tablesorter.min.js", "bootstrap.min.js", "Chart.min.js",
Expand All @@ -144,7 +193,7 @@ private void copyResources(String resourceLocation, String... resources) {
// don't change this implementation unless you verified it works on Jenkins
try {
FileUtils.copyInputStreamToFile(
this.getClass().getResourceAsStream("/" + resourceLocation + "/" + resource), tempFile);
getClass().getResourceAsStream("/" + resourceLocation + "/" + resource), tempFile);
} catch (IOException e) {
// based on FileUtils implementation, should never happen even is declared
throw new ValidationException(e);
Expand Down Expand Up @@ -226,6 +275,7 @@ private void saveTrends(Trends trends, File file) {

private void generateErrorPage(Exception exception) {
LOG.log(Level.INFO, "Unexpected error", exception);
//FIXME jsonfiles can be null when generating from Features
ErrorPage errorPage = new ErrorPage(reportResult, configuration, exception, jsonFiles);
errorPage.generatePage();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,14 @@

import com.fasterxml.jackson.databind.JsonNode;
import net.masterthought.cucumber.Configuration;
import net.masterthought.cucumber.ValidationException;
import net.masterthought.cucumber.json.Embedding;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
* Deserializes embedding and stores it in attachment directory.
* Deserializes embedding.
*
* @author Damian Szczepanik (damianszczepanik@github)
*/
Expand All @@ -37,18 +31,16 @@ public Embedding deserialize(JsonNode rootNode, Configuration configuration) {
embedding = new Embedding(mimeType, encodedData);
}

storeEmbedding(embedding, configuration.getEmbeddingDirectory());

return embedding;
}

private String getBase64EncodedData(String data) {
try{
try {
// If we can successfully decode the data we consider it to be base64 encoded,
// so we do not need to do anything here
Base64.getDecoder().decode(data);
return data;
}catch (IllegalArgumentException e){
} catch (IllegalArgumentException e) {
// decoding failed, therefore we consider the data not to be encoded,
// so we need to encode it
return new String(Base64.getEncoder().encode(data.getBytes(UTF_8)), UTF_8);
Expand All @@ -65,15 +57,4 @@ private String findMimeType(JsonNode rootNode) {
return rootNode.get("mime_type").asText();
}

private void storeEmbedding(Embedding embedding, File embeddingDirectory) {
Path file = FileSystems.getDefault().getPath(embeddingDirectory.getAbsolutePath(),
embedding.getFileId() + "." + embedding.getExtension());
byte[] decodedData = Base64.getDecoder().decode(embedding.getData().getBytes(UTF_8));
try {
Files.write(file, decodedData);
} catch (IOException e) {
throw new ValidationException(e);
}
}

}
5 changes: 3 additions & 2 deletions src/test/java/net/masterthought/cucumber/ReportGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,15 @@ protected void setUpWithJson(String... jsonFiles) {

protected void initWithJson(String... jsonFiles) {
if (jsonFiles != null) {
for (String jsonFile : jsonFiles)
for (String jsonFile : jsonFiles) {
jsonReports.add(reportFromResource(jsonFile));
}
}
}

protected void initWithProperties(String... propertyFiles) {
for (String propertyFile : propertyFiles) {
this.classificationFiles.add(reportFromResourceProperties(propertyFile));
classificationFiles.add(reportFromResourceProperties(propertyFile));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,12 @@
package net.masterthought.cucumber.generators.integrations;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.File;

import net.masterthought.cucumber.generators.FeatureReportPage;
import net.masterthought.cucumber.generators.integrations.helpers.*;
import net.masterthought.cucumber.json.*;
import org.apache.commons.lang.StringUtils;
import org.junit.Test;

import net.masterthought.cucumber.generators.FeatureReportPage;
import net.masterthought.cucumber.generators.integrations.helpers.BriefAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.DocumentAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.ElementAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.EmbeddingAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.FeatureAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.HookAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.HooksAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.OutputAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.StepAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.StepsAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.TableAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.TableRowAssertion;
import net.masterthought.cucumber.generators.integrations.helpers.TagAssertion;
import net.masterthought.cucumber.json.Element;
import net.masterthought.cucumber.json.Embedding;
import net.masterthought.cucumber.json.Feature;
import net.masterthought.cucumber.json.Hook;
import net.masterthought.cucumber.json.Output;
import net.masterthought.cucumber.json.Result;
import net.masterthought.cucumber.json.Row;
import net.masterthought.cucumber.json.Step;
import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Damian Szczepanik (damianszczepanik@github)
Expand Down Expand Up @@ -306,13 +284,14 @@ public void generatePage_generatesEmbedding() {
assertThat(embeddingsElement).hasSameSizeAs(embeddings);
embeddingsElement[0].getLinks()[0].hasLabelAndAddress(embeddings[0].getName(), "");
embeddingsElement[0].hasImageContent(embeddings[0]);
assertEmbeddingFileExist(embeddings[0]);
// Embeddings are not generated when the page is generated, therefore it we should not check if the file exists
// assertEmbeddingFileExist(embeddings[0]);
embeddingsElement[2].getLinks()[0].hasLabelAndAddress("Attachment 3 (Plain text)", "");
embeddingsElement[2].hasTextContent(embeddings[2].getData());
assertEmbeddingFileExist(embeddings[2]);
// assertEmbeddingFileExist(embeddings[2]);
embeddingsElement[3].getLinks()[0].hasLabelAndAddress(embeddings[3].getName(), "");
embeddingsElement[3].hasSrcDocContent(embeddings[3].getData());
assertEmbeddingFileExist(embeddings[3]);
// assertEmbeddingFileExist(embeddings[3]);
}

@Test
Expand All @@ -335,7 +314,8 @@ public void generatePage_OnRubyFormat_ForAfterHook_generatesEmbedding() {

assertThat(embeddingsElement).hasSameSizeAs(embeddings);
embeddingsElement[0].hasImageContent(embeddings[0]);
assertEmbeddingFileExist(embeddings[0]);
// Embeddings are not generated when the page is generated, therefore it we should not check if the file exists
// assertEmbeddingFileExist(embeddings[0]);
}

@Test
Expand Down Expand Up @@ -425,8 +405,8 @@ private static void validateHook(HookAssertion[] hookAssertions, Hook[] hooks, S
}
}

private void assertEmbeddingFileExist(Embedding embedding) {
File file = new File(configuration.getEmbeddingDirectory(), embedding.getFileName());
assertThat(file).exists();
}
// private void assertEmbeddingFileExist(Embedding embedding) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused method should be removed, not commented out

git keeps history of changes

// File file = new File(configuration.getEmbeddingDirectory(), embedding.getFileName());
// assertThat(file).exists();
// }
}