diff --git a/src/__tests__/connect-mail-redirect.test.tsx b/src/__tests__/connect-mail-redirect.test.tsx
index 522e31db1..b54d87ef0 100644
--- a/src/__tests__/connect-mail-redirect.test.tsx
+++ b/src/__tests__/connect-mail-redirect.test.tsx
@@ -1,10 +1,13 @@
// Component-level: ConnectMail builds redirectUri via relativeUrl so magic-link return
// keeps personal-iban and other query params from redirectPath / current search.
+// Also covers the house-brand wallet default when no `?wallet=` URL param is present.
const mockSignInWithMail = jest.fn();
const mockRedirectPath = jest.fn();
const mockNavigate = jest.fn();
const mockUseAppParams = jest.fn();
+const mockOnCancel = jest.fn();
+const mockLocationSearch = jest.fn(() => '?user=user@example.com');
jest.mock('@dfx.swiss/react', () => ({
Utils: { createRules: () => ({}) },
@@ -27,7 +30,7 @@ jest.mock('@dfx.swiss/react-components', () => ({
}));
jest.mock('react-router-dom', () => ({
- useLocation: () => ({ search: '?user=user@example.com' }),
+ useLocation: () => ({ search: mockLocationSearch() }),
}));
jest.mock('../contexts/app-handling.context', () => ({
@@ -51,6 +54,10 @@ jest.mock('../hooks/navigation.hook', () => ({
useNavigation: () => ({ navigate: mockNavigate }),
}));
+jest.mock('../components/home/connect-shared', () => ({
+ ConnectError: ({ error }: { error: string }) =>
{error}
,
+}));
+
import { act, render, screen, waitFor } from '@testing-library/react';
import { createRef } from 'react';
import ConnectMail from '../components/home/wallet/connect-mail';
@@ -63,6 +70,8 @@ describe('ConnectMail login redirect', () => {
jest.clearAllMocks();
mockSignInWithMail.mockResolvedValue(undefined);
mockUseAppParams.mockReturnValue({ wallet: undefined, recommendationCode: undefined });
+ mockRedirectPath.mockReturnValue('/buy');
+ mockLocationSearch.mockReturnValue('?user=user@example.com');
locationStub = {
href: 'http://localhost/login',
@@ -86,7 +95,7 @@ describe('ConnectMail login redirect', () => {
blockchain={undefined}
isConnect={false}
onLogin={jest.fn()}
- onCancel={jest.fn()}
+ onCancel={mockOnCancel}
onSwitch={jest.fn()}
/>,
);
@@ -142,4 +151,117 @@ describe('ConnectMail login redirect', () => {
expect(redirectUri).toBe('http://localhost/buy');
expect(redirectUri).not.toContain('?');
});
+
+ it('sends wallet=DFX Wallet when no wallet URL param is present', async () => {
+ mockUseAppParams.mockReturnValue({ wallet: undefined, recommendationCode: undefined });
+
+ renderConnectMail();
+
+ await act(async () => {
+ screen.getByRole('button', { name: 'Next' }).click();
+ });
+
+ await waitFor(() => expect(mockSignInWithMail).toHaveBeenCalled());
+
+ expect(mockSignInWithMail).toHaveBeenCalledWith(
+ 'user@example.com',
+ 'http://localhost/buy',
+ undefined,
+ 'DFX Wallet',
+ );
+ });
+
+ it('forwards an explicit wallet URL param unchanged', async () => {
+ mockUseAppParams.mockReturnValue({ wallet: 'RealUnit', recommendationCode: 'ref-1' });
+
+ renderConnectMail();
+
+ await act(async () => {
+ screen.getByRole('button', { name: 'Next' }).click();
+ });
+
+ await waitFor(() => expect(mockSignInWithMail).toHaveBeenCalled());
+
+ expect(mockSignInWithMail).toHaveBeenCalledWith(
+ 'user@example.com',
+ 'http://localhost/buy',
+ 'ref-1',
+ 'RealUnit',
+ );
+ });
+
+ it('shows the sent confirmation and Back returns to home', async () => {
+ renderConnectMail();
+
+ await act(async () => {
+ screen.getByRole('button', { name: 'Next' }).click();
+ });
+
+ await waitFor(() =>
+ expect(
+ screen.getByText('We have sent an email with further instructions to the address provided.'),
+ ).toBeInTheDocument(),
+ );
+
+ await act(async () => {
+ screen.getByRole('button', { name: 'Back' }).click();
+ });
+
+ expect(mockOnCancel).toHaveBeenCalled();
+ expect(mockNavigate).toHaveBeenCalledWith({ pathname: '/' }, { clearParams: ['user'] });
+ });
+
+ it('surfaces the API error message when sign-in fails', async () => {
+ mockSignInWithMail.mockRejectedValue({ message: 'Mail service down' });
+
+ renderConnectMail();
+
+ await act(async () => {
+ screen.getByRole('button', { name: 'Next' }).click();
+ });
+
+ await waitFor(() => expect(screen.getByTestId('connect-error')).toHaveTextContent('Mail service down'));
+ });
+
+ it('falls back to Unknown error when the rejection has no message', async () => {
+ mockSignInWithMail.mockRejectedValue({});
+
+ renderConnectMail();
+
+ await act(async () => {
+ screen.getByRole('button', { name: 'Next' }).click();
+ });
+
+ await waitFor(() => expect(screen.getByTestId('connect-error')).toHaveTextContent('Unknown error'));
+ });
+
+ it('omits redirectUri when redirectPath is unset', async () => {
+ mockRedirectPath.mockReturnValue(undefined);
+
+ renderConnectMail();
+
+ await act(async () => {
+ screen.getByRole('button', { name: 'Next' }).click();
+ });
+
+ await waitFor(() => expect(mockSignInWithMail).toHaveBeenCalled());
+
+ expect(mockSignInWithMail.mock.calls[0][1]).toBeFalsy();
+ });
+
+ it('submits without a prefilled mail when the user query param is absent', async () => {
+ mockLocationSearch.mockReturnValue('');
+
+ renderConnectMail();
+
+ await act(async () => {
+ screen.getByRole('button', { name: 'Next' }).click();
+ });
+
+ await waitFor(() => expect(mockSignInWithMail).toHaveBeenCalled());
+
+ // No `user` query → RHF default is undefined; the form still submits under the test mock.
+ expect(mockSignInWithMail.mock.calls[0][0]).toBeUndefined();
+ expect(mockSignInWithMail.mock.calls[0][3]).toBe('DFX Wallet');
+ });
});
diff --git a/src/components/home/wallet/connect-mail.tsx b/src/components/home/wallet/connect-mail.tsx
index c0be37f7a..addf2ff63 100644
--- a/src/components/home/wallet/connect-mail.tsx
+++ b/src/components/home/wallet/connect-mail.tsx
@@ -22,6 +22,11 @@ interface FormData {
mail: string;
}
+// Resolvable name of the backend default wallet (wallet table: "DFX Wallet", Config.defaultWalletId = 1).
+// Sent when no explicit `wallet` URL param is present, so POST /v1/auth/mail resolves the login wallet by
+// name rather than via getDefault(). Partner embeds that pass `?wallet=` keep their value (??).
+const DFX_WALLET_NAME = 'DFX Wallet';
+
export default function ConnectMail({ onCancel }: ConnectProps): JSX.Element {
const { translate, translateError } = useSettingsContext();
const { signInWithMail } = useAuth();
@@ -62,7 +67,7 @@ export default function ConnectMail({ onCancel }: ConnectProps): JSX.Element {
async function submit({ mail }: FormData): Promise {
setIsLoading(true);
setError(undefined);
- signInWithMail(mail, redirectUri, recommendationCode, wallet)
+ signInWithMail(mail, redirectUri, recommendationCode, wallet ?? DFX_WALLET_NAME)
.then(() => setMailSent(true))
.catch((error: ApiError) => setError(error.message ?? 'Unknown error'))
.finally(() => setIsLoading(false));