MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m38s
- Desktop shows "offline" with a red dot under the user name when the server is unreachable; phones show a green/red dot on the avatar. Based on the app-wide realtime connection, so a server outage is noticed even while the device has network (4 s grace period). - Screenshots converted to WebP (1280 px desktop, 480 px phone, q90), e2e/screenshots.sh converts with cwebp; new journal picture with round corners. Rules in AGENTS.md. - Release v0.26.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
import { act, renderHook } from '@testing-library/react';
|
|
|
|
let statusListener: ((connected: boolean) => void) | null = null;
|
|
let currentConnected = false;
|
|
vi.mock('../api', () => ({
|
|
onRealtimeStatus: (cb: (connected: boolean) => void) => {
|
|
statusListener = cb;
|
|
cb(currentConnected);
|
|
return () => (statusListener = null);
|
|
},
|
|
}));
|
|
|
|
import { useServerOnline, SERVER_OFFLINE_GRACE_MS } from '../useServerOnline';
|
|
|
|
function setDeviceOnline(value: boolean) {
|
|
Object.defineProperty(navigator, 'onLine', { value, configurable: true });
|
|
}
|
|
|
|
describe('useServerOnline', () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
currentConnected = false;
|
|
setDeviceOnline(true);
|
|
});
|
|
afterEach(() => vi.useRealTimers());
|
|
|
|
it('stays online during the grace period at app start', () => {
|
|
const { result } = renderHook(() => useServerOnline());
|
|
expect(result.current).toBe(true);
|
|
act(() => statusListener?.(true));
|
|
act(() => vi.advanceTimersByTime(SERVER_OFFLINE_GRACE_MS + 100));
|
|
expect(result.current).toBe(true);
|
|
});
|
|
|
|
it('turns offline once the server stays unreachable and back online on reconnect', () => {
|
|
currentConnected = true;
|
|
const { result } = renderHook(() => useServerOnline());
|
|
act(() => statusListener?.(false));
|
|
expect(result.current).toBe(true);
|
|
act(() => vi.advanceTimersByTime(SERVER_OFFLINE_GRACE_MS + 100));
|
|
expect(result.current).toBe(false);
|
|
act(() => statusListener?.(true));
|
|
expect(result.current).toBe(true);
|
|
});
|
|
|
|
it('repeated failed reconnects do not postpone the offline state', () => {
|
|
currentConnected = true;
|
|
const { result } = renderHook(() => useServerOnline());
|
|
for (let i = 0; i < 4; i++) {
|
|
act(() => statusListener?.(false));
|
|
act(() => vi.advanceTimersByTime(SERVER_OFFLINE_GRACE_MS / 2));
|
|
}
|
|
expect(result.current).toBe(false);
|
|
});
|
|
|
|
it('is offline right away without device network', () => {
|
|
currentConnected = true;
|
|
const { result } = renderHook(() => useServerOnline());
|
|
act(() => {
|
|
setDeviceOnline(false);
|
|
statusListener?.(false);
|
|
});
|
|
expect(result.current).toBe(false);
|
|
});
|
|
});
|