New dashboard tests using elasticdump

This commit is contained in:
LeeDr 2016-05-26 16:32:53 -05:00
parent 6981325f7a
commit 988be91266
10 changed files with 344 additions and 8 deletions

8
kibana4.json Normal file

File diff suppressed because one or more lines are too long

1
mapping_kibana4.json Normal file
View file

@ -0,0 +1 @@
{".kibana":{"mappings":{"config":{"properties":{"buildNum":{"type":"keyword"}}},"index-pattern":{"properties":{"fields":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"timeFieldName":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"title":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}}},"search":{"properties":{"columns":{"type":"text"},"description":{"type":"text"},"hits":{"type":"integer"},"kibanaSavedObjectMeta":{"properties":{"searchSourceJSON":{"type":"text"}}},"sort":{"type":"text"},"title":{"type":"text"},"version":{"type":"integer"}}},"visualization":{"properties":{"description":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"kibanaSavedObjectMeta":{"properties":{"searchSourceJSON":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}}},"title":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"uiStateJSON":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"version":{"type":"integer"},"visState":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}}},"server":{"properties":{"uuid":{"type":"keyword"}}},"dashboard":{"properties":{"description":{"type":"text"},"hits":{"type":"integer"},"kibanaSavedObjectMeta":{"properties":{"searchSourceJSON":{"type":"text"}}},"optionsJSON":{"type":"text"},"panelsJSON":{"type":"text"},"timeFrom":{"type":"text"},"timeRestore":{"type":"boolean"},"timeTo":{"type":"text"},"title":{"type":"text"},"uiStateJSON":{"type":"text"},"version":{"type":"integer"}}}}}}

View file

@ -151,6 +151,7 @@
"auto-release-sinon": "1.0.3",
"babel-eslint": "4.1.8",
"chokidar": "1.4.3",
"elasticdump": "2.1.1",
"eslint": "1.10.3",
"eslint-plugin-mocha": "1.1.0",
"expect.js": "0.3.1",

View file

@ -140,4 +140,52 @@ ScenarioManager.prototype.loadIfEmpty = function (id) {
});
};
/**
* Add fields to the config doc (like setting timezone and defaultIndex)
* @return {Promise} A promise that is resolved when elasticsearch has a response
*/
ScenarioManager.prototype.updateConfigDoc = function (docMap) {
// first we need to get the config doc's id so we can use it in our _update call
var self = this;
var configId;
var docMapString = JSON.stringify(docMap);
return this.client.search({
index: '.kibana',
type: 'config'
})
.then(function (response) {
if (response.errors) {
throw new Error(
'get config failed\n' +
response.items
.map(i => i[Object.keys(i)[0]].error)
.filter(Boolean)
.map(err => ' ' + JSON.stringify(err))
.join('\n')
);
} else {
configId = response.hits.hits[0]._id;
console.log('config._id =' + configId);
}
})
// now that we have the id, we can update
// return scenarioManager.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'});
.then(function (response) {
console.log('updating config with ' + docMapString);
return self.client.update({
index: '.kibana',
type: 'config',
id: configId,
body: {
'doc':
docMap
}
});
})
.catch(function (err) {
throw err;
});
};
module.exports = ScenarioManager;

View file

@ -2,8 +2,6 @@ import {
bdd,
scenarioManager,
common,
// settingsPage,
// headerPage,
consolePage
} from '../../../support';

View file

@ -8,10 +8,6 @@ import { bdd, remote, scenarioManager, defaultTimeout } from '../../../support';
return remote.setWindowSize(1200,800);
});
bdd.after(function unloadMakelogs() {
return scenarioManager.unload('logstashFunctional');
});
require('./_console');
});
}());

View file

@ -24,7 +24,8 @@ define(function (require) {
'intern/dojo/node!./status_page',
'intern/dojo/node!./apps/settings',
'intern/dojo/node!./apps/visualize',
'intern/dojo/node!./apps/console'
'intern/dojo/node!./apps/console',
'intern/dojo/node!./apps/dashboard'
], function () {});
});
});

View file

