mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-19 12:58:09 +00:00
Initial attempt at some tests that help us determine if the game has basic functionality
Signed-off-by: Tyler Marques <me@tylermarques.com>
This commit is contained in:
parent
e81f41fc57
commit
eebfba0a1b
15 changed files with 848 additions and 9 deletions
281
ai_animation/tests/e2e/test-helpers.ts
Normal file
281
ai_animation/tests/e2e/test-helpers.ts
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
import { Page, expect } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Helper function to wait for the game to be ready and loaded
|
||||
*/
|
||||
export async function waitForGameReady(page: Page, timeout = 15000): Promise<void> {
|
||||
// Wait for the app to fully load
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Wait for Three.js scene to initialize
|
||||
await page.waitForSelector('canvas', { timeout });
|
||||
|
||||
// Wait for essential UI elements to be present
|
||||
await expect(page.locator('#play-btn')).toBeVisible({ timeout });
|
||||
await expect(page.locator('#prev-btn')).toBeVisible({ timeout });
|
||||
await expect(page.locator('#next-btn')).toBeVisible({ timeout });
|
||||
|
||||
// Ensure play button is enabled (indicating game is loaded)
|
||||
await expect(page.locator('#play-btn')).toBeEnabled({ timeout });
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to start game playback and verify it started
|
||||
*/
|
||||
export async function startGamePlayback(page: Page): Promise<void> {
|
||||
await page.click('#play-btn');
|
||||
await expect(page.locator('#play-btn')).toHaveText(/⏸ Pause/);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to stop game playback and verify it stopped
|
||||
*/
|
||||
export async function stopGamePlayback(page: Page): Promise<void> {
|
||||
await page.click('#play-btn');
|
||||
await expect(page.locator('#play-btn')).toHaveText(/▶ Play/);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check if a victory message is present
|
||||
*/
|
||||
export async function checkForVictoryMessage(page: Page): Promise<string | null> {
|
||||
try {
|
||||
const newsText = await page.locator('#news-banner-content').textContent();
|
||||
const victoryPattern = /GAME OVER.*WINS|VICTORIOUS|🏆.*WINS/i;
|
||||
|
||||
if (newsText && victoryPattern.test(newsText)) {
|
||||
return newsText;
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get current game ID
|
||||
*/
|
||||
export async function getCurrentGameId(page: Page): Promise<string | null> {
|
||||
try {
|
||||
return await page.locator('#game-id-display').textContent();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check if game is still playing
|
||||
*/
|
||||
export async function isGamePlaying(page: Page): Promise<boolean> {
|
||||
try {
|
||||
const playButtonText = await page.locator('#play-btn').textContent();
|
||||
return playButtonText?.includes('⏸') || false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for victory timing measurement result
|
||||
*/
|
||||
export interface VictoryTimingResult {
|
||||
victoryDetected: boolean;
|
||||
victoryMessage: string | null;
|
||||
displayDuration: number;
|
||||
nextGameStarted: boolean;
|
||||
gameIdChanged: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to measure victory message timing and next game transition
|
||||
*/
|
||||
export async function measureVictoryTiming(
|
||||
page: Page,
|
||||
maxWaitTime = 60000
|
||||
): Promise<VictoryTimingResult> {
|
||||
const startTime = Date.now();
|
||||
let victoryMessage: string | null = null;
|
||||
let victoryDetected = false;
|
||||
let victoryStartTime = 0;
|
||||
let nextGameStarted = false;
|
||||
let gameIdChanged = false;
|
||||
let initialGameId: string | null = null;
|
||||
|
||||
// Get initial game ID
|
||||
initialGameId = await getCurrentGameId(page);
|
||||
|
||||
while ((Date.now() - startTime) < maxWaitTime) {
|
||||
// Check for victory message
|
||||
if (!victoryDetected) {
|
||||
victoryMessage = await checkForVictoryMessage(page);
|
||||
if (victoryMessage) {
|
||||
victoryDetected = true;
|
||||
victoryStartTime = Date.now();
|
||||
console.log('Victory message detected:', victoryMessage);
|
||||
}
|
||||
}
|
||||
|
||||
// If victory was detected, monitor for next game transition
|
||||
if (victoryDetected) {
|
||||
// Check if game ID changed
|
||||
const currentGameId = await getCurrentGameId(page);
|
||||
if (currentGameId && currentGameId !== initialGameId) {
|
||||
gameIdChanged = true;
|
||||
nextGameStarted = true;
|
||||
console.log('Game ID changed from', initialGameId, 'to', currentGameId);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if victory message disappeared (indicating new game started)
|
||||
const currentVictoryMessage = await checkForVictoryMessage(page);
|
||||
if (!currentVictoryMessage) {
|
||||
nextGameStarted = true;
|
||||
console.log('Victory message disappeared, indicating new game started');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
const displayDuration = victoryDetected ? Date.now() - victoryStartTime : 0;
|
||||
|
||||
return {
|
||||
victoryDetected,
|
||||
victoryMessage,
|
||||
displayDuration,
|
||||
nextGameStarted,
|
||||
gameIdChanged
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check if a two-power conversation is currently displayed
|
||||
*/
|
||||
export async function isTwoPowerConversationOpen(page: Page): Promise<boolean> {
|
||||
try {
|
||||
// Look for the dialogue overlay that appears when a two-power conversation is shown
|
||||
const overlay = page.locator('.dialogue-overlay');
|
||||
return await overlay.isVisible();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to wait for a two-power conversation to close
|
||||
*/
|
||||
export async function waitForTwoPowerConversationToClose(page: Page, timeout = 5000): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
while ((Date.now() - startTime) < timeout) {
|
||||
const isOpen = await isTwoPowerConversationOpen(page);
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
await page.waitForTimeout(100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get current phase name
|
||||
*/
|
||||
export async function getCurrentPhaseName(page: Page): Promise<string | null> {
|
||||
try {
|
||||
const phaseText = await page.locator('#phase-display').textContent();
|
||||
// Extract phase name from "Era: {phaseName}" format
|
||||
return phaseText?.replace('Era: ', '') || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for manual advancement result
|
||||
*/
|
||||
export interface ManualAdvancementResult {
|
||||
victoryReached: boolean;
|
||||
phasesAdvanced: number;
|
||||
twoPowerConversationsFound: number;
|
||||
conversationPhases: string[];
|
||||
finalPhaseName: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to advance game manually by clicking next button with conversation tracking
|
||||
*/
|
||||
export async function advanceGameManually(
|
||||
page: Page,
|
||||
maxClicks = 50,
|
||||
trackConversations = false
|
||||
): Promise<boolean | ManualAdvancementResult> {
|
||||
let clicks = 0;
|
||||
let twoPowerConversationsFound = 0;
|
||||
const conversationPhases: string[] = [];
|
||||
|
||||
while (clicks < maxClicks) {
|
||||
try {
|
||||
// Check for victory message first
|
||||
const victoryMessage = await checkForVictoryMessage(page);
|
||||
if (victoryMessage) {
|
||||
if (trackConversations) {
|
||||
return {
|
||||
victoryReached: true,
|
||||
phasesAdvanced: clicks,
|
||||
twoPowerConversationsFound,
|
||||
conversationPhases,
|
||||
finalPhaseName: await getCurrentPhaseName(page)
|
||||
};
|
||||
}
|
||||
return true; // Victory reached
|
||||
}
|
||||
|
||||
// Get current phase name for tracking
|
||||
const currentPhase = await getCurrentPhaseName(page);
|
||||
|
||||
// Check if we can advance
|
||||
const nextButton = page.locator('#next-btn');
|
||||
if (await nextButton.isEnabled()) {
|
||||
|
||||
// If tracking conversations, check if one opened after clicking next
|
||||
if (trackConversations) {
|
||||
await page.waitForTimeout(300); // Give time for conversation to appear
|
||||
const conversationOpen = await isTwoPowerConversationOpen(page);
|
||||
|
||||
if (conversationOpen) {
|
||||
twoPowerConversationsFound++;
|
||||
if (currentPhase) {
|
||||
conversationPhases.push(currentPhase);
|
||||
}
|
||||
console.log(`Two-power conversation detected at phase: ${currentPhase}`);
|
||||
|
||||
// Wait for conversation to close automatically or close it manually
|
||||
await waitForTwoPowerConversationToClose(page, 35000); // 35 seconds max
|
||||
}
|
||||
await nextButton.click();
|
||||
await page.waitForTimeout(100);
|
||||
}
|
||||
} else {
|
||||
// Can't advance anymore, might be at end
|
||||
break;
|
||||
}
|
||||
|
||||
clicks++;
|
||||
} catch (error) {
|
||||
console.log('Error during manual advance:', error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (trackConversations) {
|
||||
return {
|
||||
victoryReached: false,
|
||||
phasesAdvanced: clicks,
|
||||
twoPowerConversationsFound,
|
||||
conversationPhases,
|
||||
finalPhaseName: await getCurrentPhaseName(page)
|
||||
};
|
||||
}
|
||||
|
||||
return false; // Didn't reach victory
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue