From 3b5de853199ff8bf876872ed4c5243a11d0c9c83 Mon Sep 17 00:00:00 2001 From: slhx agent Date: Fri, 12 Jun 2026 14:43:47 +0200 Subject: [PATCH] test(v0): prove dynamic reusable row updates Use one TodoRow projection for initial render and generated dynamic Vec row replacement batches, keeping the normal path on generated helpers instead of raw effects or registry plumbing. req: canonical_authoring/003 --- examples/v0/src/main.rs | 59 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/examples/v0/src/main.rs b/examples/v0/src/main.rs index f2e2302..a875ca7 100644 --- a/examples/v0/src/main.rs +++ b/examples/v0/src/main.rs @@ -427,16 +427,30 @@ fn todos_view(todos: &[TodoRecord]) -> Todos { // req: html_safety/002 req: view/001 req: canonical_authoring/003 Todos { summary: todo_summary(todos), - rows: todos - .iter() - .map(|todo| TodoRow { - id: TodoId(todo.id), - title: todo.title.clone(), - }) - .collect(), + rows: todo_rows(todos), } } +#[cfg(test)] +fn refresh_todo_rows_effect(todos: &[TodoRecord]) -> impl IntoEffect { + // req: canonical_authoring/003 + let row_updates = todo_rows(todos) + .into_iter() + .map(|row| todos::todo_row.replace(row)) + .collect::>(); + (row_updates, todos::summary.set(todo_summary(todos))) +} + +fn todo_rows(todos: &[TodoRecord]) -> Vec { + todos + .iter() + .map(|todo| TodoRow { + id: TodoId(todo.id), + title: todo.title.clone(), + }) + .collect() +} + fn todo_summary(todos: &[TodoRecord]) -> String { match todos.len() { 0 => "No todos".to_owned(), @@ -587,6 +601,37 @@ mod tests { .is_none()); } + // req: canonical_authoring/003 req: test/005 + #[test] + fn reusable_todo_partial_supports_dynamic_replace_batches() { + let todos = vec![ + TodoRecord { + id: 7, + title: "Ship v0".to_owned(), + }, + TodoRecord { + id: 8, + title: "Write docs".to_owned(), + }, + ]; + + let initial = ui::page(&todos_view(&todos)); + let document = Html::parse_fragment(initial.as_str()); + assert_eq!( + document + .select(&selector(&keyed_items_selector("li"))) + .count(), + 2 + ); + + let refresh = + inspect_batch(refresh_todo_rows_effect(&todos).into_batch(ui::BUILD_FINGERPRINT)); + assert_eq!(refresh.op_count(), 3); + assert!(refresh.replaces_keyed_html_containing(todos::todo_row, "7", "Ship v0")); + assert!(refresh.replaces_keyed_html_containing(todos::todo_row, "8", "Write docs")); + assert!(refresh.updates_text(todos::summary)); + } + // req: html_safety/002 req: view/001 req: test/005 #[test] fn empty_todos_payload_is_rendered_by_a_hemplate_view() {