diff --git a/packages/clerk-js/src/ui/common/EmailLinkVerify.tsx b/packages/clerk-js/src/ui/common/EmailLinkVerify.tsx index 7d6a81f40a9..2f1d1c80c6f 100644 --- a/packages/clerk-js/src/ui/common/EmailLinkVerify.tsx +++ b/packages/clerk-js/src/ui/common/EmailLinkVerify.tsx @@ -1,5 +1,6 @@ import { EmailLinkErrorCodeStatus, isEmailLinkError } from '@clerk/shared/error'; import { useClerk } from '@clerk/shared/react'; +import { noop } from '@clerk/shared/utils'; import React from 'react'; import { completeSignUpFlow } from '../../utils'; @@ -11,16 +12,18 @@ import type { EmailLinkUIStatus } from './EmailLinkStatusCard'; import { EmailLinkStatusCard } from './EmailLinkStatusCard'; export type EmailLinkVerifyProps = { - redirectUrlComplete?: string; + continuePath?: string; + onVerifiedOnOtherDevice?: () => void; redirectUrl?: string; + redirectUrlComplete?: string; + texts: Record; verifyEmailPath?: string; verifyPhonePath?: string; - continuePath?: string; - texts: Record; }; export const EmailLinkVerify = (props: EmailLinkVerifyProps) => { - const { redirectUrl, redirectUrlComplete, verifyEmailPath, verifyPhonePath, continuePath } = props; + const { redirectUrl, redirectUrlComplete, verifyEmailPath, verifyPhonePath, continuePath, onVerifiedOnOtherDevice } = + props; const { handleEmailLinkVerification } = useClerk(); const { navigate } = useRouter(); const signUp = useCoreSignUp(); @@ -30,7 +33,17 @@ export const EmailLinkVerify = (props: EmailLinkVerifyProps) => { try { // Avoid loading flickering await sleep(750); - await handleEmailLinkVerification({ redirectUrlComplete, redirectUrl }, navigate); + const result = await handleEmailLinkVerification( + { + onVerifiedOnOtherDevice: onVerifiedOnOtherDevice || noop, + redirectUrlComplete, + redirectUrl, + }, + navigate, + ); + + if (result !== null) return; + setVerificationStatus('verified_switch_tab'); await sleep(750); await completeSignUpFlow({ diff --git a/packages/clerk-js/src/ui/components/SignUp/__tests__/SignUpEmailLinkFlowComplete.test.tsx b/packages/clerk-js/src/ui/components/SignUp/__tests__/SignUpEmailLinkFlowComplete.test.tsx index 7f4dbcf97ef..0af090b6a77 100644 --- a/packages/clerk-js/src/ui/components/SignUp/__tests__/SignUpEmailLinkFlowComplete.test.tsx +++ b/packages/clerk-js/src/ui/components/SignUp/__tests__/SignUpEmailLinkFlowComplete.test.tsx @@ -30,7 +30,7 @@ describe('SignUpEmailLinkFlowComplete', () => { }); describe('Success', () => { - it('shows the success message when successfully verified', async () => { + it('handles verification and redirects instead of showing success message', async () => { const { wrapper, fixtures } = await createFixtures(f => { f.withEmailAddress({ required: true }); }); @@ -38,7 +38,32 @@ describe('SignUpEmailLinkFlowComplete', () => { render(, { wrapper }); timers.runOnlyPendingTimers(); await waitFor(() => expect(fixtures.clerk.handleEmailLinkVerification).toHaveBeenCalled()); - screen.getByText(/success/i); + // The component should not show a success message since it should redirect + expect(screen.queryByText(/success/i)).not.toBeInTheDocument(); + }); + }); + + it('allows custom onVerifiedOnOtherDevice behavior', async () => { + const customHandler = jest.fn(); + const { wrapper, fixtures } = await createFixtures(f => { + f.withEmailAddress({ required: true }); + }); + + // Mock handleEmailLinkVerification to call onVerifiedOnOtherDevice + fixtures.clerk.handleEmailLinkVerification.mockImplementationOnce(async options => { + // Simulate verification happening on a different device + if (options.onVerifiedOnOtherDevice) { + options.onVerifiedOnOtherDevice(); + } + return null; // Return null to indicate no redirect happened + }); + + await runFakeTimers(async timers => { + render(, { wrapper }); + timers.runOnlyPendingTimers(); + await waitFor(() => expect(fixtures.clerk.handleEmailLinkVerification).toHaveBeenCalled()); + // The custom handler should be called when verification happens on a different device + expect(customHandler).toHaveBeenCalled(); }); }); });