Files
pleno-vue/tests/unit/paginated-list-export.spec.js
T

204 lines
5.6 KiB
JavaScript

// @vitest-environment jsdom
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import axios from "axios";
import { usePaginatedList } from "@/components/pagination/paginatedList.vue";
import * as tableExportService from "@/services/TableExcelExportService.js";
vi.mock("axios", () => ({
default: {
get: vi.fn(),
},
}));
describe("paginated list excel export flow", () => {
beforeEach(() => {
vi.clearAllMocks();
localStorage.clear();
localStorage.setItem("token", "test-token");
});
afterEach(() => {
vi.useRealTimers();
});
it("fetches all pages for export and preserves filters/search/order/query params", async () => {
axios.get
.mockResolvedValueOnce({
data: {
data: [{ id: 1 }, { id: 2 }],
meta: { pagination: { page: 1, per_page: 2, total: 5 } },
},
})
.mockResolvedValueOnce({
data: {
data: [{ id: 3 }, { id: 4 }],
meta: { pagination: { page: 2, per_page: 2, total: 5 } },
},
})
.mockResolvedValueOnce({
data: {
data: [{ id: 5 }],
meta: { pagination: { page: 3, per_page: 2, total: 5 } },
},
});
const list = usePaginatedList();
list.setEndpoint("/orders", false);
list.search("AB12345", false);
list.setFilter("department_id", 7, false);
list.setOrder("created_at", "desc");
list.setAdditionalQueryParameters({ include_archived: "1" });
list.setMetaItemsPerPage(2, false);
const rows = await list.fetchAllPagesForExport();
expect(rows.map((row) => row.id)).toEqual([1, 2, 3, 4, 5]);
expect(axios.get).toHaveBeenCalledTimes(3);
expect(axios.get).toHaveBeenNthCalledWith(
1,
expect.stringContaining("/orders"),
expect.objectContaining({
params: expect.objectContaining({
page: 1,
limit: 2,
search: "AB12345",
filters: "department_id:7",
order: "created_at:desc",
include_archived: "1",
}),
})
);
expect(axios.get).toHaveBeenNthCalledWith(
3,
expect.stringContaining("/orders"),
expect.objectContaining({
params: expect.objectContaining({
page: 3,
limit: 2,
search: "AB12345",
filters: "department_id:7",
order: "created_at:desc",
include_archived: "1",
}),
})
);
});
it("supports export transforms for client-side list shaping", async () => {
axios.get.mockResolvedValue({
data: {
data: [
{ id: 1, status: "active" },
{ id: 2, status: "archived" },
],
meta: { pagination: { page: 1, per_page: 100, total: 2 } },
},
});
const list = usePaginatedList();
list.setEndpoint("/users", false);
list.setExportTransform((rows) => rows.filter((row) => row.status === "active"));
const rows = await list.fetchAllPagesForExport();
expect(rows).toEqual([{ id: 1, status: "active" }]);
});
it("exports to excel and resets export loading state", async () => {
const exportSpy = vi.spyOn(tableExportService, "exportRowsToExcel").mockReturnValue("users.xlsx");
const list = usePaginatedList();
list.setEndpoint("/users", false);
const result = await list.exportToExcel({
rows: [{ id: 1, name: "Alice" }],
filename: "users",
sheetName: "Users",
});
expect(result).toBe(true);
expect(exportSpy).toHaveBeenCalledWith(
[{ id: 1, name: "Alice" }],
expect.objectContaining({
filename: "users",
sheetName: "Users",
})
);
expect(list.isExporting.value).toBe(false);
});
it("debounces auto-loaded search requests", async () => {
vi.useFakeTimers();
axios.get.mockResolvedValue({
data: {
data: [{ id: 1 }],
meta: { pagination: { page: 1, per_page: 100, total: 1 } },
},
});
const list = usePaginatedList();
list.setEndpoint("/subusers", false);
list.search("a");
list.search("al");
list.search("alpha");
expect(axios.get).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(249);
expect(axios.get).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(1);
expect(axios.get).toHaveBeenCalledTimes(1);
expect(axios.get).toHaveBeenCalledWith(
expect.stringContaining("/subusers"),
expect.objectContaining({
params: expect.objectContaining({
search: "alpha",
}),
})
);
});
it("aborts stale in-flight list requests when a newer load starts", async () => {
const abortSignals = [];
let requestCount = 0;
axios.get.mockImplementation((url, options = {}) => {
requestCount += 1;
abortSignals.push(options.signal);
return new Promise((resolve, reject) => {
options.signal?.addEventListener("abort", () => {
const error = new Error("canceled");
error.code = "ERR_CANCELED";
reject(error);
});
if (requestCount === 2) {
resolve({
data: {
data: [{ id: 2 }],
meta: { pagination: { page: 1, per_page: 100, total: 1 } },
},
});
}
});
});
const list = usePaginatedList();
list.setEndpoint("/subusers", false);
const firstRequest = list.loadList();
const secondRequest = list.loadList();
await Promise.all([firstRequest, secondRequest]);
expect(axios.get).toHaveBeenCalledTimes(2);
expect(abortSignals[0]?.aborted).toBe(true);
expect(list.list.value).toEqual([{ id: 2 }]);
expect(list.isLoading.value).toBe(false);
});
});