// @vitest-environment jsdom
import { describe, expect, test } from "vitest";
import { createSilentExportController } from "../src/content/market/silent-export-controller";
describe("silent-export-controller", () => {
test("replays exports from the live market page state when the request snapshot attribute is missing", async () => {
document.body.innerHTML = '
';
const marketRoot = document.querySelector(".base-author-list") as HTMLElement & {
__vue__?: {
_setupState?: Record;
};
};
marketRoot.__vue__ = {
_setupState: {
__$temp_1: {
reqParams: {
scene_param: {
platform_source: 1
},
page_param: {
limit: "20",
page: "2"
},
search_param: {
seach_type: 3
}
}
}
}
};
const requestedPages: number[] = [];
const requestedHeaders: Array = [];
const requestedUrls: string[] = [];
const controller = createSilentExportController({
document,
fetchImpl: async (url, init) => {
const body = JSON.parse(String(init?.body ?? "{}")) as {
page_param?: { page?: number | string };
};
const pageNo = Number(body.page_param?.page ?? 0);
requestedPages.push(pageNo);
requestedHeaders.push(init?.headers);
requestedUrls.push(url);
return {
json: async () => ({
authors: [
{
attribute_datas: {
nickname: `达人${pageNo}`,
price_20_60: pageNo * 1000
},
star_id: String(pageNo)
}
],
pagination: {
limit: 20,
page: pageNo,
total_count: 100
}
}),
ok: true
};
}
});
const records = await controller.exportRecords({
mode: "count",
pageCount: 2
});
expect(requestedPages).toEqual([2, 3]);
expect(
requestedUrls.every((url) =>
url.endsWith("/gw/api/gsearch/search_for_author_square")
)
).toBe(true);
expect(requestedHeaders).toEqual([
{
Accept: "application/json, text/plain, */*",
"Agw-Js-Conv": "str",
"Content-Type": "application/json",
"x-login-source": "1"
},
{
Accept: "application/json, text/plain, */*",
"Agw-Js-Conv": "str",
"Content-Type": "application/json",
"x-login-source": "1"
}
]);
expect(records?.map((record) => record.authorId)).toEqual(["2", "3"]);
});
test("replays paged exports when the page number is nested inside the request body", async () => {
document.documentElement.setAttribute(
"data-sces-market-request-snapshot",
JSON.stringify({
body: JSON.stringify({
page_param: {
page: 2
}
}),
method: "POST",
url: "https://xingtu.cn/api/mock-market-search"
})
);
const requestedPages: number[] = [];
const controller = createSilentExportController({
document,
fetchImpl: async (_url, init) => {
const body = JSON.parse(String(init?.body ?? "{}")) as {
page_param?: { page?: number };
};
const pageNo = body.page_param?.page ?? 0;
requestedPages.push(pageNo);
return {
json: async () => ({
authors: [
{
attribute_datas: {
nickname: `达人${pageNo}`,
price_20_60: pageNo * 1000
},
star_id: String(pageNo)
}
],
pagination: {
limit: 20,
page: pageNo,
total_count: 100
}
}),
ok: true
};
}
});
const records = await controller.exportRecords({
mode: "count",
pageCount: 2
});
expect(requestedPages).toEqual([2, 3]);
expect(records?.map((record) => record.authorId)).toEqual(["2", "3"]);
});
test("starts from page 1 when the captured request omits an explicit page number", async () => {
document.documentElement.setAttribute(
"data-sces-market-request-snapshot",
JSON.stringify({
body: JSON.stringify({
filters: {
keyword: "test"
}
}),
method: "POST",
url: "https://xingtu.cn/api/mock-market-search"
})
);
const requestedPages: number[] = [];
const controller = createSilentExportController({
document,
fetchImpl: async (_url, init) => {
const body = JSON.parse(String(init?.body ?? "{}")) as { page?: number };
const page = body.page ?? 0;
requestedPages.push(page);
return {
json: async () => ({
data: {
marketList: [
{
attribute_datas: {
nickname: `达人${page}`,
price_20_60: page * 1000
},
star_id: String(page)
}
]
}
}),
ok: true
};
}
});
const records = await controller.exportRecords({
mode: "count",
pageCount: 2
});
expect(requestedPages).toEqual([1, 2]);
expect(records?.map((record) => record.authorId)).toEqual(["1", "2"]);
});
test("accepts snapshot headers that contain non-string values from the live XHR capture", async () => {
document.body.innerHTML = "";
document.documentElement.setAttribute(
"data-sces-market-request-snapshot",
JSON.stringify({
body: JSON.stringify({
page_param: {
page: "1",
limit: "20"
}
}),
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json",
"x-login-source": 1,
"Agw-Js-Conv": "str"
},
method: "POST",
url: "/gw/api/gsearch/search_for_author_square"
})
);
const requestedPages: number[] = [];
const controller = createSilentExportController({
document,
fetchImpl: async (_url, init) => {
const body = JSON.parse(String(init?.body ?? "{}")) as {
page_param?: { page?: number | string };
};
requestedPages.push(Number(body.page_param?.page ?? 0));
return {
json: async () => ({
authors: [
{
attribute_datas: {
nickname: "达人1"
},
star_id: "1"
}
],
pagination: {
limit: 20,
page: 1,
total_count: 20
}
}),
ok: true
};
}
});
const records = await controller.exportRecords({
mode: "count",
pageCount: 1
});
expect(requestedPages).toEqual([1]);
expect(records?.map((record) => record.authorId)).toEqual(["1"]);
});
});