diff --git a/src/cli/program.nodes-media.e2e.test.ts b/src/cli/program.nodes-media.e2e.test.ts index f7212e57694..040a6d569a2 100644 --- a/src/cli/program.nodes-media.e2e.test.ts +++ b/src/cli/program.nodes-media.e2e.test.ts @@ -50,6 +50,34 @@ function mockNodeGateway(command?: string, payload?: Record) { }); } +function nodeInvokeCalls(): Array<{ + method?: unknown; + params: Record; + commandParams: Record; +}> { + return callGateway.mock.calls + .map((call) => call[0] as { method?: unknown; params?: Record }) + .filter((call) => call.method === "node.invoke") + .map((call) => { + const params = call.params ?? {}; + const commandParams = (params.params ?? {}) as Record; + return { method: call.method, params, commandParams }; + }); +} + +function latestNodeInvokeCall() { + const call = nodeInvokeCalls().at(-1); + if (!call) { + throw new Error("expected node.invoke gateway call"); + } + return call; +} + +function expectNonEmptyString(value: unknown) { + expect(typeof value).toBe("string"); + expect((value as string).length).toBeGreaterThan(0); +} + describe("cli program (nodes media)", () => { let program: Command; @@ -103,11 +131,9 @@ describe("cli program (nodes media)", () => { await runNodesCommand(["nodes", "camera", "snap", "--node", "ios-node"]); - const invokeCalls = callGateway.mock.calls - .map((call) => call[0] as { method?: string; params?: Record }) - .filter((call) => call.method === "node.invoke"); + const invokeCalls = nodeInvokeCalls(); const facings = invokeCalls - .map((call) => (call.params?.params as { facing?: string } | undefined)?.facing) + .map((call) => call.commandParams.facing) .filter((facing): facing is string => Boolean(facing)) .toSorted((a, b) => a.localeCompare(b)); expect(facings).toEqual(["back", "front"]); @@ -147,23 +173,16 @@ describe("cli program (nodes media)", () => { await runNodesCommand(["nodes", "camera", "clip", "--node", "ios-node", "--duration", "3000"]); - expect(callGateway).toHaveBeenCalledWith( - expect.objectContaining({ - method: "node.invoke", - params: expect.objectContaining({ - nodeId: "ios-node", - command: "camera.clip", - timeoutMs: 90000, - idempotencyKey: expect.any(String), - params: expect.objectContaining({ - facing: "front", - durationMs: 3000, - includeAudio: true, - format: "mp4", - }), - }), - }), - ); + const invoke = latestNodeInvokeCall(); + expect(invoke.method).toBe("node.invoke"); + expect(invoke.params.nodeId).toBe("ios-node"); + expect(invoke.params.command).toBe("camera.clip"); + expect(invoke.params.timeoutMs).toBe(90000); + expectNonEmptyString(invoke.params.idempotencyKey); + expect(invoke.commandParams.facing).toBe("front"); + expect(invoke.commandParams.durationMs).toBe(3000); + expect(invoke.commandParams.includeAudio).toBe(true); + expect(invoke.commandParams.format).toBe("mp4"); await expectLoggedSingleMediaFile({ expectedPathPattern: /openclaw-camera-clip-front-.*\.mp4$/, @@ -191,24 +210,17 @@ describe("cli program (nodes media)", () => { "cam-123", ]); - expect(callGateway).toHaveBeenCalledWith( - expect.objectContaining({ - method: "node.invoke", - params: expect.objectContaining({ - nodeId: "ios-node", - command: "camera.snap", - timeoutMs: 20000, - idempotencyKey: expect.any(String), - params: expect.objectContaining({ - facing: "front", - maxWidth: 640, - quality: 0.8, - delayMs: 2000, - deviceId: "cam-123", - }), - }), - }), - ); + const invoke = latestNodeInvokeCall(); + expect(invoke.method).toBe("node.invoke"); + expect(invoke.params.nodeId).toBe("ios-node"); + expect(invoke.params.command).toBe("camera.snap"); + expect(invoke.params.timeoutMs).toBe(20000); + expectNonEmptyString(invoke.params.idempotencyKey); + expect(invoke.commandParams.facing).toBe("front"); + expect(invoke.commandParams.maxWidth).toBe(640); + expect(invoke.commandParams.quality).toBe(0.8); + expect(invoke.commandParams.delayMs).toBe(2000); + expect(invoke.commandParams.deviceId).toBe("cam-123"); await expectLoggedSingleMediaFile(); }); @@ -234,21 +246,14 @@ describe("cli program (nodes media)", () => { "cam-123", ]); - expect(callGateway).toHaveBeenCalledWith( - expect.objectContaining({ - method: "node.invoke", - params: expect.objectContaining({ - nodeId: "ios-node", - command: "camera.clip", - timeoutMs: 90000, - idempotencyKey: expect.any(String), - params: expect.objectContaining({ - includeAudio: false, - deviceId: "cam-123", - }), - }), - }), - ); + const invoke = latestNodeInvokeCall(); + expect(invoke.method).toBe("node.invoke"); + expect(invoke.params.nodeId).toBe("ios-node"); + expect(invoke.params.command).toBe("camera.clip"); + expect(invoke.params.timeoutMs).toBe(90000); + expectNonEmptyString(invoke.params.idempotencyKey); + expect(invoke.commandParams.includeAudio).toBe(false); + expect(invoke.commandParams.deviceId).toBe("cam-123"); await expectLoggedSingleMediaFile(); }); @@ -263,16 +268,11 @@ describe("cli program (nodes media)", () => { await runNodesCommand(["nodes", "camera", "clip", "--node", "ios-node", "--duration", "10s"]); - expect(callGateway).toHaveBeenCalledWith( - expect.objectContaining({ - method: "node.invoke", - params: expect.objectContaining({ - nodeId: "ios-node", - command: "camera.clip", - params: expect.objectContaining({ durationMs: 10_000 }), - }), - }), - ); + const invoke = latestNodeInvokeCall(); + expect(invoke.method).toBe("node.invoke"); + expect(invoke.params.nodeId).toBe("ios-node"); + expect(invoke.params.command).toBe("camera.clip"); + expect(invoke.commandParams.durationMs).toBe(10_000); }); it("fails nodes camera snap on invalid facing", async () => {