@grafana/plugin-e2e 1.3.2 → 1.4.0-canary.779.878465c.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.
@@ -4,6 +4,9 @@ export type E2ESelectors = {
4
4
  apis: APIs;
5
5
  };
6
6
  export type APIs = {
7
+ Alerting: {
8
+ eval: string;
9
+ };
7
10
  DataSource: {
8
11
  resourcePattern: string;
9
12
  resourceUIDPattern: string;
@@ -78,11 +81,27 @@ export type Components = {
78
81
  RefreshPicker: {
79
82
  runButtonV2: string;
80
83
  };
84
+ QueryTab: {
85
+ addQuery: string;
86
+ addExpression: string;
87
+ };
81
88
  QueryEditorRows: {
82
89
  rows: string;
83
90
  };
84
91
  QueryEditorRow: {
85
92
  title: (refId: string) => string;
93
+ actionButton: (title: string) => string;
94
+ };
95
+ AlertRules: {
96
+ previewButton: string;
97
+ ruleNameField: string;
98
+ newFolderButton: string;
99
+ newFolderNameCreateButton: string;
100
+ newEvaluationGroupButton: string;
101
+ newFolderNameField: string;
102
+ newEvaluationGroupName: string;
103
+ newEvaluationGroupInterval: string;
104
+ newEvaluationGroupCreate: string;
86
105
  };
87
106
  Alert: {
88
107
  alertV2: (severity: string) => string;
@@ -130,6 +149,14 @@ export type Pages = {
130
149
  Home: {
131
150
  url: string;
132
151
  };
152
+ Alerting: {
153
+ AddAlertRule: {
154
+ url: string;
155
+ };
156
+ EditAlertRule: {
157
+ url: (alertRuleUid: string) => string;
158
+ };
159
+ };
133
160
  DataSource: {
134
161
  name: string;
135
162
  delete: string;
@@ -1,4 +1,9 @@
1
1
  export declare const versionedAPIs: {
2
+ Alerting: {
3
+ eval: {
4
+ "8.0.0": string;
5
+ };
6
+ };
2
7
  DataSource: {
3
8
  resourcePattern: {
4
9
  "8.0.0": string;
@@ -3,6 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.versionedAPIs = void 0;
4
4
  const constants_1 = require("./constants");
5
5
  exports.versionedAPIs = {
6
+ Alerting: {
7
+ eval: {
8
+ [constants_1.MIN_GRAFANA_VERSION]: '/api/v1/eval',
9
+ },
10
+ },
6
11
  DataSource: {
7
12
  resourcePattern: {
8
13
  [constants_1.MIN_GRAFANA_VERSION]: '/api/datasources/*/resources',
@@ -128,6 +128,16 @@ export declare const versionedComponents: {
128
128
  "8.0.0": string;
129
129
  };
130
130
  };
131
+ QueryTab: {
132
+ addQuery: {
133
+ '10.2.0': string;
134
+ "8.0.0": string;
135
+ };
136
+ addExpression: {
137
+ '11.0.0': string;
138
+ '9.5.2': string;
139
+ };
140
+ };
131
141
  QueryEditorRows: {
132
142
  rows: {
133
143
  "8.0.0": string;
@@ -137,6 +147,39 @@ export declare const versionedComponents: {
137
147
  title: {
138
148
  "8.0.0": (refId: string) => string;
139
149
  };
150
+ actionButton: {
151
+ '10.4.0': (title: string) => string;
152
+ "8.0.0": (title: string) => string;
153
+ };
154
+ };
155
+ AlertRules: {
156
+ previewButton: {
157
+ '11.1.0': string;
158
+ };
159
+ ruleNameField: {
160
+ '11.1.0': string;
161
+ };
162
+ newFolderButton: {
163
+ '11.1.0': string;
164
+ };
165
+ newFolderNameField: {
166
+ '11.1.0': string;
167
+ };
168
+ newFolderNameCreateButton: {
169
+ '11.1.0': string;
170
+ };
171
+ newEvaluationGroupButton: {
172
+ '11.1.0': string;
173
+ };
174
+ newEvaluationGroupName: {
175
+ '11.1.0': string;
176
+ };
177
+ newEvaluationGroupInterval: {
178
+ '11.1.0': string;
179
+ };
180
+ newEvaluationGroupCreate: {
181
+ '11.1.0': string;
182
+ };
140
183
  };
141
184
  Alert: {
142
185
  alertV2: {
@@ -127,6 +127,16 @@ exports.versionedComponents = {
127
127
  [constants_1.MIN_GRAFANA_VERSION]: 'RefreshPicker run button',
128
128
  },
129
129
  },
130
+ QueryTab: {
131
+ addQuery: {
132
+ '10.2.0': 'data-testid query-tab-add-query',
133
+ [constants_1.MIN_GRAFANA_VERSION]: 'Query editor add query button',
134
+ },
135
+ addExpression: {
136
+ '11.0.0': 'data-testid query-tab-add-expression',
137
+ '9.5.2': 'query-tab-add-expression',
138
+ },
139
+ },
130
140
  QueryEditorRows: {
131
141
  rows: {
132
142
  [constants_1.MIN_GRAFANA_VERSION]: 'Query editor row',
@@ -136,6 +146,21 @@ exports.versionedComponents = {
136
146
  title: {
137
147
  [constants_1.MIN_GRAFANA_VERSION]: (refId) => `Query editor row title ${refId}`,
138
148
  },
149
+ actionButton: {
150
+ '10.4.0': (title) => `data-testid ${title}`,
151
+ [constants_1.MIN_GRAFANA_VERSION]: (title) => `${title}`,
152
+ },
153
+ },
154
+ AlertRules: {
155
+ previewButton: { '11.1.0': 'data-testid alert-rule preview-button' },
156
+ ruleNameField: { '11.1.0': 'data-testid alert-rule name-field' },
157
+ newFolderButton: { '11.1.0': 'data-testid alert-rule new-folder-button' },
158
+ newFolderNameField: { '11.1.0': 'data-testid alert-rule name-folder-name-field' },
159
+ newFolderNameCreateButton: { '11.1.0': 'data-testid alert-rule name-folder-name-create-button' },
160
+ newEvaluationGroupButton: { '11.1.0': 'data-testid alert-rule new-evaluation-group-button' },
161
+ newEvaluationGroupName: { '11.1.0': 'data-testid alert-rule new-evaluation-group-name' },
162
+ newEvaluationGroupInterval: { '11.1.0': 'data-testid alert-rule new-evaluation-group-interval' },
163
+ newEvaluationGroupCreate: { '11.1.0': 'data-testid alert-rule new-evaluation-group-create-button' },
139
164
  },
140
165
  Alert: {
141
166
  alertV2: {
@@ -4,6 +4,19 @@ export declare const versionedPages: {
4
4
  "8.0.0": string;
5
5
  };
6
6
  };
7
+ Alerting: {
8
+ AddAlertRule: {
9
+ url: {
10
+ "10.1.0": string;
11
+ "8.0.0": string;
12
+ };
13
+ };
14
+ EditAlertRule: {
15
+ url: {
16
+ "8.0.0": (alertRuleUid: string) => string;
17
+ };
18
+ };
19
+ };
7
20
  DataSource: {
8
21
  saveAndTest: {
9
22
  '10.0.0': string;
@@ -8,6 +8,19 @@ exports.versionedPages = {
8
8
  [constants_1.MIN_GRAFANA_VERSION]: '/',
9
9
  },
10
10
  },
11
+ Alerting: {
12
+ AddAlertRule: {
13
+ url: {
14
+ ['10.1.0']: '/alerting/new/alerting',
15
+ [constants_1.MIN_GRAFANA_VERSION]: '/alerting/new',
16
+ },
17
+ },
18
+ EditAlertRule: {
19
+ url: {
20
+ [constants_1.MIN_GRAFANA_VERSION]: (alertRuleUid) => `alerting/${alertRuleUid}/edit`,
21
+ },
22
+ },
23
+ },
11
24
  DataSource: {
12
25
  saveAndTest: {
13
26
  '10.0.0': 'data-testid Data source settings page Save and Test button',
@@ -0,0 +1,6 @@
1
+ import { TestFixture } from '@playwright/test';
2
+ import { AlertRuleEditPage } from '../models/pages/AlertRuleEditPage';
3
+ import { PlaywrightArgs } from '../types';
4
+ type AlertRuleEditPageFixture = TestFixture<AlertRuleEditPage, PlaywrightArgs>;
5
+ export declare const alertRuleEditPage: AlertRuleEditPageFixture;
6
+ export {};
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.alertRuleEditPage = void 0;
27
+ const semver = __importStar(require("semver"));
28
+ const AlertRuleEditPage_1 = require("../models/pages/AlertRuleEditPage");
29
+ const alertRuleEditPage = async ({ page, selectors, grafanaVersion, request }, use, testInfo) => {
30
+ if (semver.lt(grafanaVersion, '9.5.0')) {
31
+ console.log('Testing alert rules with plugin-e2e is only supported for Grafana 9.5.0 and later. Checkout https://grafana.com/developers/plugin-tools/e2e-test-a-plugin/test-a-data-source-plugin/annotation-queries#test-the-entire-annotation-query-execution-flow to see how to skip tests for a range of Grafana versions.');
32
+ }
33
+ const alertRuleEditPage = new AlertRuleEditPage_1.AlertRuleEditPage({ page, selectors, grafanaVersion, request, testInfo });
34
+ await alertRuleEditPage.goto();
35
+ await use(alertRuleEditPage);
36
+ };
37
+ exports.alertRuleEditPage = alertRuleEditPage;
@@ -0,0 +1,6 @@
1
+ import { TestFixture } from '@playwright/test';
2
+ import { AlertRuleArgs, PlaywrightArgs } from '../../types';
3
+ import { AlertRuleEditPage } from '../../models/pages/AlertRuleEditPage';
4
+ type GotoAlertRuleEditPageFixture = TestFixture<(args: AlertRuleArgs) => Promise<AlertRuleEditPage>, PlaywrightArgs>;
5
+ export declare const gotoAlertRuleEditPage: GotoAlertRuleEditPageFixture;
6
+ export {};
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.gotoAlertRuleEditPage = void 0;
27
+ const semver = __importStar(require("semver"));
28
+ const AlertRuleEditPage_1 = require("../../models/pages/AlertRuleEditPage");
29
+ const gotoAlertRuleEditPage = async ({ request, page, selectors, grafanaVersion }, use, testInfo) => {
30
+ await use(async (args) => {
31
+ if (semver.lt(grafanaVersion, '9.5.0')) {
32
+ console.log('Testing alert rules with plugin-e2e is only supported for Grafana 9.4.0 and later. Checkout https://grafana.com/developers/plugin-tools/e2e-test-a-plugin/test-a-data-source-plugin/annotation-queries#test-the-entire-annotation-query-execution-flow to see how to skip tests for a range of Grafana versions.');
33
+ }
34
+ const alertRuleEditPage = new AlertRuleEditPage_1.AlertRuleEditPage({ page, selectors, grafanaVersion, request, testInfo }, args);
35
+ await alertRuleEditPage.goto();
36
+ return alertRuleEditPage;
37
+ });
38
+ };
39
+ exports.gotoAlertRuleEditPage = gotoAlertRuleEditPage;
@@ -0,0 +1,5 @@
1
+ import { TestFixture } from '@playwright/test';
2
+ import { AlertRule, PlaywrightArgs, ReadProvisionedAlertRuleArgs } from '../../types';
3
+ type ReadProvisionedAlertRuleFixture = TestFixture<(args: ReadProvisionedAlertRuleArgs) => Promise<AlertRule>, PlaywrightArgs>;
4
+ export declare const readProvisionedAlertRule: ReadProvisionedAlertRuleFixture;
5
+ export {};
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readProvisionedAlertRule = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const yaml_1 = require("yaml");
9
+ const fs_1 = require("fs");
10
+ const ALERTING_DIR = 'alerting';
11
+ const readProvisionedAlertRule = async ({ provisioningRootDir }, use) => {
12
+ await use(async ({ fileName, groupName, ruleTitle }) => {
13
+ const resolvedPath = path_1.default.resolve(path_1.default.join(provisioningRootDir, ALERTING_DIR, fileName));
14
+ const contents = await fs_1.promises.readFile(resolvedPath, 'utf8');
15
+ const yml = (0, yaml_1.parse)(contents);
16
+ let group = yml.groups[0];
17
+ if (groupName) {
18
+ group = yml.groups.find((group) => group.name === groupName);
19
+ }
20
+ let rule = group.rules[0];
21
+ if (ruleTitle) {
22
+ rule = group.rules.find((rule) => rule.title === ruleTitle);
23
+ }
24
+ return rule;
25
+ });
26
+ };
27
+ exports.readProvisionedAlertRule = readProvisionedAlertRule;
package/dist/index.js CHANGED
@@ -26,10 +26,12 @@ const gotoAppPage_1 = require("./fixtures/commands/gotoAppPage");
26
26
  const gotoDashboardPage_1 = require("./fixtures/commands/gotoDashboardPage");
27
27
  const gotoDataSourceConfigPage_1 = require("./fixtures/commands/gotoDataSourceConfigPage");
28
28
  const gotoPanelEditPage_1 = require("./fixtures/commands/gotoPanelEditPage");
29
+ const gotoAlertRuleEditPage_1 = require("./fixtures/commands/gotoAlertRuleEditPage");
29
30
  const gotoVariableEditPage_1 = require("./fixtures/commands/gotoVariableEditPage");
30
31
  const login_1 = require("./fixtures/commands/login");
31
32
  const readProvisionedDashboard_1 = require("./fixtures/commands/readProvisionedDashboard");
32
33
  const readProvisionedDataSource_1 = require("./fixtures/commands/readProvisionedDataSource");
34
+ const readProvisionedAlertRule_1 = require("./fixtures/commands/readProvisionedAlertRule");
33
35
  const dashboardPage_1 = require("./fixtures/dashboardPage");
34
36
  const explorePage_1 = require("./fixtures/explorePage");
35
37
  const grafanaVersion_1 = require("./fixtures/grafanaVersion");
@@ -38,6 +40,7 @@ const page_1 = require("./fixtures/page");
38
40
  const panelEditPage_1 = require("./fixtures/panelEditPage");
39
41
  const selectors_1 = require("./fixtures/selectors");
40
42
  const variableEditPage_1 = require("./fixtures/variableEditPage");
43
+ const alertRuleEditPage_1 = require("./fixtures/alertRuleEditPage");
41
44
  const options_1 = require("./options");
42
45
  const toHaveAlert_1 = require("./matchers/toHaveAlert");
43
46
  const toDisplayPreviews_1 = require("./matchers/toDisplayPreviews");
@@ -87,9 +90,11 @@ exports.test = test_1.test.extend({
87
90
  panelEditPage: panelEditPage_1.panelEditPage,
88
91
  variableEditPage: variableEditPage_1.variableEditPage,
89
92
  annotationEditPage: annotationEditPage_1.annotationEditPage,
93
+ alertRuleEditPage: alertRuleEditPage_1.alertRuleEditPage,
90
94
  explorePage: explorePage_1.explorePage,
91
95
  createDataSource: createDataSource_1.createDataSource,
92
96
  readProvisionedDataSource: readProvisionedDataSource_1.readProvisionedDataSource,
97
+ readProvisionedAlertRule: readProvisionedAlertRule_1.readProvisionedAlertRule,
93
98
  readProvisionedDashboard: readProvisionedDashboard_1.readProvisionedDashboard,
94
99
  isFeatureToggleEnabled: isFeatureToggleEnabled_1.isFeatureToggleEnabled,
95
100
  createUser: createUser_1.createUser,
@@ -97,6 +102,7 @@ exports.test = test_1.test.extend({
97
102
  gotoPanelEditPage: gotoPanelEditPage_1.gotoPanelEditPage,
98
103
  gotoVariableEditPage: gotoVariableEditPage_1.gotoVariableEditPage,
99
104
  gotoAnnotationEditPage: gotoAnnotationEditPage_1.gotoAnnotationEditPage,
105
+ gotoAlertRuleEditPage: gotoAlertRuleEditPage_1.gotoAlertRuleEditPage,
100
106
  gotoDataSourceConfigPage: gotoDataSourceConfigPage_1.gotoDataSourceConfigPage,
101
107
  gotoAppConfigPage: gotoAppConfigPage_1.gotoAppConfigPage,
102
108
  gotoAppPage: gotoAppPage_1.gotoAppPage,
@@ -0,0 +1,11 @@
1
+ import { DataSourcePicker } from './DataSourcePicker';
2
+ import { getByGrafanaSelectorOptions, PluginTestCtx } from '../../types';
3
+ import { GrafanaPage } from '../pages/GrafanaPage';
4
+ import { Locator } from '@playwright/test';
5
+ export declare class AlertRuleQuery extends GrafanaPage {
6
+ readonly ctx: PluginTestCtx;
7
+ readonly locator: Locator;
8
+ datasource: DataSourcePicker;
9
+ constructor(ctx: PluginTestCtx, locator: Locator);
10
+ getByGrafanaSelector(selector: string, options?: getByGrafanaSelectorOptions): Locator;
11
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AlertRuleQuery = void 0;
4
+ const DataSourcePicker_1 = require("./DataSourcePicker");
5
+ const GrafanaPage_1 = require("../pages/GrafanaPage");
6
+ class AlertRuleQuery extends GrafanaPage_1.GrafanaPage {
7
+ ctx;
8
+ locator;
9
+ datasource;
10
+ constructor(ctx, locator) {
11
+ super(ctx);
12
+ this.ctx = ctx;
13
+ this.locator = locator;
14
+ this.datasource = new DataSourcePicker_1.DataSourcePicker(ctx, locator);
15
+ }
16
+ getByGrafanaSelector(selector, options = {}) {
17
+ return super.getByGrafanaSelector(selector, {
18
+ root: this.locator,
19
+ ...options,
20
+ });
21
+ }
22
+ }
23
+ exports.AlertRuleQuery = AlertRuleQuery;
@@ -1,7 +1,9 @@
1
+ import { Locator } from '@playwright/test';
1
2
  import { PluginTestCtx } from '../../types';
2
3
  import { GrafanaPage } from '../pages/GrafanaPage';
3
4
  export declare class DataSourcePicker extends GrafanaPage {
4
- constructor(ctx: PluginTestCtx);
5
+ private root?;
6
+ constructor(ctx: PluginTestCtx, root?: Locator | undefined);
5
7
  /**
6
8
  * Sets the data source picker to the provided name
7
9
  */
@@ -3,14 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DataSourcePicker = void 0;
4
4
  const GrafanaPage_1 = require("../pages/GrafanaPage");
5
5
  class DataSourcePicker extends GrafanaPage_1.GrafanaPage {
6
- constructor(ctx) {
6
+ root;
7
+ constructor(ctx, root) {
7
8
  super(ctx);
9
+ this.root = root;
8
10
  }
9
11
  /**
10
12
  * Sets the data source picker to the provided name
11
13
  */
12
14
  async set(name) {
13
- await this.getByGrafanaSelector(this.ctx.selectors.components.DataSourcePicker.container)
15
+ await this.getByGrafanaSelector(this.ctx.selectors.components.DataSourcePicker.container, { root: this.root })
14
16
  .locator('input')
15
17
  .fill(name);
16
18
  // this is a hack to get the selection to work in 10.ish versions of Grafana.
@@ -70,11 +70,11 @@ class Panel extends GrafanaPage_1.GrafanaPage {
70
70
  // before 9.5.0, there were no proper selectors for the panel menu items
71
71
  if (semver.lt(this.ctx.grafanaVersion, '9.5.0')) {
72
72
  panelMenu = this.locator.getByRole('heading');
73
- parentMenuItem = this.ctx.page.getByText(options?.parentItem ?? '');
74
- menuItem = this.ctx.page.getByRole('menu').getByText(item);
73
+ this.ctx.page.locator(`[aria-label="Panel header item ${options?.parentItem}"]`);
74
+ this.ctx.page.locator(`[aria-label="Panel header item ${item}"]`);
75
75
  }
76
76
  await panelMenu.click({ force: true });
77
- options?.parentItem && (await parentMenuItem.hover());
77
+ options?.parentItem && parentMenuItem.hover();
78
78
  await menuItem.click();
79
79
  }
80
80
  /**
@@ -0,0 +1,34 @@
1
+ import { AlertRuleArgs, NavigateOptions, PluginTestCtx, RequestOptions } from '../../types';
2
+ import { GrafanaPage } from './GrafanaPage';
3
+ import { AlertRuleQuery } from '../components/AlertRuleQuery';
4
+ export declare class AlertRuleEditPage extends GrafanaPage {
5
+ readonly ctx: PluginTestCtx;
6
+ readonly args?: AlertRuleArgs | undefined;
7
+ constructor(ctx: PluginTestCtx, args?: AlertRuleArgs | undefined);
8
+ /**
9
+ * Navigates to the annotation edit page. If a dashboard uid was not provided, it's assumed that it's a new dashboard.
10
+ */
11
+ goto(options?: NavigateOptions): Promise<void>;
12
+ /**
13
+ * Returns a locator for hte alert rule name field
14
+ */
15
+ get alertRuleNameField(): import("playwright-core").Locator;
16
+ /**
17
+ * Returns an instance of the {@link AlertRuleQuery} class for the query row with the provided refId.
18
+ */
19
+ getAlertRuleQueryRow(refId: string): AlertRuleQuery;
20
+ /**
21
+ * Clicks the "Add query" button and returns an instance of the {@link AlertRuleQuery} class for the new query row.
22
+ */
23
+ clickAddQueryRow(): Promise<AlertRuleQuery>;
24
+ /**
25
+ * Clicks the evaluate button and waits for the evaluation to complete. If the evaluation is successful, the status code of the response is 200.
26
+ * If one or more queries are invalid, an error status code is returned.
27
+ *
28
+ * Note that this method intercepts the response of the alerting evaluation endpoint and returns the status code of the first failed query.
29
+ * This means that any mocks defined with page.route in your tests will be overriden.
30
+ *
31
+ * Only supported for Grafana version 9.5.0 ad later.
32
+ */
33
+ evaluate(options?: RequestOptions): Promise<import("playwright-core").Response>;
34
+ }
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.AlertRuleEditPage = void 0;
27
+ const semver = __importStar(require("semver"));
28
+ const GrafanaPage_1 = require("./GrafanaPage");
29
+ const AlertRuleQuery_1 = require("../components/AlertRuleQuery");
30
+ class AlertRuleEditPage extends GrafanaPage_1.GrafanaPage {
31
+ ctx;
32
+ args;
33
+ constructor(ctx, args) {
34
+ super(ctx);
35
+ this.ctx = ctx;
36
+ this.args = args;
37
+ }
38
+ /**
39
+ * Navigates to the annotation edit page. If a dashboard uid was not provided, it's assumed that it's a new dashboard.
40
+ */
41
+ async goto(options) {
42
+ const { AddAlertRule, EditAlertRule } = this.ctx.selectors.pages.Alerting;
43
+ const url = this.args?.uid ? EditAlertRule.url(this.args.uid) : AddAlertRule.url;
44
+ return super.navigate(url, options);
45
+ }
46
+ /**
47
+ * Returns a locator for hte alert rule name field
48
+ */
49
+ get alertRuleNameField() {
50
+ if (semver.gte(this.ctx.grafanaVersion, '11.1.0')) {
51
+ return this.getByGrafanaSelector(this.ctx.selectors.components.AlertRules.ruleNameField);
52
+ }
53
+ return this.ctx.page.getByPlaceholder('Give your alert rule a name');
54
+ }
55
+ /**
56
+ * Returns an instance of the {@link AlertRuleQuery} class for the query row with the provided refId.
57
+ */
58
+ getAlertRuleQueryRow(refId) {
59
+ const locator = this.getByGrafanaSelector(this.ctx.selectors.components.QueryEditorRows.rows).filter({
60
+ has: this.getByGrafanaSelector(this.ctx.selectors.components.QueryEditorRow.title(refId)),
61
+ });
62
+ return new AlertRuleQuery_1.AlertRuleQuery(this.ctx, locator);
63
+ }
64
+ /**
65
+ * Clicks the "Add query" button and returns an instance of the {@link AlertRuleQuery} class for the new query row.
66
+ */
67
+ async clickAddQueryRow() {
68
+ await this.getByGrafanaSelector(this.ctx.selectors.components.QueryTab.addQuery).click();
69
+ const locator = this.getByGrafanaSelector(this.ctx.selectors.components.QueryEditorRows.rows).last();
70
+ return new AlertRuleQuery_1.AlertRuleQuery(this.ctx, locator);
71
+ }
72
+ /**
73
+ * Clicks the evaluate button and waits for the evaluation to complete. If the evaluation is successful, the status code of the response is 200.
74
+ * If one or more queries are invalid, an error status code is returned.
75
+ *
76
+ * Note that this method intercepts the response of the alerting evaluation endpoint and returns the status code of the first failed query.
77
+ * This means that any mocks defined with page.route in your tests will be overriden.
78
+ *
79
+ * Only supported for Grafana version 9.5.0 ad later.
80
+ */
81
+ async evaluate(options) {
82
+ // it seems like when clicking the evaluate button to quickly after filling in the alert query form, form values have not been propagated to the state, so we wait a bit before clicking
83
+ await this.ctx.page.waitForTimeout(1000);
84
+ // Starting from Grafana 10.0.0, the alerting evaluation endpoint started returning errors in a different way.
85
+ // Even if one or many of the queries is failed, the status code for the http response is 200 so we have to check the status of each query instead.
86
+ // If at least one query is failed, we the response of the evaluate method is mapped to the status of the first failed query.
87
+ if (semver.gte(this.ctx.grafanaVersion, '10.0.0')) {
88
+ this.ctx.page.route(this.ctx.selectors.apis.Alerting.eval, async (route) => {
89
+ const response = await route.fetch();
90
+ if (!response.ok()) {
91
+ return route.fulfill({ response });
92
+ }
93
+ let body = await response.json();
94
+ const statuses = Object.keys(body.results).map((key) => body.results[key].status);
95
+ route.fulfill({
96
+ response,
97
+ status: statuses.every((status) => status >= 200 && status < 300) ? 200 : statuses[0],
98
+ });
99
+ });
100
+ }
101
+ const responsePromise = this.ctx.page.waitForResponse((resp) => resp.url().includes(this.ctx.selectors.apis.Alerting.eval), options);
102
+ let evaluateButton = this.getByGrafanaSelector(this.ctx.selectors.components.AlertRules.previewButton);
103
+ if (semver.lt(this.ctx.grafanaVersion, '11.1.0')) {
104
+ evaluateButton = this.ctx.page.getByRole('button', { name: 'Preview', exact: true });
105
+ }
106
+ const evalReq = this.ctx.page
107
+ .waitForRequest((req) => req.url().includes(this.ctx.selectors.apis.Alerting.eval), {
108
+ timeout: 5000,
109
+ })
110
+ .catch(async () => {
111
+ // intermittently, the first click doesn't trigger the request, so in that case we click again
112
+ await evaluateButton.click();
113
+ });
114
+ await evaluateButton.click();
115
+ await evalReq;
116
+ return responsePromise;
117
+ }
118
+ }
119
+ exports.AlertRuleEditPage = AlertRuleEditPage;
package/dist/types.d.ts CHANGED
@@ -8,6 +8,7 @@ import { DataSourceConfigPage } from './models/pages/DataSourceConfigPage';
8
8
  import { ExplorePage } from './models/pages/ExplorePage';
9
9
  import { PanelEditPage } from './models/pages/PanelEditPage';
10
10
  import { VariableEditPage } from './models/pages/VariableEditPage';
11
+ import { AlertRuleEditPage } from './models/pages/AlertRuleEditPage';
11
12
  export type PluginOptions = {
12
13
  /**
13
14
  * When using the readProvisioning fixture, files will be read from this directory. If no directory is provided,
@@ -149,6 +150,16 @@ export type PluginFixture = {
149
150
  * If no name is provided, the first data source in the list of data sources will be returned.
150
151
  */
151
152
  readProvisionedDataSource<T = {}, S = {}>(args: ReadProvisionedDataSourceArgs): Promise<DataSourceSettings<T, S>>;
153
+ /**
154
+ * Fixture command that reads a yaml file in the provisioning/alerting directory.
155
+ *
156
+ * The file name should be the name of the file with the .yaml|.yml extension.
157
+ * If a group name is provided, the first group that matches the name will be returned.
158
+ * If no group name is provided, the first group in the list of groups will be returned.
159
+ * If a rule title is provided, the first rule that matches the title will be returned.
160
+ * If no rule title is provided, the first rule in the group will be returned.
161
+ */
162
+ readProvisionedAlertRule(args: ReadProvisionedAlertRuleArgs): Promise<AlertRule>;
152
163
  /**
153
164
  * Fixture command that reads a dashboard json file in the provisioning/dashboards directory.
154
165
  *
@@ -189,6 +200,13 @@ export type PluginFixture = {
189
200
  * To load an existing dashboard with an existing annotation, use the {@link gotoAnnotationEditPage} fixture.
190
201
  */
191
202
  annotationEditPage: AnnotationEditPage;
203
+ /**
204
+ * Isolated {@link AlertRuleEditPage} instance for each test.
205
+ *
206
+ * When using this fixture in a test, you will get an empty alert rule page form
207
+ * To load an existing alert rule, use the {@link gotoAlertRulePage} fixture.
208
+ */
209
+ alertRuleEditPage: AlertRuleEditPage;
192
210
  /**
193
211
  * Isolated {@link ExplorePage} instance for each test.
194
212
  */
@@ -216,6 +234,10 @@ export type PluginFixture = {
216
234
  * Fixture command that navigates an annotation edit page for an already existing annotation query in a dashboard.
217
235
  */
218
236
  gotoAnnotationEditPage: (args: DashboardEditViewArgs<string>) => Promise<AnnotationEditPage>;
237
+ /**
238
+ * Fixture command that navigates to an alert rule edit page for an already existing alert query.
239
+ */
240
+ gotoAlertRuleEditPage: (args: AlertRuleArgs) => Promise<AlertRuleEditPage>;
219
241
  /**
220
242
  * Fixture command that navigates a configuration page for an already existing data source instance.
221
243
  */
@@ -258,6 +280,15 @@ export interface DataSourceSettings<T = {}, S = {}> {
258
280
  jsonData: T;
259
281
  secureJsonData?: S;
260
282
  }
283
+ export interface AlertRule {
284
+ uid: string;
285
+ title: string;
286
+ data: Array<{
287
+ refId: string;
288
+ datasourceUid: string;
289
+ model: any;
290
+ }>;
291
+ }
261
292
  /**
262
293
  * The dashboard object
263
294
  */
@@ -356,19 +387,36 @@ export type DashboardEditViewArgs<T> = {
356
387
  dashboard?: DashboardPageArgs;
357
388
  id: T;
358
389
  };
390
+ export type AlertRuleArgs = {
391
+ uid: string;
392
+ };
359
393
  export type ReadProvisionedDashboardArgs = {
360
394
  /**
361
395
  * The path, relative to the provisioning folder, to the dashboard json file
362
396
  */
363
397
  fileName: string;
364
398
  };
399
+ export type ReadProvisionedAlertRuleArgs = {
400
+ /**
401
+ * The name of the yaml file in the provisioning/alerting folder
402
+ */
403
+ fileName: string;
404
+ /**
405
+ * The name of the alert group in the groups list. Will use the first group if not provided
406
+ */
407
+ groupName?: string;
408
+ /**
409
+ * The name of the alert rule in the rules list. Will use the first rule in the group if not provided
410
+ */
411
+ ruleTitle?: string;
412
+ };
365
413
  export type ReadProvisionedDataSourceArgs = {
366
414
  /**
367
- * The path, relative to the provisioning folder, to the dashboard json file
415
+ * The name of the yaml file in the provisioning/datasources folder
368
416
  */
369
417
  fileName: string;
370
418
  /**
371
- * The name of the data source in the datasources list
419
+ * The name of the data source in the datasources list. Will use the first data source if not provided
372
420
  */
373
421
  name?: string;
374
422
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grafana/plugin-e2e",
3
- "version": "1.3.2",
3
+ "version": "1.4.0-canary.779.878465c.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -55,5 +55,5 @@
55
55
  "start": "cls || clear"
56
56
  }
57
57
  },
58
- "gitHead": "f40350d6158ef15563f72cf491e2b9eb3eceea15"
58
+ "gitHead": "878465cc433e2ad6520df388370fc7692542b8ce"
59
59
  }