Spaces:
Sleeping
Sleeping
| import { describe, it, expect, beforeEach, vi } from 'vitest'; | |
| import { http, HttpResponse } from 'msw'; | |
| import { useTripStore } from '../../../src/store/tripStore'; | |
| import { filesApi } from '../../../src/api/client'; | |
| import { resetAllStores, seedStore } from '../../helpers/store'; | |
| import { buildTripFile } from '../../helpers/factories'; | |
| import { server } from '../../helpers/msw/server'; | |
| vi.mock('../../../src/api/websocket', () => ({ | |
| connect: vi.fn(), | |
| disconnect: vi.fn(), | |
| getSocketId: vi.fn(() => null), | |
| joinTrip: vi.fn(), | |
| leaveTrip: vi.fn(), | |
| addListener: vi.fn(), | |
| removeListener: vi.fn(), | |
| setRefetchCallback: vi.fn(), | |
| setPreReconnectHook: vi.fn(), | |
| })); | |
| beforeEach(() => { | |
| resetAllStores(); | |
| }); | |
| describe('filesSlice', () => { | |
| describe('loadFiles', () => { | |
| it('FE-FILES-001: loadFiles fetches and replaces files array', async () => { | |
| const staleFile = buildTripFile({ trip_id: 1, filename: 'stale.pdf' }); | |
| seedStore(useTripStore, { files: [staleFile] }); | |
| const freshFile = buildTripFile({ trip_id: 1, filename: 'fresh.pdf' }); | |
| server.use( | |
| http.get('/api/trips/1/files', () => HttpResponse.json({ files: [freshFile] })), | |
| ); | |
| await useTripStore.getState().loadFiles(1); | |
| const files = useTripStore.getState().files; | |
| expect(files).toHaveLength(1); | |
| expect(files[0].filename).toBe('fresh.pdf'); | |
| }); | |
| it('FE-FILES-002: loadFiles silently catches errors', async () => { | |
| server.use( | |
| http.get('/api/trips/1/files', () => | |
| HttpResponse.json({ message: 'Error' }, { status: 500 }) | |
| ), | |
| ); | |
| // Should not throw | |
| await useTripStore.getState().loadFiles(1); | |
| }); | |
| }); | |
| describe('addFile', () => { | |
| it('FE-FILES-003: addFile uploads and prepends file to files array', async () => { | |
| const existing = buildTripFile({ trip_id: 1, filename: 'existing.pdf' }); | |
| seedStore(useTripStore, { files: [existing] }); | |
| const uploaded = buildTripFile({ trip_id: 1, filename: 'new-upload.pdf' }); | |
| // FormData POST hangs on CI — mock at the API boundary instead of MSW. | |
| const uploadSpy = vi.spyOn(filesApi, 'upload').mockResolvedValueOnce({ file: uploaded }); | |
| const formData = new FormData(); | |
| formData.append('file', new Blob(['content'], { type: 'application/pdf' }), 'new-upload.pdf'); | |
| const result = await useTripStore.getState().addFile(1, formData); | |
| uploadSpy.mockRestore(); | |
| expect(result.filename).toBe('new-upload.pdf'); | |
| const files = useTripStore.getState().files; | |
| expect(files).toHaveLength(2); | |
| // prepends | |
| expect(files[0].filename).toBe('new-upload.pdf'); | |
| }); | |
| it('FE-FILES-004: addFile on failure throws', async () => { | |
| server.use( | |
| http.post('/api/trips/1/files', () => | |
| HttpResponse.json({ message: 'Error' }, { status: 500 }) | |
| ), | |
| ); | |
| const formData = new FormData(); | |
| await expect(useTripStore.getState().addFile(1, formData)).rejects.toThrow(); | |
| }); | |
| }); | |
| describe('deleteFile', () => { | |
| it('FE-FILES-005: deleteFile removes file from array after API success', async () => { | |
| const file1 = buildTripFile({ id: 10, trip_id: 1 }); | |
| const file2 = buildTripFile({ id: 20, trip_id: 1 }); | |
| seedStore(useTripStore, { files: [file1, file2] }); | |
| await useTripStore.getState().deleteFile(1, 10); | |
| const files = useTripStore.getState().files; | |
| expect(files).toHaveLength(1); | |
| expect(files[0].id).toBe(20); | |
| }); | |
| it('FE-FILES-006: deleteFile on failure throws', async () => { | |
| const file = buildTripFile({ id: 10, trip_id: 1 }); | |
| seedStore(useTripStore, { files: [file] }); | |
| server.use( | |
| http.delete('/api/trips/1/files/10', () => | |
| HttpResponse.json({ message: 'Error' }, { status: 500 }) | |
| ), | |
| ); | |
| await expect(useTripStore.getState().deleteFile(1, 10)).rejects.toThrow(); | |
| // File remains since server-first (only removes after success) | |
| expect(useTripStore.getState().files).toHaveLength(1); | |
| }); | |
| }); | |
| }); | |