@grafana/plugin-e2e 3.5.1 → 3.6.0-canary.2586.24780926778.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -193,6 +193,17 @@ declare class Panel extends GrafanaPage {
193
193
  clickOnMenuItem(item: string, options?: {
194
194
  parentItem?: string;
195
195
  }): Promise<void>;
196
+ /**
197
+ * Scrolls the panel into the viewport, triggering its query if not yet started.
198
+ *
199
+ * In Grafana 13.x+ with scenes, panels are lazy-rendered: the panel element does not
200
+ * exist in the DOM until its grid container enters the viewport. This method scrolls
201
+ * `.react-grid-item` containers progressively until the panel element appears, then
202
+ * scrolls the panel element precisely into view. In older Grafana versions the panel
203
+ * element is always in the DOM, so the loop exits early and delegates to the standard
204
+ * scroll path.
205
+ */
206
+ scrollIntoView(): Promise<void>;
196
207
  /**
197
208
  * Returns the locator for the panel error (if any)
198
209
  */
@@ -342,11 +353,31 @@ declare class DashboardPage extends GrafanaPage {
342
353
  readonly dashboard?: DashboardPageArgs | undefined;
343
354
  dataSourcePicker: any;
344
355
  timeRange: TimeRange;
356
+ private pendingQueryCount;
357
+ private hasSeenQuery;
345
358
  constructor(ctx: PluginTestCtx, dashboard?: DashboardPageArgs | undefined);
346
359
  /**
347
360
  * Navigates to the dashboard page. If a dashboard uid was not provided, it's assumed that it's a new dashboard.
348
361
  */
349
362
  goto(options?: NavigateOptions): Promise<void>;
363
+ /**
364
+ * Scrolls each panel container into view so that below-fold panels have their queries triggered.
365
+ * Targets .react-grid-item containers (always in the DOM, even before VizPanel renders) rather
366
+ * than panel title/content elements which only exist after lazy rendering. Playwright's native
367
+ * scrollIntoViewIfNeeded finds the correct scroll container automatically across Grafana versions.
368
+ */
369
+ private scrollToRevealAllPanels;
370
+ /**
371
+ * Waits until all initiated panel queries have received responses.
372
+ *
373
+ * By default only waits for queries already triggered (panels in the viewport at navigation
374
+ * time or explicitly scrolled into view). Pass `scrollAll: true` to first scroll the full
375
+ * dashboard so that below-fold panels are also triggered before waiting.
376
+ */
377
+ waitForPanelsQueriesToComplete({ timeout, scrollAll, }?: {
378
+ timeout?: number;
379
+ scrollAll?: boolean;
380
+ }): Promise<void>;
350
381
  /**
351
382
  * Navigates to the panel edit page for the given panel id
352
383
  *
@@ -1384,6 +1415,10 @@ declare const expect: _playwright_test.Expect<{
1384
1415
  toHaveChecked: typeof toHaveChecked;
1385
1416
  toHaveColor: typeof toHaveColor;
1386
1417
  toHaveNoA11yViolations: typeof toHaveNoA11yViolations;
1418
+ toHavePanelErrors: (dashboard: DashboardPage, expectedCount?: number) => Promise<{
1419
+ pass: boolean;
1420
+ message: () => string;
1421
+ }>;
1387
1422
  }>;
1388
1423
 
1389
1424
  declare global {
@@ -1456,6 +1491,12 @@ declare global {
1456
1491
  * You can use this in conjunction with the .toHaveNoA11yViolations matcher to assert that there are no accessibility violations on the page.
1457
1492
  */
1458
1493
  toHaveNoA11yViolations(results: AxeResults, options?: A11yViolationsOptions): Promise<R>;
1494
+ /**
1495
+ * Asserts that a dashboard has panel errors. Omit `count` to assert at least one error.
1496
+ * Pass `count` to assert exactly that many panels with errors.
1497
+ * Use `.not.toHavePanelErrors()` to assert no panel errors.
1498
+ */
1499
+ toHavePanelErrors(this: Matchers<unknown, DashboardPage>, count?: number): Promise<R>;
1459
1500
  }
1460
1501
  }
