Sync saved searches that don't have local modifications on a dashboard (#14452)

* Add tests to catch error

* Fix test

* Don't store column and sort state in panel uiState unless explicitly overridden in a dashboard.

* add debug messages

* Elements can go stale between the find by and the click event so wrap in a retry

* fix bad merge with master
This commit is contained in:
Stacey Gammon 2017-11-02 16:47:40 -04:00 committed by GitHub
parent 403f966d39
commit 92964ef45f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 49 additions and 36 deletions

View file

@ -34,36 +34,34 @@ export class SearchEmbeddableHandler extends EmbeddableHandler {
searchScope.panel = panel; searchScope.panel = panel;
container.registerPanelIndexPattern(panel.panelIndex, savedObject.searchSource.get('index')); container.registerPanelIndexPattern(panel.panelIndex, savedObject.searchSource.get('index'));
// This causes changes to a saved search to be hidden, but also allows // If there is column or sort data on the panel, that means the original columns or sort settings have
// the user to locally modify and save changes to a saved search only in a dashboard. // been overridden in a dashboard.
// See https://github.com/elastic/kibana/issues/9523 for more details. searchScope.columns = searchScope.panel.columns || searchScope.savedObj.columns;
searchScope.panel = container.updatePanel(searchScope.panel.panelIndex, { searchScope.sort = searchScope.panel.sort || searchScope.savedObj.sort;
columns: searchScope.panel.columns || searchScope.savedObj.columns,
sort: searchScope.panel.sort || searchScope.savedObj.sort
});
const uiState = savedObject.uiStateJSON ? JSON.parse(savedObject.uiStateJSON) : {}; const uiState = savedObject.uiStateJSON ? JSON.parse(savedObject.uiStateJSON) : {};
searchScope.uiState = container.createChildUistate(getPersistedStateId(panel), uiState); searchScope.uiState = container.createChildUistate(getPersistedStateId(panel), uiState);
searchScope.setSortOrder = function setSortOrder(columnName, direction) { searchScope.setSortOrder = function setSortOrder(columnName, direction) {
searchScope.panel = container.updatePanel(searchScope.panel.panelIndex, { sort: [columnName, direction] }); searchScope.panel = container.updatePanel(searchScope.panel.panelIndex, { sort: [columnName, direction] });
searchScope.sort = searchScope.panel.sort;
}; };
searchScope.addColumn = function addColumn(columnName) { searchScope.addColumn = function addColumn(columnName) {
savedObject.searchSource.get('index').popularizeField(columnName, 1); savedObject.searchSource.get('index').popularizeField(columnName, 1);
columnActions.addColumn(searchScope.panel.columns, columnName); columnActions.addColumn(searchScope.columns, columnName);
searchScope.panel = container.updatePanel(searchScope.panel.panelIndex, { columns: searchScope.panel.columns }); searchScope.panel = container.updatePanel(searchScope.panel.panelIndex, { columns: searchScope.columns });
}; };
searchScope.removeColumn = function removeColumn(columnName) { searchScope.removeColumn = function removeColumn(columnName) {
savedObject.searchSource.get('index').popularizeField(columnName, 1); savedObject.searchSource.get('index').popularizeField(columnName, 1);
columnActions.removeColumn(searchScope.panel.columns, columnName); columnActions.removeColumn(searchScope.columns, columnName);
searchScope.panel = container.updatePanel(searchScope.panel.panelIndex, { columns: searchScope.panel.columns }); searchScope.panel = container.updatePanel(searchScope.panel.panelIndex, { columns: searchScope.columns });
}; };
searchScope.moveColumn = function moveColumn(columnName, newIndex) { searchScope.moveColumn = function moveColumn(columnName, newIndex) {
columnActions.moveColumn(searchScope.panel.columns, columnName, newIndex); columnActions.moveColumn(searchScope.columns, columnName, newIndex);
searchScope.panel = container.updatePanel(searchScope.panel.panelIndex, { columns: searchScope.panel.columns }); searchScope.panel = container.updatePanel(searchScope.panel.panelIndex, { columns: searchScope.columns });
}; };
searchScope.filter = function (field, value, operator) { searchScope.filter = function (field, value, operator) {

View file

@ -1,7 +1,7 @@
<doc-table <doc-table
search-source="savedObj.searchSource" search-source="savedObj.searchSource"
sorting="panel.sort" sorting="sort"
columns="panel.columns" columns="columns"
data-shared-item data-shared-item
data-title="{{savedObj.title}}" data-title="{{savedObj.title}}"
data-description="{{savedObj.description}}" data-description="{{savedObj.description}}"

View file

@ -65,22 +65,39 @@ export default function ({ getService, getPageObjects }) {
}); });
}); });
it('Saved search with column changes will not update when the saved object changes', async () => { it('Saved search with no changes will update when the saved object changes', async () => {
await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.header.clickDiscover();
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.setTimepickerInDataRange(); await PageObjects.dashboard.setTimepickerInDataRange();
await PageObjects.discover.clickFieldListItemAdd('bytes');
await PageObjects.discover.saveSearch('my search');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.header.clickToastOK();
await PageObjects.header.clickDashboard();
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.dashboard.addSavedSearch('my search');
await PageObjects.dashboard.saveDashboard('No local edits');
await PageObjects.header.clickToastOK();
await PageObjects.header.clickDiscover(); await PageObjects.header.clickDiscover();
await PageObjects.discover.clickFieldListItemAdd('bytes');
await PageObjects.discover.clickFieldListItemAdd('agent'); await PageObjects.discover.clickFieldListItemAdd('agent');
await PageObjects.discover.saveSearch('my search'); await PageObjects.discover.saveSearch('my search');
await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.header.clickToastOK(); await PageObjects.header.clickToastOK();
await PageObjects.header.clickDashboard(); await PageObjects.header.clickDashboard();
await PageObjects.dashboard.addSavedSearch('my search'); await PageObjects.header.waitUntilLoadingHasFinished();
const headers = await PageObjects.discover.getColumnHeaders();
expect(headers.length).to.be(3);
expect(headers[1]).to.be('bytes');
expect(headers[2]).to.be('agent');
});
it('Saved search with column changes will not update when the saved object changes', async () => {
await PageObjects.discover.removeHeaderColumn('bytes'); await PageObjects.discover.removeHeaderColumn('bytes');
await PageObjects.dashboard.clickEdit();
await PageObjects.dashboard.saveDashboard('Has local edits'); await PageObjects.dashboard.saveDashboard('Has local edits');
await PageObjects.header.clickToastOK(); await PageObjects.header.clickToastOK();

View file

@ -262,15 +262,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
await this.clickAddVisualization(); await this.clickAddVisualization();
log.debug('filter visualization (' + vizName + ')'); log.debug('filter visualization (' + vizName + ')');
await this.filterVizNames(vizName); await this.filterVizNames(vizName);
// this second wait is usually enough to avoid the await this.clickVizNameLink(vizName);
// 'stale element reference: element is not attached to the page document'
// on the next step
await PageObjects.common.sleep(1000);
// but wrap in a try loop since it can still happen
await retry.try(() => {
log.debug('click visualization (' + vizName + ')');
return this.clickVizNameLink(vizName);
});
await PageObjects.header.clickToastOK(); await PageObjects.header.clickToastOK();
// this second click of 'Add' collapses the Add Visualization pane // this second click of 'Add' collapses the Add Visualization pane
await this.clickAddVisualization(); await this.clickAddVisualization();

View file

@ -129,20 +129,26 @@ export function FindProvider({ getService }) {
async clickByPartialLinkText(linkText, timeout = defaultFindTimeout) { async clickByPartialLinkText(linkText, timeout = defaultFindTimeout) {
log.debug(`clickByPartialLinkText(${linkText})`); log.debug(`clickByPartialLinkText(${linkText})`);
const element = await retry.try(async () => await this.byPartialLinkText(linkText, timeout)); await retry.try(async () => {
const element = await this.byPartialLinkText(linkText, timeout);
await element.click(); await element.click();
});
} }
async clickByLinkText(linkText, timeout = defaultFindTimeout) { async clickByLinkText(linkText, timeout = defaultFindTimeout) {
log.debug(`clickByLinkText(${linkText})`); log.debug(`clickByLinkText(${linkText})`);
await retry.try(async () => {
const element = await this.byLinkText(linkText, timeout); const element = await this.byLinkText(linkText, timeout);
await element.click(); await element.click();
});
} }
async clickByCssSelector(selector, timeout = defaultFindTimeout) { async clickByCssSelector(selector, timeout = defaultFindTimeout) {
log.debug(`clickByCssSelector(${selector})`); log.debug(`clickByCssSelector(${selector})`);
await retry.try(async () => {
const element = await this.byCssSelector(selector, timeout); const element = await this.byCssSelector(selector, timeout);
await element.click(); await element.click();
});
} }
} }