@@ -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