RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Configs/AGENTS.md/grafana/grafana

AGENTS.md

e2e-playwright/dashboard-new-layouts/AGENTS.md
AGENTS.md

Quality

62/100

Scores the file, not the repository.

Length

1,777 words

12 headings · 5 code blocks

Repository

76k

— · pushed 0 days ago

Last changed

today

First indexed 3 days ago.
grafana/grafana/e2e-playwright/dashboard-new-layouts/AGENTS.mdRawGitHub
1# dashboard-new-layouts E2E Tests — Agent Guide
2 
3## Purpose
4 
5This suite contains Playwright E2E tests for the V2 dashboard layout system. Tests use **page objects** to wrap raw selector chains behind user-intent methods. The full rationale is in [`_page_objects_strategy.md`](./_page_objects_strategy.md).
6 
7## Page Objects Reference
8 
9All page objects live in `page-objects/`. Only the top-level ones (`Controls`, `Sidebar`, `Panels`, `Rows`, `Tabs`, `Canvas`) are re-exported from `page-objects/index.ts` — import those in specs. Sidebar panes (`Toolbar`, `AddOptions`, `DashboardOptions`, `PanelOptions`, `TabOptions`, `VariableOptions`, `ContentOutline`) and shared sub-options (`ConditionalRenderingOptions`, `RepeatOptions` under `sidebar/shared/`) are not exported; reach them via `sidebar.*` (e.g. `sidebar.toolbar`, `sidebar.panelOptions.repeatOptions`). Every page object extends the abstract `PageObject` base class (`PageObject.ts`), which holds the shared `page`, `dashboardPage`, `selectors`, and `components` dependencies as `protected` fields.
10 
11| Class | File | UI Region | Key Methods / Getters |
12| ----------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
13| `PageObject` | `PageObject.ts` | _(abstract base — not used directly)_ | Shared constructor taking a `PageObjectArgs` object (`page`, `dashboardPage`, `selectors`, `components`) |
14| `Controls` | `Controls.ts` | Top nav bar (edit, save, timepicker, share, ...) and variable submenu | `getContainer()`, `enterEditMode()`, `exitEditMode()`, `saveDashboard(title?)`, `clickBackToDashboard()`, `openControlsMenu()`, `openShareSnapshotDrawer()`; `timeRange` sub-object: `set(from, to)`, `selectPreset(presetLabel)`; `variables` sub-object: `getLabel(variableLabel)`, `getInput(variableLabel)`, `setValue(variableLabel, text)`, `getDropdownTrigger(variableLabel)`, `openDropdown(variableLabel)`, `getOption(optionLabel)`, `selectOption(variableLabel, optionLabel)`, `deselectOption(variableLabel, optionLabel)`, `addFilter(variableLabel, [label, operator, value])` |
15| `Sidebar` | `sidebar/Sidebar.ts` | Whole sidebar region (toolbar + open pane) | `.toolbar`, `.addOptions`, `.dashboardOptions`, `.panelOptions`, `.tabOptions`, `.variableOptions`, `.contentOutline` sub-objects; `getContainer()`, `clickGoBackButton()`, `getDockToggle()`, `clickCloseButton()`, `clickDeleteButton({ confirm? })` |
16| `Toolbar` | `sidebar/Toolbar.ts` | Icon strip — accessed via `sidebar.toolbar` | `getButton(name)`, `clickButton(name)`, `getVisibilityToggle()` |
17| `AddOptions` | `sidebar/AddOptions.ts` | "Add" pane (default pane on new dashboards) — via `sidebar.addOptions` | `clickNewPanelButton()`, `clickAddTabButton()`, `clickNewVariableButton()` |
18| `ContentOutline` | `sidebar/ContentOutline.ts` | Content outline pane — via `sidebar.contentOutline` | `getTree()`, `clickItem(name)`, `toggleNode(name)` |
19| `DashboardOptions` | `sidebar/DashboardOptions.ts` | Dashboard options pane — via `sidebar.dashboardOptions` | `getTitleInput()`, `getDescriptionTextarea()`, `switchLayout(layoutType, { confirm? })` |
20| `PanelOptions` | `sidebar/PanelOptions.ts` | Panel options pane — via `sidebar.panelOptions` | `.conditionalRenderingOptions`, `.repeatOptions`; `getTitleInput()`, `setTitle(title)`, `getDescriptionTextarea()`, `toggleTransparentBackground()` |
21| `TabOptions` | `sidebar/TabOptions.ts` | Tab options pane — via `sidebar.tabOptions` | `.conditionalRenderingOptions`, `.repeatOptions` |
22| `ConditionalRenderingOptions` | `sidebar/shared/ConditionalRenderingOptions.ts` | Shared conditional rendering rules — via `*.conditionalRenderingOptions` | `selectVisibility('show' \| 'hide')`, `selectMatch('all' \| 'any')`, `addVariableRule(name, operator, value)`, `addTimeRangeRule(lessThan)` |
23| `RepeatOptions` | `sidebar/shared/RepeatOptions.ts` | Shared repeat options — via `*.repeatOptions` | `repeatByVariable(variableName)`, `disableRepeatByVariable()` |
24| `VariableOptions` | `sidebar/VariableOptions.ts` | Variable sidebar — via `sidebar.variableOptions` | `selectVariableType(type)`, `setName(name)`, `setLabel(label)`, `selectDisplay(displayLabel)`; type-specific sub-objects: `datasource.selectType(dsType)`, `datasource.setNameFilter(filter)`, `custom.openEditor()`, `custom.selectFormat(format)`, `custom.setValues(values)`, `custom.getPreviewOfValues()`, `custom.getPreviewTable()`, `custom.clickApplyButton()`, `groupby.selectDatasource(ds)`, `adhoc.selectDatasource(ds)`, `query.openEditor()`, `query.selectTargetDatasource(ds)`, `query.setTestDataQuery(query)`, `query.runQuery()`, `query.getPreviewOfValues()`, `query.clickApplyButton()`, `constant.setValue(value)`, `textbox.setValue(value)`, `interval.toggleAuto()` |
25| `Panels` | `Panels.ts` | The dashboard panels in the edit canvas | `getPanels(title, scope?)`, `getPanel(title, scope?)`, `getHeaders(title?, scope?)` — string titles match exactly, RegExp filters by text, no argument returns all headers; `getHeader(title, scope?)`, `getBodies()`, `selectByTitle(title \| RegExp \| Array<title \| RegExp>)`, `selectByIndex(index)`, `selectMenuItem(panelTitle, menuPath[])` |
26| `Rows` | `Rows.ts` | A row of a rows layout in the dashboard canvas | `getTitle(rowTitle)`, `getContent(rowTitle)` — content wrapper (grid or nested tabs) right after the row header |
27| `Tabs` | `Tabs.ts` | Tab bar of a tabs layout (top-level or nested in a row) | `getTitle(tabTitle, scope?)` — pass `rows.getContent(rowTitle)` as `scope` to look up a tab inside a specific row; `getContent(tabTitle)` — the layout container holding the tab's content; `select(tabTitle)` — click the tab title |
28| `Canvas` | `Canvas.ts` | Edit canvas add-actions strip (per grid, revealed on hover) | `getContainer()`, `addPanel(panelsContainer?)`, `addTab()`, `addRow()`, `groupPanels('row' \| 'tab', panelsContainer?)` — pass `panelsContainer` (e.g. `tabs.getContent(...)`, `rows.getContent(...)`) to target the add-actions strip of a nested grid |
29 
30> The show/hide visibility toggle is a **Toolbar** control (`sidebar.toolbar.getVisibilityToggle()`), even though its selector lives under `components.Sidebar.*`. `Toolbar.getButton(name)` resolves buttons by accessible name, scoped to the sidebar container.
31 
32> This table grows as specs are migrated — only methods needed by migrated specs exist.
33 
34### Base class & constructor
35 
36All page objects inherit from `PageObject`, which provides the shared constructor. It takes a single `PageObjectArgs` object:
37 
38```typescript
39// page-objects/PageObject.ts
40export interface PageObjectArgs {
41 page: Page;
42 dashboardPage: DashboardPage;
43 selectors: E2ESelectorGroups;
44 components: Components;
45}
46 
47export abstract class PageObject {
48 constructor({ page, dashboardPage, selectors, components }: PageObjectArgs) {
49 // assigned to protected fields
50 }
51}
52```
53 
54Simple page objects (e.g. `Controls`, `Toolbar`) inherit the constructor directly — no override needed. Page objects that compose sub-objects (e.g. `Sidebar`) declare `constructor(args: PageObjectArgs)`, call `super(args)`, and pass the same `args` to their children.
55 
56All four dependencies come from the Playwright test arguments:
57 
58```typescript
59test('example', async ({ gotoDashboardPage, selectors, page, components }) => {
60 const dashboardPage = await gotoDashboardPage({ uid: 'some-uid' });
61 const controls = new Controls({ page, dashboardPage, selectors, components });
62 // ...
63});
64```
65 
66## How to Write a New Test
67 
681. **Identify which page objects you need.** Check the table above. If the interaction you need isn't covered, add the method to the appropriate page object — only what the new test requires.
69 
702. **Scaffold the spec** following this structure:
71 
72```typescript
73import { test, expect } from '@grafana/plugin-e2e';
74 
75import { Controls, Sidebar } from './page-objects';
76 
77test.describe(
78 'Feature name',
79 {
80 tag: ['@dashboards'],
81 },
82 () => {
83 test('describes the user-visible behavior', async ({ gotoDashboardPage, selectors, page, components }) => {
84 const dashboardPage = await gotoDashboardPage({ uid: 'dashboard-uid' });
85 
86 const controls = new Controls({ page, dashboardPage, selectors, components });
87 const sidebar = new Sidebar({ page, dashboardPage, selectors, components });
88 
89 await controls.enterEditMode();
90 // ... test body using page objects (the toolbar is reached via sidebar.toolbar)
91 });
92 }
93);
94```
95 
961. **Verify locally:**
97 
98```bash
99yarn e2e:pw --project dashboard-new-layouts --reporter list --repeat-each=3 -- &lt;spec-filename&gt;
100```
101 
102## Conventions
103 
104### Page objects
105 
106- **Locator getters** (e.g. `getTitleInput()`) return a Playwright `Locator` — for elements that specs assert on (or both act on and assert on). The test owns the assertion — never the page object.
107- **Action methods** (e.g. `enterEditMode()`, `clickCloseButton()`) wrap interactions — multi-step flows or single clicks on act-only elements — and use `test.step()` so the HTML report shows named steps.
108- **When a spec needs both**, pair them: the action method delegates to the getter (see `Toolbar.getButton()` / `clickButton()`).
109- **No speculative methods.** Only add methods needed by the spec being migrated.
110- **Plural vs singular getters** (e.g. `getPanels()` / `getPanel()`): plural getters return every match — assert counts or narrow (`.first()`, `.nth()`) in the spec; singular getters return the first match.
111- **Scoped lookups**: getters with a `scope?: Locator` parameter search inside that container — pass `rows.getContent(...)` or `tabs.getContent(...)` to look up elements in a specific row or tab.
112- **No waits or retries inside page objects** unless the pre-refactor code had them. Keep `toPass()` retries, drag-and-drop, scroll logic, and `boundingBox()` in the spec or in `utils.ts`.
113 
114### Selector scoping
115 
116- **Scope lookups to the owning container.** A bare `page.getByRole(...)` searches the whole page and can match an unrelated element with the same role and name — if not today, then after an unrelated UI change. Elements that belong to a region with a page object must be looked up through that region's container: e.g. radio buttons in the sidebar are `sidebar.getContainer().getByRole('radio', { name: '...' })`, the same way `Toolbar.getButton()` scopes button names to `Sidebar.container`. Inside a page object, chain from the container selector (`this.dashboardPage.getByGrafanaSelector(this.selectors.components.Sidebar.container).getByRole(...)`).
117- **Portalled UI is the only exception.** Select/Combobox option lists, modals, tooltips, and toasts render in a portal at the document root, outside their logical parent, so they cannot be scoped to it. Anchor them to the portal's own root instead: `page.getByRole('listbox').getByRole('option', { name })` (see `RepeatOptions`), or `page.getByRole('dialog', { name: 'Delete panel?' })`. A bare `page.getByRole('option', ...)` with no anchor is still too broad.
118 
119### Specs
120 
121- **One raw `getByGrafanaSelector` is allowed** for one-off assertions that aren't reusable interactions (e.g. a breadcrumb check).
122- **Timing-sensitive mechanics stay inline** — `toPass()`, `mouse` sequences, `page.evaluate()`.
123- **Test setup stays in the spec** — API calls, dashboard provisioning, navigation via `gotoDashboardPage()`.
124- **Each spec is fully migrated or untouched.** No file should mix page-object calls and raw selectors for the same UI region.
125 
126### Adding a method to a page object
127 
1281. Find the raw selector chain in the spec you're migrating.
1292. Copy it into the appropriate page object class — mechanical extraction, no rewrites. New page objects must extend `PageObject` from `PageObject.ts`.
1303. For interactions (multi-step flows or single clicks on act-only elements), wrap in `test.step('Human-readable name', async () => { ... })`.
1314. For elements the spec asserts on, return a `Locator` (getter pattern, no `test.step` needed).
1325. Run `--repeat-each=3` on the migrated spec.
133 
134## Canonical Example
135 
136`dashboards-title-description.spec.ts` — the seed spec demonstrating the full pattern:
137 
138```typescript
139await controls.enterEditMode();
140await sidebar.toolbar.clickButton('Options');
141 
142const titleInput = sidebar.dashboardOptions.getTitleInput();
143await expect(titleInput).toHaveValue('Annotation filtering');
144 
145const newTitle = 'New dashboard title';
146await titleInput.fill(newTitle);
147await expect(titleInput).toHaveValue(newTitle);
148```
149 
150## Migration Status
151 
152**26 of 30 specs migrated.** Non-migrated specs are listed first by descending selectors usage count (a rough proxy for migration effort). "Selectors usage count" is the number of times the spec accesses the `selectors` object (`selectors.components...`, `selectors.pages...`, etc.).
153 
154| Spec | Status | Lines of code | Selectors usage count |
155| ----------------------------------------------------- | ----------- | ------------- | --------------------- |
156| `dashboard-group-panels.spec.ts` | Not started | 918 | 224 |
157| `dashboards-repeats-tabs-layout.spec.ts` | Not started | 482 | 73 |
158| `dashboards-panel-layouts.spec.ts` | Not started | 425 | 70 |
159| `dashboard-repeats-row-layout.spec.ts` | Not started | 551 | 61 |
160| `dashboards-repeats-custom-grid.spec.ts` | Migrated | — | — |
161| `dashboards-repeats-auto-grid.spec.ts` | Migrated | — | — |
162| `dashboards-title-description.spec.ts` | Migrated | — | — |
163| `dashboards-edit-panel-title-description.spec.ts` | Migrated | — | — |
164| `dashboards-edit-panel-transparent-bg.spec.ts` | Migrated | — | — |
165| `dashboard-mobile-sidebar.spec.ts` | Migrated | — | — |
166| `dashboard-hide-sidebar.spec.ts` | Migrated | — | — |
167| `dashboards-remove-panel.spec.ts` | Migrated | — | — |
168| `dashboard-duplicate-panel.spec.ts` | Migrated | — | — |
169| `dashboard-sidepane.spec.ts` | Migrated | — | — |
170| `dashboard-outline.spec.ts` | Migrated | — | — |
171| `dashboards-conditional-rendering.spec.ts` | Migrated | — | — |
172| `dashboards-add-panel.spec.ts` | Migrated | — | — |
173| `dashboards-edit-variables.spec.ts` | Migrated | — | — |
174| `dashboard-tabs-scroll.spec.ts` | Migrated | — | — |
175| `dashboards-repeats-snapshots.spec.ts` | Migrated | — | — |
176| `dashboards-move-panel.spec.ts` | Migrated | — | — |
177| `dashboard-conditional-rendering-load-change.spec.ts` | Migrated | — | — |
178| `dashboards-edit-custom-variables.spec.ts` | Migrated | — | — |
179| `dashboards-edit-query-variables.spec.ts` | Migrated | — | — |
180| `dashboard-keybindings.spec.ts` | Migrated | — | — |
181| `dashboards-edit-adhoc-variables.spec.ts` | Migrated | — | — |
182| `dashboards-edit-group-by-variables.spec.ts` | Migrated | — | — |
183| `dashboards-edit-datasource-variables.spec.ts` | Migrated | — | — |
184| `dashboard-url-syncing.spec.ts` | Migrated | — | — |
185| `dashboard-tabs-drag-drop.spec.ts` | Migrated | — | — |
186 
187See [`_page_objects_strategy.md`](./_page_objects_strategy.md) for the full migration plan.
188 

