Files
koc-loop/tests/interaction-fixes.test.mjs

106 lines
4.2 KiB
JavaScript
Raw Normal View History

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
// 2026-10-08 第二轮交互走查修复:
// 1) 完成态判据统一为「曝光量+阅读量齐即 complete」(原 d7_likes 条件全库无赋值方,永不触发)
// 2) 回收队列头部统计改为「已发布 N / 总数」口径,未回填行不再计入
// 3) 行内更新/补填链接从原生 prompt() 改为弹窗表单,失败保留已输入内容
// 4) 顶栏「获取KOC领取链接」从导航跳转改为直接弹出各任务领取链接列表
test("creator metrics completion follows the exposure+views criterion", async () => {
const sources = await Promise.all([
readFile(new URL("../app/api/partner/route.ts", import.meta.url), "utf8"),
readFile(new URL("../app/api/action/route.ts", import.meta.url), "utf8"),
]);
for (const source of sources) {
assert.match(
source,
/status = CASE WHEN exposure IS NOT NULL AND views IS NOT NULL THEN 'complete' ELSE status END/,
);
}
// 死条件必须清除:d7_likes 只被置 NULL 从不赋值,不能作为完成判据
for (const source of sources) {
assert.doesNotMatch(
source,
/CASE WHEN d7_likes IS NOT NULL THEN 'complete'/,
);
}
});
test("manual metrics refreshes the task status afterwards", async () => {
const source = await readFile(
new URL("../app/api/action/route.ts", import.meta.url),
"utf8",
);
assert.match(source, /import \{ refreshTaskStatus \} from "\.\.\/\.\.\/\.\.\/lib\/task-service";/);
const manual = source.slice(
source.indexOf('body.action === "manual_metrics"'),
source.indexOf('body.action === "bind_wecom_external_id"'),
);
assert.ok(manual.includes("manual_metrics"), "必须截取到 manual_metrics 分支");
assert.match(manual, /SELECT task_id FROM distributions WHERE id = \?/);
assert.match(manual, /refreshTaskStatus\(ownerTask\.task_id, db\)/);
});
test("recovery queue header counts only published rows", async () => {
const source = await readFile(
new URL("../app/admin-app.tsx", import.meta.url),
"utf8",
);
assert.match(
source,
/const publishedCount = taskDistributions\.filter\(/,
);
assert.match(
source,
/已发布 \{publishedCount\} \/ \{taskDistributions\.length\} 篇/,
);
});
test("publish url editing uses a modal instead of window.prompt", async () => {
const source = await readFile(
new URL("../app/admin-app.tsx", import.meta.url),
"utf8",
);
assert.match(source, /const \[publishUrlModal, setPublishUrlModal\]/);
assert.match(source, /onSubmit=\{submitPublishUrl\}/);
assert.match(source, /支持长链、短链或完整分享文案/);
// 提交失败保留弹窗内容(runAction 失败返回 false)
assert.match(source, /if \(updated\) setPublishUrlModal\(null\);/);
// 更新链接流程本身不再用 prompt(剪贴板复制失败兜底的 prompt 保留)
const updateFn = source.slice(
source.indexOf("const updateDistributionPublishUrl"),
source.indexOf("const submitPublishUrl"),
);
assert.ok(updateFn.includes("updateDistributionPublishUrl"), "必须截取到函数体");
assert.doesNotMatch(updateFn, /prompt/);
});
test("claim links button opens a modal listing every task link", async () => {
const source = await readFile(
new URL("../app/admin-app.tsx", import.meta.url),
"utf8",
);
assert.match(source, /const \[claimLinksOpen, setClaimLinksOpen\] = useState\(false\);/);
// 顶栏按钮不再是导航跳转
assert.match(source, /onClick=\{\(\) => setClaimLinksOpen\(true\)\}/);
assert.doesNotMatch(
source,
/onClick=\{\(\) => setActiveNav\("tasks"\)\}\s*\n\s*获取KOC领取链接/,
);
// 弹窗里每个任务给出复制按钮,无链接的任务明示未生成
assert.match(source, /claim-links-list/);
assert.match(source, /KOC领取链接已复制/);
assert.match(source, /尚未生成领取链接/);
// 弹窗内点击(含复制按钮)不能冒泡到 backdrop 导致误关闭
const modal = source.slice(
source.indexOf('className="modal-card compact claim-links-modal"'),
source.indexOf('<div className="claim-links-list">'),
);
assert.match(
modal,
/onMouseDown=\{\(event\) => event\.stopPropagation\(\)\}/,
);
});