🌳
pt0/gamesapp/testsF/liveUiActionsAI.mjs
1/**
2 * UI action executors for Playwright-based live testing.
3 * Maps game actions to actual UI interactions.
4 */
7// Check what UI state is currently visible
8const getCurrentUiState = async (page) => {
9 const checks = [
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' },
15 ]
17 for (const { testid, state } of checks) {
18 const isVisible = await page.locator(`[data-testid="${testid}"]`).isVisible().catch(() => false)
19 if (isVisible) return state
20 }
21 return 'unknown'
24export const executeUiAction = async (page, { playerAction, ...params }) => {
25 const actionMap = {
26 setPlayerAlias: () => setPlayerAlias(page, params.playerAlias),
27 submitDescription: () => submitDescription(page, params.descriptionText),
28 submitDrawing: () => submitDrawing(page, params.finishedTrails),
29 leaveGame: () => leaveGame(page),
30 }
32 const actionFn = actionMap[playerAction]
33 assertDefined(actionFn, {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)
43 if (!isJoinVisible) {
44 // Already joined, skip
45 return { ok: true, skipped: true }
46 }
48 // Wait for alias input to be ready
49 await aliasInput.waitFor({ state: 'visible', timeout: 10000 })
51 // Clear and fill
52 await aliasInput.clear()
53 await aliasInput.fill(alias)
55 // Click join button
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 })
61 return { ok: true }
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}` }
70 }
72 // Wait for description input
73 const descInput = page.locator('[data-testid="description-input"]')
74 await descInput.waitFor({ state: 'visible', timeout: 10000 })
76 // Fill description
77 await descInput.fill(descriptionText)
79 // Click submit
80 await page.click('[data-testid="submit-description-btn"]')
82 // Wait for the action to complete - either waiting state or next task
83 await Promise.race([
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 }),
87 ]).catch(() => {})
89 return { ok: true }
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}` }
98 }
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()
127 if (isDisabled) {
128 // Draw more
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)
134 }
136 await submitBtn.click({ timeout: 10000 })
138 // Wait for action to complete
139 await Promise.race([
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 }),
143 ]).catch(() => {})
145 return { ok: true }
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()
155 })
157 // Wait a bit for the action to process
158 await page.waitForTimeout(1000)
160 return { ok: true }