diff --git a/apps/worker/src/auth/AuthContext.test.tsx b/apps/worker/src/auth/AuthContext.test.tsx
new file mode 100644
index 0000000..42eb116
--- /dev/null
+++ b/apps/worker/src/auth/AuthContext.test.tsx
@@ -0,0 +1,49 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
+import { AuthProvider, useAuth } from './AuthContext';
+import { clearToken, setToken } from '../lib/auth-storage';
+
+// The sign-in network call is irrelevant here; stub it so nothing hits the network.
+vi.mock('../lib/api', () => ({
+ signIn: vi.fn(),
+}));
+
+function SignOutButton() {
+ const { signOut } = useAuth();
+ return (
+
+ );
+}
+
+describe('AuthContext signOut', () => {
+ beforeEach(() => {
+ clearToken();
+ window.history.replaceState(null, '', '/');
+ });
+
+ afterEach(() => {
+ vi.clearAllMocks();
+ window.history.replaceState(null, '', '/');
+ });
+
+ it('resets the path to / so re-login lands on the Stopwatch', async () => {
+ const user = userEvent.setup();
+ setToken('tok');
+ // Simulate logging out while sitting on the Account tab.
+ window.history.replaceState(null, '', '/account');
+ expect(window.location.pathname).toBe('/account');
+
+ render(
+
+
+
+ );
+
+ await user.click(screen.getByRole('button', { name: 'Uitloggen' }));
+
+ expect(window.location.pathname).toBe('/');
+ });
+});
diff --git a/apps/worker/src/auth/AuthContext.tsx b/apps/worker/src/auth/AuthContext.tsx
index e9ae72d..e2dda0f 100644
--- a/apps/worker/src/auth/AuthContext.tsx
+++ b/apps/worker/src/auth/AuthContext.tsx
@@ -19,6 +19,8 @@ export function AuthProvider({ children }: { children: ReactNode }) {
}, []);
const signOut = useCallback(() => {
+ // Reset the router path so the next authed mount lands on Stopwatch, not Account.
+ window.history.replaceState(null, '', '/');
clearToken();
setIsAuthed(false);
}, []);
diff --git a/apps/worker/src/screens/History.test.tsx b/apps/worker/src/screens/History.test.tsx
index fae0806..a3c370c 100644
--- a/apps/worker/src/screens/History.test.tsx
+++ b/apps/worker/src/screens/History.test.tsx
@@ -31,6 +31,8 @@ function session(overrides: Partial = {}): WorkSession {
start_time: '2026-01-02T08:30:00.000Z',
end_time: '2026-01-02T09:31:01.000Z',
duration_seconds: 3661,
+ paused_seconds: 0,
+ paused_at: null,
status: 'completed',
source: 'app',
notes: null,
@@ -44,7 +46,7 @@ function renderHistory() {
return render(
- ,
+
);
}
@@ -94,6 +96,23 @@ describe('History', () => {
expect(card.textContent).not.toContain('1 inlegzolen');
});
+ it('renders a Pauze line when paused_seconds > 0', async () => {
+ mockedApiFetch.mockResolvedValue([session({ paused_seconds: 125 })]);
+ renderHistory();
+
+ const card = (await screen.findByText('Frezen')).closest('.session-card') as HTMLElement;
+ // 125s -> "2m 5s".
+ expect(card.textContent).toContain('Pauze 2m 5s');
+ });
+
+ it('does not render a Pauze line when paused_seconds is 0', async () => {
+ mockedApiFetch.mockResolvedValue([session({ paused_seconds: 0 })]);
+ renderHistory();
+
+ const card = (await screen.findByText('Frezen')).closest('.session-card') as HTMLElement;
+ expect(card.textContent).not.toContain('Pauze');
+ });
+
it('triggers the CSV download on Exporteer CSV', async () => {
const user = userEvent.setup();
renderHistory();
diff --git a/apps/worker/src/screens/History.tsx b/apps/worker/src/screens/History.tsx
index 6aa0d61..b905ba3 100644
--- a/apps/worker/src/screens/History.tsx
+++ b/apps/worker/src/screens/History.tsx
@@ -45,6 +45,9 @@ function SessionCard({ session }: { session: WorkSession }) {
{session.pair_count} {noun}
{formatDuration(session.duration_seconds)}
+ {session.paused_seconds > 0 && (
+ Pauze {formatDuration(session.paused_seconds)}
+ )}
);