Screen shares and game shares can sit static for long stretches — a still
desktop, a paused video, a code editor with no caret movement. The
encoder genuinely stops emitting RTP, the subscriber's track goes muted,
and the 15s camera window would falsely reap the live tile.
watchVideoTrackForRemoval now takes a per-call windowMs; the sub-PC
ontrack handler passes VIDEO_REMOVE_MUTE_WINDOW_SCREEN_MS (120s) for
screen + game and VIDEO_REMOVE_MUTE_WINDOW_MS (15s) for camera. A
genuine unshare still resolves through the 'ended' path within a
frame, so the longer window only affects the slow-failure case.
Tests bumped to 18: new screen-window assertions + invalid-windowMs
fallback to the default rather than disabling removal entirely.
15 unit + integration tests that extract the function and the shipped
VIDEO_REMOVE_MUTE_WINDOW_MS constant from web/zebra-spaces.html so the
assertions track exactly what's deployed. Drives synthetic mute/
unmute/ended event sequences against a fake EventTarget track with a
fake clock injected through a Function-constructor harness.
Unit coverage:
- shipped window must be >= 10s (catches accidental shorten)
- initial mute (never flowed) never removes
- flowing + sustained mute past window removes
- mute + unmute within window cancels removal
- ended event removes immediately + cancels pending timer
- removeFn is idempotent (no double-call across mute, ended, or later
events)
- redundant mute events do not stack timers
- rescue and removal both emit logLine telemetry
- rapid mute/unmute oscillation never removes while unmute lands in time
Integration coverage (realistic lifecycles):
- fresh track -> flow -> publisher unshares -> tile removed
- mobile network handoff (long mute) recovers without removal
- peer leaves abruptly (ended fires) -> tile removed once
- hard refresh of publisher (SFU supplant renegotiation gap) -> tile
survives — this is the cascade fox flagged where a phone reconnect
was killing the host's view of its camera
- publisher process crashes (mute holds indefinitely) -> removed at
window
Wired into Makefile as test-video-removal + added to test-all. Pure
Node, no browser or proxy server needed.