2 * UI action executors for Playwright-based live testing. 3 * Maps game actions to actual UI interactions. 7// Check what UI state is currently visible 8const getCurrentUiState = async (page) => { 10 { testid: 'alias-input', state: 'join' }, 11 { testid: 'description-input', state: 'describe' }, 12 { testid: 'drawing-canvas', state: 'draw' }, 13 { testid: 'waiting-state', state: 'waiting' }, 14 { testid: 'game-area', state: 'inGame' }, 17 for (const { testid, state } of checks) { 18 const isVisible = await page.locator(`[data-testid="${testid}"]`).isVisible().catch(() => false) 19 if (isVisible) return state 24export const executeUiAction = async (page, { playerAction, ...params }) => { 26 setPlayerAlias: () => setPlayerAlias(page, params.playerAlias), 27 submitDescription: () => submitDescription(page, params.descriptionText), 28 submitDrawing: () => submitDrawing(page, params.finishedTrails), 29 leaveGame: () => leaveGame(page), 32 const actionFn = actionMap[playerAction] 35 return await actionFn() 38const setPlayerAlias = async (page, alias) => { 39 // Check if join form is visible - if not, player is already in game 40 const aliasInput = page.locator('[data-testid="alias-input"]') 41 const isJoinVisible = await aliasInput.isVisible().catch(() => false) 44 // Already joined, skip 45 return { ok: true, skipped: true } 48 // Wait for alias input to be ready 49 await aliasInput.waitFor({ state: 'visible', timeout: 10000 }) 52 await aliasInput.clear() 53 await aliasInput.fill(alias) 56 await page.click('[data-testid="join-button"]') 58 // Wait for game area to appear (indicates successful join) 59 await page.waitForSelector('[data-testid="game-area"]', { timeout: 15000 }) 64const submitDescription = async (page, descriptionText) => { 65 // Check if we're in the right state 66 const currentState = await getCurrentUiState(page) 67 if (currentState !== 'describe') { 68 // Not in describe state, skip 69 return { ok: true, skipped: true, reason: `wrong state: ${currentState}` } 72 // Wait for description input 73 const descInput = page.locator('[data-testid="description-input"]') 74 await descInput.waitFor({ state: 'visible', timeout: 10000 }) 77 await descInput.fill(descriptionText) 80 await page.click('[data-testid="submit-description-btn"]') 82 // Wait for the action to complete - either waiting state or next task 84 page.waitForSelector('[data-testid="waiting-state"]', { timeout: 10000 }), 85 page.waitForSelector('[data-testid="drawing-canvas"]', { timeout: 10000 }), 86 page.waitForSelector('[data-testid="description-input"]', { timeout: 10000 }), 92const submitDrawing = async (page, finishedTrails) => { 93 // Check if we're in the right state 94 const currentState = await getCurrentUiState(page) 95 if (currentState !== 'draw') { 96 // Not in draw state, skip 97 return { ok: true, skipped: true, reason: `wrong state: ${currentState}` } 100 // Wait for canvas wrapper 101 const canvasWrapper = page.locator('[data-testid="drawing-canvas"]') 102 await canvasWrapper.waitFor({ state: 'visible', timeout: 10000 }) 104 // Get the actual canvas element inside 105 const canvas = canvasWrapper.locator('canvas') 106 const box = await canvas.boundingBox() 110 // Draw a simple line (POC - doesn't replicate exact trails) 111 // For now, just draw something so the submit button becomes enabled 112 await page.mouse.move(box.x + 50, box.y + 50) 113 await page.mouse.down() 114 await page.mouse.move(box.x + 100, box.y + 100) 115 await page.mouse.move(box.x + 150, box.y + 150) 116 await page.mouse.up() 118 // Small delay to let React state update 119 await page.waitForTimeout(200) 121 // Click submit - wait for button to be enabled 122 const submitBtn = page.locator('[data-testid="submit-drawing-btn"]') 123 await submitBtn.waitFor({ state: 'visible', timeout: 5000 }) 125 // Check if button is disabled (not enough ink drawn) 126 const isDisabled = await submitBtn.isDisabled() 129 await page.mouse.move(box.x + 50, box.y + 100) 130 await page.mouse.down() 131 await page.mouse.move(box.x + 150, box.y + 100) 132 await page.mouse.up() 133 await page.waitForTimeout(200) 136 await submitBtn.click({ timeout: 10000 }) 138 // Wait for action to complete 140 page.waitForSelector('[data-testid="waiting-state"]', { timeout: 10000 }), 141 page.waitForSelector('[data-testid="description-input"]', { timeout: 10000 }), 142 page.waitForSelector('[data-testid="drawing-canvas"]', { timeout: 10000 }), 148const leaveGame = async (page) => { 149 // Click leave button 150 await page.click('[data-testid="leave-game-btn"]') 152 // Handle confirmation dialog - the GameActionBtn uses browser confirm() 153 page.once('dialog', async dialog => { 154 await dialog.accept() 157 // Wait a bit for the action to process 158 await page.waitForTimeout(1000)