chore: stage all in-progress work before repo split
CI / test (push) Has been cancelled

Web app: new entities (Image, RenderedAsset, SharedImage, Token,
DeviceImageHistory), enums, repositories, controllers, message handlers,
migrations, tests, frontend upload/library/sticker UI, Vue components.

Firmware: EPD background screen binaries + gen scripts, setup_bg header.

Infra: ddev config, test bundle, gitignore coverage dir.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 12:11:31 -04:00
parent dd0970ed7c
commit 4002ff9fbf
156 changed files with 27333 additions and 92 deletions
+165
View File
@@ -0,0 +1,165 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useImagesStore } from '@/stores/images'
import type { Image } from '@/types'
const makeImage = (overrides: Partial<Image> = {}): Image => ({
id: 1,
originalFilename: 'photo.jpg',
thumbnailUrl: '/thumb/1.jpg',
originalUrl: '/orig/1.jpg',
uploadedAt: '2026-01-01T00:00:00Z',
approvedDeviceIds: [],
cropParams: null,
stickerState: null,
...overrides,
})
describe('images store', () => {
beforeEach(() => {
vi.unstubAllGlobals()
vi.restoreAllMocks()
setActivePinia(createPinia())
})
afterEach(() => {
// unstubAllGlobals is handled in beforeEach so stubs don't leak
// even if a test throws before afterEach runs
})
it('fetchImages success populates images and clears loading', async () => {
const mockImages = [makeImage(), makeImage({ id: 2 })]
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve(mockImages),
}))
const store = useImagesStore()
await store.fetchImages()
expect(store.images).toEqual(mockImages)
expect(store.loading).toBe(false)
expect(store.error).toBeNull()
})
it('fetchImages network error sets error state', async () => {
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('Net error')))
const store = useImagesStore()
await store.fetchImages()
expect(store.images).toEqual([])
expect(store.error).toBe('Net error')
expect(store.loading).toBe(false)
})
it('uploadImage prepends to images list on success', async () => {
const existing = makeImage({ id: 1 })
const newImage = makeImage({ id: 2 })
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve(newImage),
}))
const store = useImagesStore()
store.images = [existing]
const file = new File(['data'], 'photo.jpg', { type: 'image/jpeg' })
const result = await store.uploadImage(file)
expect(result).toEqual(newImage)
expect(store.images[0]).toEqual(newImage)
expect(store.images).toHaveLength(2)
})
it('uploadImage throws with error message on failure', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: false,
json: () => Promise.resolve({ error: 'File too large' }),
}))
const store = useImagesStore()
const file = new File(['data'], 'photo.jpg', { type: 'image/jpeg' })
await expect(store.uploadImage(file)).rejects.toThrow('File too large')
})
it('deleteImage removes image from list', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: true }))
const store = useImagesStore()
store.images = [makeImage({ id: 1 }), makeImage({ id: 2 })]
await store.deleteImage(1)
expect(store.images).toHaveLength(1)
expect(store.images[0].id).toBe(2)
})
it('deleteImage throws on failure', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: false }))
const store = useImagesStore()
store.images = [makeImage()]
await expect(store.deleteImage(1)).rejects.toThrow('Delete failed')
})
it('setApproval updates image in list', async () => {
const original = makeImage({ id: 1, approvedDeviceIds: [] })
const updated = makeImage({ id: 1, approvedDeviceIds: [42] })
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve(updated),
}))
const store = useImagesStore()
store.images = [original]
await store.setApproval(1, 42, true)
expect(store.images[0].approvedDeviceIds).toEqual([42])
})
it('fetchPendingCount stores the count', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ count: 5 }),
}))
const store = useImagesStore()
await store.fetchPendingCount()
expect(store.pendingCount).toBe(5)
})
it('approveShared decrements pendingCount', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ id: 1, status: 'approved' }),
}))
const store = useImagesStore()
store.pendingCount = 3
await store.approveShared(1, [42])
expect(store.pendingCount).toBe(2)
})
it('declineShared decrements pendingCount', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ id: 1, status: 'declined' }),
}))
const store = useImagesStore()
store.pendingCount = 2
await store.declineShared(1)
expect(store.pendingCount).toBe(1)
})
})