Files
shanxia2.0/web-admin/scripts/wechat-notice-binding.test.cjs

261 lines
7.7 KiB
JavaScript

const test = require('node:test')
const assert = require('node:assert/strict')
const fs = require('node:fs')
const { createRequire } = require('node:module')
const { resolve } = require('node:path')
const req = createRequire(resolve(__dirname, '../package.json'))
const vue = req('vue')
const ts = req('typescript')
const { parse } = req('@vue/compiler-sfc')
const path = resolve(
__dirname,
'../src/components/core/layouts/art-header-bar/widget/WechatNoticeDialog.vue'
)
const flush = async () => {
await Promise.resolve()
await vue.nextTick()
await Promise.resolve()
}
const deferred = () => {
let resolve, reject
const promise = new Promise((a, b) => {
resolve = a
reject = b
})
return { promise, resolve, reject }
}
function mount(overrides = {}) {
assert.ok(fs.existsSync(path), '微信通知绑定弹窗尚未实现')
const source = parse(fs.readFileSync(path, 'utf8')).descriptor.scriptSetup.content
const js = ts
.transpileModule(source, {
compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext },
transformers: {
before: [
(context) => (node) =>
ts.visitNode(node, function visit(n) {
if (ts.isImportDeclaration(n)) return undefined
return ts.visitEachChild(n, visit, context)
})
]
}
})
.outputText.replace(/export \{\};?/g, '')
.replace(/Object.defineProperty\(exports,[^;]+;/g, '')
.replace(/exports\.__esModule[^;]+;/g, '')
const props = vue.reactive({ visible: true })
const timers = new Map()
let timerId = 0
let unmount
const env = {
...vue,
defineProps: () => props,
defineEmits: () => (_, v) => {
props.visible = v
},
onBeforeUnmount: (fn) => {
unmount = fn
},
setTimeout: (fn, delay) => {
timers.set(++timerId, { fn, delay })
return timerId
},
clearTimeout: (id) => timers.delete(id),
ElMessage: { success() {} },
ElMessageBox: { confirm: async () => {} },
fetchWechatNoticeBinding: async () => ({ enabled: true, bound: false, subscribed: false }),
startWechatNoticeBinding: async () => ({
sessionId: 's1',
authorizeUrl: 'https://open.weixin.qq.com/connect/oauth2/authorize?state=abc',
expiresIn: 60
}),
fetchWechatNoticeBindingSession: async () => ({ status: 'waiting', subscribed: false }),
confirmWechatNoticeBinding: async () => {},
deleteWechatNoticeBinding: async () => {},
...overrides
}
const scope = vue.effectScope()
const state = scope.run(() =>
new Function(
...Object.keys(env),
`${js}\nreturn {binding,session,error,loading,starting,submitting,unbinding,canConfirm,loadBinding,startBinding,confirmBinding,unbind,closeDialog}`
)(...Object.values(env))
)
return {
...state,
props,
timers,
runTimer: (delay) => {
const entry = [...timers].find(([, t]) => t.delay === delay)
assert.ok(entry, `missing timer ${delay}`)
timers.delete(entry[0])
return entry[1].fn()
},
destroy: () => {
unmount()
scope.stop()
}
}
}
test('未启用时不生成二维码', async () => {
let starts = 0
const vm = mount({
fetchWechatNoticeBinding: async () => ({ enabled: false, bound: false, subscribed: false }),
startWechatNoticeBinding: () => {
starts++
}
})
await flush()
await vm.startBinding()
assert.equal(starts, 0)
vm.destroy()
})
test('轮询串行,未关注的扫码不能确认绑定', async () => {
const pending = deferred()
const vm = mount({ fetchWechatNoticeBindingSession: () => pending.promise })
await flush()
await vm.startBinding()
const poll = vm.runTimer(3000)
assert.equal(
[...vm.timers.values()].some((t) => t.delay === 3000),
false
)
pending.resolve({ status: 'scanned', subscribed: false, verificationCode: '123456' })
await poll
assert.equal(vm.canConfirm.value, false)
assert.equal(
[...vm.timers.values()].some((t) => t.delay === 3000),
false
)
vm.destroy()
})
test('核对已关注微信的验证码后允许确认,成功后重新读取绑定', async () => {
let confirmed = 0
const vm = mount({
fetchWechatNoticeBinding: async () => ({
enabled: true,
bound: confirmed > 0,
subscribed: confirmed > 0
}),
fetchWechatNoticeBindingSession: async () => ({
status: 'scanned',
subscribed: true,
verificationCode: '123456'
}),
confirmWechatNoticeBinding: async (id) => {
assert.equal(id, 's1')
confirmed++
}
})
await flush()
await vm.startBinding()
await vm.runTimer(3000)
assert.equal(vm.canConfirm.value, true)
await vm.confirmBinding()
assert.equal(confirmed, 1)
assert.equal(vm.binding.value.bound, true)
assert.equal(vm.session.value, null)
vm.destroy()
})
test('关闭后忽略仍在返回的生成二维码请求', async () => {
const pending = deferred()
const vm = mount({ startWechatNoticeBinding: () => pending.promise })
await flush()
const request = vm.startBinding()
vm.closeDialog()
await flush()
pending.resolve({ sessionId: 'old', authorizeUrl: 'https://open.weixin.qq.com', expiresIn: 60 })
await request
assert.equal(vm.session.value, null)
assert.equal(vm.timers.size, 0)
vm.destroy()
})
test('关闭并重开后,旧轮询结果不能污染新弹窗', async () => {
const pending = deferred()
const vm = mount({ fetchWechatNoticeBindingSession: () => pending.promise })
await flush()
await vm.startBinding()
const poll = vm.runTimer(3000)
vm.closeDialog()
await flush()
vm.props.visible = true
await flush()
pending.resolve({ status: 'scanned', subscribed: true, verificationCode: 'old' })
await poll
assert.equal(vm.session.value, null)
assert.equal(vm.canConfirm.value, false)
vm.destroy()
})
test('过期后停止轮询,不能提交确认', async () => {
let confirms = 0
const vm = mount({
confirmWechatNoticeBinding: () => {
confirms++
}
})
await flush()
await vm.startBinding()
vm.runTimer(60000)
assert.equal(vm.session.value.status, 'expired')
assert.equal(vm.timers.size, 0)
await vm.confirmBinding()
assert.equal(confirms, 0)
vm.destroy()
})
test('轮询失败显示可恢复提示并停止轮询', async () => {
const vm = mount({
fetchWechatNoticeBindingSession: async () => {
throw new Error('network')
}
})
await flush()
await vm.startBinding()
await vm.runTimer(3000)
assert.ok(vm.error.value)
assert.equal(
[...vm.timers.values()].some((t) => t.delay === 3000),
false
)
assert.equal(vm.canConfirm.value, false)
vm.destroy()
})
test('确认解绑后忽略此前仍在返回的扫码轮询', async () => {
const pendingPoll = deferred()
const pendingDelete = deferred()
const vm = mount({
fetchWechatNoticeBinding: async () => ({ enabled: true, bound: true, subscribed: true }),
fetchWechatNoticeBindingSession: () => pendingPoll.promise,
deleteWechatNoticeBinding: () => pendingDelete.promise
})
await flush()
await vm.startBinding()
const poll = vm.runTimer(3000)
const deletion = vm.unbind()
await flush()
pendingPoll.resolve({ status: 'scanned', subscribed: true, verificationCode: 'stale' })
await poll
assert.equal(vm.session.value, null)
pendingDelete.resolve()
await deletion
vm.destroy()
})
test('取消解绑不发送删除请求', async () => {
let deletes = 0
const vm = mount({
fetchWechatNoticeBinding: async () => ({ enabled: true, bound: true, subscribed: true }),
ElMessageBox: {
confirm: async () => {
throw 'cancel'
}
},
deleteWechatNoticeBinding: async () => {
deletes++
}
})
await flush()
await vm.unbind()
assert.equal(deletes, 0)
assert.equal(vm.unbinding.value, false)
vm.destroy()
})