diff --git a/public/app/features/dashboard/components/DashboardSettings/template.html b/public/app/features/dashboard/components/DashboardSettings/template.html index b630c6e81bd..d94632a8253 100644 --- a/public/app/features/dashboard/components/DashboardSettings/template.html +++ b/public/app/features/dashboard/components/DashboardSettings/template.html @@ -63,7 +63,7 @@
-
diff --git a/public/app/features/datasources/DataSourcesListItem.tsx b/public/app/features/datasources/DataSourcesListItem.tsx index dc6f50ebf4a..e5490507bbc 100644 --- a/public/app/features/datasources/DataSourcesListItem.tsx +++ b/public/app/features/datasources/DataSourcesListItem.tsx @@ -19,7 +19,7 @@ export class DataSourcesListItem extends PureComponent { {dataSource.name}
-
+
{dataSource.name} {dataSource.isDefault && default}
diff --git a/public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap b/public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap index 3ab1b1d53aa..ba750c4ce9c 100644 --- a/public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap +++ b/public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap @@ -32,6 +32,7 @@ exports[`Render should render component 1`] = ` className="card-item-details" >
gdev-cloudwatch diff --git a/public/app/features/datasources/settings/BasicSettings.tsx b/public/app/features/datasources/settings/BasicSettings.tsx index d13af25a561..010df1663b8 100644 --- a/public/app/features/datasources/settings/BasicSettings.tsx +++ b/public/app/features/datasources/settings/BasicSettings.tsx @@ -10,7 +10,7 @@ export interface Props { const BasicSettings: FC = ({ dataSourceName, isDefault, onDefaultChange, onNameChange }) => { return ( -
+
= ({ dataSourceName, isDefault, onDefaultChange, placeholder="Name" onChange={event => onNameChange(event.target.value)} required + aria-label="Datasource settings page name input field" />
- {/* - //@ts-ignore */} - onDefaultChange(event.target.checked)} /> + { + // @ts-ignore + onDefaultChange(event.target.checked); + }} + />
); diff --git a/public/app/features/datasources/settings/ButtonRow.tsx b/public/app/features/datasources/settings/ButtonRow.tsx index 16d25be293c..cdd47cdafeb 100644 --- a/public/app/features/datasources/settings/ButtonRow.tsx +++ b/public/app/features/datasources/settings/ButtonRow.tsx @@ -27,7 +27,13 @@ const ButtonRow: FC = ({ isReadOnly, onDelete, onSubmit, onTest }) => { Test )} - diff --git a/public/app/features/datasources/settings/DataSourceSettingsPage.tsx b/public/app/features/datasources/settings/DataSourceSettingsPage.tsx index e0e9b5e58b1..41bee55fbc7 100644 --- a/public/app/features/datasources/settings/DataSourceSettingsPage.tsx +++ b/public/app/features/datasources/settings/DataSourceSettingsPage.tsx @@ -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'; diff --git a/public/app/features/datasources/settings/__snapshots__/BasicSettings.test.tsx.snap b/public/app/features/datasources/settings/__snapshots__/BasicSettings.test.tsx.snap index 4a1e7620f32..a52413c5beb 100644 --- a/public/app/features/datasources/settings/__snapshots__/BasicSettings.test.tsx.snap +++ b/public/app/features/datasources/settings/__snapshots__/BasicSettings.test.tsx.snap @@ -2,6 +2,7 @@ exports[`Render should render component 1`] = `
- +
diff --git a/public/e2e-test/pages/dashboards/dashboardPage.ts b/public/e2e-test/pages/dashboards/dashboardPage.ts new file mode 100644 index 00000000000..3dffcea5550 --- /dev/null +++ b/public/e2e-test/pages/dashboards/dashboardPage.ts @@ -0,0 +1,11 @@ +import { ClickablePageObject, ClickablePageObjectType, Selector, TestPage } from '@grafana/toolkit/src/e2e'; + +export interface DashboardPage { + settings: ClickablePageObjectType; +} + +export const dashboardPage = new TestPage({ + pageObjects: { + settings: new ClickablePageObject(Selector.fromAriaLabel('Dashboard settings navbar button')), + }, +}); diff --git a/public/e2e-test/pages/dashboards/dashboardSettingsPage.ts b/public/e2e-test/pages/dashboards/dashboardSettingsPage.ts new file mode 100644 index 00000000000..73a5b25c993 --- /dev/null +++ b/public/e2e-test/pages/dashboards/dashboardSettingsPage.ts @@ -0,0 +1,11 @@ +import { ClickablePageObject, ClickablePageObjectType, Selector, TestPage } from '@grafana/toolkit/src/e2e'; + +export interface DashboardSettingsPage { + deleteDashBoard: ClickablePageObjectType; +} + +export const dashboardSettingsPage = new TestPage({ + pageObjects: { + deleteDashBoard: new ClickablePageObject(Selector.fromAriaLabel('Dashboard settings page delete dashboard button')), + }, +}); diff --git a/public/e2e-test/pages/datasources/dataSources.ts b/public/e2e-test/pages/datasources/dataSources.ts index e3325fb54b6..711f6efbd3e 100644 --- a/public/e2e-test/pages/datasources/dataSources.ts +++ b/public/e2e-test/pages/datasources/dataSources.ts @@ -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({ - url: '/datasources', - pageObjects: {}, -}); +export const dataSourcesPageFactory = (testDataSourceName: string) => + new TestPage({ + url: '/datasources', + pageObjects: { + testData: new ClickablePageObject(Selector.fromAriaLabel(`Data source list item for ${testDataSourceName}`)), + }, + }); diff --git a/public/e2e-test/pages/datasources/editDataSourcePage.ts b/public/e2e-test/pages/datasources/editDataSourcePage.ts index 38303b17de6..92f2ff62c54 100644 --- a/public/e2e-test/pages/datasources/editDataSourcePage.ts +++ b/public/e2e-test/pages/datasources/editDataSourcePage.ts @@ -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({ 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')), diff --git a/public/e2e-test/pages/modals/confirmModal.ts b/public/e2e-test/pages/modals/confirmModal.ts new file mode 100644 index 00000000000..42bb31a83a1 --- /dev/null +++ b/public/e2e-test/pages/modals/confirmModal.ts @@ -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({ + pageObjects: { + delete: new ClickablePageObject(Selector.fromAriaLabel('Confirm Modal Danger Button')), + success: new PageObject(Selector.fromSelector('.alert-success')), + }, +}); diff --git a/public/e2e-test/scenarios/smoke.test.ts b/public/e2e-test/scenarios/smoke.test.ts index fa93014d481..6ce271d7736 100644 --- a/public/e2e-test/scenarios/smoke.test.ts +++ b/public/e2e-test/scenarios/smoke.test.ts @@ -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); + } );