diff --git a/tests/html.rs b/tests/html.rs
index 315f74a..1499c70 100644
--- a/tests/html.rs
+++ b/tests/html.rs
@@ -1,17 +1,110 @@
use pagetop::prelude::*;
#[pagetop::test]
-async fn prepare_markup_is_empty() {
- let _app = service::test::init_service(Application::new().test()).await;
+async fn prepare_markup_render_none_is_empty_string() {
+ assert_eq!(render(&PrepareMarkup::None), "");
+}
+#[pagetop::test]
+async fn prepare_markup_render_escaped_escapes_html_and_ampersands() {
+ let pm = PrepareMarkup::Escaped(String::from("& \" ' "));
+ assert_eq!(render(&pm), "<b>& " ' </b>");
+}
+
+#[pagetop::test]
+async fn prepare_markup_render_raw_is_inserted_verbatim() {
+ let pm = PrepareMarkup::Raw(String::from("bold"));
+ assert_eq!(render(&pm), "bold");
+}
+
+#[pagetop::test]
+async fn prepare_markup_render_with_keeps_structure() {
+ let pm = PrepareMarkup::With(html! {
+ h2 { "Sample title" }
+ p { "This is a paragraph." }
+ });
+ assert_eq!(
+ render(&pm),
+ "
Sample title
This is a paragraph.
"
+ );
+}
+
+#[pagetop::test]
+async fn prepare_markup_does_not_double_escape_when_wrapped_in_html_macro() {
+ // Escaped: dentro de `html!` no debe volver a escaparse.
+ let escaped = PrepareMarkup::Escaped("x".into());
+ let wrapped_escaped = html! { div { (escaped) } };
+ assert_eq!(
+ wrapped_escaped.into_string(),
+ "<i>x</i>
"
+ );
+
+ // Raw: tampoco debe escaparse al integrarlo.
+ let raw = PrepareMarkup::Raw("x".into());
+ let wrapped_raw = html! { div { (raw) } };
+ assert_eq!(wrapped_raw.into_string(), "x
");
+
+ // With: debe incrustar el Markup tal cual.
+ let with = PrepareMarkup::With(html! { span.title { "ok" } });
+ let wrapped_with = html! { div { (with) } };
+ assert_eq!(
+ wrapped_with.into_string(),
+ "ok
"
+ );
+}
+
+#[pagetop::test]
+async fn prepare_markup_unicode_is_preserved() {
+ // Texto con acentos y emojis debe conservarse (salvo el escape HTML de signos).
+ let esc = PrepareMarkup::Escaped("Hello, tomorrow coffee ☕ & donuts!".into());
+ assert_eq!(render(&esc), "Hello, tomorrow coffee ☕ & donuts!");
+
+ // Raw debe pasar íntegro.
+ let raw = PrepareMarkup::Raw("Title — section © 2025".into());
+ assert_eq!(render(&raw), "Title — section © 2025");
+}
+
+#[pagetop::test]
+async fn prepare_markup_is_empty_semantics() {
assert!(PrepareMarkup::None.is_empty());
- assert!(PrepareMarkup::Text(String::from("")).is_empty());
- assert!(!PrepareMarkup::Text(String::from("x")).is_empty());
-
assert!(PrepareMarkup::Escaped(String::new()).is_empty());
- assert!(!PrepareMarkup::Escaped("a".into()).is_empty());
+ assert!(PrepareMarkup::Escaped(String::from("")).is_empty());
+ assert!(!PrepareMarkup::Escaped(String::from("x")).is_empty());
+
+ assert!(PrepareMarkup::Raw(String::new()).is_empty());
+ assert!(PrepareMarkup::Raw(String::from("")).is_empty());
+ assert!(!PrepareMarkup::Raw("a".into()).is_empty());
assert!(PrepareMarkup::With(html! {}).is_empty());
assert!(!PrepareMarkup::With(html! { span { "!" } }).is_empty());
+
+ // Ojo: espacios NO deberían considerarse vacíos (comportamiento actual).
+ assert!(!PrepareMarkup::Escaped(" ".into()).is_empty());
+ assert!(!PrepareMarkup::Raw(" ".into()).is_empty());
+}
+
+#[pagetop::test]
+async fn prepare_markup_equivalence_between_render_and_inline_in_html_macro() {
+ let cases = [
+ PrepareMarkup::None,
+ PrepareMarkup::Escaped("x".into()),
+ PrepareMarkup::Raw("x".into()),
+ PrepareMarkup::With(html! { b { "x" } }),
+ ];
+
+ for pm in cases {
+ let rendered = render(&pm);
+ let in_macro = html! { (pm) }.into_string();
+ assert_eq!(
+ rendered, in_macro,
+ "The output of Render and (pm) inside html! must match"
+ );
+ }
+}
+
+// HELPERS *****************************************************************************************
+
+fn render(x: &impl Render) -> String {
+ x.render().into_string()
}