| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- 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()
- })
|