const test = require("node:test"); const assert = require("node:assert/strict"); const fs = require("node:fs"); const vm = require("node:vm"); function runtime() { const calls = [], notices = []; let confirm = true, failure = false, release; const module = { exports: {} }; vm.runInNewContext( fs.readFileSync("pets-fe/utils/task-completion.js", "utf8"), { module, require: (name) => name.includes("date") ? { localDate: () => "2026-10-05" } : { secure: async (...args) => { calls.push(args); if (release) await release; if (failure) throw new Error("offline"); }, toast: (error) => notices.push(error.message), }, wx: { showModal: async () => ({ confirm }), showToast: (opts) => notices.push(opts.title), vibrateShort() {}, }, }, ); const page = { data: { tasks: [ { id: "a", completed: false }, { id: "b", completed: true }, ], taskBusyIds: [], completingAll: false, }, setData(value) { Object.assign(this.data, value); }, }; return { api: module.exports, page, calls, notices, cancel: () => { confirm = false; }, fail: () => { failure = true; }, hold: (p) => { release = p; }, }; } test("完成即时更新且不能撤销,快速重复点击只发送一次", async () => { const r = runtime(); let release; r.hold( new Promise((resolve) => { release = resolve; }), ); const action = r.api.complete(r.page, r.page.data.tasks[0]); await r.api.complete(r.page, r.page.data.tasks[0]); assert.equal(r.calls.length, 1); release(); await action; assert.equal(r.page.data.tasks[0].completed, true); assert.equal(r.page.data.undoneCount, 0); await r.api.complete(r.page, r.page.data.tasks[0]); assert.equal(r.calls.length, 1); assert.equal(r.calls[0][2].completed, true); }); test("一键完成只发送未完成项,确认取消不发送请求", async () => { const r = runtime(); r.cancel(); await r.api.completeAll(r.page); assert.equal(r.calls.length, 0); assert.equal(r.page.data.completingAll, false); const s = runtime(); await s.api.completeAll(s.page); assert.equal(s.calls[0][0], "/tasks/completions"); assert.equal(JSON.stringify(s.calls[0][2].task_ids), '["a"]'); assert.equal(s.page.data.done, 2); }); test("完成请求失败保留待办,释放按钮可重试", async () => { const r = runtime(); r.fail(); await r.api.completeAll(r.page); assert.equal(r.page.data.tasks[0].completed, false); assert.equal(r.page.data.completingAll, false); await r.api.complete(r.page, r.page.data.tasks[0]); assert.equal(r.page.data.taskBusyIds.length, 0); assert.equal(r.page.data.tasks[0].completed, false); });