- `LIVE_RELOAD=false npm run dev` keeps the renderer alive across a machine suspend; the reload client otherwise destroys the session under test. - `dev-peer.sh` plus a separate userdata dir runs a second local peer. - `tools/voice-probe.js` samples peer state and RTP counters from a live window, persisting to localStorage so a renderer reload cannot erase it. - e2e helpers for voice pairs, peer-role election, and a TURN relay. - Electron single-instance and dev-client-load decisions move into rules files with colocated specs.
73 lines
2.2 KiB
TypeScript
73 lines
2.2 KiB
TypeScript
import {
|
|
describe,
|
|
expect,
|
|
it,
|
|
vi
|
|
} from 'vitest';
|
|
|
|
import {
|
|
DEV_CLIENT_LOAD_ATTEMPTS,
|
|
DevClientLoadDeps,
|
|
loadDevelopmentClientWithRetry
|
|
} from './dev-client-load.rules';
|
|
|
|
function createDeps(overrides: Partial<DevClientLoadDeps> = {}): DevClientLoadDeps {
|
|
return {
|
|
isAborted: () => false,
|
|
load: vi.fn().mockResolvedValue(undefined),
|
|
onGiveUp: vi.fn(),
|
|
onRetry: vi.fn(),
|
|
wait: vi.fn().mockResolvedValue(undefined),
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
describe('loadDevelopmentClientWithRetry', () => {
|
|
it('loads once when the dev server answers', async () => {
|
|
const deps = createDeps();
|
|
|
|
await expect(loadDevelopmentClientWithRetry(deps)).resolves.toBe('loaded');
|
|
expect(deps.load).toHaveBeenCalledTimes(1);
|
|
expect(deps.onRetry).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('retries a rebuild gap and reports the recovered load', async () => {
|
|
const load = vi.fn()
|
|
.mockRejectedValueOnce(new Error('ERR_CONNECTION_REFUSED (-102)'))
|
|
.mockResolvedValue(undefined);
|
|
const deps = createDeps({ load });
|
|
|
|
await expect(loadDevelopmentClientWithRetry(deps)).resolves.toBe('loaded');
|
|
expect(load).toHaveBeenCalledTimes(2);
|
|
expect(deps.onRetry).toHaveBeenCalledTimes(1);
|
|
expect(deps.wait).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('reports failure instead of throwing so the caller still wires the window', async () => {
|
|
const error = new Error('ERR_FAILED (-2)');
|
|
const deps = createDeps({ load: vi.fn().mockRejectedValue(error) });
|
|
|
|
await expect(loadDevelopmentClientWithRetry(deps)).resolves.toBe('failed');
|
|
expect(deps.load).toHaveBeenCalledTimes(DEV_CLIENT_LOAD_ATTEMPTS);
|
|
expect(deps.onGiveUp).toHaveBeenCalledWith(DEV_CLIENT_LOAD_ATTEMPTS, error);
|
|
});
|
|
|
|
it('stops retrying once the window is gone', async () => {
|
|
let windowAlive = true;
|
|
|
|
const load = vi.fn().mockImplementation(() => {
|
|
windowAlive = false;
|
|
|
|
return Promise.reject(new Error('ERR_FAILED (-2)'));
|
|
});
|
|
const deps = createDeps({
|
|
isAborted: () => !windowAlive,
|
|
load
|
|
});
|
|
|
|
await expect(loadDevelopmentClientWithRetry(deps)).resolves.toBe('aborted');
|
|
expect(load).toHaveBeenCalledTimes(1);
|
|
expect(deps.onGiveUp).not.toHaveBeenCalled();
|
|
});
|
|
});
|