2026-10-09 17:36:32 +08:00
import { flushPromises , mount } from '@vue/test-utils'
import { beforeEach , describe , expect , it , vi } from 'vitest'
import TransportRouteDialog from './TransportRouteDialog.vue'
import { resolveRouteLocation , routeLocation , RouteAddressError , type RouteLocation } from '../routeLocations'
vi . mock ( '../routeLocations' , async importOriginal => ({ ... await importOriginal < object >(), resolveRouteLocation : vi.fn () }))
const global = { stubs : {
AppDialog : { template : '<div><slot /></div>' }, ElButton : { template : '<button><slot /></button>' }, ElLink : { template : '<a><slot /></a>' },
LoadErrorState : { props : [ 'message' ], template : '<p role="alert">{{ message }}</p>' }, ApiErrorMeta : true ,
ElSelect : { props : [ 'modelValue' ], emits : [ 'update:modelValue' ], template : '<select :value="modelValue" @change="$emit(\'update:modelValue\', Number($event.target.value))"><slot /></select>' },
ElOption : { props : [ 'label' , 'value' ], template : '<option :value="value">{{ label }}</option>' },
} }
const endpoints = { origin : { kind : 'warehouse' , label : '洛杉矶仓' , warehouseId : '123' } as RouteLocation , destinations : [ routeLocation ( 'ABE3' ), routeLocation ( 'ONT8' )] }
const addresses : Record < string , string > = { '洛杉矶仓' : '100 Example Street, Los Angeles, CA, US' , ABE3 : '200 Example Road, Example City, PA, 18000, US' , ONT8 : '300 Example Avenue, Ontario, CA, US' }
beforeEach (() => { vi . mocked ( resolveRouteLocation ). mockReset (). mockImplementation ( async location => location . kind === 'address' ? location.address : addresses [ location . label ] ! ) })
describe ( 'transport route address resolution' , () => {
it ( 'does not query until opened and sends full addresses to both map URLs' , async () => {
const wrapper = mount ( TransportRouteDialog , { props : { ... endpoints , modelValue : false }, global })
expect ( resolveRouteLocation ). not . toHaveBeenCalled ()
await wrapper . setProps ({ modelValue : true }); await flushPromises ()
const embedded = new URL ( wrapper . get ( 'iframe' ). attributes ( 'src' ) ! )
const external = new URL ( wrapper . get ( 'a' ). attributes ( 'href' ) ! )
expect ( embedded . searchParams . get ( 'saddr' )). toBe ( addresses [ '洛杉矶仓' ])
expect ( embedded . searchParams . get ( 'daddr' )). toBe ( addresses . ABE3 )
expect ( external . searchParams . get ( 'destination' )). toBe ( addresses . ABE3 )
expect ( wrapper . text ()). toContain ( '目的地地址:' + addresses . ABE3 )
expect ( resolveRouteLocation ). toHaveBeenCalledTimes ( 2 )
const iframe = wrapper . get ( 'iframe' ). element
2026-10-20 15:48:27 +08:00
await wrapper . findAll ( 'button' ). find ( button => button . text () === '重新加载地图' ) ! . trigger ( 'click' )
2026-10-09 17:36:32 +08:00
expect ( wrapper . get ( 'iframe' ). element ). not . toBe ( iframe )
expect ( resolveRouteLocation ). toHaveBeenCalledTimes ( 2 )
await wrapper . setProps ({ modelValue : false })
expect ( wrapper . find ( 'iframe' ). exists ()). toBe ( false )
wrapper . unmount ()
})
it ( 'resolves only the selected warehouse and reuses the origin for another destination' , async () => {
const wrapper = mount ( TransportRouteDialog , { props : { ... endpoints , modelValue : true }, global }); await flushPromises ()
await wrapper . get ( 'select' ). setValue ( '1' ); await flushPromises ()
expect ( new URL ( wrapper . get ( 'iframe' ). attributes ( 'src' ) ! ). searchParams . get ( 'daddr' )). toBe ( addresses . ONT8 )
expect ( resolveRouteLocation ). toHaveBeenCalledTimes ( 3 )
wrapper . unmount ()
})
it ( 'never leaves the previous map visible while resolving a new destination, and ignores late results' , async () => {
let finish !: ( value : string ) => void
vi . mocked ( resolveRouteLocation ). mockImplementation ( location => location . label === 'ABE3' ? new Promise ( resolve => { finish = resolve }) : Promise . resolve ( addresses [ location . label ] ! ))
const wrapper = mount ( TransportRouteDialog , { props : { ... endpoints , modelValue : true }, global })
expect ( wrapper . find ( 'iframe' ). exists ()). toBe ( false )
await wrapper . get ( 'select' ). setValue ( '1' ); await flushPromises ()
finish ( 'stale address' ); await flushPromises ()
expect ( new URL ( wrapper . get ( 'iframe' ). attributes ( 'src' ) ! ). searchParams . get ( 'daddr' )). toBe ( addresses . ONT8 )
await wrapper . setProps ({ modelValue : false }); await flushPromises ()
expect ( wrapper . find ( 'iframe' ). exists ()). toBe ( false )
wrapper . unmount ()
})
it ( 'shows missing address errors instead of falling back to a warehouse code' , async () => {
vi . mocked ( resolveRouteLocation ). mockRejectedValue ( new RouteAddressError ( 'ABE3缺少完整仓库地址' ))
const wrapper = mount ( TransportRouteDialog , { props : { ... endpoints , modelValue : true }, global }); await flushPromises ()
expect ( wrapper . text ()). toContain ( 'ABE3缺少完整仓库地址' )
expect ( wrapper . find ( 'iframe' ). exists ()). toBe ( false ); expect ( wrapper . find ( 'a' ). exists ()). toBe ( false )
wrapper . unmount ()
})
it ( 'does not request missing endpoints' , async () => {
const wrapper = mount ( TransportRouteDialog , { props : { origin : routeLocation ( '' ), destinations : [], modelValue : true }, global }); await flushPromises ()
expect ( resolveRouteLocation ). not . toHaveBeenCalled ()
expect ( wrapper . text ()). toContain ( '缺少起点或目的地' )
wrapper . unmount ()
})
})