You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: consolidate E2E selector patterns into CRITICAL-PATHS.md
- tests/e2e/CRITICAL-PATHS.md: merge 5 patterns from CURRENT-STATE.md;
add Why column to all 12 entries for full context (canonical reference)
- docs/CURRENT-STATE.md: replace duplicate 5-row selector table with a
one-line canonical reference → tests/e2e/CRITICAL-PATHS.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| Tailwind multi-class elements |`.grid .grid-cols-4 button`|`div.grid-cols-4 button`| Tailwind puts both classes on the **same** element — space selector requires nesting |
132
-
| Playwright strict mode |`getByRole('heading')`|`getByRole('heading', { name: 'X' })`| Adding seed data can create new headings, breaking previously unambiguous selectors |
133
-
| Dialog scoping |`page.getByRole('button', ...).last()`|`page.getByRole('dialog').getByRole('button', ...)`| Dialog renders after page buttons — scope to dialog to avoid fragile `.last()` ordering |
134
-
|`onFinish` vs `onCancel` separation | Call `setTreinoEmSessao(null)` in `onFinish`| Call it only in `onCancel`| Closing the session in `onFinish` unmounts the component before feedback state can render |
135
-
| CPF uniqueness in E2E | Hard-coded CPF |`timestamp`-derived CPF | Unique constraints fail on re-runs if the same CPF is always used |
127
+
→ Maintained in [`tests/e2e/CRITICAL-PATHS.md`](../tests/e2e/CRITICAL-PATHS.md) — Lessons Learned section (12 validated patterns across It2–It4).
| Section card vs row ambiguity |`div.rounded-lg` matches both outer Card AND inner workout rows | Use `div.rounded-lg.border.p-4` to scope to row-level elements only |
64
-
| Unique test data | Repeated runs with same `objetivo` → strict-mode violations on ALUNO assertions | Append `Date.now()` to objetivo string |
| Shadcn `<Select>` aluno trigger |`getByRole('combobox', { name: /escolha um aluno/i })` fails if Select value changes |`.filter({ hasText: /escolha um aluno/i })`| Shadcn `SelectTrigger` renders the chosen value as its text — accessible name changes after selection |
59
+
| Strict mode with enrollment alunos |`getByRole('option', { name: 'Aluno E2E' })` matches all `e2e+timestamp` alunos | Add `{ exact: true }`| Accumulated enrollment alunos share the prefix; strict mode throws on multiple matches |
60
+
| Custom Combobox (cmdk) trigger |`getByRole('combobox', { name: /selecione/i })` returns 0 — button has no ARIA accessible name | Scope to container: `locator('div.rounded-md.border.p-4').first().getByRole('combobox')`| cmdk trigger button has no `aria-label`; must scope by DOM position instead |
61
+
| cmdk option selection |`.click()` on `getByRole('option')` doesn't reliably trigger `onPointerDown` in portals | Use `searchInput.press('ArrowDown')` + `searchInput.press('Enter')`| cmdk listens to `onPointerDown`, not `onClick`; keyboard nav is more reliable in portals |
62
+
| Shadcn sidebar intercepting clicks | Save button behind sidebar div → pointer-events intercepted |`saveButton.evaluate((el: HTMLElement) => el.click())` (JS click bypasses overlap) | CSS `pointer-events` on overlapping Shadcn sidebar div blocks Playwright synthetic click |
63
+
| Section card vs row ambiguity |`div.rounded-lg` matches both outer Card AND inner workout rows | Use `div.rounded-lg.border.p-4` to scope to row-level elements only | Shadcn `Card` and its child rows share `rounded-lg`; the border+padding combo is row-specific |
64
+
| Unique test data | Repeated runs with same `objetivo` → strict-mode violations on ALUNO assertions | Append `Date.now()` to objetivo string | Treinos persist across runs in local DB; identical text creates multiple matches in strict mode |
65
+
| Tailwind multi-class elements |`.grid .grid-cols-4 button` — treats two classes as parent/child |`div.grid-cols-4 button`| Tailwind puts both classes on the **same** element; CSS space selector implies nesting |
66
+
| Ambiguous heading selectors |`getByRole('heading')` breaks when seed data adds new headings |`getByRole('heading', { name: 'X' })` — always specify name | Playwright strict mode throws if the selector matches more than one element |
67
+
| Dialog scoping |`page.getByRole('button', ...).last()` is fragile if page gains new buttons |`page.getByRole('dialog').getByRole('button', ...)` — scope to dialog | Dialog renders after page-level buttons; `.last()` ordering breaks when DOM changes |
68
+
|`WorkoutSession` state reset |`setTreinoEmSessao(null)` in `onFinish` unmounts component before feedback renders | Call `setTreinoEmSessao(null)` only in `onCancel`, not `onFinish`|`onFinish` transitions state; unmounting early drops feedback card before it can appear |
69
+
| CPF uniqueness in E2E | Hard-coded CPF fails on second run (unique DB constraint) | Use timestamp-derived CPF: `` `${timestamp}`.slice(-11).replace(...) `` or similar | Enrollment tests persist `Aluno` records; re-running with the same CPF hits a unique constraint |
0 commit comments