55 lines
3.5 KiB
TypeScript
55 lines
3.5 KiB
TypeScript
import { flushPromises, mount } from '@vue/test-utils'
|
|||
|
|
import { beforeEach, expect, it, vi } from 'vitest'
|
||
|
|
import { ApiResponseError } from '@/shared/api/http'
|
||
|
|
import ResourcePhotoPanel from './ResourcePhotoPanel.vue'
|
||
|
|
import * as api from '../api/resourcePhotos'
|
||
|
|
const security = vi.hoisted(() => ({ writable: true }))
|
||
|
|
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => security.writable }) }))
|
||
|
|
vi.mock('../api/resourcePhotos', () => ({ getResourcePhoto: vi.fn(), uploadResourcePhoto: vi.fn(), downloadResourcePhoto: vi.fn() }))
|
||
|
|
const photo = { fileId: '81', fileName: 'driver.png', contentType: 'image/png', sizeBytes: 3 }
|
||
|
|
const options = { props: { kind: 'DRIVER' as const, resourceId: '7' }, global: { directives: { loading: () => undefined }, stubs: {
|
||
|
|
ElButton: { props: ['disabled'], template: '<button :disabled="disabled"><slot /></button>' },
|
||
|
|
AppDialog: { props: ['modelValue'], template: '<div v-if="modelValue"><slot /></div>' },
|
||
|
|
} } }
|
||
|
|
beforeEach(() => {
|
||
|
|
vi.resetAllMocks(); security.writable = true
|
||
|
|
vi.mocked(api.getResourcePhoto).mockResolvedValue({ recordVersion: '2', photo })
|
||
|
|
URL.createObjectURL = vi.fn().mockReturnValue('blob:photo'); URL.revokeObjectURL = vi.fn()
|
||
|
|
})
|
||
|
|
it('keeps reading independent from upload permission and downloads only on preview', async () => {
|
||
|
|
security.writable = false
|
||
|
|
vi.mocked(api.downloadResourcePhoto).mockResolvedValue(new Blob(['png'], { type: 'image/png' }))
|
||
|
|
const wrapper = mount(ResourcePhotoPanel, options); await flushPromises()
|
||
|
|
expect(wrapper.text()).not.toContain('上传 / 替换')
|
||
|
|
expect(api.downloadResourcePhoto).not.toHaveBeenCalled()
|
||
|
|
await wrapper.get('button').trigger('click'); await flushPromises()
|
||
|
|
expect(api.downloadResourcePhoto).toHaveBeenCalledWith('DRIVER', '7', '81')
|
||
|
|
wrapper.unmount(); expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:photo')
|
||
|
|
})
|
||
|
|
it('submits the independent photo version and releases busy state after failure', async () => {
|
||
|
|
vi.mocked(api.uploadResourcePhoto).mockRejectedValueOnce(new ApiResponseError('版本冲突', 'A0409'))
|
||
|
|
.mockResolvedValueOnce({ recordVersion: '3', photo })
|
||
|
|
const wrapper = mount(ResourcePhotoPanel, options); await flushPromises()
|
||
|
|
const input = wrapper.get('input[type="file"]'), file = new File(['png'], 'driver.png', { type: 'image/png' })
|
||
|
|
Object.defineProperty(input.element, 'files', { configurable: true, value: [file] })
|
||
|
|
await input.trigger('change'); await flushPromises()
|
||
|
|
expect(api.uploadResourcePhoto).toHaveBeenCalledWith('DRIVER', '7', '2', file)
|
||
|
|
expect(wrapper.text()).toContain('版本冲突')
|
||
|
|
await input.trigger('change'); await flushPromises()
|
||
|
|
expect(api.uploadResourcePhoto).toHaveBeenCalledTimes(2)
|
||
|
|
wrapper.unmount()
|
||
|
|
})
|
||
|
|
it('does not apply an upload result to a different resource', async () => {
|
||
|
|
let finish!: (value: api.ResourcePhotoDetail) => void
|
||
|
|
vi.mocked(api.uploadResourcePhoto).mockImplementation(() => new Promise(resolve => { finish = resolve }))
|
||
|
|
const wrapper = mount(ResourcePhotoPanel, options); await flushPromises()
|
||
|
|
const input = wrapper.get('input[type="file"]')
|
||
|
|
Object.defineProperty(input.element, 'files', { value: [new File(['png'], 'driver.png', { type: 'image/png' })] })
|
||
|
|
await input.trigger('change')
|
||
|
|
vi.mocked(api.getResourcePhoto).mockResolvedValue({ recordVersion: '0', photo: null })
|
||
|
|
await wrapper.setProps({ resourceId: '8' }); await flushPromises()
|
||
|
|
finish({ recordVersion: '3', photo }); await flushPromises()
|
||
|
|
expect(wrapper.text()).toContain('尚未上传司机照片')
|
||
|
|
wrapper.unmount()
|
||
|
|
})
|