[8.6] [ML] Explain Log Rate Spikes: Additional functional tests with artificial logs dataset (#145763) (#146038)

# Backport

This will backport the following commits from `main` to `8.6`:
- [[ML] Explain Log Rate Spikes: Additional functional tests with
artificial logs dataset
(#145763)](https://github.com/elastic/kibana/pull/145763)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Walter
Rafelsberger","email":"walter.rafelsberger@elastic.co"},"sourceCommit":{"committedDate":"2022-11-22T17:28:54Z","message":"[ML]
Explain Log Rate Spikes: Additional functional tests with artificial
logs dataset (#145763)\n\nAdditional functional tests with artificial
logs dataset.\r\n\r\nThis generates an artificial logs dataset with the
purpose of asserting\r\nthe case where grouping needs to identify 2
non-overlapping groups. The\r\nprimary use case of the test is to track
possible regressions related to\r\nwhen trying to improve
`frequent_items` aggregation settings and the\r\ncustom grouping
code.","sha":"c45f55660584381d385e11817d4285e3e24eb546","branchLabelMapping":{"^v8.7.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":[":ml","release_note:skip","Feature:ML/AIOps","v8.6.0","v8.7.0"],"number":145763,"url":"https://github.com/elastic/kibana/pull/145763","mergeCommit":{"message":"[ML]
Explain Log Rate Spikes: Additional functional tests with artificial
logs dataset (#145763)\n\nAdditional functional tests with artificial
logs dataset.\r\n\r\nThis generates an artificial logs dataset with the
purpose of asserting\r\nthe case where grouping needs to identify 2
non-overlapping groups. The\r\nprimary use case of the test is to track
possible regressions related to\r\nwhen trying to improve
`frequent_items` aggregation settings and the\r\ncustom grouping
code.","sha":"c45f55660584381d385e11817d4285e3e24eb546"}},"sourceBranch":"main","suggestedTargetBranches":["8.6"],"targetPullRequestStates":[{"branch":"8.6","label":"v8.6.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.7.0","labelRegex":"^v8.7.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/145763","number":145763,"mergeCommit":{"message":"[ML]
Explain Log Rate Spikes: Additional functional tests with artificial
logs dataset (#145763)\n\nAdditional functional tests with artificial
logs dataset.\r\n\r\nThis generates an artificial logs dataset with the
purpose of asserting\r\nthe case where grouping needs to identify 2
non-overlapping groups. The\r\nprimary use case of the test is to track
possible regressions related to\r\nwhen trying to improve
`frequent_items` aggregation settings and the\r\ncustom grouping
code.","sha":"c45f55660584381d385e11817d4285e3e24eb546"}}]}] BACKPORT-->

Co-authored-by: Walter Rafelsberger <walter.rafelsberger@elastic.co>
This commit is contained in:
Kibana Machine 2022-11-22 14:01:26 -05:00 committed by GitHub
parent 2a14c23678
commit 010bb24b87
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 405 additions and 37 deletions

View file

@ -8,7 +8,7 @@
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../ftr_provider_context';
import type { TestData } from './types';
import type { TestDataEsArchive } from './types';
import { farequoteDataViewTestData } from './test_data';
const ES_INDEX = 'ft_farequote';
@ -23,14 +23,14 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
// aiops / Explain Log Rate Spikes lives in the ML UI so we need some related services.
const ml = getService('ml');
function runTests(testData: TestData) {
function runTests(testData: TestDataEsArchive) {
it(`${testData.suiteTitle} loads the source data in explain log rate spikes`, async () => {
await elasticChart.setNewChartUiDebugFlag(true);
await ml.testExecution.logTestStep(
`${testData.suiteTitle} loads the saved search selection page`
);
await aiops.explainLogRateSpikes.navigateToIndexPatternSelection();
await aiops.explainLogRateSpikesPage.navigateToIndexPatternSelection();
await ml.testExecution.logTestStep(
`${testData.suiteTitle} loads the explain log rate spikes page`
@ -42,10 +42,10 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
it(`${testData.suiteTitle} displays index details`, async () => {
await ml.testExecution.logTestStep(`${testData.suiteTitle} displays the time range step`);
await aiops.explainLogRateSpikes.assertTimeRangeSelectorSectionExists();
await aiops.explainLogRateSpikesPage.assertTimeRangeSelectorSectionExists();
await ml.testExecution.logTestStep(`${testData.suiteTitle} loads data for full time range`);
await aiops.explainLogRateSpikes.clickUseFullDataButton(
await aiops.explainLogRateSpikesPage.clickUseFullDataButton(
testData.expected.totalDocCountFormatted
);
await headerPage.waitUntilLoadingHasFinished();
@ -53,53 +53,53 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
await ml.testExecution.logTestStep(
`${testData.suiteTitle} displays elements in the doc count panel correctly`
);
await aiops.explainLogRateSpikes.assertTotalDocCountHeaderExists();
await aiops.explainLogRateSpikes.assertTotalDocCountChartExists();
await aiops.explainLogRateSpikesPage.assertTotalDocCountHeaderExists();
await aiops.explainLogRateSpikesPage.assertTotalDocCountChartExists();
await ml.testExecution.logTestStep(
`${testData.suiteTitle} displays elements in the page correctly`
);
await aiops.explainLogRateSpikes.assertSearchPanelExists();
await aiops.explainLogRateSpikesPage.assertSearchPanelExists();
await ml.testExecution.logTestStep('displays empty prompt');
await aiops.explainLogRateSpikes.assertNoWindowParametersEmptyPromptExists();
await aiops.explainLogRateSpikesPage.assertNoWindowParametersEmptyPromptExists();
await ml.testExecution.logTestStep('clicks the document count chart to start analysis');
await aiops.explainLogRateSpikes.clickDocumentCountChart();
await aiops.explainLogRateSpikes.assertAnalysisSectionExists();
await aiops.explainLogRateSpikesPage.clickDocumentCountChart(testData.chartClickCoordinates);
await aiops.explainLogRateSpikesPage.assertAnalysisSectionExists();
await ml.testExecution.logTestStep('displays the no results found prompt');
await aiops.explainLogRateSpikes.assertNoResultsFoundEmptyPromptExists();
await aiops.explainLogRateSpikesPage.assertNoResultsFoundEmptyPromptExists();
await ml.testExecution.logTestStep('adjusts the brushes to get analysis results');
await aiops.explainLogRateSpikes.assertRerunAnalysisButtonExists(false);
await aiops.explainLogRateSpikesPage.assertRerunAnalysisButtonExists(false);
// Get the current width of the deviation brush for later comparison.
const brushSelectionWidthBefore = await aiops.explainLogRateSpikes.getBrushSelectionWidth(
const brushSelectionWidthBefore = await aiops.explainLogRateSpikesPage.getBrushSelectionWidth(
'aiopsBrushDeviation'
);
// Get the px values for the timestamp we want to move the brush to.
const { targetPx, intervalPx } = await aiops.explainLogRateSpikes.getPxForTimestamp(
testData.brushTargetTimestamp
const { targetPx, intervalPx } = await aiops.explainLogRateSpikesPage.getPxForTimestamp(
testData.brushDeviationTargetTimestamp
);
// Adjust the right brush handle
await aiops.explainLogRateSpikes.adjustBrushHandler(
await aiops.explainLogRateSpikesPage.adjustBrushHandler(
'aiopsBrushDeviation',
'handle--e',
targetPx + intervalPx
);
// Adjust the left brush handle
await aiops.explainLogRateSpikes.adjustBrushHandler(
await aiops.explainLogRateSpikesPage.adjustBrushHandler(
'aiopsBrushDeviation',
'handle--w',
targetPx
);
// Get the new brush selection width for later comparison.
const brushSelectionWidthAfter = await aiops.explainLogRateSpikes.getBrushSelectionWidth(
const brushSelectionWidthAfter = await aiops.explainLogRateSpikesPage.getBrushSelectionWidth(
'aiopsBrushDeviation'
);
@ -110,18 +110,18 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
expect(brushSelectionWidthBefore).not.to.be(brushSelectionWidthAfter);
expect(brushSelectionWidthAfter).not.to.be.greaterThan(intervalPx * 2);
await aiops.explainLogRateSpikes.assertRerunAnalysisButtonExists(true);
await aiops.explainLogRateSpikesPage.assertRerunAnalysisButtonExists(true);
await ml.testExecution.logTestStep('rerun the analysis with adjusted settings');
await aiops.explainLogRateSpikes.clickRerunAnalysisButton(true);
await aiops.explainLogRateSpikes.assertProgressTitle('Progress: 100% — Done.');
await aiops.explainLogRateSpikesPage.clickRerunAnalysisButton(true);
await aiops.explainLogRateSpikesPage.assertProgressTitle('Progress: 100% — Done.');
// The group switch should be disabled by default
await aiops.explainLogRateSpikes.assertSpikeAnalysisGroupSwitchExists(false);
await aiops.explainLogRateSpikesPage.assertSpikeAnalysisGroupSwitchExists(false);
// Enabled grouping
await aiops.explainLogRateSpikes.clickSpikeAnalysisGroupSwitch(false);
await aiops.explainLogRateSpikesPage.clickSpikeAnalysisGroupSwitch(false);
await aiops.explainLogRateSpikesAnalysisGroupsTable.assertSpikeAnalysisTableExists();

View file

@ -0,0 +1,225 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../ftr_provider_context';
import type { TestDataGenerated } from './types';
import { artificialLogDataViewTestData } from './test_data';
export default function ({ getPageObject, getService }: FtrProviderContext) {
const es = getService('es');
const headerPage = getPageObject('header');
const elasticChart = getService('elasticChart');
const aiops = getService('aiops');
const log = getService('log');
// aiops / Explain Log Rate Spikes lives in the ML UI so we need some related services.
const ml = getService('ml');
function runTests(testData: TestDataGenerated) {
it(`${testData.suiteTitle} loads the source data in explain log rate spikes`, async () => {
await elasticChart.setNewChartUiDebugFlag(true);
await ml.testExecution.logTestStep(
`${testData.suiteTitle} loads the saved search selection page`
);
await aiops.explainLogRateSpikesPage.navigateToIndexPatternSelection();
await ml.testExecution.logTestStep(
`${testData.suiteTitle} loads the explain log rate spikes page`
);
await ml.jobSourceSelection.selectSourceForExplainLogRateSpikes(
testData.sourceIndexOrSavedSearch
);
});
it(`${testData.suiteTitle} displays index details`, async () => {
await ml.testExecution.logTestStep(`${testData.suiteTitle} displays the time range step`);
await aiops.explainLogRateSpikesPage.assertTimeRangeSelectorSectionExists();
await ml.testExecution.logTestStep(`${testData.suiteTitle} loads data for full time range`);
await aiops.explainLogRateSpikesPage.clickUseFullDataButton(
testData.expected.totalDocCountFormatted
);
await headerPage.waitUntilLoadingHasFinished();
await ml.testExecution.logTestStep(
`${testData.suiteTitle} displays elements in the doc count panel correctly`
);
await aiops.explainLogRateSpikesPage.assertTotalDocCountHeaderExists();
await aiops.explainLogRateSpikesPage.assertTotalDocCountChartExists();
await ml.testExecution.logTestStep(
`${testData.suiteTitle} displays elements in the page correctly`
);
await aiops.explainLogRateSpikesPage.assertSearchPanelExists();
await ml.testExecution.logTestStep('displays empty prompt');
await aiops.explainLogRateSpikesPage.assertNoWindowParametersEmptyPromptExists();
await ml.testExecution.logTestStep('clicks the document count chart to start analysis');
await aiops.explainLogRateSpikesPage.clickDocumentCountChart(testData.chartClickCoordinates);
await aiops.explainLogRateSpikesPage.assertAnalysisSectionExists();
await ml.testExecution.logTestStep('displays the no results found prompt');
await aiops.explainLogRateSpikesPage.assertNoResultsFoundEmptyPromptExists();
await ml.testExecution.logTestStep('adjusts the brushes to get analysis results');
await aiops.explainLogRateSpikesPage.assertRerunAnalysisButtonExists(false);
// Get the current width of the deviation brush for later comparison.
const brushSelectionWidthBefore = await aiops.explainLogRateSpikesPage.getBrushSelectionWidth(
'aiopsBrushDeviation'
);
// Get the px values for the timestamp we want to move the brush to.
const { targetPx, intervalPx } = await aiops.explainLogRateSpikesPage.getPxForTimestamp(
testData.brushDeviationTargetTimestamp
);
// Adjust the right brush handle
await aiops.explainLogRateSpikesPage.adjustBrushHandler(
'aiopsBrushDeviation',
'handle--e',
targetPx + intervalPx * testData.brushIntervalFactor
);
// Adjust the left brush handle
await aiops.explainLogRateSpikesPage.adjustBrushHandler(
'aiopsBrushDeviation',
'handle--w',
targetPx - intervalPx * testData.brushIntervalFactor
);
if (testData.brushBaselineTargetTimestamp) {
// Get the px values for the timestamp we want to move the brush to.
const { targetPx: targetBaselinePx } =
await aiops.explainLogRateSpikesPage.getPxForTimestamp(
testData.brushBaselineTargetTimestamp
);
// Adjust the right brush handle
await aiops.explainLogRateSpikesPage.adjustBrushHandler(
'aiopsBrushBaseline',
'handle--e',
targetBaselinePx + intervalPx * testData.brushIntervalFactor
);
// Adjust the left brush handle
await aiops.explainLogRateSpikesPage.adjustBrushHandler(
'aiopsBrushBaseline',
'handle--w',
targetBaselinePx - intervalPx * testData.brushIntervalFactor
);
}
// Get the new brush selection width for later comparison.
const brushSelectionWidthAfter = await aiops.explainLogRateSpikesPage.getBrushSelectionWidth(
'aiopsBrushDeviation'
);
// Assert the adjusted brush: The selection width should have changed and
// we test if the selection is smaller than two bucket intervals.
// Finally, the adjusted brush should trigger
// a warning on the "Rerun analysis" button.
expect(brushSelectionWidthBefore).not.to.be(brushSelectionWidthAfter);
expect(brushSelectionWidthAfter).not.to.be.greaterThan(intervalPx * 21);
await aiops.explainLogRateSpikesPage.assertRerunAnalysisButtonExists(true);
await ml.testExecution.logTestStep('rerun the analysis with adjusted settings');
await aiops.explainLogRateSpikesPage.clickRerunAnalysisButton(true);
await aiops.explainLogRateSpikesPage.assertProgressTitle('Progress: 100% — Done.');
// The group switch should be disabled by default
await aiops.explainLogRateSpikesPage.assertSpikeAnalysisGroupSwitchExists(false);
// Enabled grouping
await aiops.explainLogRateSpikesPage.clickSpikeAnalysisGroupSwitch(false);
await aiops.explainLogRateSpikesAnalysisGroupsTable.assertSpikeAnalysisTableExists();
const analysisGroupsTable =
await aiops.explainLogRateSpikesAnalysisGroupsTable.parseAnalysisTable();
expect(analysisGroupsTable).to.be.eql(testData.expected.analysisGroupsTable);
await ml.testExecution.logTestStep('expand table row');
await aiops.explainLogRateSpikesAnalysisGroupsTable.assertExpandRowButtonExists();
await aiops.explainLogRateSpikesAnalysisGroupsTable.expandRow();
const analysisTable = await aiops.explainLogRateSpikesAnalysisTable.parseAnalysisTable();
expect(analysisTable).to.be.eql(testData.expected.analysisTable);
});
}
describe('explain log rate spikes - artificial log data', function () {
this.tags(['aiops']);
before(async () => {
try {
await es.indices.delete({ index: artificialLogDataViewTestData.sourceIndexOrSavedSearch });
} catch (e) {
log.error(
`Error deleting index '${artificialLogDataViewTestData.sourceIndexOrSavedSearch}' in before() callback`
);
}
// Create index with mapping
await es.indices.create({
index: artificialLogDataViewTestData.sourceIndexOrSavedSearch,
mappings: {
properties: {
user: { type: 'keyword' },
response_code: { type: 'keyword' },
url: { type: 'keyword' },
version: { type: 'keyword' },
'@timestamp': { type: 'date' },
},
},
});
await es.bulk({
refresh: 'wait_for',
body: artificialLogDataViewTestData.bulkBody,
});
await ml.testResources.createIndexPatternIfNeeded(
artificialLogDataViewTestData.sourceIndexOrSavedSearch,
'@timestamp'
);
await ml.testResources.setKibanaTimeZoneToUTC();
await ml.securityUI.loginAsMlPowerUser();
});
after(async () => {
await elasticChart.setNewChartUiDebugFlag(false);
await ml.testResources.deleteIndexPatternByTitle(
artificialLogDataViewTestData.sourceIndexOrSavedSearch
);
try {
await es.indices.delete({ index: artificialLogDataViewTestData.sourceIndexOrSavedSearch });
} catch (e) {
log.error(
`Error deleting index '${artificialLogDataViewTestData.sourceIndexOrSavedSearch}' in after() callback`
);
}
});
describe('with artificial logs', function () {
// Run tests on full farequote index.
it(`${artificialLogDataViewTestData.suiteTitle} loads the explain log rate spikes page`, async () => {
// Start navigation from the base of the ML app.
await ml.navigation.navigateToMl();
await elasticChart.setNewChartUiDebugFlag(true);
});
runTests(artificialLogDataViewTestData);
});
});
}

View file

@ -33,6 +33,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
await ml.testResources.resetKibanaTimeZone();
});
loadTestFile(require.resolve('./explain_log_rate_spikes'));
loadTestFile(require.resolve('./explain_log_rate_spikes_farequote'));
loadTestFile(require.resolve('./explain_log_rate_spikes_logs'));
});
}

View file

@ -5,13 +5,17 @@
* 2.0.
*/
import { TestData } from './types';
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
export const farequoteDataViewTestData: TestData = {
import type { GeneratedDoc, TestDataEsArchive, TestDataGenerated } from './types';
export const farequoteDataViewTestData: TestDataEsArchive = {
suiteTitle: 'farequote index pattern',
isSavedSearch: false,
sourceIndexOrSavedSearch: 'ft_farequote',
brushTargetTimestamp: 1455033600000,
brushDeviationTargetTimestamp: 1455033600000,
brushIntervalFactor: 1,
chartClickCoordinates: [0, 0],
expected: {
totalDocCountFormatted: '86,374',
analysisGroupsTable: [
@ -32,3 +36,113 @@ export const farequoteDataViewTestData: TestData = {
],
},
};
const REFERENCE_TS = 1669018354793;
const DAY_MS = 86400000;
const ES_INDEX = 'aiops_frequent_items_test';
const DEVIATION_TS = REFERENCE_TS - DAY_MS * 2;
const BASELINE_TS = DEVIATION_TS - DAY_MS * 1;
export const artificialLogDataViewTestData: TestDataGenerated = {
suiteTitle: 'artificial index pattern',
isSavedSearch: false,
sourceIndexOrSavedSearch: 'aiops_frequent_items_test',
brushBaselineTargetTimestamp: BASELINE_TS + DAY_MS / 2,
brushDeviationTargetTimestamp: DEVIATION_TS + DAY_MS / 2,
brushIntervalFactor: 10,
chartClickCoordinates: [-200, 30],
bulkBody: getArtificialLogsBulkBody(),
expected: {
totalDocCountFormatted: '8,400',
analysisGroupsTable: [
{ group: 'user: Peter', docCount: '2081' },
{ group: 'response_code: 500url: login.php', docCount: '834' },
],
analysisTable: [
{
fieldName: 'user',
fieldValue: 'Peter',
logRate: 'Chart type:bar chart',
pValue: '2.78e-22',
impact: 'High',
},
],
},
};
function getArtificialLogsBulkBody() {
const bulkBody: estypes.BulkRequest<GeneratedDoc, GeneratedDoc>['body'] = [];
const action = { index: { _index: ES_INDEX } };
let tsOffset = 0;
// Creates docs evenly spread across baseline and deviation time frame
[BASELINE_TS, DEVIATION_TS].forEach((ts) => {
['Peter', 'Paul', 'Mary'].forEach((user) => {
['200', '404', '500'].forEach((responseCode) => {
['login.php', 'user.php', 'home.php'].forEach((url) => {
// Don't add docs that match the exact pattern of the filter we want to base the test queries on
if (
!(
user === 'Peter' &&
responseCode === '500' &&
(url === 'home.php' || url === 'login.php')
)
) {
tsOffset = 0;
[...Array(100)].forEach(() => {
tsOffset += DAY_MS / 100;
const doc: GeneratedDoc = {
user,
response_code: responseCode,
url,
version: 'v1.0.0',
'@timestamp': ts + tsOffset,
};
bulkBody.push(action);
bulkBody.push(doc);
});
}
});
});
});
});
// Now let's add items to the dataset to make some specific significant terms being returned as results
['200', '404'].forEach((responseCode) => {
['login.php', 'user.php', 'home.php'].forEach((url) => {
tsOffset = 0;
[...Array(300)].forEach(() => {
tsOffset += DAY_MS / 300;
bulkBody.push(action);
bulkBody.push({
user: 'Peter',
response_code: responseCode,
url,
version: 'v1.0.0',
'@timestamp': DEVIATION_TS + tsOffset,
});
});
});
});
['Paul', 'Mary'].forEach((user) => {
['login.php', 'home.php'].forEach((url) => {
tsOffset = 0;
[...Array(400)].forEach(() => {
tsOffset += DAY_MS / 400;
bulkBody.push(action);
bulkBody.push({
user,
response_code: '500',
url,
version: 'v1.0.0',
'@timestamp': DEVIATION_TS + tsOffset,
});
});
});
});
return bulkBody;
}

View file

@ -5,12 +5,17 @@
* 2.0.
*/
export interface TestData {
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
export interface TestDataEsArchive {
suiteTitle: string;
isSavedSearch?: boolean;
sourceIndexOrSavedSearch: string;
rowsPerPage?: 10 | 25 | 50;
brushTargetTimestamp: number;
brushBaselineTargetTimestamp?: number;
brushDeviationTargetTimestamp: number;
brushIntervalFactor: number;
chartClickCoordinates: [number, number];
expected: {
totalDocCountFormatted: string;
analysisGroupsTable: Array<{ group: string; docCount: string }>;
@ -23,3 +28,15 @@ export interface TestData {
}>;
};
}
export interface GeneratedDoc {
user: string;
response_code: string;
url: string;
version: string;
'@timestamp': number;
}
export interface TestDataGenerated extends TestDataEsArchive {
bulkBody: estypes.BulkRequest<GeneratedDoc, GeneratedDoc>['body'];
}

View file

@ -9,7 +9,7 @@ import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../ftr_provider_context';
export function ExplainLogRateSpikesProvider({ getService }: FtrProviderContext) {
export function ExplainLogRateSpikesPageProvider({ getService }: FtrProviderContext) {
const browser = getService('browser');
const elasticChart = getService('elasticChart');
const testSubjects = getService('testSubjects');
@ -64,11 +64,15 @@ export function ExplainLogRateSpikesProvider({ getService }: FtrProviderContext)
await testSubjects.existOrFail(`aiopsNoResultsFoundEmptyPrompt`);
},
async clickDocumentCountChart() {
async clickDocumentCountChart(chartClickCoordinates: [number, number]) {
await elasticChart.waitForRenderComplete();
const el = await elasticChart.getCanvas();
await browser.getActions().move({ x: 0, y: 0, origin: el._webElement }).click().perform();
await browser
.getActions()
.move({ x: chartClickCoordinates[0], y: chartClickCoordinates[1], origin: el._webElement })
.click()
.perform();
await this.assertHistogramBrushesExist();
},
@ -154,7 +158,14 @@ export function ExplainLogRateSpikesProvider({ getService }: FtrProviderContext)
// Get the total count of bars and index of a bar for a given timestamp in the charts debug data.
const bars = chartDebugData?.bars?.[0].bars ?? [];
const barsCount = bars.length;
const targetDeviationBarIndex = bars.findIndex((b) => b.x === timestamp);
const closestTimestamp = bars
.map((d) => d.x)
.reduce(function (p, c) {
return Math.abs(c - timestamp) < Math.abs(p - timestamp) ? c : p;
});
const targetDeviationBarIndex = bars.findIndex((b) => b.x === closestTimestamp);
// The pixel location based on the given timestamp, calculated by taking the share of the index value
// over the total count of bars, normalized by the wrapping element's width.

View file

@ -7,18 +7,18 @@
import type { FtrProviderContext } from '../../ftr_provider_context';
import { ExplainLogRateSpikesProvider } from './explain_log_rate_spikes';
import { ExplainLogRateSpikesPageProvider } from './explain_log_rate_spikes_page';
import { ExplainLogRateSpikesAnalysisTableProvider } from './explain_log_rate_spikes_analysis_table';
import { ExplainLogRateSpikesAnalysisGroupsTableProvider } from './explain_log_rate_spikes_analysis_groups_table';
export function AiopsProvider(context: FtrProviderContext) {
const explainLogRateSpikes = ExplainLogRateSpikesProvider(context);
const explainLogRateSpikesPage = ExplainLogRateSpikesPageProvider(context);
const explainLogRateSpikesAnalysisTable = ExplainLogRateSpikesAnalysisTableProvider(context);
const explainLogRateSpikesAnalysisGroupsTable =
ExplainLogRateSpikesAnalysisGroupsTableProvider(context);
return {
explainLogRateSpikes,
explainLogRateSpikesPage,
explainLogRateSpikesAnalysisTable,
explainLogRateSpikesAnalysisGroupsTable,
};