744 lines
24 KiB
TypeScript
744 lines
24 KiB
TypeScript
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import { act, cleanup, render, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import type { ReactNode } from "react";
|
|
import { MemoryRouter } from "react-router-dom";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("./lib/api", () => ({
|
|
cancelJdQrLogin: vi.fn(),
|
|
cancelTmallQrLogin: vi.fn(),
|
|
clearJdManagedSession: vi.fn(),
|
|
clearJdSessionManagerConfig: vi.fn(),
|
|
clearPlatformSession: vi.fn(),
|
|
clearTmallManagedSession: vi.fn(),
|
|
clearTmallSessionManagerConfig: vi.fn(),
|
|
confirmTask: vi.fn(),
|
|
createTask: vi.fn(),
|
|
createTaskEventsSource: vi.fn(() => ({
|
|
addEventListener: vi.fn(),
|
|
close: vi.fn(),
|
|
removeEventListener: vi.fn()
|
|
})),
|
|
deleteTask: vi.fn(),
|
|
getJdKeywordPreview: vi.fn(),
|
|
getJdLiveSession: vi.fn(),
|
|
getJdQrLoginState: vi.fn(),
|
|
getJdSessionManager: vi.fn(),
|
|
getHistoryTasks: vi.fn(),
|
|
getPlatformReadiness: vi.fn(),
|
|
getPlatformSession: vi.fn(),
|
|
getTask: vi.fn(),
|
|
getTaskCandidates: vi.fn(),
|
|
getTaskReport: vi.fn(),
|
|
getTmallLiveSession: vi.fn(),
|
|
getTmallQrLoginState: vi.fn(),
|
|
getTmallSessionManager: vi.fn(),
|
|
importJdManagedSession: vi.fn(),
|
|
importTmallManagedSession: vi.fn(),
|
|
preparePlatform: vi.fn(),
|
|
resumeJdQrLoginManualRecovery: vi.fn(),
|
|
runJdSessionManagerHealthCheck: vi.fn(),
|
|
runJdSessionManagerRecovery: vi.fn(),
|
|
runTmallSessionManagerHealthCheck: vi.fn(),
|
|
retryTaskPlatform: vi.fn(),
|
|
startJdQrLogin: vi.fn(),
|
|
startTmallQrLogin: vi.fn(),
|
|
updateJdSessionManagerConfig: vi.fn(),
|
|
updateTmallSessionManagerConfig: vi.fn()
|
|
}));
|
|
|
|
import { App, NewTaskPage } from "./App";
|
|
import {
|
|
cancelJdQrLogin,
|
|
cancelTmallQrLogin,
|
|
clearJdManagedSession,
|
|
clearPlatformSession,
|
|
clearTmallManagedSession,
|
|
getJdKeywordPreview,
|
|
getJdLiveSession,
|
|
getJdQrLoginState,
|
|
getJdSessionManager,
|
|
getHistoryTasks,
|
|
getPlatformReadiness,
|
|
getPlatformSession,
|
|
getTask,
|
|
getTaskCandidates,
|
|
getTmallLiveSession,
|
|
getTmallQrLoginState,
|
|
getTmallSessionManager,
|
|
importJdManagedSession,
|
|
importTmallManagedSession,
|
|
preparePlatform,
|
|
createTaskEventsSource,
|
|
runJdSessionManagerHealthCheck,
|
|
runJdSessionManagerRecovery,
|
|
runTmallSessionManagerHealthCheck,
|
|
startJdQrLogin,
|
|
startTmallQrLogin,
|
|
updateJdSessionManagerConfig
|
|
} from "./lib/api";
|
|
|
|
function renderWithProviders(node: ReactNode, initialEntries?: string[]) {
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
retry: false
|
|
}
|
|
}
|
|
});
|
|
|
|
return render(
|
|
<QueryClientProvider client={queryClient}>
|
|
{initialEntries ? (
|
|
<MemoryRouter initialEntries={initialEntries}>{node}</MemoryRouter>
|
|
) : (
|
|
<MemoryRouter>{node}</MemoryRouter>
|
|
)}
|
|
</QueryClientProvider>
|
|
);
|
|
}
|
|
|
|
describe("task composer and session console", () => {
|
|
beforeEach(() => {
|
|
const jdManagerState = {
|
|
status: "healthy",
|
|
enabled: true,
|
|
autoLoginMode: "command",
|
|
commandConfigured: true,
|
|
accountConfigured: true,
|
|
passwordConfigured: false,
|
|
accountLabel: "jd***86",
|
|
browserProfilePath: "D:\\ops\\jd-profile",
|
|
heartbeatQuery: "iPhone 15",
|
|
checkIntervalMs: 600000,
|
|
runnerTimeoutMs: 300000,
|
|
pendingManualAction: false,
|
|
note: "京东会话当前可用。",
|
|
publicNote: "京东会话由运维后台维护,当前可用。",
|
|
configuredAt: "2026-04-02T08:00:00.000Z",
|
|
lastCheckAt: "2026-04-02T10:00:00.000Z",
|
|
lastRecoveredAt: "2026-04-02T09:00:00.000Z",
|
|
session: {
|
|
configured: true,
|
|
importedAt: "2026-04-02T10:00:00.000Z",
|
|
hasCookie: true,
|
|
userAgent: "stub-user-agent",
|
|
searchApiTemplate: {
|
|
available: true
|
|
},
|
|
detailTemplate: {
|
|
available: true,
|
|
skuId: "100068388533"
|
|
},
|
|
reviewsTemplate: {
|
|
available: true,
|
|
skuId: "100068388533"
|
|
}
|
|
}
|
|
};
|
|
const tmallManagerState = {
|
|
status: "healthy",
|
|
enabled: true,
|
|
heartbeatItemId: "934454505228",
|
|
checkIntervalMs: 600000,
|
|
pendingManualAction: false,
|
|
note: "天猫会话当前可用。",
|
|
publicNote: "天猫会话由运维后台维护,当前可用。",
|
|
configuredAt: "2026-04-02T08:00:00.000Z",
|
|
lastCheckAt: "2026-04-02T10:00:00.000Z",
|
|
lastHealthyAt: "2026-04-02T10:00:00.000Z",
|
|
session: {
|
|
configured: true,
|
|
importedAt: "2026-04-02T10:00:00.000Z",
|
|
hasCookie: true,
|
|
userAgent: "stub-user-agent",
|
|
detailTemplate: {
|
|
available: true,
|
|
itemId: "934454505228"
|
|
},
|
|
reviewsTemplate: {
|
|
available: true,
|
|
itemId: "934454505228"
|
|
}
|
|
}
|
|
};
|
|
|
|
vi.mocked(getPlatformReadiness).mockResolvedValue({
|
|
platforms: [
|
|
{
|
|
platform: "tmall",
|
|
ready: true,
|
|
status: "ready",
|
|
searchRequirement: "recommended",
|
|
reason: "当前工作区存在可复用会话,创建任务时会再次校验。",
|
|
lastPreparedAt: "2026-04-02T12:00:00.000Z",
|
|
expiresAt: "2026-04-03T12:00:00.000Z"
|
|
},
|
|
{
|
|
platform: "jd",
|
|
ready: false,
|
|
status: "missing",
|
|
searchRequirement: "required",
|
|
reason: "需要先完成会话准备,否则系统会标记为 SearchBlocked。"
|
|
}
|
|
]
|
|
} as any);
|
|
vi.mocked(getHistoryTasks).mockResolvedValue({
|
|
tasks: [
|
|
{
|
|
taskId: "task-1",
|
|
query: "Nintendo Switch 2",
|
|
taskStatus: "Completed",
|
|
updatedAt: "2026-04-02T12:00:00.000Z",
|
|
hasReport: true,
|
|
defaultReportVersion: 2,
|
|
failedPlatforms: [],
|
|
blockedPlatforms: []
|
|
},
|
|
{
|
|
taskId: "task-2",
|
|
query: "DJI Pocket 3",
|
|
taskStatus: "AwaitingConfirmation",
|
|
updatedAt: "2026-04-02T11:30:00.000Z",
|
|
hasReport: false,
|
|
failedPlatforms: [],
|
|
blockedPlatforms: ["jd"]
|
|
}
|
|
]
|
|
} as any);
|
|
vi.mocked(getPlatformSession).mockResolvedValue({
|
|
session: {
|
|
platform: "tmall",
|
|
ready: true,
|
|
status: "ready",
|
|
searchRequirement: "recommended",
|
|
scope: "workspace",
|
|
ttlHours: 24,
|
|
lastPreparedAt: "2026-04-02T10:00:00.000Z",
|
|
expiresAt: "2026-04-03T10:00:00.000Z",
|
|
encryptedSnapshotAvailable: true,
|
|
cipherLabel: "mock-aes-gcm-v1"
|
|
}
|
|
} as any);
|
|
vi.mocked(getJdLiveSession).mockResolvedValue({
|
|
session: {
|
|
configured: true,
|
|
importedAt: "2026-04-02T10:00:00.000Z",
|
|
hasCookie: true,
|
|
userAgent: "stub-user-agent",
|
|
searchApiTemplate: {
|
|
available: false
|
|
},
|
|
detailTemplate: {
|
|
available: true,
|
|
skuId: "100068388533"
|
|
},
|
|
reviewsTemplate: {
|
|
available: true,
|
|
skuId: "100068388533"
|
|
}
|
|
}
|
|
} as any);
|
|
vi.mocked(getJdQrLoginState).mockResolvedValue({
|
|
qrLogin: {
|
|
platform: "jd",
|
|
status: "idle",
|
|
note: "尚未启动扫码登录。",
|
|
sessionImported: false
|
|
}
|
|
} as any);
|
|
vi.mocked(getJdKeywordPreview).mockResolvedValue({
|
|
preview: {
|
|
query: "小米手环10",
|
|
search: {
|
|
source: "html",
|
|
candidateCount: 2,
|
|
selected: {
|
|
skuId: "222222222222",
|
|
score: 168,
|
|
summary: "标题完整命中关键词;位于搜索结果前列;已解析出可回放 SKU",
|
|
matchedTokens: ["小米手环10"],
|
|
candidate: {
|
|
candidateId: "jd-222222222222",
|
|
platform: "jd",
|
|
title: "小米手环10 标准版 智能手环",
|
|
price: 269,
|
|
priceLabel: "¥269",
|
|
storeName: "小米京东自营旗舰店",
|
|
productUrl: "https://item.jd.com/222222222222.html",
|
|
imageUrl: "https://img14.360buyimg.com/n2/jfs/t1/example-10.jpg",
|
|
salesHint: "已售50万+",
|
|
specLabel: "标准版",
|
|
highlights: ["14天续航"]
|
|
}
|
|
},
|
|
alternatives: [
|
|
{
|
|
skuId: "111111111111",
|
|
score: 118,
|
|
summary: "标题/卖点命中 1 个关键词片段;已解析出可回放 SKU",
|
|
matchedTokens: ["小米"],
|
|
candidate: {
|
|
candidateId: "jd-111111111111",
|
|
platform: "jd",
|
|
title: "小米手环9 NFC版",
|
|
price: 249,
|
|
priceLabel: "¥249",
|
|
storeName: "小米京东自营旗舰店",
|
|
productUrl: "https://item.jd.com/111111111111.html",
|
|
imageUrl: "https://img14.360buyimg.com/n2/jfs/t1/example-9.jpg",
|
|
salesHint: "已售20万+",
|
|
specLabel: "NFC版",
|
|
highlights: ["健康监测"]
|
|
}
|
|
}
|
|
]
|
|
},
|
|
product: {
|
|
skuId: "222222222222",
|
|
source: "api",
|
|
detail: {
|
|
skuId: "222222222222",
|
|
title: "小米手环10 标准版 智能手环",
|
|
price: "269.00",
|
|
originalPrice: "299.00",
|
|
estimatedPrice: "269.00",
|
|
shopName: "小米京东自营旗舰店",
|
|
vendorId: null,
|
|
categoryPath: ["智能设备", "智能手环"],
|
|
stockState: "现货",
|
|
mainImage: "https://img14.360buyimg.com/n2/jfs/t1/example-10.jpg",
|
|
averageScore: "4.9"
|
|
},
|
|
pagination: {
|
|
requestedPage: 1,
|
|
requestedCommentCount: 12,
|
|
maxPages: 2,
|
|
pagesFetched: 2,
|
|
pageKey: "page"
|
|
},
|
|
reviews: {
|
|
skuId: "222222222222",
|
|
total: "10000+",
|
|
goodRate: "97%",
|
|
pictureCount: "800",
|
|
tags: [
|
|
{
|
|
tagId: "tag-1",
|
|
name: "续航很久",
|
|
count: "5300"
|
|
}
|
|
],
|
|
comments: [
|
|
{
|
|
id: "comment-1",
|
|
content: "表带舒适,睡眠监测比较准。",
|
|
score: "5",
|
|
creationTime: "2026-04-03 09:00:00",
|
|
userLevelName: "PLUS会员"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
} as any);
|
|
vi.mocked(getJdSessionManager).mockResolvedValue({
|
|
manager: jdManagerState
|
|
} as any);
|
|
vi.mocked(getTmallSessionManager).mockResolvedValue({
|
|
manager: tmallManagerState
|
|
} as any);
|
|
vi.mocked(getTmallLiveSession).mockResolvedValue({
|
|
session: tmallManagerState.session
|
|
} as any);
|
|
vi.mocked(getTmallQrLoginState).mockResolvedValue({
|
|
qrLogin: {
|
|
platform: "tmall",
|
|
status: "idle",
|
|
note: "尚未启动扫码登录。",
|
|
sessionImported: false
|
|
}
|
|
} as any);
|
|
vi.mocked(clearPlatformSession).mockResolvedValue(undefined);
|
|
vi.mocked(importJdManagedSession).mockResolvedValue({
|
|
manager: {
|
|
...jdManagerState,
|
|
note: "京东会话已更新。",
|
|
lastRecoveredAt: "2026-04-02T10:05:00.000Z",
|
|
session: {
|
|
...jdManagerState.session,
|
|
importedAt: "2026-04-02T10:05:00.000Z",
|
|
}
|
|
}
|
|
} as any);
|
|
vi.mocked(importTmallManagedSession).mockResolvedValue({
|
|
manager: {
|
|
...tmallManagerState,
|
|
note: "天猫会话已更新。",
|
|
lastHealthyAt: "2026-04-02T10:05:00.000Z",
|
|
session: {
|
|
...tmallManagerState.session,
|
|
importedAt: "2026-04-02T10:05:00.000Z"
|
|
}
|
|
}
|
|
} as any);
|
|
vi.mocked(clearJdManagedSession).mockResolvedValue({
|
|
manager: {
|
|
status: "idle",
|
|
enabled: true,
|
|
autoLoginMode: "command",
|
|
commandConfigured: true,
|
|
accountConfigured: true,
|
|
passwordConfigured: false,
|
|
accountLabel: "jd***86",
|
|
heartbeatQuery: "iPhone 15",
|
|
checkIntervalMs: 600000,
|
|
runnerTimeoutMs: 300000,
|
|
pendingManualAction: false,
|
|
note: "京东会话已清理。",
|
|
publicNote: "京东会话由运维后台维护,当前尚未就绪。",
|
|
session: {
|
|
configured: false,
|
|
hasCookie: false,
|
|
searchApiTemplate: {
|
|
available: false
|
|
},
|
|
detailTemplate: {
|
|
available: false
|
|
},
|
|
reviewsTemplate: {
|
|
available: false
|
|
}
|
|
}
|
|
}
|
|
} as any);
|
|
vi.mocked(clearTmallManagedSession).mockResolvedValue({
|
|
manager: {
|
|
status: "idle",
|
|
enabled: true,
|
|
heartbeatItemId: "934454505228",
|
|
checkIntervalMs: 600000,
|
|
pendingManualAction: false,
|
|
note: "天猫会话已清理。",
|
|
publicNote: "天猫会话由运维后台维护,当前尚未就绪。",
|
|
session: {
|
|
configured: false,
|
|
hasCookie: false,
|
|
detailTemplate: {
|
|
available: false
|
|
},
|
|
reviewsTemplate: {
|
|
available: false
|
|
}
|
|
}
|
|
}
|
|
} as any);
|
|
vi.mocked(updateJdSessionManagerConfig).mockResolvedValue({
|
|
manager: jdManagerState
|
|
} as any);
|
|
vi.mocked(runJdSessionManagerHealthCheck).mockResolvedValue({
|
|
state: jdManagerState,
|
|
recovered: false
|
|
} as any);
|
|
vi.mocked(runJdSessionManagerRecovery).mockResolvedValue({
|
|
state: jdManagerState,
|
|
recovered: true
|
|
} as any);
|
|
vi.mocked(startJdQrLogin).mockResolvedValue({
|
|
qrLogin: {
|
|
platform: "jd",
|
|
status: "waiting_for_scan",
|
|
note: "二维码已生成,请扫码。",
|
|
targetId: "100068388533",
|
|
qrImageDataUrl: "data:image/png;base64,stub",
|
|
sessionImported: false
|
|
}
|
|
} as any);
|
|
vi.mocked(cancelJdQrLogin).mockResolvedValue({
|
|
qrLogin: {
|
|
platform: "jd",
|
|
status: "cancelled",
|
|
note: "扫码已取消。",
|
|
sessionImported: false
|
|
}
|
|
} as any);
|
|
vi.mocked(runTmallSessionManagerHealthCheck).mockResolvedValue({
|
|
state: tmallManagerState,
|
|
recovered: false
|
|
} as any);
|
|
vi.mocked(startTmallQrLogin).mockResolvedValue({
|
|
qrLogin: {
|
|
platform: "tmall",
|
|
status: "waiting_for_scan",
|
|
note: "二维码已生成,请扫码。",
|
|
targetId: "934454505228",
|
|
qrImageDataUrl: "data:image/png;base64,stub",
|
|
sessionImported: false
|
|
}
|
|
} as any);
|
|
vi.mocked(cancelTmallQrLogin).mockResolvedValue({
|
|
qrLogin: {
|
|
platform: "tmall",
|
|
status: "cancelled",
|
|
note: "扫码已取消。",
|
|
sessionImported: false
|
|
}
|
|
} as any);
|
|
vi.mocked(preparePlatform).mockResolvedValue({
|
|
platform: "jd",
|
|
session_ready: true,
|
|
status: "ready",
|
|
last_prepared_at: "2026-04-02T10:00:00.000Z",
|
|
expires_at: "2026-04-03T10:00:00.000Z",
|
|
encrypted_snapshot_available: true
|
|
} as any);
|
|
});
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("shows readiness details and recent task shortcuts on the new task page", async () => {
|
|
renderWithProviders(<NewTaskPage />);
|
|
|
|
expect(await screen.findByText("最近任务捷径")).toBeInTheDocument();
|
|
expect(await screen.findByText("Nintendo Switch 2")).toBeInTheDocument();
|
|
expect(await screen.findByText("DJI Pocket 3")).toBeInTheDocument();
|
|
expect(await screen.findByText("搜索要求:建议预热")).toBeInTheDocument();
|
|
expect(
|
|
await screen.findByText(/当前工作区已有可复用会话,有效至/)
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("runs the jd keyword-only preview loop from the new task page", async () => {
|
|
const user = userEvent.setup();
|
|
|
|
renderWithProviders(<NewTaskPage />);
|
|
|
|
await user.clear(await screen.findByLabelText("商品关键词 / 描述"));
|
|
await user.type(screen.getByLabelText("商品关键词 / 描述"), "小米手环10");
|
|
await user.click(screen.getByRole("button", { name: "只用关键词抓京东" }));
|
|
|
|
await waitFor(() => {
|
|
expect(getJdKeywordPreview).toHaveBeenCalledWith("小米手环10", {
|
|
commentCount: 12,
|
|
maxPages: 2
|
|
});
|
|
});
|
|
|
|
expect(await screen.findByText("小米手环10 标准版 智能手环")).toBeInTheDocument();
|
|
expect(await screen.findByText(/SKU 222222222222/)).toBeInTheDocument();
|
|
expect(await screen.findByText("好评率 97%")).toBeInTheDocument();
|
|
expect(await screen.findByText("表带舒适,睡眠监测比较准。")).toBeInTheDocument();
|
|
});
|
|
|
|
it("redirects the tmall prepare route to the unified ops page and clears the managed session", async () => {
|
|
const user = userEvent.setup();
|
|
|
|
renderWithProviders(<App />, ["/ops/platforms/tmall/prepare?from=/tasks/new"]);
|
|
|
|
await user.click(screen.getByRole("button", { name: "清理当前会话" }));
|
|
|
|
await waitFor(() => {
|
|
expect(clearTmallManagedSession).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it("redirects the legacy jd prepare route to the ops session manager page", async () => {
|
|
renderWithProviders(<App />, ["/sessions/jd/prepare?from=/tasks/new"]);
|
|
|
|
expect(await screen.findByText("京东运维会话管理")).toBeInTheDocument();
|
|
expect(screen.getByText(/返回业务页面:\/tasks\/new/)).toBeInTheDocument();
|
|
});
|
|
|
|
it("updates the confirm page when a task snapshot restores JD candidates", async () => {
|
|
let snapshotHandler: ((event: MessageEvent<string>) => void) | undefined;
|
|
vi.mocked(createTaskEventsSource).mockReturnValue({
|
|
addEventListener: vi.fn((_type: string, handler: EventListenerOrEventListenerObject) => {
|
|
snapshotHandler = handler as (event: MessageEvent<string>) => void;
|
|
}),
|
|
close: vi.fn(),
|
|
removeEventListener: vi.fn()
|
|
} as unknown as EventSource);
|
|
|
|
vi.mocked(getTask).mockResolvedValue({
|
|
task: {
|
|
taskId: "task-confirm-live",
|
|
query: "iPhone 15",
|
|
createdAt: "2026-04-07T09:00:00.000Z",
|
|
updatedAt: "2026-04-07T09:00:00.000Z",
|
|
perLinkLimit: 100,
|
|
taskTotalLimit: 500,
|
|
taskStatus: "AwaitingConfirmation",
|
|
taskStage: "confirmation",
|
|
platformRuns: [
|
|
{
|
|
platform: "tmall",
|
|
searchRequirement: "recommended",
|
|
status: "AwaitingSelection",
|
|
candidateCount: 1,
|
|
selectedCandidateIds: [],
|
|
lastUpdatedAt: "2026-04-07T09:00:00.000Z"
|
|
},
|
|
{
|
|
platform: "jd",
|
|
searchRequirement: "required",
|
|
status: "SearchBlocked",
|
|
candidateCount: 0,
|
|
selectedCandidateIds: [],
|
|
reason: "waiting for ops recovery",
|
|
lastUpdatedAt: "2026-04-07T09:00:00.000Z"
|
|
}
|
|
],
|
|
platformCandidates: {
|
|
tmall: [],
|
|
jd: []
|
|
},
|
|
events: [],
|
|
reportVersions: []
|
|
}
|
|
} as any);
|
|
vi.mocked(getTaskCandidates)
|
|
.mockResolvedValueOnce({
|
|
candidates: {
|
|
tmall: [],
|
|
jd: []
|
|
}
|
|
} as any)
|
|
.mockResolvedValue({
|
|
candidates: {
|
|
tmall: [],
|
|
jd: [
|
|
{
|
|
candidateId: "jd-100068388533",
|
|
platform: "jd",
|
|
title: "Apple iPhone 15",
|
|
price: 3898,
|
|
priceLabel: "CNY 3898",
|
|
storeName: "JD Self Operated",
|
|
productUrl: "https://item.jd.com/100068388533.html",
|
|
imageUrl: "https://img14.360buyimg.com/example.jpg",
|
|
salesHint: "sold 500+",
|
|
specLabel: "128GB",
|
|
highlights: ["A16"]
|
|
}
|
|
]
|
|
}
|
|
} as any);
|
|
|
|
renderWithProviders(<App />, ["/tasks/task-confirm-live/confirm"]);
|
|
|
|
expect(await screen.findByText("waiting for ops recovery")).toBeInTheDocument();
|
|
|
|
await act(async () => {
|
|
snapshotHandler?.(
|
|
new MessageEvent("task.snapshot", {
|
|
data: JSON.stringify({
|
|
task: {
|
|
taskId: "task-confirm-live",
|
|
query: "iPhone 15",
|
|
createdAt: "2026-04-07T09:00:00.000Z",
|
|
updatedAt: "2026-04-07T09:01:00.000Z",
|
|
perLinkLimit: 100,
|
|
taskTotalLimit: 500,
|
|
taskStatus: "AwaitingConfirmation",
|
|
taskStage: "confirmation",
|
|
platformRuns: [
|
|
{
|
|
platform: "tmall",
|
|
searchRequirement: "recommended",
|
|
status: "AwaitingSelection",
|
|
candidateCount: 1,
|
|
selectedCandidateIds: [],
|
|
lastUpdatedAt: "2026-04-07T09:00:00.000Z"
|
|
},
|
|
{
|
|
platform: "jd",
|
|
searchRequirement: "required",
|
|
status: "AwaitingSelection",
|
|
candidateCount: 1,
|
|
selectedCandidateIds: [],
|
|
lastUpdatedAt: "2026-04-07T09:01:00.000Z"
|
|
}
|
|
],
|
|
platformCandidates: {
|
|
tmall: [],
|
|
jd: []
|
|
},
|
|
events: [],
|
|
reportVersions: []
|
|
}
|
|
})
|
|
})
|
|
);
|
|
});
|
|
|
|
expect(await screen.findByText("Apple iPhone 15")).toBeInTheDocument();
|
|
await waitFor(() => {
|
|
expect(getTaskCandidates).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|
|
|
|
it("imports jd managed session payload from the ops page", async () => {
|
|
const user = userEvent.setup();
|
|
|
|
renderWithProviders(<App />, ["/ops/session-manager?platform=jd&from=/tasks/new"]);
|
|
|
|
await user.clear(await screen.findByLabelText("Cookie Header"));
|
|
await user.type(screen.getByLabelText("Cookie Header"), "thor=masked; pin=masked;");
|
|
await user.type(
|
|
screen.getByLabelText("Detail Template URL"),
|
|
"https://api.m.jd.com/?functionId=pc_detailpage_wareBusiness"
|
|
);
|
|
await user.type(
|
|
screen.getByLabelText("Reviews Template URL"),
|
|
"https://api.m.jd.com/?functionId=getLegoWareDetailComment"
|
|
);
|
|
await user.click(screen.getByRole("button", { name: "注入京东会话" }));
|
|
|
|
await waitFor(() => {
|
|
expect(importJdManagedSession).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
cookieHeader: "thor=masked; pin=masked;",
|
|
detailTemplateUrl: "https://api.m.jd.com/?functionId=pc_detailpage_wareBusiness",
|
|
reviewsTemplateUrl: "https://api.m.jd.com/?functionId=getLegoWareDetailComment"
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
it("imports tmall managed session payload from the unified ops page", async () => {
|
|
const user = userEvent.setup();
|
|
|
|
renderWithProviders(<App />, ["/ops/session-manager?platform=tmall&from=/tasks/new"]);
|
|
|
|
await user.clear(await screen.findByLabelText("Cookie Header"));
|
|
await user.type(
|
|
screen.getByLabelText("Cookie Header"),
|
|
"_m_h5_tk=masked_token_123; cookie2=masked;"
|
|
);
|
|
await user.type(
|
|
screen.getByLabelText("Detail Template URL"),
|
|
"https://detail.tmall.com/item.htm?id=934454505228"
|
|
);
|
|
await user.type(
|
|
screen.getByLabelText("Reviews Template URL"),
|
|
"https://h5api.m.tmall.com/h5/mtop.taobao.rate.detaillist.get/6.0/?data=%7B%22auctionNumId%22%3A%22934454505228%22%7D"
|
|
);
|
|
await user.click(screen.getByRole("button", { name: "注入天猫会话" }));
|
|
|
|
await waitFor(() => {
|
|
expect(importTmallManagedSession).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
cookieHeader: "_m_h5_tk=masked_token_123; cookie2=masked;",
|
|
detailTemplateUrl: "https://detail.tmall.com/item.htm?id=934454505228",
|
|
reviewsTemplateUrl:
|
|
"https://h5api.m.tmall.com/h5/mtop.taobao.rate.detaillist.get/6.0/?data=%7B%22auctionNumId%22%3A%22934454505228%22%7D"
|
|
})
|
|
);
|
|
});
|
|
});
|
|
});
|