- 使用真实讲解编号解析接口替换本地演示映射 - 成功后复用现有详情页并自动播放讲解 - 增加查询状态、业务错误提示和相关测试 Made-with: Proma
This commit is contained in:
@@ -68,6 +68,58 @@ describe('DefaultAudioPlayInfoRepository unified stop detail', () => {
|
||||
expect(request).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('resolves a museum code through info-by-code and preserves the code string', async () => {
|
||||
const requestUrls: string[] = []
|
||||
vi.stubGlobal('uni', {
|
||||
request: ({ url, success }: { url: string, success: (response: unknown) => void }) => {
|
||||
requestUrls.push(url)
|
||||
success({
|
||||
statusCode: 200,
|
||||
data: {
|
||||
code: 0,
|
||||
data: {
|
||||
available: true,
|
||||
targetType: 'STOP',
|
||||
targetId: '865546652298080256',
|
||||
resolvedStopId: '865546652298080256',
|
||||
lang: 'zh-CN',
|
||||
title: '手持折射望远镜',
|
||||
imageStatus: 'READY',
|
||||
playTargetType: 'STOP',
|
||||
playTargetId: '865546652298080256',
|
||||
hasAudio: true,
|
||||
supportedLanguages: ['zh-CN', 'yue-HK'],
|
||||
audioStatus: 'READY'
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
const repository = new DefaultAudioPlayInfoRepository()
|
||||
|
||||
const result = await repository.getStopInfoByCode('0101', 'zh-CN')
|
||||
|
||||
expect(requestUrls).toEqual([
|
||||
'/app-api/gis/guide/stop/info-by-code?code=0101&lang=zh-CN'
|
||||
])
|
||||
expect(result.resolvedStopId).toBe('865546652298080256')
|
||||
expect(result.title).toBe('手持折射望远镜')
|
||||
})
|
||||
|
||||
it('preserves info-by-code business error codes', async () => {
|
||||
vi.stubGlobal('uni', {
|
||||
request: ({ success }: { success: (response: unknown) => void }) => {
|
||||
success({ statusCode: 200, data: { code: 1020007001, msg: '馆方讲解点编号不存在', data: null } })
|
||||
}
|
||||
})
|
||||
const repository = new DefaultAudioPlayInfoRepository()
|
||||
|
||||
await expect(repository.getStopInfoByCode('9999')).rejects.toMatchObject({
|
||||
message: '馆方讲解点编号不存在',
|
||||
code: 1020007001
|
||||
})
|
||||
})
|
||||
|
||||
it('surfaces a business missing-detail response as a detail error', async () => {
|
||||
vi.stubGlobal('uni', {
|
||||
request: ({ success }: { success: (response: unknown) => void }) => {
|
||||
|
||||
@@ -256,6 +256,26 @@ describe('讲解详情音频优先布局', () => {
|
||||
}), expect.objectContaining({ displayMode: 'mini' }))
|
||||
})
|
||||
|
||||
it('编号入口加载详情后自动发起一次播放', async () => {
|
||||
mount(ExhibitDetail, {
|
||||
global: { stubs: { GuidePageFrame: GuidePageFrameStub } }
|
||||
})
|
||||
|
||||
await mocks.onLoadHandler?.({
|
||||
id: exhibit.id,
|
||||
stopId: 'stop-1',
|
||||
autoplay: '1',
|
||||
entry: 'code'
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
expect(mocks.play).toHaveBeenCalledTimes(1)
|
||||
expect(mocks.play).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ audioUrl: exhibit.audioUrl }),
|
||||
expect.objectContaining({ displayMode: 'mini' })
|
||||
)
|
||||
})
|
||||
|
||||
it('加载中使用同一高度底栏和不确定进度', async () => {
|
||||
audioState.currentAudio.value = { id: 'audio-1' }
|
||||
audioState.currentSource.value = { targetType: 'STOP', targetId: 'stop-1', lang: 'en-US' }
|
||||
|
||||
34
tests/unit/ExplainCodeEntry.spec.ts
Normal file
34
tests/unit/ExplainCodeEntry.spec.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
explainCodeDetailUrl,
|
||||
explainCodeErrorMessage,
|
||||
normalizeExplainCode
|
||||
} from '@/utils/explainCodeEntry'
|
||||
|
||||
describe('讲解编号入口', () => {
|
||||
it('清理首尾空格但保留编号前导零', () => {
|
||||
expect(normalizeExplainCode(' 0231 ')).toBe('0231')
|
||||
})
|
||||
|
||||
it.each([
|
||||
[1020007000, '请输入有效的 4 位讲解编号'],
|
||||
[1020007001, '未找到该讲解编号,请确认后重试'],
|
||||
[1020007002, '该编号的讲解内容暂不可用'],
|
||||
[429, '查询过于频繁,请稍后再试']
|
||||
])('将业务错误码 %s 转换为明确提示', (code, message) => {
|
||||
expect(explainCodeErrorMessage({ code })).toBe(message)
|
||||
})
|
||||
|
||||
it('未知错误使用网络重试提示', () => {
|
||||
expect(explainCodeErrorMessage(new Error('offline'))).toBe('编号查询失败,请检查网络后重试')
|
||||
})
|
||||
|
||||
it('详情路由携带讲解点和自动播放意图', () => {
|
||||
const url = explainCodeDetailUrl({ code: '1001', stopId: '865546652298080256' })
|
||||
expect(url.split('?')[0]).toBe('/pages/exhibit/detail')
|
||||
expect(url).toContain('stopId=865546652298080256')
|
||||
expect(url).toContain('autoplay=1')
|
||||
expect(url).toContain('entry=code')
|
||||
expect(url).toContain('guideCode=1001')
|
||||
})
|
||||
})
|
||||
@@ -67,6 +67,51 @@ describe('讲解展厅选择列表', () => {
|
||||
expect(wrapper.emitted('hallClick')).toEqual([['hall-evolution']])
|
||||
})
|
||||
|
||||
it('展厅阶段可输入讲解编号并保留前导零', async () => {
|
||||
const wrapper = mount(ExplainHallSelect, {
|
||||
props: { stage: 'hall' }
|
||||
})
|
||||
|
||||
expect(wrapper.get('.code-entry-title').text()).toBe('编号讲解')
|
||||
expect(wrapper.get('.hall-section-title').text()).toBe('按展厅选择')
|
||||
expect(wrapper.find('.code-entry-arrow').exists()).toBe(false)
|
||||
|
||||
await wrapper.get('.code-entry-button').trigger('tap')
|
||||
expect(wrapper.get('.header-title').text()).toBe('编号讲解')
|
||||
expect(wrapper.find('.code-entry-close').exists()).toBe(false)
|
||||
expect(wrapper.get('.code-entry-sheet-title').text()).toBe('输入讲解编号')
|
||||
expect(wrapper.findAll('.code-digit-cell')).toHaveLength(4)
|
||||
expect(wrapper.text()).toContain('请输入展品标签上的四位编号')
|
||||
|
||||
await wrapper.get('.code-entry-native-input').trigger('input', { detail: { value: '02a' } })
|
||||
expect(wrapper.get('.code-entry-action.primary').attributes('disabled')).toBeDefined()
|
||||
await wrapper.get('.code-entry-native-input').trigger('confirm')
|
||||
expect(wrapper.get('.code-entry-error').text()).toBe('请输入完整的 4 位讲解编号')
|
||||
|
||||
await wrapper.get('.code-entry-native-input').trigger('input', { detail: { value: '0231' } })
|
||||
expect(wrapper.findAll('.code-digit-value').map((item) => item.text())).toEqual(['0', '2', '3', '1'])
|
||||
await wrapper.get('.code-entry-action.primary').trigger('tap')
|
||||
|
||||
expect(wrapper.emitted('codeSubmit')).toEqual([['0231']])
|
||||
expect(wrapper.find('.code-entry-sheet').exists()).toBe(true)
|
||||
|
||||
await wrapper.setProps({ codeSubmitting: true })
|
||||
expect(wrapper.get('.code-entry-native-input').attributes('disabled')).toBeDefined()
|
||||
expect(wrapper.get('.code-entry-action.primary').text()).toBe('正在查询')
|
||||
|
||||
wrapper.vm.showCodeEntryError('未找到该讲解编号,请确认后重试')
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.get('.code-entry-error').text()).toBe('未找到该讲解编号,请确认后重试')
|
||||
|
||||
await wrapper.get('.header-back').trigger('tap')
|
||||
expect(wrapper.find('.code-entry-sheet').exists()).toBe(false)
|
||||
expect(wrapper.get('.header-title').text()).toBe('免费讲解')
|
||||
expect(wrapper.emitted('back')).toBeUndefined()
|
||||
|
||||
await wrapper.get('.header-back').trigger('tap')
|
||||
expect(wrapper.emitted('back')).toEqual([[]])
|
||||
})
|
||||
|
||||
it('展厅阶段使用完整展厅卡片资源', () => {
|
||||
const wrapper = mount(ExplainHallSelect, {
|
||||
props: {
|
||||
@@ -84,9 +129,9 @@ describe('讲解展厅选择列表', () => {
|
||||
|
||||
const card = wrapper.get('.hall-overview-card')
|
||||
expect(wrapper.get('.header-title').text()).toBe('免费讲解')
|
||||
expect(card.attributes('style')).toContain('--hall-card-color: #d9b06c')
|
||||
expect(card.attributes('style')).toContain('--hall-card-color: #bb965a')
|
||||
expect(card.get('.hall-full-card-image').attributes('src'))
|
||||
.toBe('/static/icons/halls/custom-cards/dinosaur.png')
|
||||
.toBe('/static/icons/halls/portal-icons/dinosaur.png')
|
||||
expect(card.find('.hall-overview-copy').exists()).toBe(false)
|
||||
expect(card.find('.hall-go-entry').exists()).toBe(false)
|
||||
})
|
||||
|
||||
@@ -33,11 +33,28 @@ const createUseCase = (detail: GuideStopDetail) => {
|
||||
getExhibitById: vi.fn().mockResolvedValue(null),
|
||||
listExplainExhibits: vi.fn().mockResolvedValue([])
|
||||
} as unknown as ExplainRepository
|
||||
const audio = { getStopDetail: vi.fn().mockResolvedValue(detail) } as unknown as AudioPlayInfoRepository
|
||||
const audio = {
|
||||
getStopDetail: vi.fn().mockResolvedValue(detail),
|
||||
getStopInfoByCode: vi.fn().mockResolvedValue({
|
||||
available: true,
|
||||
resolvedStopId: 'resolved-stop-1',
|
||||
playTargetId: 'resolved-stop-1',
|
||||
targetId: 'resolved-stop-1'
|
||||
})
|
||||
} as unknown as AudioPlayInfoRepository
|
||||
return { useCase: new ExplainUseCase(explain, audio), explain, audio }
|
||||
}
|
||||
|
||||
describe('ExplainUseCase unified stop detail', () => {
|
||||
it('resolves the museum code to the backend stop ID', async () => {
|
||||
const { useCase, audio } = createUseCase(createDetail())
|
||||
|
||||
const result = await useCase.resolveExplainCode('1001', 'zh-CN')
|
||||
|
||||
expect(audio.getStopInfoByCode).toHaveBeenCalledWith('1001', 'zh-CN')
|
||||
expect(result).toEqual({ code: '1001', stopId: 'resolved-stop-1' })
|
||||
})
|
||||
|
||||
it('uses stopId once in remote mode without requesting a catalog fallback', async () => {
|
||||
const originalMode = dataSourceConfig.explainContentMode
|
||||
Object.assign(dataSourceConfig, { explainContentMode: 'remote' })
|
||||
|
||||
Reference in New Issue
Block a user