From 988be9126687514318bad458bd81e75177ffecff Mon Sep 17 00:00:00 2001 From: LeeDr Date: Thu, 26 May 2016 16:32:53 -0500 Subject: [PATCH 1/9] New dashboard tests using elasticdump --- kibana4.json | 8 + mapping_kibana4.json | 1 + package.json | 1 + test/fixtures/scenario_manager.js | 48 ++++++ test/functional/apps/console/_console.js | 2 - test/functional/apps/console/index.js | 4 - test/functional/index.js | 3 +- test/support/index.js | 2 + test/support/pages/common.js | 83 +++++++++- test/support/pages/dashboard_page.js | 200 +++++++++++++++++++++++ 10 files changed, 344 insertions(+), 8 deletions(-) create mode 100644 kibana4.json create mode 100644 mapping_kibana4.json create mode 100644 test/support/pages/dashboard_page.js diff --git a/kibana4.json b/kibana4.json new file mode 100644 index 0000000000000..09239ffa6b8f4 --- /dev/null +++ b/kibana4.json @@ -0,0 +1,8 @@ +{"_index":".kibana","_type":"index-pattern","_id":"logstash-*","_score":1,"_source":{"title":"logstash-*","timeFieldName":"@timestamp","fields":"[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]"}} +{"_index":".kibana","_type":"visualization","_id":"Visualization-TileMap","_score":1,"_source":{"title":"Visualization TileMap","visState":"{\"title\":\"New Visualization\",\"type\":\"tile_map\",\"params\":{\"mapType\":\"Scaled Circle Markers\",\"isDesaturated\":true,\"addTooltip\":true,\"heatMaxZoom\":16,\"heatMinOpacity\":0.1,\"heatRadius\":25,\"heatBlur\":15,\"heatNormalizeData\":true,\"wms\":{\"enabled\":false,\"url\":\"https://basemap.nationalmap.gov/arcgis/services/USGSTopo/MapServer/WMSServer\",\"options\":{\"version\":\"1.3.0\",\"layers\":\"0\",\"format\":\"image/png\",\"transparent\":true,\"attribution\":\"Maps provided by USGS\",\"styles\":\"\"}}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.coordinates\",\"autoPrecision\":true,\"precision\":2}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization漢字-LineChart","_score":1,"_source":{"title":"Visualization漢字 LineChart","visState":"{\"title\":\"New Visualization\",\"type\":\"line\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"showCircles\":true,\"smoothLines\":false,\"interpolate\":\"linear\",\"scale\":\"linear\",\"drawLinesBetweenPoints\":true,\"radiusRatio\":9,\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"extension.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":false}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization漢字-AreaChart","_score":1,"_source":{"title":"Visualization漢字 AreaChart","visState":"{\"title\":\"New Visualization\",\"type\":\"area\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"smoothLines\":false,\"scale\":\"linear\",\"interpolate\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization☺-VerticalBarChart","_score":1,"_source":{"title":"Visualization☺ VerticalBarChart","visState":"{\"title\":\"New Visualization\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization☺漢字-DataTable","_score":1,"_source":{"title":"Visualization☺漢字 DataTable","visState":"{\"title\":\"New Visualization\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"histogram\",\"schema\":\"bucket\",\"params\":{\"field\":\"bytes\",\"interval\":2000,\"extended_bounds\":{}}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization-PieChart","_score":1,"_source":{"title":"Visualization PieChart","visState":"{\"title\":\"New Visualization\",\"type\":\"pie\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"isDonut\":false},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"memory\",\"interval\":40000,\"extended_bounds\":{}}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} +{"_index":".kibana","_type":"visualization","_id":"Visualization-MetricChart","_score":1,"_source":{"title":"Visualization MetricChart","visState":"{\"title\":\"New Visualization\",\"type\":\"metric\",\"params\":{\"handleNoResults\":true,\"fontSize\":60},\"aggs\":[{\"id\":\"1\",\"type\":\"percentile_ranks\",\"schema\":\"metric\",\"params\":{\"field\":\"memory\",\"values\":[99]}}],\"listeners\":{}}","uiStateJSON":"{}","description":"","version":1,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"}}} diff --git a/mapping_kibana4.json b/mapping_kibana4.json new file mode 100644 index 0000000000000..ff55e2b8221c4 --- /dev/null +++ b/mapping_kibana4.json @@ -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"}}}}}} diff --git a/package.json b/package.json index 70417f63d7446..8291ebbba9992 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/test/fixtures/scenario_manager.js b/test/fixtures/scenario_manager.js index a1822a349749e..3101d6b63da2c 100644 --- a/test/fixtures/scenario_manager.js +++ b/test/fixtures/scenario_manager.js @@ -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; diff --git a/test/functional/apps/console/_console.js b/test/functional/apps/console/_console.js index c214e3d3971d9..d1cc4c4ae96da 100644 --- a/test/functional/apps/console/_console.js +++ b/test/functional/apps/console/_console.js @@ -2,8 +2,6 @@ import { bdd, scenarioManager, common, - // settingsPage, - // headerPage, consolePage } from '../../../support'; diff --git a/test/functional/apps/console/index.js b/test/functional/apps/console/index.js index 057a57e52cfae..7e48a17b7baf5 100644 --- a/test/functional/apps/console/index.js +++ b/test/functional/apps/console/index.js @@ -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'); }); }()); diff --git a/test/functional/index.js b/test/functional/index.js index 85caa6baac0bc..52202e316f58f 100644 --- a/test/functional/index.js +++ b/test/functional/index.js @@ -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 () {}); }); }); diff --git a/test/support/index.js b/test/support/index.js index d044231ba3d47..ff9a4a38980fe 100644 --- a/test/support/index.js +++ b/test/support/index.js @@ -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()); diff --git a/test/support/pages/common.js b/test/support/pages/common.js index 169b190059a6a..441f2fc610f2b 100644 --- a/test/support/pages/common.js +++ b/test/support/pages/common.js @@ -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; diff --git a/test/support/pages/dashboard_page.js b/test/support/pages/dashboard_page.js new file mode 100644 index 0000000000000..88b8cb8f9a127 --- /dev/null +++ b/test/support/pages/dashboard_page.js @@ -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; +}()); From 1333cbe6ff7c2d03ecc0e509e8b7a577e29a2664 Mon Sep 17 00:00:00 2001 From: LeeDr Date: Fri, 27 May 2016 08:20:27 -0500 Subject: [PATCH 2/9] Add missing Dashboard test files. --- test/functional/apps/dashboard/_dashboard.js | 138 +++++++++++++++++++ test/functional/apps/dashboard/index.js | 13 ++ 2 files changed, 151 insertions(+) create mode 100644 test/functional/apps/dashboard/_dashboard.js create mode 100644 test/functional/apps/dashboard/index.js diff --git a/test/functional/apps/dashboard/_dashboard.js b/test/functional/apps/dashboard/_dashboard.js new file mode 100644 index 0000000000000..d8590b8494bbb --- /dev/null +++ b/test/functional/apps/dashboard/_dashboard.js @@ -0,0 +1,138 @@ +import { + bdd, + common, + dashboardPage, + headerPage, + scenarioManager +} from '../../../support'; + +(function () { + var expect = require('expect.js'); + + (function () { + bdd.describe('discover tab', function describeIndexTests() { + + bdd.before(function () { + + var fromTime = '2015-09-19 06:31:44.000'; + var toTime = '2015-09-23 18:31:44.000'; + + common.debug('Starting dashboard before method'); + common.debug('navigateToApp dashboard'); + return scenarioManager.unload('emptyKibana') + .then(function () { + return common.try(function () { + return scenarioManager.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'}); + }); + }) + // and load a set of makelogs data + .then(function loadkibana4() { + common.debug('load kibana index with visualizations'); + return common.elasticLoad('kibana4.json','.kibana'); + }) + .then(function () { + return scenarioManager.loadIfEmpty('logstashFunctional'); + }) + .then(function () { + return common.navigateToApp('dashboard'); + }) + .catch(common.handleError(this)); + }); + + + bdd.describe('add visualizations to dashboard', function dashboardTest() { + var visualizations = ['Visualization漢字 AreaChart', + 'Visualization☺漢字 DataTable', + 'Visualization漢字 LineChart', + 'Visualization PieChart', + 'Visualization TileMap', + 'Visualization☺ VerticalBarChart', + 'Visualization MetricChart' + ]; + + + bdd.it('should be able to add visualizations to dashboard', function addVisualizations() { + + function addVisualizations(arr) { + return arr.reduce(function (promise, vizName) { + return promise + .then(function () { + return dashboardPage.addVisualization(vizName); + }); + }, Promise.resolve()); + } + + return addVisualizations(visualizations) + .then(function () { + console.log('all done'); + }); + + }); + + bdd.it('set the timepicker time to that which contains our test data', function setTimepicker() { + var fromTime = '2015-09-19 06:31:44.000'; + var toTime = '2015-09-23 18:31:44.000'; + var testSubName = 'Dashboard Test 1'; + + // .then(function () { + common.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"'); + return headerPage.setAbsoluteRange(fromTime, toTime) + .then(function sleep() { + return common.sleep(4000); + }) + .then(function takeScreenshot() { + common.debug('Take screenshot'); + common.saveScreenshot('./screenshot-' + testSubName + '.png'); + }) + .catch(common.handleError(this)); + }); + + bdd.it('should save and load dashboard', function saveAndLoadDashboard() { + var testSubName = 'Dashboard Test 1'; +// save time on the dashboard? + return dashboardPage.saveDashboard(testSubName) + // click New Dashboard just to clear the one we just created + .then(function () { + return dashboardPage.clickNewDashboard(); + }) + .then(function () { + return dashboardPage.loadSavedDashboard(testSubName); + }) + .catch(common.handleError(this)); + }); + + bdd.it('should have all the expected visualizations', function checkVisualizations() { + return common.tryForTime(10000, function () { + return dashboardPage.getPanelTitles() + .then(function (panelTitles) { + common.log('visualization titles = ' + panelTitles); + expect(panelTitles).to.eql(visualizations); + }); + }) + .catch(common.handleError(this)); + }); + + bdd.it('should have all the expected initial sizes', function checkVisualizationSizes() { + var visObjects = [ { dataCol: '1', dataRow: '1', dataSizeX: '3', dataSizeY: '2', title: 'Visualization漢字 AreaChart' }, + { dataCol: '4', dataRow: '1', dataSizeX: '3', dataSizeY: '2', title: 'Visualization☺漢字 DataTable' }, + { dataCol: '7', dataRow: '1', dataSizeX: '3', dataSizeY: '2', title: 'Visualization漢字 LineChart' }, + { dataCol: '10', dataRow: '1', dataSizeX: '3', dataSizeY: '2', title: 'Visualization PieChart' }, + { dataCol: '1', dataRow: '3', dataSizeX: '3', dataSizeY: '2', title: 'Visualization TileMap' }, + { dataCol: '4', dataRow: '3', dataSizeX: '3', dataSizeY: '2', title: 'Visualization☺ VerticalBarChart' }, + { dataCol: '7', dataRow: '3', dataSizeX: '3', dataSizeY: '2', title: 'Visualization MetricChart' } + ]; + return common.tryForTime(10000, function () { + return dashboardPage.getPanelData() + .then(function (panelTitles) { + common.log('visualization titles = ' + panelTitles); + expect(panelTitles).to.eql(visObjects); + }); + }) + .catch(common.handleError(this)); + }); + + }); + + }); + }()); +}()); diff --git a/test/functional/apps/dashboard/index.js b/test/functional/apps/dashboard/index.js new file mode 100644 index 0000000000000..813e03d669820 --- /dev/null +++ b/test/functional/apps/dashboard/index.js @@ -0,0 +1,13 @@ +import { bdd, remote, scenarioManager, defaultTimeout } from '../../../support'; + +(function () { + bdd.describe('dashboard app', function () { + this.timeout = defaultTimeout; + + bdd.before(function () { + return remote.setWindowSize(1200,800); + }); + + require('./_dashboard'); + }); +}()); From 53ede1a4ca0a5d43305ede47994c852cc916bb75 Mon Sep 17 00:00:00 2001 From: LeeDr Date: Fri, 27 May 2016 08:47:54 -0500 Subject: [PATCH 3/9] Remove some unused vars. --- test/support/pages/common.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/support/pages/common.js b/test/support/pages/common.js index 441f2fc610f2b..91d0cdf3c1f97 100644 --- a/test/support/pages/common.js +++ b/test/support/pages/common.js @@ -13,11 +13,7 @@ export default (function () { 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) { From ac4a0ed748822d793ad34cbb9362fe8084fbb60b Mon Sep 17 00:00:00 2001 From: LeeDr Date: Fri, 27 May 2016 09:52:30 -0500 Subject: [PATCH 4/9] Move updateConfigDoc method out of scenario_manager and into its own support/es_client. --- test/fixtures/scenario_manager.js | 48 --------------- test/functional/apps/dashboard/_dashboard.js | 5 +- test/support/es_client.js | 65 ++++++++++++++++++++ test/support/index.js | 2 + 4 files changed, 70 insertions(+), 50 deletions(-) create mode 100644 test/support/es_client.js diff --git a/test/fixtures/scenario_manager.js b/test/fixtures/scenario_manager.js index 3101d6b63da2c..a1822a349749e 100644 --- a/test/fixtures/scenario_manager.js +++ b/test/fixtures/scenario_manager.js @@ -140,52 +140,4 @@ 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; diff --git a/test/functional/apps/dashboard/_dashboard.js b/test/functional/apps/dashboard/_dashboard.js index d8590b8494bbb..05eb657f02883 100644 --- a/test/functional/apps/dashboard/_dashboard.js +++ b/test/functional/apps/dashboard/_dashboard.js @@ -3,7 +3,8 @@ import { common, dashboardPage, headerPage, - scenarioManager + scenarioManager, + esClient } from '../../../support'; (function () { @@ -22,7 +23,7 @@ import { return scenarioManager.unload('emptyKibana') .then(function () { return common.try(function () { - return scenarioManager.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'}); + return esClient.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'}); }); }) // and load a set of makelogs data diff --git a/test/support/es_client.js b/test/support/es_client.js new file mode 100644 index 0000000000000..50f699cd2b275 --- /dev/null +++ b/test/support/es_client.js @@ -0,0 +1,65 @@ +var elasticsearch = require('elasticsearch'); +var Promise = require('bluebird'); + +function EsClient(server) { + if (!server) throw new Error('No server defined'); + + // NOTE: some large sets of test data can take several minutes to load + this.client = new elasticsearch.Client({ + host: server, + requestTimeout: 300000, + defer: function () { + return Promise.defer(); + } + }); +} + +/** +* Add fields to the config doc (like setting timezone and defaultIndex) +* @return {Promise} A promise that is resolved when elasticsearch has a response +*/ +EsClient.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 = EsClient; diff --git a/test/support/index.js b/test/support/index.js index ff9a4a38980fe..46e45c3e64dc4 100644 --- a/test/support/index.js +++ b/test/support/index.js @@ -1,4 +1,5 @@ import url from 'url'; +import EsClient from './es_client'; import ScenarioManager from '../fixtures/scenario_manager'; import Common from './pages/common'; import DiscoverPage from './pages/discover_page'; @@ -18,6 +19,7 @@ exports.defaultTimeout = exports.config.defaultTimeout; exports.defaultTryTimeout = exports.config.defaultTryTimeout; exports.defaultFindTimeout = exports.config.defaultFindTimeout; exports.scenarioManager = new ScenarioManager(url.format(exports.config.servers.elasticsearch)); +exports.esClient = new EsClient(url.format(exports.config.servers.elasticsearch)); defineDelayedExport('remote', (suite) => suite.remote); defineDelayedExport('common', () => new Common()); From 4ebe29e14e9d6ae9d4f0fcea5e5a208269b54cad Mon Sep 17 00:00:00 2001 From: LeeDr Date: Fri, 27 May 2016 15:49:57 -0500 Subject: [PATCH 5/9] Moved elasticdump methods from common to support/elastic_dump.js --- .../fixtures/dump_data/dashboard.data.json | 0 .../fixtures/dump_data/dashboard.mapping.json | 0 test/functional/apps/dashboard/_dashboard.js | 9 +- test/support/elastic_dump.js | 113 ++++++++++++++ test/support/es_client.js | 147 +++++++++++------- test/support/index.js | 2 + test/support/pages/common.js | 2 +- 7 files changed, 210 insertions(+), 63 deletions(-) rename kibana4.json => test/fixtures/dump_data/dashboard.data.json (100%) rename mapping_kibana4.json => test/fixtures/dump_data/dashboard.mapping.json (100%) create mode 100644 test/support/elastic_dump.js diff --git a/kibana4.json b/test/fixtures/dump_data/dashboard.data.json similarity index 100% rename from kibana4.json rename to test/fixtures/dump_data/dashboard.data.json diff --git a/mapping_kibana4.json b/test/fixtures/dump_data/dashboard.mapping.json similarity index 100% rename from mapping_kibana4.json rename to test/fixtures/dump_data/dashboard.mapping.json diff --git a/test/functional/apps/dashboard/_dashboard.js b/test/functional/apps/dashboard/_dashboard.js index 05eb657f02883..cbe99173205c1 100644 --- a/test/functional/apps/dashboard/_dashboard.js +++ b/test/functional/apps/dashboard/_dashboard.js @@ -4,7 +4,8 @@ import { dashboardPage, headerPage, scenarioManager, - esClient + esClient, + elasticDump } from '../../../support'; (function () { @@ -20,7 +21,7 @@ import { common.debug('Starting dashboard before method'); common.debug('navigateToApp dashboard'); - return scenarioManager.unload('emptyKibana') + return esClient.delete('.kibana') .then(function () { return common.try(function () { return esClient.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'}); @@ -29,7 +30,7 @@ import { // and load a set of makelogs data .then(function loadkibana4() { common.debug('load kibana index with visualizations'); - return common.elasticLoad('kibana4.json','.kibana'); + return elasticDump.elasticLoad('dashboard','.kibana'); }) .then(function () { return scenarioManager.loadIfEmpty('logstashFunctional'); @@ -65,7 +66,7 @@ import { return addVisualizations(visualizations) .then(function () { - console.log('all done'); + common.debug('done adding visualizations'); }); }); diff --git a/test/support/elastic_dump.js b/test/support/elastic_dump.js new file mode 100644 index 0000000000000..cb3abcdb989e6 --- /dev/null +++ b/test/support/elastic_dump.js @@ -0,0 +1,113 @@ +import { common, config} from './'; + +export default (function () { + var util = require('util'); + var path = require('path'); + var url = require('url'); + var resolve = require('path').resolve; + var Elasticdump = require('elasticdump').elasticdump; + + function ElasticDump() { + } + + ElasticDump.prototype = { + constructor: ElasticDump, + + /* + ** 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, + }; + // common.debug(options); + var dumper = new Elasticdump(options.input, options.output, options); + + dumper.on('log', function (message) { common.debug(message); }); + // dumper.on('debug', function (message) { common.debug(message); }); + dumper.on('error', function (error) { common.debug('error', 'Error Emitted => ' + (error.message || JSON.stringify(error))); }); + + var promise = new Promise(function (resolve, reject) { + dumper.dump(function (error, totalWrites) { + if (error) { + common.debug('THERE WAS AN ERROR :-('); + reject(Error(error)); + } else { + resolve ('elasticdumpModule success'); + } + }); + }); + return promise; + }, + + /* + ** Dumps data from Elasticsearch into json files. + ** Takes a simple filename as input like 'dashboard' (for dashboard tests). + ** Appends ''.mapping.json' and '.data.json' for the actual filenames. + ** Writes files to the Kibana root dir. + ** Fails if the files already exist, so consider appending a timestamp to filename. + */ + elasticDump: function elasticDump(index, file) { + var self = this; + common.debug('Dumping mapping from ' + url.format(config.servers.elasticsearch) + '/' + index + + ' to (' + file + '.mapping.json)'); + return this.elasticdumpModule(url.format(config.servers.elasticsearch), + file + '.mapping.json', index, 'mapping') + .then(function () { + common.debug('Dumping data from ' + url.format(config.servers.elasticsearch) + '/' + index + + ' to (' + file + '.data.json)'); + return self.elasticdumpModule(url.format(config.servers.elasticsearch), + file + '.data.json', index, 'data'); + }); + }, + + /* + ** Loads data from json files into Elasticsearch. + ** Takes a simple filename as input like 'dashboard' (for dashboard tests). + ** Appends ''.mapping.json' and '.data.json' for the actual filenames. + ** Path /test/fixtures/dump_data is hard-coded + */ + elasticLoad: function elasticLoad(file, index) { + // TODO: should we have a flag to delete the index first? + // or use scenarioManager.unload(index) ? <<- currently this + var self = this; + common.debug('Loading mapping (test/fixtures/dump_data/' + file + '.mapping.json) into ' + + url.format(config.servers.elasticsearch) + '/' + index); + return this.elasticdumpModule('test/fixtures/dump_data/' + file + '.mapping.json', + url.format(config.servers.elasticsearch), index, 'mapping') + .then(function () { + common.debug('Loading data (test/fixtures/dump_data/' + file + '.data.json) into ' + + url.format(config.servers.elasticsearch) + '/' + index); + return self.elasticdumpModule('test/fixtures/dump_data/' + file + '.data.json', + url.format(config.servers.elasticsearch), index, 'data'); + }); + }, + }; + + return ElasticDump; +}()); diff --git a/test/support/es_client.js b/test/support/es_client.js index 50f699cd2b275..76f486b468437 100644 --- a/test/support/es_client.js +++ b/test/support/es_client.js @@ -1,65 +1,96 @@ -var elasticsearch = require('elasticsearch'); -var Promise = require('bluebird'); +import { common, remote} from './'; -function EsClient(server) { - if (!server) throw new Error('No server defined'); +export default (function () { - // NOTE: some large sets of test data can take several minutes to load - this.client = new elasticsearch.Client({ - host: server, - requestTimeout: 300000, - defer: function () { - return Promise.defer(); - } - }); -} + var elasticsearch = require('elasticsearch'); + var Promise = require('bluebird'); -/** -* Add fields to the config doc (like setting timezone and defaultIndex) -* @return {Promise} A promise that is resolved when elasticsearch has a response -*/ -EsClient.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); + function EsClient(server) { + this.remote = remote; + if (!server) throw new Error('No server defined'); - 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 + // NOTE: some large sets of test data can take several minutes to load + this.client = new elasticsearch.Client({ + host: server, + requestTimeout: 300000, + defer: function () { + return Promise.defer(); } }); - }) - .catch(function (err) { - throw err; - }); -}; + } + + EsClient.prototype = { + constructor: EsClient, + + + /** + * Delete an index + * @param {string} index + * @return {Promise} A promise that is resolved when elasticsearch has a response + */ + delete: function (index) { + + return this.client.indices.delete({ + index: index + }) + .catch(function (reason) { + // if the index never existed yet, or was already deleted it's OK + if (reason.message.indexOf('index_not_found_exception') < 0) { + common.debug('reason.message: ' + reason.message); + throw reason; + } + }); + }, + + + /** + * Add fields to the config doc (like setting timezone and defaultIndex) + * @return {Promise} A promise that is resolved when elasticsearch has a response + */ + 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; + common.debug('config._id =' + configId); + } + }) + // now that we have the id, we can update + // return scenarioManager.updateConfigDoc({'dateFormat:tz':'UTC', 'defaultIndex':'logstash-*'}); + .then(function (response) { + common.debug('updating config with ' + docMapString); + return self.client.update({ + index: '.kibana', + type: 'config', + id: configId, + body: { + 'doc': + docMap + } + }); + }) + .catch(function (err) { + throw err; + }); + } + }; -module.exports = EsClient; + return EsClient; +}()); diff --git a/test/support/index.js b/test/support/index.js index 46e45c3e64dc4..476a40d1e8ee9 100644 --- a/test/support/index.js +++ b/test/support/index.js @@ -1,5 +1,6 @@ import url from 'url'; import EsClient from './es_client'; +import ElasticDump from './elastic_dump'; import ScenarioManager from '../fixtures/scenario_manager'; import Common from './pages/common'; import DiscoverPage from './pages/discover_page'; @@ -30,6 +31,7 @@ defineDelayedExport('visualizePage', () => new VisualizePage()); defineDelayedExport('dashboardPage', () => new DashboardPage()); defineDelayedExport('shieldPage', () => new ShieldPage()); defineDelayedExport('consolePage', () => new ConsolePage()); +defineDelayedExport('elasticDump', () => new ElasticDump()); // creates an export for values that aren't actually avaialable until // until tests start to run. These getters will throw errors if the export diff --git a/test/support/pages/common.js b/test/support/pages/common.js index 91d0cdf3c1f97..9d35bfce12f28 100644 --- a/test/support/pages/common.js +++ b/test/support/pages/common.js @@ -1,4 +1,4 @@ -import { common, config, defaultTryTimeout, defaultFindTimeout, remote, shieldPage } from '../'; +import { config, defaultTryTimeout, defaultFindTimeout, remote, shieldPage } from '../'; export default (function () { var Promise = require('bluebird'); From f28fc8973388b4da18bf97d12bff2230f1d883db Mon Sep 17 00:00:00 2001 From: LeeDr Date: Fri, 27 May 2016 16:20:50 -0500 Subject: [PATCH 6/9] Correct a dashboard test sub-suite name. --- test/functional/apps/dashboard/_dashboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/apps/dashboard/_dashboard.js b/test/functional/apps/dashboard/_dashboard.js index cbe99173205c1..3ccecc5ebf686 100644 --- a/test/functional/apps/dashboard/_dashboard.js +++ b/test/functional/apps/dashboard/_dashboard.js @@ -12,7 +12,7 @@ import { var expect = require('expect.js'); (function () { - bdd.describe('discover tab', function describeIndexTests() { + bdd.describe('dashboard tab', function describeIndexTests() { bdd.before(function () { From 411bacd9d3ca5e18f6da47ac958f931214860d7e Mon Sep 17 00:00:00 2001 From: LeeDr Date: Tue, 31 May 2016 12:15:24 -0500 Subject: [PATCH 7/9] Some cleanup from review comments. --- test/support/elastic_dump.js | 3 -- test/support/pages/common.js | 75 +----------------------------------- 2 files changed, 1 insertion(+), 77 deletions(-) diff --git a/test/support/elastic_dump.js b/test/support/elastic_dump.js index cb3abcdb989e6..a9111a68d5577 100644 --- a/test/support/elastic_dump.js +++ b/test/support/elastic_dump.js @@ -11,7 +11,6 @@ export default (function () { } ElasticDump.prototype = { - constructor: ElasticDump, /* ** This function is basically copied from @@ -45,11 +44,9 @@ export default (function () { skip: null, toLog: null, }; - // common.debug(options); var dumper = new Elasticdump(options.input, options.output, options); dumper.on('log', function (message) { common.debug(message); }); - // dumper.on('debug', function (message) { common.debug(message); }); dumper.on('error', function (error) { common.debug('error', 'Error Emitted => ' + (error.message || JSON.stringify(error))); }); var promise = new Promise(function (resolve, reject) { diff --git a/test/support/pages/common.js b/test/support/pages/common.js index 9d35bfce12f28..0b1b29a8cbc69 100644 --- a/test/support/pages/common.js +++ b/test/support/pages/common.js @@ -282,81 +282,8 @@ 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; From 1566cc3a9629dd3925e476b9ab4a20f4782c8a44 Mon Sep 17 00:00:00 2001 From: LeeDr Date: Tue, 31 May 2016 15:45:41 -0500 Subject: [PATCH 8/9] Remove unused self. --- test/support/elastic_dump.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/support/elastic_dump.js b/test/support/elastic_dump.js index a9111a68d5577..c08999d21b178 100644 --- a/test/support/elastic_dump.js +++ b/test/support/elastic_dump.js @@ -18,7 +18,6 @@ export default (function () { ** 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, From 9fe89d9e66f06a3c8f8e43d4655e6a25b854a211 Mon Sep 17 00:00:00 2001 From: LeeDr Date: Tue, 31 May 2016 16:50:42 -0500 Subject: [PATCH 9/9] Removed un-used requires. --- test/support/pages/common.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/support/pages/common.js b/test/support/pages/common.js index 0b1b29a8cbc69..2a0e9da658500 100644 --- a/test/support/pages/common.js +++ b/test/support/pages/common.js @@ -11,9 +11,6 @@ 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 Elasticdump = require('elasticdump').elasticdump; function injectTimestampQuery(func, url) { var formatted = modifyQueryString(url, function (parsed) {