Commands it names

  • yarn e2e:pw --project dashboard-new-layouts --reporter list --repeat-each=3 -- <spec-filename>

Sections

  • dashboard-new-layouts E2E Tests — Agent Guide
  • Purpose
  • Page Objects Reference
  • Base class & constructor
  • How to Write a New Test
  • Conventions
  • Page objects
  • Selector scoping
  • Specs
  • Adding a method to a page object
  • Canonical Example
  • Migration Status

What it covers

testcode-styletesting-strategydatabaseagent-behaviour

Stack — with the evidence

typescript

(1.00)

go

(1.00)

playwright

(0.85)

javascript

(0.60)

node

(0.60)

nx

(0.60)

monorepo

(0.60)

jest

(0.60)

eslint

(0.60)

docker

(0.60)

github-actions

(0.60)

Format

AGENTS.md

A plain-markdown README for coding agents, deliberately unopinionated: no frontmatter, no globs, no vendor keys. That minimalism is why it became the one file a dozen different agents will read, and why it carries the least per-file targeting power of any format here.

What the corpus says about it

Repository

Owner
grafana
Language
—
License
—
Archived
no

All configs in this repo

Also in grafana/grafana

Diff this repo’s formats

One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?

The other instruction files in this repository
RepositoryFormatStackCoversScoreChanged
grafana/grafanaAGENTS.md · 76kAGENTS.mdtypescriptgo+9setupbuildtestlint-format+689/1003 days ago
grafana/grafanae2e-playwright/alerting-suite/AGENTS.md · 76kAGENTS.mdtypescriptgo+9teststylearchtesting-strategy+381/1003 days ago
grafana/grafanae2e-playwright/plugin-e2e/plugin-e2e-api-tests/AGENTS.md · 76kAGENTS.mdtypescriptgo+9teststyletesting-strategygit+470/1003 days ago
grafana/grafanapackages/grafana-ui/AGENTS.md · 76kAGENTS.mdtypescriptgo+9uiagent-behaviour16/1003 days ago
grafana/grafanapkg/storage/unified/AGENTS.md · 76kAGENTS.mdtypescriptgo+9do-not46/1003 days ago
grafana/grafanapublic/app/core/journeys/AGENTS.md · 76kAGENTS.mdtypescriptgo+9testtesting-strategygitagent-behaviour73/1003 days ago
grafana/grafanapublic/app/features/AGENTS.md · 76kAGENTS.mdtypescriptgo+9agent-behaviour16/1003 days ago
grafana/grafanapublic/app/features/alerting/unified/AGENTS.md · 76kAGENTS.mdtypescriptgo+9setuptestlint-formatstyle+1176/1003 days ago
grafana/grafanapublic/app/features/expressions/components/SqlExpressions/SqlEditor/AGENTS.md · 76kAGENTS.mdtypescriptgo+9styleagent-behaviour43/1003 days ago
grafana/grafanapublic/app/plugins/panel/AGENTS.md · 76kAGENTS.mdtypescriptgo+9agent-behaviour16/1003 days ago
Diff against AGENTS.md Diff against e2e-playwright/alerting-suite/AGENTS.md Diff against e2e-playwright/plugin-e2e/plugin-e2e-api-tests/AGENTS.md Diff against packages/grafana-ui/AGENTS.md Diff against pkg/storage/unified/AGENTS.md Diff against public/app/core/journeys/AGENTS.md Diff against public/app/features/AGENTS.md Diff against public/app/features/alerting/unified/AGENTS.md Diff against public/app/features/expressions/components/SqlExpressions/SqlEditor/AGENTS.md Diff against public/app/plugins/panel/AGENTS.md

