import { describe, it, expect, vi } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { MemoryRouter, Routes, Route } from 'react-router-dom'; import { Provider } from 'react-redux'; import { configureStore } from '@reduxjs/toolkit'; import { ConfigProvider } from 'antd'; import authReducer from '../../store/slices/authSlice'; import LoginPage from './LoginPage'; function makeStore() { return configureStore({ reducer: { auth: authReducer } }); } function renderLogin() { const store = makeStore(); return { store, ...render( } /> USERS} /> , ), }; } async function fillAndSubmit(username: string, password: string, companyCode: string = 'HQ') { const user = userEvent.setup(); const inputs = screen.getAllByRole('textbox'); // username + password 在 antd Input.Password 渲染下不是 textbox // 直接通过 placeholder 找 await user.clear(screen.getByPlaceholderText('请输入你的用户名')); await user.type(screen.getByPlaceholderText('请输入你的用户名'), username); await user.clear(screen.getByPlaceholderText('请输入你的密码')); await user.type(screen.getByPlaceholderText('请输入你的密码'), password); // companyCode 默认已选 HQ,若需改动通过 AntD Select 较复杂,本测试用默认值 if (companyCode !== 'HQ') { // 跳过非默认场景的 UI 选择(依赖原生 Select 行为太复杂) } await user.click(screen.getByTestId('login-submit')); } describe('LoginPage', () => { it('renders login page with form', () => { renderLogin(); expect(screen.getByText('用户登录')).toBeInTheDocument(); expect(screen.getByTestId('login-form')).toBeInTheDocument(); expect(screen.getByTestId('login-submit')).toBeInTheDocument(); }); it('success flow: dispatches setSession and navigates to /users', async () => { const { store } = renderLogin(); await fillAndSubmit('alice', 'Password1!'); await waitFor(() => expect(screen.queryByTestId('users-page')).toBeInTheDocument(), { timeout: 3000, }); expect(store.getState().auth.accessToken).toBe('fake-jwt'); expect(store.getState().auth.userInfo?.username).toBe('alice'); }); it('bad credentials: shows 40101 error message', async () => { renderLogin(); await fillAndSubmit('alice', 'WRONG'); await waitFor(() => expect(screen.getByText('用户名或密码错误')).toBeInTheDocument(), ); }); it('locked account: shows 42301 with lockUntil time', async () => { renderLogin(); await fillAndSubmit('locked', 'X'); await waitFor(() => { const alert = screen.getByTestId('login-error-alert'); expect(alert.textContent).toMatch(/账号已锁定/); expect(alert.textContent).toMatch(/12:00/); }); }); it('deleted account: shows 40103 message', async () => { renderLogin(); await fillAndSubmit('deleted', 'X'); await waitFor(() => expect(screen.getByText('账号已被作废,禁止登录')).toBeInTheDocument(), ); }); });