Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin E2E: Replace magic strings with selectors #597

Merged
merged 21 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions packages/plugin-e2e/src/e2e-selectors/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ export type E2ESelectors = {

export type APIs = {
DataSource: {
resource: string;
healthCheck: string;
resourcePattern: string;
resourceUIDPattern: string;
queryPattern: string;
query: string;
health: (uid: string, id: string) => string;
delete: (uid: string) => string;
};
Dashboard: {
delete: (uid: string) => string;
};
};

Expand Down Expand Up @@ -237,8 +244,10 @@ export type Components = {
};
PageToolbar: {
item: (tooltip: string) => string;
shotMoreItems: string;
container: string;
itemButton: (title: string) => string;
itemButtonTitle: string;
};
QueryEditorToolbarItem: {};
BackButton: {
Expand Down Expand Up @@ -406,6 +415,20 @@ export type Pages = {
addNewPanel: string;
addNewRow: string;
addNewPanelLibrary: string;
itemButtonAddViz: string;
Settings: {
Annotations: {
List: {
url: string;
};
Edit: {
url: (annotationIndex: string) => string;
};
};
Variables: {
url: string;
};
};
};
Dashboard: {
url: (uid: string) => string;
Expand Down Expand Up @@ -467,6 +490,7 @@ export type Pages = {
tableRowDuplicateButtons: (variableName: string) => string;
tableRowRemoveButtons: (variableName: string) => string;
addVariableCTAV2: (variableName: string) => string;
addVariableCTAV2Item: string;
};
Edit: {
General: {
Expand Down
23 changes: 19 additions & 4 deletions packages/plugin-e2e/src/e2e-selectors/versioned/apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,30 @@ import { MIN_GRAFANA_VERSION } from './constants';

export const versionedAPIs = {
DataSource: {
resource: {
resourcePattern: {
[MIN_GRAFANA_VERSION]: '/api/datasources/*/resources',
},
resourceUIDPattern: {
'9.4.4': '/api/datasources/uid/*/resources',
[MIN_GRAFANA_VERSION]: '/api/datasources/*/resources',
},
query: {
queryPattern: {
[MIN_GRAFANA_VERSION]: '*/**/api/ds/query*',
},
healthCheck: {
[MIN_GRAFANA_VERSION]: 'api/datasources/uid/*/health',
query: {
[MIN_GRAFANA_VERSION]: '/api/ds/query',
},
health: {
['9.5.0']: (uid: string, _: string) => `/api/datasources/uid/${uid}/health`,
[MIN_GRAFANA_VERSION]: (_: string, id: string) => `/api/datasources/${id}/health`,
},
delete: {
[MIN_GRAFANA_VERSION]: (uid: string) => `/api/datasources/uid/${uid}`,
},
},
Dashboard: {
delete: {
[MIN_GRAFANA_VERSION]: (uid: string) => `/api/datasources/uid/${uid}`,
},
},
};
12 changes: 11 additions & 1 deletion packages/plugin-e2e/src/e2e-selectors/versioned/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ export const versionedComponents = {
menu: (title: string) => `data-testid Panel menu ${title}`,
containerByTitle: (title: string) => `${title} panel`,
headerCornerInfo: (mode: string) => `Panel header ${mode}`,
status: (status: string) => `data-testid Panel status ${status}`,
status: {
['10.2.0']: (status: string) => `data-testid Panel status ${status}`,
[MIN_GRAFANA_VERSION]: (_: string) => 'Panel status',
},
loadingBar: () => `Panel loading bar`,
HoverWidget: {
container: 'data-testid hover-header-container',
Expand Down Expand Up @@ -275,7 +278,14 @@ export const versionedComponents = {
PageToolbar: {
container: () => '.page-toolbar',
item: (tooltip: string) => `${tooltip}`,
shotMoreItems: {
[MIN_GRAFANA_VERSION]: 'Show more items',
},
itemButton: (title: string) => `data-testid ${title}`,
itemButtonTitle: {
'10.1.0': 'Add button',
[MIN_GRAFANA_VERSION]: 'Add panel button',
},
},
QueryEditorToolbarItem: {
button: (title: string) => `QueryEditor toolbar item button ${title}`,
Expand Down
19 changes: 19 additions & 0 deletions packages/plugin-e2e/src/e2e-selectors/versioned/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ export const versionedPages = {
addNewPanel: 'Add new panel',
addNewRow: 'Add new row',
addNewPanelLibrary: 'Add new panel from panel library',
itemButtonAddViz: {
[MIN_GRAFANA_VERSION]: 'Add new visualization menu item',
},
Settings: {
Annotations: {
List: {
url: '/dashboard/new?orgId=1&editview=annotations',
},
Edit: {
url: (annotationIndex: string) => `/dashboard/new?editview=annotations&editIndex=${annotationIndex}`,
},
},
Variables: {
url: '/dashboard/new?orgId=1&editview=templating',
},
},
},
Dashboard: {
url: (uid: string) => `/d/${uid}`,
Expand Down Expand Up @@ -124,6 +140,9 @@ export const versionedPages = {
tableRowDuplicateButtons: (variableName: string) => `Variable editor Table Duplicate button ${variableName}`,
tableRowRemoveButtons: (variableName: string) => `Variable editor Table Remove button ${variableName}`,
addVariableCTAV2: (name: string) => `data-testid Call to action button ${name}`,
addVariableCTAV2Item: {
[MIN_GRAFANA_VERSION]: 'Add variable',
},
},
Edit: {
General: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export const createDataSourceViaAPI = async (
const text = await createDsReq.text();
const status = await createDsReq.status();
if (status === 200) {
console.log('Data source created: ', name);
return createDsReq.json().then((r) => r.datasource);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const createDataSourceConfigPage: CreateDataSourceConfigPageFixture = async (
await use(async (args) => {
deleteDataSource = args.deleteDataSourceAfterTest ?? true;
const datasource = await createDataSourceViaAPI(request, args);
datasourceConfigPage = new DataSourceConfigPage({ page, selectors, grafanaVersion, request }, datasource.uid);
datasourceConfigPage = new DataSourceConfigPage({ page, selectors, grafanaVersion, request }, datasource);
await datasourceConfigPage.goto();
return datasourceConfigPage;
});
Expand Down
5 changes: 4 additions & 1 deletion packages/plugin-e2e/src/models/AnnotationEditPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ export class AnnotationEditPage extends GrafanaPage {
* @param options - Optional. RequestOptions to pass to waitForResponse
*/
async runQuery(options?: RequestOptions) {
const responsePromise = this.ctx.page.waitForResponse((resp) => resp.url().includes('/query'), options);
const responsePromise = this.ctx.page.waitForResponse(
(resp) => resp.url().includes(this.ctx.selectors.apis.DataSource.query),
options
);
//TODO: add new selector and use it in grafana/ui
await this.ctx.page.getByRole('button', { name: 'TEST' }).click();
return responsePromise;
Expand Down
10 changes: 8 additions & 2 deletions packages/plugin-e2e/src/models/AnnotationPage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as semver from 'semver';
import { PluginTestCtx } from '../types';
import { AnnotationEditPage } from './AnnotationEditPage';
import { GrafanaPage } from './GrafanaPage';
Expand All @@ -8,14 +9,19 @@ export class AnnotationPage extends GrafanaPage {
}

async goto() {
await this.ctx.page.goto('/dashboard/new?orgId=1&editview=annotations', {
await this.ctx.page.goto(this.ctx.selectors.pages.AddDashboard.Settings.Annotations.List.url, {
waitUntil: 'networkidle',
});
}

async clickAddNew() {
const { Dashboard } = this.ctx.selectors.pages;
this.getByTestIdOrAriaLabel(Dashboard.Settings.Annotations.List.addAnnotationCTAV2).click();

if (semver.gte(this.ctx.grafanaVersion, '8.3.0')) {
await this.getByTestIdOrAriaLabel(Dashboard.Settings.Annotations.List.addAnnotationCTAV2).click();
} else {
await this.getByTestIdOrAriaLabel(Dashboard.Settings.Annotations.List.addAnnotationCTA).click();
}
return new AnnotationEditPage(this.ctx);
}
}
11 changes: 5 additions & 6 deletions packages/plugin-e2e/src/models/DashboardPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,21 @@ export class DashboardPage extends GrafanaPage {
}

async addPanel(): Promise<PanelEditPage> {
const { components, pages } = this.ctx.selectors;
if (gte(this.ctx.grafanaVersion, '10.0.0')) {
//TODO: add new selector and use it in grafana/ui
const title = gte(this.ctx.grafanaVersion, '10.1.0') ? 'Add button' : 'Add panel button';
await this.getByTestIdOrAriaLabel(this.ctx.selectors.components.PageToolbar.itemButton(title)).click();
await this.getByTestIdOrAriaLabel(
this.ctx.selectors.pages.AddDashboard.itemButton('Add new visualization menu item')
components.PageToolbar.itemButton(components.PageToolbar.itemButtonTitle)
).click();
await this.getByTestIdOrAriaLabel(pages.AddDashboard.itemButton(pages.AddDashboard.itemButtonAddViz)).click();
} else {
await this.getByTestIdOrAriaLabel(this.ctx.selectors.pages.AddDashboard.addNewPanel).click();
await this.getByTestIdOrAriaLabel(pages.AddDashboard.addNewPanel).click();
}

return new PanelEditPage(this.ctx);
}

async deleteDashboard() {
await this.ctx.request.delete(`/api/datasources/uid/${this.dashboardUid}`);
await this.ctx.request.delete(this.ctx.selectors.apis.Dashboard.delete(this.dashboardUid));
}

async refreshDashboard() {
Expand Down
19 changes: 13 additions & 6 deletions packages/plugin-e2e/src/models/DataSourceConfigPage.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { PluginTestCtx } from '../types';
import { DataSource, PluginTestCtx } from '../types';
import { GrafanaPage } from './GrafanaPage';

export class DataSourceConfigPage extends GrafanaPage {
constructor(ctx: PluginTestCtx, private uid: string) {
constructor(ctx: PluginTestCtx, private datasource: DataSource) {
super(ctx);
}

async deleteDataSource() {
await this.ctx.request.delete(`/api/datasources/uid/${this.uid}`);
await this.ctx.request.delete(this.ctx.selectors.apis.DataSource.delete(this.datasource.uid));
}

async goto() {
await this.ctx.page.goto(this.ctx.selectors.pages.EditDataSource.url(this.uid), {
await this.ctx.page.goto(this.ctx.selectors.pages.EditDataSource.url(this.datasource.uid), {
waitUntil: 'load',
});
}
Expand All @@ -22,13 +22,20 @@ export class DataSourceConfigPage extends GrafanaPage {
* @param status the HTTP status code to return. Defaults to 200
*/
async mockHealthCheckResponse<T = any>(json: T, status = 200) {
await this.ctx.page.route(`${this.ctx.selectors.apis.DataSource.healthCheck}`, async (route) => {
await this.ctx.page.route(`${this.ctx.selectors.apis.DataSource.health}`, async (route) => {
await route.fulfill({ json, status });
});
}

async saveAndTest() {
const responsePromise = this.ctx.page.waitForResponse((resp) =>
resp
.url()
.includes(
this.ctx.selectors.apis.DataSource.health(this.datasource.uid ?? '', this.datasource.id.toString() ?? '')
)
);
await this.getByTestIdOrAriaLabel(this.ctx.selectors.pages.DataSource.saveAndTest).click();
return this.ctx.page.waitForResponse((resp) => resp.url().includes('/health'));
return responsePromise;
}
}
14 changes: 7 additions & 7 deletions packages/plugin-e2e/src/models/ExplorePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { DataSourcePicker } from './DataSourcePicker';
import { GrafanaPage } from './GrafanaPage';
import { TimeRange } from './TimeRange';

const SHOW_MORE_ITEMS = 'Show more items';

export class ExplorePage extends GrafanaPage {
datasource: DataSourcePicker;
timeRange: any;
Expand All @@ -20,24 +18,26 @@ export class ExplorePage extends GrafanaPage {
}

async getQueryEditorRow(refId: string): Promise<Locator> {
//TODO: add new selector and use it in grafana/ui
const locator = this.ctx.page.locator('[aria-label="Query editor row"]').filter({
has: this.ctx.page.locator(`[aria-label="Query editor row title ${refId}"]`),
const locator = this.getByTestIdOrAriaLabel(this.ctx.selectors.components.QueryEditorRows.rows).filter({
has: this.getByTestIdOrAriaLabel(this.ctx.selectors.components.QueryEditorRow.title(refId)),
});
await expect(locator).toBeVisible();
return locator;
}

async runQuery(options?: RequestOptions) {
const components = this.ctx.selectors.components;
const responsePromise = this.ctx.page.waitForResponse((resp) => resp.url().includes('/api/ds/query'), options);
const responsePromise = this.ctx.page.waitForResponse(
(resp) => resp.url().includes(this.ctx.selectors.apis.DataSource.query),
options
);
try {
await this.getByTestIdOrAriaLabel(components.RefreshPicker.runButtonV2).click({
timeout: 1000,
});
} catch (_) {
// handle the case when the run button is hidden behind the "Show more items" button
await this.getByTestIdOrAriaLabel(components.PageToolbar.item(SHOW_MORE_ITEMS)).click();
await this.getByTestIdOrAriaLabel(components.PageToolbar.item(components.PageToolbar.shotMoreItems)).click();
await this.getByTestIdOrAriaLabel(components.RefreshPicker.runButtonV2).last().click();
}
return responsePromise;
Expand Down
8 changes: 4 additions & 4 deletions packages/plugin-e2e/src/models/GrafanaPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export abstract class GrafanaPage {
* @param status the HTTP status code to return. Defaults to 200
*/
async mockQueryDataResponse<T = any>(json: T, status = 200) {
await this.ctx.page.route('*/**/api/ds/query*', async (route) => {
await this.ctx.page.route(this.ctx.selectors.apis.DataSource.queryPattern, async (route) => {
await route.fulfill({ json, status });
});
}
Expand All @@ -50,11 +50,11 @@ export abstract class GrafanaPage {
* @param status the HTTP status code to return. Defaults to 200
*/
async mockResourceResponse<T = any>(path: string, json: T, status = 200) {
await this.ctx.page.route(`${this.ctx.selectors.apis.DataSource.resource}/${path}`, async (route) => {
await this.ctx.page.route(`${this.ctx.selectors.apis.DataSource.resourceUIDPattern}/${path}`, async (route) => {
await route.fulfill({ json, status });
});
// some data sources use the backendSrv directly, and then the path may be different
await this.ctx.page.route(`/api/datasources/*/resources/${path}`, async (route) => {
await this.ctx.page.route(`${this.ctx.selectors.apis.DataSource.resourcePattern}/${path}`, async (route) => {
await route.fulfill({ json, status });
});
}
Expand All @@ -66,7 +66,7 @@ export abstract class GrafanaPage {
*/
async waitForQueryDataRequest(cb?: (request: Request) => boolean | Promise<boolean>) {
return this.ctx.page.waitForRequest((request) => {
if (request.url().includes('api/ds/query') && request.method() === 'POST') {
if (request.url().includes(this.ctx.selectors.apis.DataSource.query) && request.method() === 'POST') {
return cb ? cb(request) : true;
}
return false;
Expand Down
Loading
Loading