| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- import assert from 'node:assert/strict'
- import { readFileSync } from 'node:fs'
- import { createRequire } from 'node:module'
- import test from 'node:test'
- const require = createRequire(new URL('../../web-admin/package.json', import.meta.url))
- const { ref, reactive, computed } = require('vue')
- const { parse, compileTemplate } = require('@vue/compiler-sfc')
- const read = p => readFileSync(new URL(p, import.meta.url), 'utf8')
- const strip = s => s.replace(/^import[^\n]+\n/gm, '').replace(/^export /gm, '')
- const launchState = 'a'.repeat(32)
- const launchUrl = `https://w.shanxiachem.com/api/public/wechat-notices/oauth/launch?state=${launchState}`
- function api(data, statusCode = 200, code = 200) {
- const calls = []
- const source = strip(read('../api/wechat-notices.js'))
- const fns = new Function('request', 'parseResponseJson', 'resolveWebOrigin', 'resolveApiBase', `${source}; return { startWechatBinding, fetchWechatBindingSession, confirmWechatBinding, isWechatAuthorizeUrl }`)(
- async options => { calls.push(options); return { statusCode, data: { code, data, message: '请求失败' } } },
- response => response.data,
- () => 'https://w.shanxiachem.com',
- () => 'https://w.shanxiachem.com/api')
- return { ...fns, calls }
- }
- test('binding API uses owner-authenticated endpoints and only returns necessary fields', async () => {
- const start = api({ sessionId: 'a'.repeat(32), authorizeUrl: launchUrl, openid: 'private' })
- assert.equal((await start.startWechatBinding()).openid, undefined)
- assert.equal(start.calls[0].method, 'POST')
- assert.deepEqual(start.calls[0].data, { mode: 'mobile' })
- assert.equal(start.calls[0].url, '/web/wechat-notices/binding/start')
- const confirm = api({ enabled: true, bound: true, subscribed: true, openid: 'private' })
- assert.deepEqual(await confirm.confirmWechatBinding('session'), { enabled: true, bound: true, subscribed: true })
- assert.deepEqual(confirm.calls[0].data, { sessionId: 'session' })
- const session = api({ status: 'scanned', subscribed: true, verificationCode: '123456' })
- await session.fetchWechatBindingSession('a&b')
- assert.ok(session.calls[0].url.endsWith('sessionId=a%26b'))
- })
- test('binding API rejects foreign URLs, failed and malformed results', async () => {
- const fns = api({})
- for (const url of [
- 'javascript:alert(1)',
- 'http://open.weixin.qq.com/connect/oauth2/authorize?state=x',
- 'https://open.weixin.qq.com/connect/oauth2/authorize?state=' + launchState,
- 'https://evil.test/api/public/wechat-notices/oauth/launch?state=' + launchState,
- launchUrl + '#wechat_redirect'
- ]) {
- assert.equal(fns.isWechatAuthorizeUrl(url), false)
- await assert.rejects(api({ sessionId: 'x'.repeat(32), authorizeUrl: url }).startWechatBinding())
- }
- await assert.rejects(api({}, 403, 403).startWechatBinding())
- await assert.rejects(api({ status: 'scanned', subscribed: true, verificationCode: 'bad' }).fetchWechatBindingSession('session'))
- await assert.rejects(api({ enabled: true, bound: 'yes', subscribed: true }).confirmWechatBinding('session'))
- })
- test('authorization page mounts approved URL after ready and unregisters its channel', () => {
- const hooks = {}, handlers = {}, off = []
- const channel = { on: (name, fn) => { handlers[name] = fn }, off: (...args) => off.push(args) }
- const source = read('../pages/common/wechat-binding-authorize.vue')
- const descriptor = parse(source).descriptor
- const body = strip(descriptor.scriptSetup.content)
- const deps = { ref, getCurrentInstance: () => ({ proxy: { getOpenerEventChannel: () => channel } }),
- onLoad: fn => { hooks.load = fn }, onReady: fn => { hooks.ready = fn }, onUnload: fn => { hooks.unload = fn },
- isWechatAuthorizeUrl: api({}).isWechatAuthorizeUrl }
- const page = new Function(...Object.keys(deps), `${body}; return { src, errorText, onError }`)(...Object.values(deps))
- hooks.load({ url: 'https://evil.test' })
- assert.equal(page.src.value, '')
- const url = launchUrl
- handlers.wechatBindingAuthorize({ authorizeUrl: url })
- assert.equal(page.src.value, '')
- hooks.ready()
- assert.equal(page.src.value, url)
- page.onError()
- assert.match(page.errorText.value, /打开失败/)
- hooks.unload()
- assert.equal(off[0][0], 'wechatBindingAuthorize')
- const compiled = compileTemplate({ source: descriptor.template.content, filename: 'wechat-binding-authorize.vue', id: 'binding' })
- assert.deepEqual(compiled.errors, [])
- })
- test('real page composable returns from OAuth and confirms logged-in account without URL credentials', async () => {
- const flowSource = strip(read('../utils/wechatBindingFlow.js'))
- const flowFns = new Function(`${flowSource}; return { createWechatBindingFlow, initialBindingState }`)()
- const hooks = {}, calls = { routes: [], events: [], prompts: [], confirms: [] }
- const user = { isLoggedIn: true, token: 'private-jwt', userInfo: { realName: '张三' }, checkLoginStatus() {} }
- let bound = false
- const deps = { computed, reactive, onUnmounted: () => {}, onHide: fn => { hooks.hide = fn }, onShow: fn => { hooks.show = fn },
- useUserStore: () => user, PAGE_PATHS: { wechatBindingAuthorize: '/pages/common/wechat-binding-authorize', login: '/pages/login/login' },
- ...flowFns, fetchWechatNoticeStatus: async () => ({ enabled: true, bound, subscribed: bound }),
- startWechatBinding: async () => ({ sessionId: 's'.repeat(32), authorizeUrl: 'https://open.weixin.qq.com/connect/oauth2/authorize?state=nonce' }),
- fetchWechatBindingSession: async () => ({ status: 'scanned', subscribed: true, verificationCode: '654321' }),
- confirmWechatBinding: async id => { calls.confirms.push(id); bound = true; return { enabled: true, bound, subscribed: true } },
- uni: { navigateTo: options => { calls.routes.push(options.url); hooks.hide(); options.success({ eventChannel: { emit: (name, value) => calls.events.push({ name, value }) } }) },
- showModal: options => { calls.prompts.push(options.content); options.success({ confirm: true }) }, reLaunch: () => {} } }
- const use = new Function(...Object.keys(deps), `${strip(read('../utils/useWechatBinding.js'))}; return useWechatBinding`)(...Object.values(deps))
- const page = use()
- hooks.show(); await new Promise(resolve => setImmediate(resolve))
- await page.startBinding()
- assert.deepEqual(calls.routes, ['/pages/common/wechat-binding-authorize'])
- assert.ok(!JSON.stringify(calls.events).includes(user.token))
- hooks.show(); await new Promise(resolve => setImmediate(resolve))
- assert.deepEqual(calls.prompts, [])
- assert.equal(page.bindingState.bound, true)
- assert.deepEqual(calls.confirms, ['s'.repeat(32)])
- })
|