Files
shanxia2.0/web-admin/scripts/mobile-contract-delivery.test.ts
T

152 lines
5.3 KiB
TypeScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import vm from 'node:vm'
import ts from 'typescript'
// 执行真实交付函数,仅替换浏览器宿主与 HTTP 边界。
const source = readFileSync(
new URL('../src/views/m/crm/contract/print/deliverContractFile.ts', import.meta.url),
'utf8'
)
const code = ts.transpileModule(
source.replace(/^import .*$/gm, '').replace('export async function', 'async function'),
{ compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.None } }
).outputText
function harness({
wechat = true,
marked = false,
env = true,
navigation = 'success',
delayedSdk = false,
pendingBridge = false
} = {}) {
const calls = { saved: 0, uploaded: 0, navigated: [] as string[] }
const events = new EventTarget()
const script = new EventTarget()
const mini = {
getEnv(cb: (value: { miniprogram: boolean }) => void) {
if (pendingBridge) {
// JSSDK 1.6.0:桥接就绪后才读取宿主注入的环境标记。
events.addEventListener('WeixinJSBridgeReady', () => cb({ miniprogram: marked }), {
once: true
})
return
}
setTimeout(() => cb({ miniprogram: env }), 1)
},
navigateTo(options: any) {
calls.navigated.push(options.url)
if (navigation === 'success') options.success()
if (navigation === 'fail') options.fail({ errMsg: 'page not found' })
}
}
const window: any = { wx: delayedSdk ? undefined : { miniProgram: mini } }
const context = vm.createContext({
window,
navigator: { userAgent: wechat ? 'iPhone MicroMessenger/8.0' : 'Safari' },
document: {
querySelector: () => (delayedSdk ? script : null),
addEventListener: events.addEventListener.bind(events),
removeEventListener: events.removeEventListener.bind(events)
},
setTimeout: (fn: () => void, ms: number) => setTimeout(fn, Math.min(ms, 30)),
clearTimeout,
File,
encodeURIComponent,
FileSaver: {
saveAs() {
calls.saved++
}
},
isInMiniProgramWebView: () => marked,
CRM_UPLOAD_BIZ_TYPE: { CONTRACT_ATTACHMENT: 'contract' },
buildAttachmentUploadFormData: (file: File) => file,
fetchUploadAttachment: async () => {
calls.uploaded++
return { data: { fileUrl: 'contracts/a.pdf' } }
},
fetchAttachmentDownloadUrl: async () => ({
data: { downloadUrl: 'https://res.example.com/a.pdf?signature=x' }
})
})
vm.runInContext(code + '\nglobalThis.deliver = deliverContractFile', context)
return {
calls,
deliver: (isActive?: () => boolean) =>
context.deliver(new Blob(['%PDF-1.3']), '测试合同.pdf', isActive),
ready() {
window.wx = { miniProgram: mini }
script.dispatchEvent(new Event('load'))
},
bridgeReady() {
marked = true
events.dispatchEvent(new Event('WeixinJSBridgeReady'))
}
}
}
test('iPhone WeChat without synchronous markers waits for getEnv and opens native document page', async () => {
const h = harness()
assert.equal(await h.deliver(), 'mini-program-requested')
assert.equal(h.calls.saved, 0)
assert.equal(h.calls.uploaded, 1)
assert.match(h.calls.navigated[0], /^\/pages\/common\/contract-document\?url=/)
})
test('ordinary browser still requests a browser download despite a JSSDK shell', async () => {
const h = harness({ wechat: false })
assert.equal(await h.deliver(), 'browser-download')
assert.equal(h.calls.saved, 1)
assert.equal(h.calls.uploaded, 0)
})
test('waits for the real JSSDK ordering: bridge ready then injected environment marker', async () => {
const h = harness({ pendingBridge: true })
const result = h.deliver()
assert.equal(h.calls.saved, 0)
assert.equal(h.calls.uploaded, 0)
h.bridgeReady()
assert.equal(await result, 'mini-program-requested')
assert.equal(h.calls.uploaded, 1)
assert.equal(h.calls.navigated.length, 1)
})
test('waits for an already loading JSSDK before delivering', async () => {
const h = harness({ delayedSdk: true })
const result = h.deliver()
h.ready()
assert.equal(await result, 'mini-program-requested')
assert.equal(h.calls.saved, 0)
})
test('unconfirmed WeChat environment does not silently fall back to browser downloads', async () => {
const h = harness({ env: false })
await assert.rejects(h.deliver(), /小程序/)
assert.equal(h.calls.saved, 0)
assert.equal(h.calls.uploaded, 0)
})
test('missing JSSDK times out without downloading or uploading', async () => {
const h = harness({ delayedSdk: true })
await assert.rejects(h.deliver(), /微信文档接口未就绪/)
h.ready()
assert.equal(h.calls.saved, 0)
assert.equal(h.calls.uploaded, 0)
})
test('closing while environment is pending prevents file upload and navigation', async () => {
const h = harness()
let active = true
const result = h.deliver(() => active)
active = false
assert.equal(await result, undefined)
assert.equal(h.calls.saved, 0)
assert.equal(h.calls.uploaded, 0)
assert.equal(h.calls.navigated.length, 0)
})
for (const navigation of ['fail', 'silent']) {
test(
`native navigation ${navigation} rejects instead of reporting success`,
{ timeout: 200 },
async () => {
const h = harness({ marked: true, navigation })
await assert.rejects(h.deliver(), /文档/)
assert.equal(h.calls.saved, 0)
}
)
}