feat: redesign market propagation toolbar

This commit is contained in:
wxs
2026-07-16 18:21:12 +08:00
parent ca365b485a
commit 2e248fa9a3
2 changed files with 1098 additions and 123 deletions
+477 -60
View File
@@ -365,6 +365,9 @@ describe("market-content-entry", () => {
const audienceProfileFieldButton = document.querySelector(
'[data-plugin-audience-profile-fields="button"]'
) as HTMLButtonElement | null;
const exportButton = document.querySelector(
'[data-plugin-export="button"]'
) as HTMLButtonElement | null;
expect(audienceProfileExportButton?.textContent).toBe("导出选中达人数据");
expect(audienceProfileExportButton?.title).toBe(
"仅导出已勾选达人,包含内容数据、效果预估、画像等维度"
@@ -379,11 +382,29 @@ describe("market-content-entry", () => {
);
expect(batchSubmitButton?.title).toBe("将当前选中的达人提交到后续业务批次");
expect(batchSubmitButton?.style.backgroundColor).toBe("rgb(127, 29, 45)");
expect(audienceProfileExportButton?.style.backgroundColor).toBe("rgb(127, 29, 45)");
expect(audienceProfileByIdExportButton?.style.backgroundColor).toBe("rgb(127, 29, 45)");
expect(batchSubmitButton?.style.color).toBe("rgb(255, 255, 255)");
expect(audienceProfileExportButton?.style.color).toBe("rgb(255, 255, 255)");
expect(audienceProfileByIdExportButton?.style.color).toBe("rgb(255, 255, 255)");
expect([
exportButton?.style.backgroundColor,
audienceProfileExportButton?.style.backgroundColor,
audienceProfileByIdExportButton?.style.backgroundColor,
audienceProfileFieldButton?.style.backgroundColor
]).toEqual([
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",
"rgb(255, 255, 255)"
]);
expect([
exportButton?.style.color,
audienceProfileExportButton?.style.color,
audienceProfileByIdExportButton?.style.color,
audienceProfileFieldButton?.style.color
]).toEqual([
"rgb(52, 64, 84)",
"rgb(52, 64, 84)",
"rgb(52, 64, 84)",
"rgb(52, 64, 84)"
]);
});
test("renders the approved compact toolbar layout from the preview", async () => {
@@ -416,8 +437,20 @@ describe("market-content-entry", () => {
const dataGroup = document.querySelector(
'[data-plugin-toolbar-group="data"]'
) as HTMLElement | null;
const metricSelector = document.querySelector(
'[data-plugin-spread-metrics="root"]'
const csvExportGroup = document.querySelector(
'[data-plugin-toolbar-action-group="csv-export"]'
) as HTMLElement | null;
const selectedAudienceExportGroup = document.querySelector(
'[data-plugin-toolbar-action-group="selected-audience-export"]'
) as HTMLElement | null;
const idExportGroup = document.querySelector(
'[data-plugin-toolbar-action-group="id-export"]'
) as HTMLElement | null;
const batchSubmitGroup = document.querySelector(
'[data-plugin-toolbar-action-group="batch-submit"]'
) as HTMLElement | null;
const metricCatalog = document.querySelector(
'[data-plugin-spread-metric-catalog="root"]'
) as HTMLElement | null;
const rulesGroup = document.querySelector(
'[data-plugin-spread-rules="root"]'
@@ -428,15 +461,30 @@ describe("market-content-entry", () => {
const titles = Array.from(
document.querySelectorAll("[data-plugin-toolbar-title]")
) as HTMLElement[];
const buttons = Array.from(
document.querySelectorAll("[data-plugin-toolbar-group='data'] button")
) as HTMLButtonElement[];
const exportButton = document.querySelector(
'[data-plugin-export="button"]'
) as HTMLButtonElement | null;
const exportRangeSelect = document.querySelector(
'[data-plugin-export-range="select"]'
) as HTMLSelectElement | null;
const exportCustomPagesInput = document.querySelector(
'[data-plugin-export-custom-pages="input"]'
) as HTMLInputElement | null;
const audienceProfileExportButton = document.querySelector(
'[data-plugin-export-audience-profile="button"]'
) as HTMLButtonElement | null;
const audienceProfileByIdExportButton = document.querySelector(
'[data-plugin-export-audience-profile-by-id="button"]'
) as HTMLButtonElement | null;
const audienceProfileFieldButton = document.querySelector(
'[data-plugin-audience-profile-fields="button"]'
) as HTMLButtonElement | null;
const batchSubmitButton = document.querySelector(
'[data-plugin-batch-submit="button"]'
) as HTMLButtonElement | null;
const operators = Array.from(
document.querySelectorAll("[data-plugin-spread-threshold-operator]")
).map((element) => element.textContent);
const metricInputs = Array.from(
document.querySelectorAll("[data-plugin-spread-metric]")
) as HTMLInputElement[];
const ruleRows = Array.from(
document.querySelectorAll("[data-plugin-spread-rule]")
) as HTMLElement[];
@@ -451,8 +499,27 @@ describe("market-content-entry", () => {
expect(thresholdRow?.style.flexWrap).toBe("nowrap");
expect(thresholdRow?.style.alignItems).toBe("center");
expect(dataGroup?.parentElement).toBe(primaryRow);
expect(csvExportGroup?.parentElement).toBe(dataGroup);
expect(selectedAudienceExportGroup?.parentElement).toBe(dataGroup);
expect(idExportGroup?.parentElement).toBe(dataGroup);
expect(batchSubmitGroup?.parentElement).toBe(dataGroup);
expect(Array.from(csvExportGroup?.children ?? [])).toEqual([
exportButton,
exportRangeSelect,
exportCustomPagesInput
]);
expect(Array.from(selectedAudienceExportGroup?.children ?? [])).toEqual([
audienceProfileExportButton,
audienceProfileFieldButton
]);
expect(Array.from(idExportGroup?.children ?? [])).toEqual([
audienceProfileByIdExportButton
]);
expect(Array.from(batchSubmitGroup?.children ?? [])).toEqual([
batchSubmitButton
]);
expect(statusText?.parentElement).toBe(primaryRow);
expect(metricSelector?.parentElement).toBe(thresholdRow);
expect(metricCatalog?.parentElement).toBe(thresholdRow);
expect(rulesGroup?.parentElement).toBe(thresholdRow);
expect(rulesGroup?.style.flexDirection).toBe("column");
expect(primaryRow?.style.justifyContent).toBe("flex-start");
@@ -462,8 +529,20 @@ describe("market-content-entry", () => {
]);
expect(titles[0]?.style.background).toBe("rgb(238, 245, 255)");
expect(operators).toEqual(["≥", "≥"]);
expect(metricInputs.map((input) => input.checked)).toEqual([false, false]);
expect(ruleRows.map((row) => row.hidden)).toEqual([true, true]);
expect(
document.querySelector('[data-plugin-spread-metric-catalog-trigger="button"]')
?.textContent
).toBe("添加筛选指标");
expect(
document.querySelector('[data-plugin-spread-metric-selected-count="text"]')
?.textContent
).toBe("已选 1 项");
expect(
(document.querySelector(
'[data-plugin-spread-metric-catalog-panel="root"]'
) as HTMLElement | null)?.hidden
).toBe(true);
expect(ruleRows.map((row) => row.hidden)).toEqual([false, true]);
expect(thresholdInputs.map((input) => input.placeholder)).toEqual([
"",
""
@@ -472,20 +551,45 @@ describe("market-content-entry", () => {
"0.1",
"0.1"
]);
expect(buttons.map((button) => button.textContent)).toEqual([
expect([
exportButton?.textContent,
audienceProfileExportButton?.textContent,
audienceProfileByIdExportButton?.textContent,
audienceProfileFieldButton?.textContent,
batchSubmitButton?.textContent
]).toEqual([
"导出CSV",
"导出选中达人数据",
"按星图ID导出",
"选择字段",
"提交批次"
]);
expect(buttons.map((button) => button.style.backgroundColor)).toEqual([
"rgb(127, 29, 45)",
"rgb(127, 29, 45)",
"rgb(127, 29, 45)",
"rgb(127, 29, 45)",
expect([
exportButton?.style.backgroundColor,
audienceProfileExportButton?.style.backgroundColor,
audienceProfileByIdExportButton?.style.backgroundColor,
audienceProfileFieldButton?.style.backgroundColor,
batchSubmitButton?.style.backgroundColor
]).toEqual([
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",
"rgb(255, 255, 255)",
"rgb(127, 29, 45)"
]);
expect([
exportButton?.style.color,
audienceProfileExportButton?.style.color,
audienceProfileByIdExportButton?.style.color,
audienceProfileFieldButton?.style.color,
batchSubmitButton?.style.color
]).toEqual([
"rgb(52, 64, 84)",
"rgb(52, 64, 84)",
"rgb(52, 64, 84)",
"rgb(52, 64, 84)",
"rgb(255, 255, 255)"
]);
});
test("remounts the plugin action bar when the native market action row appears later", async () => {
@@ -1276,7 +1380,143 @@ describe("market-content-entry", () => {
expect(customPagesInput?.hidden).toBe(false);
});
test("toolbar exposes independent metric rules and applies personal-video constraints per rule", async () => {
test("removes the toolbar resize listener after its root is detached", async () => {
document.body.innerHTML = buildMarketFixture();
const addEventListener = vi.spyOn(window, "addEventListener");
const removeEventListener = vi.spyOn(window, "removeEventListener");
try {
const { ensurePluginToolbar, isPluginToolbarMounted } = await import(
"../src/content/market/plugin-toolbar"
);
const toolbar = ensurePluginToolbar(document, createNoopToolbarHandlers());
const resizeHandler = addEventListener.mock.calls.find(
([eventName]) => eventName === "resize"
)?.[1];
toolbar.root.remove();
expect(isPluginToolbarMounted(toolbar.root, document)).toBe(false);
expect(removeEventListener).toHaveBeenCalledWith("resize", resizeHandler);
} finally {
addEventListener.mockRestore();
removeEventListener.mockRestore();
}
});
test("moves the toolbar resize listener when its root is adopted by another window", async () => {
document.body.innerHTML = buildMarketFixture();
const addEventListener = vi.spyOn(window, "addEventListener");
const removeEventListener = vi.spyOn(window, "removeEventListener");
const alternateAddEventListener = vi.fn();
const alternateRemoveEventListener = vi.fn();
const alternateWindow = {
addEventListener: alternateAddEventListener,
removeEventListener: alternateRemoveEventListener
} as unknown as Window;
const alternateDocument = {
createTreeWalker: document.createTreeWalker.bind(document),
defaultView: alternateWindow
} as unknown as Document;
try {
const { ensurePluginToolbar, isPluginToolbarMounted } = await import(
"../src/content/market/plugin-toolbar"
);
const handlers = createNoopToolbarHandlers();
const toolbar = ensurePluginToolbar(document, handlers);
const resizeHandler = addEventListener.mock.calls.find(
([eventName]) => eventName === "resize"
)?.[1];
const originalOwnerDocument = toolbar.root.ownerDocument;
Object.defineProperty(toolbar.root, "ownerDocument", {
configurable: true,
value: alternateDocument
});
const reusedToolbar = ensurePluginToolbar(document, handlers);
expect(reusedToolbar.root).toBe(toolbar.root);
expect(removeEventListener).toHaveBeenCalledWith("resize", resizeHandler);
expect(alternateAddEventListener).toHaveBeenCalledWith("resize", resizeHandler);
Object.defineProperty(toolbar.root, "ownerDocument", {
configurable: true,
value: originalOwnerDocument
});
toolbar.root.remove();
isPluginToolbarMounted(toolbar.root, document);
} finally {
addEventListener.mockRestore();
removeEventListener.mockRestore();
}
});
test("removes the toolbar resize listener when its root has no owner window", async () => {
document.body.innerHTML = buildMarketFixture();
const addEventListener = vi.spyOn(window, "addEventListener");
const removeEventListener = vi.spyOn(window, "removeEventListener");
let root: HTMLElement | null = null;
let originalOwnerDocument: Document | null = null;
let isPluginToolbarMounted:
| ((toolbarRoot: HTMLElement, toolbarDocument: Document) => boolean)
| undefined;
try {
const toolbarModule = await import("../src/content/market/plugin-toolbar");
isPluginToolbarMounted = toolbarModule.isPluginToolbarMounted;
const handlers = createNoopToolbarHandlers();
const toolbar = toolbarModule.ensurePluginToolbar(document, handlers);
root = toolbar.root;
originalOwnerDocument = root.ownerDocument;
const resizeHandler = addEventListener.mock.calls.find(
([eventName]) => eventName === "resize"
)?.[1];
Object.defineProperty(root, "ownerDocument", {
configurable: true,
value: {
createTreeWalker: document.createTreeWalker.bind(document),
defaultView: null
} as unknown as Document
});
toolbarModule.ensurePluginToolbar(document, handlers);
expect(removeEventListener).toHaveBeenCalledWith("resize", resizeHandler);
expect(addEventListener).toHaveBeenCalledTimes(1);
Object.defineProperty(root, "ownerDocument", {
configurable: true,
value: originalOwnerDocument
});
toolbarModule.ensurePluginToolbar(document, handlers);
const resizeCalls = addEventListener.mock.calls.filter(
([eventName]) => eventName === "resize"
);
expect(resizeCalls).toHaveLength(2);
expect(resizeCalls[1]).toEqual(["resize", expect.any(Function)]);
toolbarModule.ensurePluginToolbar(document, handlers);
expect(
addEventListener.mock.calls.filter(
([eventName]) => eventName === "resize"
)
).toHaveLength(2);
} finally {
if (root && originalOwnerDocument) {
Object.defineProperty(root, "ownerDocument", {
configurable: true,
value: originalOwnerDocument
});
root.remove();
isPluginToolbarMounted?.(root, document);
}
addEventListener.mockRestore();
removeEventListener.mockRestore();
}
});
test("catalog starts with the default finish-rate rule and adds independent metrics", async () => {
document.body.innerHTML = buildMarketFixture();
const { createMarketController } = await import("../src/content/market/index");
@@ -1291,12 +1531,12 @@ describe("market-content-entry", () => {
await controller.ready;
const finishToggle = document.querySelector(
'[data-plugin-spread-metric="finishRate"]'
) as HTMLInputElement | null;
const interactionToggle = document.querySelector(
'[data-plugin-spread-metric="interactionRate"]'
) as HTMLInputElement | null;
const catalogTrigger = document.querySelector(
'[data-plugin-spread-metric-catalog-trigger="button"]'
) as HTMLButtonElement | null;
const catalogPanel = document.querySelector(
'[data-plugin-spread-metric-catalog-panel="root"]'
) as HTMLElement | null;
const finishRule = document.querySelector(
'[data-plugin-spread-rule="finishRate"]'
) as HTMLElement | null;
@@ -1307,14 +1547,22 @@ describe("market-content-entry", () => {
'[data-plugin-spread-threshold="finishRate"]'
) as HTMLInputElement | null;
expect(finishToggle?.checked).toBe(false);
expect(interactionToggle?.checked).toBe(false);
expect(finishRule?.hidden).toBe(true);
expect(catalogPanel?.hidden).toBe(true);
expect(finishRule?.hidden).toBe(false);
expect(interactionRule?.hidden).toBe(true);
expect(finishRateInput?.placeholder).toBe("");
expect(
document.querySelector('[data-plugin-spread-metric-selected-count="text"]')
?.textContent
).toBe("已选 1 项");
expect(readSpreadRuleSelect("finishRate", "type").value).toBe("2");
expect(readSpreadRuleSelect("finishRate", "onlyAssign").value).toBe("true");
expect(readSpreadRuleSelect("finishRate", "flowType").value).toBe("0");
expect(readSpreadRuleSelect("finishRate", "range").value).toBe("2");
enableSpreadMetric("finishRate");
enableSpreadMetric("interactionRate");
catalogTrigger?.click();
expect(catalogPanel?.hidden).toBe(false);
click('[data-plugin-spread-metric-catalog-action="interactionRate"]');
setSpreadRuleSelect("finishRate", "type", "2");
setSpreadRuleSelect("finishRate", "onlyAssign", "true");
setSpreadRuleSelect("finishRate", "flowType", "1");
@@ -1352,18 +1600,23 @@ describe("market-content-entry", () => {
expect(interactionFlowTypeSelect.disabled).toBe(true);
setInputValue('[data-plugin-spread-threshold="finishRate"]', "30");
if (!finishToggle) {
throw new Error("Missing finish-rate metric toggle");
}
finishToggle.checked = false;
dispatchChange('[data-plugin-spread-metric="finishRate"]');
finishToggle.checked = true;
dispatchChange('[data-plugin-spread-metric="finishRate"]');
click('[data-plugin-spread-rule-remove="finishRate"]');
expect(finishRule?.hidden).toBe(true);
catalogTrigger?.click();
click('[data-plugin-spread-metric-catalog-action="finishRate"]');
expect(finishRateInput?.value).toBe("");
expectSelectValue(
'[data-plugin-spread-rule="finishRate"] [data-plugin-spread-filter="type"]',
"1"
"2"
);
expectSelectValue(
'[data-plugin-spread-rule="finishRate"] [data-plugin-spread-filter="onlyAssign"]',
"true"
);
expectSelectValue(
'[data-plugin-spread-rule="finishRate"] [data-plugin-spread-filter="flowType"]',
"0"
);
expectSelectValue(
'[data-plugin-spread-rule="finishRate"] [data-plugin-spread-filter="range"]',
@@ -1371,6 +1624,128 @@ describe("market-content-entry", () => {
);
});
test("catalog disables duplicate metrics and restores them after deletion", async () => {
document.body.innerHTML = buildMarketFixture();
const { createMarketController } = await import("../src/content/market/index");
const controller = trackController(createMarketController({
document,
loadAuthorMetrics: async () => ({ success: false, reason: "request-failed" }),
window
}));
await controller.ready;
const finishAction = document.querySelector(
'[data-plugin-spread-metric-catalog-action="finishRate"]'
) as HTMLButtonElement | null;
expect(finishAction?.disabled).toBe(true);
expect(finishAction?.textContent).toBe("已添加");
click('[data-plugin-spread-rule-remove="finishRate"]');
expect(finishAction?.disabled).toBe(false);
expect(finishAction?.textContent).toBe("添加");
});
test("catalog search resets its visible metric list after closing", async () => {
document.body.innerHTML = buildMarketFixture();
const { createMarketController } = await import("../src/content/market/index");
const controller = trackController(createMarketController({
document,
loadAuthorMetrics: async () => ({ success: false, reason: "request-failed" }),
window
}));
await controller.ready;
click('[data-plugin-spread-metric-catalog-trigger="button"]');
const searchInput = document.querySelector(
'[data-plugin-spread-metric-catalog-search="input"]'
) as HTMLInputElement | null;
const interactionItem = document.querySelector(
'[data-plugin-spread-metric-catalog-item="interactionRate"]'
) as HTMLElement | null;
if (!searchInput) {
throw new Error("Missing metric catalog search input");
}
searchInput.value = "完播";
searchInput.dispatchEvent(new Event("input"));
expect(interactionItem?.hidden).toBe(true);
click('[data-plugin-spread-metric-catalog-close="button"]');
click('[data-plugin-spread-metric-catalog-trigger="button"]');
expect(interactionItem?.hidden).toBe(false);
});
test("busy toolbar disables catalog and selected rule actions", async () => {
document.body.innerHTML = buildMarketFixture();
const { createMarketController } = await import("../src/content/market/index");
const controller = trackController(createMarketController({
document,
loadAuthorMetrics: async () => ({ success: false, reason: "request-failed" }),
window
}));
await controller.ready;
const { ensurePluginToolbar, setToolbarBusyState } = await import(
"../src/content/market/plugin-toolbar"
);
const toolbar = ensurePluginToolbar(document, createNoopToolbarHandlers());
setToolbarBusyState(toolbar, true);
expect(
(document.querySelector(
'[data-plugin-spread-metric-catalog-trigger="button"]'
) as HTMLButtonElement | null)?.disabled
).toBe(true);
expect(
(document.querySelector(
'[data-plugin-spread-metric-catalog-action="interactionRate"]'
) as HTMLButtonElement | null)?.disabled
).toBe(true);
expect(
(document.querySelector(
'[data-plugin-spread-rule-remove="finishRate"]'
) as HTMLButtonElement | null)?.disabled
).toBe(true);
});
test("uses a collapsible catalog drawer and secondary rule controls on narrow screens", async () => {
const originalInnerWidth = window.innerWidth;
Object.defineProperty(window, "innerWidth", { configurable: true, value: 640 });
document.body.innerHTML = buildMarketFixture();
const { createMarketController } = await import("../src/content/market/index");
const controller = trackController(createMarketController({
document,
loadAuthorMetrics: async () => ({ success: false, reason: "request-failed" }),
window
}));
await controller.ready;
window.dispatchEvent(new Event("resize"));
click('[data-plugin-spread-metric-catalog-trigger="button"]');
const catalogPanel = document.querySelector(
'[data-plugin-spread-metric-catalog-panel="root"]'
) as HTMLElement | null;
expect(catalogPanel?.hidden).toBe(false);
const secondaryControls = document.querySelector(
'[data-plugin-spread-rule-secondary="finishRate"]'
) as HTMLElement | null;
expect(secondaryControls?.hidden).toBe(true);
click('[data-plugin-spread-rule-details="finishRate"]');
expect(secondaryControls?.hidden).toBe(false);
click('[data-plugin-spread-metric-catalog-trigger="button"]');
click('[data-plugin-spread-metric-catalog-close="button"]');
expect(catalogPanel?.hidden).toBe(true);
Object.defineProperty(window, "innerWidth", {
configurable: true,
value: originalInnerWidth
});
window.dispatchEvent(new Event("resize"));
});
test("reads selected spread metrics as independent validated rules", async () => {
document.body.innerHTML = buildMarketFixture();
@@ -1390,29 +1765,34 @@ describe("market-content-entry", () => {
);
const toolbar = ensurePluginToolbar(document, createNoopToolbarHandlers());
expect(readToolbarSpreadFilter(toolbar)).toEqual({
filter: { rules: [] }
});
enableSpreadMetric("finishRate");
expect(readToolbarSpreadFilter(toolbar)).toEqual({
error: "请输入有效的完播率筛选阈值"
});
setInputValue('[data-plugin-spread-threshold="finishRate"]', "30");
enableSpreadMetric("interactionRate");
click('[data-plugin-spread-metric-catalog-trigger="button"]');
click('[data-plugin-spread-metric-catalog-action="interactionRate"]');
expect(readSpreadRuleSelect("interactionRate", "type").value).toBe("2");
expect(readSpreadRuleSelect("interactionRate", "onlyAssign").value).toBe("true");
expect(readSpreadRuleSelect("interactionRate", "flowType").value).toBe("0");
expect(readSpreadRuleSelect("interactionRate", "range").value).toBe("2");
expect(
(document.querySelector(
'[data-plugin-spread-threshold="interactionRate"]'
) as HTMLInputElement | null)?.value
).toBe("");
expect(readToolbarSpreadFilter(toolbar)).toEqual({
error: "请输入有效的互动率筛选阈值"
});
setInputValue('[data-plugin-spread-threshold="interactionRate"]', "5");
setSpreadRuleSelect("finishRate", "type", "2");
setSpreadRuleSelect("finishRate", "onlyAssign", "true");
setSpreadRuleSelect("finishRate", "flowType", "1");
setSpreadRuleSelect("interactionRate", "range", "3");
expect(readToolbarSpreadFilter(toolbar)).toEqual({
filter: {
rules: [
{
config: {
flowType: 1,
flowType: 0,
onlyAssign: true,
range: 2,
type: 2
@@ -1423,9 +1803,9 @@ describe("market-content-entry", () => {
{
config: {
flowType: 0,
onlyAssign: false,
range: 3,
type: 1
onlyAssign: true,
range: 2,
type: 2
},
metric: "interactionRate",
threshold: 5
@@ -1467,6 +1847,7 @@ describe("market-content-entry", () => {
await flush();
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 80, 50);
@@ -1525,6 +1906,7 @@ describe("market-content-entry", () => {
await controller.ready;
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 80, 50);
@@ -1605,6 +1987,7 @@ describe("market-content-entry", () => {
setInputValue('[data-plugin-spread-threshold="interactionRate"]', "5");
setSpreadRuleSelect("finishRate", "type", "2");
setSpreadRuleSelect("finishRate", "onlyAssign", "true");
setSpreadRuleSelect("interactionRate", "type", "1");
setSpreadRuleSelect("interactionRate", "range", "3");
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 80, 50);
@@ -1672,9 +2055,9 @@ describe("market-content-entry", () => {
expect(loadSpreadFilterMetrics).toHaveBeenCalledTimes(1);
expect(loadSpreadFilterMetrics).toHaveBeenCalledWith("spread-a", {
flowType: 0,
onlyAssign: false,
onlyAssign: true,
range: 2,
type: 1
type: 2
});
expect(buildCsv.mock.calls[0][0].map((record) => record.authorId)).toEqual([
"a"
@@ -1726,9 +2109,9 @@ describe("market-content-entry", () => {
expect(loadSpreadFilterMetrics).toHaveBeenCalledTimes(1);
expect(loadSpreadFilterMetrics).toHaveBeenCalledWith("spread-b", {
flowType: 0,
onlyAssign: false,
onlyAssign: true,
range: 2,
type: 1
type: 2
});
expect(buildCsv.mock.calls[0][0]).toEqual([]);
});
@@ -1763,6 +2146,7 @@ describe("market-content-entry", () => {
}));
await controller.ready;
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 120, 100);
@@ -1846,6 +2230,7 @@ describe("market-content-entry", () => {
}));
await controller.ready;
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 120, 100);
@@ -1893,6 +2278,7 @@ describe("market-content-entry", () => {
}));
await controller.ready;
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 120, 100);
@@ -1938,6 +2324,7 @@ describe("market-content-entry", () => {
}));
await controller.ready;
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 120, 100);
@@ -1998,6 +2385,7 @@ describe("market-content-entry", () => {
await controller.ready;
setSelectValue('[data-plugin-export-range="select"]', "all");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 120, 100);
@@ -2047,6 +2435,7 @@ describe("market-content-entry", () => {
setSelectValue('[data-plugin-export-range="select"]', "all");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
expectButtonDisabled('[data-plugin-batch-submit="button"]', true);
@@ -2126,6 +2515,7 @@ describe("market-content-entry", () => {
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 40, 50);
@@ -2549,6 +2939,7 @@ describe("market-content-entry", () => {
await controller.ready;
clickSelectionCheckboxForAuthor("111");
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
for (let attempt = 0; attempt < 40; attempt += 1) {
if (
@@ -2623,6 +3014,7 @@ describe("market-content-entry", () => {
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 40, 50);
@@ -2658,6 +3050,7 @@ describe("market-content-entry", () => {
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-batch-submit="button"]');
await waitForMockCall(submitBatch, 40, 50);
@@ -2733,6 +3126,7 @@ describe("market-content-entry", () => {
setInputValue('[data-plugin-spread-threshold="finishRate"]', "30");
setInputValue('[data-plugin-spread-threshold="interactionRate"]', "5");
setSpreadRuleSelect("finishRate", "type", "2");
setSpreadRuleSelect("interactionRate", "type", "1");
setSpreadRuleSelect("interactionRate", "range", "3");
click('[data-plugin-batch-submit="button"]');
await waitForMockCall(submitBatch, 80, 50);
@@ -2765,6 +3159,7 @@ describe("market-content-entry", () => {
}));
await controller.ready;
removeDefaultSpreadMetricFilter();
click('[data-plugin-batch-submit="button"]');
expect(submitBatch).not.toHaveBeenCalled();
@@ -2808,6 +3203,7 @@ describe("market-content-entry", () => {
}));
await controller.ready;
removeDefaultSpreadMetricFilter();
click('[data-plugin-batch-submit="button"]');
click('[data-plugin-batch-name-confirm="button"]');
await flush();
@@ -2842,6 +3238,7 @@ describe("market-content-entry", () => {
}));
await controller.ready;
removeDefaultSpreadMetricFilter();
click('[data-plugin-batch-submit="button"]');
click('[data-plugin-batch-name-cancel="button"]');
await flush();
@@ -2883,6 +3280,7 @@ describe("market-content-entry", () => {
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-batch-submit="button"]');
await waitForMockCall(submitBatch, 40, 50);
@@ -2969,6 +3367,7 @@ describe("market-content-entry", () => {
await controller.ready;
clickSelectionCheckboxForAuthor("111");
removeDefaultSpreadMetricFilter();
click('[data-plugin-batch-submit="button"]');
for (let attempt = 0; attempt < 40; attempt += 1) {
if (
@@ -3088,6 +3487,7 @@ describe("market-content-entry", () => {
const rowSelectionCheckbox = readSelectionCheckboxForAuthor("111");
rowSelectionCheckbox.checked = true;
removeDefaultSpreadMetricFilter();
click('[data-plugin-batch-submit="button"]');
for (let attempt = 0; attempt < 40; attempt += 1) {
if (
@@ -3169,6 +3569,7 @@ describe("market-content-entry", () => {
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-batch-submit="button"]');
await waitForMockCall(submitBatch, 40, 50);
@@ -3257,6 +3658,7 @@ describe("market-content-entry", () => {
await controller.ready;
removeDefaultSpreadMetricFilter();
click('[data-plugin-batch-submit="button"]');
for (let attempt = 0; attempt < 40; attempt += 1) {
if (
@@ -3315,6 +3717,7 @@ describe("market-content-entry", () => {
}));
await controller.ready;
removeDefaultSpreadMetricFilter();
click('[data-plugin-batch-submit="button"]');
await flush();
@@ -3347,6 +3750,7 @@ describe("market-content-entry", () => {
}));
await controller.ready;
removeDefaultSpreadMetricFilter();
click('[data-plugin-batch-submit="button"]');
await flush();
@@ -3391,6 +3795,7 @@ describe("market-content-entry", () => {
personalVideoAfterSearchRate: "8% - 9%"
});
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 40, 50);
@@ -3438,6 +3843,7 @@ describe("market-content-entry", () => {
personalVideoAfterSearchRate: "0.03% - 0.2%"
});
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 40, 50);
@@ -3516,6 +3922,7 @@ describe("market-content-entry", () => {
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 120, 50);
@@ -3627,6 +4034,7 @@ describe("market-content-entry", () => {
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 120, 50);
@@ -3717,6 +4125,7 @@ describe("market-content-entry", () => {
await controller.ready;
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 120, 50);
@@ -3768,6 +4177,7 @@ describe("market-content-entry", () => {
await controller.ready;
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 120, 50);
@@ -3846,6 +4256,7 @@ describe("market-content-entry", () => {
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 120, 50);
@@ -3929,6 +4340,7 @@ describe("market-content-entry", () => {
await controller.ready;
setSelectValue('[data-plugin-export-range="select"]', "current");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 120, 50);
@@ -4045,6 +4457,7 @@ describe("market-content-entry", () => {
await controller.ready;
setSelectValue('[data-plugin-export-range="select"]', "first-5");
dispatchChange('[data-plugin-export-range="select"]');
removeDefaultSpreadMetricFilter();
click('[data-plugin-export="button"]');
await waitForMockCall(buildCsv, 160, 100);
@@ -4089,7 +4502,7 @@ describe("market-content-entry", () => {
])
);
},
20000
30_000
);
test("rehydrates rows when the market list DOM changes", async () => {
@@ -5342,6 +5755,10 @@ function click(selector: string) {
element.click();
}
function removeDefaultSpreadMetricFilter() {
click('[data-plugin-spread-rule-remove="finishRate"]');
}
function enableSpreadMetric(metric: "finishRate" | "interactionRate") {
const selector = `[data-plugin-spread-metric="${metric}"]`;
const input = document.querySelector(selector) as HTMLInputElement | null;