e9f4cb0178
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 1m10s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m43s
A Playwright spec drives the whole offline flow in the mock build: force the installed-PWA display mode, enter offline via the Settings toggle (its readiness check fetches the dawgs), assert the blue chrome + online-games-hidden + Stats-disabled, then create and play a device-local English vs_ai game with a pinned bag seed (deterministic rack NEWYMAO) -- play the opening WAY across the centre, watch the robot reply with a real move, and reload to confirm the IndexedDB replay. Enabling infra (all e2e-only; nothing enters the production build): - mock/client.ts fetchDict serves the per-variant dawgs from the preview build's /e2edict/ (was: threw 'unsupported in mock'). - scripts/e2e-dict.mjs copies the real dawgs into dist-e2e from E2E_DICT_DIR (the ui CI job fetches the scrabble-dictionary release like the Go jobs; local default: the sibling scrabble-solver/dawg); playwright.config runs it between build and preview. - localgame/id.ts setForcedSeed + gateway.ts window.__mock.setLocalSeed: a mock-only seam to pin a local game's bag seed (tree-shaken from prod). - ci.yaml ui job: fetch the dawgs + pass E2E_DICT_DIR to the e2e step. - docs/TESTING.md: the offline e2e + the mock-dawg wiring. Verified: check 0 / unit 482 / e2e 198 (both engines) / app entry 113.8/114.
37 lines
1.8 KiB
TypeScript
37 lines
1.8 KiB
TypeScript
// Local game id helpers, in a tiny standalone module with no engine/generator imports, so the
|
|
// dispatcher (lib/gamesource) can recognise a local game id — and the game screen can branch on it
|
|
// — without eagerly bundling the whole offline engine. The engine is dynamically imported only when
|
|
// a local game is actually played (keeping the app entry bundle within its size budget).
|
|
|
|
/** LOCAL_ID_PREFIX marks a game id as a local (offline) game. */
|
|
export const LOCAL_ID_PREFIX = 'local:';
|
|
|
|
/** isLocalGameId reports whether an id belongs to a local game. */
|
|
export function isLocalGameId(id: string): boolean {
|
|
return id.startsWith(LOCAL_ID_PREFIX);
|
|
}
|
|
|
|
/** newLocalGameId mints a fresh, device-unique local game id (time + random suffix — no crypto API,
|
|
* so it also works on the older engines the SPA still supports). */
|
|
export function newLocalGameId(): string {
|
|
return `${LOCAL_ID_PREFIX}${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
}
|
|
|
|
// A test seam for the mock e2e: pinning the seed makes a local game deal a known rack, so a spec
|
|
// can play a precomputed opening word. Only the mock build's window.__mock wires setForcedSeed
|
|
// (see lib/gateway.ts); a production build never calls it, so randomSeed stays truly random.
|
|
let forcedSeed: bigint | null = null;
|
|
|
|
/** setForcedSeed pins the value randomSeed returns (the mock-e2e seam); pass null to restore
|
|
* randomness. */
|
|
export function setForcedSeed(seed: bigint | null): void {
|
|
forcedSeed = seed;
|
|
}
|
|
|
|
/** randomSeed returns a random 32-bit seed for a new local game's deterministic bag (or the pinned
|
|
* seed when a test has set one via setForcedSeed). */
|
|
export function randomSeed(): bigint {
|
|
if (forcedSeed !== null) return forcedSeed;
|
|
return BigInt(Math.floor(Math.random() * 0xffffffff));
|
|
}
|