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

Add E2E tests #349

Merged
merged 2 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
738 changes: 727 additions & 11 deletions provisioning/dashboards/e2e.json

Large diffs are not rendered by default.

157 changes: 146 additions & 11 deletions test/panel.spec.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,165 @@
import { test, expect } from '@grafana/plugin-e2e';
import { TEST_IDS } from '../src/constants/tests';
import { PanelHelper } from './utils';

test.describe('Business Charts Panel', () => {
test('Check grafana version', async ({ grafanaVersion }) => {
console.log('Grafana version: ', grafanaVersion);
expect(grafanaVersion).toEqual(grafanaVersion);
});

test('should display empty chart without data and Bar Chart', async ({ gotoDashboardPage, dashboardPage }) => {
test('Should add empty default chart', async ({ readProvisionedDashboard, gotoDashboardPage }) => {
/**
* Go To E2E dashboard
* Go To Panels dashboard e2e.json
* return dashboardPage
*/
await gotoDashboardPage({ uid: 'fdd5dbe3-794c-4441-9d1c-024a537bbe99' });
const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' });
const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid });

/**
* Find panel by title with chart
* Should be visible
* Add new visualization
*/
await expect(dashboardPage.getPanelByTitle('Bar Chart').locator).toBeVisible();
const editPage = await dashboardPage.addPanel();
await editPage.setVisualization('Business Charts');
await editPage.setPanelTitle('Business Chart Test');
await editPage.backToDashboard();

/**
* Check and compare image
* Should add empty visualization without errors
*/
await expect(dashboardPage.getPanelByTitle('Bar Chart').locator.getByTestId(TEST_IDS.panel.chart)).toHaveScreenshot(
'actual-screenshot.png'
);
const panel = new PanelHelper(dashboardPage, 'Business Chart Test');
await panel.checkIfNoErrors();
await panel.checkPresence();

await panel.compareScreenshot('empty.png');
});

test('Should display error message', async ({ readProvisionedDashboard, gotoDashboardPage }) => {
/**
* Go To Panels dashboard e2e.json
* return dashboardPage
*/
const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' });
const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid });

/**
* Check Presence
*/
const panel = new PanelHelper(dashboardPage, 'Error panel');

await panel.checkIfNoErrors();
await panel.checkPresence();
await panel.checkAlert();
});

test.describe('Chart types', () => {
test('Should display Line Chart', async ({ gotoDashboardPage, readProvisionedDashboard }) => {
/**
* Go To Panels dashboard e2e.json
* return dashboardPage
*/
const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' });
const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid });

/**
* Check Presence
*/
const panel = new PanelHelper(dashboardPage, 'Line Chart (code editor)');

await panel.checkIfNoErrors();
await panel.checkPresence();
await panel.compareScreenshot('line-screenshot.png');
});

test('Should display Radar Chart', async ({ gotoDashboardPage, readProvisionedDashboard }) => {
/**
* Go To Panels dashboard e2e.json
* return dashboardPage
*/
const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' });
const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid });

/**
* Check Presence
*/
const panel = new PanelHelper(dashboardPage, 'Radar Chart (visual editor)');

await panel.checkIfNoErrors();
await panel.checkPresence();
await panel.compareScreenshot('radar-screenshot.png');
});

test('Should display Bar Chart', async ({ gotoDashboardPage, readProvisionedDashboard }) => {
/**
* Go To Panels dashboard e2e.json
* return dashboardPage
*/
const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' });
const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid });

/**
* Check Presence
*/
const panel = new PanelHelper(dashboardPage, 'Bar Chart (code editor)');

await panel.checkIfNoErrors();
await panel.checkPresence();
await panel.compareScreenshot('bar-screenshot.png');
});

test('Should display Boxplot Chart', async ({ gotoDashboardPage, readProvisionedDashboard }) => {
/**
* Go To Panels dashboard e2e.json
* return dashboardPage
*/
const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' });
const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid });

/**
* Check Presence
*/
const panel = new PanelHelper(dashboardPage, 'Boxplot (visual editor)');

await panel.checkIfNoErrors();
await panel.checkPresence();
await panel.compareScreenshot('boxplot-screenshot.png');
});

