diff --git a/pom.xml b/pom.xml index bfacb6a..2ca2b60 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.testrail testrail-junit-extensions jar - 0.1.1 + 0.1.2 testrail-junit-extensions Improvements for JUnit that allow you to take better advantage of JUnit 5 (jupiter engine) https://github.com/gurock/testrail-junit-extensions diff --git a/src/main/java/com/testrail/junit/customjunitxml/XmlReportWriter.java b/src/main/java/com/testrail/junit/customjunitxml/XmlReportWriter.java index 0b27f50..aac8781 100644 --- a/src/main/java/com/testrail/junit/customjunitxml/XmlReportWriter.java +++ b/src/main/java/com/testrail/junit/customjunitxml/XmlReportWriter.java @@ -126,6 +126,7 @@ private void writeXmlReport(TestIdentifier testIdentifier, Map entries = this.reportData.getReportEntries(testIdentifier); Map testrunProperties = getTestRunProperties(entries); for (Map.Entry property : testrunProperties.entrySet()) { - addProperty(writer, property.getKey(), property.getValue()); + addPropertyAndEscapeValue(writer, property.getKey(), property.getValue()); } writer.writeEndElement(); // properties @@ -316,6 +317,12 @@ private void addProperty(XMLStreamWriter writer, String name, String value) thro newLine(writer); } + + private void addPropertyAndEscapeValue(XMLStreamWriter writer, String name, String value) throws XMLStreamException { + writer.writeCharacters(String.format("", name, escapeIllegalCharsForAttributes(value))); + newLine(writer); + } + private void addPropertyWithInnerContent(XMLStreamWriter writer, String name, String value) throws XMLStreamException { writer.writeStartElement("property"); @@ -502,6 +509,11 @@ private void writeAttributeSafely(XMLStreamWriter writer, String name, String va writer.writeAttribute(name, escapeIllegalChars(value)); } + + private void writeAttributeSafelyEncodingSomeChars(XMLStreamWriter writer, String name, String value) throws XMLStreamException { + writer.writeAttribute(name, escapeIllegalCharsForAttributes(value)); + } + private void writeCDataSafely(XMLStreamWriter writer, String data) throws XMLStreamException { for (String safeDataPart : CDATA_SPLIT_PATTERN.split(escapeIllegalChars(data))) { writer.writeCData(safeDataPart); @@ -533,6 +545,28 @@ private static boolean isAllowedXmlCharacter(int codePoint) { || (codePoint >= 0x10000 && codePoint <= 0x10FFFF); } + static String escapeIllegalCharsForAttributes(String text) { + if (text.codePoints().allMatch(XmlReportWriter::isAllowedXmlCharacterForAttributes)) { + return text; + } + StringBuilder result = new StringBuilder(text.length() * 2); + text.codePoints().forEach(codePoint -> { + if (isAllowedXmlCharacterForAttributes(codePoint)) { + result.appendCodePoint(codePoint); + } else { // use a Character Reference (cf. https://www.w3.org/TR/xml/#NT-CharRef) + result.append("&#").append(codePoint).append(';'); + } + }); + return result.toString(); + } + + private static boolean isAllowedXmlCharacterForAttributes(int codePoint) { + // source: https://www.w3.org/TR/xml/#charsets with a workaround for enconding some characters, such as tab, newline, carriage return + return (codePoint >= 0x20 && codePoint <= 0xD7FF) // + || (codePoint >= 0xE000 && codePoint <= 0xFFFD) // + || (codePoint >= 0x10000 && codePoint <= 0x10FFFF); + } + private void newLine(XMLStreamWriter xmlWriter) throws XMLStreamException { xmlWriter.writeCharacters("\n"); } diff --git a/src/test/java/com/testrail/junit/customjunitxml/EnhancedLegacyXmlTest.java b/src/test/java/com/testrail/junit/customjunitxml/EnhancedLegacyXmlTest.java index 84f23b0..a0cbdea 100644 --- a/src/test/java/com/testrail/junit/customjunitxml/EnhancedLegacyXmlTest.java +++ b/src/test/java/com/testrail/junit/customjunitxml/EnhancedLegacyXmlTest.java @@ -14,6 +14,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.joox.JOOX.$; +import static org.joox.JOOX.attr; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod; @@ -378,6 +379,16 @@ public void shouldStoreTestRunPropertiesMultiple() throws Exception { assertThat(testcase.child("properties").children("property").matchAttr("name", "my_property2").attr("value")).isEqualTo("world"); } + @Test + public void shouldStoreMultilineTestRunProperties() throws Exception { + String testMethodName = "testWithTestRunPropertyMultiline"; + executeTestMethodWithParams(TEST_EXAMPLES_CLASS, testMethodName, "com.testrail.junit.customjunitxml.TestRailTestReporter"); + Match testsuite = readValidXmlFile(tempDirectory.resolve(REPORT_NAME)); + Match testcase = testsuite.child("testcase"); + assertThat(testcase.attr("name", String.class)).isEqualTo(testMethodName); + assertThat(testcase.child("properties").children("property").matchAttr("name", "testrail_case_field").attr("value")).isEqualTo("custom_steps:1. First step\n2. Second step\n3. Third step"); + } + private Match readValidXmlFile(Path xmlFile) throws Exception { assertTrue(Files.exists(xmlFile), () -> "File does not exist: " + xmlFile); try (BufferedReader reader = Files.newBufferedReader(xmlFile)) { @@ -398,6 +409,15 @@ static void assertValidAccordingToJenkinsSchema(Document document) throws Except } } + private void dumpJunitXMLReport(Path reportPath) { + System.out.println("Junit XML report: " + reportPath); + try { + Files.lines(reportPath).forEach(System.out::println); + } catch (Exception e) { + e.printStackTrace(); + } + } + private enum CachedSchema { JENKINS("/enhanced-jenkins-junit.xsd"); diff --git a/src/test/java/com/testrail/junit/customjunitxml/ExamplesTestRailEnabledTestExamples.java b/src/test/java/com/testrail/junit/customjunitxml/ExamplesTestRailEnabledTestExamples.java index f87e1f9..247bcd4 100644 --- a/src/test/java/com/testrail/junit/customjunitxml/ExamplesTestRailEnabledTestExamples.java +++ b/src/test/java/com/testrail/junit/customjunitxml/ExamplesTestRailEnabledTestExamples.java @@ -39,6 +39,12 @@ public void testWithMultipleTestRunProperties(TestRailTestReporter customReporte customReporter.setProperty("my_property2", "world"); } + @Test + public void testWithTestRunPropertyMultiline(TestRailTestReporter customReporter) { + customReporter.setProperty("testrail_case_field", "custom_steps:1. First step\n2. Second step\n3. Third step"); + } + + @Test @TestRail(id = "myCustomId") public void annotatedTestWithCustomId() {