golfgame/tests/e2e/specs/full-game.spec.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

254 lines
6.7 KiB
TypeScript

/**
* Full game playthrough tests
* Tests complete game sessions with the bot
*/
import { test, expect } from '@playwright/test';
import { GolfBot } from '../bot/golf-bot';
import { FreezeDetector } from '../health/freeze-detector';
import { ScreenshotValidator } from '../visual/screenshot-validator';
test.describe('Full Game Playthrough', () => {
test('bot completes 3-hole game against CPU', async ({ page }) => {
test.setTimeout(180000); // 3 minutes for 3-hole game
const bot = new GolfBot(page);
const freezeDetector = new FreezeDetector(page);
const validator = new ScreenshotValidator(page);
// Navigate to game
await bot.goto();
// Create game and add CPU
const roomCode = await bot.createGame('TestBot');
expect(roomCode).toHaveLength(4);
await bot.addCPU('Sofia');
// Take screenshot of waiting room
await validator.capture('waiting-room');
// Start game with 3 holes
await bot.startGame({ holes: 3 });
// Verify game started
const phase = await bot.getGamePhase();
expect(['initial_flip', 'playing']).toContain(phase);
// Take screenshot of game start
await validator.capture('game-start', phase);
// Play through the entire game
const result = await bot.playGame(3);
// Take final screenshot
await validator.capture('game-over', 'game_over');
// Verify game completed
expect(result.success).toBe(true);
expect(result.rounds).toBeGreaterThanOrEqual(1);
// Check for errors
const errors = bot.getConsoleErrors();
expect(errors).toHaveLength(0);
// Verify no freezes occurred
const health = await freezeDetector.runHealthCheck();
expect(health.healthy).toBe(true);
});
test('bot completes 9-hole game against CPU', async ({ page }) => {
test.setTimeout(900000); // 15 minutes for 9-hole game
const bot = new GolfBot(page);
const freezeDetector = new FreezeDetector(page);
await bot.goto();
const roomCode = await bot.createGame('TestBot');
await bot.addCPU('Marcus');
await bot.startGame({ holes: 9 });
// Play full game
const result = await bot.playGame(9);
expect(result.success).toBe(true);
expect(result.rounds).toBe(9);
// Verify game ended properly
const finalPhase = await bot.getGamePhase();
expect(finalPhase).toBe('game_over');
// Check health
const health = await freezeDetector.runHealthCheck();
expect(health.healthy).toBe(true);
});
test('bot handles initial flip phase correctly', async ({ page }) => {
const bot = new GolfBot(page);
const validator = new ScreenshotValidator(page);
await bot.goto();
await bot.createGame('TestBot');
await bot.addCPU('Sofia');
await bot.startGame({ holes: 1, initialFlips: 2 });
// Wait for initial flip phase
await page.waitForTimeout(500);
// Take screenshot before flips
await validator.capture('before-initial-flip');
// Complete initial flips
await bot.completeInitialFlips();
// Take screenshot after flips
await validator.capture('after-initial-flip');
// Verify 2 cards are face-up
const state = await bot.getGameState();
const faceUpCount = state.myPlayer?.cards.filter(c => c.faceUp).length || 0;
expect(faceUpCount).toBeGreaterThanOrEqual(2);
});
test('bot recovers from rapid turn changes', async ({ page }) => {
test.setTimeout(90000); // 90 seconds
const bot = new GolfBot(page);
await bot.goto();
await bot.createGame('TestBot');
// Add multiple fast CPUs
await bot.addCPU('Maya'); // Aggressive
await bot.addCPU('Sage'); // Sneaky finisher
await bot.startGame({ holes: 1 });
// Play with health monitoring
let frozenCount = 0;
let turnCount = 0;
while (await bot.getGamePhase() !== 'round_over' && turnCount < 50) {
if (await bot.isMyTurn()) {
const result = await bot.playTurn();
expect(result.success).toBe(true);
turnCount++;
}
// Check for freeze
if (await bot.isFrozen(2000)) {
frozenCount++;
}
await page.waitForTimeout(100);
}
// Should not have frozen
expect(frozenCount).toBe(0);
});
test('game handles all players finishing', async ({ page }) => {
test.setTimeout(90000); // 90 seconds for single round
const bot = new GolfBot(page);
const validator = new ScreenshotValidator(page);
await bot.goto();
await bot.createGame('TestBot');
await bot.addCPU('Sofia');
await bot.startGame({ holes: 1 });
// Play until round over
const roundResult = await bot.playRound(100);
expect(roundResult.success).toBe(true);
// Take screenshot of round end
await validator.capture('round-end');
// Verify all player cards are revealed
const state = await bot.getGameState();
const allRevealed = state.myPlayer?.cards.every(c => c.faceUp) ?? false;
expect(allRevealed).toBe(true);
// Verify scoreboard is visible
const scoreboardVisible = await validator.expectVisible('#game-buttons');
expect(scoreboardVisible.passed).toBe(true);
});
});
test.describe('Game Settings', () => {
test('Speed Golf mode (flip on discard)', async ({ page }) => {
test.setTimeout(90000); // 90 seconds
const bot = new GolfBot(page);
await bot.goto();
await bot.createGame('TestBot');
await bot.addCPU('Sofia');
// Start with Speed Golf (always flip)
await bot.startGame({
holes: 1,
flipMode: 'always',
});
// Play through
const result = await bot.playRound(50);
expect(result.success).toBe(true);
// No errors should occur
expect(bot.getConsoleErrors()).toHaveLength(0);
});
test('Endgame mode (optional flip)', async ({ page }) => {
test.setTimeout(90000); // 90 seconds
const bot = new GolfBot(page);
await bot.goto();
await bot.createGame('TestBot');
await bot.addCPU('Sofia');
// Start with Endgame mode
await bot.startGame({
holes: 1,
flipMode: 'endgame',
});
// Play through
const result = await bot.playRound(50);
expect(result.success).toBe(true);
expect(bot.getConsoleErrors()).toHaveLength(0);
});
test('Multiple decks with many players', async ({ page }) => {
test.setTimeout(90000);
const bot = new GolfBot(page);
await bot.goto();
await bot.createGame('TestBot');
// Add 4 CPUs (5 total players)
await bot.addCPU('Sofia');
await bot.addCPU('Marcus');
await bot.addCPU('Maya');
await bot.addCPU('Kenji');
// Start with 2 decks
await bot.startGame({
holes: 1,
decks: 2,
});
// Play through
const result = await bot.playRound(100);
expect(result.success).toBe(true);
expect(bot.getConsoleErrors()).toHaveLength(0);
});
});