-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9c5fe82
commit a4278b8
Showing
9 changed files
with
527 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,7 @@ | |
.gradle | ||
build | ||
|
||
# Project files | ||
!export.pdf | ||
SimpleTest.java | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Oops, something went wrong.