Skip to content

Commit 5209934

Browse files
author
SqlRush
committed
Normalize scripted screen event aliases
1 parent 26f4897 commit 5209934

3 files changed

Lines changed: 172 additions & 3 deletions

File tree

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ M7 progress now includes:
285285
- `internal/tui`: interaction script paste payloads now accept DOM `clipboardData`/`dataTransfer` objects and recover pasted text from `text/plain`, `plainText`, and `items[].text` fields for ClipboardEvent recording fixtures.
286286
- `internal/tui`: interaction script resize payloads now accept DOM/window size aliases such as `innerWidth`/`innerHeight`, `clientWidth`/`clientHeight`, `offsetWidth`/`offsetHeight`, plus ResizeObserver-style `contentRect` and `target` wrappers.
287287
- `internal/tui`: interaction script resize payloads now accept ResizeObserver `contentBoxSize`/`borderBoxSize` array entries using `inlineSize`/`blockSize`, covering modern browser box-size event shapes.
288+
- `internal/tui`: interaction script event expectations now normalize camel/dash/provider-style event names such as `promptSubmitted`, `dialogAction`, `messageSubmitted`, and `focusOut` to canonical screen event types, and accept value aliases such as `eventPayload`, `eventValue`, `actionValue`, and `result`.
288289
- `internal/tui`: task runtime now canonicalizes state aliases such as `active`/`inProgress`/`in_progress`, `success`/`done`/`completedSuccessfully`, `error`/`failedWithError`, and `canceled`/`cancelledByUser` before status-line summaries, task dialogs, ordering, cancellation, and scripted task expectations.
289290
- `internal/tui`: scripted task runtime payloads and task expectations now accept adjacent fields such as `taskID`, `jobId`, `runId`, `label`, `displayName`/`displayTitle`, `operationName`/`commandName`, `phase`/`taskState`/`jobStatus`/`resultState`, `message`/`currentStep`/`statusMessage`/`outputText`/`resultText`, `percent`/`percentage`/`pct`/`completionPercent`, and numeric/string task IDs or progress values.
290291
- `internal/tui`: scripted task runtime payloads, task expectations, and remove-task mutations now also share adjacent task ID aliases such as `operationId`, `requestId`, `threadId`, `workflowId`, and `toolUseId`, so recorded runtime actions can address tasks through operation/request-style IDs.

