ws method
Opens a WebSocket session on a server.ws route. The handshake runs
through real runner dispatch (pattern, params, query, headers), then
messages flow both ways with no sockets and no frames.
Implementation
Future<TestWsConnection> ws(
String path, {
Map<String, String>? headers,
}) async {
var clean = path;
var query = '';
final q = path.indexOf('?');
if (q != -1) {
clean = path.substring(0, q);
query = path.substring(q + 1);
}
if (clean.isEmpty) clean = '/';
final match = _native.matchRoute('GET', clean, wsOnly: true);
if (match == null) {
throw StateError('no websocket route for $path');
}
final id = ++_nextId;
_native.wsOutbox[id] = StreamController<WsMessage>();
_native.wsCloseCodes[id] = Completer<int>();
_native.heads.add(
RawIncomingRequest(
requestId: id,
path: clean,
query: query,
packedHeaders: packHeaders((headers ?? const {}).entries),
routePattern: match.pattern,
params: _rawParams(match),
),
);
// The session opens on dispatch (a microtask away); wait for it so
// sends cannot land before the session exists. Production cannot race
// here — the 101 round-trips before any frame — so the client restores
// that ordering explicitly.
final deadline = DateTime.now().add(_responseTimeout);
while (!_runner.wsOpenedIdsForTesting.contains(id) &&
DateTime.now().isBefore(deadline)) {
await Future<void>.delayed(const Duration(milliseconds: 5));
}
if (!_runner.wsOpenedIdsForTesting.contains(id)) {
throw StateError('websocket session never opened for $path');
}
return TestWsConnection._(_native, id);
}