import { test, expect, type Page, type Locator } from "@playwright/test"; async function createBoard(page: Page, title: string) { await page.goto("/"); await page.getByTestId("board-title").fill(title); await page.getByTestId("template").selectOption("mad-sad-glad"); await page.getByTestId("create").click(); await page.waitForURL(/\/b\/.+/); return page.url(); } async function joinAs(page: Page, boardUrl: string, name: string) { await page.goto(boardUrl); await page.getByTestId("name").fill(name); await page.getByTestId("join").click(); await expect(page.getByTestId("column").first()).toBeVisible(); } // Escribe una nota privada y la publica (queda en el área pública). // Esperamos el conteo de notas privadas para evitar clics sobre elementos que re-renderizan. async function addPublicNote(col: Locator, text: string) { await col.getByTestId("note-input").fill(text); await col.getByTestId("note-input").press("Enter"); await expect(col.getByTestId("private-note")).toHaveCount(1); await col.getByTestId("private-note").getByTestId("publish").click(); await expect(col.getByTestId("private-note")).toHaveCount(0); } // Arrastra una nota (desde su tirador) sobre el centro de otra (gesto de agrupar). async function dragOnto(page: Page, sourceNote: Locator, target: Locator) { const handle = await sourceNote.getByTestId("note-handle").boundingBox(); const t = await target.boundingBox(); if (!handle || !t) throw new Error("sin boundingBox"); await page.mouse.move(handle.x + handle.width / 2, handle.y + handle.height / 2); await page.mouse.down(); await page.mouse.move(handle.x + handle.width / 2, handle.y + handle.height / 2 + 8); // supera el umbral await page.mouse.move(t.x + t.width / 2, t.y + t.height / 2, { steps: 12 }); await page.mouse.up(); } test("agrupar dos notas arrastrando una sobre otra", async ({ page }) => { const url = await createBoard(page, "Retro grupos"); await joinAs(page, url, "Ana"); const col = page.getByTestId("column").first(); await addPublicNote(col, "primera"); await addPublicNote(col, "segunda"); await expect(col.getByTestId("note")).toHaveCount(2); const notes = col.getByTestId("note"); await dragOnto(page, notes.nth(1), notes.nth(0)); // aparece un grupo con dos notas dentro await expect(page.getByTestId("group")).toHaveCount(1); await expect(page.getByTestId("group").getByTestId("note")).toHaveCount(2); // votar el grupo como unidad await page.getByTestId("group").getByTestId("vote").click(); await expect(page.getByTestId("group").getByTestId("vote")).toHaveClass(/voted/); // y nombrarlo await page.getByTestId("group-title").fill("Tema común"); await page.getByTestId("group-title").blur(); await expect(page.getByTestId("group-title")).toHaveValue("Tema común"); }); test("multivoto: apilar varios votos en el mismo ítem y quitar", async ({ page }) => { const url = await createBoard(page, "Retro multivoto"); await joinAs(page, url, "Ana"); const col = page.getByTestId("column").first(); await addPublicNote(col, "favorita"); await expect(col.getByTestId("note")).toHaveCount(1); // dos votos al mismo ítem (multivoto activo por defecto) await page.getByTestId("vote").click(); await page.getByTestId("vote").click(); // el moderador revela los totales -> debe verse 2 await page.getByTestId("reveal-votes").click(); await expect(page.getByTestId("vote")).toContainText("2"); // quitar uno -> 1 await page.getByTestId("vote-remove").click(); await expect(page.getByTestId("vote")).toContainText("1"); }); test("el moderador cambia los ajustes en caliente (título y votos)", async ({ page }) => { const url = await createBoard(page, "Original"); await joinAs(page, url, "Mod"); await page.getByTestId("settings").click(); await page.getByTestId("settings-title").fill("Renombrada en vivo"); await page.getByTestId("settings-votes").fill("9"); await page.getByTestId("settings-save").click(); await expect(page.locator("header h1")).toHaveText("Renombrada en vivo"); await expect(page.locator(".votes-pill")).toContainText("9"); });