|
|
@@ -1,221 +0,0 @@
|
|
|
-import React from 'react';
|
|
|
-import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
-import { render, screen, waitFor } from '@testing-library/react';
|
|
|
-import userEvent from '@testing-library/user-event';
|
|
|
-import { TestWrapper } from '../../__test_utils__/test-render';
|
|
|
-import { TestRouter } from '../../__test_utils__/test-router';
|
|
|
-import { TestQueryProvider } from '../../__test_utils__/test-query';
|
|
|
-
|
|
|
-// 模拟的表单组件
|
|
|
-function LoginForm({ onSubmit }: { onSubmit: (data: any) => Promise<void> }) {
|
|
|
- const [email, setEmail] = React.useState('');
|
|
|
- const [password, setPassword] = React.useState('');
|
|
|
- const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
|
-
|
|
|
- const handleSubmit = async (e: React.FormEvent) => {
|
|
|
- e.preventDefault();
|
|
|
- setIsSubmitting(true);
|
|
|
- try {
|
|
|
- await onSubmit({ email, password });
|
|
|
- } catch (error) {
|
|
|
- console.error('Login failed:', error);
|
|
|
- } finally {
|
|
|
- setIsSubmitting(false);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- return (
|
|
|
- <form onSubmit={handleSubmit} data-testid="login-form">
|
|
|
- <div>
|
|
|
- <label htmlFor="email">Email:</label>
|
|
|
- <input
|
|
|
- id="email"
|
|
|
- type="email"
|
|
|
- value={email}
|
|
|
- onChange={(e) => setEmail(e.target.value)}
|
|
|
- required
|
|
|
- data-testid="email-input"
|
|
|
- />
|
|
|
- </div>
|
|
|
- <div>
|
|
|
- <label htmlFor="password">Password:</label>
|
|
|
- <input
|
|
|
- id="password"
|
|
|
- type="password"
|
|
|
- value={password}
|
|
|
- onChange={(e) => setPassword(e.target.value)}
|
|
|
- required
|
|
|
- data-testid="password-input"
|
|
|
- />
|
|
|
- </div>
|
|
|
- <button
|
|
|
- type="submit"
|
|
|
- disabled={isSubmitting}
|
|
|
- data-testid="submit-button"
|
|
|
- >
|
|
|
- {isSubmitting ? 'Logging in...' : 'Login'}
|
|
|
- </button>
|
|
|
- </form>
|
|
|
- );
|
|
|
-}
|
|
|
-
|
|
|
-// 模拟的登录页面组件
|
|
|
-function LoginPage() {
|
|
|
- const loginMutation = {
|
|
|
- mutateAsync: vi.fn().mockResolvedValue({ success: true }),
|
|
|
- isLoading: false,
|
|
|
- };
|
|
|
-
|
|
|
- const handleLogin = async (credentials: any) => {
|
|
|
- return loginMutation.mutateAsync(credentials);
|
|
|
- };
|
|
|
-
|
|
|
- return (
|
|
|
- <div>
|
|
|
- <h1>Login</h1>
|
|
|
- <LoginForm onSubmit={handleLogin} />
|
|
|
- </div>
|
|
|
- );
|
|
|
-}
|
|
|
-
|
|
|
-describe('Form Component Integration Tests', () => {
|
|
|
- let user: ReturnType<typeof userEvent.setup>;
|
|
|
- let loginMock: any;
|
|
|
-
|
|
|
- beforeEach(() => {
|
|
|
- user = userEvent.setup();
|
|
|
- loginMock = vi.fn().mockResolvedValue({ success: true });
|
|
|
- });
|
|
|
-
|
|
|
- it('应该渲染表单并包含所有必要的字段', () => {
|
|
|
- render(
|
|
|
- <TestWrapper>
|
|
|
- <LoginForm onSubmit={loginMock} />
|
|
|
- </TestWrapper>
|
|
|
- );
|
|
|
-
|
|
|
- expect(screen.getByTestId('login-form')).toBeInTheDocument();
|
|
|
- expect(screen.getByTestId('email-input')).toBeInTheDocument();
|
|
|
- expect(screen.getByTestId('password-input')).toBeInTheDocument();
|
|
|
- expect(screen.getByTestId('submit-button')).toBeInTheDocument();
|
|
|
- });
|
|
|
-
|
|
|
- it('应该允许用户输入邮箱和密码', async () => {
|
|
|
- render(
|
|
|
- <TestWrapper>
|
|
|
- <LoginForm onSubmit={loginMock} />
|
|
|
- </TestWrapper>
|
|
|
- );
|
|
|
-
|
|
|
- const emailInput = screen.getByTestId('email-input');
|
|
|
- const passwordInput = screen.getByTestId('password-input');
|
|
|
-
|
|
|
- await user.type(emailInput, 'test@example.com');
|
|
|
- await user.type(passwordInput, 'password123');
|
|
|
-
|
|
|
- expect(emailInput).toHaveValue('test@example.com');
|
|
|
- expect(passwordInput).toHaveValue('password123');
|
|
|
- });
|
|
|
-
|
|
|
- it('应该在表单提交时调用onSubmit回调', async () => {
|
|
|
- render(
|
|
|
- <TestWrapper>
|
|
|
- <LoginForm onSubmit={loginMock} />
|
|
|
- </TestWrapper>
|
|
|
- );
|
|
|
-
|
|
|
- await user.type(screen.getByTestId('email-input'), 'test@example.com');
|
|
|
- await user.type(screen.getByTestId('password-input'), 'password123');
|
|
|
- await user.click(screen.getByTestId('submit-button'));
|
|
|
-
|
|
|
- await waitFor(() => {
|
|
|
- expect(loginMock).toHaveBeenCalledWith({
|
|
|
- email: 'test@example.com',
|
|
|
- password: 'password123'
|
|
|
- });
|
|
|
- });
|
|
|
- });
|
|
|
-
|
|
|
- it('应该在提交时显示加载状态', async () => {
|
|
|
- const slowLoginMock = vi.fn().mockImplementation(
|
|
|
- () => new Promise(resolve => setTimeout(() => resolve({ success: true }), 100))
|
|
|
- );
|
|
|
-
|
|
|
- render(
|
|
|
- <TestWrapper>
|
|
|
- <LoginForm onSubmit={slowLoginMock} />
|
|
|
- </TestWrapper>
|
|
|
- );
|
|
|
-
|
|
|
- await user.type(screen.getByTestId('email-input'), 'test@example.com');
|
|
|
- await user.type(screen.getByTestId('password-input'), 'password123');
|
|
|
- await user.click(screen.getByTestId('submit-button'));
|
|
|
-
|
|
|
- expect(screen.getByTestId('submit-button')).toBeDisabled();
|
|
|
- expect(screen.getByTestId('submit-button')).toHaveTextContent('Logging in...');
|
|
|
-
|
|
|
- await waitFor(() => {
|
|
|
- expect(screen.getByTestId('submit-button')).not.toBeDisabled();
|
|
|
- });
|
|
|
- });
|
|
|
-
|
|
|
- it('应该与React Router集成', () => {
|
|
|
- render(
|
|
|
- <TestQueryProvider>
|
|
|
- <TestRouter initialPath="/login">
|
|
|
- <LoginPage />
|
|
|
- </TestRouter>
|
|
|
- </TestQueryProvider>
|
|
|
- );
|
|
|
-
|
|
|
- expect(screen.getByRole('heading', { name: 'Login' })).toBeInTheDocument();
|
|
|
- expect(screen.getByTestId('login-form')).toBeInTheDocument();
|
|
|
- });
|
|
|
-
|
|
|
- it('应该处理表单验证错误', async () => {
|
|
|
- const errorMock = vi.fn().mockRejectedValue(new Error('Invalid credentials'));
|
|
|
-
|
|
|
- render(
|
|
|
- <TestWrapper>
|
|
|
- <LoginForm onSubmit={errorMock} />
|
|
|
- </TestWrapper>
|
|
|
- );
|
|
|
-
|
|
|
- // 不填写必填字段直接提交
|
|
|
- await user.click(screen.getByTestId('submit-button'));
|
|
|
-
|
|
|
- // 检查表单仍然可见(没有因为错误而崩溃)
|
|
|
- expect(screen.getByTestId('login-form')).toBeInTheDocument();
|
|
|
- });
|
|
|
-
|
|
|
- it('应该支持键盘导航和辅助功能', async () => {
|
|
|
- render(
|
|
|
- <TestWrapper>
|
|
|
- <LoginForm onSubmit={loginMock} />
|
|
|
- </TestWrapper>
|
|
|
- );
|
|
|
-
|
|
|
- const emailInput = screen.getByTestId('email-input');
|
|
|
- const passwordInput = screen.getByTestId('password-input');
|
|
|
- const submitButton = screen.getByTestId('submit-button');
|
|
|
-
|
|
|
- // Tab 导航
|
|
|
- await user.tab();
|
|
|
- expect(emailInput).toHaveFocus();
|
|
|
-
|
|
|
- await user.tab();
|
|
|
- expect(passwordInput).toHaveFocus();
|
|
|
-
|
|
|
- await user.tab();
|
|
|
- expect(submitButton).toHaveFocus();
|
|
|
-
|
|
|
- // Enter 提交
|
|
|
- await user.type(emailInput, 'test@example.com');
|
|
|
- await user.type(passwordInput, 'password123');
|
|
|
- await user.keyboard('{Enter}');
|
|
|
-
|
|
|
- await waitFor(() => {
|
|
|
- expect(loginMock).toHaveBeenCalled();
|
|
|
- });
|
|
|
- });
|
|
|
-});
|