From edc65be7b652c1cc4063abbc9bc2824b8182d47f Mon Sep 17 00:00:00 2001 From: slhx agent Date: Thu, 25 Jun 2026 19:05:34 +0200 Subject: [PATCH] test(hemx-test): name rendered target assertions Add rendered target and handle assertions that include generated names in diagnostics instead of only raw runtime ids. req: test/017 req: dx/006 --- AGENTS.md | 2 +- REQUIREMENTS.md | 3 +++ hemx-test/src/lib.rs | 45 +++++++++++++++++++++++++++++++----- hemx-test/tests/inspector.rs | 10 ++++++++ 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 972fc77..b161bb7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,7 +52,7 @@ Keep it stable. Prefer pointers to canonical sources over copied structure, file - Prefer links or pointers to canonical sources over copied lists. - Avoid project trees, architecture maps, generated inventories, current file sizes, issue lists, TODO inventories, and other snapshots that will rot. - Stable commands: `cargo run -p hemx-xtask -- test`, `cargo run -p hemx-xtask -- html-examples-smoke`, `cargo check --workspace`, `redgate health --strict`. Use the xtask runner for full verification so jobs are capped from local CPU and memory; use the html_examples smoke for focused repo-owned browser verification of the HTML pattern gallery, no-reload dynamic interactions, and no `/tmp` scripts. Keep fast crate tests, focused browser smoke, and full xtask authority distinct; the full path should stay within a documented 10 minute local timeout or be split into deterministic shards under the same wrapper. req: test/004 req: test/006 req: test/012 req: test/013 req: test/014 req: test/015 req: test/016 -- Example behavior tests should prefer `hemx_test` generated-resource assertions over raw slot constants or raw effect/payload matching; keep browser selector helpers as test adapters only, not authoring APIs. req: test/008 req: test/009 req: test/010 +- Example behavior tests should prefer `hemx_test` generated-resource assertions over raw slot constants or raw effect/payload matching; rendered target/handle assertions should name the generated resource in failure messages; keep browser selector helpers as test adapters only, not authoring APIs. req: test/008 req: test/009 req: test/010 req: test/017 - Run the workout product exemplar with `cargo run -p hemx-xtask -- workout dev` and open `http://127.0.0.1:3028`; set `HEMX_WORKOUT_ADDR=127.0.0.1:3030` if the default port is busy. Its durable visual direction and recovery expectations live in `examples/workout/DESIGN.md`. req: examples/008 - Use the same Workout command surface for tests, production build, and mobile release: `cargo run -p hemx-xtask -- workout test`, `cargo run -p hemx-xtask -- workout build`, `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-release`, and `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-verify`; Android/iOS SDKs, store submission targets, and signing remain external blockers, not repo-owned secrets, and do not imply a broad `hemx-mobile` framework. req: examples/006 req: examples/011 req: examples/013 - hemx core stays small: effects, typed ids, registries, and wire schema only; keep features in core only when they fit typed resources plus the closed EffectBatch op set, and treat DOM details as runtime lowering. Workspace crates stay separated, stable-Rust-compatible, and free of kitchen-sink boundaries; new primitives must delete special cases. Public identifiers should flow through typed wrappers over internal `ResourceId`/`ResourceRef`, not special-case opcodes. Wire output lowers symbolic authoring names to compact metadata and postcard/form-encoded envelopes, not JSON. ABI/schema versions and build fingerprints must guard runtime/server compatibility. v0 scope is the checked hypermedia core plus page/runtime/wire/diagnostic/test/axum proof, not optional sync/wasm/query/auth/router breadth. req: v0_scope/001 req: v0_scope/002 req: v0_scope/005 req: laws/001 req: invariant/001 req: invariant/005 req: typed_id/001 req: typed_id/003 req: effect_algebra/001 req: effect_algebra/006 req: wire/001 req: wire/002 req: wire/003 req: wire/004 req: wire/005 req: wire/006 req: abi/001 req: abi/002 req: abi/003 req: abi/004 req: abi/005 req: misc/001 req: misc/002 req: misc/003 req: misc/004 req: misc/005 req: misc/006 req: misc/007 req: misc/008 req: misc/009 req: misc/010 diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index 7005c2d..ca363a3 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -865,6 +865,9 @@ what a valid business email is. [north_star] ### req: test/016 0 016 The full local verification path has an explicit timeout budget; any shard split is deterministic and preserves equivalent coverage. [north_star] +### req: test/017 +0 017 Test helpers for rendered runtime ids report the generated handle or slot name, not only raw `data-hid` or `data-sid` selectors. [north_star] + --- ## check diff --git a/hemx-test/src/lib.rs b/hemx-test/src/lib.rs index edf4ee7..5f43776 100644 --- a/hemx-test/src/lib.rs +++ b/hemx-test/src/lib.rs @@ -197,15 +197,34 @@ pub fn page_nav_link_selector(href: &str) -> String { /// req: test/001 req: dx/006 pub fn target_selector(target: impl GeneratedTarget) -> String { let resource = target.__hemx_resource_id(); - let attr = match resource.kind { - ResourceKind::Slot => "data-sid", - ResourceKind::Atom => "data-aid", - ResourceKind::Handle => "data-hid", - ResourceKind::Form => "data-fid", - }; + let attr = runtime_resource_attr(resource.kind); attr_selector(attr, &resource.id.to_string()) } +/// Assert rendered HTML contains a generated target and report the generated name on failure. +/// req: test/017 req: dx/006 +pub fn assert_rendered_target(target: impl GeneratedTarget, generated_name: &str, html: &str) { + let resource = target.__hemx_resource_id(); + let marker = runtime_attr_marker( + runtime_resource_attr(resource.kind), + &resource.id.to_string(), + ); + assert!( + html.contains(&marker), + "rendered hemx target `{generated_name}` missing runtime marker {marker}" + ); +} + +/// Assert rendered HTML contains a generated handle and report the generated name on failure. +/// req: test/017 req: dx/006 +pub fn assert_rendered_handle(handle: hemx_core::Handle, generated_name: &str, html: &str) { + let marker = runtime_attr_marker("data-hid", &handle.to_string()); + assert!( + html.contains(&marker), + "rendered hemx handle `{generated_name}` missing runtime marker {marker}" + ); +} + /// Build a selector for an hemx root from its authoring name. /// req: test/001 req: dx/006 pub fn root_selector(name: &str) -> String { @@ -351,6 +370,20 @@ fn assert_simple_selector_part(value: &str, label: &str) { ); } +fn runtime_resource_attr(kind: ResourceKind) -> &'static str { + match kind { + ResourceKind::Slot => "data-sid", + ResourceKind::Atom => "data-aid", + ResourceKind::Handle => "data-hid", + ResourceKind::Form => "data-fid", + } +} + +fn runtime_attr_marker(name: &str, value: &str) -> String { + let escaped = value.replace('"', """); + format!(r#"{name}="{escaped}""#) +} + fn attr_selector(name: &str, value: &str) -> String { let escaped = value.replace('\\', "\\\\").replace('"', "\\\""); format!(r#"[{name}="{escaped}"]"#) diff --git a/hemx-test/tests/inspector.rs b/hemx-test/tests/inspector.rs index b4e7a2d..932191e 100644 --- a/hemx-test/tests/inspector.rs +++ b/hemx-test/tests/inspector.rs @@ -67,10 +67,20 @@ fn builds_authoring_boundary_selectors() { hemx_test::target_selector(TestTarget(ResourceKind::Slot, 42)), r#"[data-sid="42"]"# ); + hemx_test::assert_rendered_target( + TestTarget(ResourceKind::Slot, 42), + "gallery::search_result", + r#"
  • row
  • "#, + ); assert_eq!( hemx_test::handle_button_selector(hemx_core::Handle::<()>::new(7)), r#"button[data-hid="7"]"# ); + hemx_test::assert_rendered_handle( + hemx_core::Handle::<()>::new(7), + "gallery::search", + r#""#, + ); assert_eq!(hemx_test::article_selector(), "article"); assert_eq!(hemx_test::strong_text_selector(), "strong"); assert_eq!(hemx_test::small_text_selector(), "small");