@grafana/plugin-e2e 0.25.1 → 0.26.1
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/e2e-selectors/types.d.ts +3 -1
- package/dist/e2e-selectors/versioned/apis.d.ts +2 -1
- package/dist/e2e-selectors/versioned/apis.js +2 -1
- package/dist/e2e-selectors/versioned/pages.d.ts +3 -0
- package/dist/e2e-selectors/versioned/pages.js +3 -0
- package/dist/index.js +1 -1
- package/dist/matchers/toDisplayPreviews.js +1 -1
- package/dist/matchers/toHaveAlert.js +1 -1
- package/dist/models/components/DataSourcePicker.js +1 -1
- package/dist/models/components/Panel.js +1 -1
- package/dist/models/components/TimeRange.js +7 -7
- package/dist/models/pages/AnnotationEditPage.js +9 -2
- package/dist/models/pages/AnnotationPage.js +3 -3
- package/dist/models/pages/DashboardPage.js +4 -4
- package/dist/models/pages/DataSourceConfigPage.js +1 -1
- package/dist/models/pages/ExplorePage.js +30 -7
- package/dist/models/pages/GrafanaPage.d.ts +4 -5
- package/dist/models/pages/GrafanaPage.js +3 -4
- package/dist/models/pages/PanelEditPage.js +15 -15
- package/dist/models/pages/VariableEditPage.js +41 -7
- package/dist/models/pages/VariablePage.js +2 -2
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
|
@@ -11,7 +11,7 @@ export type APIs = {
|
|
|
11
11
|
query: string;
|
|
12
12
|
health: (uid: string, id: string) => string;
|
|
13
13
|
datasourceByUID: (uid: string) => string;
|
|
14
|
-
proxy: (uid: string) => string;
|
|
14
|
+
proxy: (uid: string, id: string) => string;
|
|
15
15
|
};
|
|
16
16
|
Dashboard: {
|
|
17
17
|
delete: (uid: string) => string;
|
|
@@ -175,10 +175,12 @@ export type Pages = {
|
|
|
175
175
|
newButton: string;
|
|
176
176
|
addVariableCTAV2: (variableName: string) => string;
|
|
177
177
|
addVariableCTAV2Item: string;
|
|
178
|
+
table: string;
|
|
178
179
|
};
|
|
179
180
|
Edit: {
|
|
180
181
|
url: (dashboardUid: string, editIndex: string) => string;
|
|
181
182
|
General: {
|
|
183
|
+
selectionOptionsIncludeAllSwitch: string;
|
|
182
184
|
generalTypeSelectV2: string;
|
|
183
185
|
previewOfValuesOption: string;
|
|
184
186
|
submitButton: string;
|
|
@@ -25,7 +25,8 @@ exports.versionedAPIs = {
|
|
|
25
25
|
[constants_1.MIN_GRAFANA_VERSION]: (uid) => `/api/datasources/uid/${uid}`,
|
|
26
26
|
},
|
|
27
27
|
proxy: {
|
|
28
|
-
|
|
28
|
+
'9.4.0': (uid, _) => `api/datasources/proxy/uid/${uid}`,
|
|
29
|
+
[constants_1.MIN_GRAFANA_VERSION]: (_, id) => `/api/datasources/proxy/${id}`,
|
|
29
30
|
},
|
|
30
31
|
},
|
|
31
32
|
Dashboard: {
|
|
@@ -118,6 +118,9 @@ exports.versionedPages = {
|
|
|
118
118
|
'10.4.0': 'data-testid Variable editor Run Query button',
|
|
119
119
|
[constants_1.MIN_GRAFANA_VERSION]: 'Variable editor Submit button',
|
|
120
120
|
},
|
|
121
|
+
selectionOptionsIncludeAllSwitch: {
|
|
122
|
+
[constants_1.MIN_GRAFANA_VERSION]: 'Variable editor Form IncludeAll switch',
|
|
123
|
+
},
|
|
121
124
|
},
|
|
122
125
|
},
|
|
123
126
|
},
|
package/dist/index.js
CHANGED
|
@@ -110,7 +110,7 @@ exports.expect = test_1.expect.extend({
|
|
|
110
110
|
});
|
|
111
111
|
/** Register a custom selector engine that resolves locators for Grafana E2E selectors
|
|
112
112
|
*
|
|
113
|
-
* The same functionality is available in the {@link GrafanaPage.
|
|
113
|
+
* The same functionality is available in the {@link GrafanaPage.getByGrafanaSelector} method. However,
|
|
114
114
|
* by registering the selector engine, one can resolve locators by Grafana E2E selectors also within a locator.
|
|
115
115
|
*
|
|
116
116
|
* Example:
|
|
@@ -8,7 +8,7 @@ const toDisplayPreviews = async (variableEditPage, previewTexts, options) => {
|
|
|
8
8
|
let actual;
|
|
9
9
|
let message = `To find preview of values: ${previewTexts.join(', ')}}`;
|
|
10
10
|
try {
|
|
11
|
-
await (0, test_1.expect)(variableEditPage.
|
|
11
|
+
await (0, test_1.expect)(variableEditPage.getByGrafanaSelector(variableEditPage.ctx.selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption)).toContainText(previewTexts, options);
|
|
12
12
|
return {
|
|
13
13
|
pass: true,
|
|
14
14
|
actual: false,
|
|
@@ -8,7 +8,7 @@ const toHaveAlert = async (grafanaPage, severity, options) => {
|
|
|
8
8
|
let message = `An alert of variant ${severity} to be displayed on the page`;
|
|
9
9
|
try {
|
|
10
10
|
const filteredAlerts = grafanaPage
|
|
11
|
-
.
|
|
11
|
+
.getByGrafanaSelector(grafanaPage.ctx.selectors.components.Alert.alertV2(severity))
|
|
12
12
|
.filter({
|
|
13
13
|
hasText: options?.hasText,
|
|
14
14
|
hasNotText: options?.hasNotText,
|
|
@@ -10,7 +10,7 @@ class DataSourcePicker extends GrafanaPage_1.GrafanaPage {
|
|
|
10
10
|
* Sets the data source picker to the provided name
|
|
11
11
|
*/
|
|
12
12
|
async set(name) {
|
|
13
|
-
await this.
|
|
13
|
+
await this.getByGrafanaSelector(this.ctx.selectors.components.DataSourcePicker.container)
|
|
14
14
|
.locator('input')
|
|
15
15
|
.fill(name);
|
|
16
16
|
// this is a hack to get the selection to work in 10.ish versions of Grafana.
|
|
@@ -64,7 +64,7 @@ class Panel extends GrafanaPage_1.GrafanaPage {
|
|
|
64
64
|
if (semver.lte(this.ctx.grafanaVersion, '9.4.3')) {
|
|
65
65
|
selector = this.ctx.selectors.components.Panels.Panel.headerCornerInfo(ERROR_STATUS);
|
|
66
66
|
}
|
|
67
|
-
return this.
|
|
67
|
+
return this.getByGrafanaSelector(selector, {
|
|
68
68
|
root: this.locator,
|
|
69
69
|
});
|
|
70
70
|
}
|
|
@@ -36,7 +36,7 @@ class TimeRange extends GrafanaPage_1.GrafanaPage {
|
|
|
36
36
|
async set({ from, to, zone }) {
|
|
37
37
|
const { TimeZonePicker, TimePicker } = this.ctx.selectors.components;
|
|
38
38
|
try {
|
|
39
|
-
await this.
|
|
39
|
+
await this.getByGrafanaSelector(TimePicker.openButton).click();
|
|
40
40
|
}
|
|
41
41
|
catch (e) {
|
|
42
42
|
// seems like in older versions of Grafana the time picker markup is rendered twice
|
|
@@ -44,19 +44,19 @@ class TimeRange extends GrafanaPage_1.GrafanaPage {
|
|
|
44
44
|
}
|
|
45
45
|
if (zone) {
|
|
46
46
|
const changeTimeSettingsButton = semver.gte(this.ctx.grafanaVersion, '11.0.0')
|
|
47
|
-
? this.
|
|
47
|
+
? this.getByGrafanaSelector(TimeZonePicker.changeTimeSettingsButton)
|
|
48
48
|
: this.ctx.page.getByRole('button', { name: 'Change time settings' });
|
|
49
49
|
await changeTimeSettingsButton.click();
|
|
50
|
-
await this.
|
|
50
|
+
await this.getByGrafanaSelector(TimeZonePicker.containerV2).fill(zone);
|
|
51
51
|
}
|
|
52
|
-
await this.
|
|
53
|
-
const fromField = await this.
|
|
52
|
+
await this.getByGrafanaSelector(TimePicker.absoluteTimeRangeTitle).click();
|
|
53
|
+
const fromField = await this.getByGrafanaSelector(TimePicker.fromField);
|
|
54
54
|
await fromField.clear();
|
|
55
55
|
await fromField.fill(from);
|
|
56
|
-
const toField = await this.
|
|
56
|
+
const toField = await this.getByGrafanaSelector(TimePicker.toField);
|
|
57
57
|
await toField.clear();
|
|
58
58
|
await toField.fill(to);
|
|
59
|
-
await this.
|
|
59
|
+
await this.getByGrafanaSelector(TimePicker.applyTimeRange).click();
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
exports.TimeRange = TimeRange;
|
|
@@ -45,7 +45,14 @@ class AnnotationEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
45
45
|
const url = this.args.dashboard?.uid
|
|
46
46
|
? Dashboard.Settings.Annotations.Edit.url(this.args.dashboard.uid, this.args.id)
|
|
47
47
|
: AddDashboard.Settings.Annotations.Edit.url(this.args.id);
|
|
48
|
-
|
|
48
|
+
await super.navigate(url, options);
|
|
49
|
+
// In versions before 9.2.0, the annotation index is not part of the URL so there's no way to navigate to it directly.
|
|
50
|
+
// Instead, we have to click the nth row in the variable list to navigate to the edit page for a given annotation index.
|
|
51
|
+
if (semver.lt(this.ctx.grafanaVersion, '9.2.0') && this.args.id) {
|
|
52
|
+
const list = this.ctx.page.locator('tbody tr');
|
|
53
|
+
const variables = await list.all();
|
|
54
|
+
await variables[Number(this.args.id)].click();
|
|
55
|
+
}
|
|
49
56
|
}
|
|
50
57
|
/**
|
|
51
58
|
* Executes the annotation query defined in the annotation page and returns the response promise
|
|
@@ -54,7 +61,7 @@ class AnnotationEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
54
61
|
async runQuery(options) {
|
|
55
62
|
const responsePromise = this.ctx.page.waitForResponse((resp) => resp.url().includes(this.ctx.selectors.apis.DataSource.query), options);
|
|
56
63
|
const testButton = semver.gte(this.ctx.grafanaVersion, '11.0.0')
|
|
57
|
-
? this.
|
|
64
|
+
? this.getByGrafanaSelector(this.ctx.selectors.components.Annotations.editor.testButton)
|
|
58
65
|
: this.ctx.page.getByRole('button', { name: 'TEST' });
|
|
59
66
|
await testButton.click();
|
|
60
67
|
return responsePromise;
|
|
@@ -53,16 +53,16 @@ class AnnotationPage extends GrafanaPage_1.GrafanaPage {
|
|
|
53
53
|
if (!this.dashboard?.uid) {
|
|
54
54
|
//the dashboard doesn't have any annotations yet (except for the built-in one)
|
|
55
55
|
if (semver.gte(this.ctx.grafanaVersion, '8.3.0')) {
|
|
56
|
-
await this.
|
|
56
|
+
await this.getByGrafanaSelector(addAnnotationCTAV2).click();
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
59
|
-
await this.
|
|
59
|
+
await this.getByGrafanaSelector(addAnnotationCTA).click();
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
else {
|
|
63
63
|
//the dashboard already has annotations
|
|
64
64
|
const newQueryButton = semver.gte(this.ctx.grafanaVersion, '11.0.0')
|
|
65
|
-
? this.
|
|
65
|
+
? this.getByGrafanaSelector(addAnnotationCTAV2)
|
|
66
66
|
: this.ctx.page.getByRole('button', { name: 'New query' });
|
|
67
67
|
await newQueryButton.click();
|
|
68
68
|
}
|
|
@@ -75,7 +75,7 @@ class DashboardPage extends GrafanaPage_1.GrafanaPage {
|
|
|
75
75
|
* await expect(panel.fieldNames).toContainText(['time', 'temperature']);
|
|
76
76
|
*/
|
|
77
77
|
getPanelByTitle(title) {
|
|
78
|
-
let locator = this.
|
|
78
|
+
let locator = this.getByGrafanaSelector(this.ctx.selectors.components.Panels.Panel.title(title), {
|
|
79
79
|
startsWith: true,
|
|
80
80
|
});
|
|
81
81
|
// in older versions, the panel selector is added to a child element, so we need to go up two levels to get the wrapper
|
|
@@ -101,11 +101,11 @@ class DashboardPage extends GrafanaPage_1.GrafanaPage {
|
|
|
101
101
|
async addPanel() {
|
|
102
102
|
const { components, pages } = this.ctx.selectors;
|
|
103
103
|
if (semver.gte(this.ctx.grafanaVersion, '10.0.0')) {
|
|
104
|
-
await this.
|
|
105
|
-
await this.
|
|
104
|
+
await this.getByGrafanaSelector(components.PageToolbar.itemButton(components.PageToolbar.itemButtonTitle)).click();
|
|
105
|
+
await this.getByGrafanaSelector(pages.AddDashboard.itemButton(pages.AddDashboard.itemButtonAddViz)).click();
|
|
106
106
|
}
|
|
107
107
|
else {
|
|
108
|
-
await this.
|
|
108
|
+
await this.getByGrafanaSelector(pages.AddDashboard.addNewPanel).click();
|
|
109
109
|
}
|
|
110
110
|
const panelId = await this.ctx.page.evaluate(() => {
|
|
111
111
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -39,7 +39,7 @@ class DataSourceConfigPage extends GrafanaPage_1.GrafanaPage {
|
|
|
39
39
|
const saveResponsePromise = this.ctx.page.waitForResponse((resp) => resp.url().includes(datasourceByUID(this.datasource.uid)));
|
|
40
40
|
const healthPath = options?.path ?? health(this.datasource.uid, this.datasource.id.toString());
|
|
41
41
|
const healthResponsePromise = this.ctx.page.waitForResponse((resp) => resp.url().includes(healthPath));
|
|
42
|
-
await this.
|
|
42
|
+
await this.getByGrafanaSelector(this.ctx.selectors.pages.DataSource.saveAndTest).click();
|
|
43
43
|
return saveResponsePromise.then(() => healthResponsePromise);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -1,7 +1,30 @@
|
|
|
1
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
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
26
|
exports.ExplorePage = void 0;
|
|
4
|
-
const semver = require(
|
|
27
|
+
const semver = __importStar(require("semver"));
|
|
5
28
|
const DataSourcePicker_1 = require("../components/DataSourcePicker");
|
|
6
29
|
const GrafanaPage_1 = require("./GrafanaPage");
|
|
7
30
|
const TimeRange_1 = require("../components/TimeRange");
|
|
@@ -26,7 +49,7 @@ class ExplorePage extends GrafanaPage_1.GrafanaPage {
|
|
|
26
49
|
}
|
|
27
50
|
getPanelLocators(suffix, text) {
|
|
28
51
|
const page = this.ctx.page;
|
|
29
|
-
let locator = this.
|
|
52
|
+
let locator = this.getByGrafanaSelector(this.ctx.selectors.components.Panels.Panel.title(suffix), {
|
|
30
53
|
startsWith: true,
|
|
31
54
|
});
|
|
32
55
|
// having to use these selectors is unfortunate, but the Explore page did not use data-testid on the panels before Grafana 10.
|
|
@@ -48,8 +71,8 @@ class ExplorePage extends GrafanaPage_1.GrafanaPage {
|
|
|
48
71
|
* Returns the locator for the query editor row with the given refId
|
|
49
72
|
*/
|
|
50
73
|
getQueryEditorRow(refId) {
|
|
51
|
-
return this.
|
|
52
|
-
has: this.
|
|
74
|
+
return this.getByGrafanaSelector(this.ctx.selectors.components.QueryEditorRows.rows).filter({
|
|
75
|
+
has: this.getByGrafanaSelector(this.ctx.selectors.components.QueryEditorRow.title(refId)),
|
|
53
76
|
});
|
|
54
77
|
}
|
|
55
78
|
/**
|
|
@@ -59,14 +82,14 @@ class ExplorePage extends GrafanaPage_1.GrafanaPage {
|
|
|
59
82
|
const components = this.ctx.selectors.components;
|
|
60
83
|
const responsePromise = this.ctx.page.waitForResponse((resp) => resp.url().includes(this.ctx.selectors.apis.DataSource.query), options);
|
|
61
84
|
try {
|
|
62
|
-
await this.
|
|
85
|
+
await this.getByGrafanaSelector(components.RefreshPicker.runButtonV2).click({
|
|
63
86
|
timeout: 1000,
|
|
64
87
|
});
|
|
65
88
|
}
|
|
66
89
|
catch (_) {
|
|
67
90
|
// handle the case when the run button is hidden behind the "Show more items" button
|
|
68
|
-
await this.
|
|
69
|
-
await this.
|
|
91
|
+
await this.getByGrafanaSelector(components.PageToolbar.item(components.PageToolbar.shotMoreItems)).click();
|
|
92
|
+
await this.getByGrafanaSelector(components.RefreshPicker.runButtonV2).last().click();
|
|
70
93
|
}
|
|
71
94
|
return responsePromise;
|
|
72
95
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Locator, Request, Response } from '@playwright/test';
|
|
2
|
-
import {
|
|
2
|
+
import { getByGrafanaSelectorOptions, NavigateOptions, PluginTestCtx } from '../../types';
|
|
3
3
|
/**
|
|
4
4
|
* Base class for all Grafana pages.
|
|
5
5
|
*
|
|
@@ -10,11 +10,10 @@ export declare abstract class GrafanaPage {
|
|
|
10
10
|
constructor(ctx: PluginTestCtx);
|
|
11
11
|
protected navigate(url: string, options?: NavigateOptions): Promise<void>;
|
|
12
12
|
/**
|
|
13
|
-
* Get a locator
|
|
14
|
-
*
|
|
15
|
-
* @param root optional root locator to search within. If no locator is provided, the page will be used
|
|
13
|
+
* Get a locator based on a Grafana E2E selector. A grafana E2E selector is defined in @grafana/e2e-selectors or in plugin-e2e/src/e2e-selectors.
|
|
14
|
+
* An E2E selector is a string that identifies a specific element in the Grafana UI. The element referencing the E2E selector use the data-testid or aria-label attribute.
|
|
16
15
|
*/
|
|
17
|
-
|
|
16
|
+
getByGrafanaSelector(selector: string, options?: getByGrafanaSelectorOptions): Locator;
|
|
18
17
|
/**
|
|
19
18
|
* Mocks the response of the datasource query call
|
|
20
19
|
* @param json the json response to return
|
|
@@ -21,11 +21,10 @@ class GrafanaPage {
|
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* Get a locator
|
|
25
|
-
*
|
|
26
|
-
* @param root optional root locator to search within. If no locator is provided, the page will be used
|
|
24
|
+
* Get a locator based on a Grafana E2E selector. A grafana E2E selector is defined in @grafana/e2e-selectors or in plugin-e2e/src/e2e-selectors.
|
|
25
|
+
* An E2E selector is a string that identifies a specific element in the Grafana UI. The element referencing the E2E selector use the data-testid or aria-label attribute.
|
|
27
26
|
*/
|
|
28
|
-
|
|
27
|
+
getByGrafanaSelector(selector, options) {
|
|
29
28
|
const startsWith = options?.startsWith ? '^' : '';
|
|
30
29
|
if (selector.startsWith('data-testid')) {
|
|
31
30
|
return (options?.root || this.ctx.page).locator(`[data-testid${startsWith}="${selector}"]`);
|
|
@@ -47,7 +47,7 @@ class PanelEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
47
47
|
}
|
|
48
48
|
getPanelLocator() {
|
|
49
49
|
// only one panel is allowed in the panel edit page, so we don't need to use panel title to locate it
|
|
50
|
-
const locator = this.
|
|
50
|
+
const locator = this.getByGrafanaSelector(this.ctx.selectors.components.Panels.Panel.title(''), {
|
|
51
51
|
startsWith: true,
|
|
52
52
|
});
|
|
53
53
|
// in older versions, the panel selector is added to a child element, so we need to go up two levels to get the wrapper
|
|
@@ -70,7 +70,7 @@ class PanelEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
70
70
|
}
|
|
71
71
|
async toggleTableView() {
|
|
72
72
|
await (0, utils_1.radioButtonSetChecked)(this.ctx.page, 'Table view', true);
|
|
73
|
-
let locator = this.
|
|
73
|
+
let locator = this.getByGrafanaSelector(this.ctx.selectors.components.Panels.Panel.toggleTableViewPanel(''));
|
|
74
74
|
if (semver.lt(this.ctx.grafanaVersion, '10.4.0')) {
|
|
75
75
|
locator = this.ctx.page.getByRole('table');
|
|
76
76
|
}
|
|
@@ -78,7 +78,7 @@ class PanelEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
78
78
|
}
|
|
79
79
|
async untoggleTableView() {
|
|
80
80
|
await (0, utils_1.radioButtonSetChecked)(this.ctx.page, 'Table view', false);
|
|
81
|
-
this.panel = new Panel_1.Panel(this.ctx, this.
|
|
81
|
+
this.panel = new Panel_1.Panel(this.ctx, this.getByGrafanaSelector(this.ctx.selectors.components.Panels.Panel.title(''), { startsWith: true }));
|
|
82
82
|
}
|
|
83
83
|
/**
|
|
84
84
|
* Sets the title of the panel. This method will open the panel options, set the title and close the panel options.
|
|
@@ -88,8 +88,8 @@ class PanelEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
88
88
|
const { OptionsGroup, PanelEditor } = this.ctx.selectors.components;
|
|
89
89
|
await this.collapseSection(OptionsGroup.groupTitle);
|
|
90
90
|
const vizInput = semver.gte(this.ctx.grafanaVersion, '11.0.0')
|
|
91
|
-
? this.
|
|
92
|
-
: this.
|
|
91
|
+
? this.getByGrafanaSelector(PanelEditor.OptionsPane.fieldInput(TITLE))
|
|
92
|
+
: this.getByGrafanaSelector(OptionsGroup.group(OptionsGroup.groupTitle)).locator('input').first();
|
|
93
93
|
await vizInput.fill(titleText);
|
|
94
94
|
await this.ctx.page.keyboard.press('Tab');
|
|
95
95
|
}
|
|
@@ -98,22 +98,22 @@ class PanelEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
98
98
|
*/
|
|
99
99
|
async setVisualization(visualization) {
|
|
100
100
|
// toggle options pane if panel edit is not visible
|
|
101
|
-
const showPanelEditElement = this.
|
|
101
|
+
const showPanelEditElement = this.getByGrafanaSelector('Show options pane');
|
|
102
102
|
const showPanelEditElementCount = await showPanelEditElement.count();
|
|
103
103
|
if (showPanelEditElementCount > 0) {
|
|
104
104
|
await showPanelEditElement.click();
|
|
105
105
|
}
|
|
106
|
-
await this.
|
|
107
|
-
await this.
|
|
108
|
-
await (0, test_1.expect)(this.
|
|
106
|
+
await this.getByGrafanaSelector(this.ctx.selectors.components.PanelEditor.toggleVizPicker).click();
|
|
107
|
+
await this.getByGrafanaSelector(this.ctx.selectors.components.PluginVisualization.item(visualization)).click();
|
|
108
|
+
await (0, test_1.expect)(this.getByGrafanaSelector(this.ctx.selectors.components.PanelEditor.toggleVizPicker), `Could not set visualization to ${visualization}. Ensure the panel is installed.`).toHaveText(visualization);
|
|
109
109
|
}
|
|
110
110
|
/**
|
|
111
111
|
* Expands the section for the given category name. If the section is already expanded, this method does nothing.
|
|
112
112
|
*/
|
|
113
113
|
async collapseSection(categoryName) {
|
|
114
|
-
const section = this.
|
|
114
|
+
const section = this.getByGrafanaSelector(this.ctx.selectors.components.OptionsGroup.group(categoryName));
|
|
115
115
|
await (0, test_1.expect)(section, `Could not find any section for category: ${categoryName}`).toBeVisible();
|
|
116
|
-
const sectionToggle = this.
|
|
116
|
+
const sectionToggle = this.getByGrafanaSelector(this.ctx.selectors.components.OptionsGroup.toggle(categoryName));
|
|
117
117
|
const expandedAttr = (await sectionToggle.getAttribute('aria-expanded')) ?? '';
|
|
118
118
|
if (/false/.test(expandedAttr)) {
|
|
119
119
|
await section.click();
|
|
@@ -123,7 +123,7 @@ class PanelEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
123
123
|
* Returns the name of the visualization currently selected in the panel editor
|
|
124
124
|
*/
|
|
125
125
|
getVisualizationName() {
|
|
126
|
-
return this.
|
|
126
|
+
return this.getByGrafanaSelector(this.ctx.selectors.components.PanelEditor.toggleVizPicker);
|
|
127
127
|
}
|
|
128
128
|
/**
|
|
129
129
|
* Clicks the "Apply" button in the panel editor
|
|
@@ -135,8 +135,8 @@ class PanelEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
135
135
|
* Returns the locator for the query editor row with the given refId
|
|
136
136
|
*/
|
|
137
137
|
getQueryEditorRow(refId) {
|
|
138
|
-
return this.
|
|
139
|
-
has: this.
|
|
138
|
+
return this.getByGrafanaSelector(this.ctx.selectors.components.QueryEditorRows.rows).filter({
|
|
139
|
+
has: this.getByGrafanaSelector(this.ctx.selectors.components.QueryEditorRow.title(refId)),
|
|
140
140
|
});
|
|
141
141
|
}
|
|
142
142
|
/**
|
|
@@ -155,7 +155,7 @@ class PanelEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
155
155
|
const defaultPredicate = (resp) => resp.url().includes(this.ctx.selectors.apis.DataSource.query);
|
|
156
156
|
const responsePromise = this.ctx.page.waitForResponse(options?.waitForResponsePredicateCallback ?? defaultPredicate, options);
|
|
157
157
|
// in older versions of grafana, the refresh button is rendered twice. this is a workaround to click the correct one
|
|
158
|
-
const refreshPanelButton = this.
|
|
158
|
+
const refreshPanelButton = this.getByGrafanaSelector(this.ctx.selectors.components.PanelEditor.General.content).locator(`selector=${this.ctx.selectors.components.RefreshPicker.runButtonV2}`);
|
|
159
159
|
await refreshPanelButton.click();
|
|
160
160
|
return responsePromise;
|
|
161
161
|
}
|
|
@@ -1,7 +1,30 @@
|
|
|
1
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
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
26
|
exports.VariableEditPage = void 0;
|
|
4
|
-
const
|
|
27
|
+
const semver = __importStar(require("semver"));
|
|
5
28
|
const DataSourcePicker_1 = require("../components/DataSourcePicker");
|
|
6
29
|
const GrafanaPage_1 = require("./GrafanaPage");
|
|
7
30
|
class VariableEditPage extends GrafanaPage_1.GrafanaPage {
|
|
@@ -23,17 +46,24 @@ class VariableEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
23
46
|
? Dashboard.Settings.Variables.Edit.url(this.args.dashboard.uid, this.args.id)
|
|
24
47
|
: AddDashboard.Settings.Variables.Edit.url(this.args.id);
|
|
25
48
|
await super.navigate(url, options);
|
|
49
|
+
// In versions before 9.2.0, the variable index is not part of the URL so there's no way to navigate to it directly.
|
|
50
|
+
// Instead, we have to click the nth row in the variable list to navigate to the edit page for a given variable index.
|
|
51
|
+
if (semver.lt(this.ctx.grafanaVersion, '9.2.0') && this.args.id) {
|
|
52
|
+
const list = this.getByGrafanaSelector(this.ctx.selectors.pages.Dashboard.Settings.Variables.List.table).locator('tbody tr');
|
|
53
|
+
const variables = await list.all();
|
|
54
|
+
await variables[Number(this.args.id)].click();
|
|
55
|
+
}
|
|
26
56
|
}
|
|
27
57
|
/**
|
|
28
58
|
* Sets the type of variable in the 'Variable type' dropdown to the given type
|
|
29
59
|
*/
|
|
30
60
|
async setVariableType(type) {
|
|
31
|
-
await this.
|
|
61
|
+
await this.getByGrafanaSelector(this.ctx.selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2)
|
|
32
62
|
.locator('input')
|
|
33
63
|
.fill(type);
|
|
34
64
|
await this.ctx.page.keyboard.press('ArrowDown');
|
|
35
65
|
await this.ctx.page.keyboard.press('Enter');
|
|
36
|
-
await this.
|
|
66
|
+
await this.getByGrafanaSelector(this.ctx.selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2).scrollIntoViewIfNeeded();
|
|
37
67
|
}
|
|
38
68
|
/**
|
|
39
69
|
* Triggers the variable query to run. Note that unlike {@link PanelEditPage.refreshPanel}, this method doesn't
|
|
@@ -44,12 +74,16 @@ class VariableEditPage extends GrafanaPage_1.GrafanaPage {
|
|
|
44
74
|
*/
|
|
45
75
|
async runQuery() {
|
|
46
76
|
// in 9.2.0, the submit button got a new purpose. it no longer submits the form, but instead runs the query
|
|
47
|
-
if (gte(this.ctx.grafanaVersion, '9.2.0')) {
|
|
48
|
-
await this.
|
|
77
|
+
if (semver.gte(this.ctx.grafanaVersion, '9.2.0')) {
|
|
78
|
+
await this.getByGrafanaSelector(this.ctx.selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton).click();
|
|
49
79
|
}
|
|
50
80
|
else {
|
|
51
|
-
// in 9.
|
|
52
|
-
|
|
81
|
+
// in version before 9.2.0, there's no submit button, so instead we click the "Include all" button to trigger the query
|
|
82
|
+
const queryDataRequest = this.waitForQueryDataRequest();
|
|
83
|
+
const includeAllSwitch = this.getByGrafanaSelector(this.ctx.selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch).locator('..');
|
|
84
|
+
await includeAllSwitch.click();
|
|
85
|
+
await queryDataRequest;
|
|
86
|
+
await includeAllSwitch.click();
|
|
53
87
|
}
|
|
54
88
|
}
|
|
55
89
|
}
|
|
@@ -27,10 +27,10 @@ class VariablePage extends GrafanaPage_1.GrafanaPage {
|
|
|
27
27
|
async clickAddNew() {
|
|
28
28
|
const { addVariableCTAV2, addVariableCTAV2Item, newButton } = this.ctx.selectors.pages.Dashboard.Settings.Variables.List;
|
|
29
29
|
if (!this.dashboard?.uid) {
|
|
30
|
-
await this.
|
|
30
|
+
await this.getByGrafanaSelector(addVariableCTAV2(addVariableCTAV2Item)).click();
|
|
31
31
|
}
|
|
32
32
|
else {
|
|
33
|
-
await this.
|
|
33
|
+
await this.getByGrafanaSelector(newButton).click();
|
|
34
34
|
}
|
|
35
35
|
const editIndex = await this.ctx.page.evaluate(() => {
|
|
36
36
|
const urlParams = new URLSearchParams(window.location.search);
|
package/dist/types.d.ts
CHANGED
|
@@ -418,7 +418,7 @@ export type NavigateOptions = {
|
|
|
418
418
|
export type AppPageNavigateOptions = NavigateOptions & {
|
|
419
419
|
path?: string;
|
|
420
420
|
};
|
|
421
|
-
export type
|
|
421
|
+
export type getByGrafanaSelectorOptions = {
|
|
422
422
|
/**
|
|
423
423
|
*Optional root locator to search within. If no locator is provided, the page will be used
|
|
424
424
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grafana/plugin-e2e",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
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": "
|
|
58
|
+
"gitHead": "e6b578f7f1dbd3d405ac150e71b625ed3db5c82b"
|
|
59
59
|
}
|