#[slhx::surface] pub mod ui {} #[cfg(test)] mod tests { use super::ui; use slhx::{push, Effect, IntoEffect, NavigateMode}; use slhx_test::inspect; #[derive(Clone, Debug)] struct Todo { id: u64, title: String, } #[derive(Clone, Debug)] struct TodoInput { title: String, } #[derive(Clone, Debug)] struct WizardInput { step: u8, } #[derive(Clone, Debug)] struct Credentials { email: String, password: String, } // req: examples/001 req: ceremony/001 req: build/001 req: build/005 req: codegen/002 #[test] fn counter_updates_a_generated_slot() { fn increment(count: u64) -> impl IntoEffect { ui::slots::counter_value.text(count + 1) } let effect = inspect(increment(1)); assert!(effect.has_slot(ui::slots::counter_value)); assert!(matches!(effect.ops(), [Effect::Put { .. }])); } // req: examples/001 req: progressive_disclosure/001 req: build/001 req: build/005 req: codegen/002 #[test] fn todos_append_keyed_rows_from_form_input() { fn add_todo(input: TodoInput) -> impl IntoEffect { let todo = Todo { id: 7, title: input.title }; ui::slots::todo_row.append(todo.id.to_string(), format!("
  • {}
  • ", todo.title)) } let effect = inspect(add_todo(TodoInput { title: "Ship v0".into() })); assert!(effect.has_resource(ui::slots::todo_row.id())); assert!(matches!(effect.ops(), [Effect::Insert { key, .. }] if key == "7")); } // req: examples/001 req: progressive_disclosure/001 req: build/001 req: build/005 req: codegen/004 #[test] fn wizard_form_swaps_the_current_step() { fn next_step(input: WizardInput) -> impl IntoEffect { ui::slots::wizard_step.text(format!("Step {}", input.step + 1)) } let effect = inspect(next_step(WizardInput { step: 1 })); assert!(effect.has_slot(ui::slots::wizard_step)); assert!(matches!(effect.ops(), [Effect::Put { .. }])); } // req: examples/001 req: page_swap/002 req: page_swap/003 req: build/001 req: build/005 #[test] fn page_swap_updates_content_and_history() { fn load_docs() -> impl IntoEffect { ( ui::slots::content.text("

    Docs

    "), ui::slots::title.text("Docs"), push("/docs"), ) } let effect = inspect(load_docs()); assert!(effect.has_slot(ui::slots::content)); assert!(effect.has_slot(ui::slots::title)); assert!(matches!(effect.ops().last(), Some(Effect::Navigate { url, mode: NavigateMode::Push, .. }) if url == "/docs")); } // req: examples/001 req: progressive_disclosure/001 req: build/001 req: build/005 req: codegen/004 #[test] fn auth_flow_reads_typed_input_and_updates_status() { fn login(input: Credentials) -> impl IntoEffect { let status = if input.email == "demo@example.com" && !input.password.is_empty() { "Signed in" } else { "Try again" }; ui::slots::login_status.text(status) } let effect = inspect(login(Credentials { email: "demo@example.com".into(), password: "secret".into() })); assert!(effect.has_slot(ui::slots::login_status)); assert!(matches!(effect.ops(), [Effect::Put { .. }])); } // req: examples/001 req: push/001 req: push/003 req: build/001 req: build/005 #[test] fn sse_notifications_update_a_generated_slot() { fn notification(message: &str) -> impl IntoEffect { ui::slots::notifications.text(message) } let effect = inspect(notification("Build finished")); assert!(effect.has_slot(ui::slots::notifications)); assert!(matches!(effect.ops(), [Effect::Put { .. }])); } }