---
name: frontend-e2e
description: Use this skill for Playwright end-to-end tests in this repository when adding or updating DSS-backed E2E coverage, shared Playwright fixtures, E2E helpers, or E2E CI workflows under resource/frontend/tests/e2e and .github/workflows.
---

# Frontend E2E

Use this skill for Playwright-based E2E work in this repository.

These tests run against a real DSS instance and a real semantic-models project surface. They are not local-only component tests and they are not Vitest browser tests.

## Core Principles

- Keep one spec file per page or user flow.
- Default to reusing the shared semantic model created for the test session.
- Separate DSS controls from plugin controls.
- Use stable `data-testid` selectors inside the plugin iframe.
- Make tests self-cleaning: semantic models created by the suite must be deleted automatically.
- Keep E2E assertions broad and durable. These are smoke and integration checks, not pixel-perfect UI specs.

## Current Layout

The current Playwright E2E layout is:

```text
resource/frontend/
  playwright.config.ts
  playwright.setup.ts
  tests/e2e/
    semantic-models-list.spec.ts
    entities.spec.ts
    entity-detail.spec.ts
    relationships.spec.ts
    cross-entity-metrics.spec.ts
    glossary.spec.ts
    golden-queries.spec.ts
    instructions.spec.ts
    indexing.spec.ts
    playground.spec.ts
    helpers/
      dss-controls.ts
      sm-controls.ts
```

## Ownership Split

Keep helper responsibilities split exactly like this unless there is a strong reason to change it:

### `tests/e2e/helpers/dss-controls.ts`

Owns DSS-side concerns:

- DSS URL construction
- project key resolution
- semantic-model list navigation
- semantic-model create and delete lifecycle
- editor URL opening
- DSS page assertions outside the plugin iframe

### `tests/e2e/helpers/sm-controls.ts`

Owns plugin-side concerns:

- locating the editor iframe
- page-root assertions inside the iframe
- sidebar navigation
- entity-detail navigation
- manual entity creation flow
- plugin-specific selectors and helper methods

Do not mix DSS list-page mechanics into `sm-controls.ts`.
Do not put iframe UI logic into `dss-controls.ts`.

## Shared Fixture Model

The suite is organized around `resource/frontend/playwright.setup.ts`.

Default fixtures:

- `dssUserRole`
  - worker-scoped option naming the DSS user the worker runs as
  - set per Playwright project in `playwright.config.ts`, defaults to `default`
- `workerStorageState`
  - logs into DSS once per worker, as the user resolved from `dssUserRole`
  - stores auth state under `resource/frontend/playwright/.auth/` (one file per role)
- `sharedSemanticModel`
  - creates one semantic model per worker
  - deletes it in teardown
- `semanticModels`
  - exposes DSS helper methods for a test
- `sharedEditor`
  - opens the shared semantic model editor for the current test

### Default Rule

Use `sharedEditor` unless the test truly needs its own semantic model.

That keeps:

- the suite faster
- the data model simpler
- the cleanup model centralized

### When To Use A Fresh Semantic Model

Only create a dedicated semantic model when a test:

- mutates state in a way that breaks later tests
- needs a very specific initial state
- needs to validate cleanup or creation logic itself

If you need isolation, call the DSS helper explicitly rather than expanding the shared fixture behavior for everyone.

## Spec Organization

Keep specs small and focused.

Preferred pattern:

- one spec file per routed page
- one spec file for a distinct flow such as manual entity creation
- shared behavior in helpers, not in giant multi-page spec files

Examples:

- `entities.spec.ts`: entities landing page smoke
- `entity-detail.spec.ts`: entity creation and detail reopening flow
- `playground.spec.ts`: playground page smoke
- `cross-entity-metrics.spec.ts`: feature-flagged page smoke

Do not rebuild the same setup in every spec.
Import from `../../playwright.setup`.

## Selector Strategy

Inside the plugin iframe, prefer `data-testid`.

Use `getByRole` or visible text only when:

- the control is truly a DSS-owned control outside the iframe
- the text itself is the behavior under test
- a durable `data-testid` does not make sense

Add `data-testid` for:

- page roots
- primary actions
- repeated cards or rows
- wizard steps
- dialogs or panels that tests must target directly

Examples already used by the suite:

- `entities-view`
- `entity-editor-view`
- `entities-add-button`
- `add-entity-wizard`
- `manual-select-datasets-step`
- `manual-dataset-row`

## Data Dependencies

These tests depend on real DSS state, so be explicit about what is created by the suite and what must exist in the target instance.

### Data Created By The Suite

- one shared semantic model per worker
- optionally additional semantic models if a test requests one
- entities created during plugin flows

The semantic model cleanup is handled by the Playwright fixtures and DSS helpers.

