feat(fase-19): wire EmojiPicker into 4 integration sites + EP-E specs
Replaces the four legacy hardcoded emoji arrays (settings avatar,
onboarding-create, CreateCollectiveModal, manage-collective rename) with
`<EmojiPicker selected={…} onSelect={…} />`. The shared component
delivers ~280 codepoints across 4 tabs instead of the previous 8–24
per surface, with consistent layout and behaviour.
Backwards-incompatible testid change on the manage page: the old
`collective-emoji-option-{e}` per-cell testid is gone — every cell now
lives under the picker's `emoji-picker-cell-{e}` namespace. Two existing
specs (onboarding O-02 + rename MC-01a) are updated to drive the new
tab → cell flow.
New e2e file `tests/e2e/emoji-picker.test.ts` covers EP-E-01..02:
- EP-E-01 — Ana on /settings picks 🍎 from "Comida"; DB row updates.
- EP-E-02 — Eva on /onboarding creates a collective with 🐱 from
"Animales"; sidebar reflects the choice.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
116
apps/web/tests/e2e/emoji-picker.test.ts
Normal file
116
apps/web/tests/e2e/emoji-picker.test.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* EP-E series — Fase 19.4.3.
|
||||
*
|
||||
* The avatar / collective-icon picker on /settings, /onboarding,
|
||||
* /collective/manage and inside CreateCollectiveModal is the new
|
||||
* `<EmojiPicker>` 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
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user