
com.github.cukedoctor.renderer.CukedoctorStepsRenderer Maven / Gradle / Ivy
The newest version!
package com.github.cukedoctor.renderer;
import static com.github.cukedoctor.api.CukedoctorDocumentBuilder.Factory.newInstance;
import static com.github.cukedoctor.util.Assert.*;
import static com.github.cukedoctor.util.Constants.Markup.*;
import static com.github.cukedoctor.util.Constants.newLine;
import com.github.cukedoctor.api.CukedoctorDocumentBuilder;
import com.github.cukedoctor.api.model.*;
import com.github.cukedoctor.config.CukedoctorConfig;
import com.github.cukedoctor.spi.StepsRenderer;
import com.github.cukedoctor.util.Constants;
import com.github.cukedoctor.util.Formatter;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/** Created by pestano on 28/02/16. */
public class CukedoctorStepsRenderer extends AbstractBaseRenderer implements StepsRenderer {
public CukedoctorStepsRenderer() {}
public CukedoctorStepsRenderer(CukedoctorConfig cukedoctorConfig) {
this.cukedoctorConfig = cukedoctorConfig;
}
@Override
public String renderSteps(List steps, Scenario scenario, Feature feature) {
docBuilder.clear();
docBuilder.textLine("==========");
for (Step step : steps) {
docBuilder.append(step.getKeyword(), "::", newLine());
docBuilder.append(step.getName() + " ", Status.getStatusIcon(step.getStatus()));
if (!cukedoctorConfig.isHideStepTime()) {
docBuilder.append(renderStepTime(step.getResult()));
}
docBuilder.append(renderStepTable(step));
if (notNull(step.getDocString()) && hasText(step.getDocString().getValue())) {
if (step.getDocString().isDiscrete()
|| step.hasDiscreteComment()
|| isDiscrete(scenario)
|| isDiscrete(feature)) {
// discrete step renders inside a sidebar block and has [discrete] class on every line
renderDiscreteSidebarBlock(step.getDocString());
} else {
renderListingBlock(step.getDocString());
}
}
if (step.getResult() != null && !Status.passed.equals(step.getStatus())) {
if (step.getResult().getErrorMessage() != null) {
docBuilder.append(
newLine(), "IMPORTANT: ", step.getResult().getErrorMessage(), newLine());
}
}
renderOutput(step);
renderAttachments(step);
enrichStep(step);
}
docBuilder.textLine("==========").newLine();
return docBuilder.toString();
}
private boolean isDiscrete(Scenario scenario) {
if (!scenario.hasTags()) return false;
return isDiscrete(scenario.getTags());
}
private boolean isDiscrete(Feature feature) {
if (!feature.hasTags()) return false;
return isDiscrete(feature.getTags());
}
private boolean isDiscrete(Iterable tags) {
for (Tag tag : tags) {
if (tag.isDiscrete()) return true;
}
return false;
}
void renderOutput(Step step) {
if (step.hasOutput()) {
docBuilder.textLine(listing());
for (Output output : step.getOutput()) {
docBuilder.textLine(output.getValue());
}
docBuilder.textLine(listing());
}
}
private void renderAttachments(Step step) {
if (!step.hasEmbeddings()) {
return;
}
// HACK to pass an int by ref
AtomicInteger notNamedCount = new AtomicInteger(1);
for (Embedding attachment : step.getEmbeddings()) {
if (!shouldRenderAttachment(attachment)) {
continue;
}
renderAttachmentTitle(attachment, notNamedCount);
renderAttachmentContent(attachment);
}
}
private boolean shouldRenderAttachment(Embedding attachment) {
return hasText(attachment.getMimeType()) && attachment.getMimeType().startsWith("text/");
}
private void renderAttachmentContent(Embedding attachment) {
docBuilder.textLine("[%collapsible]");
docBuilder.textLine(exampleBlock());
if (hasText(attachment.getData())) {
docBuilder.textLine(getAttachmentContent(attachment));
}
docBuilder.textLine(exampleBlock());
}
private String getAttachmentContent(Embedding attachment) {
// HACK When using the JSON formatter, there is no way to tell if the attachment has been
// base64-encoded
// or not. In this implementation, we assume that anything that could be base64-encoded actually
// is.
//
// The new message formatter
// (https://github.com/cucumber/cucumber/blob/master/messages/messages.proto)
// provides an explicit content_encoding field to make this unambiguous. Until we adopt it, the
// hack
// remains.
// Additionally, we assume UTF-8 encoding. An enterprising contributor may wish to parse the
// mime type
// to check if a charset parameter has been specified.
try {
return new String(Base64.getDecoder().decode(attachment.getData()), StandardCharsets.UTF_8);
} catch (IllegalArgumentException e) {
return attachment.getData();
}
}
private void renderAttachmentTitle(Embedding attachment, AtomicInteger notNamedCount) {
docBuilder.newLine();
docBuilder.append(".");
docBuilder.textLine(getAttachmentTitle(attachment, notNamedCount));
}
private String getAttachmentTitle(Embedding attachment, AtomicInteger notNamedCount) {
if (hasText(attachment.getName())) {
return attachment.getName();
}
return "Attachment " + notNamedCount.getAndIncrement();
}
private void renderListingBlock(DocString docString) {
if (docString.getContentType() != null && !docString.getContentType().equals("")) {
docBuilder.append("[source,", docString.getContentType(), "]", newLine());
}
docBuilder.append(listing(), newLine(), newLine());
docBuilder.append(docString.getValue().replaceAll("\\n", newLine()));
docBuilder.append(newLine(), newLine(), listing(), newLine());
}
private void renderDiscreteSidebarBlock(DocString docString) {
docBuilder.append("******", newLine(), newLine());
String[] lines =
docString
.getValue()
.replaceAll("\\*\\*\\*\\*", "*****")
.replaceAll(exampleBlock(), exampleBlock() + "=")
.split("\\n");
// every line that starts with \n and not contains pipe(|) will have discrete class
// pipe is skipped because it denotes table cells in asciidoc and putting
// a discrete class will break tables
// also includes are skipped
// regex try:("^(?=.*^\\n)(?!\\n\\|).*$")
boolean isListing = false; // control if line is inside a listing
boolean isTable = false; // control if line is inside a table
for (String line : lines) {
if (!isListing) {
line = line.replaceAll("\r", "");
}
if (isListing) {
if (line.contains("----")) {
// end listing
isListing = false;
}
docBuilder.textLine(line);
continue;
}
if (!isListing && line.contains("----")) {
isListing = true;
docBuilder.textLine(line);
continue;
}
// do not add discrete to callouts
if (line.startsWith("<") && line.endsWith(">")) {
docBuilder.textLine(line);
continue;
}
// do not add discrete to complex blocks otherwise it will produce invalid markup like below:
/** [discrete] [IMPORTANT] [discrete] ====== */
if (line.startsWith("======")) {
docBuilder.textLine(line);
continue;
}
if (isTable) {
// skip discrete class when within a table
docBuilder.textLine(line);
if (line.contains(table())) {
isTable = false; // end table
}
continue;
}
// skips discrete class for table col (it must add discrete to table itself)
if (line.contains(table())) {
isTable = true;
}
// not inside a table neither listing then add discrete class to line
docBuilder.textLine(Constants.DISCRETE).textLine(line);
}
docBuilder.append(newLine(), newLine(), "******", newLine());
}
private void enrichStep(Step step) {
if (step != null && step.hasComments()) {
int numComments = step.getComments().size();
for (Comment comment : step.getComments()) {
if (hasText(comment.getValue())
&& (comment.getValue().contains("{") && comment.getValue().contains("}"))) {
String line = comment.getValue();
// do not add new line for listing, complex blocks, callouts
if (comment.getValue().contains("[source")
|| line.contains("====")
|| line.contains(Constants.Markup.listing())
|| (line.startsWith("<") && line.endsWith(">"))) {
docBuilder.textLine(
line.replaceAll("\\n", newLine())
.replaceAll("#\\{", "")
.replaceAll("# \\{", "")
.replaceAll("}", ""));
} else {
docBuilder.textLine(
line.replaceAll("\\n", newLine())
.replaceAll("#\\{", newLine())
.replaceAll("# \\{", newLine())
.replaceAll("}", ""));
}
}
// add new line to last comment
if (step.getComments().indexOf(comment) == numComments - 1) {
docBuilder.newLine();
}
}
}
}
String renderStepTime(Result result) {
if (result == null || result.getDuration() == null) {
return "";
}
return " [small right]#(" + Formatter.formatTime(result.getDuration()) + ")#";
}
String renderStepTable(Step step) {
// TODO convert to AsciidocBuilder
CukedoctorDocumentBuilder builder = newInstance();
builder.newLine();
if (notEmpty(step.getRows())) {
builder.newLine();
builder
.append("[cols=\"" + step.getRows()[0].getCells().length + "*\", options=\"header\"]")
.newLine();
builder.textLine(table());
Row header = step.getRows()[0];
for (String col : header.getCells()) {
builder.append(tableCol(), col).newLine();
}
for (int i = 1; i < step.getRows().length; i++) {
for (String cell : step.getRows()[i].getCells()) {
builder.append(tableCol(), cell).newLine();
}
}
builder.textLine(table());
builder.newLine();
}
return builder.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy