/** * 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. // Create via the lists overview. await page.goto('/lists'); const listInput = page.getByPlaceholder(/weekly shop|compra semanal/i); await expect(listInput).toBeVisible({ timeout: 15_000 }); const listName = `S-03-list-${Date.now()}`; await listInput.fill(listName); await listInput.press('Enter'); // Wait for the new list's heading so we can click into it const heading = page.getByRole('heading', { name: new RegExp(`^${listName}$`) }); await expect(heading).toBeVisible({ timeout: 5_000 }); // Navigate via the heading (linked to /lists/[id]) await heading.click(); await expect(page).toHaveURL(/\/lists\/[0-9a-f-]+$/, { timeout: 5_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 }); }); });