1+ import { renderHook } from '@testing-library/react' ;
12import { afterEach , beforeEach , describe , expect , test , vi } from 'vitest' ;
23
34import {
@@ -20,7 +21,7 @@ const mocks = vi.hoisted(() => ({
2021 resetPassword : vi . fn ( ) ,
2122 } ,
2223 jsSignedInSessions : [ { id : 'sess_123' } ] ,
23- getClerkInstance : vi . fn ( ) ,
24+ useClerk : vi . fn ( ) ,
2425 setActive : vi . fn ( ) ,
2526 synchronizeNativeClientToJs : vi . fn ( ) ,
2627 nativeModule : {
@@ -32,8 +33,8 @@ const mocks = vi.hoisted(() => ({
3233 } ,
3334} ) ) ;
3435
35- vi . mock ( '../../provider/singleton ' , ( ) => ( {
36- getClerkInstance : mocks . getClerkInstance ,
36+ vi . mock ( '@clerk/react ' , ( ) => ( {
37+ useClerk : mocks . useClerk ,
3738} ) ) ;
3839
3940vi . mock ( '../../utils/native-module' , ( ) => ( {
@@ -60,13 +61,17 @@ const nativeTrustedDevice = {
6061 revokedAt : null ,
6162} ;
6263
64+ function renderTrustedDevices ( useHook = useTrustedDevicesOnIos ) {
65+ return renderHook ( ( ) => useHook ( ) ) . result . current ;
66+ }
67+
6368let unregisterNativeToJsSyncHandler : ( ( ) => void ) | undefined ;
6469
6570beforeEach ( ( ) => {
6671 __internal_resetNativeClientSyncCoordinator ( ) ;
6772 unregisterNativeToJsSyncHandler = registerNativeToJsSyncHandler ( mocks . synchronizeNativeClientToJs ) ;
6873 mocks . synchronizeNativeClientToJs . mockResolvedValue ( undefined ) ;
69- mocks . getClerkInstance . mockReturnValue ( {
74+ mocks . useClerk . mockReturnValue ( {
7075 client : { signIn : mocks . jsSignIn , signedInSessions : mocks . jsSignedInSessions } ,
7176 setActive : mocks . setActive ,
7277 } ) ;
@@ -94,7 +99,7 @@ describe('useTrustedDevices on iOS', () => {
9499 unavailableReason : null ,
95100 } ) ;
96101
97- const trustedDevices = useTrustedDevicesOnIos ( ) ;
102+ const trustedDevices = renderTrustedDevices ( ) ;
98103 const availability = await trustedDevices . getAvailability ( {
99104 id : 'td_123' ,
100105 identifierHint : 'sean@example.com' ,
@@ -115,7 +120,7 @@ describe('useTrustedDevices on iOS', () => {
115120 unavailableReason : null ,
116121 } ) ;
117122
118- const availability = useTrustedDevicesOnIos ( ) . getAvailability ( ) ;
123+ const availability = renderTrustedDevices ( ) . getAvailability ( ) ;
119124 await Promise . resolve ( ) ;
120125
121126 expect ( mocks . nativeModule . getTrustedDeviceAvailability ) . not . toHaveBeenCalled ( ) ;
@@ -133,7 +138,7 @@ describe('useTrustedDevices on iOS', () => {
133138 } ) ;
134139 trackPendingJsToNativeSync ( nativeSync ) ;
135140
136- const availability = expect ( useTrustedDevicesOnIos ( ) . getAvailability ( ) ) . rejects . toMatchObject ( {
141+ const availability = expect ( renderTrustedDevices ( ) . getAvailability ( ) ) . rejects . toMatchObject ( {
137142 code : 'environment_unavailable' ,
138143 } ) ;
139144
@@ -146,7 +151,7 @@ describe('useTrustedDevices on iOS', () => {
146151 test ( 'lists trusted devices and converts native timestamps to dates' , async ( ) => {
147152 mocks . nativeModule . listTrustedDevices . mockResolvedValue ( [ nativeTrustedDevice ] ) ;
148153
149- const [ trustedDevice ] = await useTrustedDevicesOnIos ( ) . list ( ) ;
154+ const [ trustedDevice ] = await renderTrustedDevices ( ) . list ( ) ;
150155
151156 expect ( trustedDevice ) . toEqual ( {
152157 ...nativeTrustedDevice ,
@@ -166,7 +171,7 @@ describe('useTrustedDevices on iOS', () => {
166171 } ,
167172 ] ) ;
168173
169- const [ trustedDevice ] = await useTrustedDevicesOnIos ( ) . list ( ) ;
174+ const [ trustedDevice ] = await renderTrustedDevices ( ) . list ( ) ;
170175
171176 expect ( trustedDevice . lastUsedAt ) . toBeNull ( ) ;
172177 expect ( trustedDevice . revokedAt ) . toBeNull ( ) ;
@@ -180,7 +185,7 @@ describe('useTrustedDevices on iOS', () => {
180185 trackPendingJsToNativeSync ( nativeSync ) ;
181186 mocks . nativeModule . listTrustedDevices . mockResolvedValue ( [ nativeTrustedDevice ] ) ;
182187
183- const listing = useTrustedDevicesOnIos ( ) . list ( ) ;
188+ const listing = renderTrustedDevices ( ) . list ( ) ;
184189 await Promise . resolve ( ) ;
185190
186191 expect ( mocks . nativeModule . listTrustedDevices ) . not . toHaveBeenCalled ( ) ;
@@ -191,13 +196,18 @@ describe('useTrustedDevices on iOS', () => {
191196 } ) ;
192197
193198 test ( 'returns stable operation identities' , ( ) => {
194- expect ( useTrustedDevicesOnIos ( ) ) . toBe ( useTrustedDevicesOnIos ( ) ) ;
199+ const { result, rerender } = renderHook ( ( ) => useTrustedDevicesOnIos ( ) ) ;
200+ const initialTrustedDevices = result . current ;
201+
202+ rerender ( ) ;
203+
204+ expect ( result . current ) . toBe ( initialTrustedDevices ) ;
195205 } ) ;
196206
197207 test ( 'enrolls with the safe default authentication policy' , async ( ) => {
198208 mocks . nativeModule . enrollTrustedDevice . mockResolvedValue ( nativeTrustedDevice ) ;
199209
200- const trustedDevice = await useTrustedDevicesOnIos ( ) . enroll ( {
210+ const trustedDevice = await renderTrustedDevices ( ) . enroll ( {
201211 deviceName : "Sean's iPhone" ,
202212 identifierHint : 'sean@example.com' ,
203213 reason : 'Use Face ID to trust this device.' ,
@@ -220,7 +230,7 @@ describe('useTrustedDevices on iOS', () => {
220230 trackPendingJsToNativeSync ( nativeSync ) ;
221231 mocks . nativeModule . enrollTrustedDevice . mockResolvedValue ( nativeTrustedDevice ) ;
222232
223- const enrollment = useTrustedDevicesOnIos ( ) . enroll ( ) ;
233+ const enrollment = renderTrustedDevices ( ) . enroll ( ) ;
224234 await Promise . resolve ( ) ;
225235
226236 expect ( mocks . nativeModule . enrollTrustedDevice ) . not . toHaveBeenCalled ( ) ;
@@ -237,7 +247,7 @@ describe('useTrustedDevices on iOS', () => {
237247 revokedAt : 1_700_000_300_000 ,
238248 } ) ;
239249
240- const trustedDevice = await useTrustedDevicesOnIos ( ) . revoke ( 'td_123' ) ;
250+ const trustedDevice = await renderTrustedDevices ( ) . revoke ( 'td_123' ) ;
241251
242252 expect ( mocks . nativeModule . revokeTrustedDevice ) . toHaveBeenCalledWith ( 'td_123' ) ;
243253 expect ( trustedDevice . status ) . toBe ( 'revoked' ) ;
@@ -255,7 +265,7 @@ describe('useTrustedDevices on iOS', () => {
255265 status : 'revoked' ,
256266 } ) ;
257267
258- const revocation = useTrustedDevicesOnIos ( ) . revoke ( 'td_123' ) ;
268+ const revocation = renderTrustedDevices ( ) . revoke ( 'td_123' ) ;
259269 await Promise . resolve ( ) ;
260270
261271 expect ( mocks . nativeModule . revokeTrustedDevice ) . not . toHaveBeenCalled ( ) ;
@@ -272,7 +282,7 @@ describe('useTrustedDevices on iOS', () => {
272282 createdSessionId : 'sess_123' ,
273283 } ) ;
274284
275- const result = await useTrustedDevicesOnIos ( ) . signIn ( {
285+ const result = await renderTrustedDevices ( ) . signIn ( {
276286 identifierHint : 'sean@example.com' ,
277287 reason : 'Use Face ID to sign in.' ,
278288 } ) ;
@@ -302,7 +312,7 @@ describe('useTrustedDevices on iOS', () => {
302312 createdSessionId : 'sess_other' ,
303313 } ) ;
304314
305- const result = await useTrustedDevicesOnIos ( ) . signIn ( ) ;
315+ const result = await renderTrustedDevices ( ) . signIn ( ) ;
306316
307317 expect ( result ) . toMatchObject ( {
308318 status : 'complete' ,
@@ -323,7 +333,7 @@ describe('useTrustedDevices on iOS', () => {
323333 createdSessionId : null ,
324334 } ) ;
325335
326- const result = await useTrustedDevicesOnIos ( ) . signIn ( ) ;
336+ const result = await renderTrustedDevices ( ) . signIn ( ) ;
327337
328338 expect ( result ) . toMatchObject ( {
329339 status : 'complete' ,
@@ -339,7 +349,7 @@ describe('useTrustedDevices on iOS', () => {
339349 createdSessionId : 'sess_missing' ,
340350 } ) ;
341351
342- await expect ( useTrustedDevicesOnIos ( ) . signIn ( ) ) . rejects . toThrow (
352+ await expect ( renderTrustedDevices ( ) . signIn ( ) ) . rejects . toThrow (
343353 'Unable to synchronize the trusted-device sign-in with the Clerk JS client: the created session is missing.' ,
344354 ) ;
345355 } ) ;
@@ -356,7 +366,7 @@ describe('useTrustedDevices on iOS', () => {
356366 createdSessionId : 'sess_123' ,
357367 } ) ;
358368
359- const signIn = useTrustedDevicesOnIos ( ) . signIn ( ) ;
369+ const signIn = renderTrustedDevices ( ) . signIn ( ) ;
360370 await Promise . resolve ( ) ;
361371
362372 expect ( mocks . nativeModule . signInWithTrustedDevice ) . not . toHaveBeenCalled ( ) ;
@@ -385,7 +395,7 @@ describe('useTrustedDevices on iOS', () => {
385395 return Promise . resolve ( ) ;
386396 } ) ;
387397
388- const result = await useTrustedDevicesOnIos ( ) . signIn ( ) ;
398+ const result = await renderTrustedDevices ( ) . signIn ( ) ;
389399
390400 expect ( result ) . toMatchObject ( {
391401 status,
@@ -409,7 +419,7 @@ describe('useTrustedDevices on iOS', () => {
409419 ) ;
410420
411421 let didResolve = false ;
412- const signIn = useTrustedDevicesOnIos ( )
422+ const signIn = renderTrustedDevices ( )
413423 . signIn ( )
414424 . then ( result => {
415425 didResolve = true ;
@@ -434,22 +444,34 @@ describe('useTrustedDevices on iOS', () => {
434444 createdSessionId : null ,
435445 } ) ;
436446
437- await expect ( useTrustedDevicesOnIos ( ) . signIn ( ) ) . rejects . toThrow (
447+ await expect ( renderTrustedDevices ( ) . signIn ( ) ) . rejects . toThrow (
438448 'Unable to synchronize the trusted-device sign-in with the Clerk JS client: the sign-in attempt does not match.' ,
439449 ) ;
440450 } ) ;
441451
442- test ( 'rejects when the Clerk JS instance is unavailable after synchronization' , async ( ) => {
452+ test ( 'uses the Clerk instance from the React provider after synchronization' , async ( ) => {
453+ const providerSetActive = vi . fn ( ) ;
454+ const providerSignIn = {
455+ ...mocks . jsSignIn ,
456+ id : 'sia_provider' ,
457+ } ;
443458 mocks . nativeModule . signInWithTrustedDevice . mockResolvedValue ( {
444- id : 'sia_native ' ,
459+ id : 'sia_provider ' ,
445460 status : 'complete' ,
446- createdSessionId : 'sess_native' ,
461+ createdSessionId : 'sess_provider' ,
462+ } ) ;
463+ mocks . useClerk . mockReturnValue ( {
464+ client : {
465+ signIn : providerSignIn ,
466+ signedInSessions : [ { id : 'sess_provider' } ] ,
467+ } ,
468+ setActive : providerSetActive ,
447469 } ) ;
448- mocks . getClerkInstance . mockReturnValueOnce ( undefined ) ;
449470
450- await expect ( useTrustedDevicesOnIos ( ) . signIn ( ) ) . rejects . toThrow (
451- 'Unable to synchronize the trusted-device sign-in with the Clerk JS client: the Clerk instance is unavailable.' ,
452- ) ;
471+ const result = await renderTrustedDevices ( ) . signIn ( ) ;
472+
473+ expect ( result . signIn ) . toBe ( providerSignIn ) ;
474+ expect ( result . setActive ) . toBe ( providerSetActive ) ;
453475 } ) ;
454476
455477 test ( 'rejects when the Clerk JS client is unavailable after synchronization' , async ( ) => {
@@ -458,12 +480,12 @@ describe('useTrustedDevices on iOS', () => {
458480 status : 'complete' ,
459481 createdSessionId : 'sess_native' ,
460482 } ) ;
461- mocks . getClerkInstance . mockReturnValueOnce ( {
483+ mocks . useClerk . mockReturnValue ( {
462484 client : undefined ,
463485 setActive : mocks . setActive ,
464486 } ) ;
465487
466- await expect ( useTrustedDevicesOnIos ( ) . signIn ( ) ) . rejects . toThrow (
488+ await expect ( renderTrustedDevices ( ) . signIn ( ) ) . rejects . toThrow (
467489 'Unable to synchronize the trusted-device sign-in with the Clerk JS client: the client sign-in resource is unavailable.' ,
468490 ) ;
469491 } ) ;
@@ -488,7 +510,7 @@ describe('useTrustedDevices on iOS', () => {
488510 createdSessionId : null ,
489511 } ) ;
490512
491- const trustedDevices = useTrustedDevicesOnIos ( ) ;
513+ const trustedDevices = renderTrustedDevices ( ) ;
492514 const [ device ] = await trustedDevices . list ( ) ;
493515 const signIn = await trustedDevices . signIn ( ) ;
494516
@@ -506,7 +528,7 @@ describe('useTrustedDevices on iOS', () => {
506528 } ) ;
507529 mocks . nativeModule . signInWithTrustedDevice . mockRejectedValue ( nativeError ) ;
508530
509- const operation = useTrustedDevicesOnIos ( ) . signIn ( ) ;
531+ const operation = renderTrustedDevices ( ) . signIn ( ) ;
510532
511533 await expect ( operation ) . rejects . toBe ( nativeError ) ;
512534 await operation . catch ( error => {
@@ -522,7 +544,7 @@ describe('useTrustedDevices on iOS', () => {
522544 Object . assign ( mocks . nativeModule , { signInWithTrustedDevice : undefined } ) ;
523545
524546 try {
525- await expect ( useTrustedDevicesOnIos ( ) . signIn ( ) ) . rejects . toThrow (
547+ await expect ( renderTrustedDevices ( ) . signIn ( ) ) . rejects . toThrow (
526548 'Biometric trusted devices require a development build containing a compatible version of @clerk/expo.' ,
527549 ) ;
528550 } finally {
@@ -553,7 +575,7 @@ describe('useTrustedDevices on Android', () => {
553575 } ) ;
554576 mocks . jsSignedInSessions . splice ( 0 , mocks . jsSignedInSessions . length , { id : 'sess_android' } ) ;
555577
556- const trustedDevices = useTrustedDevicesOnAndroid ( ) ;
578+ const trustedDevices = renderTrustedDevices ( useTrustedDevicesOnAndroid ) ;
557579
558580 await expect ( trustedDevices . getAvailability ( { identifierHint : 'sean@example.com' } ) ) . resolves . toEqual ( {
559581 isAvailable : true ,
0 commit comments