Files
sternboard/e2e/tests/groups.spec.ts
Millaguie f11ea58c2c
CI/CD Pipeline / test (push) Successful in 26s
CI/CD Pipeline / build-and-push (push) Successful in 44s
Sternboard: free retro board, no signup (initial)
Servidor Fastify + Socket.IO + Drizzle/Postgres; front Vite+React (EN/ES, tema claro/oscuro).
Tableros por columnas con plantillas, notas privadas/públicas, agrupación, votos
configurables (multivoto), timer compartido con alarma, fases, action items,
export Markdown/JSON e import, autodestrucción, gestión de columnas.
Imagen única (front servido por el servidor) y CI que la construye y publica.
2026-06-16 10:27:33 +02:00

100 lines
4.1 KiB
TypeScript

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");
});