@@ -72,12 +72,14 @@ jest.mock('@salesforce/retail-react-app/app/hooks/use-current-basket', () => {
7272} )
7373
7474const mockLoginWithPasskey = jest . fn ( ) . mockResolvedValue ( undefined )
75+ const mockAbortPasskeyLogin = jest . fn ( )
7576
7677jest . mock ( '@salesforce/retail-react-app/app/hooks/use-passkey-login' , ( ) => {
7778 return {
7879 __esModule : true ,
7980 usePasskeyLogin : jest . fn ( ( ) => ( {
80- loginWithPasskey : mockLoginWithPasskey
81+ loginWithPasskey : mockLoginWithPasskey ,
82+ abortPasskeyLogin : mockAbortPasskeyLogin
8183 } ) )
8284 }
8385} )
@@ -455,8 +457,8 @@ describe('passkey login', () => {
455457 } )
456458 } )
457459
458- test ( 'does not call loginWithPasskey when customer is registered ' , async ( ) => {
459- // Mock registered customer
460+ test ( 'does not prompt for passkey when user is already logged in ' , async ( ) => {
461+ // When customer is registered, we must not trigger passkey (no prompt)
460462 mockUseCurrentCustomer . mockReturnValue ( {
461463 data : {
462464 isRegistered : true ,
@@ -466,9 +468,75 @@ describe('passkey login', () => {
466468
467469 renderWithProviders ( < ContactInfo /> )
468470
469- // Wait a bit to ensure useEffect has run
470471 await waitFor ( ( ) => {
471472 expect ( mockLoginWithPasskey ) . not . toHaveBeenCalled ( )
472473 } )
473474 } )
475+
476+ test ( 'calls abortPasskeyLogin when component unmounts' , async ( ) => {
477+ const { unmount} = renderWithProviders ( < ContactInfo /> )
478+
479+ // Wait for passkey login to be triggered
480+ await waitFor ( ( ) => {
481+ expect ( mockLoginWithPasskey ) . toHaveBeenCalled ( )
482+ } )
483+
484+ // Verify abort hasn't been called yet
485+ expect ( mockAbortPasskeyLogin ) . not . toHaveBeenCalled ( )
486+
487+ // Unmount the component (simulates navigating away)
488+ unmount ( )
489+
490+ // Verify abort was called during cleanup
491+ expect ( mockAbortPasskeyLogin ) . toHaveBeenCalled ( )
492+ } )
493+
494+ test ( 'Passkey prompt is aborted when user logs in with password' , async ( ) => {
495+ // This test verifies that when the user logs in with password while the passkey
496+ // flow is pending, the useEffect cleanup runs (customer becomes registered) and
497+ // abortPasskeyLogin is called.
498+ //
499+ // The actual abort behavior is tested in use-passkey-login.test.js.
500+
501+ let customerRegistered = false
502+ mockUseCurrentCustomer . mockImplementation ( ( ) => ( {
503+ data : { isRegistered : customerRegistered }
504+ } ) )
505+
506+ // When login succeeds, mark customer as registered so the next render triggers
507+ // the useEffect cleanup (abortPasskeyLogin)
508+ global . server . use (
509+ rest . post ( '*/oauth2/login' , ( req , res , ctx ) => {
510+ customerRegistered = true
511+ return res ( ctx . delay ( 0 ) , ctx . status ( 200 ) , ctx . json ( mockedRegisteredCustomer ) )
512+ } )
513+ )
514+
515+ const { user} = renderWithProviders ( < ContactInfo /> )
516+
517+ // Wait for passkey login to be triggered (passkey prompt is "pending")
518+ await waitFor ( ( ) => {
519+ expect ( mockLoginWithPasskey ) . toHaveBeenCalled ( )
520+ } )
521+ expect ( mockAbortPasskeyLogin ) . not . toHaveBeenCalled ( )
522+
523+ // User switches to login and logs in with password
524+ const trigger = screen . getByText ( / A l r e a d y h a v e a n a c c o u n t \? L o g i n / i)
525+ await user . click ( trigger )
526+
527+ await user . type ( screen . getByLabelText ( 'Email' ) , validEmail )
528+ await user . type ( screen . getByLabelText ( 'Password' ) , password )
529+
530+ const loginButton = screen . getByText ( 'Log In' )
531+ await user . click ( loginButton )
532+
533+ // Login succeeds; component re-renders with isRegistered: true; cleanup runs
534+ await waitFor (
535+ ( ) => {
536+ expect ( mockAbortPasskeyLogin ) . toHaveBeenCalled ( )
537+ expect ( mockGoToNextStep ) . toHaveBeenCalled ( )
538+ } ,
539+ { timeout : 3000 }
540+ )
541+ } )
474542} )
0 commit comments