Files
frontend-miniapp/tests/unit/WechatMiniProgramBridgeService.spec.ts
lyf 8b3e3f8d55
Some checks failed
CI / verify (push) Has been cancelled
修复小程序讲解页面返回回退
2026-07-28 10:40:25 +08:00

135 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// @vitest-environment happy-dom
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const target = { latitude: 22.604321, longitude: 114.058765, name: '停车场 A&B', address: '龙华区 #1' }
const setEmbeddedRuntime = () => Object.assign(window, { __wxjs_environment: 'miniprogram' })
const addWechatSdkMarker = () => {
const script = document.createElement('script')
script.dataset.wechatJsSdk = 'true'
document.head.appendChild(script)
}
const loadService = async () => {
vi.resetModules()
return import('@/services/WechatMiniProgramBridgeService')
}
beforeEach(() => {
vi.useFakeTimers()
vi.stubGlobal('uni', { showToast: vi.fn() })
delete (window as Window & { wx?: unknown }).wx
delete (window as Window & { __wxjs_environment?: string }).__wxjs_environment
window.history.replaceState({}, '', '/')
})
afterEach(() => {
vi.useRealTimers()
vi.unstubAllGlobals()
document.querySelectorAll('script[data-wechat-js-sdk="true"]').forEach((node) => node.remove())
})
describe('微信小程序 web-view 桥接', () => {
it('普通浏览器立即返回 not-embedded不加载 SDK', async () => {
const { openWechatMiniProgramLocation } = await loadService()
await expect(openWechatMiniProgramLocation(target)).resolves.toEqual({ status: 'not-embedded' })
expect(document.querySelector('[data-wechat-js-sdk="true"]')).toBeNull()
})
it('等待延迟注入的 bridge 后导航到编码后的宿主页', async () => {
setEmbeddedRuntime()
addWechatSdkMarker()
const { openWechatMiniProgramLocation } = await loadService()
const result = openWechatMiniProgramLocation(target)
await vi.advanceTimersByTimeAsync(100)
const navigateTo = vi.fn(({ success }) => success())
Object.assign(window, { wx: { miniProgram: { getEnv: (done: (value: { miniprogram: boolean }) => void) => done({ miniprogram: true }), navigateTo } } })
await vi.advanceTimersByTimeAsync(100)
await expect(result).resolves.toEqual({ status: 'opened' })
expect(navigateTo).toHaveBeenCalledWith(expect.objectContaining({
url: expect.stringContaining('/pages/open-location/index?latitude=22.604321&longitude=114.058765&name=%E5%81%9C%E8%BD%A6%E5%9C%BA%20A%26B')
}))
})
it('handles getEnv false, navigate fail, page not found, and callback timeout', async () => {
setEmbeddedRuntime()
const { openWechatMiniProgramLocation } = await loadService()
Object.assign(window, { wx: { miniProgram: { getEnv: (done: (value: { miniprogram: boolean }) => void) => done({ miniprogram: false }), navigateTo: vi.fn() } } })
await expect(openWechatMiniProgramLocation(target)).resolves.toEqual({ status: 'failed', reason: 'not-in-mini-program' })
Object.assign(window, { wx: { miniProgram: { getEnv: (done: (value: { miniprogram: boolean }) => void) => done({ miniprogram: true }) } } })
await expect(openWechatMiniProgramLocation(target)).resolves.toEqual({ status: 'failed', reason: 'bridge-unavailable' })
Object.assign(window, { wx: { miniProgram: { getEnv: (done: (value: { miniprogram: boolean }) => void) => done({ miniprogram: true }), navigateTo: vi.fn(({ fail }) => fail({ errMsg: 'navigateTo:fail permission denied' })) } } })
await expect(openWechatMiniProgramLocation(target)).resolves.toEqual({ status: 'failed', reason: 'navigation-failed' })
const navigateTo = vi.fn(({ fail }) => fail({ errMsg: 'navigateTo:fail page not found' }))
Object.assign(window, { wx: { miniProgram: { getEnv: (done: (value: { miniprogram: boolean }) => void) => done({ miniprogram: true }), navigateTo } } })
await expect(openWechatMiniProgramLocation(target)).resolves.toEqual({ status: 'failed', reason: 'page-not-found' })
Object.assign(window, { wx: { miniProgram: { getEnv: (done: (value: { miniprogram: boolean }) => void) => done({ miniprogram: true }), navigateTo: vi.fn() } } })
const timeoutResult = openWechatMiniProgramLocation(target)
await vi.advanceTimersByTimeAsync(3001)
await expect(timeoutResult).resolves.toEqual({ status: 'failed', reason: 'timeout' })
})
it('deduplicates rapid calls while navigation is in flight', async () => {
setEmbeddedRuntime()
const { openWechatMiniProgramLocation } = await loadService()
let succeed: (() => void) | undefined
const navigateTo = vi.fn(({ success }) => { succeed = success })
Object.assign(window, { wx: { miniProgram: { getEnv: (done: (value: { miniprogram: boolean }) => void) => done({ miniprogram: true }), navigateTo } } })
const first = openWechatMiniProgramLocation(target)
expect(openWechatMiniProgramLocation(target)).toBe(first)
await Promise.resolve()
await Promise.resolve()
await Promise.resolve()
expect(navigateTo).toHaveBeenCalledTimes(1)
succeed?.()
await expect(first).resolves.toEqual({ status: 'opened' })
expect(navigateTo).toHaveBeenCalledTimes(1)
})
it('等待延迟 bridge 后返回宿主小程序', async () => {
setEmbeddedRuntime()
addWechatSdkMarker()
const { returnToWechatMiniProgram } = await loadService()
const result = returnToWechatMiniProgram()
await vi.advanceTimersByTimeAsync(100)
const navigateBack = vi.fn(({ success }) => success())
Object.assign(window, {
wx: {
miniProgram: {
getEnv: (done: (value: { miniprogram: boolean }) => void) => done({ miniprogram: true }),
navigateBack
}
}
})
await vi.advanceTimersByTimeAsync(100)
await expect(result).resolves.toEqual({ status: 'returned' })
expect(navigateBack).toHaveBeenCalledWith(expect.objectContaining({ delta: 1 }))
})
it('原生返回失败时委托宿主页执行安全回退', async () => {
setEmbeddedRuntime()
const { MINI_PROGRAM_RETURN_MESSAGE, returnToWechatMiniProgram } = await loadService()
const postMessage = vi.fn()
Object.assign(window, {
wx: {
miniProgram: {
getEnv: (done: (value: { miniprogram: boolean }) => void) => done({ miniprogram: true }),
navigateBack: ({ fail }: { fail?: (error: { errMsg?: string }) => void }) => fail?.({ errMsg: 'navigateBack:fail page stack is empty' }),
postMessage
}
}
})
await expect(returnToWechatMiniProgram()).resolves.toEqual({ status: 'returned' })
expect(postMessage).toHaveBeenCalledWith({
data: { type: MINI_PROGRAM_RETURN_MESSAGE }
})
})
})