@ -5,6 +5,7 @@ import DiscoverPage from './pages/discover_page';
import SettingsPage from './pages/settings_page';
import HeaderPage from './pages/header_page';
import VisualizePage from './pages/visualize_page';
import DashboardPage from './pages/dashboard_page';
import ShieldPage from './pages/shield_page';
import ConsolePage from './pages/console_page';
@ -24,6 +25,7 @@ defineDelayedExport('discoverPage', () => new DiscoverPage());
defineDelayedExport('headerPage', () => new HeaderPage());
defineDelayedExport('settingsPage', () => new SettingsPage());
defineDelayedExport('visualizePage', () => new VisualizePage());
defineDelayedExport('dashboardPage', () => new DashboardPage());
defineDelayedExport('shieldPage', () => new ShieldPage());
defineDelayedExport('consolePage', () => new ConsolePage());

View file

@ -11,6 +11,13 @@ export default (function () {
var format = require('url').format;
var util = require('util');
var path = require('path');
var url = require('url');
var resolve = require('path').resolve;
var __dirname = path.resolve(path.dirname());
var __pwd = path.resolve('.');
var bin = resolve(__dirname, '../../node_modules/.bin/elasticdump');
var Elasticdump = require('elasticdump').elasticdump;
var kIndex = '.kibana';
function injectTimestampQuery(func, url) {
var formatted = modifyQueryString(url, function (parsed) {
@ -279,7 +286,81 @@ export default (function () {
return this.remote
.setFindTimeout(defaultFindTimeout)
.findDisplayedByCssSelector(testSubjSelector(selector));
}
},
/*
** This function is basically copied from
** https://github.com/taskrabbit/elasticsearch-dump/blob/master/bin/elasticdump
** and allows calling elasticdump for importing or exporting data from Elasticsearch
*/
elasticdumpModule: function elasticdumpModule(myinput, myoutput, index, mytype) {
var self = this;
var options = {
limit: 100,
offset: 0,
debug: false,
type: mytype,
delete: false,
all: false,
maxSockets: null,
input: myinput,
'input-index': null,
output: myoutput,
'output-index': index,
inputTransport: null,
outputTransport: null,
searchBody: null,
sourceOnly: false,
jsonLines: false,
format: '',
'ignore-errors': false,
scrollTime: '10m',
timeout: null,
skip: null,
toLog: null,
};
self.debug(options);
var dumper = new Elasticdump(options.input, options.output, options);
dumper.on('log', function (message) { self.debug(message); });
dumper.on('debug', function (message) { self.debug(message); });
dumper.on('error', function (error) { self.debug('error', 'Error Emitted => ' + (error.message || JSON.stringify(error))); });
var promise = new Promise(function (resolve, reject) {
dumper.dump(function (error, totalWrites) {
if (error) {
self.debug('THERE WAS AN ERROR :-(');
reject(Error(error));
} else {
resolve ('elasticdumpModule success');
}
});
});
return promise;
},
elasticDump: function elasticDump(index, file) {
var self = this;
self.debug('Dumping mapping from ' + url.format(config.servers.elasticsearch) + '/' + index + ' to (mapping_' + file + ')');
return this.elasticdumpModule(url.format(config.servers.elasticsearch), 'mapping_' + file, index, 'mapping')
.then(function () {
self.debug('Dumping data from ' + url.format(config.servers.elasticsearch) + '/' + index + ' to (' + file + ')');
return self.elasticdumpModule(url.format(config.servers.elasticsearch), file,index, 'data');
});
},
elasticLoad: function elasticLoad(file, index) {
// TODO: should we have a flag to delete the index first?
// or use scenarioManager.unload(index) ?
var self = this;
self.debug('Loading mapping (mapping_' + file + ') into ' + url.format(config.servers.elasticsearch) + '/' + index);
return this.elasticdumpModule('mapping_' + file, url.format(config.servers.elasticsearch), index, 'mapping')
.then(function () {
self.debug('Loading data (' + file + ')');
return self.elasticdumpModule(file, url.format(config.servers.elasticsearch), index, 'data');
});
},
};
return Common;

View file

@ -0,0 +1,200 @@
import { remote, common, defaultFindTimeout } from '../';
export default (function () {
var thisTime;
function DashboardPage() {
this.remote = remote;
thisTime = this.remote.setFindTimeout(defaultFindTimeout);
}
DashboardPage.prototype = {
constructor: DashboardPage,
clickNewDashboard: function clickNewDashboard() {
return thisTime
.findByCssSelector('button.ng-scope[aria-label="New Dashboard"]')
.click();
},
clickAddVisualization: function clickAddVisualization() {
return thisTime
.findByCssSelector('button.ng-scope[aria-label="Add a panel to the dashboard"]')
.click();
},
filterVizNames: function filterVizNames(vizName) {
return thisTime
.findByCssSelector('input[placeholder="Visualizations Filter..."]')
.click()
.pressKeys(vizName);
},
clickVizNameLink: function clickVizNameLink(vizName) {
return thisTime
.findByLinkText(vizName)
.click();
},
closeAddVizualizationPanel: function closeAddVizualizationPanel() {
common.debug('-------------close panel');
return thisTime
.findByCssSelector('i.fa fa-chevron-up')
.click();
},
addVisualization: function addVisualization(vizName) {
var self = this;
return this.clickAddVisualization()
.then(function () {
common.debug('filter visualization (' + vizName + ')');
return self.filterVizNames(vizName);
})
// this second wait is usually enough to avoid the
// 'stale element reference: element is not attached to the page document'
// on the next step
.then(function () {
return common.sleep(1000);
})
.then(function () {
// but wrap in a try loop since it can still happen
return common.try(function () {
common.debug('click visualization (' + vizName + ')');
return self.clickVizNameLink(vizName);
});
})
// this second click of 'Add' collapses the Add Visualization pane
.then(function () {
return self.clickAddVisualization();
});
},
saveDashboard: function saveDashboard(dashName) {
var self = this;
return thisTime
.findByCssSelector('button.ng-scope[aria-label="Save Dashboard"]')
.click()
.then(function () {
return common.sleep(1000);
})
.then(function () {
common.debug('saveButton button clicked');
return thisTime
.findById('dashboardTitle')
.type(dashName);
})
// click save button
.then(function () {
return thisTime
.findByCssSelector('.btn-primary')
.click();
})
// verify that green message at the top of the page.
// it's only there for about 5 seconds
.then(function () {
return thisTime
.findByCssSelector('kbn-truncated.toast-message.ng-isolate-scope')
.getVisibleText();
});
},
clickDashboardByLinkText: function clickDashboardByLinkText(dashName) {
return thisTime
.findByLinkText(dashName)
.click();
},
// use the search filter box to narrow the results down to a single
// entry, or at least to a single page of results
loadSavedDashboard: function loadSavedDashboard(dashName) {
var self = this;
return thisTime
.findByCssSelector('button.ng-scope[aria-label="Load Saved Dashboard"]')
.click()
.then(function filterDashboard() {
common.debug('Load Saved Dashboard button clicked');
return self.remote
.findByCssSelector('input[name="filter"]')
.click()
.type(dashName.replace('-',' '));
})
.then(function () {
return common.sleep(1000);
})
.then(function clickDashboardByLinkedText() {
return self
.clickDashboardByLinkText(dashName);
});
},
getPanelTitles: function getPanelTitles() {
common.debug('in getPanelTitles');
return thisTime
.findAllByCssSelector('span.panel-title')
.then(function (titleObjects) {
function getTitles(chart) {
return chart.getAttribute('title');
}
var getTitlePromises = titleObjects.map(getTitles);
return Promise.all(getTitlePromises);
});
},
getPanelData: function getPanelData() {
common.debug('in getPanelData');
return thisTime
.findAllByCssSelector('li.gs-w')
.then(function (titleObjects) {
function getTitles(chart) {
var obj = {};
return chart.getAttribute('data-col')
.then(function (theData) {
obj = {dataCol:theData};
return chart;
})
.then(function (chart) {
return chart.getAttribute('data-row')
.then(function (theData) {
obj.dataRow = theData;
return chart;
});
})
.then(function (chart) {
return chart.getAttribute('data-sizex')
.then(function (theData) {
obj.dataSizeX = theData;
return chart;
});
})
.then(function (chart) {
return chart.getAttribute('data-sizey')
.then(function (theData) {
obj.dataSizeY = theData;
return chart;
});
})
.then(function (chart) {
return chart.findByCssSelector('span.panel-title')
.then(function (titleElement) {
return titleElement.getAttribute('title');
})
.then(function (theData) {
obj.title = theData;
return obj;
});
});
}
var getTitlePromises = titleObjects.map(getTitles);
return Promise.all(getTitlePromises);
});
}
};
return DashboardPage;
}());