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>
95 lines
3.6 KiB
TypeScript
95 lines
3.6 KiB
TypeScript
/**
|
|
* O-series: /onboarding — collective creation (Fase 7.1).
|
|
*
|
|
* Eva is the seeded no-collective user; the app layout redirects her to
|
|
* /onboarding automatically after login. Each test resets her state in
|
|
* beforeEach so tests don't leak into each other.
|
|
*/
|
|
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('Onboarding', () => {
|
|
test.beforeEach(async () => {
|
|
await resetEva();
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
await resetEva();
|
|
await closePool();
|
|
});
|
|
|
|
test('O-01: non-member Eva is auto-redirected to /onboarding after login', async ({ page }) => {
|
|
await loginAs(page, USERS.eva, { waitForCollective: false });
|
|
// The root +layout.svelte sends users with zero collectives to /onboarding.
|
|
await expect(page).toHaveURL(/\/onboarding$/, { timeout: 15_000 });
|
|
// Create-tab form should be visible by default.
|
|
await expect(page.getByTestId('collective-name-input')).toBeVisible();
|
|
await expect(page.getByTestId('collective-submit')).toBeVisible();
|
|
});
|
|
|
|
test('O-02: happy path — name + emoji → redirect to /lists + sidebar updates', async ({
|
|
page
|
|
}) => {
|
|
await loginAs(page, USERS.eva, { waitForCollective: false });
|
|
await expect(page).toHaveURL(/\/onboarding$/, { timeout: 15_000 });
|
|
|
|
const name = `Eva Home ${Date.now()}`;
|
|
await page.getByTestId('collective-name-input').fill(name);
|
|
// EmojiPicker (Fase 19) — 🏡 lives in the "objects" tab; default is "faces".
|
|
await page.getByTestId('emoji-picker-tab-objects').click();
|
|
await page.getByTestId('emoji-picker-cell-🏡').click();
|
|
await page.getByTestId('collective-submit').click();
|
|
|
|
// Landing page after creation.
|
|
await expect(page).toHaveURL(/\/lists$/, { timeout: 15_000 });
|
|
|
|
// Sidebar (desktop) shows the new name with the chosen emoji.
|
|
const sidebar = page.getByTestId('desktop-sidebar');
|
|
await expect(sidebar.getByText(name, { exact: false })).toBeVisible({ timeout: 10_000 });
|
|
await expect(sidebar.getByText('🏡', { exact: false })).toBeVisible();
|
|
|
|
// localStorage cached the active collective id matching the DB row.
|
|
const activeId = await page.evaluate(() => localStorage.getItem('activeCollectiveId'));
|
|
expect(activeId).toBeTruthy();
|
|
|
|
const res = await sql('SELECT id, name, emoji FROM public.collectives WHERE id = $1', [
|
|
activeId
|
|
]);
|
|
expect(res.rowCount).toBe(1);
|
|
expect(res.rows[0].name).toBe(name);
|
|
expect(res.rows[0].emoji).toBe('🏡');
|
|
|
|
// Eva is admin of the new collective.
|
|
const m = await sql(
|
|
'SELECT role FROM public.collective_members WHERE collective_id = $1 AND user_id = $2',
|
|
[activeId, USERS.eva.id]
|
|
);
|
|
expect(m.rowCount).toBe(1);
|
|
expect(m.rows[0].role).toBe('admin');
|
|
});
|
|
|
|
test('O-03: empty name → submit disabled, no POST fires', async ({ page }) => {
|
|
await loginAs(page, USERS.eva, { waitForCollective: false });
|
|
await expect(page).toHaveURL(/\/onboarding$/, { timeout: 15_000 });
|
|
|
|
// With an empty name, the submit button is disabled by `disabled={!collectiveName.trim()}`.
|
|
const submit = page.getByTestId('collective-submit');
|
|
await expect(submit).toBeDisabled();
|
|
|
|
// A whitespace-only name should still count as empty.
|
|
await page.getByTestId('collective-name-input').fill(' ');
|
|
await expect(submit).toBeDisabled();
|
|
|
|
// No collective rows created for Eva.
|
|
const res = await sql('SELECT count(*)::int AS n FROM public.collectives WHERE created_by = $1', [
|
|
USERS.eva.id
|
|
]);
|
|
expect(res.rows[0].n).toBe(0);
|
|
});
|
|
});
|