test(offline): e2e hotseat flow + fix $state-proxy persist

Add the Playwright mock e2e for offline pass-and-play (Chromium+WebKit):
create a 2-seat hotseat with a PIN-locked seat, verify the rack is
withheld, unlock (wrong then right PIN), host-skip the current turn, and
terminate from the lobby behind the master PIN.

The e2e surfaced a persistence bug: the roster PINs are $state proxies,
which fail structured-clone on the IndexedDB put (silently — best-effort
store), so a created hotseat game vanished on reload. Snapshot the seats
+ host PIN to plain objects at the create() call site.
This commit is contained in:
Ilia Denisov
2026-07-07 11:53:50 +02:00
parent 452a686e07
commit 66278e34b7
2 changed files with 107 additions and 2 deletions
+5 -2
View File
@@ -230,15 +230,18 @@
return;
}
const id = newLocalGameId();
// Snapshot the roster + PINs to plain objects: the rows/pins are $state proxies, and a proxy
// fails structured-clone when the record is persisted to IndexedDB (silently, since the store
// is best-effort) — so the game would vanish on reload. See lib/localgame/store.
await localSource.create({
id,
variant: inviteVariant,
dictVersion: version,
seed: randomSeed(),
multipleWords: multipleWordsForRequest(inviteVariant, multipleWords),
seats: buildSeats(rows),
seats: $state.snapshot(buildSeats(rows)),
hotseat: true,
hostPin,
hostPin: hostPin ? $state.snapshot(hostPin) : undefined,
});
navigate(`/game/${id}`);
} catch (e) {