test('Should display Boxplot Chart code editor', async ({ gotoDashboardPage, readProvisionedDashboard }) => {
/**
* Go To Panels dashboard e2e.json
* return dashboardPage
*/
const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' });
const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid });

/**
* Check Presence
*/
const panel = new PanelHelper(dashboardPage, 'Boxplot (code editor)');

await panel.checkIfNoErrors();
await panel.checkPresence();
await panel.compareScreenshot('boxplot-code-screenshot.png');
});

test('Should display Scatter Chart ', async ({ gotoDashboardPage, readProvisionedDashboard, page }) => {
/**
* Go To Panels dashboard e2e.json
* return dashboardPage
*/
const dashboard = await readProvisionedDashboard({ fileName: 'e2e.json' });
const dashboardPage = await gotoDashboardPage({ uid: dashboard.uid });

/**
* Check Presence
*/
const panel = new PanelHelper(dashboardPage, 'Scatter');

await page.waitForTimeout(500);

await panel.checkIfNoErrors();
await panel.checkPresence();
await panel.compareScreenshot('scatter-screenshot.png');
});
});
});
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions test/utils/charts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Locator } from '@playwright/test';
import { DashboardPage, expect, Panel } from '@grafana/plugin-e2e';
import { TEST_IDS } from '../../src/constants/tests';
import { getLocatorSelectors, LocatorSelectors } from './selectors';

/**
* Panel Helper
*/
export class PanelHelper {
private readonly locator: Locator;
private readonly panel: Panel;
private readonly title: string;
private readonly selectors: LocatorSelectors<typeof TEST_IDS.panel>;

constructor(dashboardPage: DashboardPage, panelTitle: string) {
this.panel = dashboardPage.getPanelByTitle(panelTitle);
this.title = panelTitle;
this.locator = this.panel.locator;
this.selectors = getLocatorSelectors(TEST_IDS.panel)(this.locator);
}

private getMsg(msg: string): string {
return `Panel: ${msg}`;
}

public async checkIfNoErrors() {
return expect(this.panel.getErrorIcon(), this.getMsg('Check If No Errors')).not.toBeVisible();
}

public async checkPresence() {
return expect(this.selectors.chart(), this.getMsg(`Check ${this.title} Presence`)).toBeVisible();
}

public async compareScreenshot(name: string) {
return expect(this.selectors.chart(), this.getMsg(`Check ${this.title} Screenshot`)).toHaveScreenshot(name);
}

public async checkAlert() {
return expect(this.selectors.error(), this.getMsg(`Check ${this.title} Alert`)).toBeVisible();
}
}
2 changes: 2 additions & 0 deletions test/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './selectors';
export * from './charts';
51 changes: 51 additions & 0 deletions test/utils/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Locator } from '@playwright/test';

/**
* Selector
*/
type LocatorSelector<TArgs extends unknown[]> = (...args: TArgs) => ReturnType<() => Locator>;

/**
* Check If Selector Object
*/
type IsSelectorObject<TCandidate> = TCandidate extends {
selector: (...args: unknown[]) => void;
apply: (...args: unknown[]) => void;
}
? TCandidate & { selector: TCandidate['selector']; apply: TCandidate['apply'] }
: never;

/**
* Selectors
*/
export type LocatorSelectors<T> = {
[K in keyof T]: T[K] extends (...args: infer Args) => void
? LocatorSelector<Args>
: T[K] extends IsSelectorObject<T[K]>
? LocatorSelector<Parameters<T[K]['selector']>>
: LocatorSelector<[]>;
};

export const getLocatorSelectors =
<TSelectors extends Record<keyof TSelectors, TSelectors[keyof TSelectors]>>(
selectors: TSelectors
): ((locator: Locator) => LocatorSelectors<TSelectors>) =>
(locator) => {
return Object.entries(selectors).reduce((acc, [key, selector]) => {
const getElement = (...args: unknown[]): Locator => {
const getValue = typeof selector === 'object' && 'selector' in selector! ? selector.selector : selector;
const value = typeof getValue === 'function' ? getValue(...args) : getValue;

if (value.startsWith('data-testid')) {
return locator.getByTestId(value);
}

return locator.getByLabel(value);
};

return {
...acc,
[key]: getElement,
};
}, {} as LocatorSelectors<TSelectors>);
};
Loading