/* @vitest-environment jsdom */
import {
cleanup,
fireEvent,
render,
screen,
within,
} from '@testing-library/react';
import { expect, test, vi } from 'vitest';
import {
PuzzleOnboardingLoginOverlay,
type PuzzleOnboardingPhase,
PuzzleOnboardingView,
} from './PuzzleOnboardingView';
function renderOnboarding({
prompt = '月亮糖果工厂',
phase = 'input',
error = null,
onPromptChange = vi.fn(),
onSubmit = vi.fn(),
onSkip = vi.fn(),
}: {
prompt?: string;
phase?: PuzzleOnboardingPhase;
error?: string | null;
onPromptChange?: (value: string) => void;
onSubmit?: () => void;
onSkip?: () => void;
} = {}) {
render(
,
);
return { onPromptChange, onSubmit, onSkip };
}
test('PuzzleOnboardingView uses shared dark textarea and error status chrome', () => {
const { onPromptChange } = renderOnboarding({
error: '拼图生成失败',
});
const textarea = screen.getByPlaceholderText('把你的梦讲给我听吧');
fireEvent.change(textarea, { target: { value: '一座会唱歌的城堡' } });
expect(textarea.tagName).toBe('TEXTAREA');
expect(textarea.className).toContain('platform-text-field--editor-dark');
expect(textarea.className).toContain('min-h-32');
expect(onPromptChange).toHaveBeenCalledWith('一座会唱歌的城堡');
expect(screen.getByText('拼图生成失败').className).toContain(
'platform-status-message',
);
expect(screen.getByText('拼图生成失败').className).toContain(
'border-rose-300/15',
);
expect(screen.queryByRole('dialog')).toBeNull();
});
test('PuzzleOnboardingView preserves submit, skip, and disabled phase behavior', () => {
const { onSubmit, onSkip } = renderOnboarding();
const submitButton = screen.getByRole('button', { name: '生成' });
const skipButton = screen.getByRole('button', { name: '跳过' });
expect(submitButton.className).toContain('platform-action-button--accent');
expect(submitButton.className).toContain('bg-amber-200');
expect(submitButton.className).toContain('w-full');
expect(skipButton.className).toContain(
'platform-action-button--editor-dark',
);
expect(skipButton.className).toContain('rounded-full');
expect(skipButton.className).toContain('absolute');
fireEvent.click(submitButton);
fireEvent.click(skipButton);
expect(onSubmit).toHaveBeenCalledTimes(1);
expect(onSkip).toHaveBeenCalledTimes(1);
cleanup();
renderOnboarding({ prompt: '', phase: 'input' });
expect(screen.getByRole('button', { name: '生成' })).toHaveProperty(
'disabled',
true,
);
cleanup();
renderOnboarding({ phase: 'generating' });
expect(screen.getByPlaceholderText('把你的梦讲给我听吧')).toHaveProperty(
'disabled',
true,
);
expect(screen.getByRole('button', { name: '跳过' })).toHaveProperty(
'disabled',
true,
);
cleanup();
renderOnboarding({ phase: 'generated' });
expect(screen.getByPlaceholderText('把你的梦讲给我听吧')).toHaveProperty(
'disabled',
true,
);
expect(screen.getByRole('button', { name: '生成' })).toHaveProperty(
'disabled',
true,
);
});
test('PuzzleOnboardingLoginOverlay uses shared error status and keeps login action', () => {
const onLogin = vi.fn();
const { rerender } = render(
,
);
const dialog = screen.getByRole('dialog', { name: '登录后保存你的拼图' });
expect(dialog.className).toContain('platform-modal-shell');
expect(dialog.className).toContain('!max-w-[24rem]');
expect(dialog.parentElement?.className).toContain('z-[110]');
expect(
within(dialog).queryByRole('button', { name: '关闭' }),
).toBeNull();
fireEvent.click(screen.getByRole('button', { name: '注册账号 / 登录' }));
expect(onLogin).toHaveBeenCalledTimes(1);
expect(screen.getByRole('button', { name: '注册账号 / 登录' }).className).toContain(
'platform-action-button--accent',
);
expect(screen.getByText('保存首访拼图失败').className).toContain(
'platform-status-message',
);
rerender(
,
);
expect(screen.getByRole('button', { name: '注册账号 / 登录' })).toHaveProperty(
'disabled',
true,
);
});