mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
e2eTests: Adds cleanup of created datasource and dashboard (#20244)
* e2eTests: Adds cleanup of created datasource and dashboard * Chore: Fixes Prettier error * Tests: Updates snapshots
This commit is contained in:
parent
f2415a319e
commit
85c4311c9f
@ -63,7 +63,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form-button-row">
|
||||
<button class="btn btn-danger" ng-click="ctrl.deleteDashboard()" ng-show="ctrl.canDelete">
|
||||
<button class="btn btn-danger" ng-click="ctrl.deleteDashboard()" ng-show="ctrl.canDelete" aria-label="Dashboard settings page delete dashboard button">
|
||||
Delete Dashboard
|
||||
</button>
|
||||
</div>
|
||||
|
@ -19,7 +19,7 @@ export class DataSourcesListItem extends PureComponent<Props> {
|
||||
<img src={dataSource.typeLogoUrl} alt={dataSource.name} />
|
||||
</figure>
|
||||
<div className="card-item-details">
|
||||
<div className="card-item-name">
|
||||
<div className="card-item-name" aria-label={`Data source list item for ${dataSource.name}`}>
|
||||
{dataSource.name}
|
||||
{dataSource.isDefault && <span className="btn btn-secondary btn-small card-item-label">default</span>}
|
||||
</div>
|
||||
|
@ -32,6 +32,7 @@ exports[`Render should render component 1`] = `
|
||||
className="card-item-details"
|
||||
>
|
||||
<div
|
||||
aria-label="Data source list item for gdev-cloudwatch"
|
||||
className="card-item-name"
|
||||
>
|
||||
gdev-cloudwatch
|
||||
|
@ -10,7 +10,7 @@ export interface Props {
|
||||
|
||||
const BasicSettings: FC<Props> = ({ dataSourceName, isDefault, onDefaultChange, onNameChange }) => {
|
||||
return (
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form-group" aria-label="Datasource settings page basic settings">
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30" style={{ marginRight: '3px' }}>
|
||||
<FormLabel
|
||||
@ -28,11 +28,17 @@ const BasicSettings: FC<Props> = ({ dataSourceName, isDefault, onDefaultChange,
|
||||
placeholder="Name"
|
||||
onChange={event => onNameChange(event.target.value)}
|
||||
required
|
||||
aria-label="Datasource settings page name input field"
|
||||
/>
|
||||
</div>
|
||||
{/*
|
||||
//@ts-ignore */}
|
||||
<Switch label="Default" checked={isDefault} onChange={event => onDefaultChange(event.target.checked)} />
|
||||
<Switch
|
||||
label="Default"
|
||||
checked={isDefault}
|
||||
onChange={event => {
|
||||
// @ts-ignore
|
||||
onDefaultChange(event.target.checked);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -27,7 +27,13 @@ const ButtonRow: FC<Props> = ({ isReadOnly, onDelete, onSubmit, onTest }) => {
|
||||
Test
|
||||
</button>
|
||||
)}
|
||||
<button type="submit" className="btn btn-danger" disabled={isReadOnly} onClick={onDelete}>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-danger"
|
||||
disabled={isReadOnly}
|
||||
onClick={onDelete}
|
||||
aria-label="Delete button"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<a className="btn btn-inverse" href={`${config.appSubUrl}/datasources`}>
|
||||
|
@ -3,36 +3,31 @@ import React, { PureComponent } from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { connect } from 'react-redux';
|
||||
import isString from 'lodash/isString';
|
||||
|
||||
// Components
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { PluginSettings, GenericDataSourcePlugin } from './PluginSettings';
|
||||
import { GenericDataSourcePlugin, PluginSettings } from './PluginSettings';
|
||||
import BasicSettings from './BasicSettings';
|
||||
import ButtonRow from './ButtonRow';
|
||||
|
||||
// Services & Utils
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
|
||||
// Actions & selectors
|
||||
import { getDataSource, getDataSourceMeta } from '../state/selectors';
|
||||
import {
|
||||
dataSourceLoaded,
|
||||
deleteDataSource,
|
||||
loadDataSource,
|
||||
setDataSourceName,
|
||||
setIsDefault,
|
||||
updateDataSource,
|
||||
dataSourceLoaded,
|
||||
} from '../state/actions';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { getRouteParamsId } from 'app/core/selectors/location';
|
||||
|
||||
// Types
|
||||
import { StoreState, CoreEvents } from 'app/types/';
|
||||
import { CoreEvents, StoreState } from 'app/types/';
|
||||
import { UrlQueryMap } from '@grafana/runtime';
|
||||
import { DataSourceSettings, DataSourcePluginMeta } from '@grafana/data';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { DataSourcePluginMeta, DataSourceSettings, NavModel } from '@grafana/data';
|
||||
import { getDataSourceLoadingNav } from '../state/navModel';
|
||||
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
|
||||
import { importDataSourcePlugin } from 'app/features/plugins/plugin_loader';
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<div
|
||||
aria-label="Datasource settings page basic settings"
|
||||
className="gf-form-group"
|
||||
>
|
||||
<div
|
||||
@ -21,6 +22,7 @@ exports[`Render should render component 1`] = `
|
||||
Name
|
||||
</Component>
|
||||
<Input
|
||||
aria-label="Datasource settings page name input field"
|
||||
className="gf-form-input max-width-23"
|
||||
onChange={[Function]}
|
||||
placeholder="Name"
|
||||
|
@ -12,6 +12,7 @@ exports[`Render should render component 1`] = `
|
||||
Test
|
||||
</button>
|
||||
<button
|
||||
aria-label="Delete button"
|
||||
className="btn btn-danger"
|
||||
disabled={true}
|
||||
onClick={[MockFunction]}
|
||||
@ -42,6 +43,7 @@ exports[`Render should render with buttons enabled 1`] = `
|
||||
Save & Test
|
||||
</button>
|
||||
<button
|
||||
aria-label="Delete button"
|
||||
className="btn btn-danger"
|
||||
disabled={false}
|
||||
onClick={[MockFunction]}
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
<div class="confirm-modal-buttons">
|
||||
<button ng-show="onAltAction" type="button" class="btn btn-primary" ng-click="dismiss();onAltAction();">{{altActionText}}</button>
|
||||
<button ng-show="onConfirm" type="button" class="btn btn-danger" ng-click="onConfirm();dismiss();" ng-disabled="!confirmTextValid" give-focus="true">{{yesText}}</button>
|
||||
<button ng-show="onConfirm" type="button" class="btn btn-danger" ng-click="onConfirm();dismiss();" ng-disabled="!confirmTextValid" give-focus="true" aria-label="Confirm Modal Danger Button">{{yesText}}</button>
|
||||
<button type="button" class="btn btn-inverse" ng-click="dismiss()">{{noText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
11
public/e2e-test/pages/dashboards/dashboardPage.ts
Normal file
11
public/e2e-test/pages/dashboards/dashboardPage.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { ClickablePageObject, ClickablePageObjectType, Selector, TestPage } from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface DashboardPage {
|
||||
settings: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const dashboardPage = new TestPage<DashboardPage>({
|
||||
pageObjects: {
|
||||
settings: new ClickablePageObject(Selector.fromAriaLabel('Dashboard settings navbar button')),
|
||||
},
|
||||
});
|
11
public/e2e-test/pages/dashboards/dashboardSettingsPage.ts
Normal file
11
public/e2e-test/pages/dashboards/dashboardSettingsPage.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { ClickablePageObject, ClickablePageObjectType, Selector, TestPage } from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface DashboardSettingsPage {
|
||||
deleteDashBoard: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const dashboardSettingsPage = new TestPage<DashboardSettingsPage>({
|
||||
pageObjects: {
|
||||
deleteDashBoard: new ClickablePageObject(Selector.fromAriaLabel('Dashboard settings page delete dashboard button')),
|
||||
},
|
||||
});
|
@ -1,8 +1,13 @@
|
||||
import { TestPage } from '@grafana/toolkit/src/e2e';
|
||||
import { ClickablePageObject, ClickablePageObjectType, Selector, TestPage } from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface DataSourcesPage {}
|
||||
export interface DataSourcesPage {
|
||||
testData: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const dataSourcesPage = new TestPage<DataSourcesPage>({
|
||||
url: '/datasources',
|
||||
pageObjects: {},
|
||||
});
|
||||
export const dataSourcesPageFactory = (testDataSourceName: string) =>
|
||||
new TestPage<DataSourcesPage>({
|
||||
url: '/datasources',
|
||||
pageObjects: {
|
||||
testData: new ClickablePageObject(Selector.fromAriaLabel(`Data source list item for ${testDataSourceName}`)),
|
||||
},
|
||||
});
|
||||
|
@ -1,13 +1,18 @@
|
||||
import {
|
||||
TestPage,
|
||||
ClickablePageObjectType,
|
||||
PageObjectType,
|
||||
ClickablePageObject,
|
||||
ClickablePageObjectType,
|
||||
InputPageObject,
|
||||
InputPageObjectType,
|
||||
PageObject,
|
||||
PageObjectType,
|
||||
Selector,
|
||||
TestPage,
|
||||
} from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface EditDataSourcePage {
|
||||
name: InputPageObjectType;
|
||||
default: ClickablePageObjectType;
|
||||
delete: ClickablePageObjectType;
|
||||
saveAndTest: ClickablePageObjectType;
|
||||
alert: PageObjectType;
|
||||
alertMessage: PageObjectType;
|
||||
@ -15,6 +20,11 @@ export interface EditDataSourcePage {
|
||||
|
||||
export const editDataSourcePage = new TestPage<EditDataSourcePage>({
|
||||
pageObjects: {
|
||||
name: new InputPageObject(Selector.fromAriaLabel('Datasource settings page name input field')),
|
||||
default: new ClickablePageObject(
|
||||
Selector.fromSelector('[aria-label="Datasource settings page basic settings"] .gf-form-switch')
|
||||
),
|
||||
delete: new ClickablePageObject(Selector.fromAriaLabel('Delete button')),
|
||||
saveAndTest: new ClickablePageObject(Selector.fromAriaLabel('Save and Test button')),
|
||||
alert: new PageObject(Selector.fromAriaLabel('Datasource settings page Alert')),
|
||||
alertMessage: new PageObject(Selector.fromAriaLabel('Datasource settings page Alert message')),
|
||||
|
13
public/e2e-test/pages/modals/confirmModal.ts
Normal file
13
public/e2e-test/pages/modals/confirmModal.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { ClickablePageObject, ClickablePageObjectType, PageObject, Selector, TestPage } from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface ConfirmModal {
|
||||
delete: ClickablePageObjectType;
|
||||
success: PageObject;
|
||||
}
|
||||
|
||||
export const confirmModal = new TestPage<ConfirmModal>({
|
||||
pageObjects: {
|
||||
delete: new ClickablePageObject(Selector.fromAriaLabel('Confirm Modal Danger Button')),
|
||||
success: new PageObject(Selector.fromSelector('.alert-success')),
|
||||
},
|
||||
});
|
@ -1,84 +1,142 @@
|
||||
import { Browser, Page, Target } from 'puppeteer-core';
|
||||
|
||||
import { e2eScenario, constants, takeScreenShot, compareScreenShots } from '@grafana/toolkit/src/e2e';
|
||||
import { compareScreenShots, constants, e2eScenario, takeScreenShot } from '@grafana/toolkit/src/e2e';
|
||||
import { addDataSourcePage } from 'e2e-test/pages/datasources/addDataSourcePage';
|
||||
import { editDataSourcePage } from 'e2e-test/pages/datasources/editDataSourcePage';
|
||||
import { dataSourcesPage } from 'e2e-test/pages/datasources/dataSources';
|
||||
import { dataSourcesPageFactory } from 'e2e-test/pages/datasources/dataSources';
|
||||
import { createDashboardPage } from 'e2e-test/pages/dashboards/createDashboardPage';
|
||||
import { saveDashboardModal } from 'e2e-test/pages/dashboards/saveDashboardModal';
|
||||
import { dashboardsPageFactory } from 'e2e-test/pages/dashboards/dashboardsPage';
|
||||
import { panel } from 'e2e-test/pages/panels/panel';
|
||||
import { editPanelPage } from 'e2e-test/pages/panels/editPanel';
|
||||
import { sharePanelModal } from 'e2e-test/pages/panels/sharePanelModal';
|
||||
import { confirmModal } from '../pages/modals/confirmModal';
|
||||
import { dashboardPage } from '../pages/dashboards/dashboardPage';
|
||||
import { dashboardSettingsPage } from '../pages/dashboards/dashboardSettingsPage';
|
||||
|
||||
e2eScenario(
|
||||
'Login scenario, create test data source, dashboard, panel, and export scenario',
|
||||
'should pass',
|
||||
async (browser: Browser, page: Page) => {
|
||||
// Add TestData DB
|
||||
await addDataSourcePage.init(page);
|
||||
await addDataSourcePage.navigateTo();
|
||||
await addDataSourcePage.pageObjects.testDataDB.exists();
|
||||
await addDataSourcePage.pageObjects.testDataDB.click();
|
||||
const addTestDataSourceAndVerify = async (page: Page) => {
|
||||
// Add TestData DB
|
||||
const testDataSourceName = `TestData-${new Date().getTime()}`;
|
||||
await addDataSourcePage.init(page);
|
||||
await addDataSourcePage.navigateTo();
|
||||
await addDataSourcePage.pageObjects.testDataDB.exists();
|
||||
await addDataSourcePage.pageObjects.testDataDB.click();
|
||||
|
||||
await editDataSourcePage.init(page);
|
||||
await editDataSourcePage.waitForNavigation();
|
||||
await editDataSourcePage.pageObjects.saveAndTest.click();
|
||||
await editDataSourcePage.pageObjects.alert.exists();
|
||||
await editDataSourcePage.pageObjects.alertMessage.containsText('Data source is working');
|
||||
await editDataSourcePage.init(page);
|
||||
await editDataSourcePage.waitForNavigation();
|
||||
await editDataSourcePage.pageObjects.name.enter(testDataSourceName);
|
||||
await editDataSourcePage.pageObjects.default.click();
|
||||
await editDataSourcePage.pageObjects.saveAndTest.click();
|
||||
await editDataSourcePage.pageObjects.alert.exists();
|
||||
await editDataSourcePage.pageObjects.alertMessage.containsText('Data source is working');
|
||||
|
||||
// Verify that data source is listed
|
||||
const url = await editDataSourcePage.getUrlWithoutBaseUrl();
|
||||
const expectedUrl = url.substring(1, url.length - 1);
|
||||
const selector = `a[href="${expectedUrl}"]`;
|
||||
// Verify that data source is listed
|
||||
const url = await editDataSourcePage.getUrlWithoutBaseUrl();
|
||||
const expectedUrl = url.substring(1, url.length - 1);
|
||||
const selector = `a[href="${expectedUrl}"]`;
|
||||
|
||||
await dataSourcesPage.init(page);
|
||||
await dataSourcesPage.navigateTo();
|
||||
await dataSourcesPage.expectSelector({ selector });
|
||||
const dataSourcesPage = dataSourcesPageFactory(testDataSourceName);
|
||||
await dataSourcesPage.init(page);
|
||||
await dataSourcesPage.navigateTo();
|
||||
await dataSourcesPage.expectSelector({ selector });
|
||||
|
||||
// Create a new Dashboard
|
||||
await createDashboardPage.init(page);
|
||||
await createDashboardPage.navigateTo();
|
||||
await createDashboardPage.pageObjects.addQuery.click();
|
||||
return testDataSourceName;
|
||||
};
|
||||
|
||||
await editPanelPage.init(page);
|
||||
await editPanelPage.waitForNavigation();
|
||||
await editPanelPage.pageObjects.queriesTab.click();
|
||||
await editPanelPage.pageObjects.scenarioSelect.select('string:csv_metric_values');
|
||||
await editPanelPage.pageObjects.visualizationTab.click();
|
||||
await editPanelPage.pageObjects.showXAxis.click();
|
||||
await editPanelPage.pageObjects.saveDashboard.click();
|
||||
const addDashboardAndSetupTestDataGraph = async (page: Page) => {
|
||||
// Create a new Dashboard
|
||||
const dashboardTitle = `Dashboard-${new Date().getTime()}`;
|
||||
await createDashboardPage.init(page);
|
||||
await createDashboardPage.navigateTo();
|
||||
await createDashboardPage.pageObjects.addQuery.click();
|
||||
|
||||
// Confirm save modal
|
||||
await saveDashboardModal.init(page);
|
||||
await saveDashboardModal.expectSelector({ selector: 'save-dashboard-as-modal' });
|
||||
const dashboardTitle = new Date().toISOString();
|
||||
await saveDashboardModal.pageObjects.name.enter(dashboardTitle);
|
||||
await saveDashboardModal.pageObjects.save.click();
|
||||
await saveDashboardModal.pageObjects.success.exists();
|
||||
await editPanelPage.init(page);
|
||||
await editPanelPage.waitForNavigation();
|
||||
await editPanelPage.pageObjects.queriesTab.click();
|
||||
await editPanelPage.pageObjects.scenarioSelect.select('string:csv_metric_values');
|
||||
await editPanelPage.pageObjects.visualizationTab.click();
|
||||
await editPanelPage.pageObjects.showXAxis.click();
|
||||
await editPanelPage.pageObjects.saveDashboard.click();
|
||||
|
||||
// Share the dashboard
|
||||
const dashboardsPage = dashboardsPageFactory(dashboardTitle);
|
||||
await dashboardsPage.init(page);
|
||||
await dashboardsPage.navigateTo();
|
||||
await dashboardsPage.pageObjects.dashboard.exists();
|
||||
await dashboardsPage.pageObjects.dashboard.click();
|
||||
// Confirm save modal
|
||||
await saveDashboardModal.init(page);
|
||||
await saveDashboardModal.expectSelector({ selector: 'save-dashboard-as-modal' });
|
||||
await saveDashboardModal.pageObjects.name.enter(dashboardTitle);
|
||||
await saveDashboardModal.pageObjects.save.click();
|
||||
await saveDashboardModal.pageObjects.success.exists();
|
||||
|
||||
await panel.init(page);
|
||||
await panel.pageObjects.panelTitle.click();
|
||||
await panel.pageObjects.share.click();
|
||||
return dashboardTitle;
|
||||
};
|
||||
|
||||
// Verify that a new tab is opened
|
||||
const targetPromise = new Promise(resolve => browser.once('targetcreated', resolve));
|
||||
await sharePanelModal.init(page);
|
||||
await sharePanelModal.pageObjects.directLinkRenderedImage.click();
|
||||
const newTarget: Target = (await targetPromise) as Target;
|
||||
expect(newTarget.url()).toContain(`${constants.baseUrl}/render/d-solo`);
|
||||
const clickOnSharePanelImageLinkAndCompareImages = async (browser: Browser, page: Page, dashboardTitle: string) => {
|
||||
// Share the dashboard
|
||||
const dashboardsPage = dashboardsPageFactory(dashboardTitle);
|
||||
await dashboardsPage.init(page);
|
||||
await dashboardsPage.navigateTo();
|
||||
await dashboardsPage.pageObjects.dashboard.exists();
|
||||
await dashboardsPage.pageObjects.dashboard.click();
|
||||
|
||||
// Take snapshot of page
|
||||
await panel.init(page);
|
||||
await panel.pageObjects.panelTitle.click();
|
||||
await panel.pageObjects.share.click();
|
||||
|
||||
// Verify that a new tab is opened
|
||||
const targetPromise = new Promise(resolve => browser.once('targetcreated', resolve));
|
||||
await sharePanelModal.init(page);
|
||||
await sharePanelModal.pageObjects.directLinkRenderedImage.click();
|
||||
const newTarget: Target = (await targetPromise) as Target;
|
||||
expect(newTarget.url()).toContain(`${constants.baseUrl}/render/d-solo`);
|
||||
|
||||
// Take snapshot of page only when running on CircleCI
|
||||
if (process.env.CIRCLE_SHA1) {
|
||||
const newPage = await newTarget.page();
|
||||
const fileName = 'smoke-test-scenario';
|
||||
await takeScreenShot(newPage, fileName);
|
||||
await compareScreenShots(fileName);
|
||||
}
|
||||
};
|
||||
|
||||
const cleanUpTestDataSource = async (page: Page, testDataSourceName: string) => {
|
||||
const dataSourcesPage = dataSourcesPageFactory(testDataSourceName);
|
||||
await dataSourcesPage.init(page);
|
||||
await dataSourcesPage.navigateTo();
|
||||
await dataSourcesPage.pageObjects.testData.click();
|
||||
|
||||
await editDataSourcePage.init(page);
|
||||
await editDataSourcePage.pageObjects.delete.exists();
|
||||
await editDataSourcePage.pageObjects.delete.click();
|
||||
|
||||
await confirmModal.init(page);
|
||||
await confirmModal.pageObjects.delete.click();
|
||||
await confirmModal.pageObjects.success.exists();
|
||||
};
|
||||
|
||||
const cleanDashboard = async (page: Page, dashboardTitle: string) => {
|
||||
const dashboardsPage = dashboardsPageFactory(dashboardTitle);
|
||||
await dashboardsPage.init(page);
|
||||
await dashboardsPage.navigateTo();
|
||||
await dashboardsPage.pageObjects.dashboard.exists();
|
||||
await dashboardsPage.pageObjects.dashboard.click();
|
||||
|
||||
await dashboardPage.init(page);
|
||||
await dashboardPage.pageObjects.settings.click();
|
||||
|
||||
await dashboardSettingsPage.init(page);
|
||||
await dashboardSettingsPage.pageObjects.deleteDashBoard.click();
|
||||
|
||||
await confirmModal.init(page);
|
||||
await confirmModal.pageObjects.delete.click();
|
||||
await confirmModal.pageObjects.success.exists();
|
||||
};
|
||||
|
||||
e2eScenario(
|
||||
'Login scenario, create test data source, dashboard, panel, and export scenario',
|
||||
'should pass',
|
||||
async (browser: Browser, page: Page) => {
|
||||
const testDataSourceName = await addTestDataSourceAndVerify(page);
|
||||
const dashboardTitle = await addDashboardAndSetupTestDataGraph(page);
|
||||
await clickOnSharePanelImageLinkAndCompareImages(browser, page, dashboardTitle);
|
||||
await cleanUpTestDataSource(page, testDataSourceName);
|
||||
await cleanDashboard(page, dashboardTitle);
|
||||
}
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user