Ver Fonte

feat(simulation): add literal screen text matching (#36085)

James Long há 1 mês atrás
pai
commit
855a9569b5

+ 4 - 0
packages/simulation/src/frontend/actions.ts

@@ -104,6 +104,10 @@ export function state(harness: Harness) {
   }
 }
 
+export function matches(harness: Pick<Harness, "screen">, text: string) {
+  return harness.screen().includes(text)
+}
+
 export async function screenshot(harness: Harness, name?: string) {
   await harness.renderOnce()
   const image = SimulationPng.screenshot(harness.renderer)

+ 2 - 0
packages/simulation/src/frontend/server.ts

@@ -21,6 +21,8 @@ async function handle(
     case "ui.state": {
       return SimulationActions.state(harness)
     }
+    case "ui.matches":
+      return SimulationActions.matches(harness, request.params.text)
     case "ui.recording.finish":
       if (!finishRecording) throw new Error("UI recording is not available")
       return finishRecording()

+ 7 - 0
packages/simulation/src/protocol/index.ts

@@ -97,12 +97,18 @@ export namespace Frontend {
   export const RecordingFinish = Schema.String
   export type RecordingFinish = Schema.Schema.Type<typeof RecordingFinish>
 
+  export const Matches = Schema.Boolean
+  export type Matches = Schema.Schema.Type<typeof Matches>
+
   export const ScreenshotParams = Schema.Struct({ name: Schema.optional(Schema.String) })
   export interface ScreenshotParams extends Schema.Schema.Type<typeof ScreenshotParams> {}
 
   export const TypeParams = Schema.Struct({ text: Schema.String })
   export interface TypeParams extends Schema.Schema.Type<typeof TypeParams> {}
 
+  export const MatchesParams = Schema.Struct({ text: Schema.String })
+  export interface MatchesParams extends Schema.Schema.Type<typeof MatchesParams> {}
+
   export const PressParams = Schema.Struct({ key: Schema.String, modifiers: Schema.optional(KeyModifiers) })
   export interface PressParams extends Schema.Schema.Type<typeof PressParams> {}
 
@@ -121,6 +127,7 @@ export namespace Frontend {
     Schema.Struct({ ...JsonRpc.RequestFields, method: Schema.Literal("ui.arrow"), params: ArrowParams }),
     Schema.Struct({ ...JsonRpc.RequestFields, method: Schema.Literal("ui.focus"), params: FocusParams }),
     Schema.Struct({ ...JsonRpc.RequestFields, method: Schema.Literal("ui.click"), params: ClickParams }),
+    Schema.Struct({ ...JsonRpc.RequestFields, method: Schema.Literal("ui.matches"), params: MatchesParams }),
     Schema.Struct({
       ...JsonRpc.RequestFields,
       method: Schema.Literal("ui.screenshot"),

+ 11 - 0
packages/simulation/test/actions.test.ts

@@ -0,0 +1,11 @@
+import { expect, test } from "bun:test"
+import { matches } from "../src/frontend/actions"
+
+test("matches literal screen text", () => {
+  const harness = { screen: () => "OpenCode [ready].*" }
+
+  expect(matches(harness, "OpenCode")).toBe(true)
+  expect(matches(harness, "[ready].*")).toBe(true)
+  expect(matches(harness, "OpenCode.*ready")).toBe(false)
+  expect(matches(harness, "opencode")).toBe(false)
+})

+ 21 - 0
packages/simulation/test/protocol.test.ts

@@ -0,0 +1,21 @@
+import { expect, test } from "bun:test"
+import { Frontend } from "../src/protocol"
+
+test("decodes ui.matches text params", () => {
+  expect(
+    Frontend.decodeRequest({
+      jsonrpc: "2.0",
+      id: 1,
+      method: "ui.matches",
+      params: { text: "OpenCode [ready].*" },
+    }),
+  ).toMatchObject({ method: "ui.matches", params: { text: "OpenCode [ready].*" } })
+  expect(() =>
+    Frontend.decodeRequest({
+      jsonrpc: "2.0",
+      id: 1,
+      method: "ui.matches",
+      params: { pattern: "OpenCode.*" },
+    }),
+  ).toThrow()
+})