232 lines
7.4 KiB
TypeScript
232 lines
7.4 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
vi.mock('@/utils/hostEnvironment', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('@/utils/hostEnvironment')>()
|
|
return {
|
|
...actual,
|
|
isNativeWechatMiniProgram: () => false
|
|
}
|
|
})
|
|
|
|
import {
|
|
buildThirdPartyMapSearchUri,
|
|
openWechatMiniProgramLocation
|
|
} from '@/services/ThirdPartyMapSearchService'
|
|
|
|
const target = {
|
|
latitude: 22.604321,
|
|
longitude: 114.058765,
|
|
name: '深圳自然博物馆',
|
|
address: '深圳市龙华区民治街道'
|
|
}
|
|
|
|
const createWindow = (overrides: Record<string, unknown> = {}) => ({
|
|
location: { search: '', hash: '' },
|
|
navigator: { userAgent: 'Mozilla/5.0 MicroMessenger/8.0.50' },
|
|
...overrides
|
|
})
|
|
|
|
beforeEach(() => {
|
|
vi.useFakeTimers()
|
|
vi.stubGlobal('document', undefined)
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers()
|
|
vi.unstubAllGlobals()
|
|
})
|
|
|
|
describe('微信小程序 web-view 地图桥接', () => {
|
|
it('桥接延迟注入时快速调用两次也只跳转一次', async () => {
|
|
const navigateTo = vi.fn((options: { success?: () => void }) => options.success?.())
|
|
const testWindow = createWindow()
|
|
vi.stubGlobal('window', testWindow)
|
|
vi.stubGlobal('uni', { showToast: vi.fn() })
|
|
|
|
setTimeout(() => {
|
|
Object.assign(testWindow, {
|
|
__wxjs_environment: 'miniprogram',
|
|
wx: { miniProgram: { navigateTo } }
|
|
})
|
|
}, 100)
|
|
|
|
const first = openWechatMiniProgramLocation(target)
|
|
const second = openWechatMiniProgramLocation(target)
|
|
await vi.advanceTimersByTimeAsync(150)
|
|
|
|
await expect(first).resolves.toEqual({ status: 'opened' })
|
|
await expect(second).resolves.toEqual({ status: 'opened' })
|
|
expect(navigateTo).toHaveBeenCalledTimes(1)
|
|
|
|
const url = navigateTo.mock.calls[0]?.[0].url
|
|
const params = new URLSearchParams(url.split('?')[1])
|
|
expect(Object.fromEntries(params)).toEqual({
|
|
latitude: String(target.latitude),
|
|
longitude: String(target.longitude),
|
|
name: target.name,
|
|
address: target.address
|
|
})
|
|
})
|
|
|
|
it('普通浏览器返回 H5 分支并保留第三方地图 URI', async () => {
|
|
const showToast = vi.fn()
|
|
vi.stubGlobal('window', createWindow({
|
|
navigator: { userAgent: 'Mozilla/5.0 Chrome/126.0' }
|
|
}))
|
|
vi.stubGlobal('uni', { showToast })
|
|
|
|
await expect(openWechatMiniProgramLocation(target)).resolves.toEqual({ status: 'not-embedded' })
|
|
expect(showToast).not.toHaveBeenCalled()
|
|
expect(buildThirdPartyMapSearchUri('amap', {
|
|
keyword: '深圳自然博物馆',
|
|
region: '深圳市'
|
|
})).toContain('https://uri.amap.com/search?')
|
|
})
|
|
|
|
it('显式宿主环境缺少桥接时显示错误提示', async () => {
|
|
const showToast = vi.fn()
|
|
vi.stubGlobal('window', createWindow({
|
|
location: { search: '?embedded=weapp', hash: '' }
|
|
}))
|
|
vi.stubGlobal('uni', { showToast })
|
|
|
|
const result = openWechatMiniProgramLocation(target)
|
|
await vi.advanceTimersByTimeAsync(2600)
|
|
|
|
await expect(result).resolves.toEqual({ status: 'failed', reason: 'bridge-unavailable' })
|
|
expect(showToast).toHaveBeenCalledWith({
|
|
title: '小程序地图桥接加载失败',
|
|
icon: 'none'
|
|
})
|
|
})
|
|
|
|
it('微信 SDK 首次加载失败后再次点击会重新创建脚本', async () => {
|
|
const showToast = vi.fn()
|
|
let currentScript: Record<string, unknown> | null = null
|
|
const appendChild = vi.fn((script: Record<string, unknown>) => {
|
|
currentScript = script
|
|
setTimeout(() => (script.onerror as (() => void) | undefined)?.(), 0)
|
|
})
|
|
vi.stubGlobal('window', createWindow({
|
|
location: { search: '?embedded=weapp', hash: '' }
|
|
}))
|
|
vi.stubGlobal('document', {
|
|
querySelector: () => currentScript,
|
|
createElement: () => {
|
|
const script: Record<string, unknown> = { dataset: {} }
|
|
script.remove = () => {
|
|
if (currentScript === script) currentScript = null
|
|
}
|
|
return script
|
|
},
|
|
head: { appendChild }
|
|
})
|
|
vi.stubGlobal('uni', { showToast })
|
|
|
|
const first = openWechatMiniProgramLocation(target)
|
|
await vi.advanceTimersByTimeAsync(2600)
|
|
await expect(first).resolves.toEqual({ status: 'failed', reason: 'bridge-unavailable' })
|
|
|
|
const second = openWechatMiniProgramLocation(target)
|
|
await vi.advanceTimersByTimeAsync(2600)
|
|
await expect(second).resolves.toEqual({ status: 'failed', reason: 'bridge-unavailable' })
|
|
expect(appendChild).toHaveBeenCalledTimes(2)
|
|
})
|
|
|
|
it('宿主页跳转失败时给出缺页提示', async () => {
|
|
const showToast = vi.fn()
|
|
const navigateTo = vi.fn((options: { fail?: (error: unknown) => void }) => {
|
|
options.fail?.({ errMsg: 'navigateTo:fail page not found' })
|
|
})
|
|
vi.stubGlobal('window', createWindow({
|
|
location: { search: '?embedded=weapp', hash: '' },
|
|
wx: { miniProgram: { navigateTo } }
|
|
}))
|
|
vi.stubGlobal('uni', { showToast })
|
|
|
|
await expect(openWechatMiniProgramLocation(target)).resolves.toEqual({
|
|
status: 'failed',
|
|
reason: 'navigation-failed'
|
|
})
|
|
expect(showToast).toHaveBeenCalledWith({
|
|
title: '宿主小程序未配置地图页',
|
|
icon: 'none'
|
|
})
|
|
})
|
|
|
|
it('宿主页没有回调时超时返回并显示提示', async () => {
|
|
const showToast = vi.fn()
|
|
let navigateOptions: { fail?: (error: unknown) => void } | undefined
|
|
vi.stubGlobal('window', createWindow({
|
|
location: { search: '?embedded=weapp', hash: '' },
|
|
wx: {
|
|
miniProgram: {
|
|
navigateTo: vi.fn((options: { fail?: (error: unknown) => void }) => {
|
|
navigateOptions = options
|
|
})
|
|
}
|
|
}
|
|
}))
|
|
vi.stubGlobal('uni', { showToast })
|
|
|
|
const result = openWechatMiniProgramLocation(target)
|
|
await vi.advanceTimersByTimeAsync(3100)
|
|
|
|
await expect(result).resolves.toEqual({ status: 'failed', reason: 'navigation-failed' })
|
|
expect(showToast).toHaveBeenCalledWith({
|
|
title: '宿主地图页响应超时',
|
|
icon: 'none'
|
|
})
|
|
navigateOptions?.fail?.({ errMsg: 'navigateTo:fail page not found' })
|
|
expect(showToast).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('非缺页类宿主跳转失败使用通用提示', async () => {
|
|
const showToast = vi.fn()
|
|
vi.stubGlobal('window', createWindow({
|
|
location: { search: '?embedded=weapp', hash: '' },
|
|
wx: {
|
|
miniProgram: {
|
|
navigateTo: vi.fn((options: { fail?: (error: unknown) => void }) => {
|
|
options.fail?.({ errMsg: 'navigateTo:fail page stack full' })
|
|
})
|
|
}
|
|
}
|
|
}))
|
|
vi.stubGlobal('uni', { showToast })
|
|
|
|
await expect(openWechatMiniProgramLocation(target)).resolves.toEqual({
|
|
status: 'failed',
|
|
reason: 'navigation-failed'
|
|
})
|
|
expect(showToast).toHaveBeenCalledWith({
|
|
title: '宿主地图页跳转失败',
|
|
icon: 'none'
|
|
})
|
|
})
|
|
|
|
it('宿主桥接同步抛错时显示调用失败提示', async () => {
|
|
const showToast = vi.fn()
|
|
vi.stubGlobal('window', createWindow({
|
|
location: { search: '?embedded=weapp', hash: '' },
|
|
wx: {
|
|
miniProgram: {
|
|
navigateTo: vi.fn(() => {
|
|
throw new Error('bridge unavailable')
|
|
})
|
|
}
|
|
}
|
|
}))
|
|
vi.stubGlobal('uni', { showToast })
|
|
|
|
await expect(openWechatMiniProgramLocation(target)).resolves.toEqual({
|
|
status: 'failed',
|
|
reason: 'navigation-failed'
|
|
})
|
|
expect(showToast).toHaveBeenCalledWith({
|
|
title: '宿主地图桥接调用失败',
|
|
icon: 'none'
|
|
})
|
|
})
|
|
})
|