test(e2e): unblock the 3 fixme tests in modifier-profiles spec
P6 (source chain in pinned panel):
Required two server-side changes to make the badge actually meaningful:
- Add `profile` field to GameStatePayload schema (server emits it,
client receives it) so multiplayer clients see the room's active
profile metadata, not just the modifier facts.
- Make `ChessEngine.activeProfile` mutable via `setActiveProfile()`
so PredictionManager can sync it from `game.state` snapshots.
Also wire `modifier-profile.updated` through GameClient + Prediction-
Manager so hot-swap broadcasts update the engine's profile field
reactively.
Fix Lobby.handlePlaySolo's resetToFreshGame to forward the selected
profile to the new ChessEngine — otherwise the local engine had
modifier facts (via server reconcile) but no profile metadata,
breaking source-chain attribution and any other profile-aware UI.
P7 (multiplayer propose → approve → both observe updated):
P8 (multiplayer propose → reject → no updated broadcast):
Implemented at the WS-protocol level using two parallel raw sockets
per test (mirrors multiplayer.spec.ts pattern). Critical sequencing:
- Both sockets opened concurrently via Promise.all so opponent is
listening BEFORE host's propose arrives at the server (otherwise
proposal-pending broadcasts to nobody and the test deadlocks).
- Token must travel at the envelope level, not in payload, for the
server's reconnect-by-token path to fire (otherwise hits ROOM_FULL
on the second connection from each player).
- game.move payload uses algebraic notation strings ('a2', 'a3'), not
square indices — the protocol schema only accepts strings.
- Host re-uses original room.create token, opponent re-uses their
join token. Server's reconnectManager treats both as grace-window
reconnects since the original WS closed cleanly.
Verification:
- 1231 unit tests pass (96 files)
- 58/58 Playwright tests pass in 1.9 min (was 55 + 3 fixme)
- Total Playwright surface coverage: solo-smoke (7) + multiplayer (2) +
full-flow (1) + layouts (24) + modifier-profiles (24 — including all
8 T2-polish tests, 0 fixme).
This commit is contained in:
parent
748dde5d4c
commit
567480a788
8 changed files with 647 additions and 42 deletions
|
|
@ -487,6 +487,7 @@ function handleRoomJoin(
|
|||
.error("room.join: missing session for existing room");
|
||||
return;
|
||||
}
|
||||
const activeProfile = session.getProfile();
|
||||
broadcastToRoom(
|
||||
payload.code,
|
||||
envelope("game.state", {
|
||||
|
|
@ -496,6 +497,7 @@ function handleRoomJoin(
|
|||
moveHistory: [],
|
||||
activeRules: [...result.activeRules],
|
||||
activations: session.getPresetActivations(),
|
||||
...(activeProfile !== undefined ? { profile: activeProfile } : {}),
|
||||
// fen is a UI convenience for v1; we haven't wired FEN generation
|
||||
// on the server yet, so we send an empty string. Clients that need
|
||||
// FEN can derive it from `facts`.
|
||||
|
|
@ -573,6 +575,7 @@ function handleReconnect(
|
|||
|
||||
// Snapshot: authoritative state for the returning client. The client
|
||||
// discards its local fact store and rebuilds from this frame.
|
||||
const reconnectActiveProfile = session.getProfile();
|
||||
sendTo(
|
||||
ws,
|
||||
envelope("game.state", {
|
||||
|
|
@ -586,6 +589,7 @@ function handleReconnect(
|
|||
moveHistory: [],
|
||||
activeRules: [...room.rulesetIds],
|
||||
activations: session.getPresetActivations(),
|
||||
...(reconnectActiveProfile !== undefined ? { profile: reconnectActiveProfile } : {}),
|
||||
fen: "",
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -545,6 +545,11 @@ export const GameStatePayloadSchema = z.object({
|
|||
/** Full preset activation set authoritative on the server. Optional
|
||||
* on the wire so older servers don't break the schema check. */
|
||||
activations: z.array(PresetActivationSchema).optional(),
|
||||
/** Active modifier profile for the room (T2). Optional — absent for
|
||||
* vanilla rooms or when the server hasn't been told about a profile.
|
||||
* Clients use this to populate `engine.activeProfile` so source-chain
|
||||
* attribution and other profile-aware UI works on snapshot replay. */
|
||||
profile: ModifierProfileSchema.optional(),
|
||||
fen: z.string(),
|
||||
});
|
||||
export type GameStatePayload = z.infer<typeof GameStatePayloadSchema>;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue