🔥 Elimina Render para usar siempre el contexto

This commit is contained in:
Manuel Cillero 2025-09-11 19:58:50 +02:00
parent ddf78c2de8
commit e3ca6079ff
12 changed files with 205 additions and 146 deletions

View file

@ -8,10 +8,10 @@ async fn poweredby_default_shows_only_pagetop_recognition() {
let html = render_component(&p);
// Debe mostrar el bloque de reconocimiento a PageTop.
assert!(html.contains("poweredby__pagetop"));
assert!(html.as_str().contains("poweredby__pagetop"));
// Y NO debe mostrar el bloque de copyright.
assert!(!html.contains("poweredby__copyright"));
assert!(!html.as_str().contains("poweredby__copyright"));
}
#[pagetop::test]
@ -22,17 +22,20 @@ async fn poweredby_new_includes_current_year_and_app_name() {
let html = render_component(&p);
let year = Utc::now().format("%Y").to_string();
assert!(html.contains(&year), "HTML should include the current year");
assert!(
html.as_str().contains(&year),
"HTML should include the current year"
);
// El nombre de la app proviene de `global::SETTINGS.app.name`.
let app_name = &global::SETTINGS.app.name;
assert!(
html.contains(app_name),
html.as_str().contains(app_name),
"HTML should include the application name"
);
// Debe existir el span de copyright.
assert!(html.contains("poweredby__copyright"));
assert!(html.as_str().contains("poweredby__copyright"));
}
#[pagetop::test]
@ -43,8 +46,8 @@ async fn poweredby_with_copyright_overrides_text() {
let p = PoweredBy::default().with_copyright(Some(custom));
let html = render_component(&p);
assert!(html.contains(custom));
assert!(html.contains("poweredby__copyright"));
assert!(html.as_str().contains(custom));
assert!(html.as_str().contains("poweredby__copyright"));
}
#[pagetop::test]
@ -54,9 +57,9 @@ async fn poweredby_with_copyright_none_hides_text() {
let p = PoweredBy::new().with_copyright(None::<String>);
let html = render_component(&p);
assert!(!html.contains("poweredby__copyright"));
assert!(!html.as_str().contains("poweredby__copyright"));
// El reconocimiento a PageTop siempre debe aparecer.
assert!(html.contains("poweredby__pagetop"));
assert!(html.as_str().contains("poweredby__pagetop"));
}
#[pagetop::test]
@ -67,7 +70,7 @@ async fn poweredby_link_points_to_crates_io() {
let html = render_component(&p);
assert!(
html.contains("https://pagetop.cillero.es"),
html.as_str().contains("https://pagetop.cillero.es"),
"Link should point to pagetop.cillero.es"
);
}
@ -89,12 +92,8 @@ async fn poweredby_getter_reflects_internal_state() {
// HELPERS *****************************************************************************************
fn render(x: &impl Render) -> String {
x.render().into_string()
}
fn render_component<C: Component>(c: &C) -> String {
fn render_component<C: Component>(c: &C) -> Markup {
let mut cx = Context::default();
let pm = c.prepare_component(&mut cx);
render(&pm)
pm.render()
}

View file

@ -2,19 +2,19 @@ use pagetop::prelude::*;
#[pagetop::test]
async fn prepare_markup_render_none_is_empty_string() {
assert_eq!(render(&PrepareMarkup::None), "");
assert_eq!(PrepareMarkup::None.render().as_str(), "");
}
#[pagetop::test]
async fn prepare_markup_render_escaped_escapes_html_and_ampersands() {
let pm = PrepareMarkup::Escaped(String::from("<b>& \" ' </b>"));
assert_eq!(render(&pm), "&lt;b&gt;&amp; &quot; ' &lt;/b&gt;");
let pm = PrepareMarkup::Escaped("<b>& \" ' </b>".to_string());
assert_eq!(pm.render().as_str(), "&lt;b&gt;&amp; &quot; ' &lt;/b&gt;");
}
#[pagetop::test]
async fn prepare_markup_render_raw_is_inserted_verbatim() {
let pm = PrepareMarkup::Raw(String::from("<b>bold</b><script>1<2</script>"));
assert_eq!(render(&pm), "<b>bold</b><script>1<2</script>");
let pm = PrepareMarkup::Raw("<b>bold</b><script>1<2</script>".to_string());
assert_eq!(pm.render().as_str(), "<b>bold</b><script>1<2</script>");
}
#[pagetop::test]
@ -24,7 +24,7 @@ async fn prepare_markup_render_with_keeps_structure() {
p { "This is a paragraph." }
});
assert_eq!(
render(&pm),
pm.render().as_str(),
"<h2>Sample title</h2><p>This is a paragraph.</p>"
);
}
@ -33,7 +33,7 @@ async fn prepare_markup_render_with_keeps_structure() {
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("<i>x</i>".into());
let wrapped_escaped = html! { div { (escaped) } };
let wrapped_escaped = html! { div { (escaped.render()) } };
assert_eq!(
wrapped_escaped.into_string(),
"<div>&lt;i&gt;x&lt;/i&gt;</div>"
@ -41,12 +41,12 @@ async fn prepare_markup_does_not_double_escape_when_wrapped_in_html_macro() {
// Raw: tampoco debe escaparse al integrarlo.
let raw = PrepareMarkup::Raw("<i>x</i>".into());
let wrapped_raw = html! { div { (raw) } };
let wrapped_raw = html! { div { (raw.render()) } };
assert_eq!(wrapped_raw.into_string(), "<div><i>x</i></div>");
// With: debe incrustar el Markup tal cual.
let with = PrepareMarkup::With(html! { span.title { "ok" } });
let wrapped_with = html! { div { (with) } };
let wrapped_with = html! { div { (with.render()) } };
assert_eq!(
wrapped_with.into_string(),
"<div><span class=\"title\">ok</span></div>"
@ -57,11 +57,14 @@ async fn prepare_markup_does_not_double_escape_when_wrapped_in_html_macro() {
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 ☕ &amp; donuts!");
assert_eq!(
esc.render().as_str(),
"Hello, tomorrow coffee ☕ &amp; donuts!"
);
// Raw debe pasar íntegro.
let raw = PrepareMarkup::Raw("Title — section © 2025".into());
assert_eq!(render(&raw), "Title — section © 2025");
assert_eq!(raw.render().as_str(), "Title — section © 2025");
}
#[pagetop::test]
@ -69,11 +72,11 @@ async fn prepare_markup_is_empty_semantics() {
assert!(PrepareMarkup::None.is_empty());
assert!(PrepareMarkup::Escaped(String::new()).is_empty());
assert!(PrepareMarkup::Escaped(String::from("")).is_empty());
assert!(!PrepareMarkup::Escaped(String::from("x")).is_empty());
assert!(PrepareMarkup::Escaped("".to_string()).is_empty());
assert!(!PrepareMarkup::Escaped("x".to_string()).is_empty());
assert!(PrepareMarkup::Raw(String::new()).is_empty());
assert!(PrepareMarkup::Raw(String::from("")).is_empty());
assert!(PrepareMarkup::Raw("".to_string()).is_empty());
assert!(!PrepareMarkup::Raw("a".into()).is_empty());
assert!(PrepareMarkup::With(html! {}).is_empty());
@ -94,17 +97,12 @@ async fn prepare_markup_equivalence_between_render_and_inline_in_html_macro() {
];
for pm in cases {
let rendered = render(&pm);
let in_macro = html! { (pm) }.into_string();
let rendered = pm.render();
let in_macro = html! { (rendered) }.into_string();
assert_eq!(
rendered, in_macro,
rendered.as_str(),
in_macro,
"The output of Render and (pm) inside html! must match"
);
}
}
// HELPERS *****************************************************************************************
fn render(x: &impl Render) -> String {
x.render().into_string()
}