fix: unify analytics and report pagination controls

This commit is contained in:
hectorzhao
2026-09-08 14:52:12 +08:00
parent 6d3c78330d
commit 2a9d03be2e
10 changed files with 423 additions and 80 deletions
+113 -2
View File
@@ -1,4 +1,4 @@
import { render, screen, waitFor } from '@testing-library/react';
import { act, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { MemoryRouter } from 'react-router-dom';
@@ -57,7 +57,7 @@ describe('report workbench pages', () => {
items: [task('1'), task('2')],
total: 2,
page: 1,
pageSize: 10,
pageSize: 25,
});
render(
<MemoryRouter>
@@ -77,6 +77,117 @@ describe('report workbench pages', () => {
screen.getAllByRole('checkbox').forEach((checkbox) => expect(checkbox).not.toBeChecked());
});
it('changes detail page size while retaining applied filters and clearing current-page selection', async () => {
adminApi.listReportDetailsPage.mockImplementation(async ({ page, pageSize }) => ({
items: [task(`${page}-1`), task(`${page}-2`)],
total: 258,
page,
pageSize,
}));
render(
<MemoryRouter initialEntries={['/admin/report-tasks?signatureId=signature-filter&scope=pending']}>
<AdminReportTasksPage />
</MemoryRouter>,
);
const user = userEvent.setup();
await screen.findByText('签名1-1');
expect(adminApi.listReportDetailsPage).toHaveBeenLastCalledWith(
expect.objectContaining({ page: 1, pageSize: 25, signatureId: 'signature-filter', status: 'pending' }),
);
const sizeControl = screen.getByLabelText(/^每页数量/);
expect(sizeControl.closest('.ui-pagination')).not.toBeNull();
await user.click(sizeControl);
expect(screen.getAllByRole('option').map((option) => option.textContent)).toEqual([
'10 条/页',
'25 条/页',
'50 条/页',
'100 条/页',
]);
await user.click(screen.getByRole('option', { name: '25 条/页' }));
await user.type(screen.getByRole('textbox', { name: '企业/应用/通道/报备对象' }), '测试企业');
await user.click(screen.getByRole('button', { name: '查询' }));
await user.click(screen.getByRole('button', { name: '下一页' }));
await screen.findByText('签名2-1');
await user.click(screen.getByRole('button', { name: '全选当页' }));
expect(screen.getByRole('button', { name: '批量修改状态(2' })).toBeEnabled();
await user.click(sizeControl);
await user.click(screen.getByRole('option', { name: '100 条/页' }));
await screen.findByText('签名1-1');
expect(adminApi.listReportDetailsPage).toHaveBeenLastCalledWith(
expect.objectContaining({
page: 1,
pageSize: 100,
keyword: '测试企业',
signatureId: 'signature-filter',
status: 'pending',
}),
);
expect(screen.getByRole('button', { name: '批量修改状态(0' })).toBeDisabled();
expect(screen.getByRole('spinbutton', { name: '跳转页码' })).toHaveAttribute('max', '3');
screen.getAllByRole('checkbox').forEach((checkbox) => expect(checkbox).not.toBeChecked());
for (const size of [10, 50, 25]) {
await user.click(sizeControl);
await user.click(screen.getByRole('option', { name: `${size} 条/页` }));
await waitFor(() =>
expect(adminApi.listReportDetailsPage).toHaveBeenLastCalledWith(
expect.objectContaining({ page: 1, pageSize: size, keyword: '测试企业' }),
),
);
}
});
it('keeps the latest detail page size result when an earlier request finishes last', async () => {
const initialPage = { items: [task('old-25')], total: 258, page: 1, pageSize: 25 };
const latestPage = { items: [task('new-100')], total: 258, page: 1, pageSize: 100 };
let resolveInitial!: (value: typeof initialPage) => void;
let resolveLatest!: (value: typeof latestPage) => void;
adminApi.listReportDetailsPage
.mockImplementationOnce(() => new Promise((resolve) => (resolveInitial = resolve)))
.mockImplementationOnce(() => new Promise((resolve) => (resolveLatest = resolve)));
render(
<MemoryRouter>
<AdminReportTasksPage />
</MemoryRouter>,
);
const user = userEvent.setup();
await user.click(screen.getByLabelText(/^每页数量/));
await user.click(screen.getByRole('option', { name: '100 条/页' }));
await waitFor(() => expect(adminApi.listReportDetailsPage).toHaveBeenCalledTimes(2));
await act(async () => resolveLatest(latestPage));
expect(screen.getByText('签名new-100')).toBeVisible();
await act(async () => resolveInitial(initialPage));
expect(screen.queryByText('签名old-25')).not.toBeInTheDocument();
expect(screen.getByText('签名new-100')).toBeVisible();
expect(screen.getByLabelText(/^每页数量/)).toHaveTextContent('100 条/页');
});
it('keeps the current detail request failure visible without replacing it with a stale failure', async () => {
let rejectInitial!: (error: Error) => void;
let rejectLatest!: (error: Error) => void;
adminApi.listReportDetailsPage
.mockImplementationOnce(() => new Promise((_resolve, reject) => (rejectInitial = reject)))
.mockImplementationOnce(() => new Promise((_resolve, reject) => (rejectLatest = reject)));
render(
<MemoryRouter>
<AdminReportTasksPage />
</MemoryRouter>,
);
const user = userEvent.setup();
await user.click(screen.getByLabelText(/^每页数量/));
await user.click(screen.getByRole('option', { name: '100 条/页' }));
await waitFor(() => expect(adminApi.listReportDetailsPage).toHaveBeenCalledTimes(2));
await act(async () => rejectLatest(new Error('当前分页加载失败')));
expect(screen.getByText('当前分页加载失败')).toBeVisible();
await act(async () => rejectInitial(new Error('已过期的分页请求失败')));
expect(screen.getByText('当前分页加载失败')).toBeVisible();
expect(screen.queryByText('已过期的分页请求失败')).not.toBeInTheDocument();
});
it('uses the shared current-page selection button in the material pool heading', async () => {
const material = {
id: 'signature:signature-1',