Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions tests/integration/test_recording_sync.das
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
options gen2
options indenting = 4
options no_unused_block_arguments = false
options no_unused_function_arguments = false

require dastest/testing_boost public
require imgui/imgui_playwright public
require daslib/json public
require daslib/json_boost public

//! Integration coverage for the two recording-sync primitives added in dasImgui PR #186:
//! `wait_for_hover` and `record_check`. Uses the `color_button_hover` feature — the CBH_RED
//! swatch carries the per-widget IsItemHovered telemetry (the `hover` field) that wait_for_hover
//! polls. One app spawn covers both (record_check's `app` arg is unused).

let FEATURE = "modules/dasImgui/examples/features/color_button_hover.das"
let TARGET = "CBH_WIN/CBH_RED"

[test]
def test_recording_sync(t : T?) {
with_imgui_app(FEATURE) $(d) {
var snap = wait_for_widget(d, TARGET, 15.0f)
t |> success(snap != null, "{TARGET} renders")
if (snap == null) return

// ---- wait_for_hover ----
// Move onto the swatch — it must report hovered (the move -> wait_for_hover -> click gate).
move_to(d, widget_click_point(snap, TARGET))
t |> success(wait_for_hover(d, TARGET) != null, "wait_for_hover registers hover after move_to")

// Move off — hover must clear (expected=false on a PRESENT widget).
move_to(d, (4.0f, 4.0f))
t |> success(wait_for_hover(d, TARGET, false) != null, "wait_for_hover sees hover clear after moving away")

// Regression for the #186-A false-pass: a missing / misspelled ident must NOT pass as
// "not hovered". The old predicate coalesced a null hover to false and returned on the FIRST
// poll; the widget_exists guard makes it time out instead. A tiny timeout still separates the
// two (old=immediate non-null, fixed=times out) without spending the wall-clock to wait it out.
t |> success(wait_for_hover(d, "CBH_WIN/__no_such_widget__", false, 0.3f) == null,
"missing widget does not false-pass wait_for_hover(expected=false)")

// ---- record_check ----
// Returns its `ok` argument, and accumulates (does NOT panic) on false — the
// accumulate-don't-panic discipline; the teardown-panic surfacing is exercised transitively
// by every record_* driver in the suite. Reaching the line after the false call proves it
// didn't panic mid-body.
t |> success(record_check(d, "ok branch returns true", true), "record_check returns true when ok")
let failed = record_check(d, "intentional miss (accumulated, not panicked)", false)
t |> success(!failed, "record_check returns false and accumulates instead of panicking")
}
}
6 changes: 5 additions & 1 deletion widgets/imgui_playwright.das
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,12 @@ def public wait_for_hover(app : ImguiApp; widget_ident : string; expected : bool
//! race a real continuous-hover mouse never hits (synthetic != real). Bounded by ``timeout_sec``;
//! returns the matching snapshot or null on timeout. ``null`` is a real failure — gate it with
//! ``record_check`` (recording) or ``success`` (test); never proceed to the click silently.
//! The ``widget_exists`` guard is load-bearing for ``expected=false``: an absent / misspelled
//! ident yields a null ``hover`` that ``?? false`` would coalesce to ``false``, so a missing
//! widget would false-pass as "not hovered" and mask a sync failure — same guard as
//! ``widget_rendered``.
return wait_until_sec(app, timeout_sec) $(var snap) {
return (find_widget(snap, widget_ident)?["hover"] ?? false) == expected
return widget_exists(snap, widget_ident) && (find_widget(snap, widget_ident)?["hover"] ?? false) == expected
}
}

Expand Down
Loading