From cbb1338b59b1432f10a63bae5c16000f77ef6695 Mon Sep 17 00:00:00 2001 From: Paulo Afonso Carvalho Date: Sat, 7 Feb 2026 11:47:46 +0000 Subject: [PATCH 1/3] Add Span as a valid HtmlPage root element and root builder extensions for div, tr, and span, with tests --- .../src/main/java/htmlflow/HtmlPage.java | 8 +- .../kotlin/htmlflow/HtmlFlowExtensions.kt | 14 +- .../test/TestKotlinExtensionsOnPartials.kt | 205 +++++++++++++++++- 3 files changed, 215 insertions(+), 12 deletions(-) diff --git a/htmlflow-core/src/main/java/htmlflow/HtmlPage.java b/htmlflow-core/src/main/java/htmlflow/HtmlPage.java index d9d6e508..cd2ef858 100644 --- a/htmlflow-core/src/main/java/htmlflow/HtmlPage.java +++ b/htmlflow-core/src/main/java/htmlflow/HtmlPage.java @@ -33,10 +33,8 @@ import java.io.InputStreamReader; import java.io.UncheckedIOException; import java.net.URL; -import org.xmlet.htmlapifaster.Div; -import org.xmlet.htmlapifaster.Element; -import org.xmlet.htmlapifaster.Html; -import org.xmlet.htmlapifaster.Tr; + +import org.xmlet.htmlapifaster.*; /** * The root container for HTML elements. It is responsible for managing the {@code @@ -85,6 +83,8 @@ public final Tr tr() { return new Tr<>(this); } + public final Span span() {return new Span<>(this);} + /** * Returns a new instance of HtmlFlow with the same properties of this object but with indented * set to the value of isIndented parameter. diff --git a/htmlflow-kotlin/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt b/htmlflow-kotlin/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt index 97449d4b..53056fdf 100644 --- a/htmlflow-kotlin/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt +++ b/htmlflow-kotlin/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt @@ -4,9 +4,12 @@ package htmlflow import htmlflow.continuations.HtmlContinuationSuspendableTerminationNode import htmlflow.visitor.HtmlVisitor import htmlflow.visitor.PreprocessingVisitor +import org.xmlet.htmlapifaster.Div import org.xmlet.htmlapifaster.Element import org.xmlet.htmlapifaster.Html +import org.xmlet.htmlapifaster.Span import org.xmlet.htmlapifaster.Text +import org.xmlet.htmlapifaster.Tr /** Alternative close tag function for `__()`. */ inline val , Z : Element<*, *>> T.l: Z @@ -20,10 +23,13 @@ inline val HtmlPage.html: Html } /** Root builder of HTML element with lambda with receiver. */ -inline fun HtmlPage.html(block: Html.() -> Unit): HtmlPage { - (this.visitor as HtmlVisitor).write(HtmlPage.HEADER) - return Html(self()).also { it.block() }.l -} +inline fun HtmlPage.html(block: Html.() -> Unit): HtmlPage = this.html().also(block).l + +inline fun HtmlPage.div(block: Div.() -> Unit): HtmlPage = this.div().apply(block).l + +inline fun HtmlPage.tr(block: Tr.() -> Unit): HtmlPage = this.tr().apply(block).l + +inline fun HtmlPage.span(block: Span.() -> Unit): HtmlPage = this.span().apply(block).l /** Text node property. */ inline var , Z : Element<*, *>> T.text: T diff --git a/htmlflow-kotlin/src/test/kotlin/htmlflow/test/TestKotlinExtensionsOnPartials.kt b/htmlflow-kotlin/src/test/kotlin/htmlflow/test/TestKotlinExtensionsOnPartials.kt index 813aad9c..b7a619d7 100644 --- a/htmlflow-kotlin/src/test/kotlin/htmlflow/test/TestKotlinExtensionsOnPartials.kt +++ b/htmlflow-kotlin/src/test/kotlin/htmlflow/test/TestKotlinExtensionsOnPartials.kt @@ -1,6 +1,7 @@ package htmlflow.test import htmlflow.* +import htmlflow.HtmlFlow.doc import htmlflow.test.model.Track import kotlinx.coroutines.reactive.asFlow import kotlinx.coroutines.runBlocking @@ -13,7 +14,6 @@ import java.time.Duration import java.time.LocalDate import java.util.* - /** * These tests do not contain any assertion because they are only a sample for README.md * and HtmlFlow site examples. @@ -82,7 +82,7 @@ class TestKotlinExtensionsOnPartials { actual.trackDoc(spaceOddity) assertEquals(actual.toString(), trackView.render(spaceOddity)) // trackView.setOut(System.out).write(spaceOddity); - } + } /** * Sample showcase of loop with HtmlDoc @@ -275,5 +275,202 @@ class TestKotlinExtensionsOnPartials { // view.setOut(System.out).write(Owner("Ze Manel", "Rua da Alfandega")) } - class Owner(val name: String, val address: String) -} \ No newline at end of file + class Owner(val name: String, val address: String) + + @Test + fun testThatDivElementCanBeUsedOnHtmlDocAndHtmlView() { + val mainMenuDoc = + StringBuilder() + .apply { + doc { + div { + attrId("main-menu") + a { + attrHref("/series") + text("My Series") + } + a { + attrHref("/movies") + text("My Movies") + } + a { + attrHref("/musics") + text("My Musics") + } + } + } + }.toString() + + assertEquals(expectedMainMenuDoc, mainMenuDoc) + + val favoriteMovies = + listOf( + Movie("Inception", "Christopher Nolan", 2010, "Sci-Fi"), + Movie("The Godfather", "Francis Ford Coppola", 1972, "Crime"), + Movie("Pulp Fiction", "Quentin Tarantino", 1994, "Crime"), + ) + + val favoriteMoviesView = + view> { + div { + attrId("favorite-movies") + dyn { movies: List -> + movies.forEach { movie -> + div { + p { text("Movie: ${movie.title}") } + p { text("Director: ${movie.director}") } + p { text("Release Year: ${movie.releaseYear}") } + p { text("Genre: ${movie.genre}") } + } + } + } + } + }.render(favoriteMovies) + + assertEquals(expectedFavoriteMoviesView, favoriteMoviesView) + } + + data class Movie( + val title: String, + val director: String, + val releaseYear: Int, + val genre: String, + ) + + @Test + fun testThatTrElementCanBeUsedOnHtmlDoc() { + val newAgentRow = + view{ + tr { + td { + dyn { agent: Agent -> + text(agent.name) + } + } + td { + dyn { agent: Agent -> + text(agent.email) + } + } + td { + dyn { agent: Agent -> + text(agent.id) + } + } + } + }.render(Agent("Agent Smith 0", "void0@null.org", "0")) + + assertEquals(expectedAgentRow, newAgentRow) + } + +data class Agent( + val name: String, + val email: String, + val id: String, +) + @Test + fun testThatSpanElementCanBeUsedOnHtmlView() { + + val counter1 = Counter(0) + val counter2 = Counter(1) + val counterSpans = + StringBuilder() + .apply { + doc { + span { + text("The counter 1 has the value ${counter1.count}") + } + span { + text("The counter 2 has the value ${counter2.count}") + } + } + }.toString() + + assertEquals(expectedCounterSpans, counterSpans) + } + data class Counter(val count: Int) + +} + +private const val expectedFavoriteMoviesView = + """
+
+

+ Movie: Inception +

+

+ Director: Christopher Nolan +

+

+ Release Year: 2010 +

+

+ Genre: Sci-Fi +

+
+
+

+ Movie: The Godfather +

+

+ Director: Francis Ford Coppola +

+

+ Release Year: 1972 +

+

+ Genre: Crime +

+
+
+

+ Movie: Pulp Fiction +

+

+ Director: Quentin Tarantino +

+

+ Release Year: 1994 +

+

+ Genre: Crime +

+
+
""" + +private const val expectedMainMenuDoc = +""" +""" + +private const val expectedAgentRow = +""" + + Agent Smith 0 + + + void0@null.org + + + 0 + +""" + + +private val expectedCounterSpans = +""" + + The counter 1 has the value 0 + + + The counter 2 has the value 1 +""" \ No newline at end of file From 1ffd09a911fd9a536fe818223ab865d982d4917c Mon Sep 17 00:00:00 2001 From: Paulo Afonso Carvalho Date: Sun, 8 Feb 2026 12:01:11 +0000 Subject: [PATCH 2/3] Refactor html extension functions and unit test --- .../main/kotlin/htmlflow/HtmlFlowExtensions.kt | 2 +- .../test/TestKotlinExtensionsOnPartials.kt | 18 ++++-------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/htmlflow-kotlin/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt b/htmlflow-kotlin/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt index 53056fdf..dbdc0937 100644 --- a/htmlflow-kotlin/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt +++ b/htmlflow-kotlin/src/main/kotlin/htmlflow/HtmlFlowExtensions.kt @@ -23,7 +23,7 @@ inline val HtmlPage.html: Html } /** Root builder of HTML element with lambda with receiver. */ -inline fun HtmlPage.html(block: Html.() -> Unit): HtmlPage = this.html().also(block).l +inline fun HtmlPage.html(block: Html.() -> Unit): HtmlPage = this.html().apply(block).l inline fun HtmlPage.div(block: Div.() -> Unit): HtmlPage = this.div().apply(block).l diff --git a/htmlflow-kotlin/src/test/kotlin/htmlflow/test/TestKotlinExtensionsOnPartials.kt b/htmlflow-kotlin/src/test/kotlin/htmlflow/test/TestKotlinExtensionsOnPartials.kt index b7a619d7..717f5e28 100644 --- a/htmlflow-kotlin/src/test/kotlin/htmlflow/test/TestKotlinExtensionsOnPartials.kt +++ b/htmlflow-kotlin/src/test/kotlin/htmlflow/test/TestKotlinExtensionsOnPartials.kt @@ -342,20 +342,10 @@ class TestKotlinExtensionsOnPartials { val newAgentRow = view{ tr { - td { - dyn { agent: Agent -> - text(agent.name) - } - } - td { - dyn { agent: Agent -> - text(agent.email) - } - } - td { - dyn { agent: Agent -> - text(agent.id) - } + dyn { agent: Agent -> + td { text(agent.name) } + td { text(agent.email) } + td { text(agent.id) } } } }.render(Agent("Agent Smith 0", "void0@null.org", "0")) From 27daf51143d951fea01efc68e8fdd349ecb63882 Mon Sep 17 00:00:00 2001 From: Paulo Afonso Carvalho Date: Sun, 8 Feb 2026 16:06:05 +0000 Subject: [PATCH 3/3] Remove test testFlowifierTuerSourceforgeHomepage from Flowifiers tests, replace wildcard import to import in HtmlPage --- .../test/java/htmlflow/flowifier/FlowifierTest.java | 5 ----- htmlflow-core/src/main/java/htmlflow/HtmlPage.java | 11 ++++++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/flowifier/src/test/java/htmlflow/flowifier/FlowifierTest.java b/flowifier/src/test/java/htmlflow/flowifier/FlowifierTest.java index 09a20563..e744f1d8 100644 --- a/flowifier/src/test/java/htmlflow/flowifier/FlowifierTest.java +++ b/flowifier/src/test/java/htmlflow/flowifier/FlowifierTest.java @@ -60,11 +60,6 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) { } } - @Test - public void testFlowifierTuerSourceforgeHomepage() throws Exception { - testFlowifier("https://tuer.sourceforge.io/en/"); - } - @Test public void testFlowifierWithGamboa() throws Exception { testFlowifier("https://gamboa.pt/"); diff --git a/htmlflow-core/src/main/java/htmlflow/HtmlPage.java b/htmlflow-core/src/main/java/htmlflow/HtmlPage.java index cd2ef858..c03a4e63 100644 --- a/htmlflow-core/src/main/java/htmlflow/HtmlPage.java +++ b/htmlflow-core/src/main/java/htmlflow/HtmlPage.java @@ -33,8 +33,11 @@ import java.io.InputStreamReader; import java.io.UncheckedIOException; import java.net.URL; - -import org.xmlet.htmlapifaster.*; +import org.xmlet.htmlapifaster.Element; +import org.xmlet.htmlapifaster.Html; +import org.xmlet.htmlapifaster.Tr; +import org.xmlet.htmlapifaster.Div; +import org.xmlet.htmlapifaster.Span; /** * The root container for HTML elements. It is responsible for managing the {@code @@ -83,7 +86,9 @@ public final Tr tr() { return new Tr<>(this); } - public final Span span() {return new Span<>(this);} + public final Span span() { + return new Span<>(this); + } /** * Returns a new instance of HtmlFlow with the same properties of this object but with indented