diff --git a/test/functional/apps/visualize/group6/_tsvb_tsdb_basic.ts b/test/functional/apps/visualize/group6/_tsvb_tsdb_basic.ts new file mode 100644 index 0000000000000..765c7a9d3ec4d --- /dev/null +++ b/test/functional/apps/visualize/group6/_tsvb_tsdb_basic.ts @@ -0,0 +1,75 @@ +/* + * 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'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const { visualBuilder } = getPageObjects(['visualBuilder']); + const log = getService('log'); + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + const testSubjects = getService('testSubjects'); + + describe('visual builder tsdb check', function describeIndexTests() { + before(async () => { + log.info(`loading sample TSDB index...`); + await esArchiver.load('test/functional/fixtures/es_archiver/kibana_sample_data_logs_tsdb'); + log.info(`creating the TSDB data view...`); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_logs_tsdb' + ); + log.info(`setting the TSDB dataView as default...`); + await kibanaServer.uiSettings.replace({ + defaultIndex: '90943e30-9a47-11e8-b64d-95841ca0c247', + }); + }); + + after(async () => { + log.info(`removing the TSDB index...`); + await esArchiver.unload('test/functional/fixtures/es_archiver/kibana_sample_data_logs_tsdb'); + log.info(`removing the TSDB dataView...`); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_logs_tsdb' + ); + log.info(`unsetting the TSDB dataView default...`); + await kibanaServer.uiSettings.unset('defaultIndex'); + }); + + beforeEach(async () => { + await visualBuilder.resetPage(); + }); + + it('should render from a tsdb dataView regular fields with no issues', async () => { + await visualBuilder.selectAggType('Average'); + await visualBuilder.setFieldForAggregation('bytes'); + const isFieldForAggregationValid = await visualBuilder.checkFieldForAggregationValidity(); + expect(isFieldForAggregationValid).to.be(true); + expect(await testSubjects.exists('visualization-error-text')).to.be(false); + }); + + it('should render from a tsdb dataView supported tsdb field type', async () => { + await visualBuilder.selectAggType('Average'); + await visualBuilder.setFieldForAggregation('bytes_gauge'); + const isFieldForAggregationValid = await visualBuilder.checkFieldForAggregationValidity(); + expect(isFieldForAggregationValid).to.be(true); + expect(await testSubjects.exists('visualization-error-text')).to.be(false); + }); + + it('should show an error when using an unsupported tsdb field type', async () => { + await visualBuilder.selectAggType('Average'); + await visualBuilder.setFieldForAggregation('bytes_counter'); + // this is still returning true + const isFieldForAggregationValid = await visualBuilder.checkFieldForAggregationValidity(); + expect(isFieldForAggregationValid).to.be(true); + // but an error should appear in visualization + expect(await testSubjects.exists('visualization-error-text')).to.be(true); + }); + }); +} diff --git a/test/functional/apps/visualize/group6/index.ts b/test/functional/apps/visualize/group6/index.ts index 03027c7a31a8a..062f558625771 100644 --- a/test/functional/apps/visualize/group6/index.ts +++ b/test/functional/apps/visualize/group6/index.ts @@ -27,6 +27,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_tag_cloud')); loadTestFile(require.resolve('./_tsvb_markdown')); loadTestFile(require.resolve('./_tsvb_table')); + loadTestFile(require.resolve('./_tsvb_tsdb_basic')); loadTestFile(require.resolve('./_vega_chart')); }); } diff --git a/test/functional/fixtures/es_archiver/kibana_sample_data_logs_tsdb/data.json.gz b/test/functional/fixtures/es_archiver/kibana_sample_data_logs_tsdb/data.json.gz new file mode 100644 index 0000000000000..7c358cfc48ac1 Binary files /dev/null and b/test/functional/fixtures/es_archiver/kibana_sample_data_logs_tsdb/data.json.gz differ diff --git a/test/functional/fixtures/es_archiver/kibana_sample_data_logs_tsdb/mappings.json b/test/functional/fixtures/es_archiver/kibana_sample_data_logs_tsdb/mappings.json new file mode 100644 index 0000000000000..19722e1df922b --- /dev/null +++ b/test/functional/fixtures/es_archiver/kibana_sample_data_logs_tsdb/mappings.json @@ -0,0 +1,174 @@ +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "kibana_sample_data_logstsdb", + "mappings": { + "_data_stream_timestamp": { + "enabled": true + }, + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "bytes": { + "type": "long" + }, + "bytes_counter": { + "time_series_metric": "counter", + "type": "long" + }, + "bytes_gauge": { + "time_series_metric": "gauge", + "type": "long" + }, + "clientip": { + "type": "ip" + }, + "event": { + "properties": { + "dataset": { + "type": "keyword" + } + } + }, + "extension": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "geo": { + "properties": { + "coordinates": { + "type": "geo_point" + }, + "dest": { + "type": "keyword" + }, + "src": { + "type": "keyword" + }, + "srcdest": { + "type": "keyword" + } + } + }, + "host": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "index": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "ip": { + "type": "ip" + }, + "machine": { + "properties": { + "os": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "ram": { + "type": "long" + } + } + }, + "memory": { + "type": "double" + }, + "message": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "phpmemory": { + "type": "long" + }, + "referer": { + "type": "keyword" + }, + "request": { + "time_series_dimension": true, + "type": "keyword" + }, + "response": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "tags": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "timestamp": { + "path": "@timestamp", + "type": "alias" + }, + "url": { + "time_series_dimension": true, + "type": "keyword" + }, + "utc_time": { + "type": "date" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "mode": "time_series", + "number_of_replicas": "0", + "number_of_shards": "1", + "routing_path": "request", + "time_series": { + "end_time": "2023-06-28T09:17:00.283Z", + "start_time": "2023-03-28T09:17:00.283Z" + } + } + } + } +} \ No newline at end of file diff --git a/test/functional/fixtures/kbn_archiver/kibana_sample_data_logs_tsdb.json b/test/functional/fixtures/kbn_archiver/kibana_sample_data_logs_tsdb.json new file mode 100644 index 0000000000000..6d2c95fa0ac3d --- /dev/null +++ b/test/functional/fixtures/kbn_archiver/kibana_sample_data_logs_tsdb.json @@ -0,0 +1,22 @@ +{ + "attributes": { + "fieldFormatMap": "{\"hour_of_day\":{}}", + "name": "Kibana Sample Data Logs (TSDB)", + "runtimeFieldMap": "{\"hour_of_day\":{\"type\":\"long\",\"script\":{\"source\":\"emit(doc['timestamp'].value.getHour());\"}}}", + "timeFieldName": "timestamp", + "title": "kibana_sample_data_logstsdb" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-04-27T13:09:20.333Z", + "id": "90943e30-9a47-11e8-b64d-95841ca0c247", + "managed": false, + "references": [], + "sort": [ + 1682600960333, + 64 + ], + "type": "index-pattern", + "typeMigrationVersion": "7.11.0", + "updated_at": "2023-04-27T13:09:20.333Z", + "version": "WzIxLDFd" +} \ No newline at end of file diff --git a/x-pack/test/functional/apps/lens/group4/tsdb.ts b/x-pack/test/functional/apps/lens/group4/tsdb.ts index 02b96a74f444e..3828ef875d3af 100644 --- a/x-pack/test/functional/apps/lens/group4/tsdb.ts +++ b/x-pack/test/functional/apps/lens/group4/tsdb.ts @@ -6,130 +6,283 @@ */ import expect from '@kbn/expect'; +import { partition } from 'lodash'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'timePicker', 'lens', 'dashboard']); const testSubjects = getService('testSubjects'); const es = getService('es'); + const find = getService('find'); const log = getService('log'); const indexPatterns = getService('indexPatterns'); const kibanaServer = getService('kibanaServer'); const esArchiver = getService('esArchiver'); describe('lens tsdb', function () { - const dataViewTitle = 'sample-01'; - const rollupDataViewTitle = 'sample-01,sample-01-rollup'; - const fromTime = 'Jun 17, 2022 @ 00:00:00.000'; - const toTime = 'Jun 23, 2022 @ 00:00:00.000'; - const testArchive = 'test/functional/fixtures/es_archiver/search/downsampled'; - const testIndex = 'sample-01'; - const testRollupIndex = 'sample-01-rollup'; - - before(async () => { - // create rollup data - log.info(`loading ${testIndex} index...`); - await esArchiver.loadIfNeeded(testArchive); - log.info(`add write block to ${testIndex} index...`); - await es.indices.addBlock({ index: testIndex, block: 'write' }); - try { - log.info(`rolling up ${testIndex} index...`); - // es client currently does not have method for downsample - await es.transport.request({ - method: 'POST', - path: '/sample-01/_downsample/sample-01-rollup', - body: { fixed_interval: '1h' }, - }); - } catch (err) { - log.info(`ignoring resource_already_exists_exception...`); - if (!err.message.match(/resource_already_exists_exception/)) { - throw err; + describe('downsampling', () => { + const dataViewTitle = 'sample-01'; + const rollupDataViewTitle = 'sample-01,sample-01-rollup'; + const fromTime = 'Jun 17, 2022 @ 00:00:00.000'; + const toTime = 'Jun 23, 2022 @ 00:00:00.000'; + const testArchive = 'test/functional/fixtures/es_archiver/search/downsampled'; + const testIndex = 'sample-01'; + const testRollupIndex = 'sample-01-rollup'; + + before(async () => { + // create rollup data + log.info(`loading ${testIndex} index...`); + await esArchiver.loadIfNeeded(testArchive); + log.info(`add write block to ${testIndex} index...`); + await es.indices.addBlock({ index: testIndex, block: 'write' }); + try { + log.info(`rolling up ${testIndex} index...`); + // es client currently does not have method for downsample + await es.transport.request({ + method: 'POST', + path: '/sample-01/_downsample/sample-01-rollup', + body: { fixed_interval: '1h' }, + }); + } catch (err) { + log.info(`ignoring resource_already_exists_exception...`); + if (!err.message.match(/resource_already_exists_exception/)) { + throw err; + } } - } - log.info(`creating ${rollupDataViewTitle} data view...`); - await indexPatterns.create( - { - title: rollupDataViewTitle, - timeFieldName: '@timestamp', - }, - { override: true } - ); - await indexPatterns.create( - { - title: dataViewTitle, - timeFieldName: '@timestamp', - }, - { override: true } - ); - await kibanaServer.uiSettings.update({ - 'dateFormat:tz': 'UTC', - defaultIndex: '0ae0bc7a-e4ca-405c-ab67-f2b5913f2a51', - 'timepicker:timeDefaults': '{ "from": "now-1y", "to": "now" }', + log.info(`creating ${rollupDataViewTitle} data view...`); + await indexPatterns.create( + { + title: rollupDataViewTitle, + timeFieldName: '@timestamp', + }, + { override: true } + ); + await indexPatterns.create( + { + title: dataViewTitle, + timeFieldName: '@timestamp', + }, + { override: true } + ); + await kibanaServer.uiSettings.update({ + 'dateFormat:tz': 'UTC', + defaultIndex: '0ae0bc7a-e4ca-405c-ab67-f2b5913f2a51', + 'timepicker:timeDefaults': '{ "from": "now-1y", "to": "now" }', + }); }); - }); - after(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - await kibanaServer.uiSettings.replace({}); - await es.indices.delete({ index: [testIndex, testRollupIndex] }); - }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.uiSettings.replace({}); + await es.indices.delete({ index: [testIndex, testRollupIndex] }); + }); - describe('for regular metric', () => { - it('defaults to median for non-rolled up metric', async () => { - await PageObjects.common.navigateToApp('lens'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await PageObjects.lens.switchDataPanelIndexPattern(dataViewTitle); - await PageObjects.lens.waitForField('kubernetes.container.memory.available.bytes'); - await PageObjects.lens.dragFieldToWorkspace( - 'kubernetes.container.memory.available.bytes', - 'xyVisChart' - ); - expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql( - 'Median of kubernetes.container.memory.available.bytes' - ); + describe('for regular metric', () => { + it('defaults to median for non-rolled up metric', async () => { + await PageObjects.common.navigateToApp('lens'); + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.lens.switchDataPanelIndexPattern(dataViewTitle); + await PageObjects.lens.waitForField('kubernetes.container.memory.available.bytes'); + await PageObjects.lens.dragFieldToWorkspace( + 'kubernetes.container.memory.available.bytes', + 'xyVisChart' + ); + expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql( + 'Median of kubernetes.container.memory.available.bytes' + ); + }); + + it('does not show a warning', async () => { + await PageObjects.lens.openDimensionEditor('lnsXY_yDimensionPanel'); + await testSubjects.missingOrFail('median-partial-warning'); + await PageObjects.lens.assertNoEditorWarning(); + await PageObjects.lens.closeDimensionEditor(); + }); }); - it('does not show a warning', async () => { - await PageObjects.lens.openDimensionEditor('lnsXY_yDimensionPanel'); - await testSubjects.missingOrFail('median-partial-warning'); - await PageObjects.lens.assertNoEditorWarning(); - await PageObjects.lens.closeDimensionEditor(); + describe('for rolled up metric', () => { + it('defaults to average for rolled up metric', async () => { + await PageObjects.lens.switchDataPanelIndexPattern(rollupDataViewTitle); + await PageObjects.lens.removeLayer(); + await PageObjects.lens.waitForField('kubernetes.container.memory.available.bytes'); + await PageObjects.lens.dragFieldToWorkspace( + 'kubernetes.container.memory.available.bytes', + 'xyVisChart' + ); + expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql( + 'Average of kubernetes.container.memory.available.bytes' + ); + }); + it('shows warnings in editor when using median', async () => { + await PageObjects.lens.openDimensionEditor('lnsXY_yDimensionPanel'); + await testSubjects.existOrFail('median-partial-warning'); + await testSubjects.click('lns-indexPatternDimension-median'); + await PageObjects.lens.waitForVisualization('xyVisChart'); + await PageObjects.lens.assertMessageListContains( + 'Median of kubernetes.container.memory.available.bytes uses a function that is unsupported by rolled up data. Select a different function or change the time range.', + 'warning' + ); + }); + it('shows warnings in dashboards as well', async () => { + await PageObjects.lens.save('New', false, false, false, 'new'); + + await PageObjects.dashboard.waitForRenderComplete(); + await PageObjects.lens.assertMessageListContains( + 'Median of kubernetes.container.memory.available.bytes uses a function that is unsupported by rolled up data. Select a different function or change the time range.', + 'warning' + ); + }); }); }); - describe('for rolled up metric', () => { - it('defaults to average for rolled up metric', async () => { - await PageObjects.lens.switchDataPanelIndexPattern(rollupDataViewTitle); - await PageObjects.lens.removeLayer(); - await PageObjects.lens.waitForField('kubernetes.container.memory.available.bytes'); - await PageObjects.lens.dragFieldToWorkspace( - 'kubernetes.container.memory.available.bytes', - 'xyVisChart' - ); - expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yDimensionPanel')).to.eql( - 'Average of kubernetes.container.memory.available.bytes' + describe('field types support', () => { + before(async () => { + log.info(`loading sample TSDB index...`); + await esArchiver.load('test/functional/fixtures/es_archiver/kibana_sample_data_logs_tsdb'); + log.info(`creating the TSDB data view...`); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_logs_tsdb' ); + log.info(`setting the TSDB dataView as default...`); + await kibanaServer.uiSettings.replace({ + defaultIndex: '90943e30-9a47-11e8-b64d-95841ca0c247', + }); + await PageObjects.common.navigateToApp('lens'); + await PageObjects.lens.goToTimeRange(); }); - it('shows warnings in editor when using median', async () => { - await PageObjects.lens.openDimensionEditor('lnsXY_yDimensionPanel'); - await testSubjects.existOrFail('median-partial-warning'); - await testSubjects.click('lns-indexPatternDimension-median'); - await PageObjects.lens.waitForVisualization('xyVisChart'); - await PageObjects.lens.assertMessageListContains( - 'Median of kubernetes.container.memory.available.bytes uses a function that is unsupported by rolled up data. Select a different function or change the time range.', - 'warning' + + after(async () => { + log.info(`removing the TSDB index...`); + await esArchiver.unload( + 'test/functional/fixtures/es_archiver/kibana_sample_data_logs_tsdb' + ); + log.info(`removing the TSDB dataView...`); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_logs_tsdb' ); + log.info(`unsetting the TSDB dataView default...`); + await kibanaServer.uiSettings.unset('defaultIndex'); }); - it('shows warnings in dashboards as well', async () => { - await PageObjects.lens.save('New', false, false, false, 'new'); - await PageObjects.dashboard.waitForRenderComplete(); - await PageObjects.lens.assertMessageListContains( - 'Median of kubernetes.container.memory.available.bytes uses a function that is unsupported by rolled up data. Select a different function or change the time range.', - 'warning' - ); + afterEach(async () => { + await PageObjects.lens.removeLayer(); }); + + // skip count for now as it's a special function and will + // change automatically the unsupported field to Records when detected + const operationsByFieldSupport = [ + 'average', + 'max', + 'last_value', + 'median', + 'percentile', + 'percentile_rank', + 'standard_deviation', + 'sum', + 'unique_count', + ].map((name) => ({ + name, + label: `${name[0].toUpperCase()}${name.slice(1).replace('_', ' ')}`, + counter: ['max', 'counter_rate'].includes(name), + gauge: true, + })); + + for (const fieldType of ['counter', 'gauge'] as const) { + const [supportedOperations, unsupportedOperatons] = partition( + operationsByFieldSupport, + (op) => op[fieldType] + ); + if (supportedOperations.length) { + it(`should allow operations when supported by ${fieldType} field type`, async () => { + // Counter rate requires a date histogram dimension configured to work + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'date_histogram', + field: '@timestamp', + }); + + // minimum supports all tsdb field types + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'min', + field: `bytes_${fieldType}`, + keepOpen: true, + }); + + // now check if the provided function has no incompatibility tooltip + for (const supportedOp of supportedOperations) { + expect( + testSubjects.exists(`lns-indexPatternDimension-${supportedOp.name} incompatible`, { + timeout: 500, + }) + ).to.eql(supportedOp[fieldType]); + } + + for (const supportedOp of supportedOperations) { + // try to change to the provided function and check all is ok + await PageObjects.lens.selectOperation(supportedOp.name); + + expect( + await find.existsByCssSelector( + '[data-test-subj="indexPattern-field-selection-row"] .euiFormErrorText' + ) + ).to.be(false); + + // return in a clean state before checking the next operation + await PageObjects.lens.selectOperation('min'); + } + await PageObjects.lens.closeDimensionEditor(); + }); + } + if (unsupportedOperatons.length) { + it(`should notify the incompatibility of unsupported operations for the ${fieldType} field type`, async () => { + // Counter rate requires a date histogram dimension configured to work + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'date_histogram', + field: '@timestamp', + }); + + // minimum supports all tsdb field types + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'min', + field: `bytes_${fieldType}`, + keepOpen: true, + }); + + // now check if the provided function has the incompatibility tooltip + for (const unsupportedOp of unsupportedOperatons) { + expect( + testSubjects.exists( + `lns-indexPatternDimension-${unsupportedOp.name} incompatible`, + { + timeout: 500, + } + ) + ).to.eql(!unsupportedOp[fieldType]); + } + + for (const unsupportedOp of unsupportedOperatons) { + // try to change to the provided function and check if it's in an incompatibility state + await PageObjects.lens.selectOperation(unsupportedOp.name, true); + + const fieldSelectErrorEl = await find.byCssSelector( + '[data-test-subj="indexPattern-field-selection-row"] .euiFormErrorText' + ); + + expect(await fieldSelectErrorEl.getVisibleText()).to.be( + 'This field does not work with the selected function.' + ); + + // return in a clean state before checking the next operation + await PageObjects.lens.selectOperation('min'); + } + await PageObjects.lens.closeDimensionEditor(); + }); + } + } }); }); } diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 6db18af0abeaa..751ca0d6dcde3 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -191,22 +191,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont if (opts.operation === 'formula') { await this.switchToFormula(); } else { - const operationSelector = opts.isPreviousIncompatible - ? `lns-indexPatternDimension-${opts.operation} incompatible` - : `lns-indexPatternDimension-${opts.operation}`; - async function getAriaPressed() { - const operationSelectorContainer = await testSubjects.find(operationSelector); - await testSubjects.click(operationSelector); - const ariaPressed = await operationSelectorContainer.getAttribute('aria-pressed'); - return ariaPressed; - } - - // adding retry here as it seems that there is a flakiness of the operation click - // it seems that the aria-pressed attribute is updated to true when the button is clicked - await retry.waitFor('aria pressed to be true', async () => { - const ariaPressedStatus = await getAriaPressed(); - return ariaPressedStatus === 'true'; - }); + await this.selectOperation(opts.operation, opts.isPreviousIncompatible); } if (opts.field) { await this.selectOptionFromComboBox('indexPattern-dimension-field', opts.field); @@ -574,6 +559,25 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }); }, + async selectOperation(operation: string, isPreviousIncompatible: boolean = false) { + const operationSelector = isPreviousIncompatible + ? `lns-indexPatternDimension-${operation} incompatible` + : `lns-indexPatternDimension-${operation}`; + async function getAriaPressed() { + const operationSelectorContainer = await testSubjects.find(operationSelector); + await testSubjects.click(operationSelector); + const ariaPressed = await operationSelectorContainer.getAttribute('aria-pressed'); + return ariaPressed; + } + + // adding retry here as it seems that there is a flakiness of the operation click + // it seems that the aria-pressed attribute is updated to true when the button is clicked + await retry.waitFor('aria pressed to be true', async () => { + const ariaPressedStatus = await getAriaPressed(); + return ariaPressedStatus === 'true'; + }); + }, + async enableTimeShift() { await testSubjects.click('indexPattern-advanced-accordion'); },