fix: unify analytics and report pagination controls
This commit is contained in:
@@ -36,4 +36,61 @@ describe('independent analytics tabs', () => {
|
||||
expect(within(unreported).getByLabelText('统计日期')).toHaveValue('2026-08-25');
|
||||
expect(api.getSignatureRetirementHeatmap).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
['签名通道发送质量', 'getSignatureQuality'],
|
||||
['未报备签名', 'getUnreportedSignatures'],
|
||||
] as const)('keeps %s page size in the footer and resets the page with applied filters', async (tab, method) => {
|
||||
api[method].mockImplementation(async (query) => ({ ...query, total: 120, items: [] }));
|
||||
render(<AdminAnalyticsPage />);
|
||||
fireEvent.click(screen.getByRole('tab', { name: tab }));
|
||||
const panel = screen.getByRole('region', { name: tab });
|
||||
await waitFor(() => expect(within(panel).getByRole('button', { name: '下一页' })).toBeEnabled());
|
||||
fireEvent.change(within(panel).getByLabelText('统计日期'), { target: { value: '2026-08-20' } });
|
||||
fireEvent.click(within(panel).getByRole('button', { name: '查询统计' }));
|
||||
await waitFor(() => expect(api[method]).toHaveBeenLastCalledWith(expect.objectContaining({ date: '2026-08-20' })));
|
||||
fireEvent.click(within(panel).getByRole('button', { name: '下一页' }));
|
||||
await waitFor(() => expect(api[method]).toHaveBeenLastCalledWith(expect.objectContaining({ page: 2 })));
|
||||
// An unsubmitted date must not alter a pagination request.
|
||||
fireEvent.change(within(panel).getByLabelText('统计日期'), { target: { value: '2026-08-21' } });
|
||||
const size = within(panel).getByLabelText(/^每页数量/);
|
||||
expect(size.closest('.ui-pagination')).not.toBeNull();
|
||||
expect(size.closest('.page-actions')).toBeNull();
|
||||
fireEvent.click(size);
|
||||
expect(screen.getAllByRole('option').map((option) => option.textContent)).toEqual([
|
||||
'10 条/页',
|
||||
'25 条/页',
|
||||
'50 条/页',
|
||||
'100 条/页',
|
||||
]);
|
||||
fireEvent.click(screen.getByRole('option', { name: '100 条/页' }));
|
||||
await waitFor(() =>
|
||||
expect(api[method]).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ page: 1, pageSize: 100, date: '2026-08-20' }),
|
||||
),
|
||||
);
|
||||
await waitFor(() => expect(within(panel).getByLabelText('跳转页码')).toHaveValue(1));
|
||||
});
|
||||
|
||||
it.each(['企业签名活跃度', '通道签名活跃度'])(
|
||||
'preserves %s footer size independently even with no rows',
|
||||
async (tab) => {
|
||||
render(<AdminAnalyticsPage />);
|
||||
fireEvent.click(screen.getByRole('tab', { name: tab }));
|
||||
await waitFor(() => expect(api.getSignatureRetirementHeatmap).toHaveBeenCalledTimes(1));
|
||||
const panel = screen.getByRole('region', { name: tab });
|
||||
const size = within(panel).getByLabelText(/^每页数量/);
|
||||
expect(size.closest('.ui-pagination')).not.toBeNull();
|
||||
expect(within(panel).getByRole('button', { name: '下一页' })).toBeDisabled();
|
||||
fireEvent.click(size);
|
||||
fireEvent.click(screen.getByRole('option', { name: '50 条/页' }));
|
||||
fireEvent.click(screen.getByRole('tab', { name: '签名通道发送质量' }));
|
||||
expect(
|
||||
within(screen.getByRole('region', { name: '签名通道发送质量' })).getByLabelText(/^每页数量/),
|
||||
).toHaveTextContent('25 条/页');
|
||||
fireEvent.click(screen.getByRole('tab', { name: tab }));
|
||||
expect(size).toHaveTextContent('50 条/页');
|
||||
expect(api.getSignatureRetirementHeatmap).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -10,18 +10,7 @@ import {
|
||||
type UnreportedSignatureItem,
|
||||
type PagedResult,
|
||||
} from '@/api/adminApi';
|
||||
import {
|
||||
Breadcrumb,
|
||||
Button,
|
||||
CarrierTag,
|
||||
Input,
|
||||
Pagination,
|
||||
Select,
|
||||
Tabs,
|
||||
Table,
|
||||
Tag,
|
||||
type TableColumn,
|
||||
} from '@/components/ui';
|
||||
import { Breadcrumb, Button, CarrierTag, Input, Pagination, Tabs, Table, Tag, type TableColumn } from '@/components/ui';
|
||||
import './AdminAnalyticsPage.css';
|
||||
import { successRateClassName, successRateTone } from '@/utils/successRate';
|
||||
|
||||
@@ -242,6 +231,12 @@ function AnalyticsPanel({ kind }: { kind: string }) {
|
||||
void loadData(page, appliedKeyword, keyword, appliedDate);
|
||||
}
|
||||
|
||||
function changePageSize(size: number) {
|
||||
setPageSize(size);
|
||||
if (kind === 'quality' || kind === 'unreported')
|
||||
void loadData(1, appliedKeyword, appliedUnreportedKeyword, appliedDate, size);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="page-stack" aria-label={analyticsTabs.find((tab) => tab.value === kind)?.label}>
|
||||
<div className="page-heading">
|
||||
@@ -249,17 +244,6 @@ function AnalyticsPanel({ kind }: { kind: string }) {
|
||||
<p className="muted">日期、筛选和分页在当前页签内独立生效。</p>
|
||||
</div>
|
||||
<div className="page-actions">
|
||||
<Select
|
||||
label="每页数量"
|
||||
value={String(pageSize)}
|
||||
options={[10, 25, 50, 100].map((value) => ({ value: String(value), label: `${value} 条/页` }))}
|
||||
onChange={(event) => {
|
||||
const size = Number(event.target.value);
|
||||
setPageSize(size);
|
||||
if (kind === 'quality' || kind === 'unreported')
|
||||
void loadData(1, appliedKeyword, appliedUnreportedKeyword, appliedDate, size);
|
||||
}}
|
||||
/>
|
||||
<Input
|
||||
aria-label="统计日期"
|
||||
max={shanghaiDateKey()}
|
||||
@@ -312,27 +296,31 @@ function AnalyticsPanel({ kind }: { kind: string }) {
|
||||
pagination={false}
|
||||
rowKey="signatureId"
|
||||
/>
|
||||
{(signatureQuality?.total ?? 0) > 0 ? (
|
||||
<Pagination
|
||||
nextDisabled={
|
||||
(signatureQuality?.page ?? 1) >=
|
||||
Math.ceil((signatureQuality?.total ?? 0) / (signatureQuality?.pageSize ?? 10))
|
||||
}
|
||||
onNext={() => changeSignaturePage((signatureQuality?.page ?? 1) + 1)}
|
||||
onPageChange={changeSignaturePage}
|
||||
onPrevious={() => changeSignaturePage((signatureQuality?.page ?? 1) - 1)}
|
||||
page={signatureQuality?.page ?? 1}
|
||||
previousDisabled={(signatureQuality?.page ?? 1) <= 1}
|
||||
total={signatureQuality?.total ?? 0}
|
||||
totalPages={Math.ceil((signatureQuality?.total ?? 0) / (signatureQuality?.pageSize ?? 10))}
|
||||
/>
|
||||
) : null}
|
||||
<Pagination
|
||||
pageSize={pageSize}
|
||||
onPageSizeChange={changePageSize}
|
||||
nextDisabled={
|
||||
(signatureQuality?.page ?? 1) >=
|
||||
Math.max(1, Math.ceil((signatureQuality?.total ?? 0) / (signatureQuality?.pageSize ?? pageSize)))
|
||||
}
|
||||
onNext={() => changeSignaturePage((signatureQuality?.page ?? 1) + 1)}
|
||||
onPageChange={changeSignaturePage}
|
||||
onPrevious={() => changeSignaturePage((signatureQuality?.page ?? 1) - 1)}
|
||||
page={signatureQuality?.page ?? 1}
|
||||
previousDisabled={(signatureQuality?.page ?? 1) <= 1}
|
||||
total={signatureQuality?.total ?? 0}
|
||||
totalPages={Math.max(
|
||||
1,
|
||||
Math.ceil((signatureQuality?.total ?? 0) / (signatureQuality?.pageSize ?? pageSize)),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{kind === 'enterprise' ? (
|
||||
<RetirementHeatmap
|
||||
pageSize={pageSize}
|
||||
onPageSizeChange={changePageSize}
|
||||
date={appliedDate}
|
||||
dimensionType="enterprise"
|
||||
dimensions={retirementDimensions}
|
||||
@@ -343,6 +331,7 @@ function AnalyticsPanel({ kind }: { kind: string }) {
|
||||
{kind === 'channel' ? (
|
||||
<RetirementHeatmap
|
||||
pageSize={pageSize}
|
||||
onPageSizeChange={changePageSize}
|
||||
date={appliedDate}
|
||||
dimensionType="channel"
|
||||
dimensions={retirementDimensions}
|
||||
@@ -353,6 +342,8 @@ function AnalyticsPanel({ kind }: { kind: string }) {
|
||||
|
||||
{kind === 'unreported' ? (
|
||||
<UnreportedSignaturesCard
|
||||
pageSize={pageSize}
|
||||
onPageSizeChange={changePageSize}
|
||||
data={unreportedSignatures}
|
||||
keyword={unreportedKeyword}
|
||||
loading={loading}
|
||||
@@ -375,6 +366,7 @@ function AnalyticsPanel({ kind }: { kind: string }) {
|
||||
|
||||
function RetirementHeatmap({
|
||||
pageSize,
|
||||
onPageSizeChange,
|
||||
date,
|
||||
dimensionType,
|
||||
dimensions,
|
||||
@@ -382,6 +374,7 @@ function RetirementHeatmap({
|
||||
title,
|
||||
}: {
|
||||
pageSize: number;
|
||||
onPageSizeChange: (pageSize: number) => void;
|
||||
date: string;
|
||||
dimensionType: 'enterprise' | 'channel';
|
||||
dimensions: SignatureRetirementHeatmapDimension[];
|
||||
@@ -504,16 +497,6 @@ function RetirementHeatmap({
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<Pagination
|
||||
nextDisabled={currentPage >= totalPages}
|
||||
onNext={() => setPage(currentPage + 1)}
|
||||
onPageChange={setPage}
|
||||
onPrevious={() => setPage(currentPage - 1)}
|
||||
page={currentPage}
|
||||
previousDisabled={currentPage <= 1}
|
||||
total={rows.length}
|
||||
totalPages={totalPages}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<p className="empty-state">
|
||||
@@ -522,11 +505,25 @@ function RetirementHeatmap({
|
||||
: '暂无已确认到运营商的报备事实,尚未形成检测热力图。'}
|
||||
</p>
|
||||
)}
|
||||
<Pagination
|
||||
pageSize={pageSize}
|
||||
onPageSizeChange={onPageSizeChange}
|
||||
nextDisabled={currentPage >= totalPages}
|
||||
onNext={() => setPage(currentPage + 1)}
|
||||
onPageChange={setPage}
|
||||
onPrevious={() => setPage(currentPage - 1)}
|
||||
page={currentPage}
|
||||
previousDisabled={currentPage <= 1}
|
||||
total={rows.length}
|
||||
totalPages={totalPages}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function UnreportedSignaturesCard({
|
||||
pageSize,
|
||||
onPageSizeChange,
|
||||
data,
|
||||
keyword,
|
||||
loading,
|
||||
@@ -534,6 +531,8 @@ function UnreportedSignaturesCard({
|
||||
onPageChange,
|
||||
onSearch,
|
||||
}: {
|
||||
pageSize: number;
|
||||
onPageSizeChange: (pageSize: number) => void;
|
||||
data: (PagedResult<UnreportedSignatureItem> & { date: string }) | null;
|
||||
keyword: string;
|
||||
loading: boolean;
|
||||
@@ -553,7 +552,7 @@ function UnreportedSignaturesCard({
|
||||
render: (record) => `${record.messageCount.toLocaleString('zh-CN')} 条`,
|
||||
},
|
||||
];
|
||||
const totalPages = Math.max(1, Math.ceil((data?.total ?? 0) / (data?.pageSize ?? 10)));
|
||||
const totalPages = Math.max(1, Math.ceil((data?.total ?? 0) / (data?.pageSize ?? pageSize)));
|
||||
return (
|
||||
<div className="surface signature-quality-card">
|
||||
<div className="signature-quality-card__heading">
|
||||
@@ -589,18 +588,18 @@ function UnreportedSignaturesCard({
|
||||
pagination={false}
|
||||
rowKey={(record) => `${record.signatureId}:${record.applicationId ?? ''}`}
|
||||
/>
|
||||
{(data?.total ?? 0) > 0 ? (
|
||||
<Pagination
|
||||
nextDisabled={(data?.page ?? 1) >= totalPages}
|
||||
onNext={() => onPageChange((data?.page ?? 1) + 1)}
|
||||
onPageChange={onPageChange}
|
||||
onPrevious={() => onPageChange((data?.page ?? 1) - 1)}
|
||||
page={data?.page ?? 1}
|
||||
previousDisabled={(data?.page ?? 1) <= 1}
|
||||
total={data?.total ?? 0}
|
||||
totalPages={totalPages}
|
||||
/>
|
||||
) : null}
|
||||
<Pagination
|
||||
pageSize={pageSize}
|
||||
onPageSizeChange={onPageSizeChange}
|
||||
nextDisabled={(data?.page ?? 1) >= totalPages}
|
||||
onNext={() => onPageChange((data?.page ?? 1) + 1)}
|
||||
onPageChange={onPageChange}
|
||||
onPrevious={() => onPageChange((data?.page ?? 1) - 1)}
|
||||
page={data?.page ?? 1}
|
||||
previousDisabled={(data?.page ?? 1) <= 1}
|
||||
total={data?.total ?? 0}
|
||||
totalPages={totalPages}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Download, Eye, Search } from 'lucide-react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { adminApi, fileDownloadUrl, type ReportTask, type SingleReportMaterialDetail } from '@/api/adminApi';
|
||||
@@ -192,6 +192,8 @@ export function AdminReportTasksPage() {
|
||||
const [exportTask, setExportTask] = useState<ReportTask | null>(null);
|
||||
const [exportBusy, setExportBusy] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(25);
|
||||
const requestId = useRef(0);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [appliedFilters, setAppliedFilters] = useState({
|
||||
keyword: '',
|
||||
@@ -200,9 +202,8 @@ export function AdminReportTasksPage() {
|
||||
status: initialStatus,
|
||||
carrier: 'all',
|
||||
});
|
||||
const pageSize = 10;
|
||||
|
||||
function loadData(targetPage = page, filters = appliedFilters) {
|
||||
const id = ++requestId.current;
|
||||
adminApi
|
||||
.listReportDetailsPage({
|
||||
signatureId: searchParams.get('signatureId') || undefined,
|
||||
@@ -216,17 +217,23 @@ export function AdminReportTasksPage() {
|
||||
pageSize,
|
||||
})
|
||||
.then((result) => {
|
||||
if (id !== requestId.current) return;
|
||||
setTasks(result.items);
|
||||
setTotal(result.total);
|
||||
setSelected(new Set());
|
||||
setError('');
|
||||
})
|
||||
.catch((failure: Error) => setError(failure.message || '报备明细加载失败'));
|
||||
.catch((failure: Error) => {
|
||||
if (id === requestId.current) setError(failure.message || '报备明细加载失败');
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadData(page);
|
||||
}, [page]);
|
||||
return () => {
|
||||
requestId.current += 1;
|
||||
};
|
||||
}, [page, pageSize]);
|
||||
|
||||
async function saveTaskStatus() {
|
||||
if (!statusTask) return;
|
||||
@@ -521,8 +528,14 @@ export function AdminReportTasksPage() {
|
||||
nextDisabled={page * pageSize >= total}
|
||||
onNext={() => setPage((current) => current + 1)}
|
||||
onPageChange={setPage}
|
||||
onPageSizeChange={(size) => {
|
||||
setPageSize(size);
|
||||
setPage(1);
|
||||
setSelected(new Set());
|
||||
}}
|
||||
onPrevious={() => setPage((current) => Math.max(1, current - 1))}
|
||||
page={page}
|
||||
pageSize={pageSize}
|
||||
previousDisabled={page <= 1}
|
||||
total={total}
|
||||
totalPages={Math.max(1, Math.ceil(total / pageSize))}
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user