195 lines
6.3 KiB
TypeScript
195 lines
6.3 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest'
|
|
import type { MuseumFloor, MuseumPoi } from '@/domain/museum'
|
|
import type { PoiCategoryId } from '@/domain/poiCategories'
|
|
import type { GuideRepository } from '@/repositories/GuideRepository'
|
|
import type { GuideModelRepository } from '@/repositories/GuideModelRepository'
|
|
import { GuideUseCase } from '@/usecases/guideUseCase'
|
|
|
|
const floors: MuseumFloor[] = [
|
|
{ id: 'L1', label: '1F', order: 1 },
|
|
{ id: 'L2', label: '2F', order: 2 }
|
|
]
|
|
|
|
const quickCategoryIds: readonly PoiCategoryId[] = [
|
|
'exhibition-hall',
|
|
'cinema',
|
|
'ticket-office',
|
|
'dining',
|
|
'shopping',
|
|
'service-center',
|
|
'restroom',
|
|
'nursing-room',
|
|
'elevator',
|
|
'escalator'
|
|
]
|
|
|
|
const createPoi = (id: string, floorId: string, categoryId: string): MuseumPoi => ({
|
|
id,
|
|
name: id,
|
|
floorId,
|
|
floorLabel: floorId === 'L2' ? '2F' : '1F',
|
|
primaryCategory: {
|
|
id: categoryId,
|
|
label: categoryId,
|
|
iconType: categoryId
|
|
},
|
|
categories: [],
|
|
accessible: true
|
|
})
|
|
|
|
const destinationsByFloor: Record<string, MuseumPoi[]> = {
|
|
L1: [createPoi('space-l1-hall', 'L1', 'exhibition_hall')],
|
|
L2: [createPoi('space-l2-hall', 'L2', 'exhibition_hall')]
|
|
}
|
|
|
|
const quickResultsByFloor: Record<string, Partial<Record<PoiCategoryId, MuseumPoi[]>>> = {
|
|
L1: {
|
|
restroom: [createPoi('facility-l1-restroom', 'L1', 'toilet')]
|
|
},
|
|
L2: {
|
|
restroom: [createPoi('facility-l2-restroom', 'L2', 'toilet')]
|
|
}
|
|
}
|
|
|
|
const keywordResultsByFloor: Record<string, MuseumPoi[]> = {
|
|
L1: [createPoi('facility-l1-shop', 'L1', 'shop')],
|
|
L2: [createPoi('facility-l2-shop', 'L2', 'shop')]
|
|
}
|
|
|
|
const availability = quickCategoryIds.map((id, index) => ({
|
|
id,
|
|
count: index === quickCategoryIds.indexOf('restroom') ? 1 : 0,
|
|
disabled: id !== 'restroom'
|
|
}))
|
|
|
|
const createRepository = () => ({
|
|
getAssetBaseUrl: vi.fn(() => ''),
|
|
getFloors: vi.fn().mockResolvedValue(floors),
|
|
normalizeFloorId: vi.fn((floorId: string) => ({
|
|
'1F': 'L1',
|
|
'2F': 'L2'
|
|
}[floorId] || floorId)),
|
|
listPois: vi.fn().mockResolvedValue([]),
|
|
listSpacePoints: vi.fn().mockResolvedValue([]),
|
|
listDestinationPois: vi.fn((floorId?: string) => (
|
|
Promise.resolve(destinationsByFloor[floorId || 'L1'] || [])
|
|
)),
|
|
listQuickFindPois: vi.fn((categoryId: PoiCategoryId, floorId?: string) => (
|
|
Promise.resolve(quickResultsByFloor[floorId || 'L1']?.[categoryId] || [])
|
|
)),
|
|
getQuickFindCategoryAvailability: vi.fn().mockResolvedValue(availability),
|
|
getPoiById: vi.fn().mockResolvedValue(null),
|
|
searchPois: vi.fn((keyword?: string, floorId?: string) => (
|
|
Promise.resolve(keyword ? keywordResultsByFloor[floorId || 'L1'] || [] : [])
|
|
)),
|
|
getLocationPreview: vi.fn().mockResolvedValue(null),
|
|
getRouteReadiness: vi.fn().mockResolvedValue({})
|
|
})
|
|
|
|
const expectMapListParity = (state: { results: MuseumPoi[], visiblePoiIds: string[] }) => {
|
|
expect(state.visiblePoiIds).toEqual(state.results.map((poi) => poi.id))
|
|
}
|
|
|
|
describe('GuideUseCase point search state', () => {
|
|
it('loads initial destinations through listDestinationPois and never searchPois', async () => {
|
|
const repository = createRepository()
|
|
const useCase = new GuideUseCase(
|
|
repository as unknown as GuideRepository,
|
|
{} as GuideModelRepository
|
|
)
|
|
|
|
await expect(useCase.getInitialSearchSpacePoints('L2'))
|
|
.resolves.toEqual(destinationsByFloor.L2)
|
|
|
|
expect(repository.listDestinationPois).toHaveBeenCalledWith('L2')
|
|
expect(repository.searchPois).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('creates the default state from current-floor destinations and relays all ten availability entries', async () => {
|
|
const repository = createRepository()
|
|
const useCase = new GuideUseCase(
|
|
repository as unknown as GuideRepository,
|
|
{} as GuideModelRepository
|
|
)
|
|
|
|
const state = await useCase.createInitialPoiSearchState('L1')
|
|
|
|
expect(state).toMatchObject({
|
|
mode: 'default',
|
|
floorId: 'L1',
|
|
floorLabel: '1F',
|
|
keyword: '',
|
|
categoryId: ''
|
|
})
|
|
expect(state.results).toEqual(destinationsByFloor.L1)
|
|
expectMapListParity(state)
|
|
expect(state.categories.map((item) => ({
|
|
id: item.definition.id,
|
|
count: item.count,
|
|
disabled: item.disabled
|
|
}))).toEqual(availability)
|
|
expect(repository.getQuickFindCategoryAvailability).toHaveBeenCalledWith('L1')
|
|
expect(repository.listDestinationPois).toHaveBeenCalledWith('L1')
|
|
expect(repository.searchPois).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('keeps category, keyword, clear, and floor-change states on canonical result IDs', async () => {
|
|
const repository = createRepository()
|
|
const useCase = new GuideUseCase(
|
|
repository as unknown as GuideRepository,
|
|
{} as GuideModelRepository
|
|
)
|
|
|
|
const initial = await useCase.createInitialPoiSearchState('L1')
|
|
const category = await useCase.selectPoiSearchCategory(initial, 'restroom')
|
|
|
|
expect(category).toMatchObject({
|
|
mode: 'category',
|
|
floorId: 'L1',
|
|
categoryId: 'restroom'
|
|
})
|
|
expect(category.results).toEqual(quickResultsByFloor.L1.restroom)
|
|
expectMapListParity(category)
|
|
expect(repository.listQuickFindPois).toHaveBeenCalledWith('restroom', 'L1')
|
|
expect(repository.searchPois).not.toHaveBeenCalled()
|
|
|
|
const keyword = await useCase.searchPoiKeyword(category, 'shop')
|
|
|
|
expect(keyword).toMatchObject({
|
|
mode: 'keyword',
|
|
floorId: 'L1',
|
|
keyword: 'shop',
|
|
categoryId: ''
|
|
})
|
|
expect(keyword.results).toEqual(keywordResultsByFloor.L1)
|
|
expectMapListParity(keyword)
|
|
expect(repository.searchPois).toHaveBeenLastCalledWith('shop', 'L1')
|
|
|
|
const switchedFloor = await useCase.changePoiSearchFloor(keyword, 'L2')
|
|
|
|
expect(switchedFloor).toMatchObject({
|
|
mode: 'keyword',
|
|
floorId: 'L2',
|
|
floorLabel: '2F',
|
|
keyword: 'shop',
|
|
categoryId: ''
|
|
})
|
|
expect(switchedFloor.results).toEqual(keywordResultsByFloor.L2)
|
|
expectMapListParity(switchedFloor)
|
|
expect(repository.searchPois).toHaveBeenLastCalledWith('shop', 'L2')
|
|
|
|
const cleared = await useCase.clearPoiSearch(switchedFloor)
|
|
|
|
expect(cleared).toMatchObject({
|
|
mode: 'default',
|
|
floorId: 'L2',
|
|
floorLabel: '2F',
|
|
keyword: '',
|
|
categoryId: ''
|
|
})
|
|
expect(cleared.results).toEqual(destinationsByFloor.L2)
|
|
expectMapListParity(cleared)
|
|
expect(repository.listDestinationPois).toHaveBeenLastCalledWith('L2')
|
|
})
|
|
})
|