Skip to content
Testing
Skill

/add-e2e-selectors

Add reliable @grafana/e2e-selectors to interactive elements and key containers in the Grafana frontend. Use when adding e2e selectors, data-testid attributes, or test selectors to React components, when a file or component lacks selectors for testing, or when asked to make

From plugin
grafana
76k2 skills
Install
$ npx -y skills add grafana/grafana --skill add-e2e-selectors --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/add-e2e-selectors

Context preview

The summary Claude sees to decide when to auto-load this skill.

Add reliable @grafana/e2e-selectors to interactive elements and key containers in the Grafana frontend. Use when adding e2e selectors, data-testid attributes, or test selectors to React components, when a file or component lacks selectors for testing, or when asked to make

SKILL.md

add-e2e-selectors.SKILL.md
name: add-e2e-selectors
description: Add reliable @grafana/e2e-selectors to interactive elements and key containers in the Grafana frontend. Use when adding e2e selectors, data-testid attributes, or test selectors to React components, when a file or component lacks selectors for testing, or when asked to make elements testable. Also use when given a Grafana Pathfinder interactive guide (a guide directory or content.json) to audit or fix — it extracts the guide's reftarget selectors, identifies weak ones, and fixes them at the source in Grafana's JSX. Defines versioned selectors in the e2e-selectors package and wires data-testid into JSX. Accepts a file path, a list of targets, a directory, a pathfinder guide, or the current/open file.

Add e2e-selectors

Find interactive elements and key containers in the Grafana frontend that lack a stable test selector, define a **versioned** selector in the `@grafana/e2e-selectors` package, and wire it into the JSX as `data-testid`. This encodes the package's layout (`pages` vs `components`), its semver versioning scheme, and its strict reuse / never-delete rules so selectors are added correctly and don't break plugin end-to-end tests.

Resolve the target

Interpret the argument to decide scope:

  • **A file path** → only that file.
  • **A list of files or described elements** → each named target, one by one.
  • **A directory** → all `.tsx` files under it.
  • **A Grafana Pathfinder interactive guide** — a guide directory or its `content.json` (e.g.

under `grafana-pathfinder-app/src/bundled-interactives/`) → see "Pathfinder guide as target".

  • **"current file" / "open file" / no path but a file is open** → the open file.
  • **No argument and no open file** → ask for a target; never scan the whole frontend.

Pathfinder guide as target

Interactive guides drive Grafana's UI through CSS selectors (`reftarget` fields), so a weak selector breaks a guide the same way it breaks a test. Process the guide, then fix the weak targets at the source:

1. Extract targets: `grep -o '"reftarget": *"[^"]*"' <guide>/content.json` 2. Skip navigation targets — blocks with `"action": "navigate"` or URL-shaped values (starting with `/` or `http`). 3. Classify the rest. **Strong** (leave alone): `data-testid`/`data-cy`-based selectors and `grafana:` / `{grafana:...}` tokens. **Weak** (fix): text matching (`:contains`, `:text`), `aria-label` / `placeholder` / `title` attributes, `href`, bare-id compounds, and positional selectors (`:nth-of-type`, `:nth-child`, `:nth-match`). 4. Locate the JSX in this repo rendering each weak target (search by id, testid fragment, button text, aria-label). Some targets are not fixable here — external npm packages (`@grafana/plugin-ui`, `@grafana/prometheus`), Monaco editor internals, instance data (dashboard titles, datasource names) — report those instead of forcing a change. 5. Run Steps 1–6 below for the located elements. Updating the guide itself is out of scope — include a weak-selector → new-selector mapping in your summary so the guide author can adopt them.

Step 1 — Find the version key

All new selectors added in this run use a single version key: the current `main` version with `-pre` and build tags stripped.

grep -m1 '"version"' package.json

`13.2.0-pre` → use `'13.2.0'`. On a release branch, read main's copy instead (`git show main:package.json | grep -m1 '"version"'` — the local `main` ref can be stale, so fetch first if in doubt). If you know the change will be backported, use the lowest release version instead. Never hardcode — always compute it.

Step 2 — Identify elements

Target these in the file:

  • **Interactive controls** a test would click or type into: `button`, `input`, `select`,

`textarea`, `a`, links, toggles/switches, checkboxes, radios, menu items, tabs, and the grafana-ui components that render them (`Button`, `IconButton`, `Input`, `Select`, `Switch`, `Checkbox`, `Tab`, etc.).

  • **Key containers** tests scope queries to: modals, panels, page sections, dialogs.

Skip any element that already has a `data-testid` or an existing selector — don't duplicate.

**Exception — a static testid on a repeated item.** A hardcoded literal inside a `.map()` (every card rendering the same `data-testid="data-source-card"`) identifies nothing: consumers are forced into text matching or positional hacks to pick one item, so it's as weak as no selector at all. Migrate it:

1. Define a parameterized selector keyed by a stable per-item value. 2. Preserve the legacy literal as the `MIN_GRAFANA_VERSION` entry so version-resolved consumers keep working against older Grafana, keeping the signature compatible:

   dataSourceCard: {
     '13.2.0': (name: string) => `data-testid data source card ${name}`,
     [MIN_GRAFANA_VERSION]: (_name: string) => 'data-source-card',
   },

3. Update every in-repo usage of the old literal (jest and Playwright — grep `public/` and `e2e-playwright/`), and note in your summary that external code hardcoding the literal will need the same one-line update.

Loop rule (important)

Inside a `.map()` / list render, **do not** put a selector on each inner interactive element — that bloats the DOM and degrades render performance. Instead attach **one parameterized container selector to the repeated row/wrapper**, keyed by a unique value. Tests then scope their queries within the matched row.

Canonical example — `public/app/features/browse-dashboards/components/DashboardsTree.tsx`:

<div
  key={key}
  {...rowProps}
  data-testid={selectors.pages.BrowseDashboards.table.row(
    'title' in dashboardItem ? dashboardItem.title : dashboardItem.uid
  )}
>
  {row.cells.map((cell) => /* inner cells get NO individual selector */)}
</div>

Step 3 — Reuse check (never duplicate)

Before defining anything, search the package for an existing selector covering this UI:

grep -rn "<keyword>" packages/grafan
Read more
Ships withgrafana

The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.

Get the whole plugin
Stats
76,176
Stars
14,535
Forks
Active
Maintenance
TypeScript
Language
AGPL-3.0
License
23m ago
Last commit
12y ago
Created

Repo: grafana/grafana