























@@ -80,6 +80,12 @@ type RespondCall = [
8080}?,
8181];
828283+type MockCallSource = {
84+mock: {
85+calls: ArrayLike<ReadonlyArray<unknown>>;
86+};
87+};
88+8389type TestNodeSession = {
8490nodeId: string;
8591commands: string[];
@@ -112,6 +118,22 @@ function requireString(value: unknown, label: string): string {
112118return value as string;
113119}
114120121+function mockCall(source: MockCallSource, callIndex = 0): ReadonlyArray<unknown> {
122+const call = source.mock.calls[callIndex];
123+if (!call) {
124+throw new Error(`expected mock call ${callIndex}`);
125+}
126+return call;
127+}
128+129+function firstRespondCall(source: MockCallSource): RespondCall {
130+return mockCall(source) as RespondCall;
131+}
132+133+function mockArg(source: MockCallSource, callIndex: number, argIndex: number) {
134+return mockCall(source, callIndex)[argIndex];
135+}
136+115137function isLowerHex(value: string): boolean {
116138for (let index = 0; index < value.length; index += 1) {
117139const code = value.charCodeAt(index);
@@ -380,22 +402,22 @@ describe("node.pair.request", () => {
380402permissions: { camera: true },
381403}),
382404);
383-expect(broadcast.mock.calls.at(0)?.[0]).toBe("node.pair.resolved");
384-expect(broadcast.mock.calls.at(0)?.[1]).toEqual({
405+expect(mockArg(broadcast, 0, 0)).toBe("node.pair.resolved");
406+expect(mockArg(broadcast, 0, 1)).toEqual({
385407requestId: "req-old",
386408nodeId: "ios-node-1",
387409decision: "rejected",
388410ts: expect.any(Number),
389411});
390-expect(broadcast.mock.calls.at(1)?.[0]).toBe("node.pair.requested");
391-expect(broadcast.mock.calls.at(1)?.[1]).toEqual(
412+expect(mockArg(broadcast, 1, 0)).toBe("node.pair.requested");
413+expect(mockArg(broadcast, 1, 1)).toEqual(
392414expect.objectContaining({
393415requestId: "req-new",
394416nodeId: "ios-node-1",
395417permissions: { camera: true },
396418}),
397419);
398-expect(respond.mock.calls.at(0)?.[0]).toBe(true);
420+expect(firstRespondCall(respond)[0]).toBe(true);
399421});
400422});
401423@@ -426,10 +448,10 @@ describe("node plugin surface refresh", () => {
426448});
427449428450expect(respond).toHaveBeenCalledTimes(1);
429-const call = respond.mock.calls.at(0) as RespondCall | undefined;
430-expect(call?.[0]).toBe(true);
431-expect(call?.[2]).toBeUndefined();
432-const payload = requireRecord(call?.[1], "refresh payload");
451+const call = firstRespondCall(respond);
452+expect(call[0]).toBe(true);
453+expect(call[2]).toBeUndefined();
454+const payload = requireRecord(call[1], "refresh payload");
433455expect(payload.surface).toBe("canvas");
434456expect(payload.expiresAtMs).toBe(1_100);
435457const pluginSurfaceUrls = requireRecord(payload.pluginSurfaceUrls, "refresh surface urls");
@@ -482,10 +504,10 @@ describe("node.invoke APNs wake path", () => {
482504};
483505484506const respond = await invokeNode({ nodeRegistry });
485-const call = respond.mock.calls.at(0) as RespondCall | undefined;
486-expect(call?.[0]).toBe(false);
487-expect(call?.[2]?.code).toBe(ErrorCodes.UNAVAILABLE);
488-expect(call?.[2]?.message).toBe("node not connected");
507+const call = firstRespondCall(respond);
508+expect(call[0]).toBe(false);
509+expect(call[2]?.code).toBe(ErrorCodes.UNAVAILABLE);
510+expect(call[2]?.message).toBe("node not connected");
489511expect(mocks.sendApnsBackgroundWake).not.toHaveBeenCalled();
490512expect(nodeRegistry.invoke).not.toHaveBeenCalled();
491513});
@@ -591,13 +613,13 @@ describe("node.invoke APNs wake path", () => {
591613592614expect(mocks.sendApnsBackgroundWake).toHaveBeenCalledTimes(1);
593615expect(nodeRegistry.invoke).toHaveBeenCalledTimes(1);
594-expectRecordFields(nodeRegistry.invoke.mock.calls.at(0)?.[0], "node invoke payload", {
616+expectRecordFields(mockArg(nodeRegistry.invoke, 0, 0), "node invoke payload", {
595617nodeId: "ios-node-reconnect",
596618command: "camera.capture",
597619});
598-const call = respond.mock.calls.at(0) as RespondCall | undefined;
599-expect(call?.[0]).toBe(true);
600-expectRecordFields(call?.[1], "respond payload", { ok: true, nodeId: "ios-node-reconnect" });
620+const call = firstRespondCall(respond);
621+expect(call[0]).toBe(true);
622+expectRecordFields(call[1], "respond payload", { ok: true, nodeId: "ios-node-reconnect" });
601623});
602624603625it("broadcasts canonical Talk capture events for successful PTT node commands", async () => {
@@ -635,16 +657,12 @@ describe("node.invoke APNs wake path", () => {
635657isWebchatConnect: () => false,
636658});
637659638-expect(respond.mock.calls.at(0)?.[0]).toBe(true);
639-expect(broadcast.mock.calls.at(0)?.[0]).toBe("talk.event");
640-const broadcastPayload = expectRecordFields(
641-broadcast.mock.calls.at(0)?.[1],
642-"broadcast payload",
643-{
644-nodeId: "android-talk-node",
645-command: "talk.ptt.start",
646-},
647-);
660+expect(firstRespondCall(respond)[0]).toBe(true);
661+expect(mockArg(broadcast, 0, 0)).toBe("talk.event");
662+const broadcastPayload = expectRecordFields(mockArg(broadcast, 0, 1), "broadcast payload", {
663+nodeId: "android-talk-node",
664+command: "talk.ptt.start",
665+});
648666const talkEvent = expectRecordFields(broadcastPayload.talkEvent, "talk event", {
649667type: "capture.started",
650668sessionId: "node:android-talk-node:talk:capture-1",
@@ -659,7 +677,7 @@ describe("node.invoke APNs wake path", () => {
659677nodeId: "android-talk-node",
660678command: "talk.ptt.start",
661679});
662-expect(broadcast.mock.calls.at(0)?.[2]).toEqual({ dropIfSlow: true });
680+expect(mockArg(broadcast, 0, 2)).toEqual({ dropIfSlow: true });
663681});
664682665683it("clears stale registrations after an invalid device token wake failure", async () => {
@@ -772,14 +790,14 @@ describe("node.invoke APNs wake path", () => {
772790idempotencyKey: "idem-queued",
773791},
774792});
775-const call = respond.mock.calls.at(0) as RespondCall | undefined;
776-expect(call?.[0]).toBe(false);
777-expect(call?.[2]?.code).toBe(ErrorCodes.UNAVAILABLE);
778-expect(call?.[2]?.message).toBe("node command queued until iOS returns to foreground");
793+const call = firstRespondCall(respond);
794+expect(call[0]).toBe(false);
795+expect(call[2]?.code).toBe(ErrorCodes.UNAVAILABLE);
796+expect(call[2]?.message).toBe("node command queued until iOS returns to foreground");
779797expect(mocks.sendApnsBackgroundWake).not.toHaveBeenCalled();
780798781799const pullRespond = await pullPending("ios-node-queued", ["canvas.navigate"]);
782-const pullCall = pullRespond.mock.calls.at(0) as RespondCall | undefined;
800+const pullCall = firstRespondCall(pullRespond);
783801const pullPayload = requireRespondPayload(pullCall, "pull response");
784802expectRecordFields(pullPayload, "pull payload", {
785803nodeId: "ios-node-queued",
@@ -790,7 +808,7 @@ describe("node.invoke APNs wake path", () => {
790808});
791809792810const repeatedPullRespond = await pullPending("ios-node-queued", ["canvas.navigate"]);
793-const repeatedPullCall = repeatedPullRespond.mock.calls.at(0) as RespondCall | undefined;
811+const repeatedPullCall = firstRespondCall(repeatedPullRespond);
794812const repeatedPullPayload = requireRespondPayload(repeatedPullCall, "repeated pull response");
795813expectRecordFields(repeatedPullPayload, "repeated pull payload", {
796814nodeId: "ios-node-queued",
@@ -807,15 +825,15 @@ describe("node.invoke APNs wake path", () => {
807825expect(isUuidV4(queuedActionId)).toBe(true);
808826809827const ackRespond = await ackPending("ios-node-queued", [queuedActionId], ["canvas.navigate"]);
810-const ackCall = ackRespond.mock.calls.at(0) as RespondCall | undefined;
828+const ackCall = firstRespondCall(ackRespond);
811829expectRecordFields(requireRespondPayload(ackCall, "ack response"), "ack payload", {
812830nodeId: "ios-node-queued",
813831ackedIds: [queuedActionId],
814832remainingCount: 0,
815833});
816834817835const emptyPullRespond = await pullPending("ios-node-queued", ["canvas.navigate"]);
818-const emptyPullCall = emptyPullRespond.mock.calls.at(0) as RespondCall | undefined;
836+const emptyPullCall = firstRespondCall(emptyPullRespond);
819837expectRecordFields(
820838requireRespondPayload(emptyPullCall, "empty pull response"),
821839"empty pull payload",
@@ -871,7 +889,7 @@ describe("node.invoke APNs wake path", () => {
871889"camera.snap",
872890"canvas.navigate",
873891]);
874-const preChangePullCall = preChangePullRespond.mock.calls.at(0) as RespondCall | undefined;
892+const preChangePullCall = firstRespondCall(preChangePullRespond);
875893const preChangePayload = requireRespondPayload(preChangePullCall, "pre-change pull response");
876894expectRecordFields(preChangePayload, "pre-change pull payload", {
877895nodeId: "ios-node-policy",
@@ -884,7 +902,7 @@ describe("node.invoke APNs wake path", () => {
884902allowlistedCommands.delete("camera.snap");
885903886904const pullRespond = await pullPending("ios-node-policy", ["camera.snap", "canvas.navigate"]);
887-const pullCall = pullRespond.mock.calls.at(0) as RespondCall | undefined;
905+const pullCall = firstRespondCall(pullRespond);
888906expectRecordFields(requireRespondPayload(pullCall, "pull response"), "pull payload", {
889907nodeId: "ios-node-policy",
890908actions: [],
@@ -929,7 +947,7 @@ describe("node.invoke APNs wake path", () => {
929947});
930948931949const pullRespond = await pullPending("ios-node-dedupe", ["canvas.navigate"]);
932-const pullCall = pullRespond.mock.calls.at(0) as RespondCall | undefined;
950+const pullCall = firstRespondCall(pullRespond);
933951const pullPayload = requireRespondPayload(pullCall, "pull response");
934952expectRecordFields(pullPayload, "pull payload", {
935953nodeId: "ios-node-dedupe",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。