1461
1502
  }
package/dist/index.js CHANGED
@@ -42,6 +42,7 @@ var toBeChecked = require('./matchers/toBeChecked.js');
42
42
  var toHaveNoA11yViolations = require('./matchers/toHaveNoA11yViolations.js');
43
43
  var toHaveChecked = require('./matchers/toHaveChecked.js');
44
44
  var toHaveColor = require('./matchers/toHaveColor.js');
45
+ var toHavePanelErrors = require('./matchers/toHavePanelErrors.js');
45
46
  var DataSourcePicker = require('./models/components/DataSourcePicker.js');
46
47
  var Panel = require('./models/components/Panel.js');
47
48
  var TimeRange = require('./models/components/TimeRange.js');
@@ -104,7 +105,8 @@ const expect = test$1.expect.extend({
104
105
  toBeChecked: toBeChecked.toBeChecked,
105
106
  toHaveChecked: toHaveChecked.toHaveChecked,
106
107
  toHaveColor: toHaveColor.toHaveColor,
107
- toHaveNoA11yViolations: toHaveNoA11yViolations.toHaveNoA11yViolations
108
+ toHaveNoA11yViolations: toHaveNoA11yViolations.toHaveNoA11yViolations,
109
+ toHavePanelErrors: toHavePanelErrors.toHavePanelErrors
108
110
  });
109
111
 
