value");
+ }
+
+ public record Center(String value) {}
+}
diff --git a/html/src/test/java/org/kohsuke/stapler/html/IncludesTest.java b/html/src/test/java/org/kohsuke/stapler/html/IncludesTest.java
new file mode 100644
index 000000000..55eae3250
--- /dev/null
+++ b/html/src/test/java/org/kohsuke/stapler/html/IncludesTest.java
@@ -0,0 +1,31 @@
+package org.kohsuke.stapler.html;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+public final class IncludesTest extends HtmlTestCase {
+
+ public void testIncludes() throws Exception {
+ assertThat(
+ load("top"),
+ is(
+ " A prologue. There are 23 items. An epilogue.
"));
+ }
+
+ @HtmlView("top")
+ public Top getTop() {
+ return new Top(new Nested());
+ }
+
+ public record Top(@HtmlInclude("center") Nested nested) {}
+
+ public static final class Nested {
+
+ @HtmlView("center")
+ public Center getCenter() {
+ return new Center("23");
+ }
+
+ public record Center(String count) {}
+ }
+}
diff --git a/html/src/test/java/org/kohsuke/stapler/html/StructureTest.java b/html/src/test/java/org/kohsuke/stapler/html/StructureTest.java
new file mode 100644
index 000000000..646653836
--- /dev/null
+++ b/html/src/test/java/org/kohsuke/stapler/html/StructureTest.java
@@ -0,0 +1,59 @@
+package org.kohsuke.stapler.html;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+
+import edu.umd.cs.findbugs.annotations.CheckForNull;
+import java.util.List;
+
+public final class StructureTest extends HtmlTestCase {
+
+ Status status;
+
+ public void testStandalone() throws Exception {
+ status = new Status(
+ "Something #123",
+ null,
+ false,
+ new Status.Items(
+ List.of(new Status.Items.Element("k1", "v1"), new Status.Items.Element("k2", "v2 &c."))));
+ assertThat(
+ load("standalone"),
+ allOf(
+ containsString("Something #123"),
+ containsString("All items:
"
+ + "| Initial name | Initial value |
"
+ + " | k1 | v1 |
| k2 | v2 &c. |
"
+ + "| Penultimate name | Penultimate value |
"
+ + "| Final name | Final value |
"
+ + "
"),
+ not(containsString("Error in")),
+ not(containsString("There are"))));
+ status = new Status("Something #456", new Status.Warning("rotor"), true, null);
+ assertThat(
+ load("standalone"),
+ allOf(
+ containsString("Something #456"),
+ not(containsString("All items:")),
+ containsString("Error in rotor."),
+ containsString("
There are no items.")));
+ }
+
+ @HtmlView("standalone")
+ public Status getStandalone() throws Exception {
+ return status;
+ }
+
+ public record Status(
+ String displayName, @CheckForNull Warning warning, boolean empty, @CheckForNull Items nonempty) {
+
+ public record Warning(String component) {}
+
+ public record Items(List elements) {
+
+ public record Element(String name, String value) {}
+ }
+ }
+}
diff --git a/html/src/test/resources/org/kohsuke/stapler/html/IncludedFromTest/center.xhtml b/html/src/test/resources/org/kohsuke/stapler/html/IncludedFromTest/center.xhtml
new file mode 100644
index 000000000..a6777fa92
--- /dev/null
+++ b/html/src/test/resources/org/kohsuke/stapler/html/IncludedFromTest/center.xhtml
@@ -0,0 +1,5 @@
+
+
+
+ Center text includes a value.
+
diff --git a/html/src/test/resources/org/kohsuke/stapler/html/IncludedFromTest/top.jelly b/html/src/test/resources/org/kohsuke/stapler/html/IncludedFromTest/top.jelly
new file mode 100644
index 000000000..e7a589fb8
--- /dev/null
+++ b/html/src/test/resources/org/kohsuke/stapler/html/IncludedFromTest/top.jelly
@@ -0,0 +1,7 @@
+
+
+
+ A prologue.
+
+ An epilogue.
+
diff --git a/html/src/test/resources/org/kohsuke/stapler/html/IncludesTest/Nested/center.xhtml b/html/src/test/resources/org/kohsuke/stapler/html/IncludesTest/Nested/center.xhtml
new file mode 100644
index 000000000..d5197bf44
--- /dev/null
+++ b/html/src/test/resources/org/kohsuke/stapler/html/IncludesTest/Nested/center.xhtml
@@ -0,0 +1,4 @@
+
+
+ There are 1234 items.
+
diff --git a/html/src/test/resources/org/kohsuke/stapler/html/IncludesTest/top.xhtml b/html/src/test/resources/org/kohsuke/stapler/html/IncludesTest/top.xhtml
new file mode 100644
index 000000000..0191811e2
--- /dev/null
+++ b/html/src/test/resources/org/kohsuke/stapler/html/IncludesTest/top.xhtml
@@ -0,0 +1,6 @@
+
+
+ A prologue.
+ sample interior text
+ An epilogue.
+
diff --git a/html/src/test/resources/org/kohsuke/stapler/html/StructureTest/standalone.xhtml b/html/src/test/resources/org/kohsuke/stapler/html/StructureTest/standalone.xhtml
new file mode 100644
index 000000000..d22d0e994
--- /dev/null
+++ b/html/src/test/resources/org/kohsuke/stapler/html/StructureTest/standalone.xhtml
@@ -0,0 +1,27 @@
+
+
+
+
+ XXX #123
+
+
+
+ Error in some component.
+
+ There are no items.
+
+
All items:
+
+
+ | Initial name | Initial value |
+
+
+ | name |
+ value |
+
+ | Penultimate name | Penultimate value |
+ | Final name | Final value |
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 5510665b2..70672521f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,7 @@
jsp
jelly
groovy
+ html