diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 21791c9..901657c 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: - java-version: [17, 21, 23] + java-version: [17, 21, 24] steps: - name: Checkout source repository diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index c1d316f..f4c68af 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,8 +1,9 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.9 -bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.2.0 +bld.extension-exec=com.uwyn.rife2:bld-exec:1.0.5 +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.10 +bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.2.3 bld.extension-tests=com.uwyn.rife2:bld-tests-badge:1.4.6 bld.repositories=MAVEN_CENTRAL,MAVEN_LOCAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.version=2.2.1 diff --git a/src/bld/java/rife/render/TemplateRenderersBuild.java b/src/bld/java/rife/render/TemplateRenderersBuild.java index 4f4fc5e..9e450d5 100644 --- a/src/bld/java/rife/render/TemplateRenderersBuild.java +++ b/src/bld/java/rife/render/TemplateRenderersBuild.java @@ -19,6 +19,7 @@ package rife.render; import rife.bld.BuildCommand; import rife.bld.Project; +import rife.bld.extension.ExecOperation; import rife.bld.extension.JacocoReportOperation; import rife.bld.extension.PmdOperation; import rife.bld.extension.TestsBadgeOperation; @@ -27,6 +28,9 @@ import rife.bld.publish.PublishInfo; import rife.bld.publish.PublishLicense; import rife.bld.publish.PublishScm; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import static rife.bld.dependencies.Repository.*; @@ -50,8 +54,8 @@ public class TemplateRenderersBuild extends Project { scope(compile) .include(dependency("com.uwyn.rife2", "rife2", version(1, 9, 1))); scope(test) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 0))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 0))) + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 2))) .include(dependency("org.assertj", "assertj-core", version(3, 27, 3))); javadocOperation().javadocOptions() @@ -116,10 +120,36 @@ public class TemplateRenderersBuild extends Project { .execute(); } + @Override public void test() throws Exception { - testsBadgeOperation.executeOnce(() -> testsBadgeOperation + var testResultsDir = "build/test-results/test/"; + + var op = testsBadgeOperation .url(property("testsBadgeUrl")) .apiKey(property("testsBadgeApiKey")) - .fromProject(this)); + .fromProject(this); + op.testToolOptions().reportsDir(new File(testResultsDir)); + op.execute(); + + var xunitViewer = new File("/usr/bin/xunit-viewer"); + if (xunitViewer.exists() && xunitViewer.canExecute()) { + var reportsDir = "build/reports/tests/test/"; + + var iconv = new File("/usr/bin/iconv"); + if (iconv.exists() && iconv.canExecute()) { + var junitTestResults = new File(testResultsDir, "TEST-junit-jupiter.xml").getPath(); + new ExecOperation() + .fromProject(this) + .command("iconv", "-f", "CP1250", "-t", "UTF8", "-o", junitTestResults, junitTestResults) + .execute(); + + Files.createDirectories(Path.of(reportsDir)); + + new ExecOperation() + .fromProject(this) + .command(xunitViewer.getPath(), "-r", testResultsDir, "-o", reportsDir + "index.html") + .execute(); + } + } } } diff --git a/src/main/java/rife/render/RenderUtils.java b/src/main/java/rife/render/RenderUtils.java index c05f5b9..8dcfdab 100644 --- a/src/main/java/rife/render/RenderUtils.java +++ b/src/main/java/rife/render/RenderUtils.java @@ -596,10 +596,16 @@ public final class RenderUtils { properties.getProperty("hours", " hours "))); } - sb.append(minutes).append(plural(minutes, properties.getProperty("minute", " minute"), - properties.getProperty("minutes", " minutes"))); + if (minutes == 0) { + if (years == 0 && months == 0 && weeks == 0 && days == 0 && hours == 0) { + sb.append(0).append(properties.getProperty("minute", " minute")); + } + } else { + sb.append(minutes).append(plural(minutes, properties.getProperty("minute", " minute"), + properties.getProperty("minutes", " minutes"))); + } - return sb.toString(); + return sb.toString().trim(); } /** diff --git a/src/test/java/rife/render/TestCase.java b/src/test/java/rife/render/TestCase.java index 2759ff6..fb2b264 100644 --- a/src/test/java/rife/render/TestCase.java +++ b/src/test/java/rife/render/TestCase.java @@ -28,51 +28,66 @@ class TestCase { static final String SAMPLE_TEXT = "This is a test."; @Test - void testCapitalize() { + void capitalize() { var t = TemplateFactory.TXT.get("capitalize"); t.setAttribute(FOO, SAMPLE_TEXT.toLowerCase(Localization.getLocale())); assertThat(t.getContent()).isEqualTo(SAMPLE_TEXT); } @Test - void testLowercase() { + void lowercase() { var t = TemplateFactory.TXT.get("lowercase"); var bean = new ValueBean("this IS a TEST."); t.setBean(bean); assertThat(t.getContent()).isEqualTo(bean.getValue() + ": this is a test."); - bean = new ValueBean(""); + } + + @Test + void lowercaseWithEmpty() { + var t = TemplateFactory.TXT.get("lowercase"); + var bean = new ValueBean(""); t.setBean(bean); assertThat(t.getContent()).isEqualTo(bean.getValue() + ": "); } @Test - void testSwapCase() { + void swapCase() { var t = TemplateFactory.TXT.get("swapCase"); t.setAttribute(FOO, "tHiS iS a TeSt"); assertThat(t.getContent()).isEqualTo("ThIs Is A tEsT"); } @Test - void testTrim() { + void trim() { var t = TemplateFactory.TXT.get("trim"); t.setAttribute(FOO, "\t" + SAMPLE_TEXT + " \n"); assertThat(t.getContent()).isEqualTo(SAMPLE_TEXT); + } + + @Test + void trimWithEmpty() { + var t = TemplateFactory.TXT.get("trim"); t.setAttribute(FOO, ""); assertThat(t.getContent()).isEmpty(); } @Test - void testUncapitalize() { + void uncapitalize() { var t = TemplateFactory.TXT.get("uncapitalize"); t.setAttribute(FOO, SAMPLE_TEXT); assertThat(t.getContent()).isEqualTo(SAMPLE_TEXT.toLowerCase(Localization.getLocale())); } @Test - void testUppercase() { + void uppercase() { var t = TemplateFactory.TXT.get("uppercase"); t.setAttribute("bar", SAMPLE_TEXT); assertThat(t.getContent()).isEqualTo(SAMPLE_TEXT.toUpperCase(Localization.getLocale())); + } + + @Test + void uppercaseWithEmpty() { + var t = TemplateFactory.TXT.get("uppercase"); t.setAttribute("bar", ""); assertThat(t.getContent()).isEmpty(); } diff --git a/src/test/java/rife/render/TestDateTime.java b/src/test/java/rife/render/TestDateTime.java index bfb8cc7..c9b9c1a 100644 --- a/src/test/java/rife/render/TestDateTime.java +++ b/src/test/java/rife/render/TestDateTime.java @@ -25,31 +25,31 @@ import static org.assertj.core.api.Assertions.assertThatCode; class TestDateTime { @Test - void testBestTime() { + void bestTime() { var t = TemplateFactory.HTML.get("beatTime"); assertThat(t.getContent()).matches("@\\d{3}"); } @Test - void testDateIso() { + void dateIso() { var t = TemplateFactory.HTML.get("dateIso"); assertThatCode(() -> RenderUtils.ISO_8601_DATE_FORMATTER.parse(t.getContent())).doesNotThrowAnyException(); } @Test - void testDateTimeIso() { + void dateTimeIso() { var t = TemplateFactory.HTML.get("dateTimeIso"); assertThatCode(() -> RenderUtils.ISO_8601_FORMATTER.parse(t.getContent())).doesNotThrowAnyException(); } @Test - void testDateTimeRfc2822() { + void dateTimeRfc2822() { var t = TemplateFactory.HTML.get("dateTimeRfc2822"); assertThatCode(() -> RenderUtils.RFC_2822_FORMATTER.parse(t.getContent())).doesNotThrowAnyException(); } @Test - void testDateTimeUtc() { + void dateTimeUtc() { var t = TemplateFactory.HTML.get("dateTimeUtc"); var content = t.getContent(); assertThatCode(() -> RenderUtils.ISO_8601_FORMATTER.parse(content)).doesNotThrowAnyException(); @@ -58,13 +58,13 @@ class TestDateTime { } @Test - void testTimeIso() { + void timeIso() { var t = TemplateFactory.HTML.get("timeIso"); assertThatCode(() -> RenderUtils.ISO_8601_TIME_FORMATTER.parse(t.getContent())).doesNotThrowAnyException(); } @Test - void testYear() { + void year() { var t = TemplateFactory.HTML.get("year"); var year = java.time.Year.now().toString(); assertThat(t.getContent()).isEqualTo(year + "
" + year); diff --git a/src/test/java/rife/render/TestEncode.java b/src/test/java/rife/render/TestEncode.java index bc390d4..2ea95af 100644 --- a/src/test/java/rife/render/TestEncode.java +++ b/src/test/java/rife/render/TestEncode.java @@ -24,25 +24,38 @@ import static org.assertj.core.api.Assertions.assertThat; class TestEncode { @Test - void testEncodeBase64() { - var t = TemplateFactory.TXT.get("encodeBase64"); - t.setValue(TestCase.FOO, TestCase.SAMPLE_TEXT); - assertThat(t.getContent()).isEqualTo(t.getValue(TestCase.FOO) + ": VGhpcyBpcyBhIHRlc3Qu"); + void decodeRot13() { + var t = TemplateFactory.TXT.get("rot13"); + var rot13 = "Guvf vf n grfg."; - t = TemplateFactory.HTML.get("encodeBase64"); - t.setValue(TestCase.FOO, TestCase.SAMPLE_TEXT + " URL Encoded."); - assertThat(t.getContent()).as("with URL encoding").contains("VGhpcyBpcyBhIHRlc3QuIFVSTCBFbmNvZGVkLg%3D%3D"); + t.setValue("value", rot13); + assertThat(t.getContent()).isEqualTo(rot13 + ": " + TestCase.SAMPLE_TEXT); } @Test - void testEncodeHtml() { + void encodeBase64() { + var t = TemplateFactory.TXT.get("encodeBase64"); + t.setValue(TestCase.FOO, TestCase.SAMPLE_TEXT); + assertThat(t.getContent()).isEqualTo(t.getValue(TestCase.FOO) + ": VGhpcyBpcyBhIHRlc3Qu"); + } + + @Test + void encodeBase64WithUrlEncoding() { + var t = TemplateFactory.HTML.get("encodeBase64"); + t.setValue(TestCase.FOO, TestCase.SAMPLE_TEXT + " URL Encoded."); + assertThat(t.getContent()).as("with URL encoding") + .contains("VGhpcyBpcyBhIHRlc3QuIFVSTCBFbmNvZGVkLg%3D%3D"); + } + + @Test + void encodeHtml() { var t = TemplateFactory.HTML.get("encodeHtml"); t.setAttribute(TestCase.FOO, ""); assertThat(t.getContent()).isEqualTo("<a test &>"); } @Test - void testEncodeHtmlEntities() { + void encodeHtmlEntities() { var t = TemplateFactory.HTML.get("encodeHtmlEntities"); t.setAttribute(TestCase.FOO, "john@doe.com"); assertThat(t.getContent()).isEqualTo( @@ -50,74 +63,83 @@ class TestEncode { } @Test - void testEncodeJs() { + void encodeJs() { var t = TemplateFactory.TXT.get("encodeJs"); t.setAttribute(TestCase.FOO, "'\"\\/"); assertThat(t.getContent()).isEqualTo("\\'\\\"\\\\\\/"); - - t = TemplateFactory.TXT.get("encodeJs"); - t.setAttribute(TestCase.FOO, "This is\f\b a\r\n\ttest"); - assertThat(t.getContent()).isEqualTo("This is\\f\\b a\\r\\n\\ttest"); - - t = TemplateFactory.HTML.get("encodeJs"); - t.setAttribute(TestCase.FOO, '"' + TestCase.SAMPLE_TEXT + '"'); - assertThat(t.getContent()).as("with unicode") - .isEqualTo("\\u005C\\u0022\\u0054\\u0068\\u0069\\u0073\\u0020\\u0069\\u0073\\u0020\\u0061\\u0020\\u0074\\u0065\\u0073\\u0074\\u002E\\u005C\\u0022"); } @Test - void testEncodeJson() { + void encodeJsWithSpecialCharacters() { + var t = TemplateFactory.TXT.get("encodeJs"); + t.setAttribute(TestCase.FOO, "This is\f\b a\r\n\ttest"); + assertThat(t.getContent()).isEqualTo("This is\\f\\b a\\r\\n\\ttest"); + } + + @Test + void encodeJsWithUnicode() { + var t = TemplateFactory.HTML.get("encodeJs"); + t.setAttribute(TestCase.FOO, '"' + TestCase.SAMPLE_TEXT + '"'); + assertThat(t.getContent()).isEqualTo( + "\\u005C\\u0022\\u0054\\u0068\\u0069\\u0073\\u0020\\u0069\\u0073\\u0020\\u0061\\u0020\\u0074\\u0065\\u0073\\u0074\\u002E\\u005C\\u0022"); + } + + @Test + void encodeJson() { var t = TemplateFactory.JSON.get("encodeJson"); t.setAttribute(TestCase.FOO, "This is a \"•test\""); assertThat(t.getContent()).isEqualTo("{\n \"foo\": \"This is a \\\"\\u2022test\\\"\"\n}"); - - t = TemplateFactory.HTML.get("encodeJson"); - t.setAttribute(TestCase.FOO, "\"\""); - assertThat(t.getContent()).as("with html").isEqualTo("\\"<test>\\""); } @Test - void testEncodeRot13() { + void encodeJsonWithHtml() { + var t = TemplateFactory.HTML.get("encodeJson"); + t.setAttribute(TestCase.FOO, "\"\""); + assertThat(t.getContent()).isEqualTo("\\"<test>\\""); + } + + @Test + void encodeRot13() { var t = TemplateFactory.TXT.get("rot13"); var rot13 = "Guvf vf n grfg."; - // Encode var bean = new ValueBean(TestCase.SAMPLE_TEXT); t.setBean(bean); - assertThat(t.getContent()).as("encode").isEqualTo(bean.getValue() + ": " + rot13); - - // Decode - t.setValue("value", rot13); - assertThat(t.getContent()).as("decode").isEqualTo(rot13 + ": " + TestCase.SAMPLE_TEXT); + assertThat(t.getContent()).isEqualTo(bean.getValue() + ": " + rot13); } @Test - void testEncodeUnicode() { + void encodeUnicode() { var t = TemplateFactory.TXT.get("encodeUnicode"); t.setAttribute(TestCase.FOO, TestCase.SAMPLE_TEXT); assertThat(t.getContent()).isEqualTo( "\\u0054\\u0068\\u0069\\u0073\\u0020\\u0069\\u0073\\u0020\\u0061\\u0020\\u0074\\u0065\\u0073\\u0074\\u002E"); - - t = TemplateFactory.HTML.get("encodeUnicode"); - t.setAttribute(TestCase.FOO, '"' + TestCase.SAMPLE_TEXT + '"'); - assertThat(t.getContent()).as("with js") - .contains("'\\\\u0022\\\\u0054\\\\u0068\\\\u0069\\\\u0073\\\\u0020\\\\u0069\\\\u0073\\\\u0020\\\\u0061\\\\u0020\\\\u0074\\\\u0065\\\\u0073\\\\u0074\\\\u002E\\\\u0022'"); } @Test - void testEncodeUrl() { + void encodeUnicodeWithJs() { + var t = TemplateFactory.HTML.get("encodeUnicode"); + t.setAttribute(TestCase.FOO, '"' + TestCase.SAMPLE_TEXT + '"'); + assertThat(t.getContent()).contains( + "'\\\\u0022\\\\u0054\\\\u0068\\\\u0069\\\\u0073\\\\u0020\\\\u0069\\\\u0073\\\\u0020\\\\u0061\\\\u0020\\\\u0074\\\\u0065\\\\u0073\\\\u0074\\\\u002E\\\\u0022'"); + } + + @Test + void encodeUrl() { var t = TemplateFactory.HTML.get("encodeUrl"); t.setAttribute(TestCase.FOO, "a test &"); assertThat(t.getContent()).isEqualTo("a test &"); - - t = TemplateFactory.HTML.get("encodeUrlwithUnicode"); - t.setAttribute(TestCase.FOO, "a=test"); - assertThat(t.getContent()).as("with unicode") - .contains("https://foo.com/\\u0061\\u0025\\u0033\\u0044\\u0074\\u0065\\u0073\\u0074"); } @Test - void testEncodeXml() { + void encodeUrlWithUnicode() { + var t = TemplateFactory.HTML.get("encodeUrlwithUnicode"); + t.setAttribute(TestCase.FOO, "a=test"); + assertThat(t.getContent()).contains("https://foo.com/\\u0061\\u0025\\u0033\\u0044\\u0074\\u0065\\u0073\\u0074"); + } + + @Test + void encodeXml() { var t = TemplateFactory.XML.get("encodeXml"); t.setAttribute(TestCase.FOO, "a test &"); assertThat(t.getContent()).isEqualTo("\n a test &\n"); diff --git a/src/test/java/rife/render/TestFormat.java b/src/test/java/rife/render/TestFormat.java index eb1840b..c7fed3e 100644 --- a/src/test/java/rife/render/TestFormat.java +++ b/src/test/java/rife/render/TestFormat.java @@ -17,63 +17,56 @@ package rife.render; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import rife.template.TemplateFactory; import static org.assertj.core.api.Assertions.assertThat; +@SuppressWarnings("PMD.AvoidDuplicateLiterals") class TestFormat { @Test - void testAbbreviate() { + void abbreviate() { + var t = TemplateFactory.TXT.get("abbreviate"); + t.setAttribute(TestCase.FOO, TestCase.SAMPLE_TEXT); + assertThat(t.getContent()).endsWith("...").hasSize(8); + } + + @Test + void abbreviateHtml() { var t = TemplateFactory.HTML.get("abbreviate"); t.setAttribute(TestCase.FOO, TestCase.SAMPLE_TEXT); - assertThat(t.getContent()).as("activate.html").endsWith("…").hasSize(19); - - t = TemplateFactory.TXT.get("abbreviate"); - t.setAttribute(TestCase.FOO, TestCase.SAMPLE_TEXT); - assertThat(t.getContent()).as("activate.txt").endsWith("...").hasSize(8); + assertThat(t.getContent()).hasSize(19); } @Test - void testFormatCreditCard() { - var t = TemplateFactory.TXT.get("formatCreditCard"); - t.setAttribute(TestCase.FOO, "4342 2565 6244 0179"); - assertThat(t.getContent()).as("US VISA").isEqualTo("0179"); - t.setAttribute(TestCase.FOO, "5130-3899-9169-8324"); - assertThat(t.getContent()).as("FR MASTERCARD").isEqualTo("8324"); - t.setAttribute(TestCase.FOO, "374380141731053"); - assertThat(t.getContent()).as("UK AMEX").isEqualTo("1053"); - t.setAttribute(TestCase.FOO, "000000000000001"); - assertThat(t.getContent()).as("000000000000001").isEmpty(); - t.setAttribute(TestCase.FOO, ""); - assertThat(t.getContent()).as("").isEmpty(); + void mask() { + var t = TemplateFactory.TXT.get("mask"); + var foo = "374380141731053"; + t.setAttribute(TestCase.FOO, foo); + assertThat(t.getContent()).isEqualTo("***************"); } @Test - void testMask() { + void maskHtml() { var t = TemplateFactory.HTML.get("mask"); var foo = "374380141731053"; t.setAttribute(TestCase.FOO, foo); - assertThat(t.getContent()).as("mask.html") - .isEqualTo("3743•••••••••••"); - - t = TemplateFactory.TXT.get("mask"); - t.setAttribute(TestCase.FOO, foo); - assertThat(t.getContent()).as("mask.txt").isEqualTo("***************"); + assertThat(t.getContent()).isEqualTo("3743•••••••••••"); } @Test - void testNormalize() { + void normalize() { var t = TemplateFactory.HTML.get("normalize"); var foo = "News for January 6, 2023 (Paris)"; t.setValue(TestCase.FOO, foo); - assertThat(t.getContent()).isEqualTo("" - + foo + ""); + assertThat(t.getContent()).isEqualTo("" + foo + ""); } @Test @DisabledOnCi - void testQrCode() { + void qrCode() { var t = TemplateFactory.SVG.get("qrCode"); var foo = "https://example.com/"; t.setAttribute(TestCase.FOO, foo); @@ -82,7 +75,7 @@ class TestFormat { @Test @DisabledOnCi - void testShortenUrl() { + void shortenUrl() { var t = TemplateFactory.HTML.get("shortenUrl"); var url = "https://example.com/"; var shortUrl = "https://is.gd/AG3Hwv"; @@ -92,28 +85,108 @@ class TestFormat { assertThat(t.getContent()).isEqualTo("foo"); } - @Test - void testUptime() { - var t = TemplateFactory.TXT.get("uptime"); - assertThat(t.getContent()).as("uptime.txt").isEqualTo("0 minute\n0 minuto\n0 minute"); + @Nested + @DisplayName("Credit Card Format Tests") + class CreditCardFormatTests { + @Test + void amexCreditCard() { + var t = TemplateFactory.TXT.get("formatCreditCard"); + t.setAttribute(TestCase.FOO, "374380141731053"); + assertThat(t.getContent()).isEqualTo("1053"); + } - t = TemplateFactory.HTML.get("uptime"); - t.setAttribute(Uptime.class.getName(), 547800300076L); - assertThat(t.getContent()).as("uptime.html") - .isEqualTo("17 années, 4 mois, 2 semaines, 1 jour, 6 heures, 45 minutes"); - t.setAttribute(Uptime.class.getName(), 120000L); - assertThat(t.getContent()).as("uptime.html: 2 min").isEqualTo("2 minutes"); + @Test + void creditCardWithEmpty() { + var t = TemplateFactory.TXT.get("formatCreditCard"); + t.setAttribute(TestCase.FOO, ""); + assertThat(t.getContent()).isEmpty(); + } - t = TemplateFactory.JSON.get("uptime"); - t.setAttribute(Uptime.class.getName(), 5999964460000L); - assertThat(t.getContent()).as("uptime.json") - .isEqualTo("190 years 3 months 4 days 47 minutes"); - t.setAttribute(Uptime.class.getName(), 34822860000L); - assertThat(t.getContent()).as("uptime.json: 1 year...") - .isEqualTo("1 year 1 month 1 week 1 day 1 hour 1 minute"); + @Test + void discoverCreditCard() { + var t = TemplateFactory.TXT.get("formatCreditCard"); + t.setAttribute(TestCase.FOO, "6011 1076-8252 0629"); + assertThat(t.getContent()).isEqualTo("0629"); + } - t = TemplateFactory.TXT.get("uptime2"); - t.setAttribute(Uptime.class.getName(), 547800388076L); - assertThat(t.getContent()).as("uptime2.txt").isEqualTo("17YRS-4MOS-2WKS-1D-6H-46M"); + @Test + void invalidCreditCard() { + var t = TemplateFactory.TXT.get("formatCreditCard"); + t.setAttribute(TestCase.FOO, "000000000000001"); + assertThat(t.getContent()).isEmpty(); + } + + @Test + void mastercardCreditCard() { + var t = TemplateFactory.TXT.get("formatCreditCard"); + t.setAttribute(TestCase.FOO, "5130-3899-9169-8324"); + assertThat(t.getContent()).isEqualTo("8324"); + } + + @Test + void visaCreditCard() { + var t = TemplateFactory.TXT.get("formatCreditCard"); + t.setAttribute(TestCase.FOO, "4342 2565 6244 0179"); + assertThat(t.getContent()).isEqualTo("0179"); + } + } + + @Nested + @DisplayName("Uptime Tests") + class UptimeTests { + @Test + void uptime() { + var t = TemplateFactory.TXT.get("uptime"); + assertThat(t.getContent()).isEqualTo("0 minute\n0 minuto\n0 minute"); + } + + @Test + void uptimeInFrench() { + var t = TemplateFactory.HTML.get("uptime"); + t.setAttribute(Uptime.class.getName(), 547800300076L); + assertThat(t.getContent()).isEqualTo("17 années, 4 mois, 2 semaines, 1 jour, 6 heures, 45 minutes"); + } + + @Test + void uptimeInJson() { + var t = TemplateFactory.JSON.get("uptime"); + t.setAttribute(Uptime.class.getName(), 5999964460000L); + assertThat(t.getContent()).isEqualTo("190 years 3 months 4 days 47 minutes"); + } + + @Test + void uptimeInMinutes() { + var t = TemplateFactory.HTML.get("uptime"); + t.setAttribute(Uptime.class.getName(), 120000L); + assertThat(t.getContent()).isEqualTo("2 minutes"); + } + + @Test + void uptimeInMonth() { + var t = TemplateFactory.JSON.get("uptime"); + t.setAttribute(Uptime.class.getName(), 2592000000L); + assertThat(t.getContent()).isEqualTo("1 month"); + } + + @Test + void uptimeInWeeks() { + var t = TemplateFactory.TXT.get("uptime"); + t.setAttribute(Uptime.class.getName(), 1209600000L); + assertThat(t.getContent()).isEqualTo("2 weeks\n2 semanas\n2 weeks"); + } + + @Test + void uptimeWithFormatting() { + var t = TemplateFactory.TXT.get("uptime2"); + t.setAttribute(Uptime.class.getName(), 547800388076L); + assertThat(t.getContent()).isEqualTo("17YRS-4MOS-2WKS-1D-6H-46M"); + } + + @Test + void uptimeWithZero() { + var t = TemplateFactory.JSON.get("uptime"); + t.setAttribute(Uptime.class.getName(), 0L); + assertThat(t.getContent()).isEqualTo("0 minute"); + } } } diff --git a/src/test/java/rife/render/TestRenderUtils.java b/src/test/java/rife/render/TestRenderUtils.java index f1f8c46..0089241 100644 --- a/src/test/java/rife/render/TestRenderUtils.java +++ b/src/test/java/rife/render/TestRenderUtils.java @@ -17,134 +17,431 @@ package rife.render; -import org.assertj.core.api.AutoCloseableSoftAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; import java.util.Properties; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; -class TestRenderUtils { +class +TestRenderUtils { static final String SAMPLE_GERMAN = "Möchten Sie ein paar Äpfel?"; @Test - void testAbbreviate() { - assertThat(RenderUtils.abbreviate(TestCase.SAMPLE_TEXT, 9, "")).as("max=9") - .isEqualTo("This is a"); - - assertThat(RenderUtils.abbreviate(TestCase.SAMPLE_TEXT, 0, "")).as("max=0").isEmpty(); - - assertThat(RenderUtils.abbreviate(TestCase.SAMPLE_TEXT, -1, "")).as("max=-1") - .isEqualTo(TestCase.SAMPLE_TEXT); - - assertThat(RenderUtils.abbreviate("", 10, "")).as("").isEmpty(); + void htmlEntities() { + assertThat(RenderUtils.htmlEntities(SAMPLE_GERMAN)).isEqualTo( + "Möchten Sie ein paar Äpfel?"); } @Test - void testCapitalizeWords() { - assertThat(RenderUtils.capitalizeWords("hello world")).isEqualTo("Hello World"); - assertThat(RenderUtils.capitalizeWords("java programming")).isEqualTo("Java Programming"); - assertThat(RenderUtils.capitalizeWords("TEST")).isEqualTo("Test"); - assertThat(RenderUtils.capitalizeWords("multiple spaces")).isEqualTo("Multiple Spaces"); - assertThat(RenderUtils.capitalizeWords("white\t\fspaces")).isEqualTo("White\t\fSpaces"); - assertThat(RenderUtils.capitalizeWords("")).isEmpty(); - assertThat(RenderUtils.capitalizeWords(null)).isNull(); - } - - @Test - void testEncode() { - var p = new Properties(); - p.put(RenderUtils.ENCODING_PROPERTY, "blah"); - assertThat(RenderUtils.encode(TestCase.SAMPLE_TEXT, p)).as("invalid encoding").isEqualTo(TestCase.SAMPLE_TEXT); - p.put(RenderUtils.ENCODING_PROPERTY, "json"); - assertThat(RenderUtils.encode("This is a \"•test\"", p)).as("json").isEqualTo("This is a \\\"\\u2022test\\\""); - p.put(RenderUtils.ENCODING_PROPERTY, "html"); - assertThat(RenderUtils.encode("", p)).as("html").isEqualTo("<a test &>"); - p.put(RenderUtils.ENCODING_PROPERTY, "js"); - assertThat(RenderUtils.encode("\"test'", p)).as("js").isEqualTo("\\\"test\\'"); - p.put(RenderUtils.ENCODING_PROPERTY, "unicode"); - assertThat(RenderUtils.encode("test", p)).as("unicode").isEqualTo("\\u0074\\u0065\\u0073\\u0074"); - p.put(RenderUtils.ENCODING_PROPERTY, "url"); - assertThat(RenderUtils.encode("a = test", p)).as("url").isEqualTo("a%20%3D%20test"); - p.put(RenderUtils.ENCODING_PROPERTY, "xml"); - assertThat(RenderUtils.encode("Joe's Café & Bar", p)).as("xml").isEqualTo("Joe's Café & Bar"); - } - - @Test - void testEncodeJs() { - assertThat(RenderUtils.encodeJs("")).isEmpty(); - } - - @Test - void testFetchUrl() { - var s = "default"; - assertThat(RenderUtils.fetchUrl("blah", s)).isEqualTo(s); - assertThat(RenderUtils.fetchUrl("https://www.google.com/404", s)).isEqualTo(s); - assertThat(RenderUtils.fetchUrl("https://www.notreallythere.com/", s)).isEqualTo(s); - } - - @Test - void testHtmlEntities() { + void htmlEntitiesWithEmpty() { assertThat(RenderUtils.htmlEntities("")).isEmpty(); - assertThat(RenderUtils.htmlEntities(SAMPLE_GERMAN)) - .isEqualTo("Möchten Sie ein paar Äpfel?"); } @Test - void testMask() { - var foo = "4342256562440179"; - - assertThat(RenderUtils.mask("", " ", 2, false)).isEmpty(); - - assertThat(RenderUtils.mask(foo, "?", 4, false)).as("mask=?") - .isEqualTo("????????????0179"); - - assertThat(RenderUtils.mask(foo, "-", 22, true)).as("unmasked=22") - .isEqualTo("----------------"); - - assertThat(RenderUtils.mask(foo, "•", -1, false)).as("mask=•") - .isEqualTo("••••••••••••••••"); + void qrCode() { + assertThat(RenderUtils.qrCode("erik", "24")).as("svg") + .startsWith("erik"); } @Test - void testNormalize() { - assertThat(RenderUtils.normalize("")).as("empty").isEmpty(); - assertThat(RenderUtils.normalize(" &()-_=[{]}\\|;:,<.>/")).as("blank").isEmpty(); - assertThat(RenderUtils.normalize(SAMPLE_GERMAN)).as("greman").isEqualTo("mochten-sie-ein-paar-apfel"); - assertThat(RenderUtils.normalize("foo bar, ,foo:bar,foo;(bar), {foo} & bar=foo.bar[foo|bar]")) - .as("foo-bar") - .isEqualTo("foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar"); - assertThat(RenderUtils.normalize("News for January 6, 2023 (Paris)")).as("docs example") - .isEqualTo("news-for-january-6-2023-paris"); + void qrCodeWithEmpty() { + assertThat(RenderUtils.qrCode("", "12")).as("empty").isEmpty(); } @Test - void testQrCode() { - assertThat(RenderUtils.qrCode("", "12")).isEmpty(); - } - - @Test - void testRot13() { - var encoded = "Zöpugra Fvr rva cnne Äcsry?"; - assertThat(RenderUtils.rot13("")).isEmpty(); - assertThat(RenderUtils.rot13(SAMPLE_GERMAN)).as("encode").isEqualTo(encoded); - assertThat(RenderUtils.rot13(encoded)).as("decode").isEqualTo(SAMPLE_GERMAN); - } - - @Test - void testSwapCase() { - assertThat(RenderUtils.swapCase("")).isEmpty(); + void swapCase() { assertThat(RenderUtils.swapCase(SAMPLE_GERMAN)).isEqualTo("mÖCHTEN sIE EIN PAAR äPFEL?"); } @Test - void testValidateCreditCard() { - try (var softly = new AutoCloseableSoftAssertions()) { - softly.assertThat(RenderUtils.validateCreditCard("4505 4672 3366 6430")).as("visa").isTrue(); - softly.assertThat(RenderUtils.validateCreditCard("5189-5923-3915-0425")).as("mastercard").isTrue(); - softly.assertThat(RenderUtils.validateCreditCard("3433634926643302")).as("amex").isTrue(); - softly.assertThat(RenderUtils.validateCreditCard("6011 1076-8252 0629")).as("discover").isTrue(); - softly.assertThat(RenderUtils.validateCreditCard("0123456789012345")).as("invalid").isFalse(); + void swapCaseWithEmpty() { + assertThat(RenderUtils.swapCase("")).isEmpty(); + } + + @Nested + @DisplayName("Abbreviate Tests") + class AbbreviateTests { + @Test + void abbreviateWithEllipsis() { + assertThat(RenderUtils.abbreviate(TestCase.SAMPLE_TEXT, 10, "…")).isEqualTo("This is a…"); + } + + @Test + void abbreviateWithEmpty() { + assertThat(RenderUtils.abbreviate("", 10, "")).as("").isEmpty(); + } + + @Test + void abbreviateWithMarker() { + assertThat(RenderUtils.abbreviate(TestCase.SAMPLE_TEXT, 12, "...")).isEqualTo("This is a..."); + } + + @Test + void abbreviateWithMax() { + assertThat(RenderUtils.abbreviate(TestCase.SAMPLE_TEXT, 9, "")).isEqualTo("This is a"); + } + + @Test + void abbreviateWithMaxNegative() { + assertThat(RenderUtils.abbreviate(TestCase.SAMPLE_TEXT, -1, "")) + .isEqualTo(TestCase.SAMPLE_TEXT); + } + + @Test + void abbreviateWithMaxZero() { + assertThat(RenderUtils.abbreviate(TestCase.SAMPLE_TEXT, 0, "")).isEmpty(); + } + } + + @Nested + @DisplayName("Capitalize Tests") + class CapitalizeTests { + @Test + void capitalizeWords() { + assertThat(RenderUtils.capitalizeWords("hello world")).isEqualTo("Hello World"); + } + + @Test + void capitalizeWordsWithEmpty() { + assertThat(RenderUtils.capitalizeWords("")).isEmpty(); + } + + @Test + void capitalizeWordsWithMultipleSpaces() { + assertThat(RenderUtils.capitalizeWords("multiple spaces")).isEqualTo("Multiple Spaces"); + } + + @Test + void capitalizeWordsWithNull() { + assertThat(RenderUtils.capitalizeWords(null)).isNull(); + } + + @Test + void capitalizeWordsWithSpecialCharacters() { + assertThat(RenderUtils.capitalizeWords("white\t\fspaces")).isEqualTo("White\t\fSpaces"); + } + + @Test + void capitalizeWordsWithUppercase() { + assertThat(RenderUtils.capitalizeWords("HELLO World")).isEqualTo("Hello World"); + } + } + + @Nested + @DisplayName("Encode JavaScript Tests") + class EncodeJavaScriptTests { + private static Stream javascriptEscapeTestCases() { + return Stream.of( + Arguments.of("test's", "test\\'s"), + Arguments.of("test\"s", "test\\\"s"), + Arguments.of("test\\s", "test\\\\s"), + Arguments.of("test/s", "test\\/s"), + Arguments.of("test\bs", "test\\bs"), + Arguments.of("test\ns", "test\\ns"), + Arguments.of("test\ts", "test\\ts"), + Arguments.of("test\fs", "test\\fs"), + Arguments.of("test\rs", "test\\rs"), + Arguments.of("a'b\"c\\d/e\bf\ng\th\fi\rj", "a\\'b\\\"c\\\\d\\/e\\bf\\ng\\th\\fi\\rj") + ); + } + + @Test + void encodeJsAllSpecialChars() { + String input = "'\"\\/\b\n\t\f\r"; + String expected = "\\'\\\"\\\\\\/\\b\\n\\t\\f\\r"; + assertThat(RenderUtils.encodeJs(input)).isEqualTo(expected); + } + + @Test + void encodeJsBackslash() { + assertThat(RenderUtils.encodeJs("This is a test \\string\\")) + .isEqualTo("This is a test \\\\string\\\\"); + } + + @Test + void encodeJsBackspace() { + assertThat(RenderUtils.encodeJs("abc\bdef")).isEqualTo("abc\\bdef"); + } + + @Test + void encodeJsBlankInput() { + assertThat(RenderUtils.encodeJs(" ")).isEqualTo(" "); + } + + @Test + void encodeJsCarriageReturn() { + assertThat(RenderUtils.encodeJs("abc\rdef")).isEqualTo("abc\\rdef"); + } + + @Test + void encodeJsConsecutiveSpecialChars() { + assertThat(RenderUtils.encodeJs("''\"\"\\\\")).isEqualTo("\\'\\'\\\"\\\"\\\\\\\\"); + } + + @Test + void encodeJsDoubleQuote() { + assertThat(RenderUtils.encodeJs("This is a test \"string\"")).isEqualTo("This is a test \\\"string\\\""); + } + + @Test + void encodeJsEmptyInput() { + assertThat(RenderUtils.encodeJs("")).isEqualTo(""); + } + + @Test + void encodeJsFormFeed() { + assertThat(RenderUtils.encodeJs("abc\fdef")).isEqualTo("abc\\fdef"); + } + + @Test + void encodeJsForwardSlash() { + assertThat(RenderUtils.encodeJs("This is a test /string/")).isEqualTo("This is a test \\/string\\/"); + } + + @Test + void encodeJsMixedChars() { + String input = "Hello 'World' and \"JavaScript\" with \\slashes/ and \nnewlines."; + String expected = "Hello \\'World\\' and \\\"JavaScript\\\" with \\\\slashes\\/ and \\nnewlines."; + assertThat(RenderUtils.encodeJs(input)).isEqualTo(expected); + } + + @Test + void encodeJsNewline() { + assertThat(RenderUtils.encodeJs("abc\ndef")).isEqualTo("abc\\ndef"); + } + + @Test + void encodeJsNoSpecialChars() { + String input = "Hello World 123!"; + assertThat(input).isEqualTo(RenderUtils.encodeJs(input)); + } + + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = {" ", " ", "\t", "\n"}) + // isBlank() handles these + void encodeJsNullEmptyOrBlankInputs(String input) { + assertThat(RenderUtils.encodeJs(input)).isEqualTo(input); + } + + @Test + void encodeJsNullInput() { + assertThat(RenderUtils.encodeJs(null)).isNull(); + } + + @Test + void encodeJsSingleQuote() { + assertThat(RenderUtils.encodeJs("This is a test 'string'")).isEqualTo("This is a test \\'string\\'"); + } + + @Test + void encodeJsSpecialCharsAtStartAndEnd() { + assertThat(RenderUtils.encodeJs("'test'")).isEqualTo("\\'test\\'"); + assertThat(RenderUtils.encodeJs("\"test\"")).isEqualTo("\\\"test\\\""); + assertThat(RenderUtils.encodeJs("\\test\\")).isEqualTo("\\\\test\\\\"); + } + + @Test + void encodeJsTab() { + assertThat(RenderUtils.encodeJs("abc\tdef")).isEqualTo("abc\\tdef"); + } + + @ParameterizedTest + @MethodSource("javascriptEscapeTestCases") + void encodeJsVariousSpecialCharsParameterized(String input, String expected) { + assertThat(RenderUtils.encodeJs(input)).as("encodeJs(%s,%s)", input, expected).isEqualTo(expected); + } + } + + @Nested + @DisplayName("Encode Tests") + class EncodeTests { + private final Properties p = new Properties(); + + @Test + void encodeHtml() { + p.put(RenderUtils.ENCODING_PROPERTY, "html"); + assertThat(RenderUtils.encode("", p)).isEqualTo("<a test &>"); + } + + @Test + void encodeJs() { + p.put(RenderUtils.ENCODING_PROPERTY, "js"); + assertThat(RenderUtils.encode("\"test'", p)).isEqualTo("\\\"test\\'"); + } + + @Test + void encodeJson() { + p.put(RenderUtils.ENCODING_PROPERTY, "json"); + assertThat(RenderUtils.encode("This is a \"•test\"", p)) + .isEqualTo("This is a \\\"\\u2022test\\\""); + } + + @Test + void encodeUnicode() { + p.put(RenderUtils.ENCODING_PROPERTY, "unicode"); + assertThat(RenderUtils.encode("test", p)).isEqualTo("\\u0074\\u0065\\u0073\\u0074"); + } + + @Test + void encodeUrl() { + p.put(RenderUtils.ENCODING_PROPERTY, "url"); + assertThat(RenderUtils.encode("a = test", p)).isEqualTo("a%20%3D%20test"); + } + + @Test + void encodeWithInvalidFormat() { + p.put(RenderUtils.ENCODING_PROPERTY, "blah"); + assertThat(RenderUtils.encode(TestCase.SAMPLE_TEXT, p)).isEqualTo(TestCase.SAMPLE_TEXT); + } + + @Test + void encodeXml() { + p.put(RenderUtils.ENCODING_PROPERTY, "xml"); + assertThat(RenderUtils.encode("Joe's Café & Bar", p)).isEqualTo("Joe's Café & Bar"); + } + } + + @Nested + @DisplayName("FetchUrl Tests") + class FetchUrlTests { + private static final String DEFAULT = "default"; + + @Test + void fetchUrl() { + assertThat(RenderUtils.fetchUrl("https://postman-echo.com/get?foo=bar", DEFAULT)) + .contains("\"foo\": \"bar\""); + } + + @Test + void fetchUrlWith404() { + assertThat(RenderUtils.fetchUrl("https://www.google.com/404", DEFAULT)).isEqualTo(DEFAULT); + } + + @Test + void fetchUrlWithInvalidHostname() { + assertThat(RenderUtils.fetchUrl("https://www.notreallythere.com/", DEFAULT)).isEqualTo(DEFAULT); + } + + @Test + void fetchUrlWithInvalidUrl() { + assertThat(RenderUtils.fetchUrl("blah", DEFAULT)).isEqualTo(DEFAULT); + } + } + + @Nested + @DisplayName("Mask Tests") + class MaskTests { + public static final String FOO = "4342256562440179"; + + @Test + void maskWithDash() { + assertThat(RenderUtils.mask(FOO, "–", 22, true)) + .isEqualTo("––––––––––––––––"); + } + + @Test + void maskWithEmpty() { + assertThat(RenderUtils.mask("", " ", 2, false)).isEmpty(); + } + + @Test + void maskWithHtmlBuller() { + assertThat(RenderUtils.mask(FOO, "•", -1, false)).isEqualTo( + "••••••••••••••••"); + } + + @Test + void maskWithQuestionMark() { + assertThat(RenderUtils.mask(FOO, "?", 4, false)).as("mask=?") + .isEqualTo("????????????0179"); + } + } + + @Nested + @DisplayName("Normalize Tests") + class NormalizeTests { + @Test + void normalize() { + assertThat(RenderUtils.normalize("News for January 6, 2023 (Paris)")).as("docs example") + .isEqualTo("news-for-january-6-2023-paris"); + } + + @Test + void normalizeWithGerman() { + assertThat(RenderUtils.normalize(SAMPLE_GERMAN)).as("greman") + .isEqualTo("mochten-sie-ein-paar-apfel"); + } + + @Test + void normalizeWithMixedPunctuation() { + assertThat(RenderUtils.normalize(" &()-_=[{]}\\|;:,<.>/")).as("blank").isEmpty(); + } + + @Test + void normalizeWithMixedSeparators() { + assertThat(RenderUtils.normalize("foo bar, ,foo:bar,foo;(bar), {foo} & bar=foo.bar[foo|bar]")) + .as("foo-bar") + .isEqualTo("foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar-foo-bar"); + } + } + + @Nested + @DisplayName("ROT13 Test") + class Rot13Test { + private static final String ENCODED = "Zöpugra Fvr rva cnne Äcsry?"; + + @Test + void rot13Decode() { + assertThat(RenderUtils.rot13(ENCODED)).as("decode").isEqualTo(SAMPLE_GERMAN); + } + + @Test + void rot13Encode() { + assertThat(RenderUtils.rot13(SAMPLE_GERMAN)).as("encode").isEqualTo(ENCODED); + } + + @Test + void rot13WithEmpty() { + assertThat(RenderUtils.rot13("")).isEmpty(); + } + } + + @Nested + @DisplayName("Validate Credit Card") + class ValidateCreditCard { + @Test + void amexCreditCard() { + assertThat(RenderUtils.validateCreditCard("3433634926643302")).as("amex").isTrue(); + } + + @Test + void discoverCreditCard() { + assertThat(RenderUtils.validateCreditCard("6011 1076-8252 0629")).as("discover").isTrue(); + } + + @Test + void invalidCreditCard() { + assertThat(RenderUtils.validateCreditCard("0123456789012345")).as("invalid").isFalse(); + } + + @Test + void mastercardCreditCard() { + assertThat(RenderUtils.validateCreditCard("5189-5923-3915-0425")).as("mastercard").isTrue(); + } + + @Test + void visaCreditCard() { + assertThat(RenderUtils.validateCreditCard("4505 4672 3366 6430")).as("visa").isTrue(); } } } diff --git a/src/test/resources/templates/uptime.txt b/src/test/resources/templates/uptime.txt index 826ae12..aa287df 100644 --- a/src/test/resources/templates/uptime.txt +++ b/src/test/resources/templates/uptime.txt @@ -1,6 +1,8 @@ {{v render:rife.render.Uptime/}} {{v render:rife.render.Uptime:spanish}} minute=\ minuto -minutes=\ minutos +minutes=\ minutos\ +week=\ week +weeks=\ semanas {{/v}} {{v render:rife.render.Uptime:plain}}{{/v}} \ No newline at end of file