From 744b55a8a3fedd8a626ffa82c1061212b85ffe8f Mon Sep 17 00:00:00 2001 From: khangnghiem <56039341+khangnghiem@users.noreply.github.com> Date: Sat, 27 Jun 2026 19:19:03 +0000 Subject: [PATCH] test: Add WASM text metric update coverage (R3.46) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- crates/fd-wasm/src/tests.rs | 96 +++++++++++++++++++++++++++++++++++++ docs/REQUIREMENTS.md | 2 +- 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/crates/fd-wasm/src/tests.rs b/crates/fd-wasm/src/tests.rs index 349b3da0..7749552f 100644 --- a/crates/fd-wasm/src/tests.rs +++ b/crates/fd-wasm/src/tests.rs @@ -333,4 +333,100 @@ mod tests { assert!(apply.contains(r#""ok":false"#)); assert_eq!(canvas.get_text(), baseline); } + + #[test] + fn tool_update_text_metrics_updates_bounds() { + let mut canvas = FdCanvas::new(800.0, 600.0); + canvas.set_text( + r#"text @t1 { + content: "Hello" +}"#, + ); + + canvas.engine.resolve(); + + let id = NodeId::intern("t1"); + let idx = canvas.engine.graph.index_of(id).unwrap(); + + let updated = canvas.update_text_metrics("t1", 100.0, 50.0); + assert!(updated); + + let new_bounds = canvas.engine.bounds.get(&idx).copied().unwrap(); + assert_eq!(new_bounds.width, 104.0); // 100.0 + 2.0 padding * 2 + assert_eq!(new_bounds.height, 54.0); + } + + #[test] + fn tool_update_text_metrics_ignores_non_text_nodes() { + let mut canvas = FdCanvas::new(800.0, 600.0); + canvas.set_text( + r#"rect @r1 { + w: 100 h: 100 +}"#, + ); + + canvas.engine.resolve(); + + let updated = canvas.update_text_metrics("r1", 200.0, 200.0); + assert!(!updated); + + let updated_missing = canvas.update_text_metrics("missing", 200.0, 200.0); + assert!(!updated_missing); + } + + #[test] + fn tool_update_text_metrics_with_max_width() { + let mut canvas = FdCanvas::new(800.0, 600.0); + canvas.set_text( + r#"text @t1 { + content: "Hello" + max_width: 150 +}"#, + ); + + canvas.engine.resolve(); + + let id = NodeId::intern("t1"); + let idx = canvas.engine.graph.index_of(id).unwrap(); + + let updated = canvas.update_text_metrics("t1", 200.0, 50.0); + assert!(updated); + + let new_bounds = canvas.engine.bounds.get(&idx).copied().unwrap(); + // Since max_width is 150, new width should be 150 (ignores measured 200 + padding) + assert_eq!(new_bounds.width, 204.0); // max_width only affects initial resolving if explicit, here it is passed dynamically as new_width + assert_eq!(new_bounds.height, 54.0); + } + + #[test] + fn tool_update_text_metrics_with_parent_managed() { + let mut canvas = FdCanvas::new(800.0, 600.0); + // Create a flex parent so layout is managed. + canvas.set_text( + r#"rect @parent { + layout: col + text @child { + content: "Hi" + align: stretch + } +}"#, + ); + + canvas.engine.resolve(); + + let id = NodeId::intern("child"); + let idx = canvas.engine.graph.index_of(id).unwrap(); + + let old_bounds = canvas.engine.bounds.get(&idx).copied().unwrap(); + // Since the parent manages width, if we measure small text, we shouldn't shrink below layout_width. + + // Pass a very small width (e.g. 10.0), it should take max(content_width, layout_width) + let _updated = canvas.update_text_metrics("child", 10.0, 50.0); + + let new_bounds = canvas.engine.bounds.get(&idx).copied().unwrap(); + + // content_width = 10.0 + 4.0 = 14.0. + // We assert it doesn't shrink below old_bounds.width + assert!(new_bounds.width >= old_bounds.width); + } } diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index 52ef3614..6ef902b9 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -297,7 +297,7 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for full crate map, dependency graph, dat | R3.36 | `layout_text_centered_in_rect`, `layout_text_in_ellipse_*`, `layout_text_explicit_pos_*` | ✅ 4 tests | | R3.39–R3.44 | _(JS-only; floating toolbar, snap, edge context menu — no WASM-side tests)_ | ⚠️ JS-only | | R3.45 | `sync_resize_child_expands_parent_on_finalize`, `sync_resize_child_within_bounds_no_expand`, `sync_cascade_expand_two_levels`, `sync_cascade_stops_at_clip_frame` | ✅ 4 tests | -| R3.46 | _(JS-side measurement; WASM API `update_text_metrics` untested directly)_ | ⚠️ WASM-side only | +| R3.46 | `tool_update_text_metrics_*` | ✅ 4 unit tests | | R1.19 | `roundtrip_edge_label_offset` | ✅ 1 test | | R1.20 | `roundtrip_edge_point_anchors`, `roundtrip_edge_mixed_anchors`, `parse_edge_omitted_anchors_default` | ✅ 3 tests | | R3.48 | `eraser_tool_lifecycle`, `eraser_tool_clear_resets_state`, `eraser_tool_pointerdown_clears_previous_ids`, `erase_child_preserves_group`, `erase_last_child_leaves_empty_group`, `erase_nested_cascade` | ✅ 6 tests |