Преглед изворни кода

fix(simulation): normalize named key presses (#37523)

Kit Langton пре 3 недеља
родитељ
комит
3fc06accbd
2 измењених фајлова са 49 додато и 3 уклоњено
  1. 18 2
      packages/simulation/src/frontend/actions.ts
  2. 31 1
      packages/simulation/test/actions.test.ts

+ 18 - 2
packages/simulation/src/frontend/actions.ts

@@ -1,7 +1,14 @@
 import { tmpdir } from "node:os"
 import { extname, join, resolve } from "node:path"
 import type { CliRenderer, Renderable } from "@opentui/core"
-import { createMockKeys, createMockMouse, type MockInput, type MockMouse } from "@opentui/core/testing"
+import {
+  createMockKeys,
+  createMockMouse,
+  KeyCodes,
+  type KeyInput,
+  type MockInput,
+  type MockMouse,
+} from "@opentui/core/testing"
 import { Config, Effect, FileSystem } from "effect"
 import type { SimulationProtocol } from "../protocol"
 import { SimulationRenderer } from "./renderer"
@@ -26,6 +33,15 @@ type RenderBuffer = {
 
 const decoder = new TextDecoder()
 
+function isKeyCode(key: string): key is keyof typeof KeyCodes {
+  return Object.hasOwn(KeyCodes, key)
+}
+
+function keyInput(key: string): KeyInput {
+  const named = key.toUpperCase()
+  return isKeyCode(named) ? named : key
+}
+
 function children(renderable: Renderable) {
   return renderable.getChildren().filter((child): child is Renderable => "num" in child)
 }
@@ -154,7 +170,7 @@ export const execute = Effect.fn("SimulationActions.execute")(function* (harness
       yield* Effect.tryPromise(() => harness.mockInput.typeText(action.text))
       break
     case "ui.press":
-      harness.mockInput.pressKey(action.key, action.modifiers)
+      harness.mockInput.pressKey(keyInput(action.key), action.modifiers)
       break
     case "ui.enter":
       harness.mockInput.pressEnter()

+ 31 - 1
packages/simulation/test/actions.test.ts

@@ -1,5 +1,6 @@
 import { expect, test } from "bun:test"
-import { matches } from "../src/frontend/actions"
+import { Effect } from "effect"
+import { execute, type Harness, matches } from "../src/frontend/actions"
 
 test("matches literal screen text", () => {
   const harness = { screen: () => "OpenCode [ready].*" }
@@ -9,3 +10,32 @@ test("matches literal screen text", () => {
   expect(matches(harness, "OpenCode.*ready")).toBe(false)
   expect(matches(harness, "opencode")).toBe(false)
 })
+
+test("normalizes named keys for OpenTUI", async () => {
+  const pressed: Array<readonly [string, object | undefined]> = []
+  const harness = {
+    renderer: {
+      root: { getChildren: () => [] },
+      currentFocusedRenderable: undefined,
+      currentFocusedEditor: undefined,
+    },
+    mockInput: {
+      pressKey: (key: string, modifiers?: object) => pressed.push([key, modifiers]),
+    },
+    renderOnce: async () => {},
+  } as unknown as Harness
+
+  await Effect.runPromise(
+    execute(harness, {
+      type: "ui.press",
+      key: "escape",
+      modifiers: { ctrl: true },
+    }),
+  )
+  await Effect.runPromise(execute(harness, { type: "ui.press", key: "x" }))
+
+  expect(pressed).toEqual([
+    ["ESCAPE", { ctrl: true }],
+    ["x", undefined],
+  ])
+})