From 25a02825a5b81f8c5a2db980af9dbb9cc523b208 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 25 Apr 2026 01:50:22 +0100 Subject: [PATCH] test(google-meet): share plugin harness --- extensions/google-meet/index.create.test.ts | 109 +----------- extensions/google-meet/index.test.ts | 157 ++---------------- .../src/test-support/plugin-harness.ts | 155 +++++++++++++++++ 3 files changed, 171 insertions(+), 250 deletions(-) create mode 100644 extensions/google-meet/src/test-support/plugin-harness.ts diff --git a/extensions/google-meet/index.create.test.ts b/extensions/google-meet/index.create.test.ts index ec659a7af06..71b61ebf1ee 100644 --- a/extensions/google-meet/index.create.test.ts +++ b/extensions/google-meet/index.create.test.ts @@ -1,11 +1,10 @@ import { Command } from "commander"; -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { createTestPluginApi } from "../../test/helpers/plugins/plugin-api.ts"; import plugin from "./index.js"; import { registerGoogleMeetCli } from "./src/cli.js"; import { resolveGoogleMeetConfig } from "./src/config.js"; import type { GoogleMeetRuntime } from "./src/runtime.js"; +import { captureStdout, setupGoogleMeetPlugin } from "./src/test-support/plugin-harness.js"; import { CREATE_MEET_FROM_BROWSER_SCRIPT } from "./src/transports/chrome-create.js"; const voiceCallMocks = vi.hoisted(() => ({ @@ -37,23 +36,11 @@ vi.mock("./src/voice-call-gateway.js", () => ({ endMeetVoiceCallGatewayCall: voiceCallMocks.endMeetVoiceCallGatewayCall, })); -const noopLogger = { - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - debug: vi.fn(), -}; - -function captureStdout() { - let output = ""; - const writeSpy = vi.spyOn(process.stdout, "write").mockImplementation(((chunk: unknown) => { - output += String(chunk); - return true; - }) as typeof process.stdout.write); - return { - output: () => output, - restore: () => writeSpy.mockRestore(), - }; +function setup( + config?: Parameters[1], + options?: Parameters[2], +) { + return setupGoogleMeetPlugin(plugin, config, options); } async function runCreateMeetBrowserScript(params: { buttonText: string }) { @@ -89,90 +76,6 @@ async function runCreateMeetBrowserScript(params: { buttonText: string }) { return { button, result: await fn() }; } -function setup( - config: Record = {}, - options: { - nodesInvokeHandler?: (params: { - nodeId: string; - command: string; - params?: unknown; - timeoutMs?: number; - }) => Promise; - } = {}, -) { - const methods = new Map(); - const tools: unknown[] = []; - const nodesList = vi.fn(async () => ({ - nodes: [ - { - nodeId: "node-1", - displayName: "parallels-macos", - connected: true, - caps: ["browser"], - commands: ["browser.proxy", "googlemeet.chrome"], - }, - ], - })); - const nodesInvoke = vi.fn(async (params) => { - if (options.nodesInvokeHandler) { - return options.nodesInvokeHandler(params); - } - if (params.command === "browser.proxy") { - const proxy = params.params as { path?: string; body?: { url?: string; targetId?: string } }; - if (proxy.path === "/tabs") { - return { payload: { result: { running: true, tabs: [] } } }; - } - if (proxy.path === "/tabs/open") { - return { - payload: { - result: { - targetId: "tab-1", - title: "Meet", - url: proxy.body?.url ?? "https://meet.google.com/abc-defg-hij", - }, - }, - }; - } - return { payload: { result: { ok: true } } }; - } - return { payload: { launched: true } }; - }); - const runCommandWithTimeout = vi.fn(async (argv: string[]) => { - if (argv[0] === "/usr/sbin/system_profiler") { - return { code: 0, stdout: "BlackHole 2ch", stderr: "" }; - } - return { code: 0, stdout: "", stderr: "" }; - }); - const api = createTestPluginApi({ - id: "google-meet", - name: "Google Meet", - description: "test", - version: "0", - source: "test", - config: {}, - pluginConfig: config, - runtime: { - system: { - runCommandWithTimeout, - formatNativeDependencyHint: vi.fn(() => "Install with brew install blackhole-2ch."), - }, - nodes: { - list: nodesList, - invoke: nodesInvoke, - }, - } as unknown as OpenClawPluginApi["runtime"], - logger: noopLogger, - registerGatewayMethod: (method: string, handler: unknown) => methods.set(method, handler), - registerTool: (tool: unknown) => tools.push(tool), - }); - plugin.register(api); - return { - methods, - tools, - nodesInvoke, - }; -} - describe("google-meet create flow", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/extensions/google-meet/index.test.ts b/extensions/google-meet/index.test.ts index 34de65415cb..9a2d5e2e248 100644 --- a/extensions/google-meet/index.test.ts +++ b/extensions/google-meet/index.test.ts @@ -1,10 +1,8 @@ import { EventEmitter } from "node:events"; import { PassThrough, Writable } from "node:stream"; import { Command } from "commander"; -import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"; import type { RealtimeVoiceProviderPlugin } from "openclaw/plugin-sdk/realtime-voice"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { createTestPluginApi } from "../../test/helpers/plugins/plugin-api.ts"; import plugin from "./index.js"; import { registerGoogleMeetCli } from "./src/cli.js"; import { resolveGoogleMeetConfig, resolveGoogleMeetConfigWithEnv } from "./src/config.js"; @@ -23,6 +21,11 @@ import { startNodeRealtimeAudioBridge } from "./src/realtime-node.js"; import { startCommandRealtimeAudioBridge } from "./src/realtime.js"; import { normalizeMeetUrl } from "./src/runtime.js"; import type { GoogleMeetRuntime } from "./src/runtime.js"; +import { + captureStdout, + noopLogger, + setupGoogleMeetPlugin, +} from "./src/test-support/plugin-harness.js"; import { buildMeetDtmfSequence, normalizeDialInNumber } from "./src/transports/twilio.js"; const voiceCallMocks = vi.hoisted(() => ({ @@ -54,23 +57,11 @@ vi.mock("./src/voice-call-gateway.js", () => ({ endMeetVoiceCallGatewayCall: voiceCallMocks.endMeetVoiceCallGatewayCall, })); -const noopLogger = { - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - debug: vi.fn(), -}; - -function captureStdout() { - let output = ""; - const writeSpy = vi.spyOn(process.stdout, "write").mockImplementation(((chunk: unknown) => { - output += String(chunk); - return true; - }) as typeof process.stdout.write); - return { - output: () => output, - restore: () => writeSpy.mockRestore(), - }; +function setup( + config?: Parameters[1], + options?: Parameters[2], +) { + return setupGoogleMeetPlugin(plugin, config, options); } type TestBridgeProcess = { @@ -82,134 +73,6 @@ type TestBridgeProcess = { on: EventEmitter["on"]; }; -type NodeListResult = { - nodes: Array<{ - nodeId: string; - displayName?: string; - connected?: boolean; - commands?: string[]; - caps?: string[]; - remoteIp?: string; - }>; -}; - -function setup( - config: Record = {}, - options: { - fullConfig?: Record; - nodesListResult?: NodeListResult; - nodesInvokeResult?: unknown; - browserActResult?: Record; - nodesInvokeHandler?: (params: { - nodeId: string; - command: string; - params?: unknown; - timeoutMs?: number; - }) => Promise; - } = {}, -) { - const methods = new Map(); - const tools: unknown[] = []; - const cliRegistrations: unknown[] = []; - const nodeHostCommands: unknown[] = []; - const nodesList = vi.fn( - async () => - options.nodesListResult ?? { - nodes: [ - { - nodeId: "node-1", - displayName: "parallels-macos", - connected: true, - caps: ["browser"], - commands: ["browser.proxy", "googlemeet.chrome"], - }, - ], - }, - ); - const nodesInvoke = vi.fn(async (params) => { - if (options.nodesInvokeHandler) { - return options.nodesInvokeHandler(params); - } - if (params.command === "browser.proxy") { - const proxy = params.params as { path?: string; body?: { url?: string; targetId?: string } }; - if (proxy.path === "/tabs") { - return { payload: { result: { running: true, tabs: [] } } }; - } - if (proxy.path === "/tabs/open") { - return { - payload: { - result: { - targetId: "tab-1", - title: "Meet", - url: proxy.body?.url ?? "https://meet.google.com/abc-defg-hij", - }, - }, - }; - } - if (proxy.path === "/act") { - return { - payload: { - result: { - ok: true, - targetId: proxy.body?.targetId ?? "tab-1", - result: JSON.stringify( - options.browserActResult ?? { - inCall: true, - micMuted: false, - title: "Meet call", - url: "https://meet.google.com/abc-defg-hij", - }, - ), - }, - }, - }; - } - return { payload: { result: { ok: true } } }; - } - return options.nodesInvokeResult ?? { launched: true }; - }); - const runCommandWithTimeout = vi.fn(async (argv: string[]) => { - if (argv[0] === "/usr/sbin/system_profiler") { - return { code: 0, stdout: "BlackHole 2ch", stderr: "" }; - } - return { code: 0, stdout: "", stderr: "" }; - }); - const api = createTestPluginApi({ - id: "google-meet", - name: "Google Meet", - description: "test", - version: "0", - source: "test", - config: options.fullConfig ?? {}, - pluginConfig: config, - runtime: { - system: { - runCommandWithTimeout, - formatNativeDependencyHint: vi.fn(() => "Install with brew install blackhole-2ch."), - }, - nodes: { - list: nodesList, - invoke: nodesInvoke, - }, - } as unknown as OpenClawPluginApi["runtime"], - logger: noopLogger, - registerGatewayMethod: (method: string, handler: unknown) => methods.set(method, handler), - registerTool: (tool: unknown) => tools.push(tool), - registerCli: (_registrar: unknown, opts: unknown) => cliRegistrations.push(opts), - registerNodeHostCommand: (command: unknown) => nodeHostCommands.push(command), - }); - plugin.register(api); - return { - cliRegistrations, - methods, - tools, - runCommandWithTimeout, - nodesList, - nodesInvoke, - nodeHostCommands, - }; -} - describe("google-meet plugin", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/extensions/google-meet/src/test-support/plugin-harness.ts b/extensions/google-meet/src/test-support/plugin-harness.ts new file mode 100644 index 00000000000..3d4066e5022 --- /dev/null +++ b/extensions/google-meet/src/test-support/plugin-harness.ts @@ -0,0 +1,155 @@ +import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"; +import { vi } from "vitest"; +import { createTestPluginApi } from "../../../../test/helpers/plugins/plugin-api.ts"; + +type GoogleMeetTestPluginEntry = { + register(api: OpenClawPluginApi): void; +}; + +export const noopLogger = { + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + debug: vi.fn(), +}; + +export type GoogleMeetTestNodeListResult = { + nodes: Array<{ + nodeId: string; + displayName?: string; + connected?: boolean; + commands?: string[]; + caps?: string[]; + remoteIp?: string; + }>; +}; + +export function captureStdout() { + let output = ""; + const writeSpy = vi.spyOn(process.stdout, "write").mockImplementation(((chunk: unknown) => { + output += String(chunk); + return true; + }) as typeof process.stdout.write); + return { + output: () => output, + restore: () => writeSpy.mockRestore(), + }; +} + +export function setupGoogleMeetPlugin( + plugin: GoogleMeetTestPluginEntry, + config: Record = {}, + options: { + fullConfig?: Record; + nodesListResult?: GoogleMeetTestNodeListResult; + nodesInvokeResult?: unknown; + browserActResult?: Record; + nodesInvokeHandler?: (params: { + nodeId: string; + command: string; + params?: unknown; + timeoutMs?: number; + }) => Promise; + } = {}, +) { + const methods = new Map(); + const tools: unknown[] = []; + const cliRegistrations: unknown[] = []; + const nodeHostCommands: unknown[] = []; + const nodesList = vi.fn( + async () => + options.nodesListResult ?? { + nodes: [ + { + nodeId: "node-1", + displayName: "parallels-macos", + connected: true, + caps: ["browser"], + commands: ["browser.proxy", "googlemeet.chrome"], + }, + ], + }, + ); + const nodesInvoke = vi.fn(async (params) => { + if (options.nodesInvokeHandler) { + return options.nodesInvokeHandler(params); + } + if (params.command === "browser.proxy") { + const proxy = params.params as { path?: string; body?: { url?: string; targetId?: string } }; + if (proxy.path === "/tabs") { + return { payload: { result: { running: true, tabs: [] } } }; + } + if (proxy.path === "/tabs/open") { + return { + payload: { + result: { + targetId: "tab-1", + title: "Meet", + url: proxy.body?.url ?? "https://meet.google.com/abc-defg-hij", + }, + }, + }; + } + if (proxy.path === "/act") { + return { + payload: { + result: { + ok: true, + targetId: proxy.body?.targetId ?? "tab-1", + result: JSON.stringify( + options.browserActResult ?? { + inCall: true, + micMuted: false, + title: "Meet call", + url: "https://meet.google.com/abc-defg-hij", + }, + ), + }, + }, + }; + } + return { payload: { result: { ok: true } } }; + } + return options.nodesInvokeResult ?? { launched: true }; + }); + const runCommandWithTimeout = vi.fn(async (argv: string[]) => { + if (argv[0] === "/usr/sbin/system_profiler") { + return { code: 0, stdout: "BlackHole 2ch", stderr: "" }; + } + return { code: 0, stdout: "", stderr: "" }; + }); + const api = createTestPluginApi({ + id: "google-meet", + name: "Google Meet", + description: "test", + version: "0", + source: "test", + config: options.fullConfig ?? {}, + pluginConfig: config, + runtime: { + system: { + runCommandWithTimeout, + formatNativeDependencyHint: vi.fn(() => "Install with brew install blackhole-2ch."), + }, + nodes: { + list: nodesList, + invoke: nodesInvoke, + }, + } as unknown as OpenClawPluginApi["runtime"], + logger: noopLogger, + registerGatewayMethod: (method: string, handler: unknown) => methods.set(method, handler), + registerTool: (tool: unknown) => tools.push(tool), + registerCli: (_registrar: unknown, opts: unknown) => cliRegistrations.push(opts), + registerNodeHostCommand: (command: unknown) => nodeHostCommands.push(command), + }); + plugin.register(api); + return { + cliRegistrations, + methods, + tools, + runCommandWithTimeout, + nodesList, + nodesInvoke, + nodeHostCommands, + }; +}