chore: dev-stack switches, shared e2e harness, and desktop shell rules

- `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.
This commit is contained in:
2026-08-14 03:19:29 +02:00
parent d71e3a98da
commit e49b3ec112
41 changed files with 35947 additions and 50 deletions
@@ -0,0 +1,72 @@
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();
});
});