Add Google login to checkout login form
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import React from 'react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
|
||||
vi.mock('@inertiajs/react', () => ({
|
||||
usePage: () => ({ props: { locale: 'de', googleAuth: {} } }),
|
||||
}));
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string, fallback?: string | Record<string, unknown>) => {
|
||||
if (typeof fallback === 'string') {
|
||||
return fallback;
|
||||
}
|
||||
if (fallback && typeof fallback === 'object' && typeof fallback.defaultValue === 'string') {
|
||||
return fallback.defaultValue;
|
||||
}
|
||||
return key;
|
||||
},
|
||||
}),
|
||||
Trans: ({ i18nKey }: { i18nKey: string }) => <span>{i18nKey}</span>,
|
||||
}));
|
||||
|
||||
vi.mock('react-hot-toast', () => ({
|
||||
default: {
|
||||
error: vi.fn(),
|
||||
success: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@/components/ui/button', () => ({
|
||||
Button: ({ children, onClick, ...props }: { children: React.ReactNode; onClick?: () => void }) => (
|
||||
<button type="button" onClick={onClick} {...props}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock('@/components/ui/alert', () => ({
|
||||
Alert: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
AlertDescription: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
AlertTitle: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
vi.mock('@/components/ui/collapsible', () => ({
|
||||
Collapsible: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
CollapsibleContent: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
CollapsibleTrigger: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/utils', () => ({
|
||||
cn: (...args: Array<string | boolean | undefined>) => args.filter(Boolean).join(' '),
|
||||
}));
|
||||
|
||||
vi.mock('../../../auth/LoginForm', () => ({
|
||||
default: () => <div>LoginForm</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../../../auth/RegisterForm', () => ({
|
||||
default: () => <div>RegisterForm</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../WizardContext', () => ({
|
||||
useCheckoutWizard: () => ({
|
||||
isAuthenticated: false,
|
||||
authUser: null,
|
||||
setAuthUser: vi.fn(),
|
||||
nextStep: vi.fn(),
|
||||
selectedPackage: { id: 1, name: 'Starter', price: 0, type: 'endcustomer', features: [] },
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('lucide-react', () => ({
|
||||
ChevronDown: () => <span>chevron</span>,
|
||||
LoaderCircle: () => <span>loader</span>,
|
||||
}));
|
||||
|
||||
import { AuthStep } from '../steps/AuthStep';
|
||||
|
||||
describe('AuthStep Google controls', () => {
|
||||
it('hides the top Google button when switching to login mode', () => {
|
||||
render(<AuthStep privacyHtml="" />);
|
||||
|
||||
expect(screen.getByText('checkout.auth_step.continue_with_google')).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByText('checkout.auth_step.switch_to_login'));
|
||||
|
||||
expect(screen.queryByText('checkout.auth_step.continue_with_google')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -46,6 +46,7 @@ export const AuthStep: React.FC<AuthStepProps> = ({ privacyHtml, googleProfile,
|
||||
const [mode, setMode] = useState<'login' | 'register'>('register');
|
||||
const [isRedirectingToGoogle, setIsRedirectingToGoogle] = useState(false);
|
||||
const [showGoogleHelper, setShowGoogleHelper] = useState(false);
|
||||
const showGoogleControls = mode === 'register';
|
||||
|
||||
useEffect(() => {
|
||||
if (googleAuth?.status === 'signin') {
|
||||
@@ -60,6 +61,16 @@ export const AuthStep: React.FC<AuthStepProps> = ({ privacyHtml, googleProfile,
|
||||
}
|
||||
}, [googleAuth?.error]);
|
||||
|
||||
useEffect(() => {
|
||||
if (mode !== 'login') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (showGoogleHelper) {
|
||||
setShowGoogleHelper(false);
|
||||
}
|
||||
}, [mode, showGoogleHelper]);
|
||||
|
||||
const handleLoginSuccess = (payload: AuthUserPayload | null) => {
|
||||
if (!payload) {
|
||||
return;
|
||||
@@ -148,39 +159,43 @@ export const AuthStep: React.FC<AuthStepProps> = ({ privacyHtml, googleProfile,
|
||||
>
|
||||
{t('checkout.auth_step.switch_to_login')}
|
||||
</Button>
|
||||
<div className="flex flex-1 justify-start gap-2 sm:flex-none">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleGoogleLogin}
|
||||
disabled={isRedirectingToGoogle}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
{isRedirectingToGoogle ? (
|
||||
<LoaderCircle className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<GoogleIcon className="h-4 w-4" />
|
||||
)}
|
||||
{t('checkout.auth_step.continue_with_google')}
|
||||
</Button>
|
||||
<CollapsibleTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1 rounded-full border border-muted-foreground/30 px-3 py-1 text-xs font-medium text-muted-foreground transition hover:border-muted-foreground/60 hover:text-foreground",
|
||||
showGoogleHelper && "bg-muted/60 text-foreground"
|
||||
)}
|
||||
{showGoogleControls && (
|
||||
<div className="flex flex-1 justify-start gap-2 sm:flex-none">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleGoogleLogin}
|
||||
disabled={isRedirectingToGoogle}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
{t('checkout.auth_step.google_helper_badge')}
|
||||
<ChevronDown className={cn("h-3 w-3 transition-transform", showGoogleHelper && "rotate-180")} />
|
||||
</button>
|
||||
</CollapsibleTrigger>
|
||||
</div>
|
||||
{isRedirectingToGoogle ? (
|
||||
<LoaderCircle className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<GoogleIcon className="h-4 w-4" />
|
||||
)}
|
||||
{t('checkout.auth_step.continue_with_google')}
|
||||
</Button>
|
||||
<CollapsibleTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1 rounded-full border border-muted-foreground/30 px-3 py-1 text-xs font-medium text-muted-foreground transition hover:border-muted-foreground/60 hover:text-foreground",
|
||||
showGoogleHelper && "bg-muted/60 text-foreground"
|
||||
)}
|
||||
>
|
||||
{t('checkout.auth_step.google_helper_badge')}
|
||||
<ChevronDown className={cn("h-3 w-3 transition-transform", showGoogleHelper && "rotate-180")} />
|
||||
</button>
|
||||
</CollapsibleTrigger>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<CollapsibleContent>
|
||||
<Alert className="border-dashed border-muted/60 bg-muted/20 text-xs sm:text-sm">
|
||||
<AlertDescription>{t('checkout.auth_step.google_helper')}</AlertDescription>
|
||||
</Alert>
|
||||
</CollapsibleContent>
|
||||
{showGoogleControls && (
|
||||
<CollapsibleContent>
|
||||
<Alert className="border-dashed border-muted/60 bg-muted/20 text-xs sm:text-sm">
|
||||
<AlertDescription>{t('checkout.auth_step.google_helper')}</AlertDescription>
|
||||
</Alert>
|
||||
</CollapsibleContent>
|
||||
)}
|
||||
</Collapsible>
|
||||
|
||||
{googleAuth?.error && (
|
||||
|
||||
Reference in New Issue
Block a user