收口拼图首访输入与错误提示
将拼图首访提示词文本域迁移到 PlatformTextField 将拼图首访输入错误和登录保存错误迁移到 PlatformStatusMessage 补充拼图首访输入和错误提示公共组件断言 更新 PlatformUiKit 文档和 Hermes 决策记录
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
||||
import { expect, test, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
PuzzleOnboardingLoginOverlay,
|
||||
PuzzleOnboardingView,
|
||||
type PuzzleOnboardingPhase,
|
||||
} 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(
|
||||
<PuzzleOnboardingView
|
||||
prompt={prompt}
|
||||
phase={phase}
|
||||
error={error}
|
||||
copy="把梦做成拼图"
|
||||
onPromptChange={onPromptChange}
|
||||
onSubmit={onSubmit}
|
||||
onSkip={onSkip}
|
||||
/>,
|
||||
);
|
||||
|
||||
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',
|
||||
);
|
||||
});
|
||||
|
||||
test('PuzzleOnboardingView preserves submit, skip, and disabled phase behavior', () => {
|
||||
const { onSubmit, onSkip } = renderOnboarding();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '生成' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '跳过' }));
|
||||
|
||||
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(
|
||||
<PuzzleOnboardingLoginOverlay
|
||||
isSaving={false}
|
||||
error="保存首访拼图失败"
|
||||
copy="登录后保存你的拼图"
|
||||
onLogin={onLogin}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '注册账号 / 登录' }));
|
||||
|
||||
expect(onLogin).toHaveBeenCalledTimes(1);
|
||||
expect(screen.getByText('保存首访拼图失败').className).toContain(
|
||||
'platform-status-message',
|
||||
);
|
||||
|
||||
rerender(
|
||||
<PuzzleOnboardingLoginOverlay
|
||||
isSaving
|
||||
error={null}
|
||||
copy="登录后保存你的拼图"
|
||||
onLogin={onLogin}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole('button', { name: '注册账号 / 登录' })).toHaveProperty(
|
||||
'disabled',
|
||||
true,
|
||||
);
|
||||
});
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Loader2, Sparkles } from 'lucide-react';
|
||||
|
||||
import { PlatformStatusMessage } from '../../common/PlatformStatusMessage';
|
||||
import { PlatformTextField } from '../../common/PlatformTextField';
|
||||
|
||||
export type PuzzleOnboardingPhase = 'input' | 'generating' | 'generated';
|
||||
|
||||
type PuzzleOnboardingViewProps = {
|
||||
@@ -54,13 +57,18 @@ export function PuzzleOnboardingView({
|
||||
onSubmit();
|
||||
}}
|
||||
>
|
||||
<textarea
|
||||
<PlatformTextField
|
||||
variant="textarea"
|
||||
surface="editorDark"
|
||||
tone="warm"
|
||||
density="roomy"
|
||||
size="lg"
|
||||
value={prompt}
|
||||
disabled={isGenerating || isGenerated}
|
||||
onChange={(event) => onPromptChange(event.target.value)}
|
||||
placeholder="把你的梦讲给我听吧"
|
||||
rows={4}
|
||||
className="min-h-32 w-full resize-none rounded-[1.25rem] border border-white/14 bg-black/28 px-4 py-4 text-base font-semibold leading-7 text-white shadow-[0_18px_50px_rgba(0,0,0,0.24)] outline-none backdrop-blur placeholder:text-white/42 focus:border-amber-200/70 focus:ring-2 focus:ring-amber-200/20 disabled:opacity-70"
|
||||
className="min-h-32 rounded-[1.25rem] border-white/14 bg-black/28 py-4 leading-7 shadow-[0_18px_50px_rgba(0,0,0,0.24)] backdrop-blur placeholder:text-white/42 focus:border-amber-200/70 focus:ring-2 focus:ring-amber-200/20 disabled:opacity-70"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
@@ -78,9 +86,14 @@ export function PuzzleOnboardingView({
|
||||
</button>
|
||||
</form>
|
||||
{error ? (
|
||||
<div className="w-full rounded-[1rem] border border-red-300/30 bg-red-500/14 px-4 py-3 text-sm font-semibold text-red-50">
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="editorDark"
|
||||
size="md"
|
||||
className="w-full font-semibold"
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
</section>
|
||||
</div>
|
||||
@@ -127,9 +140,14 @@ export function PuzzleOnboardingLoginOverlay({
|
||||
)}
|
||||
</button>
|
||||
{error ? (
|
||||
<div className="w-full rounded-[1rem] border border-red-300/30 bg-red-500/14 px-4 py-3 text-sm font-semibold text-red-50">
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="editorDark"
|
||||
size="md"
|
||||
className="w-full font-semibold"
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user