From b8fa912a4438621b9594b587f7f21fc87b547b3f Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 9 Oct 2026 16:56:52 -0400 Subject: [PATCH] test(mosaic): give each feature test its own fake FAPI host --- .changeset/isolate-mosaic-fake-fapi.md | 2 + packages/mosaic/docs/testing.md | 8 ++-- .../fake-fapi-isolation.feature.test.tsx | 37 +++++++++++++++++++ .../mosaic/src/__tests__/feature/fake-fapi.ts | 22 ++++++++--- .../mosaic/src/__tests__/feature/render.tsx | 4 +- packages/mosaic/vitest.setup.browser.mts | 9 ++++- 6 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 .changeset/isolate-mosaic-fake-fapi.md create mode 100644 packages/mosaic/src/__tests__/feature/fake-fapi-isolation.feature.test.tsx diff --git a/.changeset/isolate-mosaic-fake-fapi.md b/.changeset/isolate-mosaic-fake-fapi.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/isolate-mosaic-fake-fapi.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/mosaic/docs/testing.md b/packages/mosaic/docs/testing.md index 07d64e1e551..dddb9b355a0 100644 --- a/packages/mosaic/docs/testing.md +++ b/packages/mosaic/docs/testing.md @@ -165,9 +165,11 @@ in [Test placement](#test-placement). `navigate` is the router Clerk was loaded with, called with the path. The setup file (`vitest.setup.browser.mts`) starts the worker and focuses the -window. After each test it cleans up, resets the handlers and the shared query -cache, and **fails the test on any FAPI request without a handler**, or on a -`holdRequests` hold never released or failed. When a +window. Before each test it moves FAPI to a new host, so a request a previous +test's Clerk sends late fails with a network error instead of reaching this +test's handlers. After each test it cleans up, resets the handlers and the +shared query cache, and **fails the test on any FAPI request without a +handler**, or on a `holdRequests` hold never released or failed. When a feature needs a new endpoint, add a handler to `serveFapi` that mirrors what FAPI returns. diff --git a/packages/mosaic/src/__tests__/feature/fake-fapi-isolation.feature.test.tsx b/packages/mosaic/src/__tests__/feature/fake-fapi-isolation.feature.test.tsx new file mode 100644 index 00000000000..bc3a28aab87 --- /dev/null +++ b/packages/mosaic/src/__tests__/feature/fake-fapi-isolation.feature.test.tsx @@ -0,0 +1,37 @@ +import { describe, expect, it } from 'vitest'; + +import { holdRequests, serveFapi } from './fake-fapi'; +import { fapiClient, fapiSession, fapiUser } from './fapi'; +import { renderWithClerk } from './render'; + +describe('fake FAPI isolation between tests', () => { + it('leaves a request in flight when the test ends', async () => { + const user = fapiUser({ id: 'user_1' }); + serveFapi({ client: fapiClient([fapiSession({ id: 'sess_1', user })]) }); + const { clerk } = await renderWithClerk(
); + void clerk.user?.getOrganizationMemberships().catch(() => undefined); + }); + + it('does not hand the previous test’s request to this test’s handlers', async () => { + serveFapi(); + const memberships = holdRequests('get', '/v1/me/organization_memberships'); + await new Promise(resolve => setTimeout(resolve, 200)); + expect(memberships.requests).toHaveLength(0); + memberships.release(); + }); + + it('starts a request after the test ends', async () => { + const user = fapiUser({ id: 'user_1' }); + serveFapi({ client: fapiClient([fapiSession({ id: 'sess_1', user })]) }); + const { clerk } = await renderWithClerk(
); + setTimeout(() => void clerk.user?.getOrganizationMemberships().catch(() => undefined), 50); + }); + + it('does not let the previous test’s Clerk reach this test’s handlers', async () => { + serveFapi(); + const memberships = holdRequests('get', '/v1/me/organization_memberships'); + await new Promise(resolve => setTimeout(resolve, 200)); + expect(memberships.requests).toHaveLength(0); + memberships.release(); + }); +}); diff --git a/packages/mosaic/src/__tests__/feature/fake-fapi.ts b/packages/mosaic/src/__tests__/feature/fake-fapi.ts index fa82dc89c7a..07ffcd6d258 100644 --- a/packages/mosaic/src/__tests__/feature/fake-fapi.ts +++ b/packages/mosaic/src/__tests__/feature/fake-fapi.ts @@ -48,10 +48,18 @@ import { fapiWeb3Wallet, } from './fapi'; -export const PUBLISHABLE_KEY = 'pk_live_Y2xlcmsuYWJjZWYuMTIzNDUucHJvZC5sY2xjbGVyay5jb20k'; -const FAPI = 'https://clerk.abcef.12345.prod.lclclerk.com'; +const FAPI_DOMAIN = '12345.prod.lclclerk.com'; +let fapiRun = 0; +let fapiHost = `clerk.run${fapiRun}.${FAPI_DOMAIN}`; -export const fapiUrl = (path: string) => `${FAPI}${path}`; +export function switchFapiHost() { + fapiRun += 1; + fapiHost = `clerk.run${fapiRun}.${FAPI_DOMAIN}`; +} + +export const publishableKey = () => `pk_live_${btoa(`${fapiHost}$`)}`; + +export const fapiUrl = (path: string) => `https://${fapiHost}${path}`; export interface FakeFapiState { environment: FapiEnvironment; @@ -92,13 +100,17 @@ export type FakeFapiSeed = Partial + new URL(request.url).host === fapiHost ? undefined : HttpResponse.error(), + ), +); export function startWorker() { return worker.start({ quiet: true, onUnhandledRequest: request => { - if (new URL(request.url).origin === FAPI) { + if (new URL(request.url).host === fapiHost) { unhandled.push(`${request.method} ${request.url}`); } }, diff --git a/packages/mosaic/src/__tests__/feature/render.tsx b/packages/mosaic/src/__tests__/feature/render.tsx index 37da7dcface..4ed0152569b 100644 --- a/packages/mosaic/src/__tests__/feature/render.tsx +++ b/packages/mosaic/src/__tests__/feature/render.tsx @@ -6,14 +6,14 @@ import { vi } from 'vitest'; import type { MosaicLocalization } from '../../localization'; import { MosaicProvider } from '../../mosaic-provider'; -import { PUBLISHABLE_KEY } from './fake-fapi'; +import { publishableKey } from './fake-fapi'; export async function renderWithClerk( ui: ReactElement, options?: Parameters[0], localization?: MosaicLocalization, ) { - const clerk = new Clerk(PUBLISHABLE_KEY); + const clerk = new Clerk(publishableKey()); const navigate = vi.fn((_to: string) => Promise.resolve()); const wrap = (element: ReactElement) => ( diff --git a/packages/mosaic/vitest.setup.browser.mts b/packages/mosaic/vitest.setup.browser.mts index c5946a76b9b..3025b96f0a1 100644 --- a/packages/mosaic/vitest.setup.browser.mts +++ b/packages/mosaic/vitest.setup.browser.mts @@ -3,7 +3,13 @@ import * as matchers from '@testing-library/jest-dom/matchers'; import { cleanup, configure } from '@testing-library/react'; import { afterAll, afterEach, beforeAll, beforeEach, expect } from 'vitest'; -import { startWorker, takeUnhandledRequests, takeUnsettledHolds, worker } from './src/__tests__/feature/fake-fapi'; +import { + startWorker, + takeUnhandledRequests, + takeUnsettledHolds, + switchFapiHost, + worker, +} from './src/__tests__/feature/fake-fapi'; expect.extend(matchers); @@ -51,6 +57,7 @@ beforeAll(async () => { }); beforeEach(() => { + switchFapiHost(); window.focus(); });