soroban-abacus-flashcards/apps/web/e2e/arcade-modal-session.spec.ts

339 lines
12 KiB
TypeScript

import { expect, test } from '@playwright/test'
/**
* Arcade Modal Session E2E Tests
*
* These tests verify that the arcade modal session system works correctly:
* - Users are locked into games once they start
* - Automatic redirects to active games
* - Player modification is blocked during games
* - "Return to Arcade" button properly ends sessions
*/
test.describe('Arcade Modal Session - Redirects', () => {
test.beforeEach(async ({ page }) => {
// Clear arcade session before each test
await page.goto('/arcade')
await page.waitForLoadState('networkidle')
// Click "Return to Arcade" button if it exists (to clear any existing session)
const returnButton = page.locator('button:has-text("Return to Arcade")')
if (await returnButton.isVisible({ timeout: 1000 }).catch(() => false)) {
await returnButton.click()
await page.waitForLoadState('networkidle')
}
})
test('should stay on arcade lobby when no active session', async ({ page }) => {
await page.goto('/arcade')
await page.waitForLoadState('networkidle')
// Should see "Champion Arena" title
const title = page.locator('h1:has-text("Champion Arena")')
await expect(title).toBeVisible()
// Should be able to select players
const playerSection = page.locator('text=/Player|Select|Add/i')
await expect(playerSection.first()).toBeVisible()
})
test('should redirect from arcade to active game when session exists', async ({ page }) => {
// Start a game to create a session
await page.goto('/arcade')
await page.waitForLoadState('networkidle')
// Find and click a player card to activate
const playerCard = page.locator('[data-testid="player-card"]').first()
if (await playerCard.isVisible({ timeout: 2000 }).catch(() => false)) {
await playerCard.click()
await page.waitForTimeout(500)
}
// Navigate to matching game to create session
await page.goto('/arcade/matching')
await page.waitForLoadState('networkidle')
// Start the game (click Start button if visible)
const startButton = page.locator('button:has-text("Start")')
if (await startButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await startButton.click()
await page.waitForTimeout(1000)
}
// Try to navigate back to arcade lobby
await page.goto('/arcade')
await page.waitForTimeout(2000) // Give time for redirect
// Should be redirected back to the game
await expect(page).toHaveURL(/\/arcade\/matching/)
const gameTitle = page.locator('h1:has-text("Memory Pairs")')
await expect(gameTitle).toBeVisible()
})
test('should redirect to correct game when navigating to wrong game', async ({ page }) => {
// Create a session with matching game
await page.goto('/arcade')
await page.waitForLoadState('networkidle')
// Activate a player
const addPlayerButton = page.locator('button:has-text("Add Player"), button:has-text("+")')
if (
await addPlayerButton
.first()
.isVisible({ timeout: 2000 })
.catch(() => false)
) {
await addPlayerButton.first().click()
await page.waitForTimeout(500)
}
// Go to matching game
await page.goto('/arcade/matching')
await page.waitForLoadState('networkidle')
// Start game if needed
const startButton = page.locator('button:has-text("Start")')
if (await startButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await startButton.click()
await page.waitForTimeout(1000)
}
// Try to navigate to a different game
await page.goto('/arcade/memory-quiz')
await page.waitForTimeout(2000) // Give time for redirect
// Should be redirected back to matching
await expect(page).toHaveURL(/\/arcade\/matching/)
})
test('should NOT redirect when on correct game page', async ({ page }) => {
// Navigate to matching game
await page.goto('/arcade/matching')
await page.waitForLoadState('networkidle')
// Should stay on matching page
await expect(page).toHaveURL(/\/arcade\/matching/)
const gameTitle = page.locator('h1:has-text("Memory Pairs")')
await expect(gameTitle).toBeVisible()
})
})
test.describe('Arcade Modal Session - Player Modification Blocking', () => {
test.beforeEach(async ({ page }) => {
// Clear session
await page.goto('/arcade')
await page.waitForLoadState('networkidle')
const returnButton = page.locator('button:has-text("Return to Arcade")')
if (await returnButton.isVisible({ timeout: 1000 }).catch(() => false)) {
await returnButton.click()
await page.waitForLoadState('networkidle')
}
})
test('should allow player modification in arcade lobby with no session', async ({ page }) => {
await page.goto('/arcade')
await page.waitForLoadState('networkidle')
// Look for add player button (should be enabled)
const addPlayerButton = page.locator('button:has-text("Add Player"), button:has-text("+")')
const firstButton = addPlayerButton.first()
if (await firstButton.isVisible({ timeout: 2000 }).catch(() => false)) {
// Should be clickable
await expect(firstButton).toBeEnabled()
// Try to click it
await firstButton.click()
await page.waitForTimeout(500)
// Should see player added
const activePlayer = page.locator('[data-testid="active-player"]')
await expect(activePlayer.first()).toBeVisible({ timeout: 3000 })
}
})
test('should block player modification during active game', async ({ page }) => {
// Start a game
await page.goto('/arcade/matching')
await page.waitForLoadState('networkidle')
// Start game
const startButton = page.locator('button:has-text("Start")')
if (await startButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await startButton.click()
await page.waitForTimeout(1000)
}
// Look for player modification controls
// They should be disabled or have reduced opacity
const playerControls = page.locator('[data-testid="player-controls"], .player-list')
if (await playerControls.isVisible({ timeout: 1000 }).catch(() => false)) {
// Check if controls have pointer-events: none or low opacity
const opacity = await playerControls.evaluate((el) => {
return window.getComputedStyle(el).opacity
})
// If controls are visible, they should be dimmed (opacity < 1)
if (parseFloat(opacity) < 1) {
expect(parseFloat(opacity)).toBeLessThan(1)
}
}
// "Add Player" button should not be visible during game
const addPlayerButton = page.locator('button:has-text("Add Player")')
if (await addPlayerButton.isVisible({ timeout: 500 }).catch(() => false)) {
// If visible, should be disabled
const isDisabled = await addPlayerButton.isDisabled()
expect(isDisabled).toBe(true)
}
})
test('should show "Return to Arcade" button during game', async ({ page }) => {
// Start a game
await page.goto('/arcade/matching')
await page.waitForLoadState('networkidle')
// Look for "Return to Arcade" button
const returnButton = page.locator('button:has-text("Return to Arcade")')
// During game setup, might see "Setup" button instead
const setupButton = page.locator('button:has-text("Setup")')
// One of these should be visible
const hasReturnButton = await returnButton.isVisible({ timeout: 2000 }).catch(() => false)
const hasSetupButton = await setupButton.isVisible({ timeout: 2000 }).catch(() => false)
expect(hasReturnButton || hasSetupButton).toBe(true)
})
test('should NOT show "Setup" button in arcade lobby with no session', async ({ page }) => {
await page.goto('/arcade')
await page.waitForLoadState('networkidle')
// Should NOT see "Return to Arcade" or "Setup" button in lobby
const returnButton = page.locator('button:has-text("Return to Arcade")')
const setupButton = page.locator('button:has-text("Setup")')
const hasReturnButton = await returnButton.isVisible({ timeout: 1000 }).catch(() => false)
const hasSetupButton = await setupButton.isVisible({ timeout: 1000 }).catch(() => false)
// Neither should be visible in empty lobby
expect(hasReturnButton).toBe(false)
expect(hasSetupButton).toBe(false)
})
})
test.describe('Arcade Modal Session - Return to Arcade Button', () => {
test.beforeEach(async ({ page }) => {
// Clear session
await page.goto('/arcade')
await page.waitForLoadState('networkidle')
})
test('should end session and return to arcade when clicking "Return to Arcade"', async ({
page,
}) => {
// Start a game
await page.goto('/arcade/matching')
await page.waitForLoadState('networkidle')
// Start game if needed
const startButton = page.locator('button:has-text("Start")')
if (await startButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await startButton.click()
await page.waitForTimeout(1000)
}
// Find and click "Return to Arcade" button
const returnButton = page.locator('button:has-text("Return to Arcade")')
if (await returnButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await returnButton.click()
await page.waitForTimeout(1000)
// Should be redirected to arcade lobby
await expect(page).toHaveURL(/\/arcade\/?$/)
// Should see arcade lobby title
const title = page.locator('h1:has-text("Champion Arena")')
await expect(title).toBeVisible()
// Now should be able to modify players again
const addPlayerButton = page.locator('button:has-text("Add Player"), button:has-text("+")')
if (
await addPlayerButton
.first()
.isVisible({ timeout: 2000 })
.catch(() => false)
) {
await expect(addPlayerButton.first()).toBeEnabled()
}
}
})
test('should allow navigating to different game after returning to arcade', async ({ page }) => {
// Start matching game
await page.goto('/arcade/matching')
await page.waitForLoadState('networkidle')
// Return to arcade
const returnButton = page.locator(
'button:has-text("Return to Arcade"), button:has-text("Setup")'
)
if (
await returnButton
.first()
.isVisible({ timeout: 2000 })
.catch(() => false)
) {
await returnButton.first().click()
await page.waitForTimeout(1000)
}
// Should be in arcade lobby
await expect(page).toHaveURL(/\/arcade\/?$/)
// Now navigate to different game - should NOT redirect back to matching
await page.goto('/arcade/memory-quiz')
await page.waitForTimeout(2000)
// Should stay on memory-quiz (not redirect back to matching)
await expect(page).toHaveURL(/\/arcade\/memory-quiz/)
// Should see memory quiz title
const title = page.locator('h1:has-text("Memory Lightning")')
await expect(title).toBeVisible({ timeout: 3000 })
})
})
test.describe('Arcade Modal Session - Session Persistence', () => {
test('should maintain active session across page reloads', async ({ page }) => {
// Start a game
await page.goto('/arcade/matching')
await page.waitForLoadState('networkidle')
// Start game
const startButton = page.locator('button:has-text("Start")')
if (await startButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await startButton.click()
await page.waitForTimeout(1000)
}
// Reload the page
await page.reload()
await page.waitForLoadState('networkidle')
// Should still be on matching game
await expect(page).toHaveURL(/\/arcade\/matching/)
const gameTitle = page.locator('h1:has-text("Memory Pairs")')
await expect(gameTitle).toBeVisible()
// Try to navigate to arcade
await page.goto('/arcade')
await page.waitForTimeout(2000)
// Should be redirected back to matching
await expect(page).toHaveURL(/\/arcade\/matching/)
})
})