Skip to content

Commit

Permalink
export allure to pdf (via #2)
Browse files Browse the repository at this point in the history
  • Loading branch information
eroshenkoam authored Apr 7, 2020
1 parent 9c5fe82 commit a4278b8
Show file tree
Hide file tree
Showing 9 changed files with 527 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@
.gradle
build

# Project files
!export.pdf
SimpleTest.java

22 changes: 22 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ tasks.withType(Wrapper::class) {
graal {
graalVersion(project.property("graalVersion") as String?)

option("-H:+JNI")
option("-H:+AddAllCharsets")
option("-H:IncludeResources=assets/fonts/arial.ttf")
option("-H:IncludeResources=org/apache/xmlgraphics/fonts/zapfdingbats.txt")
option("-H:IncludeResources=org/apache/xmlgraphics/fonts/glyphlist.txt")

option("--no-fallback")

mainClass("io.eroshenkoam.allure.AllurePDF")
outputName("allure-pdf")
}
Expand All @@ -27,7 +35,21 @@ dependencies {
annotationProcessor("info.picocli:picocli-codegen:4.1.4")

implementation("com.fasterxml.jackson.core:jackson-databind:2.10.2")
implementation("org.apache.commons:commons-collections4:4.4")
implementation("io.qameta.allure:allure-model:2.13.1")

implementation("org.slf4j:slf4j-log4j12:1.7.25")
implementation("org.slf4j:slf4j-simple:1.7.25")
implementation("commons-logging:commons-logging:1.2")

implementation("com.google.code.findbugs:jsr305:3.0.2")

implementation("com.github.librepdf:openpdf:1.3.13")
implementation("org.apache.xmlgraphics:fop:2.4") {
exclude(group = "com.sun.media", module = "jai-codec")
exclude(group = "javax.media", module = "jai-core")
}

implementation("info.picocli:picocli:4.1.4")
implementation("commons-io:commons-io:2.6")

Expand Down
75 changes: 75 additions & 0 deletions src/main/java/io/eroshenkoam/allure/FontHolder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package io.eroshenkoam.allure;

import com.lowagie.text.Font;
import com.lowagie.text.pdf.BaseFont;

import java.awt.*;
import java.io.IOException;

public final class FontHolder {

private static final String ARIAL_FONT = "/assets/fonts/arial.ttf";

private BaseFont baseFont;

private FontHolder() {
}

public static FontHolder loadArialFont() throws IOException {
return load(ARIAL_FONT, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
}

public static FontHolder load(final String name, final String encoding, final boolean embedded) throws IOException {
final FontHolder fontHolder = new FontHolder();
fontHolder.baseFont = BaseFont.createFont(name, encoding, embedded);
return fontHolder;
}

public Font normal() {
return normal(null);
}

public Font normal(final Color color) {
return new Font(baseFont, 8, Font.NORMAL, color);
}

public Font bold() {
return bold(null);
}

public Font bold(final Color color) {
return new Font(baseFont, 8, Font.BOLD, color);
}

public Font header4() {
return header4(null);
}

public Font header4(final Color color) {
return new Font(baseFont, 10, Font.BOLD, color);
}

public Font header3() {
return header3(null);
}

public Font header3(final Color color) {
return new Font(baseFont, 14, Font.BOLD, color);
}

public Font header2() {
return header2(null);
}

public Font header2(final Color color) {
return new Font(baseFont, 18, Font.BOLD, color);
}

public Font header1() {
return header1(null);
}

public Font header1(final Color color) {
return new Font(baseFont, 22, Font.BOLD, color);
}
}
187 changes: 187 additions & 0 deletions src/main/java/io/eroshenkoam/allure/MainCommand.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,201 @@
package io.eroshenkoam.allure;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lowagie.text.Chunk;
import com.lowagie.text.Document;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.ListItem;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfWriter;
import io.qameta.allure.model.Attachment;
import io.qameta.allure.model.Label;
import io.qameta.allure.model.StepResult;
import io.qameta.allure.model.TestResult;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import picocli.CommandLine;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static io.eroshenkoam.allure.FontHolder.loadArialFont;
import static io.eroshenkoam.allure.util.PdfUtil.addEmptyLine;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.mapping;

@CommandLine.Command(
name = "allure-pdf", mixinStandardHelpOptions = true
)
public class MainCommand implements Runnable {

private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ");

@CommandLine.Parameters(
index = "0",
description = "The directories with allure result files"
)
protected Path reportPath;

@CommandLine.Option(
names = {"-o", "--output"},
defaultValue = "export.pdf",
description = "Export output directory"
)
protected Path outputPath;

@Override
public void run() {
try {
runUnsafe();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private void runUnsafe() throws IOException {
if (!Files.isDirectory(reportPath)) {
log("Input [%s] is not directory", reportPath.toAbsolutePath());
return;
}
if (Files.notExists(reportPath)) {
log("Results directory [%s] does not exists", reportPath.toAbsolutePath());
return;
}

if (Files.notExists(outputPath)) {
log("Creating output file [%s] ...", outputPath.toAbsolutePath());
Files.createFile(outputPath);
}

final FontHolder fontHolder = loadArialFont();
final List<Path> files = Files.walk(reportPath)
.filter(s -> s.toString().endsWith("-result.json"))
.collect(Collectors.toList());
log("Found [%s] rest results ...", files.size());

try (final Document document = new Document(PageSize.A4)) {
PdfWriter.getInstance(document, Files.newOutputStream(outputPath));
document.newPage();
document.open();

addTitlePage(document, "Simple report", DATE_FORMAT, fontHolder);
final Paragraph tableHeader = new Paragraph("Test Details", fontHolder.header2());
addEmptyLine(tableHeader, 2);
document.add(tableHeader);
for (Path path : files) {
final TestResult result = new ObjectMapper().readValue(path.toFile(), TestResult.class);
printTestResultDetails(document, result, fontHolder);
}
}
}

private void addTitlePage(final Document document,
final String exportName,
final DateFormat dateFormat,
final FontHolder fontHolder) {
final Paragraph preface = new Paragraph();
addEmptyLine(preface, 5);
preface.add(new Phrase("Allure Report", fontHolder.header1()));
addEmptyLine(preface, 3);
preface.add(new Phrase(exportName, fontHolder.header3()));
addEmptyLine(preface, 2);
preface.add(new Phrase("Date: ", fontHolder.bold()));
preface.add(new Phrase(dateFormat.format(new Date()), fontHolder.normal()));
preface.setAlignment(Element.ALIGN_CENTER);
document.add(preface);
}

private void printTestResultDetails(final Document document,
final TestResult testResult,
final FontHolder fontHolder) {
final Paragraph details = new Paragraph();
addTestResultHeader(testResult, fontHolder, details);
addCustomFieldsSection(testResult, fontHolder, details);
addSteps(testResult, fontHolder, details);
document.add(details);
}

private void addTestResultHeader(final TestResult testResult, final FontHolder fontHolder,
final Paragraph details) {
final Chunk testResultName = new Chunk(testResult.getName(), fontHolder.header3());
final Paragraph testResultStatus = new Paragraph(testResultName);
testResultStatus.add(String.format("%s", new Phrase(testResult.getStatus().name())));
details.add(testResultStatus);
}

private void addCustomFieldsSection(final TestResult testResult,
final FontHolder fontHolder,
final Paragraph details) {
if (CollectionUtils.isNotEmpty(testResult.getLabels())) {
final Map<String, String> labels = testResult.getLabels().stream().collect(
groupingBy(Label::getName, mapping(Label::getValue, joining(", ")))
);
details.add(new Paragraph("Labels", fontHolder.header4()));
final com.lowagie.text.List list = new com.lowagie.text.List(false);
labels.forEach((key, value) -> {
list.add(new ListItem(String.format("%s: %s", key, value), fontHolder.normal()));
});
details.add(list);
}
}

private void addSteps(final TestResult testResult, final FontHolder fontHolder, final Paragraph details) {
if (Objects.nonNull(testResult.getSteps())) {
details.add(new Paragraph("Scenario", fontHolder.header4()));
final com.lowagie.text.List list = new com.lowagie.text.List(true);
testResult.getSteps().stream()
.map(step -> createStepItem(step, fontHolder))
.forEach(list::add);
details.add(list);
}
}

private ListItem createStepItem(final StepResult step, final FontHolder fontHolder) {
final Font font = fontHolder.normal();
final String stepTitle = String.format("%s [%s]", step.getName(), step.getStatus());
final ListItem stepItem = new ListItem(stepTitle, font);
if (Objects.nonNull(step.getAttachments())) {
final com.lowagie.text.List attachments = new com.lowagie.text.List(false, false);
for (final Attachment attach : step.getAttachments()) {
final String attachmentTitle = String.format("%s (%s)", attach.getName(), attach.getType());
final ListItem attachmentItem = new ListItem(attachmentTitle, font);
final com.lowagie.text.List content = new com.lowagie.text.List(false);
for (final String line : readFile(attach)) {
content.add(new ListItem(line, font));
}
attachmentItem.add(content);
attachments.add(attachmentItem);
}
stepItem.add(attachments);
}
return stepItem;
}

private List<String> readFile(final Attachment attachment) {
final Path file = reportPath.resolve(attachment.getSource());
try (InputStream stream = Files.newInputStream(file)) {
return IOUtils.readLines(stream, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private void log(String template, Object... values) {
System.out.println(String.format(template, values));
}

}
31 changes: 31 additions & 0 deletions src/main/java/io/eroshenkoam/allure/util/ColorUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.eroshenkoam.allure.util;

import io.qameta.allure.model.Status;

import java.awt.Color;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public final class ColorUtils {

public static final Color HEADER_COLOR = new Color(236, 239, 241);

private static final Map<Status, Color> STATUS_TEXT_COLORS = new HashMap<Status, Color>() {{
put(Status.PASSED, new Color(120, 182, 60));
put(Status.FAILED, new Color(255, 38, 2));
put(Status.BROKEN, new Color(254, 190, 13));
put(Status.SKIPPED, new Color(136, 136, 136));
}};

private ColorUtils() {
throw new IllegalStateException("Do not instance");
}

public static Color statusTextColor(final Status status) {
return Optional.ofNullable(status)
.map(STATUS_TEXT_COLORS::get)
.orElseGet(() -> new Color(191, 152, 166));
}

}
43 changes: 43 additions & 0 deletions src/main/java/io/eroshenkoam/allure/util/PdfUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.eroshenkoam.allure.util;

import com.lowagie.text.Chunk;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;

import java.awt.*;
import java.util.Arrays;

import static io.eroshenkoam.allure.util.ColorUtils.HEADER_COLOR;

public final class PdfUtil {

private PdfUtil() {
throw new IllegalStateException("Do not instance");
}

public static void addToTable(final PdfPTable table, final String data, final Font font) {
table.addCell(new Phrase(data, font));
}

public static void addEmptyLine(final Paragraph paragraph, final int number) {
for (int i = 0; i < number; i++) {
paragraph.add(Chunk.NEWLINE);
}
}

public static void addTableHeaders(final PdfPTable table, final Font font, final String... headers) {
Arrays.stream(headers)
.map(header -> {
final PdfPCell pdfPCell = new PdfPCell(new Phrase(header, font));
pdfPCell.setBackgroundColor(HEADER_COLOR);
pdfPCell.setHorizontalAlignment(Element.ALIGN_CENTER);
return pdfPCell;
})
.forEach(table::addCell);
table.setHeaderRows(1);
}
}
Loading

0 comments on commit a4278b8

Please sign in to comment.