import { test, expect, stubOpenImageDialog, invokeInRenderer } from './fixtures/electron'; test.describe('Players library', () => { test('CRUD: add player with progress overlay, ring color persists in store', async ({ electronApp, editorWindow, sampleImagePath, }) => { await stubOpenImageDialog(electronApp, sampleImagePath); await editorWindow.getByTestId('players-header-btn').click(); await expect(editorWindow.getByTestId('players-modal')).toBeVisible(); await editorWindow.getByTestId('players-add').click(); await editorWindow.getByPlaceholder(/Имя игрока|Player name/i).fill('Ada Lovelace'); await editorWindow.getByTestId('players-choose-image').click(); await expect(editorWindow.getByTestId('player-token-preview')).toBeVisible(); const colorInput = editorWindow.locator('input[type="color"]').first(); await colorInput.fill('#224466'); await editorWindow.getByTestId('players-save').click(); // Progress overlay may flash quickly; assert it either appeared or save finished. await expect .poll(async () => editorWindow.locator('[data-testid^="player-row-"]').count(), { timeout: 30_000, }) .toBe(1); const listed = await invokeInRenderer<{ players: Array<{ name: string; ringColor: string }> }>( editorWindow, 'players.list', {}, ); expect(listed.players).toHaveLength(1); expect(listed.players[0]!.name).toBe('Ada Lovelace'); expect(listed.players[0]!.ringColor.toLowerCase()).toBe('#224466'); }); test('Teams: assign then delete team leaves player ungrouped; no subteam UI', async ({ electronApp, editorWindow, sampleImagePath, }) => { await stubOpenImageDialog(electronApp, sampleImagePath); await editorWindow.getByTestId('players-header-btn').click(); const modal = editorWindow.getByTestId('players-modal'); await expect(modal).toBeVisible(); await expect(modal).not.toContainText(/подкоманд|sub-?team|parentId/i); await editorWindow.getByTestId('players-add').click(); await editorWindow.getByPlaceholder(/Имя игрока|Player name/i).fill('Bob'); await editorWindow.getByTestId('players-choose-image').click(); await editorWindow.getByTestId('players-save').click(); await expect(editorWindow.locator('[data-testid^="player-row-"]')).toHaveCount(1, { timeout: 30_000, }); const dialogHandler = async (d: { message: () => string; accept: (text?: string) => Promise }) => { const msg = d.message(); if (/команд|team/i.test(msg) && /цвет|color/i.test(msg)) await d.accept('#ff0000'); else if (/цвет|color/i.test(msg)) await d.accept('#ff0000'); else await d.accept('Party'); }; editorWindow.on('dialog', dialogHandler); await editorWindow.getByTestId('players-add-team').click(); // Allow prompts to settle await editorWindow.waitForTimeout(500); editorWindow.off('dialog', dialogHandler); const state = await invokeInRenderer<{ players: Array<{ id: string; teamId: string | null }>; teams: Array<{ id: string; name: string }>; }>(editorWindow, 'players.list', {}); // If UI prompts failed (headless), create team via IPC. let team = state.teams.find((t) => t.name === 'Party'); if (!team) { const created = await invokeInRenderer<{ team: { id: string; name: string } }>( editorWindow, 'players.upsertTeam', { name: 'Party', color: '#ff0000' }, ); team = created.team; } const player = state.players[0]!; expect(team).toBeTruthy(); await invokeInRenderer(editorWindow, 'players.assignTeam', { playerId: player.id, teamId: team!.id, }); let mid = await invokeInRenderer<{ players: Array<{ teamId: string | null }> }>( editorWindow, 'players.list', {}, ); expect(mid.players[0]!.teamId).toBe(team!.id); await invokeInRenderer(editorWindow, 'players.deleteTeam', { id: team!.id }); mid = await invokeInRenderer(editorWindow, 'players.list', {}); expect(mid.players[0]!.teamId).toBeNull(); }); });