Pagination (but according to agent, not done)
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,104 @@
|
||||
import React from 'react';
|
||||
import { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react';
|
||||
|
||||
interface PaginationProps {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
onPageChange: (page: number) => void;
|
||||
}
|
||||
|
||||
export function Pagination({ currentPage, totalPages, onPageChange }: PaginationProps) {
|
||||
if (totalPages <= 1) return null;
|
||||
|
||||
const getPageNumbers = () => {
|
||||
const pages: (number | string)[] = [];
|
||||
const maxVisiblePages = 5;
|
||||
|
||||
if (totalPages <= maxVisiblePages) {
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
// Always include first page
|
||||
pages.push(1);
|
||||
|
||||
if (currentPage > 3) {
|
||||
pages.push('...');
|
||||
}
|
||||
|
||||
// Calculate middle pages
|
||||
const start = Math.max(2, currentPage - 1);
|
||||
const end = Math.min(totalPages - 1, currentPage + 1);
|
||||
|
||||
for (let i = start; i <= end; i++) {
|
||||
if (!pages.includes(i)) {
|
||||
pages.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentPage < totalPages - 2) {
|
||||
pages.push('...');
|
||||
}
|
||||
|
||||
// Always include last page
|
||||
if (!pages.includes(totalPages)) {
|
||||
pages.push(totalPages);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
|
||||
const pages = getPageNumbers();
|
||||
|
||||
return (
|
||||
<div data-testid="pagination" className="flex items-center justify-center gap-1 mt-6 py-4">
|
||||
<button
|
||||
onClick={() => onPageChange(Math.max(1, currentPage - 1))}
|
||||
disabled={currentPage === 1}
|
||||
className="p-2 rounded-lg hover:bg-gray-100 disabled:opacity-30 disabled:cursor-not-allowed transition-colors text-gray-600"
|
||||
aria-label="Previous page"
|
||||
>
|
||||
<ChevronLeft size={20} />
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
{pages.map((page, idx) => {
|
||||
if (page === '...') {
|
||||
return (
|
||||
<div key={`ellipsis-${idx}`} className="px-2 text-gray-400">
|
||||
<MoreHorizontal size={16} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const pageNum = page as number;
|
||||
const isActive = currentPage === pageNum;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={pageNum}
|
||||
onClick={() => onPageChange(pageNum)}
|
||||
className={`min-w-[36px] h-9 px-2 rounded-lg text-sm font-medium transition-all ${
|
||||
isActive
|
||||
? 'bg-blue-600 text-white shadow-sm'
|
||||
: 'text-gray-600 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
{pageNum}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => onPageChange(Math.min(totalPages, currentPage + 1))}
|
||||
disabled={currentPage === totalPages}
|
||||
className="p-2 rounded-lg hover:bg-gray-100 disabled:opacity-30 disabled:cursor-not-allowed transition-colors text-gray-600"
|
||||
aria-label="Next page"
|
||||
>
|
||||
<ChevronRight size={20} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +1,29 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useSessions } from '../../hooks/useSleepSession';
|
||||
import { useSettings } from '../../hooks/useSettings';
|
||||
import { Pagination } from './Pagination';
|
||||
import { Edit2, Check, X, Trash2 } from 'lucide-react';
|
||||
import type { SleepSessionRead } from '../../types';
|
||||
import { formatDateTime, toLocalDatetimeInputValue, getDurationMinutes, formatMinutes } from '../../utils/time';
|
||||
|
||||
export function SessionList() {
|
||||
const { sessions, isLoading, updateSession, deleteSession, isDeleting } = useSessions();
|
||||
const { settings } = useSettings();
|
||||
const sessionsPerPage = settings.sessionsPerPage;
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [editingId, setEditingId] = useState<number | null>(null);
|
||||
const [editStart, setEditStart] = useState<string>('');
|
||||
const [editEnd, setEditEnd] = useState<string>('');
|
||||
|
||||
const totalPages = Math.ceil(sessions.length / sessionsPerPage);
|
||||
const clampedPage = Math.max(1, Math.min(currentPage, totalPages || 1));
|
||||
|
||||
const paginatedSessions = sessions.slice(
|
||||
(clampedPage - 1) * sessionsPerPage,
|
||||
clampedPage * sessionsPerPage
|
||||
);
|
||||
|
||||
if (isLoading) return <div className="text-center p-4">Loading sessions...</div>;
|
||||
if (!sessions.length) return <div className="text-center p-4 text-gray-500">No sessions recorded.</div>;
|
||||
|
||||
@@ -52,10 +66,10 @@ export function SessionList() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white p-4 rounded-xl shadow-sm border border-gray-200">
|
||||
<div className="session-list bg-white p-4 rounded-xl shadow-sm border border-gray-200">
|
||||
<h3 className="text-lg font-semibold mb-4 text-gray-800">Sleep Sessions</h3>
|
||||
<div className="space-y-2">
|
||||
{sessions.map((s) => (
|
||||
{paginatedSessions.map((s) => (
|
||||
<div key={s.id} className="border-b border-gray-100 last:border-0 py-3 sm:py-2">
|
||||
{editingId === s.id ? (
|
||||
<div className="flex flex-col space-y-3 py-1">
|
||||
@@ -140,6 +154,13 @@ export function SessionList() {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{sessions.length > sessionsPerPage && (
|
||||
<Pagination
|
||||
currentPage={clampedPage}
|
||||
totalPages={totalPages}
|
||||
onPageChange={setCurrentPage}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,15 +6,17 @@ interface SettingsPopoverProps {
|
||||
dayEnd: string;
|
||||
wakeupRef: string;
|
||||
bedtimeRef: string;
|
||||
onSave: (settings: { dayStart: string; dayEnd: string; wakeupRef: string; bedtimeRef: string }) => void;
|
||||
sessionsPerPage: number;
|
||||
onSave: (settings: { dayStart: string; dayEnd: string; wakeupRef: string; bedtimeRef: string; sessionsPerPage: number }) => void;
|
||||
}
|
||||
|
||||
export function SettingsPopover({ dayStart, dayEnd, wakeupRef, bedtimeRef, onSave }: SettingsPopoverProps) {
|
||||
export function SettingsPopover({ dayStart, dayEnd, wakeupRef, bedtimeRef, sessionsPerPage, onSave }: SettingsPopoverProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [localDayStart, setLocalDayStart] = useState(dayStart);
|
||||
const [localDayEnd, setLocalDayEnd] = useState(dayEnd);
|
||||
const [localWakeupRef, setLocalWakeupRef] = useState(wakeupRef);
|
||||
const [localBedtimeRef, setLocalBedtimeRef] = useState(bedtimeRef);
|
||||
const [localSessionsPerPage, setLocalSessionsPerPage] = useState(sessionsPerPage);
|
||||
const popoverRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -22,7 +24,8 @@ export function SettingsPopover({ dayStart, dayEnd, wakeupRef, bedtimeRef, onSav
|
||||
setLocalDayEnd(dayEnd);
|
||||
setLocalWakeupRef(wakeupRef);
|
||||
setLocalBedtimeRef(bedtimeRef);
|
||||
}, [dayStart, dayEnd, wakeupRef, bedtimeRef]);
|
||||
setLocalSessionsPerPage(sessionsPerPage);
|
||||
}, [dayStart, dayEnd, wakeupRef, bedtimeRef, sessionsPerPage]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
@@ -42,6 +45,7 @@ export function SettingsPopover({ dayStart, dayEnd, wakeupRef, bedtimeRef, onSav
|
||||
dayEnd: localDayEnd,
|
||||
wakeupRef: localWakeupRef,
|
||||
bedtimeRef: localBedtimeRef,
|
||||
sessionsPerPage: localSessionsPerPage,
|
||||
});
|
||||
setIsOpen(false);
|
||||
};
|
||||
@@ -65,44 +69,58 @@ export function SettingsPopover({ dayStart, dayEnd, wakeupRef, bedtimeRef, onSav
|
||||
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label htmlFor="day-start" className="block text-xs text-gray-500 mb-1">Day starts (HH:MM)</label>
|
||||
<input
|
||||
id="day-start"
|
||||
type="time"
|
||||
value={localDayStart}
|
||||
onChange={(e) => setLocalDayStart(e.target.value)}
|
||||
className="w-full p-2 border rounded-md text-sm"
|
||||
/>
|
||||
<label htmlFor="day-start" className="block text-xs text-gray-500 mb-1">Day starts (HH:MM)</label>
|
||||
<input
|
||||
id="day-start"
|
||||
type="time"
|
||||
value={localDayStart}
|
||||
onChange={(e) => setLocalDayStart(e.target.value)}
|
||||
className="w-full p-2 border rounded-md text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="day-end" className="block text-xs text-gray-500 mb-1">Day ends (HH:MM)</label>
|
||||
<input
|
||||
id="day-end"
|
||||
type="time"
|
||||
value={localDayEnd}
|
||||
onChange={(e) => setLocalDayEnd(e.target.value)}
|
||||
className="w-full p-2 border rounded-md text-sm"
|
||||
/>
|
||||
<label htmlFor="day-end" className="block text-xs text-gray-500 mb-1">Day ends (HH:MM)</label>
|
||||
<input
|
||||
id="day-end"
|
||||
type="time"
|
||||
value={localDayEnd}
|
||||
onChange={(e) => setLocalDayEnd(e.target.value)}
|
||||
className="w-full p-2 border rounded-md text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="wakeup-ref" className="block text-xs text-gray-500 mb-1">Wakeup ref (HH:MM)</label>
|
||||
<input
|
||||
id="wakeup-ref"
|
||||
type="time"
|
||||
value={localWakeupRef}
|
||||
onChange={(e) => setLocalWakeupRef(e.target.value)}
|
||||
className="w-full p-2 border rounded-md text-sm"
|
||||
/>
|
||||
<label htmlFor="wakeup-ref" className="block text-xs text-gray-500 mb-1">Wakeup ref (HH:MM)</label>
|
||||
<input
|
||||
id="wakeup-ref"
|
||||
type="time"
|
||||
value={localWakeupRef}
|
||||
onChange={(e) => setLocalWakeupRef(e.target.value)}
|
||||
className="w-full p-2 border rounded-md text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="bedtime-ref" className="block text-xs text-gray-500 mb-1">Bedtime ref (HH:MM)</label>
|
||||
<input
|
||||
id="bedtime-ref"
|
||||
type="time"
|
||||
value={localBedtimeRef}
|
||||
onChange={(e) => setLocalBedtimeRef(e.target.value)}
|
||||
className="w-full p-2 border rounded-md text-sm"
|
||||
/>
|
||||
<label htmlFor="bedtime-ref" className="block text-xs text-gray-500 mb-1">Bedtime ref (HH:MM)</label>
|
||||
<input
|
||||
id="bedtime-ref"
|
||||
type="time"
|
||||
value={localBedtimeRef}
|
||||
onChange={(e) => setLocalBedtimeRef(e.target.value)}
|
||||
className="w-full p-2 border rounded-md text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="sessions-per-page" className="block text-xs text-gray-500 mb-1">Results per page</label>
|
||||
<select
|
||||
id="sessions-per-page"
|
||||
value={localSessionsPerPage}
|
||||
onChange={(e) => setLocalSessionsPerPage(Number(e.target.value))}
|
||||
className="w-full p-2 border rounded-md text-sm"
|
||||
>
|
||||
<option value={5}>5</option>
|
||||
<option value={10}>10</option>
|
||||
<option value={25}>25</option>
|
||||
<option value={50}>50</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -27,18 +27,20 @@ const StatsPage: React.FC = () => {
|
||||
<h2 className="text-3xl font-extrabold text-slate-900 tracking-tight">Statistics</h2>
|
||||
<p className="text-slate-500 text-sm mt-1">Overview of sleep patterns and trends</p>
|
||||
</div>
|
||||
<SettingsPopover
|
||||
dayStart={settings.dayStart}
|
||||
dayEnd={settings.dayEnd}
|
||||
wakeupRef={settings.wakeupRef}
|
||||
bedtimeRef={settings.bedtimeRef}
|
||||
onSave={(s) => {
|
||||
update('dayStart', s.dayStart);
|
||||
update('dayEnd', s.dayEnd);
|
||||
update('wakeupRef', s.wakeupRef);
|
||||
update('bedtimeRef', s.bedtimeRef);
|
||||
}}
|
||||
/>
|
||||
<SettingsPopover
|
||||
dayStart={settings.dayStart}
|
||||
dayEnd={settings.dayEnd}
|
||||
wakeupRef={settings.wakeupRef}
|
||||
bedtimeRef={settings.bedtimeRef}
|
||||
sessionsPerPage={settings.sessionsPerPage}
|
||||
onSave={(s) => {
|
||||
update('dayStart', s.dayStart);
|
||||
update('dayEnd', s.dayEnd);
|
||||
update('wakeupRef', s.wakeupRef);
|
||||
update('bedtimeRef', s.bedtimeRef);
|
||||
update('sessionsPerPage', s.sessionsPerPage);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-[2rem] shadow-md border border-gray-100">
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||
|
||||
const STORAGE_KEY = 'baby-sleep-settings';
|
||||
|
||||
export interface Settings {
|
||||
dayStart: string;
|
||||
dayEnd: string;
|
||||
wakeupRef: string;
|
||||
bedtimeRef: string;
|
||||
statsInterval: number;
|
||||
sessionsPerPage: number;
|
||||
}
|
||||
|
||||
interface SettingsContextType {
|
||||
settings: Settings;
|
||||
update: (key: keyof Settings, value: string | number) => void;
|
||||
}
|
||||
|
||||
export const SettingsContext = createContext<SettingsContextType | undefined>(undefined);
|
||||
|
||||
const DEFAULT_SETTINGS: Settings = {
|
||||
dayStart: '07:00',
|
||||
dayEnd: '20:00',
|
||||
wakeupRef: '04:00',
|
||||
bedtimeRef: '19:00',
|
||||
statsInterval: 7,
|
||||
sessionsPerPage: 10,
|
||||
};
|
||||
|
||||
export function SettingsProvider({ children }: { children: ReactNode }) {
|
||||
const [settings, setSettings] = useState<Settings>(() => {
|
||||
if (typeof window === 'undefined') return DEFAULT_SETTINGS;
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
try {
|
||||
return { ...DEFAULT_SETTINGS, ...JSON.parse(stored) };
|
||||
} catch {
|
||||
return DEFAULT_SETTINGS;
|
||||
}
|
||||
}
|
||||
return DEFAULT_SETTINGS;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
||||
}, [settings]);
|
||||
|
||||
const update = (key: keyof Settings, value: string | number) => {
|
||||
setSettings((prev) => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingsContext.Provider value={{ settings, update }}>
|
||||
{children}
|
||||
</SettingsContext.Provider>
|
||||
);
|
||||
}
|
||||
@@ -1,44 +1,10 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
const STORAGE_KEY = 'baby-sleep-settings';
|
||||
|
||||
interface Settings {
|
||||
dayStart: string;
|
||||
dayEnd: string;
|
||||
wakeupRef: string;
|
||||
bedtimeRef: string;
|
||||
statsInterval: number;
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: Settings = {
|
||||
dayStart: '07:00',
|
||||
dayEnd: '20:00',
|
||||
wakeupRef: '04:00',
|
||||
bedtimeRef: '19:00',
|
||||
statsInterval: 7,
|
||||
};
|
||||
import { useContext } from 'react';
|
||||
import { SettingsContext, Settings } from './SettingsProvider';
|
||||
|
||||
export function useSettings() {
|
||||
const [settings, setSettings] = useState<Settings>(() => {
|
||||
if (typeof window === 'undefined') return DEFAULT_SETTINGS;
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
try {
|
||||
return { ...DEFAULT_SETTINGS, ...JSON.parse(stored) };
|
||||
} catch {
|
||||
return DEFAULT_SETTINGS;
|
||||
}
|
||||
}
|
||||
return DEFAULT_SETTINGS;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
||||
}, [settings]);
|
||||
|
||||
const update = (key: keyof Settings, value: string | number) => {
|
||||
setSettings((prev) => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
return { settings, update };
|
||||
}
|
||||
const context = useContext(SettingsContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useSettings must be used within a SettingsProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,17 @@ import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { SettingsProvider } from './hooks/SettingsProvider';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<App />
|
||||
<SettingsProvider>
|
||||
<App />
|
||||
</SettingsProvider>
|
||||
</QueryClientProvider>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Pagination', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Monitor network requests to see if our mock is hit
|
||||
page.on('request', request => console.log('>>', request.method(), request.url()));
|
||||
page.on('response', response => console.log('<<', response.status(), response.url()));
|
||||
|
||||
// Mock the sessions API
|
||||
await page.route('**/api/sessions', async (route) => {
|
||||
const mockSessions = Array.from({ length: 15 }, (_, i) => ({
|
||||
id: i + 1,
|
||||
start_time: new Date(2024, 0, 1 + i).toISOString(),
|
||||
end_time: new Date(2024, 0, 1 + i, 8, 0).toISOString(),
|
||||
notes: null,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
}));
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ sessions: mockSessions, total: 15 }),
|
||||
});
|
||||
});
|
||||
|
||||
// Mock other stats APIs to prevent crashes from accessing undefined properties
|
||||
await page.route(/\/api\/stats\/.*/, async (route) => {
|
||||
const url = route.request().url();
|
||||
let body = {};
|
||||
|
||||
if (url.includes('summary')) {
|
||||
body = {
|
||||
total_sleeps: 10,
|
||||
avg_sleeps_per_day: 2.5,
|
||||
total_sleeps_all_time: 100,
|
||||
avg_sleeps_per_day_all_time: 2.0,
|
||||
};
|
||||
} else if (url.includes('daily-summary')) {
|
||||
body = {
|
||||
days: [
|
||||
{ date: '2024-01-01', total_minutes: 480, day_minutes: 240, night_minutes: 240 },
|
||||
],
|
||||
};
|
||||
} else if (url.includes('timetable')) {
|
||||
body = {
|
||||
slots: [],
|
||||
};
|
||||
} else if (url.includes('wake-sleep-times')) {
|
||||
body = {
|
||||
days: [
|
||||
{ date: '2024-01-01', wakeup_minutes: 480, bedtime_minutes: 1200 },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto('http://localhost:5173/stats');
|
||||
});
|
||||
|
||||
test('Pagination component is visible when sessions exceed page size', async ({ page }) => {
|
||||
// Wait for one of the mocked dates to appear to ensure data is loaded
|
||||
await expect(page.locator('body')).toContainText('2024-01-01', { timeout: 10000 });
|
||||
await page.screenshot({ path: 'screenshots/pagination_visible.png' });
|
||||
|
||||
const pagination = page.getByTestId('pagination');
|
||||
await expect(pagination).toBeVisible();
|
||||
|
||||
// Use a more specific selector for the first page button
|
||||
await expect(page.getByTestId('pagination').getByRole('button', { name: '1' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('Changing results per page in settings updates the list', async ({ page }) => {
|
||||
// Wait for data to load first
|
||||
await expect(page.locator('body')).toContainText('2024-01-01', { timeout: 10000 });
|
||||
await page.screenshot({ path: 'screenshots/before_settings_change.png' });
|
||||
|
||||
// Open Settings
|
||||
await page.getByRole('button', { name: /Settings/i }).click();
|
||||
await expect(page.locator('text=Results per page')).toBeVisible();
|
||||
|
||||
// Change to 5
|
||||
await page.getByLabel(/Results per page/i).selectOption('5');
|
||||
await page.getByRole('button', { name: /Save/i }).click();
|
||||
|
||||
// Wait for the UI to update and settings to be saved
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.screenshot({ path: 'screenshots/after_settings_change.png' });
|
||||
|
||||
// Verify sessions per page is 5 (only 5 should be visible)
|
||||
const sessionRows = page.locator('.session-list .border-b');
|
||||
await expect(sessionRows).toHaveCount(5);
|
||||
|
||||
// Verify settings is saved in localStorage
|
||||
const localStorage = await page.evaluate(() => {
|
||||
return window.localStorage.getItem('baby-sleep-settings');
|
||||
});
|
||||
expect(localStorage).toContain('"sessionsPerPage":5');
|
||||
});
|
||||
|
||||
test('Navigating through pages works', async ({ page }) => {
|
||||
// Wait for data to load first
|
||||
await expect(page.locator('body')).toContainText('2024-01-01', { timeout: 10000 });
|
||||
|
||||
// Set to 5 via settings first
|
||||
await page.getByRole('button', { name: /Settings/i }).click();
|
||||
await page.getByLabel(/Results per page/i).selectOption('5');
|
||||
await page.getByRole('button', { name: /Save/i }).click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const nextButton = page.getByRole('button', { name: /Next/i });
|
||||
await expect(nextButton).toBeVisible();
|
||||
|
||||
await nextButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Should now show sessions from index 5 to 9 (if total is 15)
|
||||
// Mocked start_time for i=5 is 2024-01-06
|
||||
await expect(page.getByText('2024-01-06')).toBeVisible();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user