import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import test from "node:test"; // 2026-10-08 两端走查发现的 UI 断点修复: // 1) 回填提交在缺发布截图时前置禁用并说明原因(原本只在提交后 toast 报错) // 2) 未回填链接时收起回收区块的说明/示例图,只留锁定提示 // 3) 分发表「数据状态」按任务采集计划显示等待日,没计划时明说待设置 // 4) 截图包回收清单的序号列对无 source_row 的内容回退为行号;种子内容补 source_row test("portal gates the backfill submit button on the publish screenshot", async () => { const source = await readFile( new URL("../koc-portal/app/page.tsx", import.meta.url), "utf8", ); assert.match( source, /disabled=\{working \|\| !\(screenshot \|\| selected\.publish_screenshot_key\)\}/, ); assert.match(source, /先上传发布截图,链接回填需以截图为凭证/); // 服务端/提交守卫仍保留,双保险 assert.match(source, /请上传这篇笔记的发布截图/); }); test("portal collapses recovery instructions until the link is backfilled", async () => { const source = await readFile( new URL("../koc-portal/app/page.tsx", import.meta.url), "utf8", ); const lockedHint = "回填发布链接后,这里会开放回收截图上传。"; assert.ok(source.includes(lockedHint), "锁定提示必须保留"); const tipIndex = source.indexOf( `{selected.publish_url && (\n
`, ); assert.ok(tipIndex > 0, "回收说明必须由 publish_url 门控"); // 示例图区块同样门控 assert.match( source, /\{selected\.publish_url &&\n \(payload\.task\.recoveryExampleImages\?\.length \?\? 0\) > 0 && \(/, ); }); test("admin waiting label follows the task collection plan", async () => { const [adminApp, mvpDb] = await Promise.all([ readFile(new URL("../app/admin-app.tsx", import.meta.url), "utf8"), readFile(new URL("../lib/mvp-db.ts", import.meta.url), "utf8"), ]); assert.match(adminApp, /waitingCollectionLabel/); assert.match(adminApp, /待设置采集计划/); assert.match(adminApp, /等待D\$\{Math\.min\(\.\.\.days\)\}/); assert.doesNotMatch(adminApp, /"等待D2"/); // 分发查询带上任务级采集计划字段 assert.match(mvpDb, /t\.collection_days AS task_collection_days/); assert.match(mvpDb, /t\.collection_start_date AS task_collection_start_date/); }); test("export manifest and seed contents always carry a sequence number", async () => { const [exportRoute, seed] = await Promise.all([ readFile( new URL("../app/api/screenshot-task-export/route.ts", import.meta.url), "utf8", ), readFile(new URL("../lib/mvp-db.ts", import.meta.url), "utf8"), ]); assert.match(exportRoute, /String\(row\.source_row \?\? rowIndex \+ 1\)/); assert.match(seed, /INSERT INTO contents \(id, task_id, title, body, status, source_row\)/); });