golfgame/tests/e2e/utils/timing.ts
Aaron D. Lee 6950769bc3 Version 2.0.0: Animation fixes, timing improvements, and E2E test suite
Animation fixes:
- Fix held card positioning bug (was appearing at bottom of page)
- Fix discard pile blank/white flash on turn transitions
- Fix blank card at round end by skipping animations during round_over/game_over
- Set card content before triggering flip animation to prevent flash
- Center suit symbol on 10 cards

Timing improvements:
- Reduce post-discard delay from 700ms to 500ms
- Reduce post-swap delay from 1800ms to 1000ms
- Speed up swap flip animation from 1150ms to 550ms
- Reduce CPU initial thinking delay from 150-250ms to 80-150ms
- Pause now happens after swap completes (showing result) instead of before

E2E test suite:
- Add Playwright-based test bot that plays full games
- State parser extracts game state from DOM for validation
- AI brain ports decision logic for automated play
- Freeze detector monitors for UI hangs
- Visual validator checks CSS states
- Full game, stress, and visual test specs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:33:28 -05:00

72 lines
1.8 KiB
TypeScript

/**
* Animation timing constants from animation-queue.js
* Used to wait for animations to complete before asserting state
*/
export const TIMING = {
// Core animation durations (from CSS/animation-queue.js)
flipDuration: 540,
moveDuration: 270,
pauseAfterFlip: 144,
pauseAfterDiscard: 550,
pauseBeforeNewCard: 150,
pauseAfterSwapComplete: 400,
pauseBetweenAnimations: 90,
// Derived waits for test actions
get flipComplete() {
return this.flipDuration + this.pauseAfterFlip + 100;
},
get swapComplete() {
return this.flipDuration + this.pauseAfterFlip + this.moveDuration +
this.pauseAfterDiscard + this.pauseBeforeNewCard +
this.moveDuration + this.pauseAfterSwapComplete + 200;
},
get drawComplete() {
return this.moveDuration + this.pauseBeforeNewCard + 100;
},
// Safety margins for network/processing
networkBuffer: 200,
safetyMargin: 300,
// Longer waits
turnTransition: 500,
cpuThinkingMin: 400,
cpuThinkingMax: 1200,
roundOverDelay: 1000,
};
/**
* Wait for animation queue to drain
*/
export async function waitForAnimations(page: import('@playwright/test').Page, timeout = 5000): Promise<void> {
await page.waitForFunction(
() => {
const game = (window as any).game;
if (!game?.animationQueue) return true;
return !game.animationQueue.isAnimating();
},
{ timeout }
);
}
/**
* Wait for WebSocket to be ready
*/
export async function waitForWebSocket(page: import('@playwright/test').Page, timeout = 5000): Promise<void> {
await page.waitForFunction(
() => {
const game = (window as any).game;
return game?.ws?.readyState === WebSocket.OPEN;
},
{ timeout }
);
}
/**
* Wait a fixed time plus safety margin
*/
export function safeWait(duration: number): number {
return duration + TIMING.safetyMargin;
}