const { test } = require("node:test") const assert = require("node:assert/strict") const fs = require("node:fs") const path = require("node:path") const ts = require("typescript") const vue = require("vue") const { parse, compileScript, compileTemplate } = require("@vue/compiler-sfc") const root = path.join(__dirname, "../src") function load(source, mocks) { const compiled = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 } }).outputText const mod = { exports: {} } new Function("module", "exports", "require", compiled)(mod, mod.exports, id => id in mocks ? mocks[id] : require(id)) return mod.exports } function mount() { const requests = [] const hook = load(fs.readFileSync(path.join(root, "hooks/walletInactive.ts"), "utf8"), { "/@/api/wallet/wallet-manage": { pageJoinActive: (query, signal) => new Promise((resolve, reject) => requests.push({ query, signal, resolve, reject })) }, }) const renderer = vue.createRenderer({ createElement: () => ({}), createText: () => ({}), createComment: () => ({}), setText() {}, setElementText() {}, patchProp() {}, insert() {}, remove() {}, parentNode: () => null, nextSibling: () => null, }) let state const app = renderer.createApp({ setup() { state = hook.useWalletInactive(); return () => null } }) app.mount({}) return { state, requests, close: () => app.unmount() } } const settle = () => new Promise(resolve => setImmediate(resolve)) const response = (query, records = [], extra = {}) => ({ data: { records: records.map((row, index) => ({ userId: String(index), wallets: [], ...row })), total: records.length, current: query.current, size: query.size, asOfTime: "2026-10-22 12:00:00", cutoffTime: "2026-07-22 12:00:00", unactiveMonthsNumber: query.unactiveMonthsNumber, walletNames: { "1": "饭卡钱包", "2": "消费钱包", "3": "充值钱包" }, ...extra, } }) test("挂载只请求一次,沿用原接口并传递取消信号", async () => { const page = mount() try { assert.equal(page.requests.length, 1) assert.deepEqual(page.requests[0].query, { userId: undefined, unactiveMonthsNumber: 3, deptIds: [], isInclude: true, current: 1, size: 20 }) const requests = [] const api = load(fs.readFileSync(path.join(root, "api/wallet/wallet-manage.ts"), "utf8"), { "/@/utils/request": { default: options => { requests.push(options); return Promise.resolve({}) } }, "/@/api/asyncTask": {}, }) await api.pageJoinActive(page.requests[0].query, page.requests[0].signal) assert.equal(requests[0].url, "/wallet/wallet-manage/page-join-active") assert.equal(requests[0].signal, page.requests[0].signal) assert.deepEqual(requests[0].params, { current: 1, size: 20 }) } finally { page.close() } }) test("同条件在途请求去重,新条件取消旧请求,旧响应不能覆盖结果", async () => { const page = mount() try { page.state.refresh() assert.equal(page.requests.length, 1) page.state.form.userId = "9007199254740993999" page.state.setMonths(6) assert.equal(page.requests.length, 2) assert.equal(page.requests[0].signal.aborted, true) assert.equal(page.requests[1].query.userId, "9007199254740993999") page.requests[1].resolve(response(page.requests[1].query, [{ userId: "new", totalBalance: "0", neverTransacted: true }])) await settle() page.requests[0].resolve(response(page.requests[0].query, [{ userId: "old" }])) await settle() assert.equal(page.state.rows.value[0].userId, "new") assert.equal(page.state.summary.value.balance, 0) assert.equal(page.state.metadata.value.unactiveMonthsNumber, 6) assert.equal(page.state.loading.value, false) } finally { page.close() } }) test("连续部门勾选合并查询且参数冻结,卸载取消计时与请求", async () => { const page = mount() page.state.changeDepartments(["11"], true) page.state.changeDepartments(["11", "12"], true) page.state.changeDepartments(["13"], false) await new Promise(resolve => setTimeout(resolve, 290)) assert.equal(page.requests.length, 2) assert.deepEqual(page.requests[1].query.deptIds, ["13"]) assert.equal(page.requests[1].query.isInclude, false) page.state.form.deptIds.push("14") assert.deepEqual(page.requests[1].query.deptIds, ["13"]) page.state.changeDepartments(["15"], true) page.close() await new Promise(resolve => setTimeout(resolve, 290)) assert.equal(page.requests.length, 2) assert.equal(page.requests[1].signal.aborted, true) }) test("非法月数不请求,重置恢复默认值并阻止旧失败污染新结果", async () => { const page = mount() try { for (const value of [undefined, null, 0, -1, 1.5, 121]) { page.state.form.unactiveMonthsNumber = value await page.state.search() assert.equal(page.requests.length, 1) assert.match(page.state.monthsError.value, /整数/) } page.state.form.userId = "77" page.state.form.deptIds = ["8"] page.state.pagination.current = 9 page.state.reset() assert.equal(page.requests.length, 2) assert.equal(page.requests[1].query.current, 1) assert.equal(page.requests[1].query.unactiveMonthsNumber, 3) assert.equal(page.requests[1].query.userId, undefined) assert.deepEqual(page.requests[1].query.deptIds, []) page.requests[0].reject({ msg: "旧请求失败" }) await settle() assert.equal(page.state.error.value, "") assert.equal(page.state.loading.value, true) page.requests[1].resolve(response(page.requests[1].query)) await settle() assert.equal(page.state.loaded.value, true) assert.equal(page.state.monthsError.value, "") } finally { page.close() } }) test("失败清除旧统计并可重试,真实空结果与失败状态分开", async () => { const page = mount() try { page.requests[0].resolve(response(page.requests[0].query, [{ totalBalance: -50, neverTransacted: false }], { total: 99 })) await settle() assert.equal(page.state.summary.value.balance, -50) page.state.refresh() page.requests[1].reject({ msg: "查询超时" }) await settle() assert.deepEqual(page.state.rows.value, []) assert.equal(page.state.loaded.value, false) assert.equal(page.state.error.value, "查询超时") page.state.refresh() page.requests[2].resolve(response(page.requests[2].query)) await settle() assert.equal(page.state.error.value, "") assert.equal(page.state.loaded.value, true) assert.equal(page.state.pagination.total, 0) } finally { page.close() } }) test("翻页、调整每页人数、服务端纠正页码均不会重复请求", async () => { const page = mount() try { page.requests[0].resolve(response(page.requests[0].query, [], { total: 200 })) await settle() page.state.changePage(3) assert.equal(page.requests[1].query.current, 3) assert.equal(page.state.pagination.total, 200, "在途请求不能清零总数,否则真实分页组件会自动回到第1页") page.state.changeSize(50) assert.equal(page.requests[2].query.current, 1) assert.equal(page.requests[2].query.size, 50) page.requests[2].resolve(response(page.requests[2].query, [], { current: 1, total: 0 })) await settle() page.state.changePage(1) assert.equal(page.requests.length, 3) } finally { page.close() } }) test("页面实际 Vue 模板、脚本与样式可编译", () => { const filename = path.join(root, "views/wallet/wallet-inactive/index.vue") const { descriptor, errors } = parse(fs.readFileSync(filename, "utf8"), { filename }) assert.deepEqual(errors, []) const script = compileScript(descriptor, { id: "wallet-inactive-test" }) assert.deepEqual(compileTemplate({ source: descriptor.template.content, filename, id: "wallet-inactive-test", compilerOptions: { bindingMetadata: script.bindings } }).errors, []) const sass = require("sass") assert.ok(sass.compileString(descriptor.styles[0].content).css.length > 0) }) test("旧接口或不完整响应显示失败,不将缺失余额解释为零", async () => { const page = mount() try { page.requests[0].resolve({ data: { records: [{ userId: "77", balance: 100 }], total: 1, current: 1, size: 20 } }) await settle() assert.equal(page.state.loaded.value, false) assert.match(page.state.error.value, /统计结果不完整/) assert.deepEqual(page.state.rows.value, []) } finally { page.close() } })