[Discover] Add baseline tests for time field column (#176937)

- Addresses https://github.com/elastic/kibana/issues/174074

Adding tests before I change the code via
https://github.com/elastic/kibana/pull/176834

It checks different permutations of the following conditions:
- with and without selected fields
- data view with and without a time field
- ESQL with and without a time field
- Discover and Dashboard views
- `doc_table:legacy` setting
- `doc_table:hideTimeColumn` setting

The whole test suite is running for ~23 minutes. Here is the successful
build
https://buildkite.com/elastic/kibana-pull-request/builds/193811#018dace6-044f-46f9-b7a9-1fc82d05278c

I am skipping some tests so it does not take so much time on every
Kibana build. If we ever need to check all edge cases (like in
https://github.com/elastic/kibana/pull/176834) we can temporary enable
them again. So in the end the PR adds ~7 minutes to the build.
This commit is contained in:
Julia Rechkunova 2024-02-20 14:58:05 +01:00 committed by GitHub
parent 523e91b63a
commit f004fac3ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 459 additions and 0 deletions

View file

@ -51,6 +51,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await esArchiver.unload('test/functional/fixtures/es_archiver/kibana_sample_data_logs_tsdb');
await esArchiver.unload('test/functional/fixtures/es_archiver/kibana_sample_data_flights');
await kibanaServer.savedObjects.cleanStandardList();
await kibanaServer.uiSettings.replace({});
});
beforeEach(async function () {

View file

@ -0,0 +1,455 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../ftr_provider_context';
const SEARCH_NO_COLUMNS = 'searchNoColumns';
const SEARCH_WITH_ONLY_TIMESTAMP = 'searchWithOnlyTimestampColumn';
const SEARCH_WITH_SELECTED_COLUMNS = 'searchWithSelectedColumns';
const SEARCH_WITH_SELECTED_COLUMNS_AND_TIMESTAMP = 'searchWithSelectedColumnsAndTimestamp';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const dataGrid = getService('dataGrid');
const PageObjects = getPageObjects([
'common',
'discover',
'timePicker',
'dashboard',
'unifiedFieldList',
'header',
]);
const esArchiver = getService('esArchiver');
const retry = getService('retry');
const kibanaServer = getService('kibanaServer');
const dashboardAddPanel = getService('dashboardAddPanel');
const dashboardPanelActions = getService('dashboardPanelActions');
const monacoEditor = getService('monacoEditor');
const testSubjects = getService('testSubjects');
const security = getService('security');
const docTable = getService('docTable');
const defaultSettings = {
defaultIndex: 'logstash-*',
hideAnnouncements: true,
};
describe('discover time field column', function () {
before(async () => {
await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']);
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
});
after(async () => {
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional');
await kibanaServer.savedObjects.cleanStandardList();
await kibanaServer.uiSettings.replace({});
});
async function checkInitialColumns({
hasTimeField,
hideTimeFieldColumnSetting,
savedSearchSuffix,
isTextBased,
}: {
hasTimeField: boolean;
hideTimeFieldColumnSetting: boolean;
savedSearchSuffix: string;
isTextBased?: boolean;
}) {
// check in Discover
expect(await dataGrid.getHeaderFields()).to.eql(
(hideTimeFieldColumnSetting && !isTextBased) || !hasTimeField
? ['Document']
: ['@timestamp', 'Document']
);
await PageObjects.discover.saveSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}`);
await PageObjects.discover.waitUntilSearchingHasFinished();
const isTimestampUnavailableInSidebar = isTextBased && !hasTimeField;
if (!isTimestampUnavailableInSidebar) {
await PageObjects.unifiedFieldList.clickFieldListItemAdd('@timestamp');
await PageObjects.discover.waitUntilSearchingHasFinished();
await retry.try(async () => {
expect(await dataGrid.getHeaderFields()).to.eql(
!hasTimeField
? ['@timestamp']
: hideTimeFieldColumnSetting && !isTextBased
? ['Document'] // legacy behaviour
: ['@timestamp', 'Document'] // legacy behaviour
);
});
await PageObjects.discover.saveSearch(
`${SEARCH_WITH_ONLY_TIMESTAMP}${savedSearchSuffix}`,
true
);
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemRemove('@timestamp');
await retry.try(async () => {
expect(await dataGrid.getHeaderFields()).to.eql(
(hideTimeFieldColumnSetting && !isTextBased) || !hasTimeField
? ['Document']
: ['@timestamp', 'Document']
);
});
}
// check in Dashboard
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.addSavedSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}`);
await dashboardAddPanel.closeAddPanel();
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async () => {
expect(await dataGrid.getHeaderFields()).to.eql(
(hideTimeFieldColumnSetting && !isTextBased) || !hasTimeField
? ['Document']
: ['@timestamp', 'Document']
);
});
if (!isTimestampUnavailableInSidebar) {
await dashboardPanelActions.removePanelByTitle(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}`);
await PageObjects.header.waitUntilLoadingHasFinished();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.addSavedSearch(`${SEARCH_WITH_ONLY_TIMESTAMP}${savedSearchSuffix}`);
await dashboardAddPanel.closeAddPanel();
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async () => {
expect(await dataGrid.getHeaderFields()).to.eql(
!hasTimeField
? ['@timestamp']
: hideTimeFieldColumnSetting && !isTextBased
? ['Document'] // legacy behaviour
: ['@timestamp', 'Document'] // legacy behaviour
);
});
}
}
async function checkSelectedColumns({
hasTimeField,
hideTimeFieldColumnSetting,
savedSearchSuffix,
isTextBased,
}: {
hasTimeField: boolean;
hideTimeFieldColumnSetting: boolean;
savedSearchSuffix: string;
isTextBased?: boolean;
}) {
// check in Discover
await PageObjects.unifiedFieldList.clickFieldListItemAdd('bytes');
await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension');
await retry.try(async () => {
expect(await dataGrid.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting || !hasTimeField || isTextBased
? ['bytes', 'extension']
: ['@timestamp', 'bytes', 'extension']
);
});
await PageObjects.discover.saveSearch(`${SEARCH_WITH_SELECTED_COLUMNS}${savedSearchSuffix}`);
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemAdd('@timestamp');
await retry.try(async () => {
expect(await dataGrid.getHeaderFields()).to.eql(['bytes', 'extension', '@timestamp']);
});
await PageObjects.discover.saveSearch(
`${SEARCH_WITH_SELECTED_COLUMNS_AND_TIMESTAMP}${savedSearchSuffix}`,
true
);
await PageObjects.discover.waitUntilSearchingHasFinished();
await dataGrid.clickMoveColumnLeft('@timestamp');
await retry.try(async () => {
expect(await dataGrid.getHeaderFields()).to.eql(['bytes', '@timestamp', 'extension']);
});
await dataGrid.clickMoveColumnLeft('@timestamp');
await retry.try(async () => {
expect(await dataGrid.getHeaderFields()).to.eql(
// FIXME as a part of https://github.com/elastic/kibana/issues/174074
isTextBased && !hideTimeFieldColumnSetting
? ['bytes', 'extension']
: ['@timestamp', 'bytes', 'extension']
);
});
await PageObjects.unifiedFieldList.clickFieldListItemRemove('@timestamp');
await retry.try(async () => {
expect(await dataGrid.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting || !hasTimeField || isTextBased
? ['bytes', 'extension']
: ['@timestamp', 'bytes', 'extension']
);
});
// check in Dashboard
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.addSavedSearch(`${SEARCH_WITH_SELECTED_COLUMNS}${savedSearchSuffix}`);
await dashboardAddPanel.closeAddPanel();
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async () => {
expect(await dataGrid.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting || !hasTimeField || isTextBased
? ['bytes', 'extension']
: ['@timestamp', 'bytes', 'extension']
);
});
await dashboardPanelActions.removePanelByTitle(
`${SEARCH_WITH_SELECTED_COLUMNS}${savedSearchSuffix}`
);
await PageObjects.header.waitUntilLoadingHasFinished();
await dashboardAddPanel.clickOpenAddPanel();
await dashboardAddPanel.addSavedSearch(
`${SEARCH_WITH_SELECTED_COLUMNS_AND_TIMESTAMP}${savedSearchSuffix}`
);
await dashboardAddPanel.closeAddPanel();
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.try(async () => {
expect(await dataGrid.getHeaderFields()).to.eql(['bytes', 'extension', '@timestamp']);
});
}
// Add `true` to the array to test locally with non default `doc_table:hideTimeColumn` setting too. I would not recommend committing with `true` as it doubles the whole test suit time.
[false].forEach((hideTimeFieldColumnSetting) => {
const savedSearchSuffix = hideTimeFieldColumnSetting ? 'HideTimeColumn' : 'ShowTimeColumn';
describe(`should${hideTimeFieldColumnSetting ? ' not' : ''} add a time field column`, () => {
beforeEach(async () => {
await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();
await kibanaServer.uiSettings.update({
...defaultSettings,
'doc_table:legacy': false,
'doc_table:hideTimeColumn': hideTimeFieldColumnSetting,
});
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.waitUntilSearchingHasFinished();
});
describe('data view mode', () => {
describe('time-based data view', () => {
it('should render initial columns correctly', async () => {
await checkInitialColumns({
hasTimeField: true,
hideTimeFieldColumnSetting,
savedSearchSuffix,
});
});
it('should render selected columns correctly', async () => {
await checkSelectedColumns({
hasTimeField: true,
hideTimeFieldColumnSetting,
savedSearchSuffix,
});
});
});
describe('without a time field', () => {
beforeEach(async () => {
await PageObjects.discover.createAdHocDataView('logs*', false);
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.header.waitUntilLoadingHasFinished();
});
it('should render initial columns correctly', async () => {
await checkInitialColumns({
hasTimeField: false,
hideTimeFieldColumnSetting,
savedSearchSuffix: savedSearchSuffix + '-',
});
});
it('should render selected columns correctly', async () => {
await checkSelectedColumns({
hasTimeField: false,
hideTimeFieldColumnSetting,
savedSearchSuffix: savedSearchSuffix + '-',
});
});
});
});
describe('ESQL mode', () => {
it('should render initial columns correctly', async () => {
await PageObjects.discover.selectTextBaseLang();
await checkInitialColumns({
hasTimeField: true,
hideTimeFieldColumnSetting,
savedSearchSuffix: savedSearchSuffix + 'ESQL',
isTextBased: true,
});
});
it('should render initial columns correctly when no time field', async () => {
await PageObjects.discover.selectTextBaseLang();
await monacoEditor.setCodeEditorValue('from logstash-* | limit 10 | drop @timestamp');
await testSubjects.click('querySubmitButton');
await PageObjects.header.waitUntilLoadingHasFinished();
await checkInitialColumns({
hasTimeField: false,
hideTimeFieldColumnSetting,
savedSearchSuffix: savedSearchSuffix + 'ESQLdrop',
isTextBased: true,
});
});
it('should render selected columns correctly', async () => {
await PageObjects.discover.selectTextBaseLang();
await checkSelectedColumns({
hasTimeField: true,
hideTimeFieldColumnSetting,
savedSearchSuffix: savedSearchSuffix + 'ESQL',
isTextBased: true,
});
});
});
// These tests are skipped as they take a lot of time to run. Temporary unskip them to validate current functionality if necessary.
describe.skip('legacy table', () => {
beforeEach(async () => {
await kibanaServer.uiSettings.update({
...defaultSettings,
'doc_table:hideTimeColumn': hideTimeFieldColumnSetting,
'doc_table:legacy': true,
});
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.waitUntilSearchingHasFinished();
});
it('should render initial columns correctly', async () => {
// no columns
await PageObjects.discover.loadSavedSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}`);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting ? ['Document'] : ['@timestamp', 'Document']
);
await PageObjects.discover.loadSavedSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}-`);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(['Document']);
await PageObjects.discover.loadSavedSearch(
`${SEARCH_NO_COLUMNS}${savedSearchSuffix}ESQL`
);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting ? ['Document'] : ['@timestamp', 'Document']
);
await PageObjects.discover.loadSavedSearch(
`${SEARCH_NO_COLUMNS}${savedSearchSuffix}ESQLdrop`
);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting ? ['Document'] : ['@timestamp', 'Document']
);
// only @timestamp is selected
await PageObjects.discover.loadSavedSearch(
`${SEARCH_WITH_ONLY_TIMESTAMP}${savedSearchSuffix}`
);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting ? ['@timestamp'] : ['@timestamp', '@timestamp']
);
await PageObjects.discover.loadSavedSearch(
`${SEARCH_WITH_ONLY_TIMESTAMP}${savedSearchSuffix}-`
);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(['@timestamp']);
await PageObjects.discover.loadSavedSearch(
`${SEARCH_WITH_ONLY_TIMESTAMP}${savedSearchSuffix}ESQL`
);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting ? ['@timestamp'] : ['@timestamp', '@timestamp']
);
});
it('should render selected columns correctly', async () => {
// with selected columns
await PageObjects.discover.loadSavedSearch(
`${SEARCH_WITH_SELECTED_COLUMNS}${savedSearchSuffix}`
);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting
? ['bytes', 'extension']
: ['@timestamp', 'bytes', 'extension']
);
await PageObjects.discover.loadSavedSearch(
`${SEARCH_WITH_SELECTED_COLUMNS}${savedSearchSuffix}-`
);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(['bytes', 'extension']);
await PageObjects.discover.loadSavedSearch(
`${SEARCH_WITH_SELECTED_COLUMNS}${savedSearchSuffix}ESQL`
);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting
? ['bytes', 'extension']
: ['@timestamp', 'bytes', 'extension']
);
// with selected columns and @timestamp
await PageObjects.discover.loadSavedSearch(
`${SEARCH_WITH_SELECTED_COLUMNS_AND_TIMESTAMP}${savedSearchSuffix}`
);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting
? ['bytes', 'extension', '@timestamp']
: ['@timestamp', 'bytes', 'extension', '@timestamp']
);
await PageObjects.discover.loadSavedSearch(
`${SEARCH_WITH_SELECTED_COLUMNS_AND_TIMESTAMP}${savedSearchSuffix}-`
);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(['bytes', 'extension', '@timestamp']);
await PageObjects.discover.loadSavedSearch(
`${SEARCH_WITH_SELECTED_COLUMNS_AND_TIMESTAMP}${savedSearchSuffix}ESQL`
);
await PageObjects.discover.waitUntilSearchingHasFinished();
expect(await docTable.getHeaderFields()).to.eql(
hideTimeFieldColumnSetting
? ['bytes', 'extension', '@timestamp']
: ['@timestamp', 'bytes', 'extension', '@timestamp']
);
});
});
});
});
});
}

View file

@ -21,6 +21,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
});
loadTestFile(require.resolve('./_default_columns'));
loadTestFile(require.resolve('./_time_field_column'));
loadTestFile(require.resolve('./_drag_drop'));
loadTestFile(require.resolve('./_sidebar'));
loadTestFile(require.resolve('./_request_counts'));

View file

@ -28,6 +28,7 @@ export class DiscoverPageObject extends FtrService {
private readonly fieldEditor = this.ctx.getService('fieldEditor');
private readonly queryBar = this.ctx.getService('queryBar');
private readonly comboBox = this.ctx.getService('comboBox');
private readonly savedObjectsFinder = this.ctx.getService('savedObjectsFinder');
private readonly defaultFindTimeout = this.config.get('timeouts.find');
@ -150,6 +151,7 @@ export class DiscoverPageObject extends FtrService {
public async loadSavedSearch(searchName: string) {
await this.openLoadSavedSearchPanel();
await this.savedObjectsFinder.filterEmbeddableNames(`"${searchName.replace('-', ' ')}"`);
await this.testSubjects.click(`savedObjectTitle${searchName.split(' ').join('-')}`);
await this.header.waitUntilLoadingHasFinished();
}