---
name: frontend-vitest
description: Use this skill for frontend tests in this repository when adding or updating Vitest unit tests, Vitest browser-mode view tests, MSW handlers, test fixtures, or shared frontend test utilities under resource/frontend.
---

# Frontend Vitest

Use this skill for all Vitest-based frontend tests in this repository.

## Core Principles

- Keep tests close to the feature they cover.
- Use `*.test.ts` for jsdom unit and composable tests.
- Use `*.browser.test.ts` for browser-mode routed view tests.
- Reuse the shared test harness in `src/test/` instead of rebuilding providers, router setup, or MSW wiring in each file.
- Prefer MSW handlers and fixture builders over handwritten inline API payloads.
- Browser tests should fail on unexpected network requests.
- Standardize on `data-testid` for structural selectors and direct interactions in browser-mode tests.
- Use waits only for real debounce or timer behavior, or for intentional visual pacing while demonstrating a test.

## Current Setup

- Vitest runs in two projects:
  - `unit`: jsdom-based tests with `src/test/setup.unit.ts`
  - `browser`: Playwright-backed Vitest Browser Mode with `src/test/setup.browser.ts`
- Browser-rendered Vue tests use `vitest-browser-vue`.
- Shared test support lives under `resource/frontend/src/test/`.
- Shared API mocking uses MSW in both environments:
  - browser worker: `src/test/msw/browser.ts`
  - node server: `src/test/msw/server.ts`
  - reusable handlers: `src/test/msw/handlers.ts`
- Semantic-model and playground fixtures live in `src/test/fixtures/semanticModel.fixtures.ts`.
- Routed browser tests should use `src/test/renderRoutedView.ts`.
- The MSW worker file lives at `src/test/public/mockServiceWorker.js` and is served by `vitest.config.ts` only for test runs.
- Browser setup already loads global app CSS, installs DSS/browser globals, starts MSW, and forces a desktop viewport.

## Commands

- Prefer repository-level Make targets when running checks from the repo root:
  - `make frontend-test-unit`
  - `make frontend-test-browser`
  - `make frontend-test`
- Frontend-local scripts are:
  - `yarn test:unit`
  - `yarn test:browser`
  - `yarn test:browser:ui`
- Use `make format` after test-related code changes when you want the standard repo cleanup pass.

## Source Structure

Frontend Vitest code should follow this structure:

```text
resource/frontend/
  src/
    test/
      fixtures/
        *.fixtures.ts
      msw/
        browser.ts
        handlers.ts
        server.ts
      public/
        mockServiceWorker.js
      i18n.ts
      queryClient.ts
      renderRoutedView.ts
      router.ts
      setup.browser.ts
      setup.unit.ts
      setupGlobals.ts
    views/
      [name-view]/
        [Name].view.vue
        [Name].view.browser.test.ts
    composables/
      [name].composable.ts
      [name].composable.test.ts
```

## Folder Rules

### `src/test`

- Use for shared Vitest infrastructure only.
- Keep reusable providers, router factories, query-client factories, global setup, MSW setup, and shared fixtures here.
- Do not move feature-specific assertions or one-off helper logic here just because it is used by a single test file.

### `src/test/fixtures`

- Use for reusable semantic-model, version, entity, relationship, draft, and playground builders.
- Extend builders here when multiple tests need the same backend shape.
- Prefer fixture builders over large inline object literals inside test files.

### `src/test/msw`

- Use for shared request handlers and environment-specific MSW entrypoints.
- Build handlers around generated API URLs from `src/api/generated/client`.
- Keep one shared handler model that works in both jsdom and browser-mode tests.

### `src/views/[name-view]`

- Put routed browser tests next to the view entry file they cover.
- Prefer `[Name].view.browser.test.ts` for view-level browser tests.
- Keep view-specific helpers inside the test file unless they are clearly reusable across multiple view tests.

### `src/composables`

- Keep composable unit tests next to the composable using `*.composable.test.ts` or another nearby `*.test.ts` file.
- Prefer unit tests here over browser tests when the behavior is mostly reactive logic and not routed UI behavior.

## Naming Rules

