Commits merged: - feat(FN-400): Phase-1 selector & route manifest for FN-367/FN-368 Files changed: MANIFEST.md | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) Fusion-Task-Id: FN-400
115 lines
8.9 KiB
Markdown
115 lines
8.9 KiB
Markdown
# FN-400 — Phase-1 Selector & Route Manifest
|
||
|
||
**Scope:** sase.tr web app (`apps/web`), current `main` (commit `08dbde7`).
|
||
**Source of truth for FN-367:** commit `e274e02` (`feat(FN-367): add inline retry affordance after VIN decode failure`).
|
||
**Source of truth for FN-368:** commit `0ce68a7` (`feat(FN-368): parts panel loading state, inert unavailable rows, escape hatch`).
|
||
**Convention note:** This codebase does **not** use `data-testid`. Testability hooks are `data-faro-user-action-name="…"` plus semantic ARIA (`role`, `aria-live`, `aria-disabled`). All selectors below were re-verified against the shipped tree.
|
||
|
||
---
|
||
|
||
## FN-367 — VIN decode failure → rage clicks
|
||
|
||
### Route(s)
|
||
- `css=/dashboard/search` — file: `apps/web/src/routes/dashboard/search.tsx:46` (`createFileRoute("/dashboard/search")`). VIN decode is triggered exclusively from this route. Successful decode navigates to `/dashboard/vehicles/$id` (`search.tsx:179`).
|
||
|
||
### Input selector (VIN field)
|
||
- `css=input[maxlength="17"]` — file: `apps/web/src/routes/dashboard/search.tsx:333-358`.
|
||
- Stable accessor (preferred): `role=textbox` with placeholder `"Şase numarasını girin (17 karakter)"` (`search.tsx:334`).
|
||
- **No `data-testid`, no `data-faro-user-action-name` on the input.** Use the placeholder or `role=textbox`.
|
||
|
||
### Submit / decode trigger
|
||
- `text="Şase Çöz"` button (type=submit) — file: `apps/web/src/routes/dashboard/search.tsx:399-409`.
|
||
- Stable accessor: `role=button` with name `"Şase Çöz"`.
|
||
- Form submit handler: `handleSearch` (`search.tsx:204`).
|
||
- **No `data-faro-user-action-name` on the submit button.**
|
||
|
||
### Error / failure-state selector (the element a user would rage-click)
|
||
- `role="alert"` + `aria-live="assertive"` on the error banner — file: `apps/web/src/routes/dashboard/search.tsx:412-471` (attributes at lines `414` and `415`).
|
||
- Title text inside the banner: `"Şase çözümlenemedi"` (`search.tsx:421`).
|
||
- Introduced/touched by FN-367 (commit `e274e02`). Confirmed present in current tree.
|
||
|
||
### Retry affordance selector (introduced by FN-367)
|
||
- `data-faro-user-action-name="vin-decode-retry"` — file: `apps/web/src/routes/dashboard/search.tsx:446`.
|
||
- Button text: `"Tekrar Dene"` (`search.tsx:453`).
|
||
- Rendered conditionally: only when `error` does **not** contain the substrings `"abone olun"` (subscription wall) or `"tanınamadı"` (unknown VIN — that branch shows a different "report to admin" CTA instead). See guard at `search.tsx:443`.
|
||
- Handler: `handleRetry` (`search.tsx:189`), which fires PostHog event `vin_decode_retry_clicked` (`search.tsx:193`).
|
||
|
||
### data-testid hooks added by FN-367
|
||
- **NOT PRESENT IN CURRENT CODE.** FN-367 did not add `data-testid` attributes. The verification-relevant hooks it added are: (1) `data-faro-user-action-name="vin-decode-retry"` on the retry button (`search.tsx:446`), (2) `role="alert"` + `aria-live="assertive"` on the error banner (`search.tsx:414-415`). The vitest test file `apps/web/src/routes/__tests__/dashboard-search.test.tsx` queries by role/name, not by testid — Playwright should follow the same pattern.
|
||
|
||
### Other useful hooks on this route
|
||
- `data-faro-user-action-name="fill-example-vin"` — example-VIN helper button (`search.tsx:390`).
|
||
- `data-faro-user-action-name="select-history-vin"` — history-row buttons (`search.tsx:560`).
|
||
|
||
---
|
||
|
||
## FN-368 — Parts panel → rage clicks → abandonment
|
||
|
||
### Route(s)
|
||
The parts panel is rendered indirectly via `<SchemaViewer>`, which is mounted on four category-level routes (all four are in scope; the "vehicle detail page" referenced in the spec is the first):
|
||
- `css=/dashboard/vehicles/$id/categories/$categoryId` — file: `apps/web/src/routes/dashboard/vehicles_/$id/categories_/$categoryId.tsx:157-158`. **Primary post-VIN-decode parts surface.**
|
||
- `css=/dashboard/catalog/$brandName/$modelId/categories/$categoryId` — file: `apps/web/src/routes/dashboard/catalog_/$brandName_/$modelId/categories_/$categoryId.tsx:224-225`.
|
||
- `css=/dashboard/catalog/emex/$catalogCode/$vehicleId/groups/$groupId` — file: `apps/web/src/routes/dashboard/catalog_/emex/$catalogCode_/$vehicleId_/groups/$groupId.tsx:83`.
|
||
- `css=/dashboard/catalog/pcat/$catalogId/$modelId/$carId/groups/$groupId` — file: `apps/web/src/routes/dashboard/catalog_/pcat/$catalogId_/$modelId_/$carId_/groups/$groupId.tsx:100`.
|
||
|
||
`SchemaViewer` then renders `<PartsPanel>` at `apps/web/src/components/schema/schema-viewer.tsx:100` (no-schema fallback) and `:168` (split-pane right column).
|
||
|
||
### Parts panel container selector
|
||
- `text="Parcalar"` — the heading inside the container header — file: `apps/web/src/components/schema/parts-panel.tsx:95` (loading state) and `:112` (loaded state).
|
||
- Stable accessor: `role=heading` with name `"Parcalar"`.
|
||
- **No dedicated container `data-testid` or `data-faro-user-action-name` on the wrapper `<div>`.** Use the heading text to scope locators.
|
||
|
||
### Part category / part item selector (the elements users were rage-clicking pre-fix)
|
||
- `data-faro-user-action-name="select-part"` on each part row — file: `apps/web/src/components/schema/parts-panel.tsx:146`.
|
||
- Available rows: `role="button"` + `tabIndex={0}` (`parts-panel.tsx:148-149`).
|
||
- Unavailable rows (the inert, post-fix state that visually replaces the rage-clicked target): `aria-disabled="true"` + `tabIndex={-1}` + stripped click/keydown handlers (`parts-panel.tsx:147-149`, with handler-strip logic at `:166-181`).
|
||
- Visual hint: `className` includes `opacity-40` on unavailable rows (`parts-panel.tsx:158`).
|
||
|
||
### Loading / skeleton state selector
|
||
- `text="Yukleniyor..."` (note: no diacritics in source) inside the panel header during load — file: `apps/web/src/components/schema/parts-panel.tsx:96`.
|
||
- Skeleton rows are 8× `<Skeleton>` elements rendered from `SKELETON_ROW_KEYS` (`parts-panel.tsx:18`, mapped at `:101-103`).
|
||
- Stable accessor: scope to panel by `role=heading name=Parcalar`, then assert sibling `text="Yukleniyor..."`.
|
||
- Introduced by FN-368 (the entire `if (isLoading)` block at `parts-panel.tsx:89-106` is new in commit `0ce68a7`).
|
||
|
||
### Empty / error state selector
|
||
- Empty state: `text="Bu kategori icin parca bulunamadi."` — file: `apps/web/src/components/schema/parts-panel.tsx:119`.
|
||
- Escape-hatch button (introduced by FN-368): `text="Geri don"` — file: `apps/web/src/components/schema/parts-panel.tsx:121` (`<Button … onClick={() => window.history.back()}>Geri don</Button>`).
|
||
- Stable accessor for escape hatch: `role=button` name `"Geri don"`.
|
||
- **No dedicated error state.** The panel does not render a server-error branch — upstream `use-parts` failures bubble to the page-level loading skeleton in `SchemaViewer` (`schema-viewer.tsx:69-83`). Flag for verification author: if FN-368 testing needs an explicit error UI, it does not exist in current code.
|
||
|
||
### data-testid hooks added by FN-368
|
||
- **NOT PRESENT IN CURRENT CODE.** FN-368 did not add `data-testid` attributes. The verification-relevant hooks it added are:
|
||
- `data-faro-user-action-name="select-part"` on every row (`parts-panel.tsx:146`).
|
||
- `aria-disabled="true"` on unavailable rows (`parts-panel.tsx:147`).
|
||
- `role="button"` + `tabIndex={0}` on available rows; `tabIndex={-1}` on unavailable rows (`parts-panel.tsx:148-149`).
|
||
- Loading-state branch with `"Yukleniyor..."` text + skeleton rows (`parts-panel.tsx:89-106`).
|
||
- "Geri don" escape-hatch button in empty state (`parts-panel.tsx:120-123`).
|
||
|
||
The companion vitest spec `apps/web/src/components/schema/__tests__/parts-panel.test.tsx` (added by FN-368) queries by `role` / accessible-name / text — Playwright should match that pattern.
|
||
|
||
---
|
||
|
||
## Drift check
|
||
|
||
| Selector | In commit | In current `main` | Status |
|
||
|---|---|---|---|
|
||
| `role="alert"` + `aria-live="assertive"` on VIN error banner | `e274e02` `search.tsx` | `search.tsx:414-415` | ✅ unchanged |
|
||
| `data-faro-user-action-name="vin-decode-retry"` | `e274e02` `search.tsx` | `search.tsx:446` | ✅ unchanged |
|
||
| `"Tekrar Dene"` button text | `e274e02` | `search.tsx:453` | ✅ unchanged |
|
||
| `data-faro-user-action-name="select-part"` | `0ce68a7` `parts-panel.tsx` | `parts-panel.tsx:146` | ✅ unchanged |
|
||
| `aria-disabled="true"` on unavailable rows | `0ce68a7` | `parts-panel.tsx:147` | ✅ unchanged |
|
||
| `"Yukleniyor..."` loading text | `0ce68a7` | `parts-panel.tsx:96` | ✅ unchanged |
|
||
| `"Geri don"` empty-state escape hatch | `0ce68a7` | `parts-panel.tsx:121` | ✅ unchanged |
|
||
|
||
No drift between the FN-367 / FN-368 PRs and current shipped code on `main` as of `08dbde7`.
|
||
|
||
---
|
||
|
||
## Recommended Playwright locator priority (per project convention)
|
||
|
||
1. `[data-faro-user-action-name="…"]` — preferred where present.
|
||
2. `getByRole(role, { name })` — `role=alert`, `role=button`, `role=heading`, `role=textbox`.
|
||
3. `getByText("…")` / `getByPlaceholder("…")` — last resort for elements without (1) or (2).
|
||
|
||
Do **not** request new `data-testid` attributes unless a target has none of the above; that would force a code change purely for testability and conflicts with the established convention.
|