# Active Context — Strava Frontend ## Task State - task_id: TASK-TEST-PUBLIC - status: in_progress - parent_task: — - summary: **Написать Playwright integration тесты для фронтенда в `../integration/tests/`. 3 spec файла: auth-flows ✅, public-pages, auth-protected.** - next task: TASK-TEST-PROTECTED → Verify ## ✅ CODER SUCCESS REPORT — TASK-TEST-AUTH - `../integration/tests/auth-flows.spec.ts` создан: 4 теста (login success, login failure, signup+auto-login, logout) - Self-seed via `POST /api/v0/signup`, Vuestic selectors (`input[type=email]`, `input[type=password]`) - Verified: `npx playwright test tests/auth-flows.spec.ts` → **4 passed (19.2s)** ## TASK-TEST-PUBLIC: public-pages.spec.ts **Цель:** Playwright spec для auth-сценариев (signup, login success/error, logout, validation). **Файл для создания:** `../integration/tests/auth-flows.spec.ts` **Конвенции (см. SKILL.md):** - Self-seed user via `POST /api/v0/signup` in `beforeAll` - Selectors: `input[type=email]`, `input[type=password]`, `getByRole('button', { name: 'Вход' })` - Vuestic floating labels → select by input type, NOT placeholder - Auth mock via `page.evaluate(() => localStorage.setItem(...))` - Do NOT call `expect()` inside `page.evaluate()` - Base URLs: `FRONTEND` (5173), `BACKEND` (8000) - `freshPage()` helper: goto → clear localStorage → goto again **Тесты в spec:** 1. **Signup**: `POST /api/v0/signup` → returns token, user, profile; navigate to `/auth/signup` → fill form → submit → redirect to `/explore` 2. **Login success**: pre-seed user via API → UI login → token in localStorage → redirect to `/explore` 3. **Login failure**: wrong password → error toast appears, stays on `/auth/login` 4. **Logout**: logged in → open profile dropdown → «Выход» → redirect to login, token cleared **Acceptance Criteria:** 1. Spec file created at `../integration/tests/auth-flows.spec.ts` 2. All tests self-contained (self-seed data) 3. Follows existing `sidebar-auth-switch.spec.ts` patterns 4. `npx playwright test tests/auth-flows.spec.ts` passes against integration stack ## TASK-MCP-SETUP: Playwright MCP configuration **Что сделано:** - MCP-конфиг Zoo Code (`mcp_settings.json`): server `web-browser` → `@playwright/mcp@latest`, Node 22 via nvm absolute path, `--headless` - Chromium `1234` уже установлен в `~/.cache/ms-playwright/` - `.mcp.json` создан в проекте (project-level, для совместимости) - `techContext.md` обновлён: секция "UI Verification (MCP Browser)" **Как использовать (workflow):** 1. `yarn dev` — запустить dev server (порт 5173) 2. `browser_navigate` → `http://localhost:5173/explore` — проверить публичную страницу 3. `browser_evaluate` → `() => { localStorage.setItem('token', 'test'); localStorage.setItem('user', JSON.stringify({id:1,name:'Test'})); location.reload(); }` — сымитировать auth 4. `browser_navigate` → проверить auth-страницы (`/workouts`, `/workouts/:id`) 5. `browser_snapshot` — проверить DOM-структуру 6. `browser_take_screenshot` — визуальная проверка 7. `browser_network_requests` — проверить API-запросы ### Баг (контекст) Сайдбар показывает `publicRoutes` (Лента + Маршрут) после логина до F5. Корень: [`NavigationRoutes.ts:70`](src/components/sidebar/NavigationRoutes.ts:70) — `routes` вычисляется один раз при module evaluation и кэшируется. TASK-AUTH-NAV-1 уже добавил реактивный `isAuthenticated` в `useGlobalStore` (инициализация из localStorage + `setAuthenticated` в Login/Logout). Эта задача переключает UI на computed. ### TASK-AUTH-NAV-2: реактивные маршруты (3 файла) **1. `src/components/sidebar/NavigationRoutes.ts`** - Сделать named exports: `export const authRoutes: INavigationRoute[]` и `export const publicRoutes: INavigationRoute[]` (сейчас это локальные `const`, не экспортированные). - Убрать статическое поле `routes` из default export (или оставить default без `routes`, т.к. оба потребителя перейдут на computed). `INavigationRoute` интерфейс — не трогать. - Важно: массивы `authRoutes`/`publicRoutes` остаются теми же по структуре/содержимому — только экспортируются. **2. `src/components/sidebar/AppSidebar.vue`** (Options API, `setup()`) - `import { useGlobalStore } from "../../stores/global-store";` + `import { authRoutes, publicRoutes } from "./NavigationRoutes";` (импорт `navigationRoutes` по default можно оставить/убрать — но все обращения `navigationRoutes.routes` заменить). - В `setup()`: `const globalStore = useGlobalStore();` - computed: `const routes = computed(() => globalStore.isAuthenticated ? authRoutes : publicRoutes);` - В `return` заменить `navigationRoutes` на `routes` (тепловой шаблон уже использует `navigationRoutes.routes` → поправить на `routes.value`? НЕТ — в Options API computed из setup доступно в шаблоне как `routes` без `.value`, т.е. в шаблоне `navigationRoutes.routes` → `routes`). - `setActiveExpand`: `navigationRoutes.routes.map(...)` → `routes.value.map(...)` (в setup `.value` нужен). - Шаблоны: `v-for="(route, index) in navigationRoutes.routes"` → `in routes`. **3. `src/components/app-layout-navigation/AppLayoutNavigation.vue`** (script setup) - `import { authRoutes, publicRoutes } from "../sidebar/NavigationRoutes";` + `import { useGlobalStore } from "../../stores/global-store";` - `const globalStore = useGlobalStore();` - `findRouteName` (строка ~64): `traverse(NavigationRoutes.routes)` → `traverse(globalStore.isAuthenticated ? authRoutes : publicRoutes)`. - Убедиться, что `items` computed (зависит от `route.matched` и `findRouteName`) остаётся реактивным — т.к. `findRouteName` читает `globalStore.isAuthenticated` внутри `items` computed, реactivity сохранится. ### Acceptance Criteria (TASK-AUTH-NAV-2) 1. `yarn lint` → 0 ошибок. 2. `yarn build` → 0 ошибок (vue-tsc --noEmit). 3. `NavigationRoutes.ts`: `authRoutes` и `publicRoutes` экспортируются; статического `routes` с localStorage-тернарником больше нет. 4. `AppSidebar.vue`: routes = computed от `globalStore.isAuthenticated`; `setActiveExpand` использует `routes.value`. 5. `AppLayoutNavigation.vue`: `traverse` использует выбор по `globalStore.isAuthenticated`. 6. **Ручной сценарий (ключевой)**: без F5 — выход из профиля → `/explore` → логин → сайдбар **сразу** показывает «Мои тренировки» (authRoutes). Аут → сайдбар сразу `publicRoutes`. ## ✅ CODER SUCCESS REPORT — TASK-AUTH-NAV-2 - `src/components/sidebar/NavigationRoutes.ts`: `export const authRoutes` / `export const publicRoutes`; удалён статический `routes: localStorage.getItem("token") ? ...` из default export (оставлен только `root`). - `src/components/sidebar/AppSidebar.vue`: импорт `authRoutes, publicRoutes` + `useGlobalStore`; `computed(() => globalStore.isAuthenticated ? authRoutes : publicRoutes)`; `setActiveExpand` использует `routes.value.map`; шаблон `v-for="(route, index) in routes"`. - `src/components/app-layout-navigation/AppLayoutNavigation.vue`: `const globalStore = useGlobalStore()`; `traverse(globalStore.isAuthenticated ? authRoutes : publicRoutes)` внутри `findRouteName` (вызывается в `items` computed → реactivity сохранена). - Verified: `yarn lint` exit 0, `yarn build` exit 0 (vue-tsc --noEmit 0 ошибок, vite ✓ 7.07s). ## История (закрытые задачи) - **TASK-AUTH-NAV-1**: success — `isAuthenticated` + `setAuthenticated` в `useGlobalStore`; Login `setAuthenticated(true)`, Logout `setAuthenticated(false)`. Verified lint/build 0. - TASK-SSR-DETAIL: success — SSR /public/workouts/:id под DOM WorkoutItem.vue. - TASK-404: success — HTTP 404 для неизвестных страниц. - TASK-PUBLIC-ROUTE-FIX: success — workout_public_item в top-level router. - TASK-F11/F12/F13/F14: success — SEO + фиксы карты тренировки.