Skip to content

Commit

Permalink
resolve custom selectors locally
Browse files Browse the repository at this point in the history
  • Loading branch information
sunker committed Sep 24, 2024
1 parent 9148e82 commit fa470c8
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 13 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions packages/plugin-e2e/src/fixtures/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { TestFixture } from '@playwright/test';
import { PlaywrightArgs } from '../types';
import { E2ESelectorGroup, resolveSelectors } from '@grafana/e2e-selectors';
import { E2ESelectors, PlaywrightArgs } from '../types';
import { resolveSelectors } from '@grafana/e2e-selectors';
import { resolveSelectors as resolveCustomSelectors } from '../selectors/resolver';

type SelectorFixture = TestFixture<E2ESelectorGroup, PlaywrightArgs>;
type SelectorFixture = TestFixture<E2ESelectors, PlaywrightArgs>;

export const selectors: SelectorFixture = async ({ grafanaVersion }, use) => {
const selectors = resolveSelectors(grafanaVersion);
await use(selectors);
await use({ ...resolveSelectors(grafanaVersion), ...resolveCustomSelectors(grafanaVersion) });
};
2 changes: 1 addition & 1 deletion packages/plugin-e2e/src/models/pages/AnnotationEditPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class AnnotationEditPage extends GrafanaPage {
async goto(options?: NavigateOptions) {
const { Dashboard, AddDashboard } = this.ctx.selectors.pages;
const url = this.args.dashboard?.uid
? Dashboard.Settings.Annotations.Edit.url(this.args.dashboard.uid, this.args.id)
? `${Dashboard.url(this.args.dashboard.uid)}?${Dashboard.Settings.Annotations.Edit.urlParams(this.args.id)}`
: AddDashboard.Settings.Annotations.Edit.url(this.args.id);

await super.navigate(url, options);
Expand Down
5 changes: 2 additions & 3 deletions packages/plugin-e2e/src/models/pages/DashboardPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,9 @@ export class DashboardPage extends GrafanaPage {
* Clicks the buttons to add a new panel and returns the panel edit page for the new panel
*/
async addPanel(): Promise<PanelEditPage> {
const { pages, components } = this.ctx.selectors;
const { pages, components, constants } = this.ctx.selectors;
if (semver.gte(this.ctx.grafanaVersion, '10.0.0')) {
const itemButtonTitle = semver.gte(this.ctx.grafanaVersion, '10.1.0') ? 'Add button' : 'Add panel button';
await this.getByGrafanaSelector(components.PageToolbar.itemButton(itemButtonTitle)).click();
await this.getByGrafanaSelector(components.PageToolbar.itemButton(constants.PageToolBar.itemButtonTitle)).click();
await this.getByGrafanaSelector(pages.AddDashboard.itemButton(pages.AddDashboard.itemButtonAddViz)).click();
} else {
await this.getByGrafanaSelector(pages.AddDashboard.addNewPanel).click();
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-e2e/src/models/pages/VariableEditPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class VariableEditPage extends GrafanaPage {
async goto(options?: NavigateOptions) {
const { Dashboard, AddDashboard } = this.ctx.selectors.pages;
const url = this.args.dashboard?.uid
? Dashboard.Settings.Variables.Edit.url(this.args.dashboard.uid, this.args.id)
? `${Dashboard.url(this.args.dashboard.uid)}?${Dashboard.Settings.Variables.Edit.urlParams(this.args.id)}`
: AddDashboard.Settings.Variables.Edit.url(this.args.id);

await super.navigate(url, options);
Expand Down
100 changes: 100 additions & 0 deletions packages/plugin-e2e/src/selectors/resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as semver from 'semver';
import {
SelectorResolver,
SelectorString,
VersionedFunctionSelector,
VersionedSelectorGroup,
VersionedStringSelector,
} from '@grafana/e2e-selectors';
import { versionedAPIs } from './versionedAPIs';
import { CustomSelectorGroup, SelectorsOf } from './types';
import { versionedConstants } from './versionedConstants';

export function resolveSelectors(grafanaVersion: string): CustomSelectorGroup {
const version = grafanaVersion.replace(/\-.*/, '');

return {
constants: resolveSelectorGroup(versionedConstants, version),
apis: resolveSelectorGroup(versionedAPIs, version),
};
}

function resolveSelectorGroup<T>(group: VersionedSelectorGroup, grafanaVersion: string): SelectorsOf<T> {
const result: Record<string, any> = {};

for (const [key, value] of Object.entries(group)) {
if (isVersionedStringSelector(value)) {
result[key] = resolveStringSelector(value, grafanaVersion);
}

if (isVersionedFunctionSelector(value)) {
result[key] = resolveFunctionSelector(value, grafanaVersion);
}

if (isVersionedSelectorGroup(value)) {
result[key] = resolveSelectorGroup(value, grafanaVersion);
}
}

return result as SelectorsOf<T>;
}

function isVersionedFunctionSelector(
target: VersionedFunctionSelector | VersionedStringSelector | VersionedSelectorGroup
): target is VersionedFunctionSelector {
if (typeof target === 'object') {
const [first] = Object.keys(target);
return !!semver.valid(first) && typeof target[first] === 'function';
}

return false;
}

function isVersionedStringSelector(
target: VersionedFunctionSelector | VersionedStringSelector | VersionedSelectorGroup
): target is VersionedStringSelector {
if (typeof target === 'object') {
const [first] = Object.keys(target);
return !!semver.valid(first) && typeof target[first] === 'string';
}

return false;
}

function isVersionedSelectorGroup(
target: VersionedFunctionSelector | VersionedStringSelector | VersionedSelectorGroup
): target is VersionedSelectorGroup {
if (typeof target === 'object') {
const [first] = Object.keys(target);
return !semver.valid(first);
}

return false;
}

function resolveStringSelector(versionedSelector: VersionedStringSelector, grafanaVersion: string): SelectorString {
let [versionToUse, ...versions] = Object.keys(versionedSelector).sort(semver.rcompare);

for (const version of versions) {
if (semver.gte(version, grafanaVersion)) {
versionToUse = version;
}
}

return versionedSelector[versionToUse];
}

function resolveFunctionSelector(
versionedSelector: VersionedFunctionSelector,
grafanaVersion: string
): SelectorResolver {
let [versionToUse, ...versions] = Object.keys(versionedSelector).sort(semver.rcompare);

for (const version of versions) {
if (semver.gte(version, grafanaVersion)) {
versionToUse = version;
}
}

return versionedSelector[versionToUse];
}
21 changes: 21 additions & 0 deletions packages/plugin-e2e/src/selectors/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {
VersionedFunctionSelector,
SelectorResolver,
VersionedStringSelector,
SelectorString,
} from '@grafana/e2e-selectors';
import { VersionedAPIs } from './versionedAPIs';
import { VersionedConstants } from './versionedConstants';

export type CustomSelectorGroup = {
constants: SelectorsOf<VersionedConstants>;
apis: SelectorsOf<VersionedAPIs>;
};

export type SelectorsOf<T> = {
[Property in keyof T]: T[Property] extends VersionedFunctionSelector
? SelectorResolver
: T[Property] extends VersionedStringSelector
? SelectorString
: SelectorsOf<T[Property]>;
};
48 changes: 48 additions & 0 deletions packages/plugin-e2e/src/selectors/versionedAPIs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { VersionedSelectorGroup } from '@grafana/e2e-selectors';
import { MIN_GRAFANA_VERSION } from '../e2e-selectors/versioned/constants';

export const versionedAPIs = {
Alerting: {
eval: {
[MIN_GRAFANA_VERSION]: '/api/v1/eval',
},
},
DataSource: {
resourcePattern: {
[MIN_GRAFANA_VERSION]: '/api/datasources/*/resources',
},
resourceUIDPattern: {
'9.4.4': '/api/datasources/uid/*/resources',
[MIN_GRAFANA_VERSION]: '/api/datasources/*/resources',
},
queryPattern: {
[MIN_GRAFANA_VERSION]: '*/**/api/ds/query*',
},
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`,
},
datasourceByUID: {
[MIN_GRAFANA_VERSION]: (uid: string) => `/api/datasources/uid/${uid}`,
},
proxy: {
'9.4.0': (uid: string, _: string) => `api/datasources/proxy/uid/${uid}`,
[MIN_GRAFANA_VERSION]: (_: string, id: string) => `/api/datasources/proxy/${id}`,
},
},
Dashboard: {
delete: {
[MIN_GRAFANA_VERSION]: (uid: string) => `/api/dashboards/uid/${uid}`,
},
},
Plugin: {
settings: {
[MIN_GRAFANA_VERSION]: (pluginId: string) => `/api/plugins/${pluginId}/settings`,
},
},
} satisfies VersionedSelectorGroup;

export type VersionedAPIs = typeof versionedAPIs;
13 changes: 13 additions & 0 deletions packages/plugin-e2e/src/selectors/versionedConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { VersionedSelectorGroup } from '@grafana/e2e-selectors';
import { MIN_GRAFANA_VERSION } from '../e2e-selectors/versioned/constants';

export const versionedConstants = {
PageToolBar: {
itemButtonTitle: {
'10.1.0': 'Add button',
[MIN_GRAFANA_VERSION]: 'Add panel button',
}
}
} satisfies VersionedSelectorGroup;

export type VersionedConstants = typeof versionedConstants;
7 changes: 5 additions & 2 deletions packages/plugin-e2e/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { VariablePage } from './models/pages/VariablePage';
import { AlertRuleEditPage } from './models/pages/AlertRuleEditPage';
import { GrafanaAPIClient } from './models/GrafanaAPIClient';
import { E2ESelectorGroup } from '@grafana/e2e-selectors';
import { CustomSelectorGroup } from './selectors/types';

export type PluginOptions = {
/**
Expand Down Expand Up @@ -87,7 +88,7 @@ export type PluginFixture = {
* The E2E selectors to use for the current version of Grafana.
* See https://grafana.com/developers/plugin-tools/e2e-test-a-plugin/selecting-elements#grafana-end-to-end-selectors for more information.
*/
selectors: E2ESelectorGroup;
selectors: E2ESelectors;

/**
* Fixture command that creates a data source via the Grafana API.
Expand Down Expand Up @@ -319,7 +320,7 @@ export type PluginFixture = {
/**
* The context object passed to page object models
*/
export type PluginTestCtx = { grafanaVersion: string; selectors: E2ESelectorGroup; testInfo: TestInfo } & Pick<
export type PluginTestCtx = { grafanaVersion: string; selectors: E2ESelectors; testInfo: TestInfo } & Pick<
PlaywrightTestArgs,
'page' | 'request'
>;
Expand Down Expand Up @@ -662,3 +663,5 @@ export type Visualization =
| 'Worldmap Panel';

export type AlertVariant = 'success' | 'warning' | 'error' | 'info';

export type E2ESelectors = E2ESelectorGroup & CustomSelectorGroup;

0 comments on commit fa470c8

Please sign in to comment.