@@ -159,7 +159,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||||
import { onHide, onLoad, onUnload } from '@dcloudio/uni-app'
|
import { onBackPress, onHide, onLoad, onUnload } from '@dcloudio/uni-app'
|
||||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||||
import GuideFeedbackState from '@/components/navigation/GuideFeedbackState.vue'
|
import GuideFeedbackState from '@/components/navigation/GuideFeedbackState.vue'
|
||||||
import GuideLoadingState from '@/components/navigation/GuideLoadingState.vue'
|
import GuideLoadingState from '@/components/navigation/GuideLoadingState.vue'
|
||||||
@@ -188,6 +188,7 @@ import {
|
|||||||
} from '@/utils/placeholders'
|
} from '@/utils/placeholders'
|
||||||
import { normalizeSameOriginPublicUrl } from '@/utils/publicUrl'
|
import { normalizeSameOriginPublicUrl } from '@/utils/publicUrl'
|
||||||
import { isEmbeddedInWechatMiniProgram } from '@/utils/hostEnvironment'
|
import { isEmbeddedInWechatMiniProgram } from '@/utils/hostEnvironment'
|
||||||
|
import { returnToWechatMiniProgram } from '@/services/WechatMiniProgramBridgeService'
|
||||||
import { normalizeGuideAudioLanguage } from '@/data/adapters/guideStopInfoAdapter'
|
import { normalizeGuideAudioLanguage } from '@/data/adapters/guideStopInfoAdapter'
|
||||||
import { explainGuideStopListUrl } from '@/utils/explainNavigation'
|
import { explainGuideStopListUrl } from '@/utils/explainNavigation'
|
||||||
import {
|
import {
|
||||||
@@ -355,6 +356,7 @@ const isCurrentDetailAudioTarget = computed(() => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
let detailAudioClosedOnExit = false
|
let detailAudioClosedOnExit = false
|
||||||
|
let returningToMiniProgram = false
|
||||||
|
|
||||||
const closeDetailAudioOnExit = () => {
|
const closeDetailAudioOnExit = () => {
|
||||||
if (detailAudioClosedOnExit || !isCurrentDetailAudioTarget.value) return
|
if (detailAudioClosedOnExit || !isCurrentDetailAudioTarget.value) return
|
||||||
@@ -945,15 +947,42 @@ const fallbackToExplainObjectList = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const returnToExplainObjectList = () => {
|
const cameFromExplainObjectList = () => {
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
const previousPage = pages[pages.length - 2] as { route?: string } | undefined
|
||||||
|
return previousPage?.route === 'pages/explain/guide-stop-list'
|
||||||
|
}
|
||||||
|
|
||||||
|
const returnToExplainObjectList = async () => {
|
||||||
closeDetailAudioOnExit()
|
closeDetailAudioOnExit()
|
||||||
|
if (cameFromExplainObjectList()) {
|
||||||
uni.navigateBack({
|
uni.navigateBack({
|
||||||
delta: 1,
|
delta: 1,
|
||||||
fail: fallbackToExplainObjectList
|
fail: fallbackToExplainObjectList
|
||||||
})
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmbeddedInWechatMiniProgram()) {
|
||||||
|
if (returningToMiniProgram) return
|
||||||
|
returningToMiniProgram = true
|
||||||
|
const result = await returnToWechatMiniProgram()
|
||||||
|
if (result.status === 'returned') return
|
||||||
|
|
||||||
|
returningToMiniProgram = false
|
||||||
|
uni.showToast({ title: '返回小程序失败,请重试', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fallbackToExplainObjectList()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBack = returnToExplainObjectList
|
const handleBack = () => { void returnToExplainObjectList() }
|
||||||
|
|
||||||
|
onBackPress(() => {
|
||||||
|
void returnToExplainObjectList()
|
||||||
|
return true
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -20,13 +20,15 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onUnmounted, ref } from 'vue'
|
import { onUnmounted, ref } from 'vue'
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onBackPress, onLoad } from '@dcloudio/uni-app'
|
||||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||||
import ExplainGuideStopCatalog, { type ExplainGuideStopCatalogItem } from '@/components/explain/ExplainGuideStopCatalog.vue'
|
import ExplainGuideStopCatalog, { type ExplainGuideStopCatalogItem } from '@/components/explain/ExplainGuideStopCatalog.vue'
|
||||||
import { explainUseCase } from '@/usecases/explainUseCase'
|
import { explainUseCase } from '@/usecases/explainUseCase'
|
||||||
import type { ExplainGuideStop } from '@/domain/museum'
|
import type { ExplainGuideStop } from '@/domain/museum'
|
||||||
import { normalizeExplainDetailTargetFromGuideStop } from '@/domain/explainDetailTarget'
|
import { normalizeExplainDetailTargetFromGuideStop } from '@/domain/explainDetailTarget'
|
||||||
import { navigateToGuideTopTab, type GuideTopTab } from '@/utils/guideTopTabs'
|
import { navigateToGuideTopTab, type GuideTopTab } from '@/utils/guideTopTabs'
|
||||||
|
import { isEmbeddedInWechatMiniProgram } from '@/utils/hostEnvironment'
|
||||||
|
import { returnToWechatMiniProgram } from '@/services/WechatMiniProgramBridgeService'
|
||||||
|
|
||||||
const PAGE_SIZE = 20
|
const PAGE_SIZE = 20
|
||||||
const selectedExplainHallId = ref('')
|
const selectedExplainHallId = ref('')
|
||||||
@@ -40,6 +42,7 @@ const hasMore = ref(false)
|
|||||||
const nextPageNo = ref(1)
|
const nextPageNo = ref(1)
|
||||||
let requestVersion = 0
|
let requestVersion = 0
|
||||||
let disposed = false
|
let disposed = false
|
||||||
|
let returningToMiniProgram = false
|
||||||
|
|
||||||
const toGuideStopItems = (stops: ExplainGuideStop[]): ExplainGuideStopCatalogItem[] => stops.map((stop) => ({
|
const toGuideStopItems = (stops: ExplainGuideStop[]): ExplainGuideStopCatalogItem[] => stops.map((stop) => ({
|
||||||
id: stop.id,
|
id: stop.id,
|
||||||
@@ -153,7 +156,7 @@ type ExplainPageStackEntry = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const returnToHallList = () => {
|
const returnToHallList = async () => {
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
const previousPage = pages[pages.length - 2] as ExplainPageStackEntry | undefined
|
const previousPage = pages[pages.length - 2] as ExplainPageStackEntry | undefined
|
||||||
const previousRoute = previousPage?.route
|
const previousRoute = previousPage?.route
|
||||||
@@ -170,11 +173,27 @@ const returnToHallList = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isEmbeddedInWechatMiniProgram()) {
|
||||||
|
if (returningToMiniProgram) return
|
||||||
|
returningToMiniProgram = true
|
||||||
|
const result = await returnToWechatMiniProgram()
|
||||||
|
if (result.status === 'returned') return
|
||||||
|
|
||||||
|
returningToMiniProgram = false
|
||||||
|
uni.showToast({ title: '返回小程序失败,请重试', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
fallbackToHallList()
|
fallbackToHallList()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBack = returnToHallList
|
const handleBack = () => { void returnToHallList() }
|
||||||
const handleTopTabChange = (tab: GuideTopTab) => navigateToGuideTopTab(tab)
|
const handleTopTabChange = (tab: GuideTopTab) => navigateToGuideTopTab(tab)
|
||||||
|
|
||||||
|
onBackPress(() => {
|
||||||
|
void returnToHallList()
|
||||||
|
return true
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ import {
|
|||||||
type GuideTopTab
|
type GuideTopTab
|
||||||
} from '@/utils/guideTopTabs'
|
} from '@/utils/guideTopTabs'
|
||||||
import {
|
import {
|
||||||
isEmbeddedInWechatMiniProgram,
|
isEmbeddedInWechatMiniProgram
|
||||||
navigateBackToWechatMiniProgram
|
|
||||||
} from '@/utils/hostEnvironment'
|
} from '@/utils/hostEnvironment'
|
||||||
|
import { returnToWechatMiniProgram } from '@/services/WechatMiniProgramBridgeService'
|
||||||
import { syncHostNavigationTitle } from '@/utils/hostNavigationTitle'
|
import { syncHostNavigationTitle } from '@/utils/hostNavigationTitle'
|
||||||
import { explainGuideStopListUrl } from '@/utils/explainNavigation'
|
import { explainGuideStopListUrl } from '@/utils/explainNavigation'
|
||||||
|
|
||||||
@@ -162,9 +162,10 @@ const returnToProjectExplainHome = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const returnFromStandaloneMiniProgramEntry = () => {
|
const returnFromStandaloneMiniProgramEntry = async () => {
|
||||||
isReturningFromExplainList = true
|
isReturningFromExplainList = true
|
||||||
if (navigateBackToWechatMiniProgram()) return
|
const result = await returnToWechatMiniProgram()
|
||||||
|
if (result.status === 'returned') return
|
||||||
|
|
||||||
isReturningFromExplainList = false
|
isReturningFromExplainList = false
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@@ -184,7 +185,7 @@ const returnToGuideHome = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldUseHostNavigation.value) {
|
if (shouldUseHostNavigation.value) {
|
||||||
returnFromStandaloneMiniProgramEntry()
|
void returnFromStandaloneMiniProgramEntry()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import {
|
|||||||
interface MiniProgramBridge {
|
interface MiniProgramBridge {
|
||||||
getEnv?: (callback: (result: { miniprogram?: boolean }) => void) => void
|
getEnv?: (callback: (result: { miniprogram?: boolean }) => void) => void
|
||||||
navigateTo?: (options: { url: string; success?: () => void; fail?: (error: { errMsg?: string }) => void }) => void
|
navigateTo?: (options: { url: string; success?: () => void; fail?: (error: { errMsg?: string }) => void }) => void
|
||||||
|
navigateBack?: (options: { delta: number; success?: () => void; fail?: (error: { errMsg?: string }) => void }) => void
|
||||||
|
postMessage?: (options: { data: { type: string } }) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WechatMiniProgramBridgeResult =
|
export type WechatMiniProgramBridgeResult =
|
||||||
@@ -15,13 +17,20 @@ export type WechatMiniProgramBridgeResult =
|
|||||||
| { status: 'not-embedded' }
|
| { status: 'not-embedded' }
|
||||||
| { status: 'failed'; reason: 'invalid-target' | 'bridge-unavailable' | 'not-in-mini-program' | 'navigation-failed' | 'page-not-found' | 'timeout' }
|
| { status: 'failed'; reason: 'invalid-target' | 'bridge-unavailable' | 'not-in-mini-program' | 'navigation-failed' | 'page-not-found' | 'timeout' }
|
||||||
|
|
||||||
|
export type WechatMiniProgramReturnResult =
|
||||||
|
| { status: 'returned' }
|
||||||
|
| { status: 'not-embedded' }
|
||||||
|
| { status: 'failed'; reason: 'bridge-unavailable' | 'not-in-mini-program' | 'navigation-failed' | 'timeout' }
|
||||||
|
|
||||||
const SDK_URL = 'https://res.wx.qq.com/open/js/jweixin-1.3.2.js'
|
const SDK_URL = 'https://res.wx.qq.com/open/js/jweixin-1.3.2.js'
|
||||||
|
export const MINI_PROGRAM_RETURN_MESSAGE = 'museum-guide:return'
|
||||||
const BRIDGE_WAIT_TIMEOUT = 2500
|
const BRIDGE_WAIT_TIMEOUT = 2500
|
||||||
const BRIDGE_POLL_INTERVAL = 50
|
const BRIDGE_POLL_INTERVAL = 50
|
||||||
const GET_ENV_TIMEOUT = 1500
|
const GET_ENV_TIMEOUT = 1500
|
||||||
const NAVIGATION_TIMEOUT = 3000
|
const NAVIGATION_TIMEOUT = 3000
|
||||||
|
|
||||||
let pendingNavigation: Promise<WechatMiniProgramBridgeResult> | null = null
|
let pendingNavigation: Promise<WechatMiniProgramBridgeResult> | null = null
|
||||||
|
let pendingReturn: Promise<WechatMiniProgramReturnResult> | null = null
|
||||||
let sdkLoadPromise: Promise<void> | null = null
|
let sdkLoadPromise: Promise<void> | null = null
|
||||||
|
|
||||||
const getBridge = (): MiniProgramBridge | null => {
|
const getBridge = (): MiniProgramBridge | null => {
|
||||||
@@ -108,6 +117,45 @@ const navigateToHostLocationPage = (bridge: MiniProgramBridge, target: WechatOpe
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const requestHostReturnFallback = (bridge: MiniProgramBridge) => {
|
||||||
|
if (!bridge.postMessage) return false
|
||||||
|
|
||||||
|
try {
|
||||||
|
bridge.postMessage({ data: { type: MINI_PROGRAM_RETURN_MESSAGE } })
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
console.error('请求宿主小程序安全返回失败:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const navigateBackToHost = (bridge: MiniProgramBridge) => new Promise<WechatMiniProgramReturnResult>((resolve) => {
|
||||||
|
if (!bridge.navigateBack) return resolve({ status: 'failed', reason: 'bridge-unavailable' })
|
||||||
|
|
||||||
|
let settled = false
|
||||||
|
const finish = (result: WechatMiniProgramReturnResult) => {
|
||||||
|
if (settled) return
|
||||||
|
settled = true
|
||||||
|
clearTimeout(timeout)
|
||||||
|
resolve(result)
|
||||||
|
}
|
||||||
|
const timeout = setTimeout(() => finish({ status: 'failed', reason: 'timeout' }), NAVIGATION_TIMEOUT)
|
||||||
|
|
||||||
|
try {
|
||||||
|
bridge.navigateBack({
|
||||||
|
delta: 1,
|
||||||
|
success: () => finish({ status: 'returned' }),
|
||||||
|
fail: (error) => {
|
||||||
|
console.error('返回宿主小程序失败:', error)
|
||||||
|
finish({ status: 'failed', reason: 'navigation-failed' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('调用宿主小程序返回桥接失败:', error)
|
||||||
|
finish({ status: 'failed', reason: 'navigation-failed' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const openOnce = async (target: WechatOpenLocationTarget): Promise<WechatMiniProgramBridgeResult> => {
|
const openOnce = async (target: WechatOpenLocationTarget): Promise<WechatMiniProgramBridgeResult> => {
|
||||||
const validation = validateWechatOpenLocationTarget(target)
|
const validation = validateWechatOpenLocationTarget(target)
|
||||||
if (!validation.ok) return { status: 'failed', reason: 'invalid-target' }
|
if (!validation.ok) return { status: 'failed', reason: 'invalid-target' }
|
||||||
@@ -123,3 +171,24 @@ export const openWechatMiniProgramLocation = (target: WechatOpenLocationTarget)
|
|||||||
pendingNavigation = openOnce(target).finally(() => { pendingNavigation = null })
|
pendingNavigation = openOnce(target).finally(() => { pendingNavigation = null })
|
||||||
return pendingNavigation
|
return pendingNavigation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const returnOnce = async (): Promise<WechatMiniProgramReturnResult> => {
|
||||||
|
if (!isEmbeddedInWechatMiniProgram()) return { status: 'not-embedded' }
|
||||||
|
|
||||||
|
const bridge = await waitForBridge()
|
||||||
|
if (!bridge) return { status: 'failed', reason: 'bridge-unavailable' }
|
||||||
|
if (!await isMiniProgramWebView(bridge)) return { status: 'failed', reason: 'not-in-mini-program' }
|
||||||
|
|
||||||
|
const result = await navigateBackToHost(bridge)
|
||||||
|
if (result.status === 'returned') return result
|
||||||
|
|
||||||
|
// 宿主页可能由分享或冷启动直接打开,页面栈没有上一页时改由宿主执行安全回退。
|
||||||
|
if (requestHostReturnFallback(bridge)) return { status: 'returned' }
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export const returnToWechatMiniProgram = () => {
|
||||||
|
if (pendingReturn) return pendingReturn
|
||||||
|
pendingReturn = returnOnce().finally(() => { pendingReturn = null })
|
||||||
|
return pendingReturn
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,28 +43,6 @@ export const isEmbeddedInWechatMiniProgram = () => {
|
|||||||
return /miniProgram/i.test(window.navigator?.userAgent || '')
|
return /miniProgram/i.test(window.navigator?.userAgent || '')
|
||||||
}
|
}
|
||||||
|
|
||||||
type WechatMiniProgramNavigationBridge = {
|
|
||||||
navigateBack?: (options: { delta: number }) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const navigateBackToWechatMiniProgram = () => {
|
|
||||||
if (!isEmbeddedInWechatMiniProgram() || typeof window === 'undefined') return false
|
|
||||||
|
|
||||||
const bridge = (window as Window & {
|
|
||||||
wx?: { miniProgram?: WechatMiniProgramNavigationBridge }
|
|
||||||
}).wx?.miniProgram
|
|
||||||
|
|
||||||
if (typeof bridge?.navigateBack !== 'function') return false
|
|
||||||
|
|
||||||
try {
|
|
||||||
bridge.navigateBack({ delta: 1 })
|
|
||||||
return true
|
|
||||||
} catch (error) {
|
|
||||||
console.error('返回小程序失败:', error)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isWechatMiniProgramRuntime = () => (
|
export const isWechatMiniProgramRuntime = () => (
|
||||||
isNativeWechatMiniProgram() || isEmbeddedInWechatMiniProgram()
|
isNativeWechatMiniProgram() || isEmbeddedInWechatMiniProgram()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { flushPromises, mount } from '@vue/test-utils'
|
|||||||
|
|
||||||
const mocks = vi.hoisted(() => ({
|
const mocks = vi.hoisted(() => ({
|
||||||
onLoadHandler: null as null | ((options?: Record<string, unknown>) => Promise<void>),
|
onLoadHandler: null as null | ((options?: Record<string, unknown>) => Promise<void>),
|
||||||
|
onBackPressHandler: null as null | (() => boolean),
|
||||||
onHideHandler: null as null | (() => void),
|
onHideHandler: null as null | (() => void),
|
||||||
onUnloadHandler: null as null | (() => void),
|
onUnloadHandler: null as null | (() => void),
|
||||||
enterExplainDetail: vi.fn(),
|
enterExplainDetail: vi.fn(),
|
||||||
@@ -17,7 +18,9 @@ const mocks = vi.hoisted(() => ({
|
|||||||
close: vi.fn(),
|
close: vi.fn(),
|
||||||
seekToPercent: vi.fn(),
|
seekToPercent: vi.fn(),
|
||||||
setPlaybackRate: vi.fn(),
|
setPlaybackRate: vi.fn(),
|
||||||
toggleMute: vi.fn()
|
toggleMute: vi.fn(),
|
||||||
|
embeddedInWechatMiniProgram: false,
|
||||||
|
returnToWechatMiniProgram: vi.fn(() => Promise.resolve({ status: 'failed' as const, reason: 'bridge-unavailable' as const }))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const audioState = vi.hoisted(() => ({
|
const audioState = vi.hoisted(() => ({
|
||||||
@@ -36,6 +39,9 @@ vi.mock('@dcloudio/uni-app', () => ({
|
|||||||
onLoad: (handler: (options?: Record<string, unknown>) => Promise<void>) => {
|
onLoad: (handler: (options?: Record<string, unknown>) => Promise<void>) => {
|
||||||
mocks.onLoadHandler = handler
|
mocks.onLoadHandler = handler
|
||||||
},
|
},
|
||||||
|
onBackPress: (handler: () => boolean) => {
|
||||||
|
mocks.onBackPressHandler = handler
|
||||||
|
},
|
||||||
onHide: (handler: () => void) => {
|
onHide: (handler: () => void) => {
|
||||||
mocks.onHideHandler = handler
|
mocks.onHideHandler = handler
|
||||||
},
|
},
|
||||||
@@ -74,7 +80,11 @@ vi.mock('@/composables/useGlobalAudioPlayer', () => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/utils/hostEnvironment', () => ({
|
vi.mock('@/utils/hostEnvironment', () => ({
|
||||||
isEmbeddedInWechatMiniProgram: () => false
|
isEmbeddedInWechatMiniProgram: () => mocks.embeddedInWechatMiniProgram
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/services/WechatMiniProgramBridgeService', () => ({
|
||||||
|
returnToWechatMiniProgram: () => mocks.returnToWechatMiniProgram()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
import ExhibitDetail from '@/pages/exhibit/detail.vue'
|
import ExhibitDetail from '@/pages/exhibit/detail.vue'
|
||||||
@@ -106,6 +116,7 @@ const exhibit = {
|
|||||||
describe('讲解详情音频优先布局', () => {
|
describe('讲解详情音频优先布局', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocks.onLoadHandler = null
|
mocks.onLoadHandler = null
|
||||||
|
mocks.onBackPressHandler = null
|
||||||
mocks.onHideHandler = null
|
mocks.onHideHandler = null
|
||||||
mocks.onUnloadHandler = null
|
mocks.onUnloadHandler = null
|
||||||
audioState.currentAudio.value = null
|
audioState.currentAudio.value = null
|
||||||
@@ -131,6 +142,9 @@ describe('讲解详情音频优先布局', () => {
|
|||||||
playInfo: { title: exhibit.name }
|
playInfo: { title: exhibit.name }
|
||||||
})
|
})
|
||||||
mocks.play.mockResolvedValue(undefined)
|
mocks.play.mockResolvedValue(undefined)
|
||||||
|
mocks.embeddedInWechatMiniProgram = false
|
||||||
|
mocks.returnToWechatMiniProgram.mockReset()
|
||||||
|
mocks.returnToWechatMiniProgram.mockResolvedValue({ status: 'failed', reason: 'bridge-unavailable' })
|
||||||
vi.stubGlobal('uni', {
|
vi.stubGlobal('uni', {
|
||||||
showToast: vi.fn(),
|
showToast: vi.fn(),
|
||||||
navigateBack: vi.fn(),
|
navigateBack: vi.fn(),
|
||||||
@@ -375,6 +389,28 @@ describe('讲解详情音频优先布局', () => {
|
|||||||
expect(uni.redirectTo).not.toHaveBeenCalled()
|
expect(uni.redirectTo).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('小程序直达讲解详情时返回宿主,不进入 H5 讲解对象列表', async () => {
|
||||||
|
mocks.embeddedInWechatMiniProgram = true
|
||||||
|
mocks.returnToWechatMiniProgram.mockResolvedValue({ status: 'returned' })
|
||||||
|
const wrapper = mount(ExhibitDetail, {
|
||||||
|
global: { stubs: { GuidePageFrame: GuidePageFrameStub } }
|
||||||
|
})
|
||||||
|
|
||||||
|
await mocks.onLoadHandler?.({
|
||||||
|
id: exhibit.id,
|
||||||
|
targetType: 'STOP',
|
||||||
|
targetId: 'stop-1',
|
||||||
|
hallId: 'hall-1',
|
||||||
|
hallName: '恐龙厅'
|
||||||
|
})
|
||||||
|
wrapper.getComponent(GuidePageFrameStub).vm.$emit('back')
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(mocks.returnToWechatMiniProgram).toHaveBeenCalledTimes(1)
|
||||||
|
expect(uni.navigateBack).not.toHaveBeenCalled()
|
||||||
|
expect(uni.redirectTo).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
it('离开详情页时立即关闭当前讲解播放器', async () => {
|
it('离开详情页时立即关闭当前讲解播放器', async () => {
|
||||||
audioState.currentAudio.value = { id: 'audio-1', audioUrl: exhibit.audioUrl }
|
audioState.currentAudio.value = { id: 'audio-1', audioUrl: exhibit.audioUrl }
|
||||||
audioState.currentSource.value = { targetType: 'STOP', targetId: 'stop-1', lang: 'en-US' }
|
audioState.currentSource.value = { targetType: 'STOP', targetId: 'stop-1', lang: 'en-US' }
|
||||||
@@ -407,9 +443,6 @@ describe('讲解详情音频优先布局', () => {
|
|||||||
})
|
})
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
||||||
vi.mocked(uni.navigateBack).mockImplementationOnce((options: any) => {
|
|
||||||
options.fail?.()
|
|
||||||
})
|
|
||||||
wrapper.getComponent(GuidePageFrameStub).vm.$emit('back')
|
wrapper.getComponent(GuidePageFrameStub).vm.$emit('back')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
||||||
@@ -418,6 +451,6 @@ describe('讲解详情音频优先布局', () => {
|
|||||||
expect(url.split('?')[0]).toBe('/pages/explain/guide-stop-list')
|
expect(url.split('?')[0]).toBe('/pages/explain/guide-stop-list')
|
||||||
expect(params.get('hallId')).toBe('hall-1')
|
expect(params.get('hallId')).toBe('hall-1')
|
||||||
expect(params.get('hallName')).toBe('恐龙厅')
|
expect(params.get('hallName')).toBe('恐龙厅')
|
||||||
expect(uni.navigateBack).toHaveBeenCalledWith(expect.objectContaining({ delta: 1 }))
|
expect(uni.navigateBack).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,15 +7,35 @@ import ExplainGuideStopListPage from '@/pages/explain/guide-stop-list.vue'
|
|||||||
import { explainUseCase } from '@/usecases/explainUseCase'
|
import { explainUseCase } from '@/usecases/explainUseCase'
|
||||||
|
|
||||||
const testState = vi.hoisted(() => ({
|
const testState = vi.hoisted(() => ({
|
||||||
onLoadHandler: null as null | ((options?: Record<string, string>) => void)
|
onLoadHandler: null as null | ((options?: Record<string, string>) => void),
|
||||||
|
onBackPressHandler: null as null | (() => boolean)
|
||||||
|
}))
|
||||||
|
|
||||||
|
const hostEnvironmentMocks = vi.hoisted(() => ({
|
||||||
|
embeddedInWechatMiniProgram: false
|
||||||
|
}))
|
||||||
|
|
||||||
|
const miniProgramReturnMocks = vi.hoisted(() => ({
|
||||||
|
returnToWechatMiniProgram: vi.fn(() => Promise.resolve({ status: 'failed' as const, reason: 'bridge-unavailable' as const }))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@dcloudio/uni-app', () => ({
|
vi.mock('@dcloudio/uni-app', () => ({
|
||||||
onLoad: (handler: (options?: Record<string, string>) => void) => {
|
onLoad: (handler: (options?: Record<string, string>) => void) => {
|
||||||
testState.onLoadHandler = handler
|
testState.onLoadHandler = handler
|
||||||
|
},
|
||||||
|
onBackPress: (handler: () => boolean) => {
|
||||||
|
testState.onBackPressHandler = handler
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/utils/hostEnvironment', () => ({
|
||||||
|
isEmbeddedInWechatMiniProgram: () => hostEnvironmentMocks.embeddedInWechatMiniProgram
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/services/WechatMiniProgramBridgeService', () => ({
|
||||||
|
returnToWechatMiniProgram: () => miniProgramReturnMocks.returnToWechatMiniProgram()
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('@/usecases/explainUseCase', () => ({
|
vi.mock('@/usecases/explainUseCase', () => ({
|
||||||
explainUseCase: {
|
explainUseCase: {
|
||||||
listGuideStopsPageByHall: vi.fn().mockResolvedValue({
|
listGuideStopsPageByHall: vi.fn().mockResolvedValue({
|
||||||
@@ -53,6 +73,10 @@ const mountGuideStopList = () => mount(ExplainGuideStopListPage, {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
testState.onLoadHandler = null
|
testState.onLoadHandler = null
|
||||||
|
testState.onBackPressHandler = null
|
||||||
|
hostEnvironmentMocks.embeddedInWechatMiniProgram = false
|
||||||
|
miniProgramReturnMocks.returnToWechatMiniProgram.mockReset()
|
||||||
|
miniProgramReturnMocks.returnToWechatMiniProgram.mockResolvedValue({ status: 'failed', reason: 'bridge-unavailable' })
|
||||||
vi.stubGlobal('uni', {
|
vi.stubGlobal('uni', {
|
||||||
navigateBack: vi.fn(),
|
navigateBack: vi.fn(),
|
||||||
reLaunch: vi.fn(),
|
reLaunch: vi.fn(),
|
||||||
@@ -155,6 +179,19 @@ describe('讲解对象列表返回', () => {
|
|||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('小程序直达展厅时返回宿主,不重启 H5 展厅列表', async () => {
|
||||||
|
hostEnvironmentMocks.embeddedInWechatMiniProgram = true
|
||||||
|
miniProgramReturnMocks.returnToWechatMiniProgram.mockResolvedValue({ status: 'returned' })
|
||||||
|
const wrapper = mountGuideStopList()
|
||||||
|
|
||||||
|
await wrapper.get('[data-testid="list-back"]').trigger('click')
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(miniProgramReturnMocks.returnToWechatMiniProgram).toHaveBeenCalledTimes(1)
|
||||||
|
expect(uni.reLaunch).not.toHaveBeenCalled()
|
||||||
|
wrapper.unmount()
|
||||||
|
})
|
||||||
|
|
||||||
it('页面栈返回失败时回退至独立展厅列表', async () => {
|
it('页面栈返回失败时回退至独立展厅列表', async () => {
|
||||||
vi.stubGlobal('getCurrentPages', vi.fn(() => [
|
vi.stubGlobal('getCurrentPages', vi.fn(() => [
|
||||||
{ route: 'pages/explain/list', options: {} },
|
{ route: 'pages/explain/list', options: {} },
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|||||||
import ExplainListPage from '@/pages/explain/list.vue'
|
import ExplainListPage from '@/pages/explain/list.vue'
|
||||||
|
|
||||||
const hostEnvironmentMocks = vi.hoisted(() => ({
|
const hostEnvironmentMocks = vi.hoisted(() => ({
|
||||||
embeddedInWechatMiniProgram: false,
|
embeddedInWechatMiniProgram: false
|
||||||
navigateBackToWechatMiniProgram: vi.fn(() => false)
|
}))
|
||||||
|
|
||||||
|
const miniProgramReturnMocks = vi.hoisted(() => ({
|
||||||
|
returnToWechatMiniProgram: vi.fn(() => Promise.resolve({ status: 'failed' as const, reason: 'bridge-unavailable' as const }))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const testState = vi.hoisted(() => ({
|
const testState = vi.hoisted(() => ({
|
||||||
@@ -25,8 +28,11 @@ vi.mock('@dcloudio/uni-app', () => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/utils/hostEnvironment', () => ({
|
vi.mock('@/utils/hostEnvironment', () => ({
|
||||||
isEmbeddedInWechatMiniProgram: () => hostEnvironmentMocks.embeddedInWechatMiniProgram,
|
isEmbeddedInWechatMiniProgram: () => hostEnvironmentMocks.embeddedInWechatMiniProgram
|
||||||
navigateBackToWechatMiniProgram: () => hostEnvironmentMocks.navigateBackToWechatMiniProgram()
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/services/WechatMiniProgramBridgeService', () => ({
|
||||||
|
returnToWechatMiniProgram: () => miniProgramReturnMocks.returnToWechatMiniProgram()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/usecases/explainUseCase', () => ({
|
vi.mock('@/usecases/explainUseCase', () => ({
|
||||||
@@ -62,8 +68,8 @@ const mountExplainList = () => mount(ExplainListPage, {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
hostEnvironmentMocks.embeddedInWechatMiniProgram = false
|
hostEnvironmentMocks.embeddedInWechatMiniProgram = false
|
||||||
hostEnvironmentMocks.navigateBackToWechatMiniProgram.mockReset()
|
miniProgramReturnMocks.returnToWechatMiniProgram.mockReset()
|
||||||
hostEnvironmentMocks.navigateBackToWechatMiniProgram.mockReturnValue(false)
|
miniProgramReturnMocks.returnToWechatMiniProgram.mockResolvedValue({ status: 'failed', reason: 'bridge-unavailable' })
|
||||||
testState.onLoadHandler = null
|
testState.onLoadHandler = null
|
||||||
testState.onBackPressHandler = null
|
testState.onBackPressHandler = null
|
||||||
window.location.hash = '#/pages/explain/list'
|
window.location.hash = '#/pages/explain/list'
|
||||||
@@ -100,7 +106,7 @@ describe('讲解展厅列表返回', () => {
|
|||||||
|
|
||||||
it('小程序中的首页讲解 Tab 进入时仍优先回到项目首页', async () => {
|
it('小程序中的首页讲解 Tab 进入时仍优先回到项目首页', async () => {
|
||||||
hostEnvironmentMocks.embeddedInWechatMiniProgram = true
|
hostEnvironmentMocks.embeddedInWechatMiniProgram = true
|
||||||
hostEnvironmentMocks.navigateBackToWechatMiniProgram.mockReturnValue(true)
|
miniProgramReturnMocks.returnToWechatMiniProgram.mockResolvedValue({ status: 'returned' })
|
||||||
vi.stubGlobal('getCurrentPages', vi.fn(() => [
|
vi.stubGlobal('getCurrentPages', vi.fn(() => [
|
||||||
{ route: 'pages/index/index', options: { tab: 'explain' } },
|
{ route: 'pages/index/index', options: { tab: 'explain' } },
|
||||||
{ route: 'pages/explain/list', options: {} }
|
{ route: 'pages/explain/list', options: {} }
|
||||||
@@ -110,7 +116,7 @@ describe('讲解展厅列表返回', () => {
|
|||||||
await wrapper.get('[data-testid="explain-list-back"]').trigger('click')
|
await wrapper.get('[data-testid="explain-list-back"]').trigger('click')
|
||||||
|
|
||||||
expect(uni.navigateBack).toHaveBeenCalledWith(expect.objectContaining({ delta: 1 }))
|
expect(uni.navigateBack).toHaveBeenCalledWith(expect.objectContaining({ delta: 1 }))
|
||||||
expect(hostEnvironmentMocks.navigateBackToWechatMiniProgram).not.toHaveBeenCalled()
|
expect(miniProgramReturnMocks.returnToWechatMiniProgram).not.toHaveBeenCalled()
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -128,23 +134,24 @@ describe('讲解展厅列表返回', () => {
|
|||||||
|
|
||||||
it('小程序直达独立入口时返回小程序,而不是重启项目首页', async () => {
|
it('小程序直达独立入口时返回小程序,而不是重启项目首页', async () => {
|
||||||
hostEnvironmentMocks.embeddedInWechatMiniProgram = true
|
hostEnvironmentMocks.embeddedInWechatMiniProgram = true
|
||||||
hostEnvironmentMocks.navigateBackToWechatMiniProgram.mockReturnValue(true)
|
miniProgramReturnMocks.returnToWechatMiniProgram.mockResolvedValue({ status: 'returned' })
|
||||||
window.location.hash = '#/pages/explain/list?embedded=wechat-mini-program&weapp=1'
|
window.location.hash = '#/pages/explain/list?embedded=wechat-mini-program&weapp=1'
|
||||||
window.history.replaceState({}, '', window.location.href)
|
window.history.replaceState({}, '', window.location.href)
|
||||||
const wrapper = mountExplainList()
|
const wrapper = mountExplainList()
|
||||||
|
|
||||||
expect(wrapper.getComponent(ExplainHallSelectStub).props('forceInternalHeader')).toBe(false)
|
expect(wrapper.getComponent(ExplainHallSelectStub).props('forceInternalHeader')).toBe(false)
|
||||||
await wrapper.get('[data-testid="explain-list-back"]').trigger('click')
|
await wrapper.get('[data-testid="explain-list-back"]').trigger('click')
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
expect(uni.navigateBack).not.toHaveBeenCalled()
|
expect(uni.navigateBack).not.toHaveBeenCalled()
|
||||||
expect(hostEnvironmentMocks.navigateBackToWechatMiniProgram).toHaveBeenCalledTimes(1)
|
expect(miniProgramReturnMocks.returnToWechatMiniProgram).toHaveBeenCalledTimes(1)
|
||||||
expect(uni.reLaunch).not.toHaveBeenCalled()
|
expect(uni.reLaunch).not.toHaveBeenCalled()
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('微信嵌入态从非讲解首页进入时返回小程序', async () => {
|
it('微信嵌入态从非讲解首页进入时返回小程序', async () => {
|
||||||
hostEnvironmentMocks.embeddedInWechatMiniProgram = true
|
hostEnvironmentMocks.embeddedInWechatMiniProgram = true
|
||||||
hostEnvironmentMocks.navigateBackToWechatMiniProgram.mockReturnValue(true)
|
miniProgramReturnMocks.returnToWechatMiniProgram.mockResolvedValue({ status: 'returned' })
|
||||||
window.location.hash = '#/pages/explain/list?embedded=wechat-mini-program'
|
window.location.hash = '#/pages/explain/list?embedded=wechat-mini-program'
|
||||||
window.history.replaceState({}, '', window.location.href)
|
window.history.replaceState({}, '', window.location.href)
|
||||||
vi.stubGlobal('getCurrentPages', vi.fn(() => [
|
vi.stubGlobal('getCurrentPages', vi.fn(() => [
|
||||||
@@ -154,9 +161,10 @@ describe('讲解展厅列表返回', () => {
|
|||||||
const wrapper = mountExplainList()
|
const wrapper = mountExplainList()
|
||||||
|
|
||||||
await wrapper.get('[data-testid="explain-list-back"]').trigger('click')
|
await wrapper.get('[data-testid="explain-list-back"]').trigger('click')
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
expect(uni.navigateBack).not.toHaveBeenCalled()
|
expect(uni.navigateBack).not.toHaveBeenCalled()
|
||||||
expect(hostEnvironmentMocks.navigateBackToWechatMiniProgram).toHaveBeenCalledTimes(1)
|
expect(miniProgramReturnMocks.returnToWechatMiniProgram).toHaveBeenCalledTimes(1)
|
||||||
expect(uni.reLaunch).not.toHaveBeenCalled()
|
expect(uni.reLaunch).not.toHaveBeenCalled()
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
@@ -216,7 +224,7 @@ describe('讲解展厅列表返回', () => {
|
|||||||
|
|
||||||
it('小程序直达入口的浏览器返回会委托给小程序宿主', async () => {
|
it('小程序直达入口的浏览器返回会委托给小程序宿主', async () => {
|
||||||
hostEnvironmentMocks.embeddedInWechatMiniProgram = true
|
hostEnvironmentMocks.embeddedInWechatMiniProgram = true
|
||||||
hostEnvironmentMocks.navigateBackToWechatMiniProgram.mockReturnValue(true)
|
miniProgramReturnMocks.returnToWechatMiniProgram.mockResolvedValue({ status: 'returned' })
|
||||||
window.location.hash = '#/pages/explain/list?embedded=wechat-mini-program&weapp=1'
|
window.location.hash = '#/pages/explain/list?embedded=wechat-mini-program&weapp=1'
|
||||||
window.history.replaceState({}, '', window.location.href)
|
window.history.replaceState({}, '', window.location.href)
|
||||||
const wrapper = mountExplainList()
|
const wrapper = mountExplainList()
|
||||||
@@ -230,7 +238,7 @@ describe('讲解展厅列表返回', () => {
|
|||||||
|
|
||||||
expect(uni.navigateBack).not.toHaveBeenCalled()
|
expect(uni.navigateBack).not.toHaveBeenCalled()
|
||||||
expect(pushState).toHaveBeenCalledTimes(1)
|
expect(pushState).toHaveBeenCalledTimes(1)
|
||||||
expect(hostEnvironmentMocks.navigateBackToWechatMiniProgram).toHaveBeenCalledTimes(1)
|
expect(miniProgramReturnMocks.returnToWechatMiniProgram).toHaveBeenCalledTimes(1)
|
||||||
expect(uni.reLaunch).not.toHaveBeenCalled()
|
expect(uni.reLaunch).not.toHaveBeenCalled()
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|||||||
|
|
||||||
const target = { latitude: 22.604321, longitude: 114.058765, name: '停车场 A&B', address: '龙华区 #1' }
|
const target = { latitude: 22.604321, longitude: 114.058765, name: '停车场 A&B', address: '龙华区 #1' }
|
||||||
const setEmbeddedRuntime = () => Object.assign(window, { __wxjs_environment: 'miniprogram' })
|
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 () => {
|
const loadService = async () => {
|
||||||
vi.resetModules()
|
vi.resetModules()
|
||||||
@@ -33,6 +38,7 @@ describe('微信小程序 web-view 桥接', () => {
|
|||||||
|
|
||||||
it('等待延迟注入的 bridge 后导航到编码后的宿主页', async () => {
|
it('等待延迟注入的 bridge 后导航到编码后的宿主页', async () => {
|
||||||
setEmbeddedRuntime()
|
setEmbeddedRuntime()
|
||||||
|
addWechatSdkMarker()
|
||||||
const { openWechatMiniProgramLocation } = await loadService()
|
const { openWechatMiniProgramLocation } = await loadService()
|
||||||
const result = openWechatMiniProgramLocation(target)
|
const result = openWechatMiniProgramLocation(target)
|
||||||
await vi.advanceTimersByTimeAsync(100)
|
await vi.advanceTimersByTimeAsync(100)
|
||||||
@@ -84,4 +90,45 @@ describe('微信小程序 web-view 桥接', () => {
|
|||||||
await expect(first).resolves.toEqual({ status: 'opened' })
|
await expect(first).resolves.toEqual({ status: 'opened' })
|
||||||
expect(navigateTo).toHaveBeenCalledTimes(1)
|
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 }
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||||
import {
|
import {
|
||||||
isEmbeddedInWechatMiniProgram,
|
isEmbeddedInWechatMiniProgram
|
||||||
navigateBackToWechatMiniProgram
|
|
||||||
} from '@/utils/hostEnvironment'
|
} from '@/utils/hostEnvironment'
|
||||||
|
|
||||||
const createWindow = (overrides: Record<string, unknown> = {}) => ({
|
const createWindow = (overrides: Record<string, unknown> = {}) => ({
|
||||||
@@ -50,24 +49,3 @@ describe('微信小程序宿主环境识别', () => {
|
|||||||
expect(isEmbeddedInWechatMiniProgram()).toBe(false)
|
expect(isEmbeddedInWechatMiniProgram()).toBe(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('小程序宿主返回', () => {
|
|
||||||
it('嵌入态调用小程序 navigateBack', () => {
|
|
||||||
const navigateBack = vi.fn()
|
|
||||||
vi.stubGlobal('window', createWindow({
|
|
||||||
__wxjs_environment: 'miniprogram',
|
|
||||||
wx: { miniProgram: { navigateBack } }
|
|
||||||
}))
|
|
||||||
|
|
||||||
expect(navigateBackToWechatMiniProgram()).toBe(true)
|
|
||||||
expect(navigateBack).toHaveBeenCalledWith({ delta: 1 })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('非嵌入态不会调用小程序返回桥接', () => {
|
|
||||||
const navigateBack = vi.fn()
|
|
||||||
vi.stubGlobal('window', createWindow({ wx: { miniProgram: { navigateBack } } }))
|
|
||||||
|
|
||||||
expect(navigateBackToWechatMiniProgram()).toBe(false)
|
|
||||||
expect(navigateBack).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|||||||
Reference in New Issue
Block a user