From c2ec7568f086105bea23b2a6935afd77d7b0f84e Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sun, 13 Sep 2026 01:46:21 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20(pagetop):=20A=C3=B1ade=20tests=20d?= =?UTF-8?q?e=20Margin=20y=20Padding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/html_props_spacing.rs | 183 ++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 tests/html_props_spacing.rs diff --git a/tests/html_props_spacing.rs b/tests/html_props_spacing.rs new file mode 100644 index 00000000..f99b3e5c --- /dev/null +++ b/tests/html_props_spacing.rs @@ -0,0 +1,183 @@ +use pagetop::prelude::*; + +#[pagetop::test] +async fn default_margin_adds_nothing() { + let mut cx = Context::default(); + let props = Props::default().with_prop(Margin::new()); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + + assert_eq!(html, ""); + assert!(cx.render_assets().into_string().is_empty()); +} + +#[pagetop::test] +async fn default_padding_adds_nothing() { + let mut cx = Context::default(); + let props = Props::default().with_prop(Padding::new()); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + + assert_eq!(html, ""); + assert!(cx.render_assets().into_string().is_empty()); +} + +#[pagetop::test] +async fn margin_top_adds_matching_style() { + let mut cx = Context::default(); + let props = Props::default().with_prop(Margin::new().with_top(UnitValue::RelRem(1.5))); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + let assets = cx.render_assets().into_string(); + + assert!(html.contains(r#"class="_margin-top_1_5rem_""#)); + assert!(assets.contains("_margin-top_1_5rem_{margin-top:1.5rem}")); +} + +#[pagetop::test] +async fn margin_x_sets_start_and_end() { + let mut cx = Context::default(); + let props = Props::default().with_prop(Margin::new().with_x(UnitValue::Auto)); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + let assets = cx.render_assets().into_string(); + + assert!(html.contains(r#"class="_margin-start_auto_ _margin-end_auto_""#)); + assert!(assets.contains("_margin-start_auto_{margin-inline-start:auto}")); + assert!(assets.contains("_margin-end_auto_{margin-inline-end:auto}")); +} + +#[pagetop::test] +async fn margin_y_sets_top_and_bottom() { + let mut cx = Context::default(); + let props = Props::default().with_prop(Margin::new().with_y(UnitValue::Zero)); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + let assets = cx.render_assets().into_string(); + + assert!(html.contains(r#"class="_margin-top_0_ _margin-bottom_0_""#)); + assert!(assets.contains("_margin-top_0_{margin-top:0}")); + assert!(assets.contains("_margin-bottom_0_{margin-bottom:0}")); +} + +#[pagetop::test] +async fn margin_all_sets_every_side() { + let mut cx = Context::default(); + let props = Props::default().with_prop(Margin::new().with_all(UnitValue::Px(8))); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + + assert!(html.contains( + r#"class="_margin-top_8px_ _margin-bottom_8px_ _margin-start_8px_ _margin-end_8px_""# + )); +} + +#[pagetop::test] +async fn margin_at_a_breakpoint_combines_token_and_suffix() { + let mut cx = Context::default(); + let props = + Props::default().with_prop(Margin::new().with_top_at(Breakpoint::Md, UnitValue::Px(16))); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + let assets = cx.render_assets().into_string(); + + assert!(html.contains(r#"class="_margin-top_16px_md_""#)); + assert!(assets.contains("@media(min-width:768px){._margin-top_16px_md_{margin-top:16px}}")); +} + +#[pagetop::test] +async fn successive_with_prop_calls_merge_margin_sides() { + let mut cx = Context::default(); + let props = Props::default() + .with_prop(Margin::new().with_top(UnitValue::Px(8))) + .with_prop(Margin::new().with_bottom(UnitValue::Px(16))); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + + // Each `with_prop()` only sets one side; `Margin::merge()` keeps both instead of letting the + // second call overwrite the first one entirely. + assert!(html.contains(r#"class="_margin-top_8px_ _margin-bottom_16px_""#)); +} + +#[pagetop::test] +async fn later_with_prop_call_overrides_same_side() { + let mut cx = Context::default(); + let props = Props::default() + .with_prop(Margin::new().with_top(UnitValue::Px(8))) + .with_prop(Margin::new().with_top(UnitValue::Px(24))); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + + assert!(html.contains(r#"class="_margin-top_24px_""#)); + assert!(!html.contains("_margin-top_8px_")); +} + +#[pagetop::test] +async fn padding_top_adds_matching_style() { + let mut cx = Context::default(); + let props = Props::default().with_prop(Padding::new().with_top(UnitValue::RelRem(1.0))); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + let assets = cx.render_assets().into_string(); + + assert!(html.contains(r#"class="_padding-top_1rem_""#)); + assert!(assets.contains("_padding-top_1rem_{padding-top:1rem}")); +} + +#[pagetop::test] +async fn padding_all_sets_every_side() { + let mut cx = Context::default(); + let props = Props::default().with_prop(Padding::new().with_all(UnitValue::RelRem(1.0))); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + + assert!(html.contains( + r#"class="_padding-top_1rem_ _padding-bottom_1rem_ _padding-start_1rem_ _padding-end_1rem_""# + )); +} + +#[pagetop::test] +async fn padding_at_a_breakpoint_combines_token_and_suffix() { + let mut cx = Context::default(); + let props = Props::default() + .with_prop(Padding::new().with_bottom_at(Breakpoint::Lg, UnitValue::RelRem(2.0))); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + let assets = cx.render_assets().into_string(); + + assert!(html.contains(r#"class="_padding-bottom_2rem_lg_""#)); + assert!( + assets.contains("@media(min-width:992px){._padding-bottom_2rem_lg_{padding-bottom:2rem}}") + ); +} + +#[pagetop::test] +async fn padding_auto_is_ignored() { + let mut cx = Context::default(); + // CSS does not support `padding: auto`; unlike `Margin`, a side set to `Auto` adds nothing. + let props = Props::default().with_prop(Padding::new().with_all(UnitValue::Auto)); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + + assert_eq!(html, ""); + assert!(cx.render_assets().into_string().is_empty()); +} + +#[pagetop::test] +async fn margin_and_padding_combine_on_same_component() { + let mut cx = Context::default(); + let props = Props::default() + .with_prop(Margin::new().with_bottom(UnitValue::Px(8))) + .with_prop(Padding::new().with_top(UnitValue::Px(4))); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + + // Class order follows `Props::unpack()`: margin before padding. + assert!(html.contains(r#"class="_margin-bottom_8px_ _padding-top_4px_""#)); +} + +#[pagetop::test] +async fn from_margin_for_props_op() { + let mut cx = Context::default(); + let margin = Margin::new().with_top(UnitValue::Px(8)); + let props = Props::default().with_prop(margin); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + + assert!(html.contains(r#"class="_margin-top_8px_""#)); +} + +#[pagetop::test] +async fn from_padding_for_props_op() { + let mut cx = Context::default(); + let padding = Padding::new().with_top(UnitValue::Px(8)); + let props = Props::default().with_prop(padding); + let html = html! { span (props.unpack(&mut cx)) {} }.into_string(); + + assert!(html.contains(r#"class="_padding-top_8px_""#)); +}