feat(web): a play-now button that skips the room code

QuickMatch/CancelQuickMatch join and leave the pairing queue; the
store tracks the wait as `queued` and clears it the moment a RoomState
seats the connection. The /online screen shows a waiting panel with
elapsed time, a cancel button, and — past 20 seconds — a nudge toward
the bot; leaving the page while queued cancels it. Adds the two new
error codes and a sentence to the room-code rules copy.
This commit is contained in:
tiennm99 committed 2026-09-21 01:39:54 +07:00
1 parent 4a78b8aad5
commit e6f2d1922c
7 files changed
+228 -39

No files matched your search

+22
View File
@@ -533,6 +533,28 @@ describe('pong', () => {
});
});
describe('quickMatchStatus', () => {
it('flags the wait while queued', () => {
const store = createGameStore();
store.apply(msg('quickMatchStatus', { queued: true }));
expect(store.state.queued).toBe(true);
});
it('ends the wait once the queue answers false', () => {
const store = createGameStore();
store.apply(msg('quickMatchStatus', { queued: true }));
store.apply(msg('quickMatchStatus', { queued: false }));
expect(store.state.queued).toBe(false);
});
it('ends the wait the moment a room seats this connection, even with no prior status', () => {
const store = createGameStore();
store.apply(msg('quickMatchStatus', { queued: true }));
store.apply(pair());
expect(store.state.queued).toBe(false);
});
});
describe('chat', () => {
/** @param {object} fields */
function line(fields) {
+23 -1
View File
@@ -7,7 +7,7 @@
import { beforeEach, describe, expect, it } from 'vitest';
import { create, toBinary } from '@bufbuild/protobuf';
import { PROTOCOL_VERSION } from '../src/lib/ws/messages.js';
import { PROTOCOL_VERSION, cancelQuickMatch, quickMatch } from '../src/lib/ws/messages.js';
import {
ClientMessageSchema,
ServerMessageSchema
@@ -351,6 +351,28 @@ describe('frames', () => {
});
});
describe('quick match', () => {
it('encodes a QuickMatch frame the transport will carry as-is', async () => {
const h = setup();
h.client.connect();
h.last().open();
h.client.send(quickMatch());
const sent = await sentMessages(h.last());
expect(sent.at(-1).payload.case).toBe('quickMatch');
});
it('encodes a CancelQuickMatch frame the transport will carry as-is', async () => {
const h = setup();
h.client.connect();
h.last().open();
h.client.send(cancelQuickMatch());
const sent = await sentMessages(h.last());
expect(sent.at(-1).payload.case).toBe('cancelQuickMatch');
});
});
describe('handshake ordering', () => {
it('sends Hello before announcing the connection is open', async () => {
// The server refuses everything until the handshake lands, so a listener