feat(simulation): add literal screen text matching (#36085)
This commit is contained in:
@@ -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) {
|
export async function screenshot(harness: Harness, name?: string) {
|
||||||
await harness.renderOnce()
|
await harness.renderOnce()
|
||||||
const image = SimulationPng.screenshot(harness.renderer)
|
const image = SimulationPng.screenshot(harness.renderer)
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ async function handle(
|
|||||||
case "ui.state": {
|
case "ui.state": {
|
||||||
return SimulationActions.state(harness)
|
return SimulationActions.state(harness)
|
||||||
}
|
}
|
||||||
|
case "ui.matches":
|
||||||
|
return SimulationActions.matches(harness, request.params.text)
|
||||||
case "ui.recording.finish":
|
case "ui.recording.finish":
|
||||||
if (!finishRecording) throw new Error("UI recording is not available")
|
if (!finishRecording) throw new Error("UI recording is not available")
|
||||||
return finishRecording()
|
return finishRecording()
|
||||||
|
|||||||
@@ -97,12 +97,18 @@ export namespace Frontend {
|
|||||||
export const RecordingFinish = Schema.String
|
export const RecordingFinish = Schema.String
|
||||||
export type RecordingFinish = Schema.Schema.Type<typeof RecordingFinish>
|
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 const ScreenshotParams = Schema.Struct({ name: Schema.optional(Schema.String) })
|
||||||
export interface ScreenshotParams extends Schema.Schema.Type<typeof ScreenshotParams> {}
|
export interface ScreenshotParams extends Schema.Schema.Type<typeof ScreenshotParams> {}
|
||||||
|
|
||||||
export const TypeParams = Schema.Struct({ text: Schema.String })
|
export const TypeParams = Schema.Struct({ text: Schema.String })
|
||||||
export interface TypeParams extends Schema.Schema.Type<typeof TypeParams> {}
|
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 const PressParams = Schema.Struct({ key: Schema.String, modifiers: Schema.optional(KeyModifiers) })
|
||||||
export interface PressParams extends Schema.Schema.Type<typeof PressParams> {}
|
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.arrow"), params: ArrowParams }),
|
||||||
Schema.Struct({ ...JsonRpc.RequestFields, method: Schema.Literal("ui.focus"), params: FocusParams }),
|
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.click"), params: ClickParams }),
|
||||||
|
Schema.Struct({ ...JsonRpc.RequestFields, method: Schema.Literal("ui.matches"), params: MatchesParams }),
|
||||||
Schema.Struct({
|
Schema.Struct({
|
||||||
...JsonRpc.RequestFields,
|
...JsonRpc.RequestFields,
|
||||||
method: Schema.Literal("ui.screenshot"),
|
method: Schema.Literal("ui.screenshot"),
|
||||||
|
|||||||
@@ -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)
|
||||||
|
})
|
||||||
@@ -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()
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user