惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

云风的 BLOG
云风的 BLOG
The GitHub Blog
The GitHub Blog
A
About on SuperTechFans
P
Proofpoint News Feed
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
IT之家
IT之家
Microsoft Security Blog
Microsoft Security Blog
F
Fortinet All Blogs
人人都是产品经理
人人都是产品经理
博客园 - 叶小钗
C
Check Point Blog
Microsoft Azure Blog
Microsoft Azure Blog
aimingoo的专栏
aimingoo的专栏
月光博客
月光博客
美团技术团队
D
Docker
博客园 - Franky
Y
Y Combinator Blog
大猫的无限游戏
大猫的无限游戏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 【当耐特】
罗磊的独立博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
test: tighten google meet create assertions · openclaw/op...
steipete · 2026-05-11 · via Recent Commits to openclaw:main

@@ -99,6 +99,51 @@ async function runCreateMeetBrowserScript(params: { buttonText: string }) {

9999

return { button, result: await fn() };

100100

}

101101102+

function requireRecord(value: unknown, label: string): Record<string, unknown> {

103+

expect(value, label).toBeTypeOf("object");

104+

expect(value, label).not.toBeNull();

105+

return value as Record<string, unknown>;

106+

}

107+108+

function mockCalls(mock: unknown, label: string): Array<Array<unknown>> {

109+

const mockState = (mock as { mock?: { calls?: Array<Array<unknown>> } }).mock;

110+

expect(mockState, `${label}.mock`).toBeDefined();

111+

expect(Array.isArray(mockState?.calls), `${label}.mock.calls`).toBe(true);

112+

return mockState?.calls ?? [];

113+

}

114+115+

function findMockCall(mock: unknown, label: string, predicate: (call: Array<unknown>) => boolean) {

116+

const call = mockCalls(mock, label).find(predicate);

117+

expect(call, label).toBeDefined();

118+

return call as Array<unknown>;

119+

}

120+121+

function responsePayload(respond: unknown): Record<string, unknown> {

122+

const calls = mockCalls(respond, "respond");

123+

expect(calls[0]?.[0]).toBe(true);

124+

return requireRecord(calls[0]?.[1], "response payload");

125+

}

126+127+

function responseErrorPayload(respond: unknown): Record<string, unknown> {

128+

const calls = mockCalls(respond, "respond");

129+

expect(calls[0]?.[0]).toBe(false);

130+

return requireRecord(calls[0]?.[1], "response payload");

131+

}

132+133+

function findNodeInvokeParams(

134+

nodesInvoke: unknown,

135+

label: string,

136+

predicate: (params: Record<string, unknown>) => boolean,

137+

) {

138+

const call = findMockCall(nodesInvoke, label, ([value]) => {

139+

if (!value || typeof value !== "object") {

140+

return false;

141+

}

142+

return predicate(value as Record<string, unknown>);

143+

});

144+

return requireRecord(call[0], label);

145+

}

146+102147