internal/tui/script_aliases.go

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4616,10 +4616,30 @@ func (event *ScreenEvent) UnmarshalJSON(data []byte) error {
46164616
"event",
46174617
"Name",
46184618
"name",
4619+
"EventName",
4620+
"event_name",
4621+
"eventName",
46194622
"Value",
46204623
"value",
4624+
"EventValue",
4625+
"event_value",
4626+
"eventValue",
46214627
"Payload",
46224628
"payload",
4629+
"EventPayload",
4630+
"event_payload",
4631+
"eventPayload",
4632+
"ActionValue",
4633+
"action_value",
4634+
"actionValue",
4635+
"Result",
4636+
"result",
4637+
"Selection",
4638+
"selection",
4639+
"Input",
4640+
"input",
4641+
"Prompt",
4642+
"prompt",
46234643
"Text",
46244644
"text",
46254645
"Message",
@@ -4665,12 +4685,27 @@ func (event *ScreenEvent) UnmarshalJSON(data []byte) error {
46654685
return err
46664686
}
46674687
if event.Type == "" {
4668-
if eventType := stringJSONField(fields, "Type", "type", "EventType", "event_type", "eventType", "Event", "event", "Name", "name"); eventType != "" {
4669-
event.Type = ScreenEventType(eventType)
4688+
if eventType := stringJSONField(fields, "Type", "type", "EventType", "event_type", "eventType", "Event", "event", "Name", "name", "EventName", "event_name", "eventName"); eventType != "" {
4689+
event.Type = normalizeScreenEventType(eventType)
46704690
}
46714691
}
4692+
if event.Type != "" {
4693+
event.Type = normalizeScreenEventType(string(event.Type))
4694+
}
46724695
if event.Value == "" {
4673-
event.Value = scalarStringJSONField(fields, "Value", "value", "Payload", "payload", "Text", "text", "Message", "message", "Data", "data")
4696+
event.Value = scalarStringJSONField(fields,
4697+
"Value", "value",
4698+
"EventValue", "event_value", "eventValue",
4699+
"Payload", "payload", "EventPayload", "event_payload", "eventPayload",
4700+
"ActionValue", "action_value", "actionValue",
4701+
"Result", "result",
4702+
"Selection", "selection",
4703+
"Input", "input",
4704+
"Prompt", "prompt",
4705+
"Text", "text",
4706+
"Message", "message",
4707+
"Data", "data",
4708+
)
46744709
}
46754710
if event.Display == "" {
46764711
event.Display = scalarStringJSONField(fields, "Display", "display")
@@ -4686,6 +4721,50 @@ func (event *ScreenEvent) UnmarshalJSON(data []byte) error {
46864721
return nil
46874722
}
46884723

4724+
func normalizeScreenEventType(raw string) ScreenEventType {
4725+
name := normalizeActionName(raw)
4726+
switch name {
4727+
case "", "none", "no_event", "noop", "no_op":
4728+
return ScreenEventNone
4729+
case "prompt_submitted", "prompt_submit", "submit_prompt", "submit", "submitted", "message_submitted", "input_submitted":
4730+
return ScreenEventPromptSubmitted
4731+
case "dialog_action", "dialog_action_selected", "dialog_selected", "dialog_submit", "dialog_button", "dialog_button_clicked", "action_selected", "action_clicked":
4732+
return ScreenEventDialogAction
4733+
case "cancelled", "canceled", "cancel", "prompt_cancelled", "prompt_canceled", "dismissed", "escape":
4734+
return ScreenEventCancelled
4735+
case "interrupted", "interrupt", "stop", "stopped", "stop_generation", "cancel_generation":
4736+
return ScreenEventInterrupted
4737+
case "exit_pending", "pending_exit", "confirm_exit":
4738+
return ScreenEventExitPending
4739+
case "exit", "quit", "closed":
4740+
return ScreenEventExit
4741+
case "redraw", "clear", "clear_screen", "refresh", "refresh_screen":
4742+
return ScreenEventRedraw
4743+
case "toggle_transcript", "transcript_toggled", "show_transcript":
4744+
return ScreenEventToggleTranscript
4745+
case "toggle_todos", "toggle_todo", "toggle_tasks", "tasks_toggled":
4746+
return ScreenEventToggleTodos
4747+
case "external_editor", "open_editor", "editor_opened":
4748+
return ScreenEventExternalEditor
4749+
case "stash_prompt", "prompt_stashed", "stash":
4750+
return ScreenEventStashPrompt
4751+
case "kill_agents", "agents_killed", "cancel_agents":
4752+
return ScreenEventKillAgents
4753+
case "reverse_search", "history_search", "search_history":
4754+
return ScreenEventReverseSearch
4755+
case "reverse_search_selected", "history_search_selected", "search_selected":
4756+
return ScreenEventReverseSelected
4757+
case "focus_in", "focus", "focused":
4758+
return ScreenEventFocusIn
4759+
case "focus_out", "blur", "blurred":
4760+
return ScreenEventFocusOut
4761+
case "viewport_selected", "viewport_selection", "selection", "selected":
4762+
return ScreenEventViewportSelected
4763+
default:
4764+
return ScreenEventType(raw)
4765+
}
4766+
}
4767+
46894768
func (expect *DialogResultExpectation) UnmarshalJSON(data []byte) error {
46904769
data = normalizeBoolFields(data,
46914770
"Found",

internal/tui/tui_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9964,6 +9964,95 @@ func TestRunDialogRuntimeScriptAcceptsEventAndResultAliases(t *testing.T) {
99649964
}
99659965
}
99669966

9967+
func TestRunInteractionScriptNormalizesScreenEventAliases(t *testing.T) {
9968+
steps, err := ParseInteractionScript([]byte(`[
9969+
{
9970+
"text": "go",
9971+
"key": "enter",
9972+
"expectEvent": {
9973+
"eventName": "promptSubmitted",
9974+
"eventPayload": "go"
9975+
}
9976+
},
9977+
{
9978+
"text": "again",
9979+
"key": "enter",
9980+
"expectEvents": [
9981+
{
9982+
"eventType": "messageSubmitted",
9983+
"eventValue": "again"
9984+
}
9985+
]
9986+
},
9987+
{
9988+
"focusOut": true,
9989+
"expectEvent": {
9990+
"event": "focusOut"
9991+
},
9992+
"expectFocused": false
9993+
}
9994+
]`))
9995+
if err != nil {
9996+
t.Fatal(err)
9997+
}
9998+
screen := NewREPLScreen(42, 8, nil)
9999+
result, err := RunInteractionScriptChecked(&screen, steps)
10000+
if err != nil {
10001+
t.Fatal(err)
10002+
}
10003+
if len(result.Events) != 3 ||
10004+
result.Events[0].Type != ScreenEventPromptSubmitted || result.Events[0].Value != "go" ||
10005+
result.Events[1].Type != ScreenEventPromptSubmitted || result.Events[1].Value != "again" ||
10006+
result.Events[2].Type != ScreenEventFocusOut {
10007+
t.Fatalf("events = %#v", result.Events)
10008+
}
10009+
}
10010+
10011+
func TestRunDialogRuntimeScriptNormalizesDialogEventAliases(t *testing.T) {
10012+
steps, err := ParseInteractionScript([]byte(`[
10013+
{
10014+
"requestPermission": {"requestID": "perm_event", "tool": "Bash", "actions": "Approve"},
10015+
"expectDialog": {"active": true, "permissionID": "perm_event", "dialogKind": "permission"}
10016+
},
10017+
{
10018+
"key": "enter",
10019+
"expectEvent": {
10020+
"resource": {
10021+
"attributes": {
10022+
"eventName": "dialogAction",
10023+
"actionValue": "Approve",
10024+
"permissionID": "perm_event",
10025+
"dialogKind": "permission"
10026+
}
10027+
}
10028+
},
10029+
"expectDialogResult": {
10030+
"eventName": "dialogAction",
10031+
"actionValue": "Approve",
10032+
"permissionID": "perm_event",
10033+
"dialogKind": "permission",
10034+
"result": "allowed",
10035+
"found": true
10036+
}
10037+
}
10038+
]`))
10039+
if err != nil {
10040+
t.Fatal(err)
10041+
}
10042+
screen := NewREPLScreen(42, 8, nil)
10043+
runtime := NewDialogRuntime()
10044+
result, err := RunDialogRuntimeScriptChecked(&screen, runtime, "ready", steps)
10045+
if err != nil {
10046+
t.Fatal(err)
10047+
}
10048+
if len(result.Events) != 1 || result.Events[0].Type != ScreenEventDialogAction || result.Events[0].Value != "Approve" || result.Events[0].DialogID != "perm_event" {
10049+
t.Fatalf("events = %#v", result.Events)
10050+
}
10051+
if len(result.DialogResults) != 1 || result.DialogResults[0].Status != DialogResultAllowed {
10052+
t.Fatalf("dialog results = %#v", result.DialogResults)
10053+
}
10054+
}
10055+
996710056
func TestRunDialogRuntimeScriptChecksDialogResultCounts(t *testing.T) {
996810057
steps, err := ParseInteractionScript([]byte(`[
996910058
{

0 commit comments

Comments
 (0)