// @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"]); }); });