5bcae0a075
Move the v0 keyed todo row effect payload from Rust format! HTML into a hemplate partial and require the generated slot append to carry an HTML payload. req: html_safety/002 req: view/001
138 lines
4.3 KiB
Rust
138 lines
4.3 KiB
Rust
#[slhx::surface]
|
|
pub mod ui {}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::ui;
|
|
use hemplate::Hemplate;
|
|
use slhx::{push, Effect, IntoEffect, NavigateMode, Payload, SafeHtml};
|
|
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,
|
|
}
|
|
|
|
#[derive(Hemplate)]
|
|
#[hemplate = "partials"]
|
|
struct TodoRow {
|
|
title: 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_html(todo.id.to_string(), render_todo_row(&todo))
|
|
}
|
|
|
|
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, payload, .. }] if key == "7" && matches!(payload, Payload::Html(_))));
|
|
}
|
|
|
|
fn render_todo_row(todo: &Todo) -> SafeHtml {
|
|
// req: html_safety/002 req: view/001
|
|
SafeHtml::trusted(
|
|
TodoRow { title: todo.title.clone() }
|
|
.render()
|
|
.expect("v0 todo row renders"),
|
|
)
|
|
}
|
|
|
|
// 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("<h1>Docs</h1>"),
|
|
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 { .. }]));
|
|
}
|
|
}
|