From b63c4e6c5863f2c7405eca5167c5dc9b76d8789f Mon Sep 17 00:00:00 2001 From: KrishP147 Date: Wed, 23 Sep 2026 23:13:06 -0400 Subject: [PATCH] Register: move dup helpers to utils/registerHelpers, restore login link for OAuth dup error Drops file-level react-refresh eslint-disable. OAuth block_existing msg lost its login link in #25 (errorKind null); set 'duplicate' when msg says 'already exists'. Co-Authored-By: Claude Opus 5.5 (1M context) --- frontend/src/pages/Register.jsx | 27 ++----------------- .../src/pages/__tests__/Register.test.jsx | 11 +++++++- frontend/src/utils/registerHelpers.js | 24 +++++++++++++++++ 3 files changed, 36 insertions(+), 26 deletions(-) create mode 100644 frontend/src/utils/registerHelpers.js diff --git a/frontend/src/pages/Register.jsx b/frontend/src/pages/Register.jsx index 80bcbcd..4acb6a2 100644 --- a/frontend/src/pages/Register.jsx +++ b/frontend/src/pages/Register.jsx @@ -3,30 +3,7 @@ import { supabase } from '../supabaseClient'; import { Link } from 'react-router-dom'; import { motion as Motion } from 'motion/react'; import { Mail, Lock, User, ArrowRight, AlertCircle, CheckCircle2, Eye, EyeOff } from 'lucide-react'; - -/* eslint-disable react-refresh/only-export-components -- test-only helper exports */ -// Generous 5 min: a false "already exists" for a genuinely new user is worse -// than missing a duplicate. -export const EXISTING_ACCOUNT_AGE_MS = 300_000; - -export const DUPLICATE_ACCOUNT_MESSAGE = - "An account with this email already exists — log in or reset your password."; - -// Supabase returns identities: [] when the email is already registered (confirmed -// account). For an unconfirmed duplicate, identities come back non-empty but the -// account's created_at is old (Supabase re-signup doesn't reset it). A missing or -// unparseable created_at, or missing identities, is treated as a fresh account. -export function isExistingAccount(user, now = Date.now()) { - if (!user) return false; - if (user.identities?.length === 0) return true; - if (user.created_at) { - const createdAt = Date.parse(user.created_at); - if (!Number.isNaN(createdAt) && now - createdAt > EXISTING_ACCOUNT_AGE_MS) { - return true; - } - } - return false; -} +import { isExistingAccount, DUPLICATE_ACCOUNT_MESSAGE } from '../utils/registerHelpers'; export default function Register() { const [email, setEmail] = useState(''); @@ -44,7 +21,7 @@ export default function Register() { const oauthError = localStorage.getItem('oauth_login_error'); if (oauthError) { setError(oauthError); - setErrorKind(null); + setErrorKind(oauthError.includes('already exists') ? 'duplicate' : null); localStorage.removeItem('oauth_login_error'); } }, []); diff --git a/frontend/src/pages/__tests__/Register.test.jsx b/frontend/src/pages/__tests__/Register.test.jsx index e2ebf26..a0225d2 100644 --- a/frontend/src/pages/__tests__/Register.test.jsx +++ b/frontend/src/pages/__tests__/Register.test.jsx @@ -4,7 +4,8 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { render, screen, fireEvent, waitFor, cleanup, act } from '@testing-library/react'; import { BrowserRouter } from 'react-router-dom'; -import Register, { isExistingAccount, EXISTING_ACCOUNT_AGE_MS } from '../Register'; +import Register from '../Register'; +import { isExistingAccount, EXISTING_ACCOUNT_AGE_MS } from '../../utils/registerHelpers'; // Mock Supabase vi.mock('../../supabaseClient', () => ({ @@ -496,6 +497,14 @@ describe('Register', () => { expect(localStorage.getItem('oauth_login_error')).toBeNull(); }); + it('shows log-in link for OAuth existing-account error from localStorage', () => { + localStorage.setItem('oauth_login_error', 'An account with this Google account already exists. Please login instead.'); + + renderRegister(); + + expect(screen.getByRole('link', { name: /^log in$/i })).toBeInTheDocument(); + }); + it('handles Google OAuth error', async () => { const { supabase } = await import('../../supabaseClient'); supabase.auth.signInWithOAuth.mockResolvedValue({ diff --git a/frontend/src/utils/registerHelpers.js b/frontend/src/utils/registerHelpers.js new file mode 100644 index 0000000..f88c041 --- /dev/null +++ b/frontend/src/utils/registerHelpers.js @@ -0,0 +1,24 @@ +// Pure helpers for Register: duplicate-account detection after supabase.auth.signUp. + +// Generous 5 min: a false "already exists" for a genuinely new user is worse +// than missing a duplicate. +export const EXISTING_ACCOUNT_AGE_MS = 300_000; + +export const DUPLICATE_ACCOUNT_MESSAGE = + "An account with this email already exists — log in or reset your password."; + +// Supabase returns identities: [] when the email is already registered (confirmed +// account). For an unconfirmed duplicate, identities come back non-empty but the +// account's created_at is old (Supabase re-signup doesn't reset it). A missing or +// unparseable created_at, or missing identities, is treated as a fresh account. +export function isExistingAccount(user, now = Date.now()) { + if (!user) return false; + if (user.identities?.length === 0) return true; + if (user.created_at) { + const createdAt = Date.parse(user.created_at); + if (!Number.isNaN(createdAt) && now - createdAt > EXISTING_ACCOUNT_AGE_MS) { + return true; + } + } + return false; +}