Fix map updates not propagating to the dashboard (#13589)

* Add failing tests

* Add fix by preventing uiState from being directly updated in visualization.

* Add test that would catch error caused by this PR in regards to filter agg

* Fix issue with uiState triggering dirty dashboard state by introducing temporary "sessionState" on a vis

* Click go after toggling the switch

* add more tests to ensure getRequestAggs functions as intented

* Go back to old zoom calculations. Update vis test data

I think because mapCollar is no longer saved in uiState, the save
recenters the data and we get slightly different data points from the
test data.  As far as my eye can tell, everything is working as
intended.

* fixes and tests

- incorporate the new init function which fixes the bug where we lose
map bounds data on a fresh save
- add a test that would have caught that
- adjust tests due to bug where map bounds is changing slightly.  File
another issue for that separately as it doesn’t actually affect the
users map experience.

* Fix tests

Tests relied on my original logic of defaulting to the saved zoom state
and not relying on uiState, so I went back to that logic.  Also found
another bug where mapZoom of 0 was being considered invalid, but it is
actually a valid zoom level.

* Since leaflet upgrade 'path.leaflet-clickable' can't be used to retrieve circles anymore

* Avoid stale element reference

I suspect because the page is changing, you have to keep fetching the
element afresh.  I don’t see this error on my local but saw it on
jenkins.

* remove spy select from PageObjects.visualize.getDataTableData

The function is used in the Data Table visualization where the spy pane
select doesn’t exist.
This commit is contained in:
Stacey Gammon 2017-09-07 08:43:19 -04:00 committed by GitHub
parent 752644ab13
commit d853fcae11
15 changed files with 427 additions and 200 deletions

View file

@ -14,7 +14,7 @@
<label>
Elasticsearch request body &nbsp;
</label>
<pre>{{req.fetchParams.body | json}}</pre>
<pre data-test-subj="visualizationEsRequestBody">{{req.fetchParams.body | json}}</pre>
</div>
<div ng-if="spy.mode.name === 'response'">

View file

@ -663,7 +663,7 @@ export class KibanaMap extends EventEmitter {
if (!centerFromUIState || centerFromMap.lon !== centerFromUIState[1] || centerFromMap.lat !== centerFromUIState[0]) {
visualization.uiStateVal('mapCenter', [centerFromMap.lat, centerFromMap.lon]);
}
uiState.set('mapBounds', this.getUntrimmedBounds());
visualization.sessionState.mapBounds = this.getUntrimmedBounds();
}
this.on('dragend', persistMapStateInUiState);

View file

@ -20,13 +20,11 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
class MapsVisualization {
constructor(element, vis) {
this.vis = vis;
this.$el = $(element);
this._$container = this.$el;
this._geohashLayer = null;
this._kibanaMap = null;
this._kibanaMapReady = this._makeKibanaMap();
this._baseLayerDirty = true;
this._currentParams = null;
}
@ -37,6 +35,11 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
}
}
init() {
this._kibanaMapReady = this._makeKibanaMap();
return this._kibanaMapReady;
}
async render(esResponse, status) {
return new Promise(async(resolve) => {
@ -66,7 +69,6 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
//**********************************************************************************************************
async _makeKibanaMap() {
try {
this._tmsService = await serviceSettings.getTMSService();
this._tmsError = null;
@ -88,8 +90,8 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
options.center = centerFromUIState ? centerFromUIState : this.vis.type.visConfig.defaults.mapCenter;
this._kibanaMap = new KibanaMap(containerElement, options);
uiState.set('mapZoom', this._kibanaMap.getZoomLevel());
uiState.set('mapBounds', this._kibanaMap.getUntrimmedBounds());
this.vis.sessionState.mapBounds = this._kibanaMap.getUntrimmedBounds();
this._kibanaMap.addDrawControl();
this._kibanaMap.addFitControl();
this._kibanaMap.addLegendControl();
@ -170,7 +172,6 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
* called on options change (vis.params change)
*/
async _updateParams() {
const mapParams = this._getMapsParams();
if (_.eq(this._currentParams, mapParams)) {
return;

View file

@ -1,17 +1,48 @@
import expect from 'expect.js';
import { AggTypesBucketsGeoHashProvider } from 'ui/agg_types/buckets/geo_hash';
describe('Geohash Agg', function () {
describe('Geohash Agg', () => {
// Mock BucketAggType that does not create an AggType, but instead returns the AggType parameters
const intialZoom = 10;
const initialMapBounds = {
top_left: { lat: 1.0, lon: -1.0 },
bottom_right: { lat: -1.0, lon: 1.0 }
};
const aggMock = {
getField: () => {
return {
name: 'location'
};
},
params: {
isFilteredByCollar: true,
useGeocentroid: true
},
vis: {
hasUiState: () => {
return false;
},
params: {
mapZoom: intialZoom
},
sessionState: {}
},
type: 'geohash_grid',
};
const BucketAggTypeMock = (aggOptions) => {
return aggOptions.params;
return aggOptions;
};
const AggConfigMock = (vis, aggOptions) => {
return aggOptions;
};
const PrivateMock = (provider) => {
switch (provider.name) {
case 'AggTypesBucketsBucketAggTypeProvider':
return BucketAggTypeMock;
break;
case 'VisAggConfigProvider':
return AggConfigMock;
break;
default:
return () => {};
}
@ -21,18 +52,54 @@ describe('Geohash Agg', function () {
return 7;//"visualization:tileMap:maxPrecision"
}
};
const params = AggTypesBucketsGeoHashProvider(PrivateMock, configMock); // eslint-disable-line new-cap
describe('precision parameter', function () {
function initVisSessionState() {
aggMock.vis.sessionState = {
mapBounds: initialMapBounds
};
}
function initAggParams() {
aggMock.params.isFilteredByCollar = true;
aggMock.params.useGeocentroid = true;
}
function zoomMap(zoomChange) {
aggMock.vis.params.mapZoom += zoomChange;
}
function moveMap(newBounds) {
aggMock.vis.sessionState.mapBounds = newBounds;
}
function resetMap() {
aggMock.vis.params.mapZoom = intialZoom;
aggMock.vis.sessionState.mapBounds = initialMapBounds;
aggMock.vis.sessionState.mapCollar = {
top_left: { lat: 1.5, lon: -1.5 },
bottom_right: { lat: -1.5, lon: 1.5 },
zoom: intialZoom
};
}
let geohashAgg;
beforeEach(() => {
geohashAgg = AggTypesBucketsGeoHashProvider(PrivateMock, configMock); // eslint-disable-line new-cap
});
describe('precision parameter', () => {
const PRECISION_PARAM_INDEX = 6;
const precisionParam = params[PRECISION_PARAM_INDEX];
let precisionParam;
beforeEach(() => {
precisionParam = geohashAgg.params[PRECISION_PARAM_INDEX];
});
it('should select precision parameter', () => {
expect(precisionParam.name).to.equal('precision');
});
describe('precision parameter write', function () {
describe('precision parameter write', () => {
const zoomToGeoHashPrecision = {
0: 1,
@ -77,4 +144,115 @@ describe('Geohash Agg', function () {
});
});
describe('getRequestAggs', () => {
describe('initial aggregation creation', () => {
let requestAggs;
beforeEach(() => {
initVisSessionState();
initAggParams();
requestAggs = geohashAgg.getRequestAggs(aggMock);
});
it('should create filter, geohash_grid, and geo_centroid aggregations', () => {
expect(requestAggs.length).to.equal(3);
expect(requestAggs[0].type).to.equal('filter');
expect(requestAggs[1].type).to.equal('geohash_grid');
expect(requestAggs[2].type).to.equal('geo_centroid');
});
it('should set mapCollar in vis session state', () => {
expect(aggMock.vis.sessionState).to.have.property('mapCollar');
expect(aggMock.vis.sessionState.mapCollar).to.have.property('top_left');
expect(aggMock.vis.sessionState.mapCollar).to.have.property('bottom_right');
expect(aggMock.vis.sessionState.mapCollar).to.have.property('zoom');
});
// there was a bug because of an "&& mapZoom" check which excluded 0 as a valid mapZoom, but it is.
it('should create filter, geohash_grid, and geo_centroid aggregations when zoom level 0', () => {
aggMock.vis.params.mapZoom = 0;
requestAggs = geohashAgg.getRequestAggs(aggMock);
expect(requestAggs.length).to.equal(3);
expect(requestAggs[0].type).to.equal('filter');
expect(requestAggs[1].type).to.equal('geohash_grid');
expect(requestAggs[2].type).to.equal('geo_centroid');
});
});
describe('aggregation options', () => {
beforeEach(() => {
initVisSessionState();
initAggParams();
});
it('should only create geohash_grid and geo_centroid aggregations when isFilteredByCollar is false', () => {
aggMock.params.isFilteredByCollar = false;
const requestAggs = geohashAgg.getRequestAggs(aggMock);
expect(requestAggs.length).to.equal(2);
expect(requestAggs[0].type).to.equal('geohash_grid');
expect(requestAggs[1].type).to.equal('geo_centroid');
});
it('should only create filter and geohash_grid aggregations when useGeocentroid is false', () => {
aggMock.params.useGeocentroid = false;
const requestAggs = geohashAgg.getRequestAggs(aggMock);
expect(requestAggs.length).to.equal(2);
expect(requestAggs[0].type).to.equal('filter');
expect(requestAggs[1].type).to.equal('geohash_grid');
});
});
describe('aggregation creation after map interaction', () => {
let origRequestAggs;
let origMapCollar;
beforeEach(() => {
resetMap();
initAggParams();
origRequestAggs = geohashAgg.getRequestAggs(aggMock);
origMapCollar = aggMock.vis.sessionState.mapCollar;
});
it('should not change geo_bounding_box filter aggregation and vis session state when map movement is within map collar', () => {
moveMap({
top_left: { lat: 1.1, lon: -1.1 },
bottom_right: { lat: -0.9, lon: 0.9 }
});
const newRequestAggs = geohashAgg.getRequestAggs(aggMock);
expect(JSON.stringify(origRequestAggs[0].params, null, '')).to.equal(JSON.stringify(newRequestAggs[0].params, null, ''));
const newMapCollar = aggMock.vis.sessionState.mapCollar;
expect(JSON.stringify(origMapCollar, null, '')).to.equal(JSON.stringify(newMapCollar, null, ''));
});
it('should change geo_bounding_box filter aggregation and vis session state when map movement is outside map collar', () => {
moveMap({
top_left: { lat: 10.0, lon: -10.0 },
bottom_right: { lat: 9.0, lon: -9.0 }
});
const newRequestAggs = geohashAgg.getRequestAggs(aggMock);
expect(JSON.stringify(origRequestAggs[0].params, null, '')).not.to.equal(JSON.stringify(newRequestAggs[0].params, null, ''));
const newMapCollar = aggMock.vis.sessionState.mapCollar;
expect(JSON.stringify(origMapCollar, null, '')).not.to.equal(JSON.stringify(newMapCollar, null, ''));
});
it('should change geo_bounding_box filter aggregation and vis session state when map zoom level changes', () => {
zoomMap(-1);
const newRequestAggs = geohashAgg.getRequestAggs(aggMock);
expect(JSON.stringify(origRequestAggs[0].params, null, '')).not.to.equal(JSON.stringify(newRequestAggs[0].params, null, ''));
const newMapCollar = aggMock.vis.sessionState.mapCollar;
expect(JSON.stringify(origMapCollar, null, '')).not.to.equal(JSON.stringify(newMapCollar, null, ''));
});
});
});
});

View file

@ -45,6 +45,14 @@ export function AggTypesBucketsGeoHashProvider(Private, config) {
return precision;
}
function getMapZoom(vis) {
if (vis.hasUiState() && parseInt(vis.uiStateVal('mapZoom')) >= 0) {
return parseInt(vis.uiStateVal('mapZoom'));
}
return vis.params.mapZoom;
}
function isOutsideCollar(bounds, collar) {
return bounds && collar && !geoContains(collar, bounds);
}
@ -89,11 +97,8 @@ export function AggTypesBucketsGeoHashProvider(Private, config) {
},
write: function (aggConfig, output) {
const vis = aggConfig.vis;
let currZoom;
if (vis.hasUiState()) {
currZoom = parseInt(vis.uiStateVal('mapZoom'), 10);
}
const autoPrecisionVal = zoomPrecision[currZoom >= 0 ? currZoom : parseInt(vis.params.mapZoom)];
const currZoom = getMapZoom(vis);
const autoPrecisionVal = zoomPrecision[currZoom];
output.params.precision = aggConfig.params.autoPrecision ? autoPrecisionVal : getPrecision(aggConfig.params.precision);
}
}
@ -103,25 +108,23 @@ export function AggTypesBucketsGeoHashProvider(Private, config) {
if (agg.params.isFilteredByCollar && agg.getField()) {
const vis = agg.vis;
let mapBounds;
let mapZoom;
if (vis.hasUiState()) {
mapBounds = vis.uiStateVal('mapBounds');
mapZoom = vis.uiStateVal('mapZoom');
}
if (mapBounds && mapZoom) {
const lastMapCollar = vis.uiStateVal('mapCollar');
const mapBounds = vis.sessionState.mapBounds;
const mapZoom = getMapZoom(vis);
if (mapBounds) {
const lastMapCollar = vis.sessionState.mapCollar;
let mapCollar;
if (!lastMapCollar || lastMapCollar.zoom !== mapZoom || isOutsideCollar(mapBounds, lastMapCollar)) {
mapCollar = scaleBounds(mapBounds);
mapCollar.zoom = mapZoom;
vis.getUiState().set('mapCollar', mapCollar);
vis.sessionState.mapCollar = mapCollar;
} else {
mapCollar = lastMapCollar;
}
const boundingBox = {};
delete mapCollar.zoom; // zoom is not part of bounding box filter
boundingBox[agg.getField().name] = mapCollar;
boundingBox[agg.getField().name] = {
top_left: mapCollar.top_left,
bottom_right: mapCollar.bottom_right
};
aggs.push(new AggConfig(agg.vis, {
type: 'filter',
id: 'filter_agg',

View file

@ -40,6 +40,7 @@
<div class="vis-option-item indented">
<label>
<input type="checkbox"
data-test-subj="isFilteredByCollarCheckbox"
name="isFilteredByCollar"
ng-model="agg.params.isFilteredByCollar">
Only request data around extent of map <kbn-info info="Apply geo_bounding_box filter aggregation to narrow the subject area to the map view box with collar."></kbn-info>

View file

@ -28,6 +28,7 @@
ng-class="{'is-vis-editor-sub-nav-link-selected': sidebar.section == 'data'}"
ng-click="sidebar.section='data'"
kbn-accessible-click
data-test-subj="visualizeEditDataLink"
>
Data
</a>

View file

@ -45,6 +45,10 @@ export function VisProvider(Private, indexPatterns, timefilter, getAppState) {
this.setState(this.getCurrentState(), false);
this.setUiState(uiState);
// Session state is for storing information that is transitory, and will not be saved with the visualization.
// For instance, map bounds, which depends on the view port, browser window size, etc.
this.sessionState = {};
this.API = {
indexPatterns: indexPatterns,
timeFilter: timefilter,

View file

@ -27,6 +27,7 @@
<option
ng-repeat="mode in modes.inOrder"
value="{{mode.name}}"
data-test-subj="spyModeSelect-{{ mode.name }}"
>
{{mode.display}}
</option>

View file

@ -0,0 +1,48 @@
import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const PageObjects = getPageObjects(['dashboard', 'visualize', 'header']);
const testSubjects = getService('testSubjects');
describe('dashboard state', function describeIndexTests() {
before(async function () {
await PageObjects.dashboard.initTests();
// This flip between apps fixes the url so state is preserved when switching apps in test mode.
// Without this flip the url in test mode looks something like
// "http://localhost:5620/app/kibana?_t=1486069030837#/dashboard?_g=...."
// after the initial flip, the url will look like this: "http://localhost:5620/app/kibana#/dashboard?_g=...."
await PageObjects.header.clickVisualize();
await PageObjects.header.clickDashboard();
});
it('Tile map with no changes will update with visualization changes', async () => {
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.setTimepickerInDataRange();
await PageObjects.dashboard.addVisualizations(['Visualization TileMap']);
await PageObjects.dashboard.saveDashboard('No local edits');
await testSubjects.moveMouseTo('dashboardPanel');
await PageObjects.visualize.openSpyPanel();
const tileMapData = await PageObjects.visualize.getDataTableData();
await testSubjects.moveMouseTo('dashboardPanel');
await PageObjects.visualize.closeSpyPanel();
await PageObjects.dashboard.clickEdit();
await PageObjects.dashboard.clickEditVisualization();
await PageObjects.visualize.clickMapZoomIn();
await PageObjects.visualize.clickMapZoomIn();
await PageObjects.visualize.saveVisualization('Visualization TileMap');
await PageObjects.header.clickDashboard();
await testSubjects.moveMouseTo('dashboardPanel');
await PageObjects.visualize.openSpyPanel();
const changedTileMapData = await PageObjects.visualize.getDataTableData();
await testSubjects.moveMouseTo('dashboardPanel');
await PageObjects.visualize.closeSpyPanel();
expect(changedTileMapData.length).to.not.equal(tileMapData.length);
});
});
}

View file

@ -6,6 +6,7 @@ export default function ({ getService, loadTestFile }) {
loadTestFile(require.resolve('./_view_edit'));
loadTestFile(require.resolve('./_dashboard'));
loadTestFile(require.resolve('./_dashboard_state'));
loadTestFile(require.resolve('./_dashboard_save'));
loadTestFile(require.resolve('./_dashboard_time'));
loadTestFile(require.resolve('./_dashboard_listing'));

View file

@ -7,50 +7,35 @@ export default function ({ getService, getPageObjects }) {
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings']);
describe('tile map visualize app', function describeIndexTests() {
before(function () {
before(async function () {
const fromTime = '2015-09-19 06:31:44.000';
const toTime = '2015-09-23 18:31:44.000';
log.debug('navigateToApp visualize');
return PageObjects.common.navigateToUrl('visualize', 'new')
.then(function () {
log.debug('clickTileMap');
return PageObjects.visualize.clickTileMap();
})
.then(function () {
return PageObjects.visualize.clickNewSearch();
})
.then(function () {
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
return PageObjects.header.setAbsoluteRange(fromTime, toTime);
})
.then(function () {
log.debug('select bucket Geo Coordinates');
return PageObjects.visualize.clickBucket('Geo Coordinates');
})
.then(function () {
log.debug('Click aggregation Geohash');
return PageObjects.visualize.selectAggregation('Geohash');
})
.then(function () {
log.debug('Click field geo.coordinates');
return retry.try(function tryingForTime() {
return PageObjects.visualize.selectField('geo.coordinates');
});
})
.then(function () {
return PageObjects.visualize.clickGo();
})
.then(function () {
return PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.common.navigateToUrl('visualize', 'new');
log.debug('clickTileMap');
await PageObjects.visualize.clickTileMap();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
log.debug('select bucket Geo Coordinates');
await PageObjects.visualize.clickBucket('Geo Coordinates');
log.debug('Click aggregation Geohash');
await PageObjects.visualize.selectAggregation('Geohash');
log.debug('Click field geo.coordinates');
await retry.try(async function tryingForTime() {
await PageObjects.visualize.selectField('geo.coordinates');
});
await PageObjects.visualize.clickGo();
await PageObjects.header.waitUntilLoadingHasFinished();
});
/**
* manually compare data due to possible small difference in numbers. This is browser dependent.
*/
function compareTableData(expected, actual) {
log.debug('comparing expected: ', expected);
log.debug('with actual: ', actual);
expect(actual.length).to.eql(expected.length);
@ -87,9 +72,31 @@ export default function ({ getService, getPageObjects }) {
expect(actual.map(tokenize)).to.eql(expected.map(tokenize));
}
describe('Only request data around extent of map option', async () => {
before(async () => await PageObjects.visualize.openSpyPanel());
it('when checked adds filters to aggregation', async () => {
const tableHeaders = await PageObjects.visualize.getDataTableHeaders();
expect(tableHeaders.trim()).to.equal('filter geohash_grid Count Geo Centroid');
});
it('when not checked does not add filters to aggregation', async () => {
await PageObjects.visualize.toggleIsFilteredByCollarCheckbox();
await PageObjects.visualize.clickGo();
await PageObjects.header.waitUntilLoadingHasFinished();
const tableHeaders = await PageObjects.visualize.getDataTableHeaders();
expect(tableHeaders.trim()).to.equal('geohash_grid Count Geo Centroid');
});
after(async () => {
await PageObjects.visualize.closeSpyPanel();
await PageObjects.visualize.toggleIsFilteredByCollarCheckbox();
await PageObjects.visualize.clickGo();
await PageObjects.header.waitUntilLoadingHasFinished();
});
});
describe('tile map chart', function indexPatternCreation() {
it('should show correct tile map data on default zoom level', async function () {
const expectedTableData = ['9 5,787 { "lat": 37.22448418632405, "lon": -103.01935195013255 }',
'd 5,600 { "lat": 37.44271478370398, "lon": -81.72692197253595 }',
@ -104,6 +111,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.openSpyPanel();
await PageObjects.settings.setPageSize('All');
await PageObjects.visualize.selectTableInSpyPaneSelect();
const actualTableData = await PageObjects.visualize.getDataTableData();
compareTableData(expectedTableData, actualTableData.trim().split('\n'));
await PageObjects.visualize.closeSpyPanel();
@ -133,19 +141,41 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.clickMapFitDataBounds();
await PageObjects.visualize.openSpyPanel();
await PageObjects.visualize.selectTableInSpyPaneSelect();
const data = await PageObjects.visualize.getDataTableData();
await compareTableData(expectedPrecision2DataTable, data.trim().split('\n'));
screenshots.take('map-at-zoom-3');
await PageObjects.visualize.closeSpyPanel();
});
it('Newly saved visualization retains map bounds', async () => {
const vizName1 = 'Visualization TileMap';
await PageObjects.visualize.clickMapZoomIn();
await PageObjects.visualize.clickMapZoomIn();
const mapBounds = await PageObjects.visualize.getMapBounds();
await PageObjects.visualize.closeSpyPanel();
await PageObjects.visualize.saveVisualization(vizName1);
await PageObjects.header.waitForToastMessageGone();
const afterSaveMapBounds = await PageObjects.visualize.getMapBounds();
// For some reason the values are slightly different, so we can't check that they are equal. But we did
// have a bug where after the save, there were _no_ map bounds. So this checks for the later case, but
// until we figure out how to make sure the map center is always the exact same, we can't comparison check.
expect(mapBounds).to.not.be(undefined);
expect(afterSaveMapBounds).to.not.be(undefined);
});
/*
** NOTE: Since we don't have a reliable way to know the zoom level, we can
** check some data after we save the viz, then zoom in and check that the data
** changed, then open the saved viz and check that it's back to the original data.
*/
it('should save with zoom level and load, take screenshot', function () {
const expectedTableData = [
it('should save with zoom level and load, take screenshot', async function () {
const expectedZoom5Data = [
'- 9q5 91 { "lat": 34.2934322102855, "lon": -118.57068326651722 }',
'- 9qc 89 { "lat": 38.64546895785822, "lon": -121.59105236401383 }',
'- dp3 79 { "lat": 41.68207651723318, "lon": -87.98703769162958 }',
@ -157,7 +187,7 @@ export default function ({ getService, getPageObjects }) {
'- 9yn 71 { "lat": 34.57203017311617, "lon": -92.17198946946104 }',
'- 9q9 70 { "lat": 37.327310177098425, "lon": -121.70855726221842 }'
];
const expectedTableDataZoomed = [
const expectedZoom6Data = [
'- c20g 16 { "lat": 45.59211894578766, "lon": -122.47455075674225 }',
'- c28c 13 { "lat": 48.0181491561234, "lon": -122.43847891688347 }',
'- c2e5 11 { "lat": 48.46440218389034, "lon": -119.51805034652352 }',
@ -171,72 +201,47 @@ export default function ({ getService, getPageObjects }) {
];
const vizName1 = 'Visualization TileMap';
return PageObjects.visualize.clickMapZoomIn()
.then(function () {
return PageObjects.visualize.clickMapZoomIn();
})
.then(function () {
return PageObjects.visualize.saveVisualization(vizName1);
})
.then(function (message) {
log.debug('Saved viz message = ' + message);
expect(message).to.be('Visualization Editor: Saved Visualization \"' + vizName1 + '\"');
})
.then(function testVisualizeWaitForToastMessageGone() {
return PageObjects.header.waitForToastMessageGone();
})
.then(function () {
return PageObjects.visualize.openSpyPanel();
})
// we're not selecting page size all, so we only have to verify the first page of data
.then(function getDataTableData() {
log.debug('first get the zoom level 5 page data and verify it');
return PageObjects.visualize.getDataTableData();
})
.then(function showData(data) {
compareTableData(expectedTableData, data.trim().split('\n'));
return PageObjects.visualize.closeSpyPanel();
})
.then(function () {
// zoom to level 6, and make sure we go back to the saved level 5
return PageObjects.visualize.clickMapZoomIn();
})
.then(function () {
return PageObjects.visualize.openSpyPanel();
})
.then(function getDataTableData() {
log.debug('second get the zoom level 6 page data and verify it');
return PageObjects.visualize.getDataTableData();
})
.then(function showData(data) {
compareTableData(expectedTableDataZoomed, data.trim().split('\n'));
return PageObjects.visualize.closeSpyPanel();
})
.then(function () {
return PageObjects.visualize.loadSavedVisualization(vizName1);
})
.then(function waitForVisualization() {
return PageObjects.visualize.waitForVisualization();
})
// sleep a bit before taking the screenshot or it won't show data
.then(function sleep() {
return PageObjects.common.sleep(4000);
})
.then(function () {
return PageObjects.visualize.openSpyPanel();
})
.then(function getDataTableData() {
log.debug('third get the zoom level 5 page data and verify it');
return PageObjects.visualize.getDataTableData();
})
.then(function showData(data) {
compareTableData(expectedTableData, data.trim().split('\n'));
return PageObjects.visualize.closeSpyPanel();
})
.then(function takeScreenshot() {
log.debug('Take screenshot');
screenshots.take('Visualize-site-map');
});
// For some reason the map bounds right after saving a tile map for the first time are slightly different
// than when the map is opened from the landing page. This causes the data to be slightly different.
// We should figure out why that is, but it doesn't actually affect the map the user views.
// In order to get this test to pass we'll re-open the saved visualization from the landing page.
await PageObjects.visualize.loadSavedVisualization(vizName1);
const firstMapBounds = await PageObjects.visualize.getMapBounds();
await PageObjects.visualize.openSpyPanel();
await PageObjects.visualize.selectTableInSpyPaneSelect();
const actualZoom5Data = await PageObjects.visualize.getDataTableData();
compareTableData(expectedZoom5Data, actualZoom5Data.trim().split('\n'));
await PageObjects.visualize.closeSpyPanel();
await PageObjects.visualize.clickMapZoomIn();
await PageObjects.visualize.openSpyPanel();
const actualZoom6Data = await PageObjects.visualize.getDataTableData();
compareTableData(expectedZoom6Data, actualZoom6Data.trim().split('\n'));
await PageObjects.visualize.closeSpyPanel();
await PageObjects.visualize.loadSavedVisualization(vizName1);
await PageObjects.visualize.waitForVisualization();
const secondMapBounds = await PageObjects.visualize.getMapBounds();
expect(firstMapBounds.top_left.lat).to.equal(secondMapBounds.top_left.lat);
expect(firstMapBounds.top_left.long).to.equal(secondMapBounds.top_left.long);
expect(firstMapBounds.bottom_right.lat).to.equal(secondMapBounds.bottom_right.lat);
expect(firstMapBounds.bottom_right.long).to.equal(secondMapBounds.bottom_right.long);
await PageObjects.visualize.openSpyPanel();
await PageObjects.visualize.selectTableInSpyPaneSelect();
const actualReOpenedZoom5Data = await PageObjects.visualize.getDataTableData();
compareTableData(expectedZoom5Data, actualReOpenedZoom5Data.trim().split('\n'));
await PageObjects.visualize.closeSpyPanel();
await screenshots.take('Visualize-site-map');
});
it('should zoom in to level 10', function () {
@ -274,40 +279,18 @@ export default function ({ getService, getPageObjects }) {
});
});
it('wms switch should change allow to zoom in further', function () {
return PageObjects.visualize.openSpyPanel()
.then(function () {
return PageObjects.visualize.clickOptions();
})
.then(function () {
return PageObjects.visualize.selectWMS();
})
.then(function () {
return PageObjects.visualize.clickGo();
})
.then(function () {
return PageObjects.header.waitUntilLoadingHasFinished();
})
.then(function () {
return PageObjects.common.sleep(2000);
})
.then(function () {
return PageObjects.visualize.getMapZoomInEnabled();
})
.then(function (enabled) {//should be able to zoom in again
expect(enabled).to.be(true);
})
.then(function () {
return PageObjects.visualize.clickMapZoomIn();
})
.then(function () {
return PageObjects.visualize.getMapZoomInEnabled();
})
.then(function (enabled) {//should be able to zoom in again
expect(enabled).to.be(true);
});
it('wms switch should change allow to zoom in further', async function () {
await PageObjects.visualize.openSpyPanel();
await PageObjects.visualize.clickOptions();
await PageObjects.visualize.selectWMS();
await PageObjects.visualize.clickGo();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.common.sleep(2000);
let enabled = await PageObjects.visualize.getMapZoomInEnabled();
expect(enabled).to.be(true);
await PageObjects.visualize.clickMapZoomIn();
enabled = await PageObjects.visualize.getMapZoomInEnabled();
expect(enabled).to.be(true);
});
});
});

View file

@ -29,6 +29,11 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
return logstash;
}
async clickEditVisualization() {
log.debug('clickEditVisualization');
await testSubjects.click('dashboardPanelEditLink');
}
async clickFullScreenMode() {
log.debug(`clickFullScreenMode`);
await testSubjects.click('dashboardFullScreenMode');

View file

@ -63,22 +63,6 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
return await Promise.all(elements.map(async element => await element.getVisibleText()));
}
async getVectorMapData() {
const chartTypes = await find.allByCssSelector('path.leaflet-clickable');
async function getChartColors(chart) {
const stroke = await chart.getAttribute('fill');
return { color: stroke };
}
let colorData = await Promise.all(chartTypes.map(getChartColors));
colorData = colorData.filter((country) => {
//filter empty colors
return country.color !== 'rgb(200,200,200)';
});
return colorData;
}
async getTextSizes() {
const tags = await find.allByCssSelector('text');
async function returnTagSize(tag) {
@ -303,6 +287,10 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
await find.clickByPartialLinkText('Options');
}
async clickData() {
await testSubjects.click('visualizeEditDataLink');
}
async selectWMS() {
await find.clickByCssSelector('input[name="wms.enabled"]');
}
@ -504,9 +492,13 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
return await rect.getAttribute('height');
}
async selectTableInSpyPaneSelect() {
await testSubjects.click('spyModeSelect');
await testSubjects.click('spyModeSelect-table');
}
async getDataTableData() {
const dataTable = await retry.try(
async () => testSubjects.find('paginated-table-body'));
const dataTable = await testSubjects.find('paginated-table-body');
return await dataTable.getVisibleText();
}
@ -516,6 +508,10 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
return await dataTableHeader.getVisibleText();
}
async toggleIsFilteredByCollarCheckbox() {
await testSubjects.click('isFilteredByCollarCheckbox');
}
async getMarkdownData() {
const markdown = await retry.try(async () => find.byCssSelector('visualize.ng-isolate-scope'));
return await markdown.getVisibleText();
@ -539,6 +535,20 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
await PageObjects.header.waitUntilLoadingHasFinished();
}
async getVisualizationRequest() {
log.debug('getVisualizationRequest');
await this.openSpyPanel();
await testSubjects.click('spyModeSelect');
await testSubjects.click('spyModeSelect-request');
return await testSubjects.getVisibleText('visualizationEsRequestBody');
}
async getMapBounds() {
const request = await this.getVisualizationRequest();
const requestObject = JSON.parse(request);
return requestObject.aggs.filter_agg.filter.geo_bounding_box['geo.coordinates'];
}
async clickMapZoomIn() {
await this.clickMapButton('a.leaflet-control-zoom-in');
}
@ -577,20 +587,6 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
return await this.clickMapButton('a.fa-crop');
}
async getTileMapData() {
const chartTypes = await find.allByCssSelector('path.leaflet-clickable');
async function getChartType(chart) {
const color = await chart.getAttribute('stroke');
const d = await chart.getAttribute('d');
// scale the radius up (sometimes less than 1) and then round to int
let radius = d.replace(/.*A(\d+\.\d+),.*/,'$1') * 10;
radius = Math.round(radius);
return { color, radius };
}
const getChartTypesPromises = chartTypes.map(getChartType);
return await Promise.all(getChartTypesPromises);
}
}
return new VisualizePage();

View file

@ -103,6 +103,11 @@ export function TestSubjectsProvider({ getService }) {
});
}
async moveMouseTo(selector) {
const element = await this.find(selector);
await remote.moveMouseTo(element);
}
async _mapAll(selectorAll, mapFn) {
return await retry.try(async () => {
const elements = await this.findAll(selectorAll);