feat: refine audience profile csv columns
This commit is contained in:
@@ -21,10 +21,10 @@ describe("audience-profile-client", () => {
|
||||
authorId: "7294473194298146854",
|
||||
authorName: "奇奇de海洋",
|
||||
status: "success"
|
||||
});
|
||||
}, 3);
|
||||
|
||||
expect(fetchImpl).toHaveBeenCalledWith(
|
||||
"https://www.xingtu.cn/gw/api/data_sp/author_audience_distribution?o_author_id=7294473194298146854&platform_source=1&platform_channel=1&link_type=1",
|
||||
"https://www.xingtu.cn/gw/api/data_sp/author_audience_distribution?o_author_id=7294473194298146854&platform_source=1&platform_channel=1&link_type=3",
|
||||
expect.objectContaining({
|
||||
credentials: "include",
|
||||
method: "GET"
|
||||
|
||||
@@ -4,21 +4,36 @@ import { buildAudienceProfileCsv } from "../src/content/market/audience-profile-
|
||||
import type { AudienceProfileExportRow } from "../src/content/market/audience-profile-types";
|
||||
|
||||
describe("audience-profile-csv", () => {
|
||||
test("appends structured audience profile columns after the market export columns", () => {
|
||||
test("exports only requested profile distribution columns", () => {
|
||||
const csv = buildAudienceProfileCsv([
|
||||
{
|
||||
profile: {
|
||||
age: [{ label: "31-40", value: "50%" }],
|
||||
cityTier: [{ label: "一线城市", value: "100%" }],
|
||||
cityTop: [{ label: "广州", value: "30.4%" }],
|
||||
crowd: [{ label: "都市蓝领", value: "100%" }],
|
||||
gender: [
|
||||
{ label: "男性", value: "71.7%" },
|
||||
{ label: "女性", value: "28.3%" }
|
||||
],
|
||||
interest: [{ label: "随拍", value: "100%" }],
|
||||
province: [{ label: "广东", value: "60%" }],
|
||||
status: "success"
|
||||
profiles: {
|
||||
audience: {
|
||||
age: [{ label: "31-40", value: "50%" }],
|
||||
cityTier: [{ label: "一线城市", value: "100%" }],
|
||||
crowd: [{ label: "都市蓝领", value: "100%" }],
|
||||
gender: [
|
||||
{ label: "男性", value: "71.7%" },
|
||||
{ label: "女性", value: "28.3%" }
|
||||
],
|
||||
status: "success"
|
||||
},
|
||||
fans: {
|
||||
age: [{ label: "31-40", value: "40%" }],
|
||||
cityTier: [{ label: "一线城市", value: "80%" }],
|
||||
crowd: [{ label: "都市蓝领", value: "60%" }],
|
||||
gender: [
|
||||
{ label: "男性", value: "60%" },
|
||||
{ label: "女性", value: "40%" }
|
||||
],
|
||||
status: "success"
|
||||
},
|
||||
longtimeFans: {
|
||||
age: [{ label: "31-40", value: "30%" }],
|
||||
cityTier: [{ label: "一线城市", value: "70%" }],
|
||||
crowd: [{ label: "都市蓝领", value: "50%" }],
|
||||
status: "success"
|
||||
}
|
||||
},
|
||||
record: {
|
||||
authorId: "123",
|
||||
@@ -29,44 +44,55 @@ describe("audience-profile-csv", () => {
|
||||
},
|
||||
status: "success"
|
||||
}
|
||||
}
|
||||
] satisfies AudienceProfileExportRow[]);
|
||||
} satisfies AudienceProfileExportRow
|
||||
]);
|
||||
|
||||
const [headerLine, rowLine] = csv.split("\n");
|
||||
|
||||
expect(headerLine).toContain("达人信息,连接用户数");
|
||||
expect(headerLine).toContain("画像抓取状态");
|
||||
expect(headerLine).toContain("连接用户-男性占比");
|
||||
expect(headerLine).toContain("连接用户-31-40占比");
|
||||
expect(headerLine).toContain("省份-广东占比");
|
||||
expect(headerLine).toContain("地域TOP1名称,地域TOP1占比");
|
||||
expect(headerLine).toContain("城市等级-一线城市占比");
|
||||
expect(headerLine).toContain("兴趣TOP1名称,兴趣TOP1占比");
|
||||
expect(headerLine).toContain("八大人群-都市蓝领占比");
|
||||
expect(rowLine).toContain("成功");
|
||||
expect(headerLine).not.toContain("抓取状态");
|
||||
expect(headerLine).not.toContain("失败原因");
|
||||
expect(headerLine).toContain("观众画像-男性占比");
|
||||
expect(headerLine).toContain("粉丝画像-女性占比");
|
||||
expect(headerLine).not.toContain("铁粉画像-男性占比");
|
||||
expect(headerLine).toContain("观众画像-31-40占比");
|
||||
expect(headerLine).toContain("粉丝画像-一线城市占比");
|
||||
expect(headerLine).toContain("铁粉画像-都市蓝领占比");
|
||||
expect(headerLine).not.toContain("省份");
|
||||
expect(headerLine).not.toContain("地域TOP");
|
||||
expect(headerLine).not.toContain("兴趣TOP");
|
||||
expect(rowLine).toContain("71.7%");
|
||||
expect(rowLine).toContain("广州,30.4%");
|
||||
expect(rowLine).toContain("随拍,100%");
|
||||
expect(rowLine).toContain("60%");
|
||||
});
|
||||
|
||||
test("keeps failed profile rows and marks their failure reason", () => {
|
||||
test("leaves distribution cells empty when profile loading fails", () => {
|
||||
const csv = buildAudienceProfileCsv([
|
||||
{
|
||||
profile: {
|
||||
failureReason: "request-failed",
|
||||
status: "failed"
|
||||
profiles: {
|
||||
audience: {
|
||||
failureReason: "request-failed",
|
||||
status: "failed"
|
||||
},
|
||||
fans: {
|
||||
failureReason: "timeout",
|
||||
status: "failed"
|
||||
},
|
||||
longtimeFans: {
|
||||
status: "failed"
|
||||
}
|
||||
},
|
||||
record: {
|
||||
authorId: "123",
|
||||
authorName: "达人 A",
|
||||
status: "success"
|
||||
}
|
||||
}
|
||||
] satisfies AudienceProfileExportRow[]);
|
||||
} satisfies AudienceProfileExportRow
|
||||
]);
|
||||
|
||||
const [, rowLine] = csv.split("\n");
|
||||
|
||||
expect(rowLine).toContain("失败");
|
||||
expect(rowLine).toContain("request-failed");
|
||||
expect(rowLine).not.toContain("失败");
|
||||
expect(rowLine).not.toContain("request-failed");
|
||||
expect(rowLine).not.toContain("timeout");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1624,10 +1624,24 @@ describe("market-content-entry", () => {
|
||||
{ authorId: "222", authorName: "达人 B", price21To60s: "¥22,000" }
|
||||
]);
|
||||
const buildAudienceProfileCsv = vi.fn(() => "profile-csv");
|
||||
const loadAudienceProfile = vi.fn(async () => ({
|
||||
gender: [{ label: "男性", value: "60%" }],
|
||||
status: "success" as const
|
||||
}));
|
||||
const loadAudienceProfile = vi.fn(async (_record, linkType: number) => {
|
||||
if (linkType === 4) {
|
||||
return {
|
||||
age: [{ label: "31-40", value: "30%" }],
|
||||
crowd: [{ label: "都市蓝领", value: "50%" }],
|
||||
cityTier: [{ label: "一线城市", value: "70%" }],
|
||||
status: "success" as const
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
age: [{ label: "31-40", value: "60%" }],
|
||||
crowd: [{ label: "都市蓝领", value: "80%" }],
|
||||
cityTier: [{ label: "一线城市", value: "90%" }],
|
||||
gender: [{ label: "男性", value: "60%" }],
|
||||
status: "success" as const
|
||||
};
|
||||
});
|
||||
const onCsvReady = vi.fn();
|
||||
|
||||
const { createMarketController } = await import("../src/content/market/index");
|
||||
@@ -1651,15 +1665,18 @@ describe("market-content-entry", () => {
|
||||
click('[data-plugin-export-audience-profile="button"]');
|
||||
await waitForMockCall(buildAudienceProfileCsv, 40, 50);
|
||||
|
||||
expect(loadAudienceProfile).toHaveBeenCalledTimes(1);
|
||||
expect(loadAudienceProfile).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ authorId: "222" })
|
||||
);
|
||||
expect(loadAudienceProfile).toHaveBeenCalledTimes(3);
|
||||
expect(loadAudienceProfile.mock.calls.map(([, linkType]) => linkType)).toEqual([
|
||||
3,
|
||||
1,
|
||||
4
|
||||
]);
|
||||
expect(buildAudienceProfileCsv).toHaveBeenCalledWith([
|
||||
{
|
||||
profile: {
|
||||
gender: [{ label: "男性", value: "60%" }],
|
||||
status: "success"
|
||||
profiles: {
|
||||
audience: expect.objectContaining({ status: "success" }),
|
||||
fans: expect.objectContaining({ status: "success" }),
|
||||
longtimeFans: expect.objectContaining({ status: "success" })
|
||||
},
|
||||
record: expect.objectContaining({ authorId: "222" })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user