/** * S-series (Modo Compra) — Fase 2b.3 * * Full-screen shopping session at `/lists/[id]/session`. Large tap targets, * flip animation moving items between TO BUY / CHECKED, and a confirmation * modal for "Finish shopping" that marks the list completed and returns to * `/lists`. */ import { test, expect } from '@playwright/test'; import { USERS } from '../fixtures/users.js'; import { loginAs } from '../fixtures/login.js'; const SEED_LIST_ID = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'; const SEED_LIST_PATH = `/lists/${SEED_LIST_ID}`; const SESSION_PATH = `${SEED_LIST_PATH}/session`; test.describe('Shopping session — full-screen mode', () => { test.beforeEach(async ({ page }) => { await loginAs(page, USERS.borja); }); test('S-01: navigating to /lists/[id]/session shows the full-screen container', async ({ page }) => { await page.goto(SESSION_PATH); await expect(page.getByTestId('shopping-session')).toBeVisible({ timeout: 15_000 }); // The finish CTA must be visible — it's the whole point of the view. await expect(page.getByRole('button', { name: /finish shopping|terminar compra/i })).toBeVisible(); }); test('S-02: checking an item moves it into the CHECKED section', async ({ page }) => { // Ensure at least one unchecked item exists by creating one from the // regular list page (simpler than seeding items in the session view). await page.goto(SEED_LIST_PATH); await expect(page.getByPlaceholder(/add item|añadir producto/i)).toBeVisible({ timeout: 15_000 }); const itemName = `S-02-${Date.now()}`; await page.getByPlaceholder(/add item|añadir producto/i).fill(itemName); await page.getByPlaceholder(/add item|añadir producto/i).press('Enter'); await expect(page.getByText(itemName)).toBeVisible({ timeout: 5_000 }); // Now go to the session view and check the item await page.goto(SESSION_PATH); await expect(page.getByTestId('shopping-session')).toBeVisible({ timeout: 15_000 }); const row = page.locator('[role="listitem"]').filter({ hasText: itemName }); await row.getByRole('button', { name: /toggle item/i }).click(); // The row's toggle now has aria-label "Uncheck item" (in CHECKED section) await expect( page.locator('[role="listitem"]').filter({ hasText: itemName }).getByRole('button', { name: /uncheck item/i }) ).toBeVisible({ timeout: 5_000 }); }); test('S-03: Finish shopping → confirm → list is completed and we return to /lists', async ({ page }) => { // Need a fresh list so completing it doesn't affect shared seed state. // Fase 18: masthead "New list" → modal opens → fill + submit → lands // on the detail view at /lists/[id]. await page.goto('/lists'); await page .getByRole('button', { name: /new list|nueva lista/i }) .click(); const listName = `S-03-list-${Date.now()}`; await page.getByTestId('create-list-modal-name').fill(listName); await page.getByTestId('create-list-modal-submit').click(); await expect(page).toHaveURL(/\/lists\/[0-9a-f-]+$/, { timeout: 10_000 }); // Open session mode await page.getByTestId('start-session').click(); await expect(page.getByTestId('shopping-session')).toBeVisible({ timeout: 15_000 }); // Finish shopping → confirm await page.getByRole('button', { name: /finish shopping|terminar compra/i }).click(); await page.getByRole('button', { name: /yes, finish|sí, terminar/i }).click(); // Redirected back to /lists, and the completed list is not in the // active grid anymore (it moves to "Completed"). await expect(page).toHaveURL(/\/lists\/?$/, { timeout: 10_000 }); }); });