159 lines
5.2 KiB
JavaScript
159 lines
5.2 KiB
JavaScript
import { act, renderHook } from "@testing-library/react";
|
|
import { afterEach, beforeEach, expect, test, vi } from "vitest";
|
|
import { useResourceListPage } from "./useResourcePages.js";
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
batchDeleteResources: vi.fn(),
|
|
can: vi.fn(),
|
|
confirm: vi.fn(),
|
|
deleteResource: vi.fn(),
|
|
reload: vi.fn(),
|
|
showToast: vi.fn(),
|
|
usePaginatedQuery: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/app/auth/AuthProvider.jsx", () => ({
|
|
useAuth: () => ({ can: mocks.can }),
|
|
}));
|
|
|
|
vi.mock("@/shared/hooks/usePaginatedQuery.js", () => ({
|
|
usePaginatedQuery: mocks.usePaginatedQuery,
|
|
}));
|
|
|
|
vi.mock("@/shared/ui/ConfirmProvider.jsx", () => ({
|
|
useConfirm: () => mocks.confirm,
|
|
}));
|
|
|
|
vi.mock("@/shared/ui/ToastProvider.jsx", () => ({
|
|
useToast: () => ({ showToast: mocks.showToast }),
|
|
}));
|
|
|
|
vi.mock("@/features/resources/api", async (importOriginal) => ({
|
|
...(await importOriginal()),
|
|
batchDeleteResources: mocks.batchDeleteResources,
|
|
deleteResource: mocks.deleteResource,
|
|
}));
|
|
|
|
beforeEach(() => {
|
|
mocks.can.mockReturnValue(true);
|
|
mocks.confirm.mockResolvedValue(true);
|
|
mocks.batchDeleteResources.mockResolvedValue([]);
|
|
mocks.deleteResource.mockResolvedValue({});
|
|
mocks.reload.mockResolvedValue(undefined);
|
|
mocks.usePaginatedQuery.mockReturnValue({
|
|
data: resourcePage(),
|
|
error: null,
|
|
loading: false,
|
|
reload: mocks.reload,
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
test("deleting a resource hides only that row without reloading the list", async () => {
|
|
const { result } = renderHook(() => useResourceListPage());
|
|
|
|
let deletedResourceIds = [];
|
|
await act(async () => {
|
|
deletedResourceIds = await result.current.deleteResources([resourceFixture({ resourceId: 11 })]);
|
|
});
|
|
|
|
expect(deletedResourceIds).toEqual([11]);
|
|
expect(mocks.deleteResource).toHaveBeenCalledWith(11);
|
|
expect(mocks.reload).not.toHaveBeenCalled();
|
|
expect(result.current.data.items.map((resource) => resource.resourceId)).toEqual([12]);
|
|
expect(mocks.showToast).toHaveBeenCalledWith("资源已删除", "success");
|
|
});
|
|
|
|
test("batch resource delete hides successful rows without reloading the list", async () => {
|
|
const { result } = renderHook(() => useResourceListPage());
|
|
|
|
let deletedResourceIds = [];
|
|
await act(async () => {
|
|
deletedResourceIds = await result.current.deleteResources([
|
|
resourceFixture({ resourceId: 11 }),
|
|
resourceFixture({ resourceId: 12 }),
|
|
]);
|
|
});
|
|
|
|
expect(deletedResourceIds).toEqual([11, 12]);
|
|
expect(mocks.batchDeleteResources).toHaveBeenCalledWith([11, 12]);
|
|
expect(mocks.deleteResource).not.toHaveBeenCalled();
|
|
expect(mocks.reload).not.toHaveBeenCalled();
|
|
expect(result.current.data.items).toEqual([]);
|
|
expect(mocks.showToast).toHaveBeenCalledWith("已删除 2 个资源", "success");
|
|
});
|
|
|
|
test("failed batch resource delete keeps all rows visible", async () => {
|
|
mocks.usePaginatedQuery.mockReturnValue({
|
|
data: {
|
|
items: [
|
|
resourceFixture({ resourceId: 11 }),
|
|
resourceFixture({ resourceId: 12 }),
|
|
resourceFixture({ resourceId: 13 }),
|
|
],
|
|
page: 1,
|
|
pageSize: 50,
|
|
total: 3,
|
|
},
|
|
error: null,
|
|
loading: false,
|
|
reload: mocks.reload,
|
|
});
|
|
mocks.batchDeleteResources.mockRejectedValueOnce(new Error("delete failed"));
|
|
const { result } = renderHook(() => useResourceListPage());
|
|
|
|
let deletedResourceIds = [];
|
|
await act(async () => {
|
|
deletedResourceIds = await result.current.deleteResources([
|
|
resourceFixture({ resourceId: 11 }),
|
|
resourceFixture({ resourceId: 12 }),
|
|
resourceFixture({ resourceId: 13 }),
|
|
]);
|
|
});
|
|
|
|
expect(deletedResourceIds).toEqual([]);
|
|
expect(mocks.batchDeleteResources).toHaveBeenCalledWith([11, 12, 13]);
|
|
expect(mocks.deleteResource).not.toHaveBeenCalled();
|
|
expect(result.current.data.items.map((resource) => resource.resourceId)).toEqual([11, 12, 13]);
|
|
expect(mocks.showToast).toHaveBeenCalledWith("delete failed", "error");
|
|
});
|
|
|
|
test("failed resource delete keeps the row visible", async () => {
|
|
mocks.deleteResource.mockRejectedValueOnce(new Error("delete failed"));
|
|
const { result } = renderHook(() => useResourceListPage());
|
|
|
|
let deletedResourceIds = [];
|
|
await act(async () => {
|
|
deletedResourceIds = await result.current.deleteResources([resourceFixture({ resourceId: 11 })]);
|
|
});
|
|
|
|
expect(deletedResourceIds).toEqual([]);
|
|
expect(mocks.reload).not.toHaveBeenCalled();
|
|
expect(result.current.data.items.map((resource) => resource.resourceId)).toEqual([11, 12]);
|
|
expect(mocks.showToast).toHaveBeenCalledWith("delete failed", "error");
|
|
});
|
|
|
|
function resourcePage() {
|
|
return {
|
|
items: [resourceFixture({ resourceId: 11, name: "Rose" }), resourceFixture({ resourceId: 12, name: "Ring" })],
|
|
page: 1,
|
|
pageSize: 50,
|
|
total: 2,
|
|
};
|
|
}
|
|
|
|
function resourceFixture(patch = {}) {
|
|
return {
|
|
coinPrice: 10,
|
|
name: "Rose",
|
|
resourceCode: "gift_rose",
|
|
resourceId: 11,
|
|
resourceType: "gift",
|
|
status: "active",
|
|
...patch,
|
|
};
|
|
}
|