Vue 3 + Vite + Tailwind frontend, Fastify API server, execa shell bridge. All components built, server validated, 41 unit tests passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
71 lines
2.3 KiB
JavaScript
71 lines
2.3 KiB
JavaScript
import { describe, it, expect, vi, afterEach } from 'vitest'
|
|
import { mount } from '@vue/test-utils'
|
|
import ImageInput from '../ImageInput.vue'
|
|
|
|
function makeFile(name, type, size = 100) {
|
|
return new File(['x'.repeat(size)], name, { type })
|
|
}
|
|
|
|
function setFiles(input, files) {
|
|
Object.defineProperty(input.element, 'files', { value: files, configurable: true })
|
|
}
|
|
|
|
describe('ImageInput', () => {
|
|
afterEach(() => vi.unstubAllGlobals())
|
|
|
|
it('renders the drop zone and file input', () => {
|
|
const wrapper = mount(ImageInput)
|
|
expect(wrapper.find('label').exists()).toBe(true)
|
|
expect(wrapper.find('input[type="file"]').exists()).toBe(true)
|
|
})
|
|
|
|
it('shows error for non-image file', async () => {
|
|
const wrapper = mount(ImageInput)
|
|
const input = wrapper.find('input[type="file"]')
|
|
setFiles(input, [makeFile('doc.pdf', 'application/pdf')])
|
|
await input.trigger('change')
|
|
expect(wrapper.text()).toContain('Only image files are supported.')
|
|
})
|
|
|
|
it('shows error for file exceeding 20 MB', async () => {
|
|
const wrapper = mount(ImageInput)
|
|
const input = wrapper.find('input[type="file"]')
|
|
setFiles(input, [makeFile('big.png', 'image/png', 21 * 1024 * 1024)])
|
|
await input.trigger('change')
|
|
expect(wrapper.text()).toContain('20 MB')
|
|
})
|
|
|
|
it('emits image-ready with correct shape on valid image', async () => {
|
|
class MockFileReader {
|
|
readAsDataURL() {
|
|
this.onload({ target: { result: 'data:image/png;base64,abc' } })
|
|
}
|
|
}
|
|
vi.stubGlobal('FileReader', MockFileReader)
|
|
|
|
const wrapper = mount(ImageInput)
|
|
const input = wrapper.find('input[type="file"]')
|
|
setFiles(input, [makeFile('photo.png', 'image/png')])
|
|
await input.trigger('change')
|
|
|
|
const emitted = wrapper.emitted('image-ready')
|
|
expect(emitted).toBeTruthy()
|
|
expect(emitted[0][0]).toMatchObject({
|
|
dataUrl: 'data:image/png;base64,abc',
|
|
mimeType: 'image/png',
|
|
filename: 'photo.png',
|
|
})
|
|
})
|
|
|
|
it('attaches paste listener on mount and removes it on unmount', () => {
|
|
const add = vi.spyOn(window, 'addEventListener')
|
|
const remove = vi.spyOn(window, 'removeEventListener')
|
|
|
|
const wrapper = mount(ImageInput)
|
|
expect(add).toHaveBeenCalledWith('paste', expect.any(Function))
|
|
|
|
wrapper.unmount()
|
|
expect(remove).toHaveBeenCalledWith('paste', expect.any(Function))
|
|
})
|
|
})
|