This commit is contained in:
@@ -457,6 +457,14 @@ test('floor view baseline is deterministic across repeated resets', async ({ pag
|
||||
await api.resetToViewBaseline({ view: 'floor', floorId, reason: 'floor-reset' })
|
||||
}, floor.floorId)
|
||||
const first = await getReport(page)
|
||||
const baseline = await page.evaluate((floorId) => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: VisualStabilityApi })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
if (!api) throw new Error('ThreeMap visual stability diagnostics unavailable')
|
||||
return api.getFloorBaseline(floorId)
|
||||
}, floor.floorId)
|
||||
expect(baseline, `missing floor baseline ${floor.floorId}`).not.toBeNull()
|
||||
expect(getMaximumCameraDelta(baseline!, first.camera)).toBeLessThan(1e-6)
|
||||
await page.evaluate(async (floorId) => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: VisualStabilityApi })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
@@ -467,5 +475,6 @@ test('floor view baseline is deterministic across repeated resets', async ({ pag
|
||||
expect(second.activeView).toBe('floor')
|
||||
expect(second.floorId).toBe(floor.floorId)
|
||||
expect(getMaximumCameraDelta(first.camera, second.camera)).toBeLessThan(1e-6)
|
||||
expect(getMaximumCameraDelta(baseline!, second.camera)).toBeLessThan(1e-6)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,6 +8,14 @@ import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
|
||||
const ThreeMapStub = defineComponent({
|
||||
name: 'ThreeMap',
|
||||
props: {
|
||||
initialFloorId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
initialView: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
visiblePoiIds: {
|
||||
type: Array,
|
||||
default: null
|
||||
@@ -71,4 +79,29 @@ describe('GuideMapShell 点位过滤契约', () => {
|
||||
expect(wrapper.emitted('floorChange')).toEqual([['L2']])
|
||||
expect(wrapper.emitted('targetFocus')).toEqual([[focusResult]])
|
||||
})
|
||||
|
||||
it('动态楼层业务视图不会覆盖首次外观模型初始化视图', () => {
|
||||
const wrapper = mount(GuideMapShell, {
|
||||
props: {
|
||||
mapType: 'indoor',
|
||||
indoorModelSource: modelSource,
|
||||
floors: [
|
||||
{ id: 'L1', label: '1F' },
|
||||
{ id: 'L2', label: '2F' }
|
||||
],
|
||||
activeFloor: 'L2',
|
||||
indoorView: 'floor',
|
||||
indoorInitialView: 'overview'
|
||||
},
|
||||
global: {
|
||||
stubs: {
|
||||
ThreeMap: ThreeMapStub
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const renderer = wrapper.getComponent(ThreeMapStub)
|
||||
expect(renderer.props('initialFloorId')).toBe('L2')
|
||||
expect(renderer.props('initialView')).toBe('overview')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,12 +6,25 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import IndexPage from '@/pages/index/index.vue'
|
||||
|
||||
const testState = vi.hoisted(() => ({
|
||||
mapMountCount: 0,
|
||||
mapUnmountCount: 0,
|
||||
resetToViewBaselineCalls: [] as Array<{
|
||||
view: 'overview' | 'floor'
|
||||
floorId?: string
|
||||
reason: 'poi-detail-close' | 'manual-reset' | 'floor-reset'
|
||||
}>,
|
||||
searchMountCount: 0,
|
||||
searchUnmountCount: 0,
|
||||
searchRestoreCount: 0,
|
||||
searchCollapseAfterDetailCount: 0,
|
||||
searchResetCount: 0,
|
||||
onShowHandler: null as null | (() => Promise<void> | void)
|
||||
onShowHandler: null as null | (() => Promise<void> | void),
|
||||
halls: [] as Array<{ id: string; poiId?: string; location?: { poiId?: string }; name: string }>,
|
||||
explainResults: [] as Array<{
|
||||
type: 'hall' | 'exhibit'
|
||||
hallId?: string
|
||||
exhibitId?: string
|
||||
}>
|
||||
}))
|
||||
|
||||
const hostEnvironmentMocks = vi.hoisted(() => ({
|
||||
@@ -63,8 +76,8 @@ vi.mock('@/usecases/explainUseCase', () => ({
|
||||
explainUseCase: {
|
||||
loadExplainHalls: async () => [],
|
||||
loadExplainHallSummaries: async () => ({}),
|
||||
listHalls: async () => [],
|
||||
searchExplain: async () => []
|
||||
listHalls: async () => testState.halls,
|
||||
searchExplain: async () => testState.explainResults
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -82,6 +95,25 @@ const GuideMapShellStub = defineComponent({
|
||||
targetFocusRequest: { type: Object, default: null }
|
||||
},
|
||||
emits: ['floor-change', 'poi-click', 'selection-clear'],
|
||||
setup() {
|
||||
onMounted(() => {
|
||||
testState.mapMountCount += 1
|
||||
})
|
||||
onUnmounted(() => {
|
||||
testState.mapUnmountCount += 1
|
||||
})
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
resetToViewBaseline(options: {
|
||||
view: 'overview' | 'floor'
|
||||
floorId?: string
|
||||
reason: 'poi-detail-close' | 'manual-reset' | 'floor-reset'
|
||||
}) {
|
||||
testState.resetToViewBaselineCalls.push(options)
|
||||
return true
|
||||
}
|
||||
},
|
||||
template: '<section data-testid="guide-map"><slot name="overlay" /><slot /></section>'
|
||||
})
|
||||
|
||||
@@ -162,14 +194,24 @@ const mountIndex = async () => {
|
||||
return wrapper
|
||||
}
|
||||
|
||||
const confirmLatestNavigation = () => {
|
||||
const navigation = vi.mocked(uni.navigateTo).mock.calls.at(-1)?.[0]
|
||||
navigation?.success?.({ errMsg: 'navigateTo:ok' } as never)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
hostEnvironmentMocks.embeddedInWechatMiniProgram = false
|
||||
testState.mapMountCount = 0
|
||||
testState.mapUnmountCount = 0
|
||||
testState.resetToViewBaselineCalls = []
|
||||
testState.searchMountCount = 0
|
||||
testState.searchUnmountCount = 0
|
||||
testState.searchRestoreCount = 0
|
||||
testState.searchCollapseAfterDetailCount = 0
|
||||
testState.searchResetCount = 0
|
||||
testState.onShowHandler = null
|
||||
testState.halls = []
|
||||
testState.explainResults = []
|
||||
window.history.replaceState({}, '', '/#/pages/index/index?tab=guide')
|
||||
vi.stubGlobal('uni', {
|
||||
navigateTo: vi.fn(),
|
||||
@@ -351,6 +393,7 @@ describe('首页搜索与地图闭环', () => {
|
||||
expect(testState.searchMountCount).toBe(1)
|
||||
expect(testState.searchUnmountCount).toBe(0)
|
||||
|
||||
confirmLatestNavigation()
|
||||
await testState.onShowHandler?.()
|
||||
await flushPromises()
|
||||
|
||||
@@ -378,6 +421,7 @@ describe('首页搜索与地图闭环', () => {
|
||||
|
||||
search.vm.$emit('result-tap', poi, fullSearchContext)
|
||||
await flushPromises()
|
||||
confirmLatestNavigation()
|
||||
await testState.onShowHandler?.()
|
||||
await flushPromises()
|
||||
|
||||
@@ -417,6 +461,129 @@ describe('首页搜索与地图闭环', () => {
|
||||
expect(map.props('targetFocusRequest')).toBeNull()
|
||||
})
|
||||
|
||||
it('模型点选展厅后查看详情,返回时恢复该楼层视觉基线且不重挂载地图', async () => {
|
||||
testState.halls = [{ id: 'hall-l2', poiId: poi.id, name: '二层展厅' }]
|
||||
const wrapper = await mountIndex()
|
||||
const map = wrapper.getComponent(GuideMapShellStub)
|
||||
|
||||
map.vm.$emit('poi-click', {
|
||||
...poi,
|
||||
kind: 'hall',
|
||||
primaryCategory: 'exhibition_hall',
|
||||
primaryCategoryZh: '展厅'
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const viewHallAction = wrapper.find('.poi-action.primary')
|
||||
expect(viewHallAction.exists()).toBe(true)
|
||||
await viewHallAction.trigger('tap')
|
||||
await flushPromises()
|
||||
|
||||
expect(vi.mocked(uni.navigateTo).mock.calls[0]?.[0]?.url).toContain('/pages/hall/detail?id=hall-l2')
|
||||
await testState.onShowHandler?.()
|
||||
await flushPromises()
|
||||
expect(testState.resetToViewBaselineCalls).toHaveLength(0)
|
||||
expect(wrapper.find('.guide-poi-card').exists()).toBe(true)
|
||||
|
||||
confirmLatestNavigation()
|
||||
await testState.onShowHandler?.()
|
||||
await flushPromises()
|
||||
|
||||
expect(testState.resetToViewBaselineCalls).toEqual([{
|
||||
view: 'floor',
|
||||
floorId: 'L2',
|
||||
reason: 'poi-detail-close'
|
||||
}])
|
||||
expect(map.props('activeFloor')).toBe('L2')
|
||||
expect(map.props('visiblePoiIds')).toBeNull()
|
||||
expect(map.props('targetFocusRequest')).toBeNull()
|
||||
expect(wrapper.find('.guide-poi-card').exists()).toBe(false)
|
||||
expect(testState.mapMountCount).toBe(1)
|
||||
expect(testState.mapUnmountCount).toBe(0)
|
||||
|
||||
await testState.onShowHandler?.()
|
||||
await flushPromises()
|
||||
expect(testState.resetToViewBaselineCalls).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('模型点选展厅的相关讲解可进入展品详情,并在返回时复位原楼层', async () => {
|
||||
testState.explainResults = [{ type: 'exhibit', exhibitId: 'exhibit-l2' }]
|
||||
const wrapper = await mountIndex()
|
||||
const map = wrapper.getComponent(GuideMapShellStub)
|
||||
|
||||
map.vm.$emit('poi-click', {
|
||||
...poi,
|
||||
kind: 'hall',
|
||||
primaryCategory: 'exhibition_hall',
|
||||
primaryCategoryZh: '展厅'
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
const relatedActions = wrapper.findAll('.poi-action')
|
||||
expect(relatedActions).toHaveLength(2)
|
||||
await relatedActions[1]!.trigger('tap')
|
||||
await flushPromises()
|
||||
|
||||
expect(vi.mocked(uni.navigateTo).mock.calls[0]?.[0]?.url).toContain('/pages/exhibit/detail?id=exhibit-l2')
|
||||
confirmLatestNavigation()
|
||||
await testState.onShowHandler?.()
|
||||
await flushPromises()
|
||||
|
||||
expect(testState.resetToViewBaselineCalls).toEqual([{
|
||||
view: 'floor',
|
||||
floorId: 'L2',
|
||||
reason: 'poi-detail-close'
|
||||
}])
|
||||
expect(wrapper.find('.guide-poi-card').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('模型点选展厅的相关讲解优先进入展厅详情并保留一次性返回上下文', async () => {
|
||||
testState.halls = [{ id: 'hall-l2', poiId: poi.id, name: '二层展厅' }]
|
||||
const wrapper = await mountIndex()
|
||||
const map = wrapper.getComponent(GuideMapShellStub)
|
||||
|
||||
map.vm.$emit('poi-click', {
|
||||
...poi,
|
||||
kind: 'hall',
|
||||
primaryCategory: 'exhibition_hall',
|
||||
primaryCategoryZh: '展厅'
|
||||
})
|
||||
await flushPromises()
|
||||
await wrapper.findAll('.poi-action')[1]!.trigger('tap')
|
||||
await flushPromises()
|
||||
|
||||
expect(vi.mocked(uni.navigateTo).mock.calls[0]?.[0]?.url).toContain('/pages/hall/detail?id=hall-l2')
|
||||
confirmLatestNavigation()
|
||||
await testState.onShowHandler?.()
|
||||
await flushPromises()
|
||||
expect(testState.resetToViewBaselineCalls[0]).toMatchObject({ view: 'floor', floorId: 'L2' })
|
||||
})
|
||||
|
||||
it('模型点选详情打开失败时取消返回上下文,不触发楼层复位', async () => {
|
||||
testState.halls = [{ id: 'hall-l2', poiId: poi.id, name: '二层展厅' }]
|
||||
const wrapper = await mountIndex()
|
||||
const map = wrapper.getComponent(GuideMapShellStub)
|
||||
|
||||
map.vm.$emit('poi-click', {
|
||||
...poi,
|
||||
kind: 'hall',
|
||||
primaryCategory: 'exhibition_hall',
|
||||
primaryCategoryZh: '展厅'
|
||||
})
|
||||
await flushPromises()
|
||||
await wrapper.find('.poi-action.primary').trigger('tap')
|
||||
await flushPromises()
|
||||
|
||||
const navigation = vi.mocked(uni.navigateTo).mock.calls[0]?.[0]
|
||||
navigation?.fail?.({ errMsg: 'navigateTo:fail simulated' })
|
||||
await testState.onShowHandler?.()
|
||||
await flushPromises()
|
||||
|
||||
expect(testState.resetToViewBaselineCalls).toHaveLength(0)
|
||||
expect(wrapper.find('.guide-poi-card').exists()).toBe(true)
|
||||
expect(map.props('activeFloor')).toBe('L2')
|
||||
})
|
||||
|
||||
it('地图点位卡隐藏首页 dock 时仍保留搜索组件,关闭后可继续使用原列表', async () => {
|
||||
const wrapper = await mountIndex()
|
||||
const map = wrapper.getComponent(GuideMapShellStub)
|
||||
|
||||
Reference in New Issue
Block a user