feat: honor all-range export and batch scope
This commit is contained in:
@@ -1791,7 +1791,7 @@ describe("market-content-entry", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("audience profile export requires selected creators", async () => {
|
||||
test("audience profile export requires selected creators outside of the all range", async () => {
|
||||
document.body.innerHTML = buildRealMarketFixture([
|
||||
{ authorId: "111", authorName: "达人 A", price21To60s: "¥11,000" }
|
||||
]);
|
||||
@@ -1825,6 +1825,109 @@ describe("market-content-entry", () => {
|
||||
).toContain("请先勾选需要导出数据的达人");
|
||||
});
|
||||
|
||||
test("audience profile export includes all filtered creators when all is selected without checks", async () => {
|
||||
const records = [
|
||||
{ authorId: "111", authorName: "达人 A", price21To60s: "¥11,000" },
|
||||
{ authorId: "222", authorName: "达人 B", price21To60s: "¥22,000" }
|
||||
];
|
||||
document.body.innerHTML = buildRealMarketFixture(records);
|
||||
document.documentElement.setAttribute(
|
||||
"data-sces-market-request-snapshot",
|
||||
JSON.stringify({
|
||||
body: JSON.stringify({ page: 1 }),
|
||||
method: "POST",
|
||||
url: "https://xingtu.cn/api/mock-market-search"
|
||||
})
|
||||
);
|
||||
(
|
||||
globalThis as typeof globalThis & { fetch?: typeof fetch }
|
||||
).fetch = vi.fn(async () => ({
|
||||
json: async () => ({
|
||||
data: {
|
||||
marketList: buildMarketListResponseRows(records),
|
||||
totalPages: 1
|
||||
}
|
||||
}),
|
||||
ok: true
|
||||
}));
|
||||
const buildAudienceProfileCsv = vi.fn(() => "profile-csv");
|
||||
|
||||
const { createMarketController } = await import("../src/content/market/index");
|
||||
const controller = trackController(createMarketController({
|
||||
buildAudienceProfileCsv,
|
||||
document,
|
||||
loadAudienceProfile: async () => ({ status: "success" as const }),
|
||||
loadAuthorMetrics: async () => ({
|
||||
success: false,
|
||||
reason: "request-failed"
|
||||
}),
|
||||
onCsvReady: vi.fn(),
|
||||
window
|
||||
}));
|
||||
|
||||
await controller.ready;
|
||||
setSelectValue('[data-plugin-export-range="select"]', "all");
|
||||
dispatchChange('[data-plugin-export-range="select"]');
|
||||
click('[data-plugin-export-audience-profile="button"]');
|
||||
await waitForMockCall(buildAudienceProfileCsv, 40, 50);
|
||||
|
||||
expect(
|
||||
buildAudienceProfileCsv.mock.calls[0]?.[0].map(({ record }) => record.authorId)
|
||||
).toEqual(["111", "222"]);
|
||||
});
|
||||
|
||||
test("audience profile export gives checked creators priority in the all range", async () => {
|
||||
const records = [
|
||||
{ authorId: "111", authorName: "达人 A", price21To60s: "¥11,000" },
|
||||
{ authorId: "222", authorName: "达人 B", price21To60s: "¥22,000" }
|
||||
];
|
||||
document.body.innerHTML = buildRealMarketFixture(records);
|
||||
document.documentElement.setAttribute(
|
||||
"data-sces-market-request-snapshot",
|
||||
JSON.stringify({
|
||||
body: JSON.stringify({ page: 1 }),
|
||||
method: "POST",
|
||||
url: "https://xingtu.cn/api/mock-market-search"
|
||||
})
|
||||
);
|
||||
(
|
||||
globalThis as typeof globalThis & { fetch?: typeof fetch }
|
||||
).fetch = vi.fn(async () => ({
|
||||
json: async () => ({
|
||||
data: {
|
||||
marketList: buildMarketListResponseRows(records),
|
||||
totalPages: 1
|
||||
}
|
||||
}),
|
||||
ok: true
|
||||
}));
|
||||
const buildAudienceProfileCsv = vi.fn(() => "profile-csv");
|
||||
|
||||
const { createMarketController } = await import("../src/content/market/index");
|
||||
const controller = trackController(createMarketController({
|
||||
buildAudienceProfileCsv,
|
||||
document,
|
||||
loadAudienceProfile: async () => ({ status: "success" as const }),
|
||||
loadAuthorMetrics: async () => ({
|
||||
success: false,
|
||||
reason: "request-failed"
|
||||
}),
|
||||
onCsvReady: vi.fn(),
|
||||
window
|
||||
}));
|
||||
|
||||
await controller.ready;
|
||||
clickSelectionCheckboxForAuthor("222");
|
||||
setSelectValue('[data-plugin-export-range="select"]', "all");
|
||||
dispatchChange('[data-plugin-export-range="select"]');
|
||||
click('[data-plugin-export-audience-profile="button"]');
|
||||
await waitForMockCall(buildAudienceProfileCsv, 40, 50);
|
||||
|
||||
expect(
|
||||
buildAudienceProfileCsv.mock.calls[0]?.[0].map(({ record }) => record.authorId)
|
||||
).toEqual(["222"]);
|
||||
});
|
||||
|
||||
test("audience profile export loads profiles only for selected creators", async () => {
|
||||
document.body.innerHTML = buildRealMarketFixture([
|
||||
{ authorId: "111", authorName: "达人 A", price21To60s: "¥11,000" },
|
||||
@@ -2967,8 +3070,62 @@ describe("market-content-entry", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("all-range batch submit does not fall back when checked creators are outside the results", async () => {
|
||||
document.body.innerHTML = buildRealMarketFixture([
|
||||
{ authorId: "111", authorName: "达人 A", price21To60s: "¥11,000" }
|
||||
]);
|
||||
document.documentElement.setAttribute(
|
||||
"data-sces-market-request-snapshot",
|
||||
JSON.stringify({
|
||||
body: JSON.stringify({ page: 1 }),
|
||||
method: "POST",
|
||||
url: "https://xingtu.cn/api/mock-market-search"
|
||||
})
|
||||
);
|
||||
(
|
||||
globalThis as typeof globalThis & { fetch?: typeof fetch }
|
||||
).fetch = vi.fn(async () => ({
|
||||
json: async () => ({
|
||||
data: {
|
||||
marketList: buildMarketListResponseRows([
|
||||
{ authorId: "222", authorName: "达人 B", price21To60s: "¥22,000" }
|
||||
]),
|
||||
totalPages: 1
|
||||
}
|
||||
}),
|
||||
ok: true
|
||||
}));
|
||||
const submitBatch = vi.fn(async () => ({ ok: true }));
|
||||
|
||||
const { createMarketController } = await import("../src/content/market/index");
|
||||
const controller = trackController(createMarketController({
|
||||
document,
|
||||
loadAuthorMetrics: async () => ({
|
||||
success: false,
|
||||
reason: "request-failed"
|
||||
}),
|
||||
promptBatchName: vi.fn(() => "自动选择批次"),
|
||||
submitBatch,
|
||||
window
|
||||
}));
|
||||
|
||||
await controller.ready;
|
||||
clickSelectionCheckboxForAuthor("111");
|
||||
setSelectValue('[data-plugin-export-range="select"]', "all");
|
||||
dispatchChange('[data-plugin-export-range="select"]');
|
||||
removeDefaultSpreadMetricFilter();
|
||||
click('[data-plugin-batch-submit="button"]');
|
||||
|
||||
await waitForCondition(() =>
|
||||
document.querySelector('[data-plugin-export-status="text"]')?.textContent ===
|
||||
"全部范围内没有选中的达人"
|
||||
);
|
||||
|
||||
expect(submitBatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test(
|
||||
"default paged batch submit keeps detailed progress when no creators are selected",
|
||||
"all-range batch submit keeps detailed progress when no creators are selected",
|
||||
async () => {
|
||||
const pages = [
|
||||
[
|
||||
@@ -3042,6 +3199,9 @@ describe("market-content-entry", () => {
|
||||
|
||||
await controller.ready;
|
||||
|
||||
setSelectValue('[data-plugin-export-range="select"]', "all");
|
||||
dispatchChange('[data-plugin-export-range="select"]');
|
||||
|
||||
removeDefaultSpreadMetricFilter();
|
||||
click('[data-plugin-batch-submit="button"]');
|
||||
for (let attempt = 0; attempt < 40; attempt += 1) {
|
||||
|
||||
@@ -155,6 +155,56 @@ describe("silent-export-controller", () => {
|
||||
expect(records?.map((record) => record.authorId)).toEqual(["2", "3"]);
|
||||
});
|
||||
|
||||
test("starts all-result exports from page 1 even after browsing to a later page", 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}`
|
||||
},
|
||||
star_id: String(pageNo)
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
page: pageNo,
|
||||
totalPages: 3
|
||||
}
|
||||
}),
|
||||
ok: true
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const records = await controller.exportRecords({ mode: "all" });
|
||||
|
||||
expect(requestedPages).toEqual([1, 2, 3]);
|
||||
expect(records?.map((record) => record.authorId)).toEqual(["1", "2", "3"]);
|
||||
});
|
||||
|
||||
test("keeps attribute_datas.id as the spread author id while preserving star_id as row id", async () => {
|
||||
document.documentElement.setAttribute(
|
||||
"data-sces-market-request-snapshot",
|
||||
|
||||
Reference in New Issue
Block a user