From cde6f22ecf05d2068992b77f4e8245bfadc08f87 Mon Sep 17 00:00:00 2001 From: Ljy-0827 Date: Thu, 6 Aug 2026 16:14:18 +0800 Subject: [PATCH] fix(extension): ci and bug fix --- .../src/lib/recording-step-buffer.ts | 8 +++ .../src/tools/__tests__/dispatcher.test.ts | 16 ++--- .../src/tools/__tests__/observation.test.ts | 63 +++++++++++++++++++ apps/extension/src/tools/observation.ts | 1 + 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/apps/extension/src/lib/recording-step-buffer.ts b/apps/extension/src/lib/recording-step-buffer.ts index 0c7c26e..c1c5086 100644 --- a/apps/extension/src/lib/recording-step-buffer.ts +++ b/apps/extension/src/lib/recording-step-buffer.ts @@ -21,6 +21,14 @@ function toDraftStep(payload: RecordStepPayload): DraftTraceStep | null { ...(pageUrl ? { page_url: pageUrl } : {}), } : null; + case "hover": + return payload.target + ? { + op: "hover", + target: payload.target, + ...(pageUrl ? { page_url: pageUrl } : {}), + } + : null; case "fill": return payload.target ? { diff --git a/apps/extension/src/tools/__tests__/dispatcher.test.ts b/apps/extension/src/tools/__tests__/dispatcher.test.ts index ec2c84c..30cc60b 100644 --- a/apps/extension/src/tools/__tests__/dispatcher.test.ts +++ b/apps/extension/src/tools/__tests__/dispatcher.test.ts @@ -9,6 +9,8 @@ import type { } from "@/transport/types"; import { ToolDispatcher } from "../dispatcher"; +type TestDispatcherCdp = NonNullable[0]["cdp"]>; + function fakeTransport() { const handlers = new Set(); const stateHandlers = new Set(); @@ -129,7 +131,7 @@ describe("ToolDispatcher", () => { setUserAgentOverride: vi.fn(async () => {}), setTouchEmulationEnabled: vi.fn(async () => {}), }; - const dispatcher = new ToolDispatcher({ transport, sessions, cdp }); + const dispatcher = new ToolDispatcher({ transport, sessions, cdp: cdp as TestDispatcherCdp }); dispatcher.start(); deliver(makeRequest("tool.console", { session_id: "aa11" })); @@ -172,7 +174,7 @@ describe("ToolDispatcher", () => { setUserAgentOverride: vi.fn(async () => {}), setTouchEmulationEnabled: vi.fn(async () => {}), }; - const dispatcher = new ToolDispatcher({ transport, sessions, cdp }); + const dispatcher = new ToolDispatcher({ transport, sessions, cdp: cdp as TestDispatcherCdp }); dispatcher.start(); deliver(makeRequest("tool.session_stop", { session_id: "aa11" })); @@ -327,9 +329,9 @@ describe("ToolDispatcher", () => { }); const order: string[] = []; const cdp = { - send: vi.fn(async () => { + send: vi.fn(async () => { order.push("hover"); - return {}; + return {} as T; }), detachSession: vi.fn(async () => {}), ensureNetworkCapture: vi.fn(async () => {}), @@ -344,7 +346,7 @@ describe("ToolDispatcher", () => { setUserAgentOverride: vi.fn(async () => {}), setTouchEmulationEnabled: vi.fn(async () => {}), }; - const dispatcher = new ToolDispatcher({ transport, sessions, cdp }); + const dispatcher = new ToolDispatcher({ transport, sessions, cdp: cdp as TestDispatcherCdp }); ( dispatcher as unknown as { rememberHover: (sessionId: string, result: unknown) => unknown } @@ -408,7 +410,7 @@ describe("ToolDispatcher", () => { }, }); const cdp = { - send: vi.fn(async () => ({})), + send: vi.fn(async () => ({} as T)), detachSession: vi.fn(async () => {}), ensureNetworkCapture: vi.fn(async () => {}), networkEntriesSince: vi.fn(() => ({ @@ -422,7 +424,7 @@ describe("ToolDispatcher", () => { setUserAgentOverride: vi.fn(async () => {}), setTouchEmulationEnabled: vi.fn(async () => {}), }; - const dispatcher = new ToolDispatcher({ transport, sessions, cdp }); + const dispatcher = new ToolDispatcher({ transport, sessions, cdp: cdp as TestDispatcherCdp }); const helpers = dispatcher as unknown as { rememberHover: (sessionId: string, result: unknown) => unknown; setHoverBypass: (sessionId: string, tabId: number, enabled: boolean) => Promise; diff --git a/apps/extension/src/tools/__tests__/observation.test.ts b/apps/extension/src/tools/__tests__/observation.test.ts index 9d9e003..89a4ec3 100644 --- a/apps/extension/src/tools/__tests__/observation.test.ts +++ b/apps/extension/src/tools/__tests__/observation.test.ts @@ -1543,6 +1543,69 @@ describe("buildVomScene", () => { expect(renderVom(scene).text).toContain('@e1 button "image" [hover first: My profile]'); }); + it("deduplicates hover surface probes by original trigger backend id", () => { + const axNodes: CdpAxNode[] = [ + { + nodeId: "1", + role: { type: "role", value: "RootWebArea" }, + backendDOMNodeId: 10, + childIds: ["2"], + }, + { + nodeId: "2", + parentId: "1", + role: { type: "role", value: "button" }, + name: { type: "computedString", value: "image" }, + backendDOMNodeId: 21, + }, + ]; + const scene = buildVomScene(axNodes, { + viewport: { width: 1000, height: 800 }, + iframeNodes: new Map(), + excludedBackendNodeIds: new Set(), + surfaceProbes: [ + { triggerBackendNodeId: 20, triggerAction: "hover", subItems: ["My profile"] }, + { triggerBackendNodeId: 20, triggerAction: "hover", subItems: ["Sign out"] }, + ], + nodes: [ + { + backendNodeId: 10, + parentBackendNodeId: null, + tag: "body", + attrs: {}, + rect: { x: 0, y: 0, w: 1000, h: 800 }, + paintOrder: 0, + position: "static", + pointerEvents: "auto", + }, + { + backendNodeId: 20, + parentBackendNodeId: 10, + tag: "div", + attrs: { class: "tg-avatar" }, + rect: { x: 900, y: 10, w: 30, h: 30 }, + paintOrder: 1, + position: "static", + pointerEvents: "auto", + }, + { + backendNodeId: 21, + parentBackendNodeId: 20, + tag: "div", + attrs: { class: "tg-avatar__inner" }, + rect: { x: 902, y: 12, w: 26, h: 26 }, + paintOrder: 2, + position: "static", + pointerEvents: "auto", + }, + ], + }); + + expect(scene.surfaces).toEqual([ + { triggerId: 21, triggerAction: "hover", subItems: ["My profile"] }, + ]); + }); + it("maps hover surface probes to DOM-recovered custom controls", () => { const axNodes: CdpAxNode[] = [ { diff --git a/apps/extension/src/tools/observation.ts b/apps/extension/src/tools/observation.ts index 59d39ff..a1da513 100644 --- a/apps/extension/src/tools/observation.ts +++ b/apps/extension/src/tools/observation.ts @@ -927,6 +927,7 @@ function buildConditionalSurfaces(nodes: VomNode[], captured: CapturedViewModel) probe.triggerPoint, ); if (!match) continue; + used.add(probe.triggerBackendNodeId); used.add(match.id); surfaces.push({ triggerId: match.id,