E2E: improvements to the importDashboard flow (#39693)

* E2E: improvements to the importDashboard flow
This commit is contained in:
Sarah Zinger
2021-09-29 10:27:49 -04:00
committed by GitHub
parent 012d4f0905
commit aeeaa67b0a
9 changed files with 281 additions and 132 deletions
+210
View File
@@ -0,0 +1,210 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": 321,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 6,
"options": {
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"showThresholdLabels": false,
"showThresholdMarkers": true,
"text": {}
},
"pluginVersion": "8.3.0-pre",
"title": "Gauge Example",
"type": "gauge"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 4,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"text": {},
"textMode": "auto"
},
"pluginVersion": "8.3.0-pre",
"title": "Stat",
"type": "stat"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 16
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"title": "Time series example",
"type": "timeseries"
}
],
"refresh": false,
"schemaVersion": 31,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "2021-09-01T04:00:00.000Z",
"to": "2021-09-15T04:00:00.000Z"
},
"timepicker": {},
"timezone": "",
"title": "E2E Test - Import Dashboard",
"uid": "kquZN5H7k",
"version": 4
}
+3 -113
View File
@@ -1,122 +1,12 @@
import { e2e } from '@grafana/e2e';
e2e.scenario({
describeName: 'Import Dashboard Test',
itName: 'Ensure you can import a dashboard',
describeName: 'Import Dashboards Test',
itName: 'Ensure you can import a number of json test dashboards from a specific test directory',
addScenarioDataSource: false,
addScenarioDashBoard: false,
skipScenario: false,
scenario: () => {
e2e.flows.importDashboard(TEST_DASHBOARD);
e2e.flows.importDashboards('/dashboards', 1000);
},
});
const TEST_DASHBOARD = {
annotations: {
list: [
{
builtIn: 1,
datasource: '-- Grafana --',
enable: true,
hide: true,
iconColor: 'rgba(0, 211, 255, 1)',
name: 'Annotations & Alerts',
type: 'dashboard',
},
],
},
editable: true,
gnetId: null,
graphTooltip: 0,
id: 74,
links: [],
panels: [
{
datasource: null,
fieldConfig: {
defaults: {
color: {
mode: 'palette-classic',
},
custom: {
axisLabel: '',
axisPlacement: 'auto',
barAlignment: 0,
drawStyle: 'line',
fillOpacity: 0,
gradientMode: 'none',
hideFrom: {
legend: false,
tooltip: false,
viz: false,
},
lineInterpolation: 'linear',
lineWidth: 1,
pointSize: 5,
scaleDistribution: {
type: 'linear',
},
showPoints: 'auto',
spanNulls: false,
stacking: {
group: 'A',
mode: 'none',
},
thresholdsStyle: {
mode: 'off',
},
},
mappings: [],
thresholds: {
mode: 'absolute',
steps: [
{
color: 'green',
value: null,
},
{
color: 'red',
value: 80,
},
],
},
},
overrides: [],
},
gridPos: {
h: 9,
w: 12,
x: 0,
y: 0,
},
id: 2,
options: {
legend: {
calcs: [],
displayMode: 'list',
placement: 'bottom',
},
tooltip: {
mode: 'single',
},
},
title: 'Panel Title',
type: 'timeseries',
},
],
schemaVersion: 30,
style: 'dark',
tags: [],
templating: {
list: [],
},
time: {
from: '2021-06-30T04:00:00.000Z',
to: '2021-07-02T03:59:59.000Z',
},
timepicker: {},
timezone: '',
title: 'An imported dashboard for e2e tests',
uid: '6V0Nzyz7k',
version: 1,
};
+2 -1
View File
@@ -3,5 +3,6 @@
"types": ["cypress"]
},
"extends": "../../tsconfig.json",
"include": ["**/*.ts", "../../packages/grafana-e2e/cypress/support/index.d.ts"]
"include": ["**/*.ts", "../../packages/grafana-e2e/cypress/support/index.d.ts"],
"resolveJsonModule": true
}
@@ -1,3 +1,6 @@
const fs = require('fs');
const path = require('path');
const compareScreenshots = require('./compareScreenshots');
const extendConfig = require('./extendConfig');
const readProvisions = require('./readProvisions');
@@ -12,6 +15,18 @@ module.exports = (on, config) => {
return null;
},
});
on('task', {
getJSONFilesFromDir: async ({ projectPath, relativePath }) => {
const directoryPath = path.join(projectPath, relativePath);
const jsonFiles = fs.readdirSync(directoryPath);
return jsonFiles
.filter((fileName) => /.json$/i.test(fileName))
.map((fileName) => {
const fileBuffer = fs.readFileSync(path.join(directoryPath, fileName));
return JSON.parse(fileBuffer);
});
},
});
// Always extend with this library's config and return for diffing
// @todo remove this when possible: https://github.com/cypress-io/cypress/issues/5674
@@ -23,3 +23,10 @@ Cypress.Commands.add('readProvisions', (filePaths: string[]) => {
filePaths,
});
});
Cypress.Commands.add('getJSONFilesFromDir', (dirPath: string) => {
return cy.task('getJSONFilesFromDir', {
projectPath: Cypress.config().parentTestsFolder,
relativePath: dirPath,
});
});
+1
View File
@@ -5,5 +5,6 @@ declare namespace Cypress {
compareScreenshots(config: CompareScreenshotsConfig | string): Chainable;
logToConsole(message: string, optional?: any): void;
readProvisions(filePaths: string[]): Chainable;
getJSONFilesFromDir(dirPath: string): Chainable;
}
}
@@ -7,13 +7,14 @@ type Panel = {
[key: string]: unknown;
};
type Dashboard = { title: string; panels: Panel[]; uid: string; [key: string]: unknown };
export type Dashboard = { title: string; panels: Panel[]; uid: string; [key: string]: unknown };
/**
* Smoke test a datasource by quickly importing a test dashboard for it
* Smoke test a particular dashboard by quickly importing a json file and validate that all the panels finish loading
* @param dashboardToImport a sample dashboard
* @param queryTimeout a number of ms to wait for the imported dashboard to finish loading
*/
export const importDashboard = (dashboardToImport: Dashboard) => {
export const importDashboard = (dashboardToImport: Dashboard, queryTimeout?: number) => {
e2e().visit(fromBaseUrl('/dashboard/import'));
// Note: normally we'd use 'click' and then 'type' here, but the json object is so big that using 'val' is much faster
@@ -24,7 +25,9 @@ export const importDashboard = (dashboardToImport: Dashboard) => {
e2e.components.DashboardImportPage.submit().should('be.visible').click();
e2e.components.ImportDashboardForm.name().should('be.visible').click().clear().type(dashboardToImport.title);
e2e.components.ImportDashboardForm.submit().should('be.visible').click();
e2e().wait(3000);
// wait for dashboard to load
e2e().wait(queryTimeout || 6000);
// save the newly imported dashboard to context so it'll get properly deleted later
e2e()
@@ -42,19 +45,21 @@ export const importDashboard = (dashboardToImport: Dashboard) => {
expect(dashboardToImport.uid).to.equal(uid);
});
// inspect first panel and verify data has been processed for it
e2e.components.Panels.Panel.title(dashboardToImport.panels[0].title).should('be.visible').click();
e2e.components.Panels.Panel.headerItems('Inspect').should('be.visible').click();
e2e.components.Tab.title('JSON').should('be.visible').click();
e2e().wait(3000);
e2e.components.PanelInspector.Json.content().should('be.visible').contains('Panel JSON').click();
e2e().wait(3000);
e2e.components.Select.option().should('be.visible').contains('Data').click();
e2e().wait(3000);
dashboardToImport.panels.forEach((panel) => {
// Look at the json data
e2e.components.Panels.Panel.title(panel.title).should('be.visible').click();
e2e.components.Panels.Panel.headerItems('Inspect').should('be.visible').click();
e2e.components.Tab.title('JSON').should('be.visible').click();
e2e.components.PanelInspector.Json.content().should('be.visible').contains('Panel JSON').click();
e2e.components.Select.option().should('be.visible').contains('Data').click();
// ensures that panel has loaded without knowingly hitting an error
// note: this does not prove that data came back as we expected it,
// it could get `state: Done` for no data for example
// but it ensures we didn't hit a 401 or 500 or something like that
e2e.components.CodeEditor.container().should('be.visible').contains('"state": "Done"');
// ensures that panel has loaded without knowingly hitting an error
// note: this does not prove that data came back as we expected it,
// it could get `state: Done` for no data for example
// but it ensures we didn't hit a 401 or 500 or something like that
e2e.components.CodeEditor.container().should('be.visible').contains('"state": "Done"');
// need to close panel
e2e.components.Drawer.General.close().click();
});
};
@@ -0,0 +1,19 @@
import { importDashboard, Dashboard } from './importDashboard';
import { e2e } from '../index';
/**
* Smoke test several dashboard json files from a test directory
* and validate that all the panels in each import finish loading their queries
* @param dirPath the relative path to a directory which contains json files representing dashboards,
* for example if your dashboards live in `cypress/testDashboards` you can pass `/testDashboards`
* @param queryTimeout a number of ms to wait for the imported dashboard to finish loading
*/
export const importDashboards = async (dirPath: string, queryTimeout?: number) => {
e2e()
.getJSONFilesFromDir(dirPath)
.then((jsonFiles: Dashboard[]) => {
jsonFiles.forEach((file) => {
importDashboard(file, queryTimeout || 6000);
});
});
};
+1
View File
@@ -13,6 +13,7 @@ export * from './revertAllChanges';
export * from './saveDashboard';
export * from './selectOption';
export * from './importDashboard';
export * from './importDashboards';
export {
VISUALIZATION_ALERT_LIST,