- Use `*.test.ts` for unit tests.
- Use `*.browser.test.ts` for browser-mode tests.
- Keep browser test names aligned to the view file:
  - `EntityList.view.browser.test.ts`
  - `Playground.view.browser.test.ts`
- Keep shared fixture modules explicit with `*.fixtures.ts`.
- Name `data-testid` values with a feature prefix:
  - `entities-view`
  - `entities-add-button`
  - `playground-run-button`

## Unit Test Rules

- Use unit tests for:
  - composables
  - pure helpers
  - small component behavior that does not need a real browser
  - focused state transitions or data mapping logic
- Rely on `src/test/setup.unit.ts` for global setup and the Node MSW server.
- Prefer `@vue/test-utils` for jsdom component mounting.
- Keep unit tests small and behavior-focused.
- If the value of the test depends on real routing, browser APIs, layout chrome, or browser-only timing, move it to browser mode instead.

## Browser Test Rules

- Use browser tests for:
  - routed view behavior
  - navigation flows
  - app-shell interactions
  - autosave and debounce behavior
  - browser-realistic interactions that `jsdom` does not cover well
- Render through `renderRoutedView()`.
- Use `shell: 'app-shell'` when the sidebar or full layout matters.
- Use `shell: 'view'` when only the routed page itself matters.
- Define fresh route records for the test and do not import the singleton app router.
- Use real route params and minimal stub destination routes when the downstream page is not under test.
- Use the same route names as production when sidebar state or navigation depends on route naming.
- Use `worker.use(...)` in browser tests to install handlers per test.
- Unhandled requests fail by default, so add every endpoint the view is expected to call.

## Shared Test Foundation

- `createTestQueryClient()` creates a fresh query client with retries disabled.
- `createTestRouter()` creates a fresh memory-history router per test.
- `createTestI18n()` keeps locale setup consistent.
- `renderRoutedView()` wraps:
  - Pinia
  - Vue Router
  - Vue I18n
  - Vue Query
  - `TooltipProvider`
  - `Toaster`
- `setupGlobals.ts` installs app-level browser globals and resets them between tests.
- `semanticModel.fixtures.ts` is the default place to add reusable backend-shape builders.
- `msw/handlers.ts` is the default place to add reusable read and mutation handlers.

## Selector Strategy

- Add stable `data-testid` attributes to important interactive and asserted UI elements.
- Prefer `getByTestId` for structural assertions and targeted interactions in browser-mode tests.
- Use text assertions when the copy itself is the behavior under test.
- Add `data-testid` for:
  - roots
  - primary actions
  - dialogs
  - panels
  - repeated cards or rows that tests must target directly
- Keep test IDs semantic and stable. Do not base them on styling classes.

## What To Avoid

- Do not place browser tests in `src/test/`.
- Do not hit real backend endpoints from Vitest tests.
- Do not import the production singleton router into test files.
- Do not duplicate provider plumbing that `renderRoutedView()` already supplies.
- Do not keep the MSW worker in `resource/frontend/public/`.
- Do not rely on brittle text or regex selectors when a `data-testid` is the real target.
- Do not use arbitrary sleeps as the main synchronization strategy.
- Do not add shared fixtures or handlers to `src/test/` unless they are genuinely reusable.
- Do not over-couple a first browser test to every nested feature when a stub route or smaller smoke assertion is enough.

## Preferred Decision Order

1. Is this pure logic or a composable? Write a `*.test.ts`.
2. Is this a routed view or a browser-realistic interaction? Write a `*.browser.test.ts`.
3. Does the test need shared backend data? Extend `semanticModel.fixtures.ts`.
4. Does it need shared network behavior? Extend `msw/handlers.ts`.
5. Does it need the app layout and sidebar? Use `renderRoutedView({ shell: 'app-shell' })`.
6. Does the test need to target a specific element directly? Add a stable `data-testid`.
7. Is the assertion only compensating for brittle DOM structure? Improve the selector strategy instead.

## Expected Outcome

Frontend Vitest changes should result in:

- tests that sit next to the code they cover
- a clean separation between unit and browser-mode coverage
- reusable fixtures and handlers across views
- stable selectors that survive copy or styling changes
- new view tests that need new assertions, not new plumbing
