Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions apps/threshold/src/screens/Home.perf.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// @vitest-environment jsdom
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, waitFor } from '@testing-library/react';
import Home from './Home';
import { SettingsService } from '../services/SettingsService';
import { alarmManagerService } from '../services/AlarmManagerService';

// Mock dependencies
vi.mock('@tanstack/react-router', () => ({
useNavigate: () => vi.fn(),
}));

vi.mock('../services/AlarmManagerService', () => ({
alarmManagerService: {
init: vi.fn().mockResolvedValue(undefined),
loadAlarms: vi.fn().mockResolvedValue([]),
toggleAlarm: vi.fn(),
deleteAlarm: vi.fn(),
},
}));

vi.mock('../components/MobileToolbar', () => ({
MobileToolbar: () => <div>Toolbar</div>,
}));

vi.mock('../components/AlarmItem', () => ({
AlarmItem: () => <div>AlarmItem</div>,
}));

vi.mock('../utils/PlatformUtils', () => ({
PlatformUtils: {
isMobile: vi.fn().mockReturnValue(false),
},
}));

// Mock SettingsService
vi.mock('../services/SettingsService', () => ({
SettingsService: {
getIs24h: vi.fn().mockReturnValue(true),
},
}));

// Mock Tauri APIs
vi.mock('@tauri-apps/api/event', () => ({
emit: vi.fn(),
listen: vi.fn(() => Promise.resolve(() => {})),
}));

describe('Home Screen Performance', () => {
beforeEach(() => {
vi.clearAllMocks();
});

it('should access SettingsService.getIs24h() only once on mount', async () => {
// Setup initial alarms to cause a state update
const alarms = [{ id: 1, time: '10:00', enabled: true, label: 'Test', days: [], soundUri: '' }];
(alarmManagerService.loadAlarms as any).mockResolvedValue(alarms);

// Render
render(<Home />);

// Initial render (Mount) -> getIs24h called once.
// useEffect -> loadData -> loadAlarms -> setAlarms(alarms) -> Re-render.
// Re-render -> getIs24h called again (if unoptimized).

await waitFor(() => expect(alarmManagerService.loadAlarms).toHaveBeenCalled());

// Wait for potential re-renders to settle
await new Promise(resolve => setTimeout(resolve, 50));

const callCount = (SettingsService.getIs24h as any).mock.calls.length;
console.log(`[Perf Baseline] SettingsService.getIs24h called ${callCount} times`);

// Expectation for optimized code: strictly 1 call
expect(callCount).toBe(1);
});
});
22 changes: 21 additions & 1 deletion apps/threshold/src/screens/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,36 @@ import { alarmManagerService } from '../services/AlarmManagerService';
import { AlarmItem } from '../components/AlarmItem';
import { SettingsService } from '../services/SettingsService';
import { APP_NAME } from '../constants';
import { listen } from '@tauri-apps/api/event';

interface SettingsChangedEvent {
key: string;
value: boolean | string | number;
}

const Home: React.FC = () => {
const navigate = useNavigate();
const [alarms, setAlarms] = useState<Alarm[]>([]);
const [isMobile, setIsMobile] = useState(false);
const is24h = SettingsService.getIs24h();
const [is24h, setIs24h] = useState(() => SettingsService.getIs24h());

useEffect(() => {
setIsMobile(PlatformUtils.isMobile());
}, []);

// Listen for settings changes to update is24h
useEffect(() => {
const unlistenPromise = listen<SettingsChangedEvent>('settings-changed', (event) => {
if (event.payload.key === 'is24h' && typeof event.payload.value === 'boolean') {
setIs24h(event.payload.value);
}
});

return () => {
unlistenPromise.then((unlisten) => unlisten());
};
}, []);

const handleSettingsClick = () => {
navigate({ to: '/settings' });
};
Expand Down