### Data Expected From The DSS Instance

- a reachable DSS instance
- a valid DSS project with semantic-models enabled
- a login-capable user
- at least one dataset if you test the manual entity flow

### Dataset Dependency

The manual flow depends on datasets being available in DSS.

Use:

- `E2E_MANUAL_DATASET_NAME`

when a test must select a specific dataset. If it is unset, the helper uses the first visible dataset row.

Do not hardcode dataset names in specs unless the test is intentionally tied to a named fixture.

### Feature-Flag Dependency

Some pages may not exist in every DSS instance.

For pages like cross-entity metrics:

- check whether the sidebar entry exists
- skip the test if the feature is disabled

Prefer conditional skip over making the whole suite instance-specific.

## Multi-User Runs

The suite can run the same specs as different DSS users.

- `tests/e2e/helpers/users.ts` owns role parsing and credential resolution.
- `E2E_USER_ROLES` (comma-separated, e.g. `admin,reader`) turns each role into its own Playwright project named `chromium-<role>`.
- Each role reads credentials from `E2E_<ROLE>_USERNAME` / `E2E_<ROLE>_PASSWORD`.
- When `E2E_USER_ROLES` is unset, there is a single `chromium` project using `E2E_USERNAME` / `E2E_PASSWORD`.
- Auth state is cached per role under `resource/frontend/playwright/.auth/`.

To restrict a spec to certain roles, tag the test with `@admin` (a Playwright
test tag) instead of forking it. `playwright.config.ts` applies
`grepInvert: /@admin/` to every role project except `admin` and the default
single-user role, so those tests are filtered out at collection time — they do
NOT show up as skipped for other roles, and a single-user run (no
`E2E_USER_ROLES`) still runs every spec.

```ts
test('authors entities…', { tag: '@admin' }, async ({ sharedEditor }) => {
  // …
})
```

Authoring flows that need write access (e.g. `full-spec-flow.spec.ts`,
`automatic-generation.spec.ts`) are tagged `@admin` this way.

To run only one user locally: `yarn test:e2e --project=chromium-reader`.

## Environment Variables

The current E2E setup uses:

- `E2E_DSS_ORIGIN`
- `E2E_DSS_PROJECT_KEY`
- `E2E_DSS_PROJECT_ID`
- `E2E_USERNAME`
- `E2E_PASSWORD`
- `E2E_USER_ROLES`
- `E2E_<ROLE>_USERNAME` / `E2E_<ROLE>_PASSWORD` per role in `E2E_USER_ROLES`
- `E2E_MANUAL_DATASET_NAME`

`playwright.config.ts` resolves the base URL to:

- `/projects/<projectKey>/semantic-models/`

Do not hardcode origins, project keys, or credentials in test files.

## Commands

Prefer repository-level commands:

- `make frontend-e2e-test`
- `make frontend-e2e-test-ui`
- `make format`

Frontend-local scripts are:

- `yarn test:e2e`
- `yarn test:e2e:ui`

## CI Structure

The E2E CI flow is split into:

- `.github/workflows/e2e-update-plugin.yml`
- `.github/workflows/e2e-playwright.yml`
- `.github/workflows/update-plugin-and-run-e2e.yml`

The coordinator workflow should:

1. update the plugin on the DSS instance
2. then run Playwright with `needs`

Keep plugin deployment and browser execution as separate reusable workflows.

## What To Avoid

- Do not put multiple unrelated page checks in one large spec file.
- Do not depend on pre-existing semantic models in the DSS instance.
- Do not bypass the shared fixture model unless isolation is genuinely needed.
- Do not mix DSS selectors and iframe selectors in the same helper layer.
- Do not use arbitrary sleeps as the primary synchronization strategy.
- Do not assert too much incidental copy or layout when a page-root smoke assertion is enough.
- Do not make E2E tests the place where LLM output quality is validated exactly.

## Preferred Decision Order

1. Is this a real DSS/browser flow rather than a Vitest browser test? Use Playwright E2E.
2. Is this checking a specific routed page? Add or update the page-specific `*.spec.ts`.
3. Is this a DSS-level concern? Put it in `dss-controls.ts`.
4. Is this a plugin iframe concern? Put it in `sm-controls.ts`.
5. Can the test reuse `sharedEditor`? Do that first.
6. Does the test need a stable target in the DOM? Add a `data-testid`.
7. Does the test require special DSS fixture data? Parameterize it with env vars instead of hardcoding it.

## Expected Outcome

Good E2E changes in this repository should result in:

- one clear spec per page or flow
- shared DSS login and semantic-model lifecycle
- durable selectors inside the plugin iframe
- minimal dependence on pre-existing data
- explicit env-driven DSS fixture requirements
- CI that updates the plugin before running browser tests
