/** * EP-E series — Fase 19.4.3. * * The avatar / collective-icon picker on /settings, /onboarding, * /collective/manage and inside CreateCollectiveModal is the new * `` component (4 tabs, ~280 codepoints). These specs * drive the new control end-to-end on the two primary surfaces. * * EP-E-01 — Ana on /settings switches to the avatar=emoji mode, opens * the picker, jumps to the "Comida" tab, picks 🍎, and the * `users.avatar_emoji` row + the on-page Avatar both update. * EP-E-02 — Eva creates her first collective on /onboarding using a * cell from the "Animales" tab — verifies the cross-tab * selection makes it into `collectives.emoji` and that the * sidebar renders the chosen emoji. * * Cleanup: settings test restores Ana's avatar back to initials so * downstream avatar specs don't see a stale 🍎. */ import { test, expect } from '@playwright/test'; import { USERS } from '../fixtures/users.js'; import { loginAs } from '../fixtures/login.js'; import { resetEva } from '../fixtures/db.js'; import { closePool, sql } from '@colectivo/test-utils'; test.describe.configure({ mode: 'serial' }); test.describe('EmojiPicker integration (Fase 19)', () => { test.afterAll(async () => { // Reset Ana's avatar back to initials so the rest of the suite (and // avatar-related visuals) see the default. await sql( `UPDATE public.users SET avatar_type = 'initials', avatar_emoji = NULL WHERE id = $1`, [USERS.ana.id] ); await resetEva(); await closePool(); }); test('EP-E-01: /settings — Ana picks 🍎 from the "Comida" tab', async ({ page }) => { await loginAs(page, USERS.ana); await page.goto('/settings'); // Switch the avatar mode to "Emoji" so the picker mounts. const emojiModeBtn = page.getByRole('button', { name: /Emoji/i }).first(); await expect(emojiModeBtn).toBeVisible({ timeout: 15_000 }); await emojiModeBtn.click(); // Picker root + tabs are visible. const picker = page.getByTestId('emoji-picker'); await expect(picker).toBeVisible({ timeout: 5_000 }); // Jump to "Comida" tab and pick 🍎. await page.getByTestId('emoji-picker-tab-food').click(); await expect(page.getByTestId('emoji-picker-grid')).toHaveAttribute( 'data-category', 'food' ); await page.getByTestId('emoji-picker-cell-🍎').click(); // DB row reflects the change (poll because the UPDATE fires after // onSelect lands in the Svelte runloop). await expect .poll( async () => { const r = await sql( 'SELECT avatar_type, avatar_emoji FROM public.users WHERE id = $1', [USERS.ana.id] ); return r.rows[0] ?? null; }, { timeout: 5_000 } ) .toMatchObject({ avatar_type: 'emoji', avatar_emoji: '🍎' }); }); test('EP-E-02: /onboarding — Eva creates a collective with 🐱 from "Animales"', async ({ page }) => { await resetEva(); await loginAs(page, USERS.eva, { waitForCollective: false }); await expect(page).toHaveURL(/\/onboarding$/, { timeout: 15_000 }); const name = `Eva animals ${Date.now()}`; await page.getByTestId('collective-name-input').fill(name); // Open the picker (it's mounted inline on /onboarding, no toggle). await expect(page.getByTestId('emoji-picker')).toBeVisible({ timeout: 5_000 }); // Switch to the "Animales" tab and pick 🐱. await page.getByTestId('emoji-picker-tab-animals').click(); await expect(page.getByTestId('emoji-picker-grid')).toHaveAttribute( 'data-category', 'animals' ); await page.getByTestId('emoji-picker-cell-🐱').click(); await page.getByTestId('collective-submit').click(); await expect(page).toHaveURL(/\/lists$/, { timeout: 15_000 }); const activeId = await page.evaluate(() => localStorage.getItem('activeCollectiveId')); expect(activeId).toBeTruthy(); const res = await sql('SELECT name, emoji FROM public.collectives WHERE id = $1', [ activeId ]); expect(res.rows[0].name).toBe(name); expect(res.rows[0].emoji).toBe('🐱'); // Sidebar reflects it. const sidebar = page.getByTestId('desktop-sidebar'); await expect(sidebar.getByText('🐱', { exact: false })).toBeVisible({ timeout: 10_000 }); }); });