mirror of
https://github.com/tiennm99/noitu.git
synced 2026-10-11 03:13:45 +00:00
Use the generated ServerMessage/ClientMessage union everywhere a decoded message crosses a function boundary, so a typo in a payload field is a build error instead of undefined at runtime. Clears every jsdoc/reject-any-type warning in src/ and all but one in tests/ — room-code.test.js keeps one to deliberately call normalizeRoomCode(undefined) against its documented string-only signature, which is the point of that test. Also fixes the flaky e2e helper: playingPair now waits for the guest's seat (joinRoomSeated) before readyAndStart, and readyAndStart asserts the guest's own ready row before touching Start, since a SetReady send can silently drop while the socket is not open and nothing retries it.
53 lines
1.9 KiB
JavaScript
53 lines
1.9 KiB
JavaScript
/**
|
|
* A controllable break in a page's WebSocket.
|
|
*
|
|
* Offline emulation is not usable here: Chromium's network conditions do not
|
|
* apply to a loopback socket, so a test that "went offline" against a local
|
|
* server kept playing quite happily. Routing the socket cuts it for real.
|
|
*
|
|
* The server does not reliably see it, though: closing a routed connection is
|
|
* observed by the page but does not always reach the far end, so the grace
|
|
* window may never start. That makes this the right tool for what the client
|
|
* does about a dead socket, and the wrong one for what the server does about a
|
|
* missing player. For that, take the page away instead.
|
|
* @param {import('@playwright/test').Page} page
|
|
*/
|
|
export async function cuttableSocket(page) {
|
|
/** @type {import('@playwright/test').WebSocketRoute | null} */
|
|
let live = null;
|
|
/** @type {import('@playwright/test').WebSocketRoute | null} */
|
|
let upstream = null;
|
|
let blocked = false;
|
|
|
|
await page.routeWebSocket(/\/ws$/, (ws) => {
|
|
if (blocked) {
|
|
// Refuse without connecting, so the client keeps retrying and the
|
|
// outage lasts as long as the test wants it to.
|
|
ws.close({ code: 1006 });
|
|
return;
|
|
}
|
|
live = ws;
|
|
// With no message handlers registered, frames are proxied in both
|
|
// directions, so the game runs against the real server as usual.
|
|
upstream = ws.connectToServer();
|
|
});
|
|
|
|
return {
|
|
/** Drops the current connection, optionally refusing reconnects too. */
|
|
async cut({ sustained = false } = {}) {
|
|
blocked = sustained;
|
|
// Both halves. Closing only the page side leaves the server holding
|
|
// a connection it thinks is fine, so it never starts the grace
|
|
// window and the opponent is never told.
|
|
await upstream?.close({ code: 1006 });
|
|
await live?.close({ code: 1006 });
|
|
upstream = null;
|
|
live = null;
|
|
},
|
|
/** Lets the next reconnect through. */
|
|
restore() {
|
|
blocked = false;
|
|
}
|
|
};
|
|
}
|