110
112
  Object.defineProperty(exports, "selectors", {
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ const toHavePanelErrors = async (dashboard, expectedCount) => {
4
+ const errorLocator = dashboard.getByGrafanaSelector(dashboard.ctx.selectors.components.Panels.Panel.status("error"));
5
+ const count = await errorLocator.count();
6
+ const pass = expectedCount === void 0 ? count >= 1 : count === expectedCount;
7
+ return {
8
+ pass,
9
+ message: () => expectedCount === void 0 ? `Expected at least 1 panel with errors but found ${count}` : `Expected exactly ${expectedCount} panel error(s) but found ${count}`
10
+ };
11
+ };
12
+
13
+ exports.toHavePanelErrors = toHavePanelErrors;
@@ -75,6 +75,31 @@ class Panel extends GrafanaPage.GrafanaPage {
75
75
  options?.parentItem && await parentMenuItem.hover();
76
76
  await menuItem.click();
77
77
  }
78
+ /**
79
+ * Scrolls the panel into the viewport, triggering its query if not yet started.
80
+ *
81
+ * In Grafana 13.x+ with scenes, panels are lazy-rendered: the panel element does not
82
+ * exist in the DOM until its grid container enters the viewport. This method scrolls
83
+ * `.react-grid-item` containers progressively until the panel element appears, then
84
+ * scrolls the panel element precisely into view. In older Grafana versions the panel
85
+ * element is always in the DOM, so the loop exits early and delegates to the standard
86
+ * scroll path.
87
+ */
88
+ async scrollIntoView() {
89
+ if (await this.locator.isVisible({ timeout: 500 }).catch(() => false)) {
90
+ await this.locator.scrollIntoViewIfNeeded();
91
+ return;
92
+ }
93
+ const containers = this.ctx.page.locator(".react-grid-item");
94
+ const count = await containers.count();
95
+ for (let i = 0; i < count; i++) {
96
+ await containers.nth(i).scrollIntoViewIfNeeded();
97
+ if (await this.locator.isVisible({ timeout: 500 }).catch(() => false)) {
98
+ break;
99
+ }
100
+ }
101
+ await this.locator.scrollIntoViewIfNeeded();
102
+ }
78
103
  /**
79
104
  * Returns the locator for the panel error (if any)
80
105
  */
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var semver = require('semver');
4
+ var test = require('@playwright/test');
4
5
  var DataSourcePicker = require('../components/DataSourcePicker.js');
5
6
  var GrafanaPage = require('./GrafanaPage.js');
6
7
  var PanelEditPage = require('./PanelEditPage.js');
@@ -38,6 +39,8 @@ class DashboardPage extends GrafanaPage.GrafanaPage {
38
39
  this.dashboard = dashboard;
39
40
  __publicField(this, "dataSourcePicker");
40
41
  __publicField(this, "timeRange");
42
+ __publicField(this, "pendingQueryCount", 0);
43
+ __publicField(this, "hasSeenQuery", false);
41
44
  this.dataSourcePicker = new DataSourcePicker.DataSourcePicker(ctx);
42
45
  this.timeRange = new TimeRange.TimeRange(ctx);
43
46
  }
@@ -45,6 +48,17 @@ class DashboardPage extends GrafanaPage.GrafanaPage {
45
48
  * Navigates to the dashboard page. If a dashboard uid was not provided, it's assumed that it's a new dashboard.
46
49
  */
47
50
  async goto(options = {}) {
51
+ this.ctx.page.on("request", (req) => {
52
+ if (req.url().includes(this.ctx.selectors.apis.DataSource.query)) {
53
+ this.pendingQueryCount++;
54
+ this.hasSeenQuery = true;
55
+ }
56
+ });
57
+ this.ctx.page.on("response", (res) => {
58
+ if (res.url().includes(this.ctx.selectors.apis.DataSource.query)) {
59
+ this.pendingQueryCount = Math.max(0, this.pendingQueryCount - 1);
60
+ }
61
+ });
48
62
  let url = this.dashboard?.uid ? this.ctx.selectors.pages.Dashboard.url(this.dashboard.uid) : this.ctx.selectors.pages.AddDashboard.url;
49
63
  if (this.dashboard?.timeRange) {
50
64
  options.queryParams = options?.queryParams ?? new URLSearchParams();
@@ -53,6 +67,39 @@ class DashboardPage extends GrafanaPage.GrafanaPage {
53
67
  }
54
68
  return super.navigate(url, options);
55
69
  }
70
+ /**
71
+ * Scrolls each panel container into view so that below-fold panels have their queries triggered.
72
+ * Targets .react-grid-item containers (always in the DOM, even before VizPanel renders) rather
73
+ * than panel title/content elements which only exist after lazy rendering. Playwright's native
74
+ * scrollIntoViewIfNeeded finds the correct scroll container automatically across Grafana versions.
75
+ */
76
+ async scrollToRevealAllPanels() {
77
+ const containers = this.ctx.page.locator(".react-grid-item");
78
+ const count = await containers.count();
79
+ for (let i = 0; i < count; i++) {
80
+ await containers.nth(i).scrollIntoViewIfNeeded();
81
+ await this.ctx.page.waitForTimeout(500);
82
+ }
83
+ }
84
+ /**
85
+ * Waits until all initiated panel queries have received responses.
86
+ *
87
+ * By default only waits for queries already triggered (panels in the viewport at navigation
88
+ * time or explicitly scrolled into view). Pass `scrollAll: true` to first scroll the full
89
+ * dashboard so that below-fold panels are also triggered before waiting.
90
+ */
91
+ async waitForPanelsQueriesToComplete({
92
+ timeout = 3e4,
93
+ scrollAll = false
94
+ } = {}) {
95
+ await test.expect.poll(() => this.hasSeenQuery, { timeout: 2e3 }).toBe(true).catch(() => {
96
+ });
97
+ await test.expect.poll(() => this.pendingQueryCount === 0, { timeout }).toBe(true);
98
+ if (scrollAll) {
99
+ await this.scrollToRevealAllPanels();
100
+ await test.expect.poll(() => this.pendingQueryCount === 0, { timeout }).toBe(true);
101
+ }
102
+ }
56
103
  /**
57
104
  * Navigates to the panel edit page for the given panel id
58
105
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grafana/plugin-e2e",
3
- "version": "3.5.1",
3
+ "version": "3.6.0-canary.2586.24780926778.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -54,5 +54,5 @@
54
54
  "uuid": "^13.0.0",
55
55
  "yaml": "^2.3.4"
56
56
  },
57
- "gitHead": "bbe7431074aa667f5da9880f76fba803ba1b3f73"
57
+ "gitHead": "e6e583d77ad8805571c805d035d49d06c6b2f823"
58
58
  }