Files
EasyFlow/easyflow-ui-admin/app/src/components/page/PageData.test.ts
陈子默 155af9989c feat: 优化定时任务管理与日志体验
- 增加工作流选项、时间范围筛选和日志详情展示

- 支持可配置自动刷新、轻量局部更新与响应式布局
2026-08-31 14:57:20 +08:00

309 lines
8.6 KiB
TypeScript

import { flushPromises, mount } from '@vue/test-utils';
import { describe, expect, it, vi } from 'vitest';
import PageData from './PageData.vue';
describe('page data recovery', () => {
it('loads the restored page and query without requesting the first page', async () => {
const get = vi
.fn()
.mockResolvedValue({ data: { records: [], totalRow: 50 } });
const wrapper = mount(PageData, {
global: {
directives: { loading: {} },
},
props: {
initialPageNumber: 3,
initialQueryParams: { categoryId: '7', title: '月报' },
pageSize: 18,
pageUrl: '/page',
requestClient: { get },
},
});
await flushPromises();
expect(get).toHaveBeenCalledTimes(1);
expect(get).toHaveBeenCalledWith('/page', {
params: {
categoryId: '7',
pageNumber: 3,
pageSize: 18,
title: '月报',
},
});
expect((wrapper.vm as any).getPageState()).toEqual({
pageNumber: 3,
pageSize: 18,
});
});
it('shows a retry action after a page request fails', async () => {
const get = vi
.fn()
.mockRejectedValueOnce(new Error('network unavailable'))
.mockResolvedValueOnce({ data: { records: [], totalRow: 0 } });
const wrapper = mount(PageData, {
global: {
directives: { loading: {} },
},
props: { pageUrl: '/page', requestClient: { get } },
});
await flushPromises();
expect(wrapper.text()).toContain('数据加载失败,请重试');
await wrapper.get('button').trigger('click');
await flushPromises();
expect(get).toHaveBeenCalledTimes(2);
expect(wrapper.text()).not.toContain('数据加载失败,请重试');
});
it('contracts an out-of-range page to the latest page with one retry', async () => {
const get = vi
.fn()
.mockResolvedValueOnce({ data: { records: [], totalRow: 21 } })
.mockResolvedValueOnce({
data: { records: [{ id: 'last-record' }], totalRow: 21 },
});
const wrapper = mount(PageData, {
global: {
directives: { loading: {} },
},
props: {
initialPageNumber: 4,
pageSize: 10,
pageUrl: '/page',
requestClient: { get },
},
});
await flushPromises();
expect(get).toHaveBeenCalledTimes(2);
expect(get.mock.calls.map((call) => call[1].params.pageNumber)).toEqual([
4, 3,
]);
expect((wrapper.vm as any).getPageState()).toEqual({
pageNumber: 3,
pageSize: 10,
});
});
it('coalesces repeated reloads while a page request is still running', async () => {
let resolveInitialRequest: (value: {
data: { records: never[]; totalRow: number };
}) => void = () => {};
const initialRequest = new Promise<{
data: { records: never[]; totalRow: number };
}>((resolve) => {
resolveInitialRequest = resolve;
});
const get = vi
.fn()
.mockReturnValueOnce(initialRequest)
.mockResolvedValue({ data: { records: [], totalRow: 0 } });
const wrapper = mount(PageData, {
global: {
directives: { loading: {} },
},
props: { pageUrl: '/page', requestClient: { get } },
});
const firstReload = (wrapper.vm as any).reload({ silent: true });
const secondReload = (wrapper.vm as any).reload({ silent: true });
expect(get).toHaveBeenCalledTimes(1);
resolveInitialRequest({ data: { records: [], totalRow: 0 } });
await Promise.all([firstReload, secondReload]);
await flushPromises();
expect(get).toHaveBeenCalledTimes(2);
});
it('does not let a lightweight refresh supersede an active page query', async () => {
let resolveInitialRequest: (value: {
data: { records: { id: string }[]; totalRow: number };
}) => void = () => {};
const initialRequest = new Promise<{
data: { records: { id: string }[]; totalRow: number };
}>((resolve) => {
resolveInitialRequest = resolve;
});
const get = vi.fn().mockReturnValueOnce(initialRequest);
const wrapper = mount(PageData, {
global: {
directives: { loading: {} },
},
props: {
pageUrl: '/page',
refreshUrl: '/refresh',
requestClient: { get },
},
});
const refreshRequest = (wrapper.vm as any).reload({
lightweight: true,
silent: true,
});
expect(get).toHaveBeenCalledTimes(1);
resolveInitialRequest({
data: { records: [{ id: 'counted-result' }], totalRow: 21 },
});
await refreshRequest;
await flushPromises();
expect(get).toHaveBeenCalledTimes(1);
expect(wrapper.emitted('loadSuccess')?.at(-1)?.[0]).toMatchObject({
lightweight: false,
recordCount: 1,
});
expect(wrapper.find('.el-pagination').exists()).toBe(true);
});
it('uses the lightweight refresh endpoint without replacing the total', async () => {
const get = vi
.fn()
.mockResolvedValueOnce({
data: { records: [{ id: 'initial' }], totalRow: 35 },
})
.mockResolvedValueOnce({ data: [{ id: 'latest' }] });
const wrapper = mount(PageData, {
global: {
directives: { loading: {} },
},
props: {
pageUrl: '/page',
refreshUrl: '/refresh',
requestClient: { get },
},
});
await flushPromises();
await (wrapper.vm as any).reload({ lightweight: true, silent: true });
await flushPromises();
expect(get).toHaveBeenLastCalledWith('/refresh', {
params: { pageNumber: 1, pageSize: 10 },
});
expect(wrapper.find('.el-pagination').exists()).toBe(true);
expect(wrapper.emitted('loadSuccess')?.at(-1)?.[0]).toMatchObject({
lightweight: true,
pageNumber: 1,
recordCount: 1,
});
});
it('falls back to the counted page endpoint outside the first page', async () => {
const get = vi
.fn()
.mockResolvedValue({ data: { records: [], totalRow: 30 } });
const wrapper = mount(PageData, {
global: {
directives: { loading: {} },
},
props: {
initialPageNumber: 2,
pageUrl: '/page',
refreshUrl: '/refresh',
requestClient: { get },
},
});
await flushPromises();
await (wrapper.vm as any).reload({ lightweight: true, silent: true });
await flushPromises();
expect(get).toHaveBeenLastCalledWith('/page', {
params: { pageNumber: 2, pageSize: 10 },
});
expect(wrapper.emitted('loadSuccess')?.at(-1)?.[0]).toMatchObject({
lightweight: false,
pageNumber: 2,
});
});
it('restores a mounted list to a new route state with one target request', async () => {
const get = vi
.fn()
.mockResolvedValue({ data: { records: [], totalRow: 100 } });
const wrapper = mount(PageData, {
global: {
directives: { loading: {} },
},
props: { pageUrl: '/page', requestClient: { get } },
});
await flushPromises();
expect(
(wrapper.vm as any).restoreState({
pageNumber: 4,
pageSize: 20,
queryParams: { title: '月报' },
}),
).toBe(true);
await flushPromises();
expect(get).toHaveBeenCalledTimes(2);
expect(get).toHaveBeenLastCalledWith('/page', {
params: { pageNumber: 4, pageSize: 20, title: '月报' },
});
});
it('does not request again when the restored state is unchanged', async () => {
const get = vi
.fn()
.mockResolvedValue({ data: { records: [], totalRow: 50 } });
const wrapper = mount(PageData, {
global: {
directives: { loading: {} },
},
props: {
initialPageNumber: 3,
initialQueryParams: { categoryId: undefined, title: '月报' },
pageSize: 20,
pageUrl: '/page',
requestClient: { get },
},
});
await flushPromises();
expect(
(wrapper.vm as any).restoreState({
pageNumber: 3,
pageSize: 20,
queryParams: { title: '月报' },
}),
).toBe(false);
await flushPromises();
expect(get).toHaveBeenCalledTimes(1);
});
it('detaches query state from the caller reactive object', async () => {
const get = vi
.fn()
.mockResolvedValue({ data: { records: [], totalRow: 0 } });
const wrapper = mount(PageData, {
global: {
directives: { loading: {} },
},
props: { pageUrl: '/page', requestClient: { get } },
});
await flushPromises();
const query = { title: '月报' };
(wrapper.vm as any).setQuery(query);
await flushPromises();
query.title = '未提交草稿';
await flushPromises();
expect(get).toHaveBeenCalledTimes(2);
expect(get).toHaveBeenLastCalledWith('/page', {
params: { pageNumber: 1, pageSize: 10, title: '月报' },
});
});
});