diff --git a/frontend/tests/unit/LoginPage.submitting.test.tsx b/frontend/tests/unit/LoginPage.submitting.test.tsx new file mode 100644 index 0000000..0a1943c --- /dev/null +++ b/frontend/tests/unit/LoginPage.submitting.test.tsx @@ -0,0 +1,62 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +vi.mock('../../src/api/usrApi', () => ({ + fetchCompanies: vi.fn(), + login: vi.fn(), +})); + +import { fetchCompanies, login } from '../../src/api/usrApi'; +import LoginPage from '../../src/pages/usr/Login/LoginPage'; +import { renderWithProviders } from './renderLogin'; + +const mockedFetch = fetchCompanies as unknown as ReturnType; +const mockedLogin = login as unknown as ReturnType; + +// 单项版本已自动选中(spec § 6.3),只需填用户名 / 密码即构成可提交表单 +async function fillForm(user: ReturnType) { + await user.type(screen.getByPlaceholderText('请输入你的用户名'), 'admin'); + await user.type(screen.getByPlaceholderText('请输入你的密码'), 'secret'); +} + +describe('LoginPage 提交中态', () => { + beforeEach(() => { + vi.clearAllMocks(); + localStorage.clear(); + mockedFetch.mockResolvedValue([{ id: 1, sCompanyName: '甲公司', sVersion: '标准版' }]); + }); + + it('button loading and fields disabled while submitting', async () => { + mockedLogin.mockReturnValue(new Promise(() => {})); // pending + renderWithProviders(); + expect(await screen.findByText('甲公司(标准版)')).toBeInTheDocument(); + const user = userEvent.setup(); + await user.type(screen.getByPlaceholderText('请输入你的用户名'), 'admin'); + await user.type(screen.getByPlaceholderText('请输入你的密码'), 'secret'); + const submit = screen.getByRole('button', { name: /登\s*录/ }); + await user.click(submit); + // 按钮 loading + await waitFor(() => expect(submit).toHaveClass('ant-btn-loading')); + // 字段禁用 + expect(screen.getByPlaceholderText('请输入你的用户名')).toBeDisabled(); + expect(screen.getByPlaceholderText('请输入你的密码')).toBeDisabled(); + }); + + it('ignores duplicate submit while pending', async () => { + mockedLogin.mockReturnValue(new Promise(() => {})); // pending + renderWithProviders(); + expect(await screen.findByText('甲公司(标准版)')).toBeInTheDocument(); + const user = userEvent.setup(); + await fillForm(user); + const submit = screen.getByRole('button', { name: /登\s*录/ }); + await user.click(submit); + await waitFor(() => expect(mockedLogin).toHaveBeenCalledTimes(1)); + // pending 期间再次点击 + 回车 + await user.click(submit); + await user.keyboard('{Enter}'); + // 仍只调用一次 + await new Promise((r) => setTimeout(r, 50)); + expect(mockedLogin).toHaveBeenCalledTimes(1); + }); +});