describe("google-meet create flow", () => {

103148

beforeEach(() => {

104149

vi.clearAllMocks();

@@ -164,12 +209,15 @@ describe("google-meet create flow", () => {

164209

);

165210

expect(stdout.output()).toContain("meeting uri: https://meet.google.com/new-abcd-xyz");

166211

expect(stdout.output()).toContain("space: spaces/new-space");

167-

expect(fetchMock).toHaveBeenCalledWith(

168-

"https://meet.googleapis.com/v2/spaces",

169-

expect.objectContaining({

170-

method: "POST",

171-

body: JSON.stringify({ config: { accessType: "OPEN", entryPointAccess: "ALL" } }),

172-

}),

212+

const createSpaceCall = findMockCall(

213+

fetchMock,

214+

"create space fetch",

215+

([url]) => url === "https://meet.googleapis.com/v2/spaces",

216+

);

217+

const createSpaceInit = requireRecord(createSpaceCall[1], "create space init");

218+

expect(createSpaceInit.method).toBe("POST");

219+

expect(createSpaceInit.body).toBe(

220+

JSON.stringify({ config: { accessType: "OPEN", entryPointAccess: "ALL" } }),

173221

);

174222

} finally {

175223

stdout.restore();

@@ -228,22 +276,27 @@ describe("google-meet create flow", () => {

228276229277

await handler?.({ params: { join: false }, respond });

230278231-

expect(respond.mock.calls[0]?.[0]).toBe(true);

232-

expect(respond.mock.calls[0]?.[1]).toMatchObject({

233-

source: "browser",

234-

meetingUri: "https://meet.google.com/browser-made-url",

235-

joined: false,

236-

browser: { nodeId: "node-1", targetId: "tab-1" },

279+

const payload = responsePayload(respond);

280+

expect(payload.source).toBe("browser");

281+

expect(payload.meetingUri).toBe("https://meet.google.com/browser-made-url");

282+

expect(payload.joined).toBe(false);

283+

const browser = requireRecord(payload.browser, "browser payload");

284+

expect(browser.nodeId).toBe("node-1");

285+

expect(browser.targetId).toBe("tab-1");

286+

findNodeInvokeParams(nodesInvoke, "open create tab", (params) => {

287+

if (params.command !== "browser.proxy") {

288+

return false;

289+

}

290+

if (!params.params || typeof params.params !== "object") {

291+

return false;

292+

}

293+

const proxy = params.params as Record<string, unknown>;

294+

if (!proxy.body || typeof proxy.body !== "object") {

295+

return false;

296+

}

297+

const body = proxy.body as Record<string, unknown>;

298+

return proxy.path === "/tabs/open" && body.url === "https://meet.google.com/new";

237299

});

238-

expect(nodesInvoke).toHaveBeenCalledWith(

239-

expect.objectContaining({

240-

command: "browser.proxy",

241-

params: expect.objectContaining({

242-

path: "/tabs/open",

243-

body: { url: "https://meet.google.com/new" },

244-

}),

245-

}),

246-

);

247300

});

248301249302

it("rejects access policy flags when tool create would use browser fallback", async () => {

@@ -322,23 +375,22 @@ describe("google-meet create flow", () => {

322375323376

await handler?.({ params: {}, respond });

324377325-

expect(respond.mock.calls[0]?.[0]).toBe(false);

326-

expect(respond.mock.calls[0]?.[1]).toMatchObject({

327-

source: "browser",

328-

error:

329-

"google-login-required: Sign in to Google in the OpenClaw browser profile, then retry meeting creation.",

330-

manualActionRequired: true,

331-

manualActionReason: "google-login-required",

332-

manualActionMessage:

333-

"Sign in to Google in the OpenClaw browser profile, then retry meeting creation.",

334-

browser: {

335-

nodeId: "node-1",

336-

targetId: "login-tab",

337-

browserUrl: "https://accounts.google.com/signin",

338-

browserTitle: "Sign in - Google Accounts",

339-

notes: ["Sign-in page detected."],

340-

},

341-

});

378+

const payload = responseErrorPayload(respond);

379+

expect(payload.source).toBe("browser");

380+

expect(payload.error).toBe(

381+

"google-login-required: Sign in to Google in the OpenClaw browser profile, then retry meeting creation.",

382+

);

383+

expect(payload.manualActionRequired).toBe(true);

384+

expect(payload.manualActionReason).toBe("google-login-required");

385+

expect(payload.manualActionMessage).toBe(

386+

"Sign in to Google in the OpenClaw browser profile, then retry meeting creation.",

387+

);

388+

const browser = requireRecord(payload.browser, "browser payload");

389+

expect(browser.nodeId).toBe("node-1");

390+

expect(browser.targetId).toBe("login-tab");

391+

expect(browser.browserUrl).toBe("https://accounts.google.com/signin");

392+

expect(browser.browserTitle).toBe("Sign in - Google Accounts");

393+

expect(browser.notes).toEqual(["Sign-in page detected."]);

342394

});

343395344396

it("creates and joins a Meet through the create tool action by default", async () => {

@@ -412,28 +464,32 @@ describe("google-meet create flow", () => {

412464

id: string,

413465

params: unknown,

414466

) => Promise<{

415-

details: { joined?: boolean; meetingUri?: string; join?: { session: { url: string } } };

467+

details: {

468+

source?: string;

469+

joined?: boolean;

470+

meetingUri?: string;

471+

join?: { session: { url: string } };

472+

};

416473

}>;

417474

};

418475419476

const result = await tool.execute("id", { action: "create" });

420477421-

expect(result.details).toMatchObject({

422-

source: "browser",

423-

joined: true,

424-

meetingUri: "https://meet.google.com/new-abcd-xyz",

425-

join: { session: { url: "https://meet.google.com/new-abcd-xyz" } },

478+

expect(result.details.source).toBe("browser");

479+

expect(result.details.joined).toBe(true);

480+

expect(result.details.meetingUri).toBe("https://meet.google.com/new-abcd-xyz");

481+

expect(result.details.join?.session.url).toBe("https://meet.google.com/new-abcd-xyz");

482+

findNodeInvokeParams(nodesInvoke, "googlemeet chrome start", (params) => {

483+

if (params.command !== "googlemeet.chrome") {

484+

return false;

485+

}

486+

const chromeParams = requireRecord(params.params, "chrome params");

487+

return (

488+

chromeParams.action === "start" &&

489+

chromeParams.url === "https://meet.google.com/new-abcd-xyz" &&

490+

chromeParams.launch === false

491+

);

426492

});

427-

expect(nodesInvoke).toHaveBeenCalledWith(

428-

expect.objectContaining({

429-

command: "googlemeet.chrome",

430-

params: expect.objectContaining({

431-

action: "start",

432-

url: "https://meet.google.com/new-abcd-xyz",

433-

launch: false,

434-

}),

435-

}),

436-

);

437493

});

438494439495

it("returns structured manual action from the create tool action", async () => {

@@ -486,19 +542,17 @@ describe("google-meet create flow", () => {

486542487543

const result = await tool.execute("id", { action: "create" });

488544489-

expect(result.details).toMatchObject({

490-

source: "browser",

491-

manualActionRequired: true,

492-

manualActionReason: "meet-permission-required",

493-

manualActionMessage:

494-

"Allow microphone/camera permissions for Meet in the OpenClaw browser profile, then retry meeting creation.",

495-

browser: {

496-

nodeId: "node-1",

497-

targetId: "permission-tab",

498-

browserUrl: "https://meet.google.com/new",

499-

browserTitle: "Meet",

500-

},

501-

});

545+

expect(result.details.source).toBe("browser");

546+

expect(result.details.manualActionRequired).toBe(true);

547+

expect(result.details.manualActionReason).toBe("meet-permission-required");

548+

expect(result.details.manualActionMessage).toBe(

549+

"Allow microphone/camera permissions for Meet in the OpenClaw browser profile, then retry meeting creation.",

550+

);

551+

const browser = requireRecord(result.details.browser, "browser details");

552+

expect(browser.nodeId).toBe("node-1");

553+

expect(browser.targetId).toBe("permission-tab");

554+

expect(browser.browserUrl).toBe("https://meet.google.com/new");

555+

expect(browser.browserTitle).toBe("Meet");

502556

});

503557504558

it("reuses an existing browser create tab instead of opening duplicates", async () => {

@@ -557,25 +611,35 @@ describe("google-meet create flow", () => {

557611558612

await handler?.({ params: { join: false }, respond });

559613560-

expect(respond.mock.calls[0]?.[0]).toBe(true);

561-

expect(respond.mock.calls[0]?.[1]).toMatchObject({

562-

source: "browser",

563-

meetingUri: "https://meet.google.com/reu-sedx-tab",

564-

browser: { nodeId: "node-1", targetId: "existing-create-tab" },

614+

const payload = responsePayload(respond);

615+

expect(payload.source).toBe("browser");

616+

expect(payload.meetingUri).toBe("https://meet.google.com/reu-sedx-tab");

617+

const browser = requireRecord(payload.browser, "browser payload");

618+

expect(browser.nodeId).toBe("node-1");

619+

expect(browser.targetId).toBe("existing-create-tab");

620+

findNodeInvokeParams(nodesInvoke, "focus existing tab", (params) => {

621+

if (!params.params || typeof params.params !== "object") {

622+

return false;

623+

}

624+

const proxy = params.params as Record<string, unknown>;

625+

if (!proxy.body || typeof proxy.body !== "object") {

626+

return false;

627+

}

628+

const body = proxy.body as Record<string, unknown>;

629+

return proxy.path === "/tabs/focus" && body.targetId === "existing-create-tab";

565630

});

566-

expect(nodesInvoke).toHaveBeenCalledWith(

567-

expect.objectContaining({

568-

params: expect.objectContaining({

569-

path: "/tabs/focus",

570-

body: { targetId: "existing-create-tab" },

571-

}),

572-

}),

573-

);

574-

expect(nodesInvoke).not.toHaveBeenCalledWith(

575-

expect.objectContaining({

576-

params: expect.objectContaining({ path: "/tabs/open" }),

577-

}),

578-

);

631+

const openedCreateTab = mockCalls(nodesInvoke, "nodes invoke").some(([value]) => {

632+

if (!value || typeof value !== "object") {

633+

return false;

634+

}

635+

const params = value as Record<string, unknown>;

636+

if (!params.params || typeof params.params !== "object") {

637+

return false;

638+

}

639+

const proxy = params.params as Record<string, unknown>;

640+

return proxy.path === "/tabs/open";

641+

});

642+

expect(openedCreateTab).toBe(false);

579643

});

580644581645

it.each([

@@ -589,10 +653,8 @@ describe("google-meet create flow", () => {

589653

async (buttonText, note) => {

590654

const { button, result } = await runCreateMeetBrowserScript({ buttonText });

591655592-

expect(result).toMatchObject({

593-

retryAfterMs: 1000,

594-

notes: [note],

595-

});

656+

expect(result.retryAfterMs).toBe(1000);

657+

expect(result.notes).toEqual([note]);

596658

expect(button.click).toHaveBeenCalledTimes(1);

597659

expect(result.meetingUri).toBeUndefined();

598660

expect(result.manualActionReason).toBeUndefined();