Add end-to-end interaction flow smoke
Full build / Import + headless tests (push) Successful in 39s
Full build / Full desktop build + optional release (push) Skipped

- Boot the real scene and verify inspection allow/deny, normal vs emergency radio, Help filtering, wand allow/deny and review return-to-menu.
- Document the flow coverage and smoke command; mark the 0.9.3 end-to-end audit complete.
This commit is contained in:
Gig Line Dev committed 2026-11-02 16:36:01 +01:00
1 parent 7315166361
commit e805a91ad3
6 files changed
+158 -3

No files matched your search

+4
View File
@@ -61,6 +61,10 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
gallery is 13 good / 0 bad, the screen-design judge is 13/13 and a clean
portrait measured a 0.732 vision gap against the photoreal concept. No
release build, version bump or tag was created at this gate.
- **UI flow smoke (2026-11-02):** `smoke_ui_flows.gd` boots the real scene and
exercises inspection allow/deny, normal vs emergency radio, Help filtering,
wand allow/deny and review return-to-menu. Keep this smoke in the 0.9.3/0.9.6
handoff; it complements the unit tests that only exercise panels.
- **Destructive actions (2026-11-02):** `SettingsPanel._open_destructive_dialog()`
owns one confirmation instance for reset/replay; repeated presses focus the
existing dialog, OK emits once, Cancel is inert. Emergency send remains the
+3 -1
View File
@@ -98,8 +98,10 @@ screen-design judge before any model/texture changes. No gameplay rules change.
emergency send) require the intended confirmation/hold and cannot double-fire.
(Reset/replay share one guarded confirmation dialog; emergency hold submits
once and ignores the post-submit release. Covered by session/radio tests.)
- [ ] Verify radio normal-send vs emergency hold, inspection/wand allow/deny,
- [x] Verify radio normal-send vs emergency hold, inspection/wand allow/deny,
review continue/retry/menu and Help search/filter behavior end to end.
(`smoke_ui_flows.gd` covers inspection, radio, Help, wand and review;
`smoke_campaign.gd` covers review continue/checkpoint resume.)
### Visual and judge verification
+1
View File
@@ -51,6 +51,7 @@ Implemented so far:
- Joke Shop / Sunset Strip map (map optimization): the second venue is now a stylized reconstruction of The Comedy Store block at 8433 Sunset Blvd, with a 27 m black hall, signature-covered sloping canopy, circular **THE JOKE SHOP** blade sign, red velvet curtain bays, a west-end entrance, cream and white low-rise neighbours, a dry hillside with stucco houses and palms behind the roofline, and a low retail/billboard frontage across Sunset. The same geometry serves day and late lighting through the phase system. Real-site references and limits are in [`joke-shop-map.md`](joke-shop-map.md); the map measures **22,680 triangles across 454 meshes** in the budget test.
- Venue geometry (v0.7.0, Joke Shop frontage superseded by the map-optimization pass): the TV Studio builds a stone facade, bulb canopy, glossy black car and barrier line. `WorldBuilder.VENUE_SPECS` drives facade/street/nav dimensions, and `GameManager` rebuilds only the world container when the venue changes.
- Gear fit (post-v0.6.0): Blender gear is measured against the avatar head/skirt silhouette and the checks run in both the Python tooling and the headless suite. Sunglasses, the shoulder bag and the firefighter helmet were fitted to the new faces; the Influencer portrait and gallery were recaptured with a before/after report.
- End-to-end interaction pass (0.9.3 work): `smoke_ui_flows.gd` drives inspection allow/deny, normal vs emergency radio, Help filtering, wand allow/deny and review return-to-menu in the real scene; campaign and timeline smokes cover review continue and timeline round-trips.
- Destructive-action pass (0.9.3 work): reset campaign and tutorial replay share a guarded confirmation dialog (one open instance, one emission, Cancel is inert); emergency send remains hold-to-confirm with no post-submit double-fire. Covered by session/radio tests and recorded in `docs/ui-copy.md`.
- Font/layout pass (0.9.3 work): HUD font scale was exercised at 0.75× and 1.75×; the target chip measures and caps scaled names, top labels clip, and shared action buttons ellipsize long 1.5–2× labels instead of overflowing. Covered by accessibility/button tests and recorded in `docs/ui-copy.md`.
- Modal-close pass (0.9.3 work): one top-modal close path handles Esc/right-click for every overlay, Settings opened from the menu restores the menu, Help no longer opens Pause behind it, and pointer/guard freeze state is recaptured after each close. The session smoke covers these paths.
+3
View File
@@ -35,6 +35,9 @@ godot --path . --script res://src/test/smoke_focus_radial.gd
# Session-shell smoke: menu → shift → pause → settings → menu (headless or windowed)
godot --headless --path . --script res://src/test/smoke_session_shell.gd
# End-to-end interaction smoke: inspection, radio, Help search, wand and review
godot --headless --path . --script res://src/test/smoke_ui_flows.gd
# PLAN-verb smoke: recognize, request reason, body camera, warning, blocking
godot --headless --path . --script res://src/test/smoke_plan_verbs.gd
+16 -2
View File
@@ -1,6 +1,6 @@
# UI copy and button inventory
Status: dialogue, button-state, focus, modal-close, font/layout and destructive-action passes complete; end-to-end audit in progress for 0.9.3.
Status: dialogue, button-state, focus, modal-close, font/layout, destructive-action and end-to-end passes complete; capture/smoke sign-off remains for 0.9.3.
## Screen inventory
@@ -181,10 +181,24 @@ Regression coverage lives in `test_accessibility.gd` (target-chip width/cap) and
translator workflow remains a 0.10.0 task; this pass uses deterministic long
strings and the maximum supported HUD scale.
## End-to-end interaction flows
`smoke_ui_flows.gd` boots the real scene and verifies, in order:
- inspection opens, then allow and deny both close the panel;
- a normal radio click sends and spends battery;
- an emergency hold sends once, spends battery and starts cooldown;
- Help opens, filters to radio controls and closes;
- the wand opens at The Joke Shop, then allow and deny close it;
- returning from the review restores the venue menu.
`smoke_campaign.gd` covers review continue plus checkpoint resume, and
`smoke_timeline.gd` covers review → timeline → review. These are end-to-end
scene smokes, not just panel-unit tests.
## Remaining audit work
- Player-facing labels are now routed through `content/strings.en.json`; numeric
values, empty-state clears and debug-only text remain in code by design.
- Focus/hover/pressed/disabled states and keyboard/gamepad rings are recorded above.
- Long strings and 0.75×/1.75× HUD scale are covered above.
- Capture every dialog/button state and run `screen_designs.py compare` plus the pinned judge.
+131
View File
@@ -0,0 +1,131 @@
extends SceneTree
## End-to-end interaction flows: inspection allow/deny, normal radio vs
## emergency hold, Help filtering, wand allow/deny and review return-to-menu.
##
## Usage:
## godot --headless --path . --script res://src/test/smoke_ui_flows.gd
## Exit code 0 = pass, 1 = failure.
const MAX_FRAMES := 1800
var frame := 0
var gm: Node = null
var started := false
var step := 0
var failures: Array[String] = []
func _initialize() -> void:
root.size = Vector2i(1280, 720)
root.add_child(load("res://scenes/main.tscn").instantiate())
func _process(_delta: float) -> bool:
frame += 1
if gm == null:
gm = root.find_child("GameManager", true, false)
return false
if not started:
gm.call("_on_venue_selected", "hollywood_building")
started = true
return false
if frame > MAX_FRAMES:
_fail("timeout")
return true
if frame < 200:
return false
match step:
0:
if gm.npcs.is_empty():
return false
var npc: Node = gm.npcs[0]
gm.call("_on_guard_target", npc)
gm.call("_toggle_inspection")
_check(gm.inspection_open and gm.hud.inspect_panel.visible, "inspection opens")
gm.decide_access(true)
_check(not gm.inspection_open and not gm.hud.inspect_panel.visible,
"inspection allow closes the panel")
gm.call("_toggle_inspection")
gm.decide_access(false)
_check(not gm.inspection_open, "inspection deny closes the panel")
var battery_before: float = gm.coordinator.radio.battery
gm.call("_open_radio", false)
_check(gm.radio_panel.visible, "normal radio form opens")
gm.radio_panel.action_button.pressed.emit()
_check(not gm.radio_panel.visible and gm.coordinator.radio.battery < battery_before,
"normal radio sends and spends battery")
battery_before = gm.coordinator.radio.battery
gm.call("_open_radio", true)
_check(gm.radio_panel.visible, "emergency radio form opens")
gm.radio_panel.action_button.button_down.emit()
gm.radio_panel._process(1.7)
_check(not gm.radio_panel.visible
and gm.coordinator.radio.battery < battery_before
and gm.coordinator.radio.emergency_cooldown_remaining > 0.0,
"emergency hold sends once and starts cooldown")
gm.call("_open_help")
_check(gm.help_overlay.visible, "Help opens")
gm.help_overlay._refresh("radio")
var help_text: String = gm.help_overlay._body.text
_check(help_text.to_lower().contains("radio")
and not help_text.contains("Movement:"),
"Help search filters to radio controls")
gm.help_overlay.close_panel()
_check(not gm.help_overlay.visible, "Help closes")
step = 1
frame = 0
1:
gm.switch_scenario("joke_shop")
step = 2
frame = 0
2:
if frame < 200:
return false
var wand_target: Node = null
for node in gm.npcs:
var c: Object = node.get_npc_controller() if node.has_method("get_npc_controller") else null
if c != null and not c.refuses_screening:
wand_target = node
break
if wand_target == null:
return false
gm.call("_on_guard_target", wand_target)
gm.call("_toggle_wand_screening")
_check(gm.wand_open and gm.hud.wand_panel.visible, "wand opens for a guest")
gm.decide_wand(true)
_check(not gm.wand_open and not gm.hud.wand_panel.visible,
"wand allow closes the panel")
gm._wand_cooldown = 0.0
gm.call("_toggle_wand_screening")
gm.decide_wand(false)
_check(not gm.wand_open and not gm.hud.wand_panel.visible,
"wand deny closes the panel")
var review: Dictionary = gm.coordinator.build_review()
gm.shift_review_panel.show_review(review, gm.selected_scenario, [],
{"complete": false, "next_venue": "tv_studio", "shift_number": 2})
_check(gm.shift_review_panel.visible, "review opens")
gm.call("_on_review_restart")
_check(not gm.shift_review_panel.visible and gm.start_menu.visible,
"review return-to-menu restores the venue menu")
_finish()
return true
return false
func _check(condition: bool, message: String) -> void:
if condition:
print("ok: " + message)
else:
_fail(message)
func _fail(message: String) -> void:
failures.append(message)
_finish()
func _finish() -> void:
if failures.is_empty():
print("SMOKE OK: inspection, radio, Help, wand and review flows")
quit(0)
else:
for message in failures:
printerr("SMOKE FAIL: " + message)
quit(1)