Files
kanban/frontend/e2e/requester-input.spec.ts
egutierrez eb1c13d82c feat(kanban): requester input empty + keyboard nav (issue 0088)
CardForm: drop pre-fill of requester from logged user; Enter inside the
Autocomplete no longer submits the form (Mantine handles dropdown
selection; arrows + Enter pick option without closing modal). Submit
remains via "Crear" button or Ctrl+Enter from description.

Adds data-field="requester" and data-test="add-card" selectors for stable
e2e queries.

Tests:
- vitest component test (CardForm.test.tsx): empty input, Enter does not
  submit, submit only via button. Dropdown arrow nav covered by e2e
  (jsdom portal handling is brittle).
- Playwright e2e (requester-input.spec.ts) using new browser capability
  group (pw_kanban_login, pw_keyboard_sequence) from registry.
- seed_e2e_user CLI to create deterministic test user against
  operations.db (bcrypt via standard backend hash).

Setup additions (frontend/):
- vitest + @testing-library + jsdom devDeps
- @playwright/test devDep + playwright.config.ts
- src/test/setup.ts polyfills jsdom for Mantine (matchMedia,
  visualViewport, document.fonts, ResizeObserver)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 12:57:00 +02:00

69 lines
2.7 KiB
TypeScript

import { test, expect } from "@playwright/test";
import { pw_kanban_login } from "../../../../frontend/functions/browser/pw_kanban_login";
import { pw_keyboard_sequence } from "../../../../frontend/functions/browser/pw_keyboard_sequence";
import { pw_wait_predicate } from "../../../../frontend/functions/browser/pw_wait_predicate";
const USER = process.env.KANBAN_USER || "egutierrez";
const PWD = process.env.KANBAN_PWD || "egutierrez";
test.describe("Issue 0088 — requester input vacio + nav teclado", () => {
test("input solicitante entra vacio y ArrowDown+Enter no cierra modal", async ({ page }) => {
await page.goto("/");
await pw_kanban_login(page, { username: USER, password: PWD });
// Abrir Nueva tarjeta del primer "+" disponible en alguna columna del board.
const addBtn = page.locator('[data-test="add-card"]').first();
await addBtn.dispatchEvent("click");
// Modal de Mantine abierto.
const dialog = page.locator("[role=dialog]");
await expect(dialog).toBeVisible();
// Solicitante vacio.
const requester = dialog.locator('input[data-field="requester"]');
await expect(requester).toHaveValue("");
// Necesario titulo para que un eventual submit no se descarte por el guard.
await dialog.locator("textarea").first().fill("e2e test card");
// Tipear + navegar dropdown + Enter.
await requester.focus();
await pw_keyboard_sequence(page, [
{ kind: "type", text: "a", delayMs: 50 },
{ kind: "wait", ms: 300 },
{ kind: "press", key: "ArrowDown" },
{ kind: "press", key: "Enter" },
]);
// Modal sigue visible: Enter no ha cerrado el form.
await page.waitForTimeout(300);
await expect(dialog).toBeVisible();
// Cancelar para limpiar estado.
await dialog.locator("button:has-text('Cancelar')").click();
await expect(dialog).toBeHidden();
});
test("Enter en requester con dropdown cerrado NO cierra modal", async ({ page }) => {
await page.goto("/");
await pw_kanban_login(page, { username: USER, password: PWD });
const addBtn = page.locator('[data-test="add-card"]').first();
await addBtn.dispatchEvent("click");
const dialog = page.locator("[role=dialog]");
await expect(dialog).toBeVisible();
await dialog.locator("textarea").first().fill("e2e test card 2");
const requester = dialog.locator('input[data-field="requester"]');
await requester.focus();
// Press Escape para asegurar dropdown cerrado, luego Enter.
await page.keyboard.press("Escape");
await page.keyboard.press("Enter");
await page.waitForTimeout(200);
await expect(dialog).toBeVisible();
await dialog.locator("button:has-text('Cancelar')").click();
await expect(dialog).toBeHidden();
});
});