Add end-to-end interaction flow smoke
- 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:
1 parent
7315166361
commit
e805a91ad3
6 files changed
+158
-3
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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.
|
||||
@@ -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)
|
||||
Reference in new issue
Block a user