Files
babymonitor/frontend/tests/unit/api/client.test.ts
T

300 lines
10 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import * as client from '../../../src/api/client';
describe('api client', () => {
beforeEach(() => {
vi.stubGlobal('fetch', vi.fn());
});
describe('utility functions', () => {
it('localHHMMToUTCHHMM converts correctly (assuming UTC+0 for simplicity in test, or handling offset)', () => {
const result = client.localHHMMToUTCHHMM('12:00');
expect(result).toMatch(/^\d{2}:\d{2}$/);
});
it('localToUTC converts to ISO string', () => {
const dateStr = '2026-01-01T12:00';
const result = client.localToUTC(dateStr);
expect(result).toBe(new Date(dateStr).toISOString());
});
});
describe('fetch functions', () => {
describe('fetchSummary', () => {
it('returns data on success', async () => {
const mockData = { total: 10 };
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: async () => mockData,
} as any);
const result = await client.fetchSummary('2026-01-01', '2026-01-02');
expect(result).toEqual(mockData);
expect(fetch).toHaveBeenCalledWith(expect.stringContaining('/api/stats/summary'));
});
it('throws error on non-ok response', async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
} as any);
await expect(client.fetchSummary('2026-01-01', '2026-01-02')).rejects.toThrow('Failed to fetch summary');
});
});
describe('fetchDailySummary', () => {
it('returns data on success and verifies query params', async () => {
const mockData = { some: 'data' } as any;
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: async () => mockData,
} as any);
const from_date = '2026-01-01';
const to_date = '2026-01-02';
const day_start = '22:00';
const day_end = '08:00';
const timezone_offset = -360;
const result = await client.fetchDailySummary(from_date, to_date, day_start, day_end, timezone_offset);
expect(result).toEqual(mockData);
const params = new URLSearchParams({
from_date,
to_date,
day_start,
day_end,
timezone_offset: timezone_offset.toString()
});
const expectedUrl = `/api/stats/daily-summary?${params.toString()}`;
expect(fetch).toHaveBeenCalledWith(expect.stringContaining(expectedUrl));
});
it('throws error on non-ok response', async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
} as any);
await expect(client.fetchDailySummary('2026-01-01', '2026-01-02', '22:00', '08:00')).rejects.toThrow('Failed to fetch daily-summary');
});
});
describe('fetchTimetable', () => {
it('returns data on success and verifies query params', async () => {
const mockData = { some: 'timetable' } as any;
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: async () => mockData,
} as any);
const from_date = '2026-01-01';
const to_date = '2026-01-02';
const day_start = '22:00';
const day_end = '08:00';
const timezone_offset = 0;
const result = await client.fetchTimetable(from_date, to_date, day_start, day_end, timezone_offset);
expect(result).toEqual(mockData);
const params = new URLSearchParams({
from_date,
to_date,
day_start,
day_end,
timezone_offset: timezone_offset.toString()
});
const expectedUrl = `/api/stats/timetable?${params.toString()}`;
expect(fetch).toHaveBeenCalledWith(expect.stringContaining(expectedUrl));
});
it('throws error on non-ok response', async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
} as any);
await expect(client.fetchTimetable('2026-01-01', '2026-01-02', '22:00', '08:00')).rejects.toThrow('Failed to fetch timetable');
});
});
describe('fetchWakeSleepTimes', () => {
it('returns data on success and transforms time correctly', async () => {
const mockData = { wakeup_ref: '06:30', bedtime_ref: '21:00' };
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: async () => mockData,
} as any);
const offsetSpy = vi.spyOn(Date.prototype, 'getTimezoneOffset').mockReturnValue(-60);
try {
const result = await client.fetchWakeSleepTimes('2026-01-01', '2026-01-02', '07:30', '22:00', 0);
expect(result).toEqual(mockData);
expect(fetch).toHaveBeenCalledWith(
expect.stringContaining('from_date=2026-01-01&to_date=2026-01-02&wakeup_ref=06%3A30&bedtime_ref=21%3A00&timezone_offset=0')
);
} finally {
offsetSpy.mockRestore();
}
});
it('throws error on failure', async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
} as any);
await expect(client.fetchWakeSleepTimes('2026-01-01', '2026-01-02', '07:30', '22:00')).rejects.toThrow('Failed to fetch wake-sleep-times');
});
});
describe('fetchActiveSession', () => {
it('returns session on success', async () => {
const mockSession = { id: 1, start_time: '2026-01-01T12:00', end_time: null };
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: async () => mockSession,
} as any);
const result = await client.fetchActiveSession();
expect(result).toEqual(mockSession);
});
it('returns null on 404/not-ok', async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
} as any);
const result = await client.fetchActiveSession();
expect(result).toBeNull();
});
});
describe('fetchSessions', () => {
it('returns session list on success', async () => {
const mockResponse = { sessions: [{ id: 1, start_time: '2026-01-01T12:00' }] };
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: async () => mockResponse,
} as any);
const result = await client.fetchSessions();
expect(result).toEqual(mockResponse);
expect(fetch).toHaveBeenCalledWith(expect.stringContaining('/api/sessions'));
});
it('throws error on non-ok response', async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
} as any);
await expect(client.fetchSessions()).rejects.toThrow('Failed to fetch sessions');
});
});
describe('Session mutations', () => {
it('createSession sends POST request with body', async () => {
const payload = { start_time: '2026-01-01T12:00' };
const mockResponse = { id: 1, ...payload };
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: async () => mockResponse,
} as any);
const result = await client.createSession(payload as any);
expect(result).toEqual(mockResponse);
expect(fetch).toHaveBeenCalledWith(
expect.stringContaining('/api/sessions'),
expect.objectContaining({
method: 'POST',
body: JSON.stringify(payload),
})
);
});
it('createSession throws error on non-ok response', async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
} as any);
await expect(client.createSession({ start_time: '2026-01-01T12:00' } as any)).rejects.toThrow('Failed to create session');
});
it('endSession sends PATCH request', async () => {
const sessionId = 1;
const payload = { end_time: '2026-01-01T14:00' };
const mockResponse = { id: 1, ...payload };
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: async () => mockResponse,
} as any);
const result = await client.endSession(sessionId, payload as any);
expect(result).toEqual(mockResponse);
expect(fetch).toHaveBeenCalledWith(
expect.stringContaining(`/api/sessions/${sessionId}/end`),
expect.objectContaining({
method: 'PATCH',
body: JSON.stringify(payload),
})
);
});
it('endSession throws error on non-ok response', async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
} as any);
await expect(client.endSession(1, { end_time: '2026-01-01T14:00' } as any)).rejects.toThrow('Failed to end session');
});
it('deleteSession sends DELETE request', async () => {
const sessionId = 1;
vi.mocked(fetch).mockResolvedValue({
ok: true,
} as any);
await client.deleteSession(sessionId);
expect(fetch).toHaveBeenCalledWith(
expect.stringContaining(`/api/sessions/${sessionId}`),
expect.objectContaining({ method: 'DELETE' })
);
});
it('deleteSession throws error on non-ok response', async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
} as any);
await expect(client.deleteSession(1)).rejects.toThrow('Failed to delete session');
});
it('updateSession sends PATCH request', async () => {
const sessionId = 1;
const payload = { start_time: '2026-01-01T10:00' };
const mockResponse = { id: 1, ...payload };
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: async () => mockResponse,
} as any);
const result = await client.updateSession(sessionId, payload as any);
expect(result).toEqual(mockResponse);
expect(fetch).toHaveBeenCalledWith(
expect.stringContaining(`/api/sessions/${sessionId}`),
expect.objectContaining({
method: 'PATCH',
body: JSON.stringify(payload),
})
);
});
it('updateSession throws error on non-ok response', async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
} as any);
await expect(client.updateSession(1, { start_time: '2026-01-01T10:00' } as any)).rejects.toThrow('Failed to update session');
});
});
});
});