168 lines
4.9 KiB
TypeScript
168 lines
4.9 KiB
TypeScript
/**
|
|
* useSuggestionsData Hook 单元测试
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import { renderHook, waitFor } from "@testing-library/react";
|
|
import { SWRConfig } from "swr";
|
|
import type { ReactNode } from "react";
|
|
|
|
vi.mock("@/lib/api/client", () => ({
|
|
fetchWithAuth: vi.fn(),
|
|
}));
|
|
|
|
import { fetchWithAuth } from "@/lib/api/client";
|
|
const mockFetchWithAuth = vi.mocked(fetchWithAuth);
|
|
|
|
vi.mock("next-auth/react", () => ({
|
|
useSession: vi.fn(() => ({
|
|
data: { accessToken: "test-token" },
|
|
status: "authenticated",
|
|
})),
|
|
}));
|
|
|
|
import { useSuggestionsData } from "@/lib/hooks/use-suggestions-data";
|
|
import type { SuggestionListResponse } from "@/types/suggestion";
|
|
|
|
const noRetryOptions = { shouldRetryOnError: false };
|
|
|
|
function createWrapper() {
|
|
return ({ children }: { children: ReactNode }) =>
|
|
SWRConfig({ value: { provider: () => new Map() }, children });
|
|
}
|
|
|
|
describe("useSuggestionsData", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
const mockSuggestionsResponse: SuggestionListResponse = {
|
|
suggestions: [
|
|
{
|
|
id: "sug-1",
|
|
brand_id: "brand-1",
|
|
type: "content_optimization",
|
|
priority: "high",
|
|
title: "优化内容",
|
|
description: "建议优化内容",
|
|
action: null,
|
|
expected_impact: null,
|
|
difficulty: "easy",
|
|
status: "pending",
|
|
generated_at: "2024-01-01",
|
|
updated_at: "2024-01-01",
|
|
batch_id: "batch-1",
|
|
source: "rule",
|
|
},
|
|
],
|
|
total: 1,
|
|
};
|
|
|
|
it("brandId 为空时应暂停建议请求", () => {
|
|
mockFetchWithAuth.mockResolvedValue({});
|
|
|
|
const { result } = renderHook(() => useSuggestionsData(), { wrapper: createWrapper() });
|
|
|
|
expect(result.current.suggestions).toBeUndefined();
|
|
});
|
|
|
|
it("brandId 存在时应获取建议列表", async () => {
|
|
mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse);
|
|
|
|
const { result } = renderHook(
|
|
() => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }),
|
|
{ wrapper: createWrapper() }
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.suggestions).toEqual(mockSuggestionsResponse.suggestions);
|
|
});
|
|
});
|
|
|
|
it("应支持筛选参数", async () => {
|
|
mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse);
|
|
|
|
const { result } = renderHook(
|
|
() => useSuggestionsData({
|
|
brandId: "brand-1",
|
|
filters: { type: "content_optimization", priority: "high", status: "pending" },
|
|
swrOptions: noRetryOptions,
|
|
}),
|
|
{ wrapper: createWrapper() }
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.suggestions).toEqual(mockSuggestionsResponse.suggestions);
|
|
});
|
|
|
|
const suggestionCall = mockFetchWithAuth.mock.calls.find(
|
|
(call) => typeof call[0] === "string" && call[0].includes("/suggestions")
|
|
);
|
|
expect(suggestionCall).toBeDefined();
|
|
const calledUrl = suggestionCall![0] as string;
|
|
expect(calledUrl).toContain("type=content_optimization");
|
|
expect(calledUrl).toContain("priority=high");
|
|
expect(calledUrl).toContain("status=pending");
|
|
});
|
|
|
|
it("应正确暴露 isLoading 状态", async () => {
|
|
mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse);
|
|
|
|
const { result } = renderHook(
|
|
() => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }),
|
|
{ wrapper: createWrapper() }
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.isLoading).toBe(false);
|
|
});
|
|
});
|
|
|
|
it("应正确暴露 error 状态", async () => {
|
|
mockFetchWithAuth.mockRejectedValue(new Error("加载建议失败"));
|
|
|
|
const { result } = renderHook(
|
|
() => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }),
|
|
{ wrapper: createWrapper() }
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.error).toBeDefined();
|
|
expect(result.current.error?.message).toBe("加载建议失败");
|
|
});
|
|
});
|
|
|
|
it("应提供 regenerate 方法", () => {
|
|
mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse);
|
|
|
|
const { result } = renderHook(
|
|
() => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }),
|
|
{ wrapper: createWrapper() }
|
|
);
|
|
|
|
expect(typeof result.current.regenerate).toBe("function");
|
|
});
|
|
|
|
it("应提供 updateStatus 方法", () => {
|
|
mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse);
|
|
|
|
const { result } = renderHook(
|
|
() => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }),
|
|
{ wrapper: createWrapper() }
|
|
);
|
|
|
|
expect(typeof result.current.updateStatus).toBe("function");
|
|
});
|
|
|
|
it("应提供 refresh 方法", () => {
|
|
mockFetchWithAuth.mockResolvedValue(mockSuggestionsResponse);
|
|
|
|
const { result } = renderHook(
|
|
() => useSuggestionsData({ brandId: "brand-1", swrOptions: noRetryOptions }),
|
|
{ wrapper: createWrapper() }
|
|
);
|
|
|
|
expect(typeof result.current.refresh).toBe("function");
|
|
});
|
|
});
|