Similar configs

Same format, overlapping stack, ranked by quality.

Same format, overlapping stack, ranked by quality
RepositoryFormatStackCoversScoreChanged
code-yeongyu/oh-my-openagentpackages/web/AGENTS.md · 67kAGENTS.mdtypescriptbun+10setupbuildtestlint-format+6100/1002 days ago
TryGhost/Ghoste2e/AGENTS.md · 55kAGENTS.mdtypescriptjavascript+12setupteststylearch+2100/1003 days ago
mui/material-uiAGENTS.md · 99kAGENTS.mdtypescriptjavascript+13setupbuildtestlint-format+9100/1003 days ago
n8n-io/n8npackages/@n8n/agents/AGENTS.md · 199kAGENTS.mdtypescriptlangchain+16buildteststylearch+3100/1003 days ago
duckduckgo/content-scope-scriptsspecial-pages/AGENTS.md · 70AGENTS.mdtypescriptjavascript+5buildteststylearch+3100/1003 days ago
aaif-goose/gooseAGENTS.md · 52kAGENTS.mdrusttypescript+2setupbuildtestlint-format+6100/1003 days ago
trick77/agents-md-syncAGENTS.md · 2AGENTS.mdtypescriptnode+4setupbuildteststyle+5100/1003 days ago
ethereum/go-ethereumAGENTS.md · 51kAGENTS.mdgodocker+1buildtestlint-formatgit+1100/1003 days ago
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack