import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { GuideAutoSwitchStateMachine, type GuideAutoSwitchDirection, type GuideAutoSwitchInputSource } from '@/components/map/guideAutoSwitchStateMachine' const createMachine = () => { const requests: GuideAutoSwitchDirection[] = [] const machine = new GuideAutoSwitchStateMachine({ onSwitchRequested: ({ direction }) => requests.push(direction) }) return { machine, requests } } beforeEach(() => { vi.useFakeTimers() vi.setSystemTime(0) }) afterEach(() => { vi.useRealTimers() }) describe('建筑外观与馆内楼层自动切换状态机', () => { it('连续放大 14.9% 不进入,达到 15% 后进入', () => { const { machine, requests } = createMachine() machine.beginInput(100, 'gesture') machine.updateDistance(85.1, { source: 'gesture' }) vi.advanceTimersByTime(400) expect(requests).toEqual([]) machine.updateDistance(85, { source: 'gesture' }) vi.advanceTimersByTime(120) expect(requests).toEqual(['enter-floor']) }) it('进入条件必须连续保持 120ms', () => { const { machine, requests } = createMachine() machine.beginInput(100, 'wheel') machine.updateDistance(85, { source: 'wheel' }) vi.advanceTimersByTime(119) expect(requests).toEqual([]) vi.advanceTimersByTime(1) expect(requests).toEqual(['enter-floor']) }) it('两次滚轮事件累计不足 15% 时不进入', () => { const { machine, requests } = createMachine() machine.beginInput(100, 'wheel') machine.updateDistance(95, { source: 'wheel' }) machine.beginInput(95, 'wheel') machine.updateDistance(90, { source: 'wheel' }) vi.advanceTimersByTime(400) expect(requests).toEqual([]) }) it('相对楼层初始距离拉远 19.9% 不退出,达到 20% 后退出', () => { const { machine, requests } = createMachine() machine.setView('floor') machine.setFloorInitialDistance(100) machine.updateDistance(119.9) vi.advanceTimersByTime(500) expect(requests).toEqual([]) machine.updateDistance(120) vi.advanceTimersByTime(400) expect(requests).toEqual(['exit-overview']) }) it('退出条件必须连续保持 400ms', () => { const { machine, requests } = createMachine() machine.setView('floor') machine.setFloorInitialDistance(100) machine.updateDistance(120) vi.advanceTimersByTime(399) expect(requests).toEqual([]) vi.advanceTimersByTime(1) expect(requests).toEqual(['exit-overview']) }) it('0.8% 内的触控反向抖动不重置进入基准,也不取消候选状态', () => { const { machine, requests } = createMachine() machine.beginInput(100, 'touch', { resetBaseline: true }) machine.updateDistance(85, { source: 'touch' }) vi.advanceTimersByTime(60) machine.updateDistance(85.6, { source: 'touch' }) vi.advanceTimersByTime(60) expect(requests).toEqual(['enter-floor']) }) it('明确反向缩放超过 0.8% 后取消候选并重置进入基准', () => { const { machine, requests } = createMachine() machine.beginInput(100, 'touch', { resetBaseline: true }) machine.updateDistance(85, { source: 'touch' }) vi.advanceTimersByTime(60) machine.updateDistance(86, { source: 'touch' }) vi.advanceTimersByTime(120) expect(requests).toEqual([]) machine.updateDistance(73, { source: 'touch' }) vi.advanceTimersByTime(120) expect(requests).toEqual(['enter-floor']) }) it('退出候选明确反向超过 0.8% 后会取消防抖任务', () => { const { machine, requests } = createMachine() machine.setView('floor') machine.setFloorInitialDistance(100) machine.updateDistance(120) vi.advanceTimersByTime(200) machine.updateDistance(118.9) vi.advanceTimersByTime(400) expect(requests).toEqual([]) }) it('进入意图超时后使用新的连续缩放距离重新计算基准', () => { const { machine, requests } = createMachine() machine.beginInput(100, 'wheel') machine.updateDistance(90, { source: 'wheel' }) vi.advanceTimersByTime(2001) machine.updateDistance(80, { previousDistance: 90, source: 'wheel' }) vi.advanceTimersByTime(120) expect(requests).toEqual([]) machine.updateDistance(76.5, { source: 'wheel' }) vi.advanceTimersByTime(120) expect(requests).toEqual(['enter-floor']) }) it('切换成功后 1499ms 内不能反向切换,1500ms 后可以', () => { const { machine, requests } = createMachine() machine.beginInput(100, 'wheel') machine.updateDistance(85, { source: 'wheel' }) vi.advanceTimersByTime(120) expect(requests).toEqual(['enter-floor']) machine.markTransitionSucceeded() machine.setView('floor') machine.setFloorInitialDistance(100) machine.updateDistance(120) vi.advanceTimersByTime(1499) expect(requests).toEqual(['enter-floor']) vi.advanceTimersByTime(1) expect(requests).toEqual(['enter-floor', 'exit-overview']) }) it.each(['wheel', 'button', 'touch'])( '%s 路径使用同一套 15% 进入判断', (source) => { const { machine, requests } = createMachine() machine.beginInput(100, source, { resetBaseline: source === 'touch' }) machine.updateDistance(85.1, { source }) vi.advanceTimersByTime(400) expect(requests).toEqual([]) machine.updateDistance(85, { source }) vi.advanceTimersByTime(120) expect(requests).toEqual(['enter-floor']) } ) })