From 50398eb69058d4fbf313de0672eb610d5125fd79 Mon Sep 17 00:00:00 2001 From: Michael Novotny Date: Wed, 15 Jul 2026 11:44:13 -0500 Subject: [PATCH] test(js): de-flake load fail-fast tests racing native crypto vs fake timers The pumpUntilSettled helper advanced fake time on a fixed budget, but load() awaits native crypto (cookie suffix) before scheduling its initialization timeout. On slow CI runners the crypto work resolves after all pump iterations have drained, leaving the fake timer unadvanced and the test hung until vitest's 5s testTimeout. Yield real event-loop turns until a fake timer is actually scheduled (or load settles) before spending the advance budget. Reproduced deterministically by delaying crypto.subtle.digest by 200ms: the two CI-flaky tests fail with the old helper and pass with the new one. Co-Authored-By: Claude Fable 5 --- .changeset/warm-shrimps-fall.md | 2 ++ packages/clerk-js/src/core/__tests__/clerk.test.ts | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 .changeset/warm-shrimps-fall.md diff --git a/.changeset/warm-shrimps-fall.md b/.changeset/warm-shrimps-fall.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/warm-shrimps-fall.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/clerk-js/src/core/__tests__/clerk.test.ts b/packages/clerk-js/src/core/__tests__/clerk.test.ts index 84474a62187..98f3d3aac2a 100644 --- a/packages/clerk-js/src/core/__tests__/clerk.test.ts +++ b/packages/clerk-js/src/core/__tests__/clerk.test.ts @@ -841,16 +841,24 @@ describe('Clerk singleton', () => { ...overrides, }); - // `load()` awaits native crypto (cookie suffix) before scheduling the timeout, so a single - // advance can run before the timer exists; advance the budget repeatedly until load settles. + // `load()` awaits native crypto (cookie suffix) before scheduling the timeout, and that work + // is real async the fake clock cannot advance. Yield real event-loop turns until a fake timer + // actually exists (or load settles) so slow machines can't exhaust the advance budget early. + const realSetTimeout = globalThis.setTimeout.bind(globalThis); + const yieldRealEventLoop = () => new Promise(resolve => realSetTimeout(resolve, 0)); const pumpUntilSettled = async (promise: Promise) => { let settled = false; const tracked = promise.then( v => ((settled = true), v), e => ((settled = true), Promise.reject(e)), ); - for (let i = 0; i < 20 && !settled; i++) { + for (let advances = 0; advances < 20 && !settled; ) { + if (vi.getTimerCount() === 0) { + await yieldRealEventLoop(); + continue; + } await vi.advanceTimersByTimeAsync(5000); + advances++; } await tracked; };