test(presence): rate-based cursor-throttle bound — deflake slow CI runners

The cursor-emit tests asserted a fixed <25 emits for 30 awaited mouse
moves, assuming the sweep outpaces the 50 ms throttle. On a loaded
Ubicloud runner each Playwright->Firefox->WASM round-trip can straddle
a throttle window, so 28/30 moves emitted and the test failed (twice on
main, once from a docs-only commit).

Bound by measured sweep duration instead: one emit per 50 ms window,
+1 leading edge, +2 slack. Fast machines get a tighter bound (~9-13);
slow runners scale with actual elapsed time. A broken throttle still
fails.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lg5jwWuhFH5dL8hEcBDuP2
This commit is contained in:
Gergő Törcsvári 2026-07-08 12:19:25 +02:00
commit fdac450a05
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
2 changed files with 12 additions and 3 deletions

View file

@ -233,15 +233,20 @@ test("cursor emit: mouse motion over the canvas produces throttled world coords"
const cx = box!.x + box!.width / 2;
const cy = box!.y + box!.height / 2;
const sweepStart = Date.now();
for (let i = 0; i < 30; i++) {
await page.mouse.move(cx - 150 + i * 10, cy, { steps: 1 });
}
const sweepMs = Date.now() - sweepStart;
const emits = await page.evaluate(
() => (window as unknown as PresenceWindow).__cursorEmits!,
);
expect(emits.length).toBeGreaterThan(0);
expect(emits.length).toBeLessThan(25);
// Throttle is 50 ms (≤20 Hz). On a slow runner the awaited moves themselves
// can straddle throttle windows, so bound by measured sweep duration rather
// than a fixed count: one emit per 50 ms window, +1 leading edge, +2 slack.
expect(emits.length).toBeLessThanOrEqual(Math.floor(sweepMs / 50) + 3);
const active = emits.filter((e) => e.active === 1);
expect(active.length).toBeGreaterThan(0);

View file

@ -324,16 +324,20 @@ test("cursor emit: mouse motion over the canvas produces throttled world coords"
// Sweep the pointer across the canvas center with many small steps.
const cx = box!.x + box!.width / 2;
const cy = box!.y + box!.height / 2;
const sweepStart = Date.now();
for (let i = 0; i < 30; i++) {
await page.mouse.move(cx - 150 + i * 10, cy, { steps: 1 });
}
const sweepMs = Date.now() - sweepStart;
const emits = await page.evaluate(
() => (window as unknown as PresenceWindow).__cursorEmits!,
);
expect(emits.length).toBeGreaterThan(0);
// Throttle: 30 moves in well under a second must not emit 30 times.
expect(emits.length).toBeLessThan(25);
// Throttle is 50 ms (≤20 Hz). On a slow runner the awaited moves themselves
// can straddle throttle windows, so bound by measured sweep duration rather
// than a fixed count: one emit per 50 ms window, +1 leading edge, +2 slack.
expect(emits.length).toBeLessThanOrEqual(Math.floor(sweepMs / 50) + 3);
const active = emits.filter((e) => e.active === 1);
expect(active.length).toBeGreaterThan(0);