mirror of https://github.com/grafana/grafana
Tests: Adds end-to-end tests skeleton and basic smoke test scenario (#16901)
* Chore: Adds neccessary packages * Wip: Initial dummy test in place * Feature: Downloads Chromium if needed * Fix: Adds global config object * Refactor: Adds basic e2eScenario * Build: Adds end to end tests to config * Build: Changes end to end job * Build: Adds browsers to image * Build: Adds failing test * Refactor: Adds first e2e-test scenario * Fix: Ignores test output in gitignore * Refactor: Adds compare screenshots ability * Refactor: Removes unnecessary code * Build: Removes jest-puppeteer * Fix: Replaces test snapshots * Refactor: Creates output dir if missing * Refactor: Changes aria-labels to be more consistent * Docs: Adds section about end to end tests * Fix: Fixes snapshots * Docs: Adds information about ENV variablespull/13086/head
parent
ceb9f0855b
commit
a4d287d2e1
@ -0,0 +1,15 @@ |
||||
require('module-alias/register'); |
||||
|
||||
module.exports = { |
||||
verbose: false, |
||||
transform: { |
||||
'^.+\\.(ts|tsx)$': 'ts-jest', |
||||
}, |
||||
moduleDirectories: ['node_modules', 'public'], |
||||
roots: ['<rootDir>/public/e2e-test'], |
||||
testRegex: '(\\.|/)(test)\\.(jsx?|tsx?)$', |
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], |
||||
setupFiles: [], |
||||
globals: { 'ts-jest': { isolatedModules: true } }, |
||||
setupFilesAfterEnv: ['expect-puppeteer', '<rootDir>/public/e2e-test/install/install.ts'], |
||||
}; |
@ -0,0 +1,6 @@ |
||||
export const constants = { |
||||
baseUrl: process.env.BASE_URL || 'http://localhost:3000', |
||||
chromiumRevision: '650629', |
||||
screenShotsTruthDir: './public/e2e-test/screenShots/theTruth', |
||||
screenShotsOutputDir: './public/e2e-test/screenShots/theOutput', |
||||
}; |
@ -0,0 +1,51 @@ |
||||
import fs from 'fs'; |
||||
import { PNG } from 'pngjs'; |
||||
import { Page } from 'puppeteer-core'; |
||||
import pixelmatch from 'pixelmatch'; |
||||
|
||||
import { constants } from './constants'; |
||||
|
||||
export const takeScreenShot = async (page: Page, fileName: string) => { |
||||
const outputFolderExists = fs.existsSync(constants.screenShotsOutputDir); |
||||
if (!outputFolderExists) { |
||||
fs.mkdirSync(constants.screenShotsOutputDir); |
||||
} |
||||
const path = `${constants.screenShotsOutputDir}/${fileName}.png`; |
||||
await page.screenshot({ path, type: 'png', fullPage: false }); |
||||
}; |
||||
|
||||
export const compareScreenShots = async (fileName: string) => |
||||
new Promise(resolve => { |
||||
let filesRead = 0; |
||||
|
||||
const doneReading = () => { |
||||
if (++filesRead < 2) { |
||||
return; |
||||
} |
||||
|
||||
expect(screenShotFromTest.width).toEqual(screenShotFromTruth.width); |
||||
expect(screenShotFromTest.height).toEqual(screenShotFromTruth.height); |
||||
|
||||
const diff = new PNG({ width: screenShotFromTest.width, height: screenShotFromTruth.height }); |
||||
const numDiffPixels = pixelmatch( |
||||
screenShotFromTest.data, |
||||
screenShotFromTruth.data, |
||||
diff.data, |
||||
screenShotFromTest.width, |
||||
screenShotFromTest.height, |
||||
{ threshold: 0.1 } |
||||
); |
||||
|
||||
expect(numDiffPixels).toBe(0); |
||||
resolve(); |
||||
}; |
||||
|
||||
const screenShotFromTest = fs |
||||
.createReadStream(`${constants.screenShotsOutputDir}/${fileName}.png`) |
||||
.pipe(new PNG()) |
||||
.on('parsed', doneReading); |
||||
const screenShotFromTruth = fs |
||||
.createReadStream(`${constants.screenShotsTruthDir}/${fileName}.png`) |
||||
.pipe(new PNG()) |
||||
.on('parsed', doneReading); |
||||
}); |
@ -0,0 +1,29 @@ |
||||
import puppeteer, { Browser } from 'puppeteer-core'; |
||||
|
||||
export const launchBrowser = async (): Promise<Browser> => { |
||||
const browserFetcher = puppeteer.createBrowserFetcher(); |
||||
const localRevisions = await browserFetcher.localRevisions(); |
||||
if (localRevisions.length === 0) { |
||||
throw new Error('Could not launch browser because there is no local revisions.'); |
||||
} |
||||
|
||||
let executablePath = null; |
||||
executablePath = browserFetcher.revisionInfo(localRevisions[0]).executablePath; |
||||
|
||||
const browser = await puppeteer.launch({ |
||||
headless: process.env.BROWSER ? false : true, |
||||
slowMo: process.env.SLOWMO ? 100 : 0, |
||||
defaultViewport: { |
||||
width: 1920, |
||||
height: 1080, |
||||
deviceScaleFactor: 1, |
||||
isMobile: false, |
||||
hasTouch: false, |
||||
isLandscape: false, |
||||
}, |
||||
args: ['--start-fullscreen'], |
||||
executablePath, |
||||
}); |
||||
|
||||
return browser; |
||||
}; |
@ -0,0 +1,22 @@ |
||||
import { Page } from 'puppeteer-core'; |
||||
|
||||
import { constants } from './constants'; |
||||
import { loginPage } from 'e2e-test/pages/start/loginPage'; |
||||
|
||||
export const login = async (page: Page) => { |
||||
await loginPage.init(page); |
||||
await loginPage.navigateTo(); |
||||
|
||||
await loginPage.pageObjects.username.enter('admin'); |
||||
await loginPage.pageObjects.password.enter('admin'); |
||||
await loginPage.pageObjects.submit.click(); |
||||
await loginPage.waitForResponse(); |
||||
}; |
||||
|
||||
export const ensureLoggedIn = async (page: Page) => { |
||||
await page.goto(`${constants.baseUrl}`); |
||||
if (page.url().indexOf('login') > -1) { |
||||
console.log('Redirected to login page. Logging in...'); |
||||
await login(page); |
||||
} |
||||
}; |
@ -0,0 +1,84 @@ |
||||
import { Page } from 'puppeteer-core'; |
||||
|
||||
export class Selector { |
||||
static fromAriaLabel = (selector: string) => { |
||||
return `[aria-label="${selector}"]`; |
||||
}; |
||||
|
||||
static fromSelector = (selector: string) => { |
||||
return selector; |
||||
}; |
||||
} |
||||
|
||||
export interface PageObjectType { |
||||
init: (page: Page) => Promise<void>; |
||||
exists: () => Promise<void>; |
||||
containsText: (text: string) => Promise<void>; |
||||
} |
||||
|
||||
export interface ClickablePageObjectType extends PageObjectType { |
||||
click: () => Promise<void>; |
||||
} |
||||
|
||||
export interface InputPageObjectType extends PageObjectType { |
||||
enter: (text: string) => Promise<void>; |
||||
} |
||||
|
||||
export interface SelectPageObjectType extends PageObjectType { |
||||
select: (text: string) => Promise<void>; |
||||
} |
||||
|
||||
export class PageObject implements PageObjectType { |
||||
protected page: Page = null; |
||||
|
||||
constructor(protected selector: string) {} |
||||
|
||||
init = async (page: Page): Promise<void> => { |
||||
this.page = page; |
||||
}; |
||||
|
||||
exists = async (): Promise<void> => { |
||||
const options = { visible: true } as any; |
||||
await expect(this.page).not.toBeNull(); |
||||
await expect(this.page).toMatchElement(this.selector, options); |
||||
}; |
||||
|
||||
containsText = async (text: string): Promise<void> => { |
||||
const options = { visible: true, text } as any; |
||||
await expect(this.page).not.toBeNull(); |
||||
await expect(this.page).toMatchElement(this.selector, options); |
||||
}; |
||||
} |
||||
|
||||
export class ClickablePageObject extends PageObject implements ClickablePageObjectType { |
||||
constructor(selector: string) { |
||||
super(selector); |
||||
} |
||||
|
||||
click = async (): Promise<void> => { |
||||
await expect(this.page).not.toBeNull(); |
||||
await expect(this.page).toClick(this.selector); |
||||
}; |
||||
} |
||||
|
||||
export class InputPageObject extends PageObject implements InputPageObjectType { |
||||
constructor(selector: string) { |
||||
super(selector); |
||||
} |
||||
|
||||
enter = async (text: string): Promise<void> => { |
||||
await expect(this.page).not.toBeNull(); |
||||
await expect(this.page).toFill(this.selector, text); |
||||
}; |
||||
} |
||||
|
||||
export class SelectPageObject extends PageObject implements SelectPageObjectType { |
||||
constructor(selector: string) { |
||||
super(selector); |
||||
} |
||||
|
||||
select = async (text: string): Promise<void> => { |
||||
await expect(this.page).not.toBeNull(); |
||||
await this.page.select(this.selector, text); |
||||
}; |
||||
} |
@ -0,0 +1,110 @@ |
||||
import { Page } from 'puppeteer-core'; |
||||
import { constants } from './constants'; |
||||
import { PageObject } from './pageObjects'; |
||||
|
||||
export interface ExpectSelectorConfig { |
||||
selector: string; |
||||
containsText?: string; |
||||
isVisible?: boolean; |
||||
} |
||||
|
||||
export interface TestPageType<T> { |
||||
init: (page: Page) => Promise<void>; |
||||
getUrl: () => Promise<string>; |
||||
getUrlWithoutBaseUrl: () => Promise<string>; |
||||
navigateTo: () => Promise<void>; |
||||
expectSelector: (config: ExpectSelectorConfig) => Promise<void>; |
||||
waitForResponse: () => Promise<void>; |
||||
waitForNavigation: () => Promise<void>; |
||||
waitFor: (milliseconds: number) => Promise<void>; |
||||
pageObjects: PageObjects<T>; |
||||
} |
||||
|
||||
type PageObjects<T> = { [P in keyof T]: T[P] }; |
||||
|
||||
export interface TestPageConfig<T> { |
||||
url?: string; |
||||
pageObjects?: PageObjects<T>; |
||||
} |
||||
|
||||
export class TestPage<T> implements TestPageType<T> { |
||||
pageObjects: PageObjects<T> = null; |
||||
private page: Page = null; |
||||
private pageUrl: string = null; |
||||
|
||||
constructor(config: TestPageConfig<T>) { |
||||
if (config.url) { |
||||
this.pageUrl = `${constants.baseUrl}${config.url}`; |
||||
} |
||||
if (config.pageObjects) { |
||||
this.pageObjects = config.pageObjects; |
||||
} |
||||
} |
||||
|
||||
init = async (page: Page): Promise<void> => { |
||||
this.page = page; |
||||
|
||||
if (!this.pageObjects) { |
||||
return; |
||||
} |
||||
|
||||
Object.keys(this.pageObjects).forEach(key => { |
||||
const pageObject: PageObject = this.pageObjects[key]; |
||||
pageObject.init(page); |
||||
}); |
||||
}; |
||||
|
||||
navigateTo = async (): Promise<void> => { |
||||
this.throwIfNotInitialized(); |
||||
|
||||
await this.page.goto(this.pageUrl); |
||||
}; |
||||
|
||||
expectSelector = async (config: ExpectSelectorConfig): Promise<void> => { |
||||
this.throwIfNotInitialized(); |
||||
|
||||
const { selector, containsText, isVisible } = config; |
||||
const visible = isVisible || true; |
||||
const text = containsText; |
||||
const options = { visible, text } as any; |
||||
await expect(this.page).toMatchElement(selector, options); |
||||
}; |
||||
|
||||
waitForResponse = async (): Promise<void> => { |
||||
this.throwIfNotInitialized(); |
||||
|
||||
await this.page.waitForResponse(response => response.url() === this.pageUrl && response.status() === 200); |
||||
}; |
||||
|
||||
waitForNavigation = async (): Promise<void> => { |
||||
this.throwIfNotInitialized(); |
||||
|
||||
await this.page.waitForNavigation(); |
||||
}; |
||||
|
||||
getUrl = async (): Promise<string> => { |
||||
this.throwIfNotInitialized(); |
||||
|
||||
return await this.page.url(); |
||||
}; |
||||
|
||||
getUrlWithoutBaseUrl = async (): Promise<string> => { |
||||
this.throwIfNotInitialized(); |
||||
|
||||
const url = await this.getUrl(); |
||||
|
||||
return url.replace(constants.baseUrl, ''); |
||||
}; |
||||
|
||||
waitFor = async (milliseconds: number) => { |
||||
this.throwIfNotInitialized(); |
||||
|
||||
await this.page.waitFor(milliseconds); |
||||
}; |
||||
|
||||
private throwIfNotInitialized = () => { |
||||
if (!this.page) { |
||||
throw new Error('pageFactory has not been initilized, did you forget to call init with a page?'); |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,30 @@ |
||||
import { Browser, Page } from 'puppeteer-core'; |
||||
import { launchBrowser } from './launcher'; |
||||
import { ensureLoggedIn } from './login'; |
||||
|
||||
export const e2eScenario = ( |
||||
title: string, |
||||
testDescription: string, |
||||
callback: (browser: Browser, page: Page) => void |
||||
) => { |
||||
describe(title, () => { |
||||
let browser: Browser = null; |
||||
let page: Page = null; |
||||
|
||||
beforeAll(async () => { |
||||
browser = await launchBrowser(); |
||||
page = await browser.newPage(); |
||||
await ensureLoggedIn(page); |
||||
}); |
||||
|
||||
afterAll(async () => { |
||||
if (browser) { |
||||
await browser.close(); |
||||
} |
||||
}); |
||||
|
||||
it(testDescription, async () => { |
||||
await callback(browser, page); |
||||
}); |
||||
}); |
||||
}; |
@ -0,0 +1,22 @@ |
||||
import puppeteer from 'puppeteer-core'; |
||||
import { constants } from 'e2e-test/core/constants'; |
||||
|
||||
export const downloadBrowserIfNeeded = async (): Promise<void> => { |
||||
const browserFetcher = puppeteer.createBrowserFetcher(); |
||||
const localRevisions = await browserFetcher.localRevisions(); |
||||
if (localRevisions && localRevisions.length > 0) { |
||||
console.log('Found a local revision for browser, exiting install.'); |
||||
return; |
||||
} |
||||
|
||||
console.log('Did not find any local revisions for browser, downloading latest this might take a while.'); |
||||
await browserFetcher.download(constants.chromiumRevision, (downloaded, total) => { |
||||
console.log(`Downloaded ${downloaded}bytes of ${total}bytes.`); |
||||
}); |
||||
}; |
||||
|
||||
beforeAll(async () => { |
||||
console.log('Checking Chromium'); |
||||
jest.setTimeout(60 * 1000); |
||||
await downloadBrowserIfNeeded(); |
||||
}); |
@ -0,0 +1,13 @@ |
||||
import { ClickablePageObjectType, ClickablePageObject, Selector } from 'e2e-test/core/pageObjects'; |
||||
import { TestPage } from 'e2e-test/core/pages'; |
||||
|
||||
export interface CreateDashboardPage { |
||||
addQuery: ClickablePageObjectType; |
||||
} |
||||
|
||||
export const createDashboardPage = new TestPage<CreateDashboardPage>({ |
||||
url: '/dashboard/new', |
||||
pageObjects: { |
||||
addQuery: new ClickablePageObject(Selector.fromAriaLabel('Add Query CTA button')), |
||||
}, |
||||
}); |
@ -0,0 +1,14 @@ |
||||
import { ClickablePageObjectType, ClickablePageObject, Selector } from 'e2e-test/core/pageObjects'; |
||||
import { TestPage } from 'e2e-test/core/pages'; |
||||
|
||||
export interface DashboardsPage { |
||||
dashboard: ClickablePageObjectType; |
||||
} |
||||
|
||||
export const dashboardsPageFactory = (dashboardTitle: string) => |
||||
new TestPage<DashboardsPage>({ |
||||
url: '/dashboards', |
||||
pageObjects: { |
||||
dashboard: new ClickablePageObject(Selector.fromAriaLabel(dashboardTitle)), |
||||
}, |
||||
}); |
@ -0,0 +1,20 @@ |
||||
import { |
||||
ClickablePageObjectType, |
||||
ClickablePageObject, |
||||
Selector, |
||||
InputPageObjectType, |
||||
InputPageObject, |
||||
} from 'e2e-test/core/pageObjects'; |
||||
import { TestPage } from 'e2e-test/core/pages'; |
||||
|
||||
export interface SaveDashboardModal { |
||||
name: InputPageObjectType; |
||||
save: ClickablePageObjectType; |
||||
} |
||||
|
||||
export const saveDashboardModal = new TestPage<SaveDashboardModal>({ |
||||
pageObjects: { |
||||
name: new InputPageObject(Selector.fromAriaLabel('Save dashboard title field')), |
||||
save: new ClickablePageObject(Selector.fromAriaLabel('Save dashboard button')), |
||||
}, |
||||
}); |
@ -0,0 +1,13 @@ |
||||
import { ClickablePageObject, Selector, ClickablePageObjectType } from 'e2e-test/core/pageObjects'; |
||||
import { TestPage } from 'e2e-test/core/pages'; |
||||
|
||||
export interface AddDataSourcePage { |
||||
testDataDB: ClickablePageObjectType; |
||||
} |
||||
|
||||
export const addDataSourcePage = new TestPage<AddDataSourcePage>({ |
||||
url: '/datasources/new', |
||||
pageObjects: { |
||||
testDataDB: new ClickablePageObject(Selector.fromAriaLabel('TestData DB datasource plugin')), |
||||
}, |
||||
}); |
@ -0,0 +1,7 @@ |
||||
import { TestPage } from 'e2e-test/core/pages'; |
||||
|
||||
export interface DataSourcesPage {} |
||||
|
||||
export const dataSourcesPage = new TestPage<DataSourcesPage>({ |
||||
url: '/datasources', |
||||
}); |
@ -0,0 +1,22 @@ |
||||
import { |
||||
ClickablePageObjectType, |
||||
PageObjectType, |
||||
ClickablePageObject, |
||||
PageObject, |
||||
Selector, |
||||
} from 'e2e-test/core/pageObjects'; |
||||
import { TestPage } from 'e2e-test/core/pages'; |
||||
|
||||
export interface EditDataSourcePage { |
||||
saveAndTest: ClickablePageObjectType; |
||||
alert: PageObjectType; |
||||
alertMessage: PageObjectType; |
||||
} |
||||
|
||||
export const editDataSourcePage = new TestPage<EditDataSourcePage>({ |
||||
pageObjects: { |
||||
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')), |
||||
}, |
||||
}); |
@ -0,0 +1,26 @@ |
||||
import { |
||||
SelectPageObjectType, |
||||
SelectPageObject, |
||||
Selector, |
||||
ClickablePageObjectType, |
||||
ClickablePageObject, |
||||
} from 'e2e-test/core/pageObjects'; |
||||
import { TestPage } from 'e2e-test/core/pages'; |
||||
|
||||
export interface EditPanelPage { |
||||
queriesTab: ClickablePageObjectType; |
||||
saveDashboard: ClickablePageObjectType; |
||||
scenarioSelect: SelectPageObjectType; |
||||
showXAxis: ClickablePageObjectType; |
||||
visualizationTab: ClickablePageObjectType; |
||||
} |
||||
|
||||
export const editPanelPage = new TestPage<EditPanelPage>({ |
||||
pageObjects: { |
||||
queriesTab: new ClickablePageObject(Selector.fromAriaLabel('Queries tab button')), |
||||
saveDashboard: new ClickablePageObject(Selector.fromAriaLabel('Save dashboard navbar button')), |
||||
scenarioSelect: new SelectPageObject(Selector.fromAriaLabel('Scenario Select')), |
||||
showXAxis: new ClickablePageObject(Selector.fromSelector('[aria-label="X-Axis section"] > gf-form-switch')), |
||||
visualizationTab: new ClickablePageObject(Selector.fromAriaLabel('Visualization tab button')), |
||||
}, |
||||
}); |
@ -0,0 +1,14 @@ |
||||
import { ClickablePageObjectType, ClickablePageObject, Selector } from 'e2e-test/core/pageObjects'; |
||||
import { TestPage } from 'e2e-test/core/pages'; |
||||
|
||||
export interface Panel { |
||||
panelTitle: ClickablePageObjectType; |
||||
share: ClickablePageObjectType; |
||||
} |
||||
|
||||
export const panel = new TestPage<Panel>({ |
||||
pageObjects: { |
||||
panelTitle: new ClickablePageObject(Selector.fromAriaLabel('Panel Title')), |
||||
share: new ClickablePageObject(Selector.fromAriaLabel('Share panel menu item')), |
||||
}, |
||||
}); |
@ -0,0 +1,12 @@ |
||||
import { ClickablePageObjectType, ClickablePageObject, Selector } from 'e2e-test/core/pageObjects'; |
||||
import { TestPage } from 'e2e-test/core/pages'; |
||||
|
||||
export interface SharePanelModal { |
||||
directLinkRenderedImage: ClickablePageObjectType; |
||||
} |
||||
|
||||
export const sharePanelModal = new TestPage<SharePanelModal>({ |
||||
pageObjects: { |
||||
directLinkRenderedImage: new ClickablePageObject(Selector.fromAriaLabel('Link to rendered image')), |
||||
}, |
||||
}); |
@ -0,0 +1,23 @@ |
||||
import { |
||||
InputPageObject, |
||||
ClickablePageObject, |
||||
Selector, |
||||
InputPageObjectType, |
||||
ClickablePageObjectType, |
||||
} from 'e2e-test/core/pageObjects'; |
||||
import { TestPage } from 'e2e-test/core/pages'; |
||||
|
||||
export interface LoginPage { |
||||
username: InputPageObjectType; |
||||
password: InputPageObjectType; |
||||
submit: ClickablePageObjectType; |
||||
} |
||||
|
||||
export const loginPage = new TestPage<LoginPage>({ |
||||
url: '/login', |
||||
pageObjects: { |
||||
username: new InputPageObject(Selector.fromAriaLabel('Username input field')), |
||||
password: new InputPageObject(Selector.fromAriaLabel('Password input field')), |
||||
submit: new ClickablePageObject(Selector.fromAriaLabel('Login button')), |
||||
}, |
||||
}); |
@ -0,0 +1,85 @@ |
||||
import { Browser, Page, Target } from 'puppeteer-core'; |
||||
|
||||
import { e2eScenario } from 'e2e-test/core/scenario'; |
||||
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 { 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 { constants } from 'e2e-test/core/constants'; |
||||
import { sharePanelModal } from 'e2e-test/pages/panels/sharePanelModal'; |
||||
import { takeScreenShot, compareScreenShots } from 'e2e-test/core/images'; |
||||
|
||||
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(); |
||||
|
||||
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'); |
||||
|
||||
// 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 }); |
||||
|
||||
// Create a new Dashboard
|
||||
await createDashboardPage.init(page); |
||||
await createDashboardPage.navigateTo(); |
||||
await createDashboardPage.pageObjects.addQuery.click(); |
||||
|
||||
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(); |
||||
|
||||
// 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(); |
||||
|
||||
// Share the dashboard
|
||||
const dashboardsPage = dashboardsPageFactory(dashboardTitle); |
||||
await dashboardsPage.init(page); |
||||
await dashboardsPage.navigateTo(); |
||||
await dashboardsPage.pageObjects.dashboard.exists(); |
||||
await dashboardsPage.pageObjects.dashboard.click(); |
||||
|
||||
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
|
||||
const newPage = await newTarget.page(); |
||||
const fileName = 'smoke-test-scenario'; |
||||
await takeScreenShot(newPage, fileName); |
||||
await compareScreenShots(fileName); |
||||
} |
||||
); |
After Width: | Height: | Size: 29 KiB |
Loading…
Reference in new issue