1
0

feat: settings data section with export and backups

This commit is contained in:
2026-09-03 23:17:21 +02:00
parent b33399c865
commit 9b2bc22d59
2 changed files with 66 additions and 2 deletions

View File

@@ -20,6 +20,8 @@ vi.mock('../src/api.js', async (importOriginal) => {
createUser: vi.fn(),
deleteUser: vi.fn(),
logout: vi.fn(),
triggerBackup: vi.fn(),
getBackups: vi.fn(),
},
}
})
@@ -56,12 +58,13 @@ function stubAuthFetch(user: User) {
}
beforeEach(() => {
for (const fn of [api.getSettings, api.putSettings, api.syncStatus, api.startSync, api.listUsers, api.createUser, api.deleteUser, api.logout] as const) {
for (const fn of [api.getSettings, api.putSettings, api.syncStatus, api.startSync, api.listUsers, api.createUser, api.deleteUser, api.logout, api.triggerBackup, api.getBackups] as const) {
vi.mocked(fn).mockReset()
}
vi.mocked(api.getSettings).mockResolvedValue(emptyView as never)
vi.mocked(api.syncStatus).mockResolvedValue(idleSync as never)
vi.mocked(api.listUsers).mockResolvedValue({ users: [admin] } as never)
vi.mocked(api.getBackups).mockResolvedValue({ backups: [] } as never)
stubAuthFetch(admin)
})
@@ -179,4 +182,23 @@ describe('SettingsPage', () => {
await waitFor(() => expect(screen.getByText(/42 albums synced/i)).toBeTruthy(), { timeout: 3000 })
expect(vi.mocked(api.syncStatus).mock.calls.length).toBeGreaterThanOrEqual(3)
})
it('data section: export link, backup now, backups list', async () => {
vi.mocked(api.triggerBackup).mockResolvedValue({ file: 'record-shop-2026-09-03-120000.db' } as never)
vi.mocked(api.getBackups).mockResolvedValue({
backups: [{ file: 'record-shop-2026-09-03-120000.db', sizeBytes: 12345, createdAt: '2026-09-03T12:00:00Z' }],
} as never)
renderSettings()
expect(await screen.findByRole('link', { name: /export json/i })).toHaveProperty('href')
await userEvent.click(screen.getByRole('button', { name: /back up now/i }))
await waitFor(() => expect(api.triggerBackup).toHaveBeenCalled())
await waitFor(() => expect(screen.getByText(/record-shop-2026-09-03-120000\.db/)).toBeTruthy())
})
it('backup failure surfaces in the flash banner', async () => {
vi.mocked(api.triggerBackup).mockRejectedValue(new TypeError('fetch failed'))
renderSettings()
await userEvent.click(await screen.findByRole('button', { name: /back up now/i }))
await waitFor(() => expect(screen.getByText(/backup failed/i)).toBeTruthy())
})
})