mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
E2E: Adds tests for QueryVariable CRUD (#20448)
* WIP: Adds basic template variables CRUD start * e2eTests: Adds aria-labels in VariableEditorCtrl * Refactor: Simplifies a bit * e2eTests: Adds first Template Variable CRUD for QueryVariable * Tests: Adds ArrayPageOjbectType * Tests: Adds createQueryVariable method * Tests: Refactor CRUD test * Tests: Adds datasource and dashboard to scenario * Refactor: Fixes type errors * Refactor: Move pages to toolkit
This commit is contained in:
parent
31f4dea3d0
commit
2c2ed8371d
@ -1,6 +1,6 @@
|
||||
import { Page } from 'puppeteer-core';
|
||||
import { constants } from './constants';
|
||||
import { PageObject } from './pageObjects';
|
||||
import { PageObject, Selector } from './pageObjects';
|
||||
|
||||
export interface ExpectSelectorConfig {
|
||||
selector: string;
|
||||
@ -22,22 +22,34 @@ export interface TestPageType<T> {
|
||||
}
|
||||
|
||||
type PageObjects<T> = { [P in keyof T]: T[P] };
|
||||
type SelectorFunc = () => string;
|
||||
|
||||
export interface TestPageConfig<T> {
|
||||
url?: string;
|
||||
pageObjects: PageObjects<T>;
|
||||
pageObjects: { [P in keyof T]: string | SelectorFunc };
|
||||
}
|
||||
|
||||
export class TestPage<T> implements TestPageType<T> {
|
||||
pageObjects: PageObjects<T>;
|
||||
private page?: Page;
|
||||
private pageUrl?: string;
|
||||
private readonly pageUrl?: string;
|
||||
|
||||
constructor(config: TestPageConfig<T>) {
|
||||
if (config.url) {
|
||||
this.pageUrl = `${constants.baseUrl}${config.url}`;
|
||||
}
|
||||
this.pageObjects = config.pageObjects;
|
||||
|
||||
this.pageObjects = {} as PageObjects<T>;
|
||||
Object.keys(config.pageObjects).map(async key => {
|
||||
const selector = (config.pageObjects as any)[key];
|
||||
if (typeof selector === 'function') {
|
||||
(this.pageObjects as any)[key] = new PageObject(selector());
|
||||
}
|
||||
|
||||
if (typeof selector === 'string') {
|
||||
(this.pageObjects as any)[key] = new PageObject(Selector.fromAriaLabel(selector));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
init = async (page: Page): Promise<void> => {
|
||||
|
@ -5,6 +5,10 @@ export class Selector {
|
||||
return `[aria-label="${selector}"]`;
|
||||
};
|
||||
|
||||
static fromSwitchLabel = (selector: string) => {
|
||||
return `${Selector.fromAriaLabel(selector)} .gf-form-switch input`;
|
||||
};
|
||||
|
||||
static fromSelector = (selector: string) => {
|
||||
return selector;
|
||||
};
|
||||
@ -14,6 +18,7 @@ export interface PageObjectType {
|
||||
init: (page: Page) => Promise<void>;
|
||||
exists: () => Promise<void>;
|
||||
containsText: (text: string) => Promise<void>;
|
||||
waitForSelector: (timeoutInMs?: number) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface ClickablePageObjectType extends PageObjectType {
|
||||
@ -22,13 +27,36 @@ export interface ClickablePageObjectType extends PageObjectType {
|
||||
|
||||
export interface InputPageObjectType extends PageObjectType {
|
||||
enter: (text: string) => Promise<void>;
|
||||
containsPlaceholder: (text: string) => Promise<void>;
|
||||
blur: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface SelectPageObjectType extends PageObjectType {
|
||||
select: (text: string) => Promise<void>;
|
||||
selectedTextIs: (text: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export class PageObject implements PageObjectType {
|
||||
export interface SwitchPageObjectType extends PageObjectType {
|
||||
toggle: () => Promise<void>;
|
||||
isSwitchedOn: () => Promise<void>;
|
||||
isSwitchedOff: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface ArrayPageObjectType {
|
||||
hasLength: (length: number) => Promise<void>;
|
||||
clickAtPos: (index: number) => Promise<void>;
|
||||
containsTextAtPos: (text: string, index: number) => Promise<void>;
|
||||
waitForSelector: (timeoutInMs?: number) => Promise<void>;
|
||||
}
|
||||
|
||||
export class PageObject
|
||||
implements
|
||||
PageObjectType,
|
||||
ClickablePageObjectType,
|
||||
InputPageObjectType,
|
||||
SelectPageObjectType,
|
||||
SwitchPageObjectType,
|
||||
ArrayPageObjectType {
|
||||
protected page?: Page;
|
||||
|
||||
constructor(protected selector: string) {}
|
||||
@ -38,50 +66,109 @@ export class PageObject implements PageObjectType {
|
||||
};
|
||||
|
||||
exists = async (): Promise<void> => {
|
||||
console.log('Checking for existence of:', this.selector);
|
||||
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> => {
|
||||
console.log(`Checking for existence of '${text}' for:`, this.selector);
|
||||
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);
|
||||
}
|
||||
containsPlaceholder = async (expectedPlaceholder: string): Promise<void> => {
|
||||
console.log(`Checking for placeholder '${expectedPlaceholder}' in:`, this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
const placeholder = await this.page!.$eval(this.selector, (input: any) => input.placeholder);
|
||||
await expect(placeholder).toEqual(expectedPlaceholder);
|
||||
};
|
||||
|
||||
hasLength = async (length: number): Promise<void> => {
|
||||
console.log('Checking for length of', this.selector);
|
||||
const result = await this.page!.$$eval(this.selector, elements => elements.length);
|
||||
await expect(result).toEqual(length);
|
||||
};
|
||||
|
||||
containsTextAtPos = async (text: string, index: number): Promise<void> => {
|
||||
console.log(`Checking for text ${text} at position ${index} of`, this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
const result = await this.page!.$$eval(this.selector, elements => elements.map((el: any) => el.innerText));
|
||||
await expect(result[index]!.trim()).toEqual(text);
|
||||
};
|
||||
|
||||
click = async (): Promise<void> => {
|
||||
console.log('Trying to click on:', this.selector);
|
||||
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);
|
||||
}
|
||||
clickAtPos = async (index: number): Promise<void> => {
|
||||
console.log(`Trying to clicking at position:${index} on:`, this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
const elements = await this.page!.$$(this.selector);
|
||||
const element = await elements[index];
|
||||
await element.click();
|
||||
};
|
||||
|
||||
toggle = async (): Promise<void> => {
|
||||
const switchSelector = this.selector.replace(' .gf-form-switch input', '');
|
||||
console.log('Trying to toggle:', switchSelector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
await expect(this.page).toClick(switchSelector);
|
||||
};
|
||||
|
||||
enter = async (text: string): Promise<void> => {
|
||||
console.log(`Trying to enter text:${text} into:`, this.selector);
|
||||
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> => {
|
||||
console.log(`Trying to select text:${text} in dropdown:`, this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
await this.page!.select(this.selector, text);
|
||||
};
|
||||
|
||||
selectedTextIs = async (text: string): Promise<void> => {
|
||||
console.log(`Trying to get selected text from dropdown:`, this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
const selectedText = await this.page!.$eval(this.selector, (select: any) => {
|
||||
if (select.selectedIndex === -1) {
|
||||
return '';
|
||||
}
|
||||
return select.options[select.selectedIndex].innerText;
|
||||
});
|
||||
await expect(selectedText).toEqual(text);
|
||||
};
|
||||
|
||||
waitForSelector = async (timeoutInMs?: number): Promise<void> => {
|
||||
console.log('Waiting for', this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
await this.page!.waitForSelector(this.selector, { timeout: timeoutInMs || 1000 });
|
||||
};
|
||||
|
||||
isSwitchedOn = async (): Promise<void> => {
|
||||
const checked = await this.getChecked();
|
||||
await expect(checked).toBe(true);
|
||||
};
|
||||
|
||||
isSwitchedOff = async (): Promise<void> => {
|
||||
const checked = await this.getChecked();
|
||||
await expect(checked).toBe(false);
|
||||
};
|
||||
|
||||
blur = async (): Promise<void> => {
|
||||
console.log('Trying to blur:', this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
await this.page!.$eval(this.selector, (input: any) => input.blur());
|
||||
};
|
||||
|
||||
private getChecked = async (): Promise<boolean> => {
|
||||
console.log('Trying get switch status for:', this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
return await this.page!.$eval(this.selector, (input: any) => input.checked);
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
import { Page } from 'puppeteer-core';
|
||||
|
||||
import { ClickablePageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
import { dashboardPage } from './dashboardPage';
|
||||
import { dashboardSettingsPage } from './dashboardSettingsPage';
|
||||
import { saveDashboardModal } from './saveDashboardModal';
|
||||
import { dashboardsPageFactory } from './dashboardsPage';
|
||||
import { confirmModal } from '../modals/confirmModal';
|
||||
|
||||
export interface CreateDashboardPage {
|
||||
addQuery: ClickablePageObjectType;
|
||||
saveDashboard: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const createDashboardPage = new TestPage<CreateDashboardPage>({
|
||||
url: '/dashboard/new',
|
||||
pageObjects: {
|
||||
addQuery: 'Add Query CTA button',
|
||||
saveDashboard: 'Save dashboard navbar button',
|
||||
},
|
||||
});
|
||||
|
||||
export const createEmptyDashboardPage = async (page: Page, dashboardTitle: string) => {
|
||||
await createDashboardPage.init(page);
|
||||
await createDashboardPage.navigateTo();
|
||||
await createDashboardPage.pageObjects.saveDashboard.click();
|
||||
|
||||
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 dashboardPage.init(page);
|
||||
return dashboardPage;
|
||||
};
|
||||
|
||||
export 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();
|
||||
};
|
@ -0,0 +1,49 @@
|
||||
import { ArrayPageObjectType, ClickablePageObjectType, PageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface DashboardPage {
|
||||
settings: ClickablePageObjectType;
|
||||
submenuItemLabel: ArrayPageObjectType;
|
||||
submenuItemValueDropDownValueLink: ArrayPageObjectType;
|
||||
submenuItemValueDropDownDropDown: PageObjectType;
|
||||
submenuItemValueDropDownSelectedLink: PageObjectType;
|
||||
submenuItemValueDropDownOptionText: ArrayPageObjectType;
|
||||
}
|
||||
|
||||
export const dashboardPage = new TestPage<DashboardPage>({
|
||||
pageObjects: {
|
||||
settings: 'Dashboard settings navbar button',
|
||||
submenuItemLabel: 'Dashboard template variables submenu LabelName label',
|
||||
submenuItemValueDropDownValueLink: 'Dashboard template variables Variable Value DropDown value link',
|
||||
submenuItemValueDropDownDropDown: 'Dashboard template variables Variable Value DropDown DropDown',
|
||||
submenuItemValueDropDownSelectedLink: 'Dashboard template variables Variable Value DropDown Selected link',
|
||||
submenuItemValueDropDownOptionText: 'Dashboard template variables Variable Value DropDown option text',
|
||||
},
|
||||
});
|
||||
|
||||
export interface AssertVariableLabelsAndComponentsArguments {
|
||||
label: string;
|
||||
options: string[];
|
||||
}
|
||||
|
||||
export const assertVariableLabelsAndComponents = async (
|
||||
page: TestPage<DashboardPage>,
|
||||
args: AssertVariableLabelsAndComponentsArguments[]
|
||||
) => {
|
||||
console.log('Asserting variable components and labels');
|
||||
await page.pageObjects.submenuItemLabel.waitForSelector();
|
||||
await page.pageObjects.submenuItemLabel.hasLength(args.length);
|
||||
await page.pageObjects.submenuItemValueDropDownValueLink.hasLength(args.length);
|
||||
|
||||
for (let index = 0; index < args.length; index++) {
|
||||
const { label, options } = args[index];
|
||||
await page.pageObjects.submenuItemLabel.containsTextAtPos(label, index);
|
||||
await page.pageObjects.submenuItemValueDropDownValueLink.containsTextAtPos(options[1], index);
|
||||
await page.pageObjects.submenuItemValueDropDownValueLink.clickAtPos(index);
|
||||
await page.pageObjects.submenuItemValueDropDownOptionText.hasLength(options.length);
|
||||
for (let optionIndex = 0; optionIndex < options.length; optionIndex++) {
|
||||
await page.pageObjects.submenuItemValueDropDownOptionText.containsTextAtPos(options[optionIndex], optionIndex);
|
||||
}
|
||||
}
|
||||
console.log('Asserting variable components and labels, Ok');
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
import { ClickablePageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface DashboardSettingsPage {
|
||||
deleteDashBoard: ClickablePageObjectType;
|
||||
variablesSection: ClickablePageObjectType;
|
||||
saveDashBoard: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const dashboardSettingsPage = new TestPage<DashboardSettingsPage>({
|
||||
pageObjects: {
|
||||
deleteDashBoard: 'Dashboard settings page delete dashboard button',
|
||||
variablesSection: 'Dashboard settings section Variables',
|
||||
saveDashBoard: 'Dashboard settings aside actions Save button',
|
||||
},
|
||||
});
|
@ -1,4 +1,5 @@
|
||||
import { TestPage, ClickablePageObjectType, ClickablePageObject, Selector } from '@grafana/toolkit/src/e2e';
|
||||
import { ClickablePageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface DashboardsPage {
|
||||
dashboard: ClickablePageObjectType;
|
||||
@ -8,6 +9,6 @@ export const dashboardsPageFactory = (dashboardTitle: string) =>
|
||||
new TestPage<DashboardsPage>({
|
||||
url: '/dashboards',
|
||||
pageObjects: {
|
||||
dashboard: new ClickablePageObject(Selector.fromAriaLabel(dashboardTitle)),
|
||||
dashboard: dashboardTitle,
|
||||
},
|
||||
});
|
@ -0,0 +1,14 @@
|
||||
import { ClickablePageObjectType, PageObject, Selector } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface SaveChangesDashboardModal {
|
||||
save: ClickablePageObjectType;
|
||||
success: PageObject;
|
||||
}
|
||||
|
||||
export const saveChangesDashboardModal = new TestPage<SaveChangesDashboardModal>({
|
||||
pageObjects: {
|
||||
save: 'Dashboard settings Save Dashboard Modal Save button',
|
||||
success: () => Selector.fromSelector('.alert-success'),
|
||||
},
|
||||
});
|
@ -0,0 +1,16 @@
|
||||
import { ClickablePageObjectType, InputPageObjectType, PageObject, Selector } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface SaveDashboardModal {
|
||||
name: InputPageObjectType;
|
||||
save: ClickablePageObjectType;
|
||||
success: PageObject;
|
||||
}
|
||||
|
||||
export const saveDashboardModal = new TestPage<SaveDashboardModal>({
|
||||
pageObjects: {
|
||||
name: 'Save dashboard title field',
|
||||
save: 'Save dashboard button',
|
||||
success: () => Selector.fromSelector('.alert-success'),
|
||||
},
|
||||
});
|
@ -0,0 +1,13 @@
|
||||
import { ClickablePageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface AddDataSourcePage {
|
||||
testDataDB: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const addDataSourcePage = new TestPage<AddDataSourcePage>({
|
||||
url: '/datasources/new',
|
||||
pageObjects: {
|
||||
testDataDB: 'TestData DB datasource plugin',
|
||||
},
|
||||
});
|
@ -0,0 +1,62 @@
|
||||
import { Page } from 'puppeteer-core';
|
||||
|
||||
import { ClickablePageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
import { editDataSourcePage } from './editDataSourcePage';
|
||||
import { addDataSourcePage } from './addDataSourcePage';
|
||||
import { confirmModal } from '../modals/confirmModal';
|
||||
|
||||
export interface DataSourcesPage {
|
||||
testData: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const dataSourcesPageFactory = (testDataSourceName: string) =>
|
||||
new TestPage<DataSourcesPage>({
|
||||
url: '/datasources',
|
||||
pageObjects: {
|
||||
testData: `Data source list item for ${testDataSourceName}`,
|
||||
},
|
||||
});
|
||||
|
||||
export const addTestDataSourceAndVerify = async (page: Page) => {
|
||||
// Add TestData DB
|
||||
const testDataSourceName = `e2e - 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.name.enter(testDataSourceName);
|
||||
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}"]`;
|
||||
|
||||
const dataSourcesPage = dataSourcesPageFactory(testDataSourceName);
|
||||
await dataSourcesPage.init(page);
|
||||
await dataSourcesPage.navigateTo();
|
||||
await dataSourcesPage.expectSelector({ selector });
|
||||
|
||||
return testDataSourceName;
|
||||
};
|
||||
|
||||
export 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();
|
||||
};
|
@ -0,0 +1,20 @@
|
||||
import { ClickablePageObjectType, InputPageObjectType, PageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface EditDataSourcePage {
|
||||
name: InputPageObjectType;
|
||||
delete: ClickablePageObjectType;
|
||||
saveAndTest: ClickablePageObjectType;
|
||||
alert: PageObjectType;
|
||||
alertMessage: PageObjectType;
|
||||
}
|
||||
|
||||
export const editDataSourcePage = new TestPage<EditDataSourcePage>({
|
||||
pageObjects: {
|
||||
name: 'Datasource settings page name input field',
|
||||
delete: 'Delete button',
|
||||
saveAndTest: 'Save and Test button',
|
||||
alert: 'Datasource settings page Alert',
|
||||
alertMessage: 'Datasource settings page Alert message',
|
||||
},
|
||||
});
|
@ -1,2 +1,12 @@
|
||||
export * from './loginPage';
|
||||
export * from './pluginsPage';
|
||||
export * from './dashboards/createDashboardPage';
|
||||
export * from './dashboards/dashboardPage';
|
||||
export * from './dashboards/dashboardSettingsPage';
|
||||
export * from './dashboards/dashboardsPage';
|
||||
export * from './dashboards/saveChangesDashboardModal';
|
||||
export * from './dashboards/saveDashboardModal';
|
||||
export * from './datasources/addDataSourcePage';
|
||||
export * from './datasources/dataSources';
|
||||
export * from './datasources/editDataSourcePage';
|
||||
export * from './modals/confirmModal';
|
||||
|
@ -1,11 +1,5 @@
|
||||
import { TestPage } from '../pageInfo';
|
||||
import {
|
||||
Selector,
|
||||
InputPageObject,
|
||||
InputPageObjectType,
|
||||
ClickablePageObjectType,
|
||||
ClickablePageObject,
|
||||
} from '../pageObjects';
|
||||
import { ClickablePageObjectType, InputPageObjectType } from '../pageObjects';
|
||||
|
||||
export interface LoginPage {
|
||||
username: InputPageObjectType;
|
||||
@ -16,8 +10,8 @@ export interface LoginPage {
|
||||
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')),
|
||||
username: 'Username input field',
|
||||
password: 'Password input field',
|
||||
submit: 'Login button',
|
||||
},
|
||||
});
|
||||
|
@ -0,0 +1,14 @@
|
||||
import { ClickablePageObjectType, PageObject, Selector } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface ConfirmModal {
|
||||
delete: ClickablePageObjectType;
|
||||
success: PageObject;
|
||||
}
|
||||
|
||||
export const confirmModal = new TestPage<ConfirmModal>({
|
||||
pageObjects: {
|
||||
delete: 'Confirm Modal Danger Button',
|
||||
success: () => Selector.fromSelector('.alert-success'),
|
||||
},
|
||||
});
|
@ -1,30 +1,69 @@
|
||||
import { Browser, Page } from 'puppeteer-core';
|
||||
import { launchBrowser } from './launcher';
|
||||
import { ensureLoggedIn } from './login';
|
||||
import { cleanDashboard, createEmptyDashboardPage } from './pages/dashboards/createDashboardPage';
|
||||
import { DashboardPage } from './pages/dashboards/dashboardPage';
|
||||
import { TestPage } from './pageInfo';
|
||||
import { addTestDataSourceAndVerify, cleanUpTestDataSource } from './pages/datasources/dataSources';
|
||||
|
||||
export interface ScenarioArguments {
|
||||
describeName: string;
|
||||
itName: string;
|
||||
scenario: (browser: Browser, page: Page, datasourceName?: string, dashboardPage?: TestPage<DashboardPage>) => void;
|
||||
skipScenario?: boolean;
|
||||
createTestDataSource?: boolean;
|
||||
createTestDashboard?: boolean;
|
||||
}
|
||||
|
||||
export const e2eScenario = ({
|
||||
describeName,
|
||||
itName,
|
||||
scenario,
|
||||
skipScenario = false,
|
||||
createTestDataSource = false,
|
||||
createTestDashboard = false,
|
||||
}: ScenarioArguments) => {
|
||||
describe(describeName, () => {
|
||||
if (skipScenario) {
|
||||
it.skip(itName, async () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
export const e2eScenario = (
|
||||
title: string,
|
||||
testDescription: string,
|
||||
callback: (browser: Browser, page: Page) => void
|
||||
) => {
|
||||
describe(title, () => {
|
||||
let browser: Browser;
|
||||
let page: Page;
|
||||
let testDataSourceName: string;
|
||||
let testDashboardTitle: string;
|
||||
let dashboardPage: TestPage<DashboardPage>;
|
||||
|
||||
beforeAll(async () => {
|
||||
browser = await launchBrowser();
|
||||
page = await browser.newPage();
|
||||
await ensureLoggedIn(page);
|
||||
if (createTestDataSource) {
|
||||
testDataSourceName = await addTestDataSourceAndVerify(page);
|
||||
}
|
||||
if (createTestDashboard) {
|
||||
testDashboardTitle = `e2e - ${new Date().getTime()}`;
|
||||
dashboardPage = await createEmptyDashboardPage(page, testDashboardTitle);
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (testDataSourceName) {
|
||||
await cleanUpTestDataSource(page, testDataSourceName);
|
||||
}
|
||||
if (testDashboardTitle && dashboardPage) {
|
||||
await cleanDashboard(page, testDashboardTitle);
|
||||
}
|
||||
if (browser) {
|
||||
await browser.close();
|
||||
}
|
||||
});
|
||||
|
||||
it(testDescription, async () => {
|
||||
await callback(browser, page);
|
||||
it(itName, async () => {
|
||||
await scenario(browser, page, testDataSourceName, dashboardPage);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Browser, Page } from 'puppeteer-core';
|
||||
|
||||
import { e2eScenario, takeScreenShot, pages } from '@grafana/toolkit/src/e2e';
|
||||
import { e2eScenario, pages, takeScreenShot } from '@grafana/toolkit/src/e2e';
|
||||
import { getEndToEndSettings } from '@grafana/toolkit/src/plugins';
|
||||
|
||||
// ****************************************************************
|
||||
@ -11,14 +11,18 @@ const sleep = (milliseconds: number) => {
|
||||
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
||||
};
|
||||
|
||||
e2eScenario('Common Plugin Test', 'should pass', async (browser: Browser, page: Page) => {
|
||||
const settings = getEndToEndSettings();
|
||||
const pluginPage = pages.getPluginPage(settings.plugin.id);
|
||||
await pluginPage.init(page);
|
||||
await pluginPage.navigateTo();
|
||||
// TODO: find a better way to avoid the 'loading' page
|
||||
await sleep(500);
|
||||
e2eScenario({
|
||||
describeName: 'Common Plugin Test',
|
||||
itName: 'should pass',
|
||||
scenario: async (browser: Browser, page: Page) => {
|
||||
const settings = getEndToEndSettings();
|
||||
const pluginPage = pages.getPluginPage(settings.plugin.id);
|
||||
await pluginPage.init(page);
|
||||
await pluginPage.navigateTo();
|
||||
// TODO: find a better way to avoid the 'loading' page
|
||||
await sleep(500);
|
||||
|
||||
const fileName = 'plugin-page';
|
||||
await takeScreenShot(page, fileName);
|
||||
const fileName = 'plugin-page';
|
||||
await takeScreenShot(page, fileName);
|
||||
},
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useContext, MouseEvent } from 'react';
|
||||
import React, { MouseEvent, useContext } from 'react';
|
||||
import { CallToActionCard, LinkButton, ThemeContext } from '@grafana/ui';
|
||||
import { css } from 'emotion';
|
||||
|
||||
export interface Props {
|
||||
title: string;
|
||||
buttonIcon: string;
|
||||
@ -72,7 +73,14 @@ const EmptyListCTA: React.FunctionComponent<Props> = ({
|
||||
: '';
|
||||
|
||||
const ctaElement = (
|
||||
<LinkButton size="lg" onClick={onClick} href={buttonLink} icon={buttonIcon} className={ctaElementClassName}>
|
||||
<LinkButton
|
||||
size="lg"
|
||||
onClick={onClick}
|
||||
href={buttonLink}
|
||||
icon={buttonIcon}
|
||||
className={ctaElementClassName}
|
||||
aria-label={`Call to action button ${buttonTitle}`}
|
||||
>
|
||||
{buttonTitle}
|
||||
</LinkButton>
|
||||
);
|
||||
|
@ -1,22 +1,18 @@
|
||||
// Libaries
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
// Utils & Services
|
||||
import { appEvents } from 'app/core/app_events';
|
||||
import { PlaylistSrv } from 'app/features/playlist/playlist_srv';
|
||||
|
||||
// Components
|
||||
import { DashNavButton } from './DashNavButton';
|
||||
import { DashNavTimeControls } from './DashNavTimeControls';
|
||||
import { Tooltip } from '@grafana/ui';
|
||||
|
||||
// State
|
||||
import { updateLocation } from 'app/core/actions';
|
||||
|
||||
// Types
|
||||
import { DashboardModel } from '../../state';
|
||||
import { StoreState, CoreEvents } from 'app/types';
|
||||
import { CoreEvents, StoreState } from 'app/types';
|
||||
|
||||
export interface OwnProps {
|
||||
dashboard: DashboardModel;
|
||||
@ -160,7 +156,11 @@ export class DashNav extends PureComponent<Props> {
|
||||
return (
|
||||
<div className="navbar-edit">
|
||||
<Tooltip content="Go back (Esc)">
|
||||
<button className="navbar-edit__back-btn" onClick={this.onClose}>
|
||||
<button
|
||||
className="navbar-edit__back-btn"
|
||||
onClick={this.onClose}
|
||||
aria-label="Dashboard settings Go Back button"
|
||||
>
|
||||
<i className="fa fa-arrow-left" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
@ -1,14 +1,24 @@
|
||||
<aside class="dashboard-settings__aside">
|
||||
<a href="{{::section.url}}" class="dashboard-settings__nav-item" ng-class="{active: ctrl.viewId === section.id}" ng-repeat="section in ctrl.sections">
|
||||
<a href="{{::section.url}}" class="dashboard-settings__nav-item" ng-class="{active: ctrl.viewId === section.id}" ng-repeat="section in ctrl.sections" aria-label="{{'Dashboard settings section ' + section.title}}">
|
||||
<i class="{{::section.icon}}"></i>
|
||||
{{::section.title}}
|
||||
</a>
|
||||
|
||||
<div class="dashboard-settings__aside-actions">
|
||||
<button class="btn btn-primary" ng-click="ctrl.saveDashboard()" ng-show="ctrl.canSave">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
ng-click="ctrl.saveDashboard()"
|
||||
ng-show="ctrl.canSave"
|
||||
aria-label="Dashboard settings aside actions Save button"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
<button class="btn btn-inverse" ng-click="ctrl.openSaveAsModal()" ng-show="ctrl.canSaveAs">
|
||||
<button
|
||||
class="btn btn-inverse"
|
||||
ng-click="ctrl.openSaveAsModal()"
|
||||
ng-show="ctrl.canSaveAs"
|
||||
aria-label="Dashboard settings aside actions Save As button"
|
||||
>
|
||||
Save As...
|
||||
</button>
|
||||
</div>
|
||||
|
@ -57,6 +57,7 @@ const template = `
|
||||
class="btn btn-primary"
|
||||
ng-class="{'btn-primary--processing': ctrl.isSaving}"
|
||||
ng-disabled="ctrl.saveForm.$invalid || ctrl.isSaving"
|
||||
aria-label="Dashboard settings Save Dashboard Modal Save button"
|
||||
>
|
||||
<span ng-if="!ctrl.isSaving">Save</span>
|
||||
<span ng-if="ctrl.isSaving === true">Saving...</span>
|
||||
|
@ -1,9 +1,11 @@
|
||||
<div class="submenu-controls">
|
||||
<div ng-repeat="variable in ctrl.variables" ng-hide="variable.hide === 2" class="submenu-item gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label template-variable" ng-hide="variable.hide === 1">
|
||||
{{variable.label || variable.name}}
|
||||
</label>
|
||||
<label
|
||||
class="gf-form-label template-variable"
|
||||
ng-hide="variable.hide === 1"
|
||||
aria-label="Dashboard template variables submenu LabelName label"
|
||||
>{{variable.label || variable.name}}</label>
|
||||
<value-select-dropdown ng-if="variable.type !== 'adhoc' && variable.type !== 'textbox'" dashboard="ctrl.dashboard" variable="variable" on-updated="ctrl.variableUpdated(variable)"></value-select-dropdown>
|
||||
<input type="text" ng-if="variable.type === 'textbox'" ng-model="variable.query" class="gf-form-input width-12" ng-blur="variable.current.value != variable.query && variable.updateOptions() && ctrl.variableUpdated(variable);" ng-keydown="$event.keyCode === 13 && variable.current.value != variable.query && variable.updateOptions() && ctrl.variableUpdated(variable);" ></input>
|
||||
</div>
|
||||
|
@ -37,6 +37,7 @@ export default class DefaultVariableQueryEditor extends PureComponent<VariableQu
|
||||
onBlur={this.onBlur}
|
||||
placeholder="metric name or tags query"
|
||||
required
|
||||
aria-label="Variable editor Form Default Variable Query Editor textarea"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,9 +1,13 @@
|
||||
<div ng-controller="VariableEditorCtrl" ng-init="init()">
|
||||
<div class="page-action-bar">
|
||||
<h3 class="dashboard-settings__header">
|
||||
<a ng-click="setMode('list')">Variables</a>
|
||||
<span ng-show="mode === 'new'"><i class="fa fa-fw fa-chevron-right"></i> New</span>
|
||||
<span ng-show="mode === 'edit'"><i class="fa fa-fw fa-chevron-right"></i> Edit</span>
|
||||
<a ng-click="setMode('list')" aria-label="Variable editor Header link">Variables</a>
|
||||
<span ng-show="mode === 'new'"
|
||||
><i class="fa fa-fw fa-chevron-right" aria-label="Variable editor Header mode New"></i> New</span
|
||||
>
|
||||
<span ng-show="mode === 'edit'"
|
||||
><i class="fa fa-fw fa-chevron-right" aria-label="Variable editor Header mode Edit"></i> Edit</span
|
||||
>
|
||||
</h3>
|
||||
|
||||
<div class="page-action-bar__spacer"></div>
|
||||
@ -12,25 +16,27 @@
|
||||
class="btn btn-primary"
|
||||
ng-click="setMode('new');"
|
||||
ng-if="variables.length > 0"
|
||||
ng-hide="mode === 'edit' || mode === 'new'">
|
||||
New
|
||||
</a>
|
||||
ng-hide="mode === 'edit' || mode === 'new'"
|
||||
aria-label="Variable editor New variable button"
|
||||
>
|
||||
New
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div ng-if="mode === 'list'">
|
||||
<div ng-if="variables.length === 0">
|
||||
<empty-list-cta
|
||||
<empty-list-cta
|
||||
on-click="setNewMode"
|
||||
title="emptyListCta.title"
|
||||
infoBox="emptyListCta.infoBox"
|
||||
infoBoxTitle="emptyListCta.infoBoxTitle"
|
||||
buttonTitle="emptyListCta.buttonTitle"
|
||||
buttonIcon="emptyListCta.buttonIcon"
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div ng-if="variables.length">
|
||||
<table class="filter-table filter-table--hover">
|
||||
<table class="filter-table filter-table--hover" aria-label="Variable editor Table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
@ -41,24 +47,53 @@
|
||||
<tbody>
|
||||
<tr ng-repeat="variable in variables">
|
||||
<td style="width: 1%">
|
||||
<span ng-click="edit(variable)" class="pointer template-variable"> ${{ variable.name }} </span>
|
||||
<span
|
||||
ng-click="edit(variable)"
|
||||
class="pointer template-variable"
|
||||
aria-label="Variable editor Table Name field"
|
||||
>
|
||||
${{ variable.name }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="max-width: 200px;" ng-click="edit(variable)" class="pointer max-width">
|
||||
<td
|
||||
style="max-width: 200px;"
|
||||
ng-click="edit(variable)"
|
||||
class="pointer max-width"
|
||||
aria-label="Variable editor Table Definition field"
|
||||
>
|
||||
{{ variable.definition ? variable.definition : variable.query }}
|
||||
</td>
|
||||
<td style="width: 1%">
|
||||
<i ng-click="_.move(variables,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i>
|
||||
<i
|
||||
ng-click="_.move(variables,$index,$index-1)"
|
||||
ng-hide="$first"
|
||||
class="pointer fa fa-arrow-up"
|
||||
aria-label="Variable editor Table ArrowUp button"
|
||||
></i>
|
||||
</td>
|
||||
<td style="width: 1%">
|
||||
<i ng-click="_.move(variables,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i>
|
||||
<i
|
||||
ng-click="_.move(variables,$index,$index+1)"
|
||||
ng-hide="$last"
|
||||
class="pointer fa fa-arrow-down"
|
||||
aria-label="Variable editor Table ArrowDown button"
|
||||
></i>
|
||||
</td>
|
||||
<td style="width: 1%">
|
||||
<a ng-click="duplicate(variable)" class="btn btn-inverse btn-small">
|
||||
<a
|
||||
ng-click="duplicate(variable)"
|
||||
class="btn btn-inverse btn-small"
|
||||
aria-label="Variable editor Table Duplicate button"
|
||||
>
|
||||
Duplicate
|
||||
</a>
|
||||
</td>
|
||||
<td style="width: 1%">
|
||||
<a ng-click="removeVariable(variable)" class="btn btn-danger btn-small">
|
||||
<a
|
||||
ng-click="removeVariable(variable)"
|
||||
class="btn btn-danger btn-small"
|
||||
aria-label="Variable editor Table Remove button"
|
||||
>
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</td>
|
||||
@ -68,7 +103,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form ng-if="mode === 'edit' || mode === 'new'" name="ctrl.form">
|
||||
<form ng-if="mode === 'edit' || mode === 'new'" name="ctrl.form" aria-label="Variable editor Form">
|
||||
<h5 class="section-heading">General</h5>
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
@ -82,6 +117,7 @@
|
||||
ng-model="current.name"
|
||||
required
|
||||
ng-pattern="namePattern"
|
||||
aria-label="Variable editor Form Name field"
|
||||
/>
|
||||
</div>
|
||||
<div class="gf-form max-width-19">
|
||||
@ -97,6 +133,7 @@
|
||||
ng-model="current.type"
|
||||
ng-options="k as v.name for (k, v) in variableTypes"
|
||||
ng-change="typeChanged()"
|
||||
aria-label="Variable editor Form Type select"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
@ -111,7 +148,13 @@
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-19">
|
||||
<span class="gf-form-label width-6">Label</span>
|
||||
<input type="text" class="gf-form-input" ng-model="current.label" placeholder="optional display name" />
|
||||
<input
|
||||
type="text"
|
||||
class="gf-form-input"
|
||||
ng-model="current.label"
|
||||
placeholder="optional display name"
|
||||
aria-label="Variable editor Form Label field"
|
||||
/>
|
||||
</div>
|
||||
<div class="gf-form max-width-19">
|
||||
<span class="gf-form-label width-6">Hide</span>
|
||||
@ -120,6 +163,7 @@
|
||||
class="gf-form-input"
|
||||
ng-model="current.hide"
|
||||
ng-options="f.value as f.text for f in hideOptions"
|
||||
aria-label="Variable editor Form Hide select"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
@ -139,6 +183,7 @@
|
||||
ng-model-onblur
|
||||
ng-change="runQuery()"
|
||||
required
|
||||
aria-label="Variable editor Form Interval Query field"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -149,6 +194,7 @@
|
||||
label-class="width-9"
|
||||
checked="current.auto"
|
||||
on-change="runQuery()"
|
||||
aria-label="Variable editor Form Interval AutoOption switch"
|
||||
>
|
||||
</gf-form-switch>
|
||||
|
||||
@ -162,6 +208,7 @@
|
||||
ng-model="current.auto_count"
|
||||
ng-options="f for f in [1,2,3,4,5,10,20,30,40,50,100,200,300,400,500]"
|
||||
ng-change="runQuery()"
|
||||
aria-label="Variable editor Form Interval AutoCount select"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
@ -176,6 +223,7 @@
|
||||
ng-model="current.auto_min"
|
||||
ng-change="runQuery()"
|
||||
placeholder="10s"
|
||||
aria-label="Variable editor Form Interval AutoMin field"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -192,6 +240,7 @@
|
||||
ng-blur="runQuery()"
|
||||
placeholder="1, 10, 20, myvalue, escaped\,value"
|
||||
required
|
||||
aria-label="Variable editor Form Custom Query field"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -206,6 +255,7 @@
|
||||
ng-model="current.query"
|
||||
ng-blur="runQuery()"
|
||||
placeholder="your metric prefix"
|
||||
aria-label="Variable editor Form Constant Query field"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -220,6 +270,7 @@
|
||||
ng-model="current.query"
|
||||
ng-blur="runQuery()"
|
||||
placeholder="default value, if any"
|
||||
aria-label="Variable editor Form TextBox Query field"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -237,6 +288,7 @@
|
||||
ng-options="f.value as f.name for f in datasources"
|
||||
ng-change="datasourceChanged()"
|
||||
required
|
||||
aria-label="Variable editor Form Query DataSource select"
|
||||
>
|
||||
<option value="" ng-if="false"></option>
|
||||
</select>
|
||||
@ -255,6 +307,7 @@
|
||||
class="gf-form-input"
|
||||
ng-model="current.refresh"
|
||||
ng-options="f.value as f.text for f in refreshOptions"
|
||||
aria-label="Variable editor Form Query Refresh select"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
@ -278,6 +331,7 @@
|
||||
placeholder="/.*-(.*)-.*/"
|
||||
ng-model-onblur
|
||||
ng-change="runQuery()"
|
||||
aria-label="Variable editor Form Query RegEx field"
|
||||
/>
|
||||
</div>
|
||||
<div class="gf-form max-width-21">
|
||||
@ -293,6 +347,7 @@
|
||||
ng-model="current.sort"
|
||||
ng-options="f.value as f.text for f in sortOptions"
|
||||
ng-change="runQuery()"
|
||||
aria-label="Variable editor Form Query Sort select"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
@ -309,6 +364,7 @@
|
||||
ng-model="current.query"
|
||||
ng-options="f.value as f.text for f in datasourceTypes"
|
||||
ng-change="runQuery()"
|
||||
aria-label="Variable editor Form DataSource Query field"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
@ -330,6 +386,7 @@
|
||||
placeholder="/.*-(.*)-.*/"
|
||||
ng-model-onblur
|
||||
ng-change="runQuery()"
|
||||
aria-label="Variable editor Form DataSource RegEx field"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -345,6 +402,7 @@
|
||||
ng-options="f.value as f.name for f in datasources"
|
||||
required
|
||||
ng-change="validate()"
|
||||
aria-label="Variable editor Form AdHoc DataSource select"
|
||||
>
|
||||
<option value="" ng-if="false"></option>
|
||||
</select>
|
||||
@ -362,6 +420,7 @@
|
||||
tooltip="Enables multiple values to be selected at the same time"
|
||||
checked="current.multi"
|
||||
on-change="runQuery()"
|
||||
aria-label="Variable editor Form Multi switch"
|
||||
>
|
||||
</gf-form-switch>
|
||||
<gf-form-switch
|
||||
@ -370,12 +429,19 @@
|
||||
label-class="width-10"
|
||||
checked="current.includeAll"
|
||||
on-change="runQuery()"
|
||||
aria-label="Variable editor Form IncludeAll switch"
|
||||
>
|
||||
</gf-form-switch>
|
||||
</div>
|
||||
<div class="gf-form" ng-if="current.includeAll">
|
||||
<span class="gf-form-label width-10">Custom all value</span>
|
||||
<input type="text" class="gf-form-input max-width-15" ng-model="current.allValue" placeholder="blank = auto" />
|
||||
<input
|
||||
type="text"
|
||||
class="gf-form-input max-width-15"
|
||||
ng-model="current.allValue"
|
||||
placeholder="blank = auto"
|
||||
aria-label="Variable editor Form IncludeAll field"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -387,6 +453,7 @@
|
||||
label-class="width-10"
|
||||
checked="current.useTags"
|
||||
on-change="runQuery()"
|
||||
aria-label="Variable editor Form Query UseTags switch"
|
||||
>
|
||||
</gf-form-switch>
|
||||
<div class="gf-form last" ng-if="current.useTags">
|
||||
@ -397,6 +464,7 @@
|
||||
ng-model="current.tagsQuery"
|
||||
placeholder="metric name or tags query"
|
||||
ng-model-onblur
|
||||
aria-label="Variable editor Form Query TagsQuery field"
|
||||
/>
|
||||
</div>
|
||||
<div class="gf-form" ng-if="current.useTags">
|
||||
@ -407,6 +475,7 @@
|
||||
ng-model="current.tagValuesQuery"
|
||||
placeholder="apps.$tag.*"
|
||||
ng-model-onblur
|
||||
aria-label="Variable editor Form Query TagsValuesQuery field"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -415,21 +484,43 @@
|
||||
<h5>Preview of values</h5>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form" ng-repeat="option in current.options | limitTo: optionsLimit">
|
||||
<span class="gf-form-label">{{ option.text }}</span>
|
||||
<span class="gf-form-label" aria-label="Variable editor Preview of Values option">{{ option.text }}</span>
|
||||
</div>
|
||||
<div class="gf-form" ng-if="current.options.length > optionsLimit">
|
||||
<a class="gf-form-label btn-secondary" ng-click="showMoreOptions()">Show more</a>
|
||||
<a
|
||||
class="gf-form-label btn-secondary"
|
||||
ng-click="showMoreOptions()"
|
||||
aria-label="Variable editor Preview of Values Show More link"
|
||||
>
|
||||
Show more
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info gf-form-group" ng-if="infoText">
|
||||
<div class="alert alert-info gf-form-group" ng-if="infoText" aria-label="Variable editor Form Alert">
|
||||
{{ infoText }}
|
||||
</div>
|
||||
|
||||
<div class="gf-form-button-row p-y-0">
|
||||
<button type="submit" class="btn btn-primary" ng-show="mode === 'edit'" ng-click="update();">Update</button>
|
||||
<button type="submit" class="btn btn-primary" ng-show="mode === 'new'" ng-click="add();">Add</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
ng-show="mode === 'edit'"
|
||||
ng-click="update();"
|
||||
aria-label="Variable editor Update button"
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
ng-show="mode === 'new'"
|
||||
ng-click="add();"
|
||||
aria-label="Variable editor Add button"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -1,5 +1,9 @@
|
||||
<div class="variable-link-wrapper">
|
||||
<a ng-click="vm.show()" class="variable-value-link">
|
||||
<a
|
||||
ng-click="vm.show()"
|
||||
class="variable-value-link"
|
||||
aria-label="Dashboard template variables Variable Value DropDown value link"
|
||||
>
|
||||
{{vm.linkText}}
|
||||
<span ng-repeat="tag in vm.selectedTags" bs-tooltip='tag.valuesText' data-placement="bottom">
|
||||
<span class="label-tag"tag-color-from-name="tag.text">
|
||||
@ -12,16 +16,34 @@
|
||||
|
||||
<input type="text" class="gf-form-input" style="display: none" ng-keydown="vm.keyDown($event)" ng-model="vm.search.query" ng-change="vm.debouncedQueryChanged()" ></input>
|
||||
|
||||
<div class="variable-value-dropdown" ng-if="vm.dropdownVisible" ng-class="{'multi': vm.variable.multi, 'single': !vm.variable.multi}">
|
||||
<div
|
||||
class="variable-value-dropdown"
|
||||
ng-if="vm.dropdownVisible"
|
||||
ng-class="{'multi': vm.variable.multi, 'single': !vm.variable.multi}"
|
||||
aria-label="Dashboard template variables Variable Value DropDown DropDown"
|
||||
>
|
||||
<div class="variable-options-wrapper">
|
||||
<div class="variable-options-column">
|
||||
<a class="variable-options-column-header" ng-if="vm.variable.multi" ng-class="{'many-selected': vm.selectedValues.length > 1}" bs-tooltip="'Clear selections'" data-placement="top" ng-click="vm.clearSelections()">
|
||||
<a
|
||||
class="variable-options-column-header"
|
||||
ng-if="vm.variable.multi"
|
||||
ng-class="{'many-selected': vm.selectedValues.length > 1}"
|
||||
bs-tooltip="'Clear selections'"
|
||||
data-placement="top"
|
||||
ng-click="vm.clearSelections()"
|
||||
aria-label="Dashboard template variables Variable Value DropDown Selected link"
|
||||
>
|
||||
<span class="variable-option-icon"></span>
|
||||
Selected ({{vm.selectedValues.length}})
|
||||
</a>
|
||||
<a class="variable-option pointer" ng-repeat="option in vm.search.options" ng-class="{'selected': option.selected, 'highlighted': $index === vm.highlightIndex}" ng-click="vm.selectValue(option, $event)">
|
||||
<a
|
||||
class="variable-option pointer"
|
||||
ng-repeat="option in vm.search.options"
|
||||
ng-class="{'selected': option.selected, 'highlighted': $index === vm.highlightIndex}"
|
||||
ng-click="vm.selectValue(option, $event)"
|
||||
>
|
||||
<span class="variable-option-icon"></span>
|
||||
<span>{{option.text}}</span>
|
||||
<span aria-label="Dashboard template variables Variable Value DropDown option text">{{option.text}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="variable-options-column" ng-if="vm.tags.length">
|
||||
|
@ -1,12 +0,0 @@
|
||||
import { TestPage, ClickablePageObjectType, ClickablePageObject, Selector } from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface CreateDashboardPage {
|
||||
addQuery: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const createDashboardPage = new TestPage<CreateDashboardPage>({
|
||||
url: '/dashboard/new',
|
||||
pageObjects: {
|
||||
addQuery: new ClickablePageObject(Selector.fromAriaLabel('Add Query CTA button')),
|
||||
},
|
||||
});
|
@ -1,11 +0,0 @@
|
||||
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')),
|
||||
},
|
||||
});
|
@ -1,11 +0,0 @@
|
||||
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,23 +0,0 @@
|
||||
import {
|
||||
TestPage,
|
||||
ClickablePageObjectType,
|
||||
ClickablePageObject,
|
||||
Selector,
|
||||
InputPageObjectType,
|
||||
InputPageObject,
|
||||
PageObject,
|
||||
} from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface SaveDashboardModal {
|
||||
name: InputPageObjectType;
|
||||
save: ClickablePageObjectType;
|
||||
success: PageObject;
|
||||
}
|
||||
|
||||
export const saveDashboardModal = new TestPage<SaveDashboardModal>({
|
||||
pageObjects: {
|
||||
name: new InputPageObject(Selector.fromAriaLabel('Save dashboard title field')),
|
||||
save: new ClickablePageObject(Selector.fromAriaLabel('Save dashboard button')),
|
||||
success: new PageObject(Selector.fromSelector('.alert-success')),
|
||||
},
|
||||
});
|
@ -1,12 +0,0 @@
|
||||
import { TestPage, ClickablePageObject, Selector, ClickablePageObjectType } from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface AddDataSourcePage {
|
||||
testDataDB: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const addDataSourcePage = new TestPage<AddDataSourcePage>({
|
||||
url: '/datasources/new',
|
||||
pageObjects: {
|
||||
testDataDB: new ClickablePageObject(Selector.fromAriaLabel('TestData DB datasource plugin')),
|
||||
},
|
||||
});
|
@ -1,13 +0,0 @@
|
||||
import { ClickablePageObject, ClickablePageObjectType, Selector, TestPage } from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface DataSourcesPage {
|
||||
testData: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const dataSourcesPageFactory = (testDataSourceName: string) =>
|
||||
new TestPage<DataSourcesPage>({
|
||||
url: '/datasources',
|
||||
pageObjects: {
|
||||
testData: new ClickablePageObject(Selector.fromAriaLabel(`Data source list item for ${testDataSourceName}`)),
|
||||
},
|
||||
});
|
@ -1,28 +0,0 @@
|
||||
import {
|
||||
ClickablePageObject,
|
||||
ClickablePageObjectType,
|
||||
InputPageObject,
|
||||
InputPageObjectType,
|
||||
PageObject,
|
||||
PageObjectType,
|
||||
Selector,
|
||||
TestPage,
|
||||
} from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface EditDataSourcePage {
|
||||
name: InputPageObjectType;
|
||||
delete: ClickablePageObjectType;
|
||||
saveAndTest: ClickablePageObjectType;
|
||||
alert: PageObjectType;
|
||||
alertMessage: PageObjectType;
|
||||
}
|
||||
|
||||
export const editDataSourcePage = new TestPage<EditDataSourcePage>({
|
||||
pageObjects: {
|
||||
name: new InputPageObject(Selector.fromAriaLabel('Datasource settings page name input field')),
|
||||
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')),
|
||||
},
|
||||
});
|
@ -1,13 +0,0 @@
|
||||
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,11 +1,4 @@
|
||||
import {
|
||||
TestPage,
|
||||
SelectPageObjectType,
|
||||
SelectPageObject,
|
||||
Selector,
|
||||
ClickablePageObjectType,
|
||||
ClickablePageObject,
|
||||
} from '@grafana/toolkit/src/e2e';
|
||||
import { ClickablePageObjectType, Selector, SelectPageObjectType, TestPage } from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface EditPanelPage {
|
||||
queriesTab: ClickablePageObjectType;
|
||||
@ -17,12 +10,10 @@ export interface EditPanelPage {
|
||||
|
||||
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"] [label=Show] .gf-form-switch')
|
||||
),
|
||||
visualizationTab: new ClickablePageObject(Selector.fromAriaLabel('Visualization tab button')),
|
||||
queriesTab: 'Queries tab button',
|
||||
saveDashboard: 'Save dashboard navbar button',
|
||||
scenarioSelect: 'Scenario Select',
|
||||
showXAxis: () => Selector.fromSelector('[aria-label="X-Axis section"] [label=Show] .gf-form-switch'),
|
||||
visualizationTab: 'Visualization tab button',
|
||||
},
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { TestPage, ClickablePageObjectType, ClickablePageObject, Selector } from '@grafana/toolkit/src/e2e';
|
||||
import { ClickablePageObjectType, TestPage } from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface Panel {
|
||||
panelTitle: ClickablePageObjectType;
|
||||
@ -7,7 +7,7 @@ export interface Panel {
|
||||
|
||||
export const panel = new TestPage<Panel>({
|
||||
pageObjects: {
|
||||
panelTitle: new ClickablePageObject(Selector.fromAriaLabel('Panel Title')),
|
||||
share: new ClickablePageObject(Selector.fromAriaLabel('Share panel menu item')),
|
||||
panelTitle: 'Panel Title',
|
||||
share: 'Share panel menu item',
|
||||
},
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { TestPage, ClickablePageObjectType, ClickablePageObject, Selector } from '@grafana/toolkit/src/e2e';
|
||||
import { ClickablePageObjectType, TestPage } from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface SharePanelModal {
|
||||
directLinkRenderedImage: ClickablePageObjectType;
|
||||
@ -6,6 +6,6 @@ export interface SharePanelModal {
|
||||
|
||||
export const sharePanelModal = new TestPage<SharePanelModal>({
|
||||
pageObjects: {
|
||||
directLinkRenderedImage: new ClickablePageObject(Selector.fromAriaLabel('Link to rendered image')),
|
||||
directLinkRenderedImage: 'Link to rendered image',
|
||||
},
|
||||
});
|
||||
|
92
public/e2e-test/pages/templating/variablePage.ts
Normal file
92
public/e2e-test/pages/templating/variablePage.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import {
|
||||
ClickablePageObjectType,
|
||||
InputPageObjectType,
|
||||
PageObjectType,
|
||||
Selector,
|
||||
SelectPageObjectType,
|
||||
SwitchPageObjectType,
|
||||
TestPage,
|
||||
} from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface VariablePage {
|
||||
headerLink: ClickablePageObjectType;
|
||||
modeLabel: PageObjectType;
|
||||
generalNameInput: InputPageObjectType;
|
||||
generalTypeSelect: SelectPageObjectType;
|
||||
generalLabelInput: InputPageObjectType;
|
||||
generalHideSelect: SelectPageObjectType;
|
||||
queryOptionsDataSourceSelect: SelectPageObjectType;
|
||||
queryOptionsRefreshSelect: SelectPageObjectType;
|
||||
queryOptionsRegExInput: InputPageObjectType;
|
||||
queryOptionsSortSelect: SelectPageObjectType;
|
||||
queryOptionsQueryInput: InputPageObjectType;
|
||||
selectionOptionsMultiSwitch: SwitchPageObjectType;
|
||||
selectionOptionsIncludeAllSwitch: SwitchPageObjectType;
|
||||
selectionOptionsCustomAllInput: InputPageObjectType;
|
||||
valueGroupsTagsEnabledSwitch: SwitchPageObjectType;
|
||||
valueGroupsTagsTagsQueryInput: InputPageObjectType;
|
||||
valueGroupsTagsTagsValuesQueryInput: InputPageObjectType;
|
||||
previewOfValuesOption: PageObjectType;
|
||||
addButton: ClickablePageObjectType;
|
||||
updateButton: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const variablePage = new TestPage<VariablePage>({
|
||||
pageObjects: {
|
||||
headerLink: 'Variable editor Header link',
|
||||
modeLabel: 'Variable editor Header mode New',
|
||||
generalNameInput: 'Variable editor Form Name field',
|
||||
generalTypeSelect: 'Variable editor Form Type select',
|
||||
generalLabelInput: 'Variable editor Form Label field',
|
||||
generalHideSelect: 'Variable editor Form Hide select',
|
||||
queryOptionsDataSourceSelect: 'Variable editor Form Query DataSource select',
|
||||
queryOptionsRefreshSelect: 'Variable editor Form Query Refresh select',
|
||||
queryOptionsRegExInput: 'Variable editor Form Query RegEx field',
|
||||
queryOptionsSortSelect: 'Variable editor Form Query Sort select',
|
||||
queryOptionsQueryInput: 'Variable editor Form Default Variable Query Editor textarea',
|
||||
selectionOptionsMultiSwitch: () => Selector.fromSwitchLabel('Variable editor Form Multi switch'),
|
||||
selectionOptionsIncludeAllSwitch: () => Selector.fromSwitchLabel('Variable editor Form IncludeAll switch'),
|
||||
selectionOptionsCustomAllInput: 'Variable editor Form IncludeAll field',
|
||||
valueGroupsTagsEnabledSwitch: () => Selector.fromSwitchLabel('Variable editor Form Query UseTags switch'),
|
||||
valueGroupsTagsTagsQueryInput: 'Variable editor Form Query TagsQuery field',
|
||||
valueGroupsTagsTagsValuesQueryInput: 'Variable editor Form Query TagsValuesQuery field',
|
||||
previewOfValuesOption: 'Variable editor Preview of Values option',
|
||||
addButton: 'Variable editor Add button',
|
||||
updateButton: 'Variable editor Update button',
|
||||
},
|
||||
});
|
||||
|
||||
export interface CreateQueryVariableArguments {
|
||||
page: TestPage<VariablePage>;
|
||||
name: string;
|
||||
label: string;
|
||||
datasourceName: string;
|
||||
query: string;
|
||||
}
|
||||
|
||||
export const createQueryVariable = async ({
|
||||
page,
|
||||
name,
|
||||
label,
|
||||
datasourceName,
|
||||
query,
|
||||
}: CreateQueryVariableArguments) => {
|
||||
console.log('Creating a Query Variable with required');
|
||||
await page.pageObjects.generalNameInput.enter(name);
|
||||
await page.pageObjects.generalLabelInput.enter(label);
|
||||
await page.pageObjects.queryOptionsDataSourceSelect.select(`string:${datasourceName}`);
|
||||
await page.pageObjects.queryOptionsQueryInput.exists();
|
||||
await page.pageObjects.queryOptionsQueryInput.containsPlaceholder('metric name or tags query');
|
||||
await page.pageObjects.queryOptionsQueryInput.enter(query);
|
||||
await page.pageObjects.queryOptionsQueryInput.blur();
|
||||
await page.pageObjects.previewOfValuesOption.exists();
|
||||
await page.pageObjects.selectionOptionsMultiSwitch.toggle();
|
||||
await page.pageObjects.selectionOptionsMultiSwitch.isSwitchedOn();
|
||||
await page.pageObjects.selectionOptionsIncludeAllSwitch.toggle();
|
||||
await page.pageObjects.selectionOptionsIncludeAllSwitch.isSwitchedOn();
|
||||
await page.pageObjects.selectionOptionsCustomAllInput.exists();
|
||||
await page.pageObjects.selectionOptionsCustomAllInput.containsText('');
|
||||
await page.pageObjects.selectionOptionsCustomAllInput.containsPlaceholder('blank = auto');
|
||||
await page.pageObjects.addButton.click();
|
||||
console.log('Creating a Query Variable with required, OK!');
|
||||
};
|
49
public/e2e-test/pages/templating/variablesPage.ts
Normal file
49
public/e2e-test/pages/templating/variablesPage.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { ArrayPageObjectType, ClickablePageObjectType, TestPage } from '@grafana/toolkit/src/e2e';
|
||||
|
||||
export interface VariablesPage {
|
||||
callToActionButton: ClickablePageObjectType;
|
||||
variableTableNameField: ArrayPageObjectType;
|
||||
variableTableDefinitionField: ArrayPageObjectType;
|
||||
variableTableArrowUpButton: ArrayPageObjectType;
|
||||
variableTableArrowDownButton: ArrayPageObjectType;
|
||||
variableTableDuplicateButton: ArrayPageObjectType;
|
||||
variableTableRemoveButton: ArrayPageObjectType;
|
||||
newVariableButton: ClickablePageObjectType;
|
||||
goBackButton: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const variablesPage = new TestPage<VariablesPage>({
|
||||
pageObjects: {
|
||||
callToActionButton: 'Call to action button Add variable',
|
||||
variableTableNameField: 'Variable editor Table Name field',
|
||||
variableTableDefinitionField: 'Variable editor Table Definition field',
|
||||
variableTableArrowUpButton: 'Variable editor Table ArrowUp button',
|
||||
variableTableArrowDownButton: 'Variable editor Table ArrowDown button',
|
||||
variableTableDuplicateButton: 'Variable editor Table Duplicate button',
|
||||
variableTableRemoveButton: 'Variable editor Table Remove button',
|
||||
newVariableButton: 'Variable editor New variable button',
|
||||
goBackButton: 'Dashboard settings Go Back button',
|
||||
},
|
||||
});
|
||||
|
||||
export interface AssertVariableTableArguments {
|
||||
name: string;
|
||||
query: string;
|
||||
}
|
||||
|
||||
export const assertVariableTable = async (page: TestPage<VariablesPage>, args: AssertVariableTableArguments[]) => {
|
||||
console.log('Asserting variable table');
|
||||
await page.pageObjects.variableTableNameField.waitForSelector();
|
||||
await page.pageObjects.variableTableNameField.hasLength(args.length);
|
||||
await page.pageObjects.variableTableDefinitionField.hasLength(args.length);
|
||||
await page.pageObjects.variableTableArrowUpButton.hasLength(args.length);
|
||||
await page.pageObjects.variableTableArrowDownButton.hasLength(args.length);
|
||||
await page.pageObjects.variableTableDuplicateButton.hasLength(args.length);
|
||||
await page.pageObjects.variableTableRemoveButton.hasLength(args.length);
|
||||
for (let index = 0; index < args.length; index++) {
|
||||
const { name, query } = args[index];
|
||||
await page.pageObjects.variableTableNameField.containsTextAtPos(`$${name}`, index);
|
||||
await page.pageObjects.variableTableDefinitionField.containsTextAtPos(query, index);
|
||||
}
|
||||
console.log('Asserting variable table, Ok');
|
||||
};
|
@ -1,50 +1,19 @@
|
||||
import { Browser, Page, Target } from 'puppeteer-core';
|
||||
|
||||
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 { 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 {
|
||||
cleanDashboard,
|
||||
createDashboardPage,
|
||||
dashboardsPageFactory,
|
||||
saveDashboardModal,
|
||||
} from '@grafana/toolkit/src/e2e/pages';
|
||||
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';
|
||||
|
||||
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.name.enter(testDataSourceName);
|
||||
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}"]`;
|
||||
|
||||
const dataSourcesPage = dataSourcesPageFactory(testDataSourceName);
|
||||
await dataSourcesPage.init(page);
|
||||
await dataSourcesPage.navigateTo();
|
||||
await dataSourcesPage.expectSelector({ selector });
|
||||
|
||||
return testDataSourceName;
|
||||
};
|
||||
|
||||
const addDashboardAndSetupTestDataGraph = async (page: Page) => {
|
||||
export const addDashboardAndSetupTestDataGraph = async (page: Page) => {
|
||||
// Create a new Dashboard
|
||||
const dashboardTitle = `Dashboard-${new Date().getTime()}`;
|
||||
const dashboardTitle = `e2e - Dashboard-${new Date().getTime()}`;
|
||||
await createDashboardPage.init(page);
|
||||
await createDashboardPage.navigateTo();
|
||||
await createDashboardPage.pageObjects.addQuery.click();
|
||||
@ -67,7 +36,11 @@ const addDashboardAndSetupTestDataGraph = async (page: Page) => {
|
||||
return dashboardTitle;
|
||||
};
|
||||
|
||||
const clickOnSharePanelImageLinkAndCompareImages = async (browser: Browser, page: Page, dashboardTitle: string) => {
|
||||
export const clickOnSharePanelImageLinkAndCompareImages = async (
|
||||
browser: Browser,
|
||||
page: Page,
|
||||
dashboardTitle: string
|
||||
) => {
|
||||
// Share the dashboard
|
||||
const dashboardsPage = dashboardsPageFactory(dashboardTitle);
|
||||
await dashboardsPage.init(page);
|
||||
@ -95,47 +68,14 @@ const clickOnSharePanelImageLinkAndCompareImages = async (browser: Browser, page
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
e2eScenario({
|
||||
describeName: 'Smoke tests',
|
||||
itName: 'Login scenario, create test data source, dashboard, panel, and export scenario',
|
||||
skipScenario: false,
|
||||
createTestDataSource: true,
|
||||
scenario: async (browser: Browser, page: Page) => {
|
||||
const dashboardTitle = await addDashboardAndSetupTestDataGraph(page);
|
||||
await clickOnSharePanelImageLinkAndCompareImages(browser, page, dashboardTitle);
|
||||
await cleanUpTestDataSource(page, testDataSourceName);
|
||||
await cleanDashboard(page, dashboardTitle);
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
@ -0,0 +1,103 @@
|
||||
import { e2eScenario, TestPage } from '@grafana/toolkit/src/e2e';
|
||||
import { Browser, Page } from 'puppeteer-core';
|
||||
import {
|
||||
assertVariableLabelsAndComponents,
|
||||
DashboardPage,
|
||||
dashboardSettingsPage,
|
||||
saveChangesDashboardModal,
|
||||
} from '@grafana/toolkit/src/e2e/pages';
|
||||
|
||||
import { assertVariableTable, variablesPage } from '../../pages/templating/variablesPage';
|
||||
import { createQueryVariable, variablePage } from '../../pages/templating/variablePage';
|
||||
|
||||
e2eScenario({
|
||||
describeName: 'Template Variables tests',
|
||||
itName: 'Template Variables QueryVariable CRUD',
|
||||
createTestDataSource: true,
|
||||
createTestDashboard: true,
|
||||
scenario: async (browser: Browser, page: Page, datasourceName?: string, dashboardPage?: TestPage<DashboardPage>) => {
|
||||
await dashboardPage.pageObjects.settings.click();
|
||||
|
||||
await dashboardSettingsPage.init(page);
|
||||
await dashboardSettingsPage.pageObjects.variablesSection.click();
|
||||
|
||||
await variablesPage.init(page);
|
||||
await variablesPage.pageObjects.callToActionButton.exists();
|
||||
await variablesPage.pageObjects.callToActionButton.click();
|
||||
|
||||
console.log('Asserting defaults for new variable');
|
||||
await variablePage.init(page);
|
||||
await variablePage.pageObjects.generalNameInput.exists();
|
||||
await variablePage.pageObjects.generalNameInput.containsText('');
|
||||
await variablePage.pageObjects.generalNameInput.containsPlaceholder('name');
|
||||
await variablePage.pageObjects.generalTypeSelect.exists();
|
||||
await variablePage.pageObjects.generalTypeSelect.selectedTextIs('Query');
|
||||
await variablePage.pageObjects.generalLabelInput.exists();
|
||||
await variablePage.pageObjects.generalLabelInput.containsText('');
|
||||
await variablePage.pageObjects.generalLabelInput.containsPlaceholder('optional display name');
|
||||
await variablePage.pageObjects.generalHideSelect.exists();
|
||||
await variablePage.pageObjects.generalHideSelect.selectedTextIs('');
|
||||
await variablePage.pageObjects.queryOptionsDataSourceSelect.exists();
|
||||
await variablePage.pageObjects.queryOptionsDataSourceSelect.selectedTextIs('');
|
||||
await variablePage.pageObjects.queryOptionsRefreshSelect.exists();
|
||||
await variablePage.pageObjects.queryOptionsRefreshSelect.selectedTextIs('Never');
|
||||
await variablePage.pageObjects.queryOptionsRegExInput.exists();
|
||||
await variablePage.pageObjects.queryOptionsRegExInput.containsText('');
|
||||
await variablePage.pageObjects.queryOptionsRegExInput.containsPlaceholder('/.*-(.*)-.*/');
|
||||
await variablePage.pageObjects.queryOptionsSortSelect.exists();
|
||||
await variablePage.pageObjects.queryOptionsSortSelect.selectedTextIs('Disabled');
|
||||
await variablePage.pageObjects.selectionOptionsMultiSwitch.exists();
|
||||
await variablePage.pageObjects.selectionOptionsMultiSwitch.isSwitchedOff();
|
||||
await variablePage.pageObjects.selectionOptionsIncludeAllSwitch.exists();
|
||||
await variablePage.pageObjects.selectionOptionsIncludeAllSwitch.isSwitchedOff();
|
||||
await variablePage.pageObjects.valueGroupsTagsEnabledSwitch.exists();
|
||||
await variablePage.pageObjects.valueGroupsTagsEnabledSwitch.isSwitchedOff();
|
||||
console.log('Asserting defaults for new variable, OK!');
|
||||
|
||||
await variablesPage.pageObjects.goBackButton.click();
|
||||
|
||||
await dashboardPage.pageObjects.settings.click();
|
||||
|
||||
await dashboardSettingsPage.init(page);
|
||||
await dashboardSettingsPage.pageObjects.variablesSection.click();
|
||||
|
||||
await variablesPage.pageObjects.callToActionButton.exists();
|
||||
await variablesPage.pageObjects.callToActionButton.click();
|
||||
|
||||
const queryVariables = [
|
||||
{ name: 'query1', query: '*', label: 'query1-label', options: ['All', 'A', 'B', 'C'] },
|
||||
{ name: 'query2', query: '$query1.*', label: 'query2-label', options: ['All', 'AA', 'AB', 'AC'] },
|
||||
{ name: 'query3', query: '$query1.$query2.*', label: 'query2-label', options: ['All', 'AAA', 'AAB', 'AAC'] },
|
||||
];
|
||||
|
||||
for (let queryVariableIndex = 0; queryVariableIndex < queryVariables.length; queryVariableIndex++) {
|
||||
const { name, label, query } = queryVariables[queryVariableIndex];
|
||||
const asserts = queryVariables.slice(0, queryVariableIndex + 1);
|
||||
await createQueryVariable({
|
||||
page: variablePage,
|
||||
datasourceName,
|
||||
name,
|
||||
label,
|
||||
query,
|
||||
});
|
||||
|
||||
await assertVariableTable(variablesPage, asserts);
|
||||
|
||||
await dashboardSettingsPage.pageObjects.saveDashBoard.click();
|
||||
|
||||
await saveChangesDashboardModal.init(page);
|
||||
await saveChangesDashboardModal.pageObjects.save.click();
|
||||
await saveChangesDashboardModal.pageObjects.success.exists();
|
||||
|
||||
await variablesPage.pageObjects.goBackButton.click();
|
||||
|
||||
await assertVariableLabelsAndComponents(dashboardPage, asserts);
|
||||
|
||||
await dashboardPage.pageObjects.settings.click();
|
||||
|
||||
await dashboardSettingsPage.pageObjects.variablesSection.click();
|
||||
|
||||
await variablesPage.pageObjects.newVariableButton.click();
|
||||
}
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue
Block a user