261 lines
7.7 KiB
JavaScript
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()
|
|
})
|