mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:40:43 +00:00
test(google-meet): share plugin harness
This commit is contained in:
@@ -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<typeof setupGoogleMeetPlugin>[1],
|
||||
options?: Parameters<typeof setupGoogleMeetPlugin>[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<string, unknown> = {},
|
||||
options: {
|
||||
nodesInvokeHandler?: (params: {
|
||||
nodeId: string;
|
||||
command: string;
|
||||
params?: unknown;
|
||||
timeoutMs?: number;
|
||||
}) => Promise<unknown>;
|
||||
} = {},
|
||||
) {
|
||||
const methods = new Map<string, unknown>();
|
||||
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();
|
||||
|
||||
@@ -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<typeof setupGoogleMeetPlugin>[1],
|
||||
options?: Parameters<typeof setupGoogleMeetPlugin>[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<string, unknown> = {},
|
||||
options: {
|
||||
fullConfig?: Record<string, unknown>;
|
||||
nodesListResult?: NodeListResult;
|
||||
nodesInvokeResult?: unknown;
|
||||
browserActResult?: Record<string, unknown>;
|
||||
nodesInvokeHandler?: (params: {
|
||||
nodeId: string;
|
||||
command: string;
|
||||
params?: unknown;
|
||||
timeoutMs?: number;
|
||||
}) => Promise<unknown>;
|
||||
} = {},
|
||||
) {
|
||||
const methods = new Map<string, unknown>();
|
||||
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();
|
||||
|
||||
155
extensions/google-meet/src/test-support/plugin-harness.ts
Normal file
155
extensions/google-meet/src/test-support/plugin-harness.ts
Normal file
@@ -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<string, unknown> = {},
|
||||
options: {
|
||||
fullConfig?: Record<string, unknown>;
|
||||
nodesListResult?: GoogleMeetTestNodeListResult;
|
||||
nodesInvokeResult?: unknown;
|
||||
browserActResult?: Record<string, unknown>;
|
||||
nodesInvokeHandler?: (params: {
|
||||
nodeId: string;
|
||||
command: string;
|
||||
params?: unknown;
|
||||
timeoutMs?: number;
|
||||
}) => Promise<unknown>;
|
||||
} = {},
|
||||
) {
|
||||
const methods = new Map<string, unknown>();
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user