Skip to content

Commit 1448607

Browse files
committed
test: Added more tests for various scenarios were ui should or shouldn't load
1 parent 8477178 commit 1448607

1 file changed

Lines changed: 145 additions & 41 deletions

File tree

packages/react/src/__tests__/isomorphicClerk.test.ts

Lines changed: 145 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -201,88 +201,192 @@ describe('isomorphicClerk', () => {
201201
});
202202
});
203203

204-
describe('ui.ClerkUI with pre-created Clerk instance', () => {
205-
it('passes ui.ClerkUI to clerk.load when Clerk instance is provided', async () => {
206-
const mockClerkUI = vi.fn();
204+
describe('shouldLoadUi across SDK scenarios', () => {
205+
// Helper to run getEntryChunks and return what clerk.load was called with
206+
async function runGetEntryChunks(options: Record<string, any>) {
207207
const mockLoad = vi.fn().mockResolvedValue(undefined);
208-
const mockClerkInstance = {
208+
const mockClerkInstance = options.Clerk || {
209209
load: mockLoad,
210210
loaded: false,
211211
};
212+
if (options.Clerk) {
213+
options.Clerk.load = mockLoad;
214+
options.Clerk.loaded = false;
215+
}
216+
217+
(global as any).Clerk = mockClerkInstance;
212218

213-
// Simulate the chrome-extension flow: pre-created Clerk instance + ui prop
214219
const clerk = new IsomorphicClerk({
215220
publishableKey: 'pk_test_XXX',
216-
Clerk: mockClerkInstance as any,
217-
ui: { ClerkUI: mockClerkUI } as any,
221+
...options,
218222
});
219223

220-
// Set the global Clerk so getClerkJsEntryChunk resolves
221-
(global as any).Clerk = mockClerkInstance;
222-
223224
await (clerk as any).getEntryChunks();
224225

225-
// clerk.load should have been called with ui.ClerkUI preserved
226+
return { mockLoad };
227+
}
228+
229+
// ─── @clerk/react, @clerk/nextjs, @clerk/react-router, @clerk/tanstack-react-start ───
230+
// These SDKs: no Clerk prop, no ui prop, standardBrowser omitted (undefined)
231+
// shouldLoadUi = (undefined !== false && !undefined) || !!undefined = (true && true) || false = true
232+
// → loads UI from CDN
233+
it('loads UI from CDN when no Clerk, no ui, standardBrowser omitted (nextjs/react-router/tanstack)', async () => {
234+
const { mockLoad } = await runGetEntryChunks({});
235+
236+
expect(loadClerkUIScript).toHaveBeenCalled();
226237
expect(mockLoad).toHaveBeenCalledWith(
227238
expect.objectContaining({
228239
ui: expect.objectContaining({
229-
ClerkUI: mockClerkUI,
240+
ClerkUI: (global as any).__internal_ClerkUICtor,
230241
}),
231242
}),
232243
);
233244
});
234245

235-
it('does not load UI scripts from CDN when Clerk instance is provided', async () => {
246+
// ─── @clerk/react with bundled ui prop (e.g. user passes ui={ui} from @clerk/ui) ───
247+
// These SDKs: no Clerk prop, ui with ClerkUI, standardBrowser omitted
248+
// shouldLoadUi = (true && true) || true = true
249+
// → getClerkUIEntryChunk returns the bundled ClerkUI (no CDN)
250+
it('uses bundled ClerkUI when ui prop is passed without Clerk instance (react with ui prop)', async () => {
236251
const mockClerkUI = vi.fn();
237-
const mockClerkInstance = {
238-
load: vi.fn().mockResolvedValue(undefined),
239-
loaded: false,
240-
};
241-
242-
const clerk = new IsomorphicClerk({
243-
publishableKey: 'pk_test_XXX',
244-
Clerk: mockClerkInstance as any,
245-
ui: { ClerkUI: mockClerkUI } as any,
252+
const { mockLoad } = await runGetEntryChunks({
253+
ui: { ClerkUI: mockClerkUI },
246254
});
247255

248-
(global as any).Clerk = mockClerkInstance;
256+
expect(loadClerkUIScript).not.toHaveBeenCalled();
257+
expect(mockLoad).toHaveBeenCalledWith(
258+
expect.objectContaining({
259+
ui: expect.objectContaining({
260+
ClerkUI: mockClerkUI,
261+
}),
262+
}),
263+
);
264+
});
249265

250-
await (clerk as any).getEntryChunks();
266+
// ─── @clerk/expo (native mode) ───
267+
// Expo native: Clerk instance, no ui prop, standardBrowser: false
268+
// shouldLoadUi = (false !== false && ...) || !!undefined = false || false = false
269+
// → no UI loaded (correct: native apps don't render prebuilt UI)
270+
it('does not load UI for Expo native (Clerk instance, no ui, standardBrowser: false)', async () => {
271+
const mockClerkInstance = {} as any;
272+
const { mockLoad } = await runGetEntryChunks({
273+
Clerk: mockClerkInstance,
274+
standardBrowser: false,
275+
});
251276

252-
// Should not attempt to load UI from CDN
253277
expect(loadClerkUIScript).not.toHaveBeenCalled();
278+
expect(mockLoad).toHaveBeenCalledWith(
279+
expect.objectContaining({
280+
ui: expect.objectContaining({
281+
ClerkUI: undefined,
282+
}),
283+
}),
284+
);
285+
});
286+
287+
// ─── @clerk/expo (web mode) ───
288+
// Expo web: Clerk is null, no ui prop, standardBrowser: true
289+
// shouldLoadUi = (true !== false && !null) || false = (true && true) || false = true
290+
// → loads UI from CDN (correct: web mode uses normal browser flow)
291+
it('loads UI from CDN for Expo web (Clerk: null, standardBrowser: true)', async () => {
292+
const { mockLoad } = await runGetEntryChunks({
293+
Clerk: null,
294+
standardBrowser: true,
295+
});
296+
297+
expect(loadClerkUIScript).toHaveBeenCalled();
298+
expect(mockLoad).toHaveBeenCalledWith(
299+
expect.objectContaining({
300+
ui: expect.objectContaining({
301+
ClerkUI: (global as any).__internal_ClerkUICtor,
302+
}),
303+
}),
304+
);
254305
});
255306

256-
it('passes ui.ClerkUI to clerk.load even when standardBrowser is false', async () => {
307+
// ─── @clerk/chrome-extension (without syncHost) ───
308+
// No syncHost: Clerk instance, ui with ClerkUI, standardBrowser: true
309+
// shouldLoadUi = (true && !instance) || true = false || true = true
310+
// → uses bundled ClerkUI (no CDN)
311+
it('uses bundled ClerkUI for chrome-extension without syncHost (standardBrowser: true)', async () => {
257312
const mockClerkUI = vi.fn();
258-
const mockLoad = vi.fn().mockResolvedValue(undefined);
259-
const mockClerkInstance = {
260-
load: mockLoad,
261-
loaded: false,
262-
};
313+
const mockClerkInstance = {} as any;
314+
const { mockLoad } = await runGetEntryChunks({
315+
Clerk: mockClerkInstance,
316+
ui: { ClerkUI: mockClerkUI },
317+
standardBrowser: true,
318+
});
263319

264-
// Simulate chrome-extension with syncHost: Clerk instance + ui prop + standardBrowser: false
265-
const clerk = new IsomorphicClerk({
266-
publishableKey: 'pk_test_XXX',
267-
Clerk: mockClerkInstance as any,
268-
ui: { ClerkUI: mockClerkUI } as any,
320+
expect(loadClerkUIScript).not.toHaveBeenCalled();
321+
expect(mockLoad).toHaveBeenCalledWith(
322+
expect.objectContaining({
323+
ui: expect.objectContaining({
324+
ClerkUI: mockClerkUI,
325+
}),
326+
}),
327+
);
328+
});
329+
330+
// ─── @clerk/chrome-extension (with syncHost) ───
331+
// With syncHost: Clerk instance, ui with ClerkUI, standardBrowser: false
332+
// shouldLoadUi = (false !== false && ...) || !!ClerkUI = false || true = true
333+
// → uses bundled ClerkUI (no CDN)
334+
it('uses bundled ClerkUI for chrome-extension with syncHost (standardBrowser: false)', async () => {
335+
const mockClerkUI = vi.fn();
336+
const mockClerkInstance = {} as any;
337+
const { mockLoad } = await runGetEntryChunks({
338+
Clerk: mockClerkInstance,
339+
ui: { ClerkUI: mockClerkUI },
269340
standardBrowser: false,
270341
});
271342

272-
(global as any).Clerk = mockClerkInstance;
343+
expect(loadClerkUIScript).not.toHaveBeenCalled();
344+
expect(mockLoad).toHaveBeenCalledWith(
345+
expect.objectContaining({
346+
ui: expect.objectContaining({
347+
ClerkUI: mockClerkUI,
348+
}),
349+
}),
350+
);
351+
});
273352

274-
await (clerk as any).getEntryChunks();
353+
// ─── Clerk instance provided, no ui prop, standardBrowser: true ───
354+
// shouldLoadUi = (true && !instance) || false = false || false = false
355+
// → no UI loaded (correct: Clerk instance without bundled UI, no CDN attempt)
356+
it('does not load UI when Clerk instance provided without ui prop (standardBrowser: true)', async () => {
357+
const mockClerkInstance = {} as any;
358+
const { mockLoad } = await runGetEntryChunks({
359+
Clerk: mockClerkInstance,
360+
standardBrowser: true,
361+
});
275362

276-
// clerk.load should have been called with ui.ClerkUI preserved
363+
expect(loadClerkUIScript).not.toHaveBeenCalled();
277364
expect(mockLoad).toHaveBeenCalledWith(
278365
expect.objectContaining({
279366
ui: expect.objectContaining({
280-
ClerkUI: mockClerkUI,
367+
ClerkUI: undefined,
281368
}),
282369
}),
283370
);
284-
// Should not attempt to load UI from CDN
371+
});
372+
373+
// ─── ui prop passed as server marker (no ClerkUI), no Clerk instance ───
374+
// RSC react-server export may provide ui without ClerkUI initially
375+
// shouldLoadUi = (true && true) || false = true
376+
// → getClerkUIEntryChunk is called, but uiProp exists without ClerkUI → returns undefined (skips CDN)
377+
it('skips CDN when ui prop exists without ClerkUI (server marker object)', async () => {
378+
const { mockLoad } = await runGetEntryChunks({
379+
ui: { __brand: '__clerkUI', version: '1.0.0' },
380+
});
381+
285382
expect(loadClerkUIScript).not.toHaveBeenCalled();
383+
expect(mockLoad).toHaveBeenCalledWith(
384+
expect.objectContaining({
385+
ui: expect.objectContaining({
386+
ClerkUI: undefined,
387+
}),
388+
}),
389+
);
286390
});
287391
});
288392
});

0 commit comments

Comments
 (0)