11
build / task (16.x, npm, macOS-latest) (push) Canceled after 0s
build / task (16.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, npm, windows-latest) (push) Canceled after 0s
build / task (16.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (16.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (16.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (16.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, yarn, windows-latest) (push) Canceled after 0s
build / task (18.x, npm, macOS-latest) (push) Canceled after 0s
build / task (18.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, npm, windows-latest) (push) Canceled after 0s
build / task (18.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (18.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (18.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (18.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, yarn, windows-latest) (push) Canceled after 0s
build / task (20.x, npm, macOS-latest) (push) Canceled after 0s
build / task (20.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, npm, windows-latest) (push) Canceled after 0s
build / task (20.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (20.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (20.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (20.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, yarn, windows-latest) (push) Canceled after 0s
build / task (16.x, npm, macOS-latest) (push) Canceled after 0s
build / task (16.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, npm, windows-latest) (push) Canceled after 0s
build / task (16.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (16.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (16.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (16.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, yarn, windows-latest) (push) Canceled after 0s
build / task (18.x, npm, macOS-latest) (push) Canceled after 0s
build / task (18.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, npm, windows-latest) (push) Canceled after 0s
build / task (18.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (18.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (18.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (18.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, yarn, windows-latest) (push) Canceled after 0s
build / task (20.x, npm, macOS-latest) (push) Canceled after 0s
build / task (20.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, npm, windows-latest) (push) Canceled after 0s
build / task (20.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (20.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (20.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (20.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, yarn, windows-latest) (push) Canceled after 0s
This commit is contained in:
1 parent
530cfcd939
commit
aaeb519bbc
7 files changed
+1070
-199
No files matched your search
@@ -0,0 +1,121 @@
|
||||
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 { parse, compileScript } = require("@vue/compiler-sfc")
|
||||
const vue = require("vue")
|
||||
|
||||
// 执行页面真实的 setup 和 Vue 挂载/异步组件生命周期,子表只记录请求参数,无需新增浏览器测试依赖。
|
||||
const filename = path.join(__dirname, "../src/views/hard/hard-sh/index.vue")
|
||||
const descriptor = parse(fs.readFileSync(filename, "utf8"), { filename }).descriptor
|
||||
const source = compileScript(descriptor, { id: "hard-sh-lifecycle-test" }).content
|
||||
const compiled = ts.transpileModule(source, {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, esModuleInterop: true },
|
||||
}).outputText
|
||||
|
||||
function mountPage() {
|
||||
const requests = []
|
||||
const child = (kind) => vue.defineComponent({
|
||||
setup(_, { expose }) {
|
||||
const query = (params) => requests.push({ kind, params: JSON.parse(JSON.stringify(params)) })
|
||||
expose({ doSearch: query, doCancel: query })
|
||||
return () => vue.h("table")
|
||||
},
|
||||
})
|
||||
const day = child("day")
|
||||
const month = child("month")
|
||||
const stubRequire = (id) => {
|
||||
if (id.includes("hard-sh-by-day.vue")) return day
|
||||
if (id.includes("hard-sh-by-month.vue")) return month
|
||||
if (id.endsWith("AsyncTaskProgress.vue")) return {}
|
||||
if (id.includes("hooks/batchExport")) return {
|
||||
useBatchExport: () => ({ exportTaskConfig: {}, submitExport() {}, handleExportSuccess() {}, handleExportFailure() {} }),
|
||||
}
|
||||
if (id.includes("api/hard/hard-total")) return {}
|
||||
return require(id)
|
||||
}
|
||||
const loaded = { exports: {} }
|
||||
const autoImports = ["ref", "reactive", "watch", "defineAsyncComponent", "onMounted", "nextTick"]
|
||||
new Function("module", "exports", "require", ...autoImports, compiled)(
|
||||
loaded, loaded.exports, stubRequire, ...autoImports.map((key) => vue[key]),
|
||||
)
|
||||
let bindings
|
||||
const renderer = vue.createRenderer({
|
||||
createElement: (type) => ({ type, children: [] }),
|
||||
createText: (text) => ({ text }), createComment: (text) => ({ text }),
|
||||
setText: (node, text) => { node.text = text }, setElementText: (node, text) => { node.text = text },
|
||||
parentNode: (node) => node.parent,
|
||||
nextSibling: (node) => node.parent?.children[node.parent.children.indexOf(node) + 1],
|
||||
patchProp() {},
|
||||
insert(node, parent, anchor = null) {
|
||||
if (node.parent) node.parent.children.splice(node.parent.children.indexOf(node), 1)
|
||||
node.parent = parent
|
||||
const index = anchor ? parent.children.indexOf(anchor) : -1
|
||||
parent.children.splice(index < 0 ? parent.children.length : index, 0, node)
|
||||
},
|
||||
remove(node) {
|
||||
if (node.parent) node.parent.children.splice(node.parent.children.indexOf(node), 1)
|
||||
},
|
||||
})
|
||||
const host = vue.defineComponent({
|
||||
setup() {
|
||||
bindings = loaded.exports.default.setup({}, { expose() {} })
|
||||
bindings.draggableDepMchTreeRef.value = { resetData() {} }
|
||||
return () => bindings.activeTab.value === "MCH"
|
||||
? vue.h(bindings.HardShByDay, { ref: bindings.hardShByDayRef })
|
||||
: vue.h(bindings.HardShByMonth, { ref: bindings.hardShByMonthRef })
|
||||
},
|
||||
})
|
||||
const app = renderer.createApp(host)
|
||||
app.mount({ children: [] })
|
||||
return { bindings, requests, close: () => app.unmount() }
|
||||
}
|
||||
|
||||
async function settle() {
|
||||
await new Promise((resolve) => setImmediate(resolve))
|
||||
await vue.nextTick()
|
||||
}
|
||||
|
||||
test("初次打开异步子表只查询一次,挂载前点击查询也不会报错", async () => {
|
||||
const page = mountPage()
|
||||
try {
|
||||
assert.doesNotThrow(() => page.bindings.search())
|
||||
await settle()
|
||||
assert.equal(page.requests.length, 1)
|
||||
assert.equal(page.requests[0].kind, "day")
|
||||
assert.equal(page.requests[0].params.payTimeList.length, 2)
|
||||
assert.equal(page.requests[0].params.payTimeYearStr, "")
|
||||
} finally { page.close() }
|
||||
})
|
||||
|
||||
test("切换日/月子表每次只查询一次,查询条件重置后再发送", async () => {
|
||||
const page = mountPage()
|
||||
try {
|
||||
await settle()
|
||||
page.bindings.state.queryForm.merchantId = "999"
|
||||
page.bindings.activeTab.value = "MONTH"
|
||||
await settle()
|
||||
assert.equal(page.requests.length, 2)
|
||||
assert.equal(page.requests[1].kind, "month")
|
||||
assert.equal(page.requests[1].params.merchantId, undefined)
|
||||
assert.match(page.requests[1].params.payTimeYearStr, /^\d{4}$/)
|
||||
page.bindings.activeTab.value = "MCH"
|
||||
await settle()
|
||||
assert.equal(page.requests.length, 3)
|
||||
assert.equal(page.requests[2].kind, "day")
|
||||
} finally { page.close() }
|
||||
})
|
||||
|
||||
test("部门筛选和手动刷新仍各发出一次请求", async () => {
|
||||
const page = mountPage()
|
||||
try {
|
||||
await settle()
|
||||
page.bindings.handleGetSelectNode({ id: "10" }, [{ id: "11" }], false, "DEPT")
|
||||
assert.equal(page.requests.length, 2)
|
||||
assert.deepEqual(page.requests[1].params.userDeptIds, ["11"])
|
||||
assert.equal(page.requests[1].params.isInclude, true)
|
||||
page.bindings.search()
|
||||
assert.equal(page.requests.length, 3)
|
||||
} finally { page.close() }
|
||||
})
|
||||
@@ -0,0 +1,180 @@
|
||||
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() }
|
||||
})
|
||||
Reference in new issue
Block a user