Repair connectivity correctly v1

This commit is contained in:
2026-05-17 15:15:14 +02:00
parent e769a6ee4a
commit 9d0a4478b2
18 changed files with 1125 additions and 25 deletions

View File

@@ -11,6 +11,7 @@ import { type Page } from '@playwright/test';
export async function installWebRTCTracking(page: Page): Promise<void> {
await page.addInitScript(() => {
const connections: RTCPeerConnection[] = [];
const dataChannels: RTCDataChannel[] = [];
const syntheticMediaResources: {
audioCtx: AudioContext;
source?: AudioScheduledSourceNode;
@@ -18,20 +19,40 @@ export async function installWebRTCTracking(page: Page): Promise<void> {
}[] = [];
(window as any).__rtcConnections = connections;
(window as any).__rtcDataChannels = dataChannels;
(window as any).__rtcRemoteTracks = [] as { kind: string; id: string; readyState: string }[];
(window as any).__rtcSyntheticMediaResources = syntheticMediaResources;
const OriginalRTCPeerConnection = window.RTCPeerConnection;
const trackDataChannel = (channel: RTCDataChannel) => {
if (dataChannels.includes(channel)) {
return;
}
dataChannels.push(channel);
};
(window as any).RTCPeerConnection = function(this: RTCPeerConnection, ...args: any[]) {
const pc: RTCPeerConnection = new OriginalRTCPeerConnection(...args);
const originalCreateDataChannel = pc.createDataChannel.bind(pc);
connections.push(pc);
pc.createDataChannel = ((label: string, options?: RTCDataChannelInit) => {
const channel = originalCreateDataChannel(label, options);
trackDataChannel(channel);
return channel;
}) as RTCPeerConnection['createDataChannel'];
pc.addEventListener('connectionstatechange', () => {
(window as any).__lastRtcState = pc.connectionState;
});
pc.addEventListener('datachannel', (event: RTCDataChannelEvent) => {
trackDataChannel(event.channel);
});
pc.addEventListener('track', (event: RTCTrackEvent) => {
(window as any).__rtcRemoteTracks.push({
kind: event.track.kind,
@@ -211,6 +232,66 @@ export async function waitForConnectedPeerCount(page: Page, expectedCount: numbe
);
}
/** Returns the number of tracked RTCDataChannels in the open state. */
export async function getOpenDataChannelCount(page: Page): Promise<number> {
return page.evaluate(
() => ((window as any).__rtcDataChannels as RTCDataChannel[] | undefined)?.filter(
(channel) => channel.readyState === 'open'
).length ?? 0
);
}
/** Wait until the expected number of tracked RTCDataChannels are open. */
export async function waitForOpenDataChannelCount(page: Page, expectedCount: number, timeout = 45_000): Promise<void> {
await page.waitForFunction(
(count) => ((window as any).__rtcDataChannels as RTCDataChannel[] | undefined)?.filter(
(channel) => channel.readyState === 'open'
).length === count,
expectedCount,
{ timeout }
);
}
/** Close every currently-open RTCDataChannel and return how many were closed. */
export async function closeOpenDataChannels(page: Page): Promise<number> {
return page.evaluate(() => {
const channels = ((window as any).__rtcDataChannels as RTCDataChannel[] | undefined) ?? [];
let closed = 0;
for (const channel of channels) {
if (channel.readyState !== 'open') {
continue;
}
channel.close();
closed++;
}
return closed;
});
}
/** Dispatch a synthetic data-channel error event on each open channel. */
export async function dispatchDataChannelErrors(page: Page): Promise<number> {
return page.evaluate(() => {
const channels = ((window as any).__rtcDataChannels as RTCDataChannel[] | undefined) ?? [];
let dispatched = 0;
for (const channel of channels) {
if (channel.readyState !== 'open') {
continue;
}
channel.dispatchEvent(new Event('error'));
dispatched++;
}
return dispatched;
});
}
/**
* Resume all suspended AudioContext instances created by the synthetic
* media patch. Uses CDP `Runtime.evaluate` with `userGesture: true` so