From d896d4cfd161d3ebe133739229801ed23385fbd5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 20 Oct 2026 22:48:34 +0000 Subject: [PATCH 1/3] test(browser-pay): wait for refused events in the in-flight retry test The test counted refused events as soon as the stub had recorded two Fetch.failRequest commands. The stub records that command when the frame arrives, and apply emits refused only after the CDP reply resolves. A poll can see the commands while the event list is still empty. Wait until both pauses are visible before releasing the shared relay, and until both refusals are visible before asserting. The test still requires one relay call, one warning, and two refused events. Co-authored-by: Rimmely Ewan --- packages/browser-pay/src/interceptor.protocol.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/browser-pay/src/interceptor.protocol.test.ts b/packages/browser-pay/src/interceptor.protocol.test.ts index 7187b7dda..3b21e5d2b 100644 --- a/packages/browser-pay/src/interceptor.protocol.test.ts +++ b/packages/browser-pay/src/interceptor.protocol.test.ts @@ -1110,15 +1110,19 @@ describe("interceptor CDP decisions", () => { ...paused, params: { ...paused.params, requestId: "req-b" }, }); - await new Promise((resolve) => setTimeout(resolve, 40)); + await waitFor( + () => events.filter((event) => event.type === "paused").length >= 2, + "both paused", + ); expect(calls).toBe(1); release(); await waitFor( () => stub.commands.filter( (command) => command.method === "Fetch.failRequest", - ).length >= 2, - "both failed", + ).length >= 2 && + events.filter((event) => event.type === "refused").length >= 2, + "both refused", ); expect(calls).toBe(1); expect(warnings).toEqual(["payment relay refused the paused request"]); From 8ff90300cc4272d86d65e7e053ea7631365dea95 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 20 Oct 2026 23:08:37 +0000 Subject: [PATCH 2/3] test(browser-pay): join the in-flight relay before releasing it Two paused events arrive before the second request reads its page origin and joins the shared flight. Releasing the gate at that point lets the first flight finish, and the retry starts a second relay. Wait until the second location.origin command is visible, then until the poll that applies its reply has run. After release, wait until both refused events are visible. The test still requires one relay call, one warning, two failRequest commands, and two refused events. Co-authored-by: Rimmely Ewan --- .../src/interceptor.protocol.test.ts | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/browser-pay/src/interceptor.protocol.test.ts b/packages/browser-pay/src/interceptor.protocol.test.ts index 3b21e5d2b..3b793915f 100644 --- a/packages/browser-pay/src/interceptor.protocol.test.ts +++ b/packages/browser-pay/src/interceptor.protocol.test.ts @@ -1111,21 +1111,30 @@ describe("interceptor CDP decisions", () => { params: { ...paused.params, requestId: "req-b" }, }); await waitFor( - () => events.filter((event) => event.type === "paused").length >= 2, - "both paused", + () => + stub.commands.filter( + (command) => + command.method === "Runtime.evaluate" && + command.params.expression === "location.origin", + ).length >= 2, + "second origin", ); + // The stub writes this reply before the command is visible. setImmediate + // runs after the poll that applies it, which is when req-b joins. + await new Promise((resolve) => setImmediate(resolve)); expect(calls).toBe(1); release(); await waitFor( - () => - stub.commands.filter( - (command) => command.method === "Fetch.failRequest", - ).length >= 2 && - events.filter((event) => event.type === "refused").length >= 2, + () => events.filter((event) => event.type === "refused").length >= 2, "both refused", ); expect(calls).toBe(1); expect(warnings).toEqual(["payment relay refused the paused request"]); + expect( + stub.commands.filter( + (command) => command.method === "Fetch.failRequest", + ), + ).toHaveLength(2); expect(events.filter((event) => event.type === "refused")).toHaveLength( 2, ); From 56ba05440d6e36c42931df22181f0a3b47cf4ebb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 20 Oct 2026 23:14:34 +0000 Subject: [PATCH 3/3] test(browser-pay): separate the two relay deadline waits The later-pause test slept 40ms so its second deadline trailed the first. A late timer on CI can cross both. Wait until the retry is posted, and give the first waiter 1.5s so the second still has about a second left. The shared-attempt test bounded the abort to 160ms after an 80ms sleep. Drop the sleep so the second pause joins immediately, and accept an abort from 300ms up to 2s. The attempt timeout is 400ms and the total deadline is 5s, so the bound still tells them apart. Co-authored-by: Rimmely Ewan --- .../src/interceptor.relay-in-progress.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/browser-pay/src/interceptor.relay-in-progress.test.ts b/packages/browser-pay/src/interceptor.relay-in-progress.test.ts index f7f927ab7..ed7be7b29 100644 --- a/packages/browser-pay/src/interceptor.relay-in-progress.test.ts +++ b/packages/browser-pay/src/interceptor.relay-in-progress.test.ts @@ -802,7 +802,9 @@ describe("relay_in_progress", () => { }, placeholder: HELD, profileId: "shopify-card-vault", - timeoutMs: 500, + // The later waiter starts after the retry post, so its deadline trails + // the first by about one poll backoff. 1.5s keeps that gap if a timer fires late. + timeoutMs: 1_500, }); handle.on((event) => events.push(event)); try { @@ -812,8 +814,7 @@ describe("relay_in_progress", () => { "Fetch.enable", ); emitPaused(cdp, "req-early-deadline"); - await waitFor(() => calls === 1, "first post"); - await new Promise((resolve) => setTimeout(resolve, 40)); + await waitFor(() => calls === 2, "retry posted"); emitPaused(cdp, "req-later-deadline"); await waitFor( () => @@ -1111,8 +1112,8 @@ describe("relay retry before the deadline", () => { }, placeholder: HELD, profileId: "shopify-card-vault", - timeoutMs: 2_000, - attemptTimeoutMs: 120, + timeoutMs: 5_000, + attemptTimeoutMs: 400, }); try { armPage(cdp); @@ -1122,7 +1123,6 @@ describe("relay retry before the deadline", () => { ); emitPaused(cdp, "req-shared-a"); await waitFor(() => calls === 1, "first attempt"); - await new Promise((resolve) => setTimeout(resolve, 80)); emitPaused(cdp, "req-shared-b"); await waitFor( () => count(cdp.commands, "Fetch.fulfillRequest") === 2, @@ -1130,8 +1130,8 @@ describe("relay retry before the deadline", () => { ); expect(calls).toBe(2); expect(aborts).toHaveLength(1); - expect(aborts[0]).toBeGreaterThanOrEqual(100); - expect(aborts[0]).toBeLessThan(160); + expect(aborts[0]).toBeGreaterThanOrEqual(300); + expect(aborts[0]).toBeLessThan(2_000); expect(count(cdp.commands, "Fetch.failRequest")).toBe(0); } finally { await handle.detach();