mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[ML] Explain Log Rate Spikes: Extended Functional tests. (#138661)
Adds functional tests that use the histogram brushes and run an analysis.
This commit is contained in:
parent
2fab2041c4
commit
148b48f905
15 changed files with 294 additions and 18 deletions
|
@ -234,6 +234,10 @@ export function DualBrush({
|
|||
.attr('id', (b: DualBrush) => {
|
||||
return 'aiops-brush-' + b.id;
|
||||
})
|
||||
.attr('data-test-subj', (b: DualBrush) => {
|
||||
// Uppercase the first character of the `id` so we get aiopsBrushBaseline/aiopsBrushDeviation.
|
||||
return 'aiopsBrush' + b.id.charAt(0).toUpperCase() + b.id.slice(1);
|
||||
})
|
||||
.each((brushObject: DualBrush, i, n) => {
|
||||
const x = d3.scaleLinear().domain([min, max]).rangeRound([0, widthRef.current]);
|
||||
brushObject.brush(d3.select(n[i]));
|
||||
|
@ -314,6 +318,7 @@ export function DualBrush({
|
|||
{width > 0 && (
|
||||
<svg
|
||||
className="aiops-dual-brush"
|
||||
data-test-subj="aiopsDualBrush"
|
||||
width={width}
|
||||
height={BRUSH_HEIGHT}
|
||||
style={{ marginLeft }}
|
||||
|
|
|
@ -66,6 +66,7 @@ export function ProgressControls({
|
|||
<EuiFlexItem grow={false}>
|
||||
{!isRunning && (
|
||||
<EuiButton
|
||||
data-test-subj={`aiopsRerunAnalysisButton${shouldRerunAnalysis ? ' shouldRerun' : ''}`}
|
||||
size="s"
|
||||
onClick={onRefresh}
|
||||
color={shouldRerunAnalysis ? 'warning' : 'primary'}
|
||||
|
@ -96,8 +97,8 @@ export function ProgressControls({
|
|||
</EuiButton>
|
||||
)}
|
||||
{isRunning && (
|
||||
<EuiButton size="s" onClick={onCancel}>
|
||||
<FormattedMessage id="xpack.aiops.cancelButtonTitle" defaultMessage="Cancel" />
|
||||
<EuiButton data-test-subj="aiopsCancelAnalysisButton" size="s" onClick={onCancel}>
|
||||
<FormattedMessage id="xpack.aiops.cancelAnalysisButtonTitle" defaultMessage="Cancel" />
|
||||
</EuiButton>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -33,6 +33,15 @@ import { useAiOpsKibana } from '../../../kibana_context';
|
|||
|
||||
import { BrushBadge } from './brush_badge';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
/**
|
||||
* Flag used to enable debugState on elastic charts
|
||||
*/
|
||||
_echDebugStateFlag?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export interface DocumentCountChartPoint {
|
||||
time: number | string;
|
||||
value: number;
|
||||
|
@ -271,7 +280,7 @@ export const DocumentCountChart: FC<DocumentCountChartProps> = ({
|
|||
return (
|
||||
<>
|
||||
{isBrushVisible && (
|
||||
<div className="aiopsHistogramBrushes">
|
||||
<div className="aiopsHistogramBrushes" data-test-subj="aiopsHistogramBrushes">
|
||||
<div css={{ height: BADGE_HEIGHT }}>
|
||||
<BrushBadge
|
||||
label={i18n.translate('xpack.aiops.documentCountChart.baselineBadgeLabel', {
|
||||
|
@ -326,6 +335,7 @@ export const DocumentCountChart: FC<DocumentCountChartProps> = ({
|
|||
}}
|
||||
theme={chartTheme}
|
||||
baseTheme={chartBaseTheme}
|
||||
debugState={window._echDebugStateFlag ?? false}
|
||||
/>
|
||||
<Axis
|
||||
id="bottom"
|
||||
|
|
|
@ -93,7 +93,7 @@ export const DocumentCountContent: FC<DocumentCountContentProps> = ({
|
|||
<EuiButtonEmpty
|
||||
onClick={clearSelection}
|
||||
size="xs"
|
||||
data-test-sub="aiopsClearSelectionBadge"
|
||||
data-test-subj="aiopsClearSelectionBadge"
|
||||
>
|
||||
{clearSelectionLabel}
|
||||
</EuiButtonEmpty>
|
||||
|
|
|
@ -112,7 +112,7 @@ export const ExplainLogRateSpikesAnalysis: FC<ExplainLogRateSpikesAnalysisProps>
|
|||
const showSpikeAnalysisTable = data?.changePoints.length > 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div data-test-subj="aiopsExplainLogRateSpikesAnalysis">
|
||||
<ProgressControls
|
||||
progress={data.loaded}
|
||||
progressMessage={data.loadingState ?? ''}
|
||||
|
@ -124,6 +124,7 @@ export const ExplainLogRateSpikesAnalysis: FC<ExplainLogRateSpikesAnalysisProps>
|
|||
<EuiSpacer size="xs" />
|
||||
{!isRunning && !showSpikeAnalysisTable && (
|
||||
<EuiEmptyPrompt
|
||||
data-test-subj="aiopsNoResultsFoundEmptyPrompt"
|
||||
title={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
|
@ -179,6 +180,6 @@ export const ExplainLogRateSpikesAnalysis: FC<ExplainLogRateSpikesAnalysisProps>
|
|||
selectedChangePoint={selectedChangePoint}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -56,6 +56,7 @@ export const SpikeAnalysisTable: FC<SpikeAnalysisTableProps> = ({
|
|||
|
||||
const columns: Array<EuiBasicTableColumn<ChangePoint>> = [
|
||||
{
|
||||
'data-test-subj': 'aiopsSpikeAnalysisTableColumnFieldName',
|
||||
field: 'fieldName',
|
||||
name: i18n.translate(
|
||||
'xpack.aiops.correlations.failedTransactions.correlationsTable.fieldNameLabel',
|
||||
|
@ -64,6 +65,7 @@ export const SpikeAnalysisTable: FC<SpikeAnalysisTableProps> = ({
|
|||
sortable: true,
|
||||
},
|
||||
{
|
||||
'data-test-subj': 'aiopsSpikeAnalysisTableColumnFieldValue',
|
||||
field: 'fieldValue',
|
||||
name: i18n.translate(
|
||||
'xpack.aiops.correlations.failedTransactions.correlationsTable.fieldValueLabel',
|
||||
|
@ -73,6 +75,7 @@ export const SpikeAnalysisTable: FC<SpikeAnalysisTableProps> = ({
|
|||
sortable: true,
|
||||
},
|
||||
{
|
||||
'data-test-subj': 'aiopsSpikeAnalysisTableColumnLogRate',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
|
@ -105,6 +108,7 @@ export const SpikeAnalysisTable: FC<SpikeAnalysisTableProps> = ({
|
|||
sortable: false,
|
||||
},
|
||||
{
|
||||
'data-test-subj': 'aiopsSpikeAnalysisTableColumnPValue',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
|
@ -131,6 +135,7 @@ export const SpikeAnalysisTable: FC<SpikeAnalysisTableProps> = ({
|
|||
sortable: true,
|
||||
},
|
||||
{
|
||||
'data-test-subj': 'aiopsSpikeAnalysisTableColumnImpact',
|
||||
width: NARROW_COLUMN_WIDTH,
|
||||
field: 'pValue',
|
||||
name: (
|
||||
|
@ -210,6 +215,7 @@ export const SpikeAnalysisTable: FC<SpikeAnalysisTableProps> = ({
|
|||
|
||||
return (
|
||||
<EuiBasicTable
|
||||
data-test-subj="aiopsSpikeAnalysisTable"
|
||||
compressed
|
||||
columns={columns}
|
||||
items={pageOfItems}
|
||||
|
@ -219,6 +225,7 @@ export const SpikeAnalysisTable: FC<SpikeAnalysisTableProps> = ({
|
|||
sorting={sorting as EuiTableSortingType<ChangePoint>}
|
||||
rowProps={(changePoint) => {
|
||||
return {
|
||||
'data-test-subj': `aiopsSpikeAnalysisTableRow row-${changePoint.fieldName}-${changePoint.fieldValue}`,
|
||||
onClick: () => {
|
||||
if (onPinnedChangePoint) {
|
||||
onPinnedChangePoint(changePoint);
|
||||
|
|
|
@ -6523,7 +6523,6 @@
|
|||
"xpack.aiops.progressTitle": "Progression : {progress} % — {progressMessage}",
|
||||
"xpack.aiops.searchPanel.totalDocCountLabel": "Total des documents : {strongTotalCount}",
|
||||
"xpack.aiops.searchPanel.totalDocCountNumber": "{totalCount, plural, other {#}}",
|
||||
"xpack.aiops.cancelButtonTitle": "Annuler",
|
||||
"xpack.aiops.correlations.failedTransactions.correlationsTable.fieldNameLabel": "Nom du champ",
|
||||
"xpack.aiops.correlations.failedTransactions.correlationsTable.fieldValueLabel": "Valeur du champ",
|
||||
"xpack.aiops.correlations.failedTransactions.correlationsTable.impactLabel": "Impact",
|
||||
|
|
|
@ -6519,7 +6519,6 @@
|
|||
"xpack.aiops.progressTitle": "進行状況:{progress}% — {progressMessage}",
|
||||
"xpack.aiops.searchPanel.totalDocCountLabel": "合計ドキュメント数:{strongTotalCount}",
|
||||
"xpack.aiops.searchPanel.totalDocCountNumber": "{totalCount, plural, other {#}}",
|
||||
"xpack.aiops.cancelButtonTitle": "キャンセル",
|
||||
"xpack.aiops.correlations.failedTransactions.correlationsTable.fieldNameLabel": "フィールド名",
|
||||
"xpack.aiops.correlations.failedTransactions.correlationsTable.fieldValueLabel": "フィールド値",
|
||||
"xpack.aiops.correlations.failedTransactions.correlationsTable.impactLabel": "インパクト",
|
||||
|
|
|
@ -6526,7 +6526,6 @@
|
|||
"xpack.aiops.progressTitle": "进度:{progress}% — {progressMessage}",
|
||||
"xpack.aiops.searchPanel.totalDocCountLabel": "文档总数:{strongTotalCount}",
|
||||
"xpack.aiops.searchPanel.totalDocCountNumber": "{totalCount, plural, other {#}}",
|
||||
"xpack.aiops.cancelButtonTitle": "取消",
|
||||
"xpack.aiops.correlations.failedTransactions.correlationsTable.fieldNameLabel": "字段名称",
|
||||
"xpack.aiops.correlations.failedTransactions.correlationsTable.fieldValueLabel": "字段值",
|
||||
"xpack.aiops.correlations.failedTransactions.correlationsTable.impactLabel": "影响",
|
||||
|
|
|
@ -5,12 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
import type { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import type { TestData } from './types';
|
||||
import { farequoteDataViewTestData } from './test_data';
|
||||
|
||||
export default function ({ getPageObject, getService }: FtrProviderContext) {
|
||||
const headerPage = getPageObject('header');
|
||||
const elasticChart = getService('elasticChart');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const aiops = getService('aiops');
|
||||
|
||||
|
@ -19,6 +22,8 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
|
|||
|
||||
function runTests(testData: TestData) {
|
||||
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`
|
||||
);
|
||||
|
@ -45,17 +50,75 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
|
|||
await ml.testExecution.logTestStep(
|
||||
`${testData.suiteTitle} displays elements in the doc count panel correctly`
|
||||
);
|
||||
await aiops.explainLogRateSpikes.assertTotalDocCountHeaderExist();
|
||||
await aiops.explainLogRateSpikes.assertTotalDocCountChartExist();
|
||||
await aiops.explainLogRateSpikes.assertTotalDocCountHeaderExists();
|
||||
await aiops.explainLogRateSpikes.assertTotalDocCountChartExists();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
`${testData.suiteTitle} displays elements in the page correctly`
|
||||
);
|
||||
|
||||
await aiops.explainLogRateSpikes.assertSearchPanelExist();
|
||||
await aiops.explainLogRateSpikes.assertSearchPanelExists();
|
||||
|
||||
await ml.testExecution.logTestStep('displays empty prompt');
|
||||
await aiops.explainLogRateSpikes.assertNoWindowParametersEmptyPromptExist();
|
||||
await aiops.explainLogRateSpikes.assertNoWindowParametersEmptyPromptExists();
|
||||
|
||||
await ml.testExecution.logTestStep('clicks the document count chart to start analysis');
|
||||
await aiops.explainLogRateSpikes.clickDocumentCountChart();
|
||||
await aiops.explainLogRateSpikes.assertAnalysisSectionExists();
|
||||
|
||||
await ml.testExecution.logTestStep('displays the no results found prompt');
|
||||
await aiops.explainLogRateSpikes.assertNoResultsFoundEmptyPromptExists();
|
||||
|
||||
await ml.testExecution.logTestStep('adjusts the brushes to get analysis results');
|
||||
await aiops.explainLogRateSpikes.assertRerunAnalysisButtonExists(false);
|
||||
|
||||
// Get the current width of the deviation brush for later comparison.
|
||||
const brushSelectionWidthBefore = await aiops.explainLogRateSpikes.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
|
||||
);
|
||||
|
||||
// Adjust the right brush handle
|
||||
await aiops.explainLogRateSpikes.adjustBrushHandler(
|
||||
'aiopsBrushDeviation',
|
||||
'handle--e',
|
||||
targetPx
|
||||
);
|
||||
|
||||
// Adjust the left brush handle
|
||||
await aiops.explainLogRateSpikes.adjustBrushHandler(
|
||||
'aiopsBrushDeviation',
|
||||
'handle--w',
|
||||
targetPx - intervalPx
|
||||
);
|
||||
|
||||
// Get the new brush selection width for later comparison.
|
||||
const brushSelectionWidthAfter = await aiops.explainLogRateSpikes.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 * 2);
|
||||
|
||||
await aiops.explainLogRateSpikes.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.explainLogRateSpikesAnalysisTable.assertSpikeAnalysisTableExists();
|
||||
|
||||
const analysisTable = await aiops.explainLogRateSpikesAnalysisTable.parseAnalysisTable();
|
||||
|
||||
expect(analysisTable).to.be.eql(testData.expected.analysisTable);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -71,6 +134,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
after(async () => {
|
||||
await elasticChart.setNewChartUiDebugFlag(false);
|
||||
await ml.testResources.deleteIndexPatternByTitle('ft_farequote');
|
||||
});
|
||||
|
||||
|
@ -79,6 +143,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
|
|||
it(`${farequoteDataViewTestData.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(farequoteDataViewTestData);
|
||||
|
|
|
@ -11,7 +11,17 @@ export const farequoteDataViewTestData: TestData = {
|
|||
suiteTitle: 'farequote index pattern',
|
||||
isSavedSearch: false,
|
||||
sourceIndexOrSavedSearch: 'ft_farequote',
|
||||
brushTargetTimestamp: 1455033600000,
|
||||
expected: {
|
||||
totalDocCountFormatted: '86,274',
|
||||
analysisTable: [
|
||||
{
|
||||
fieldName: 'airline',
|
||||
fieldValue: 'AAL',
|
||||
logRate: 'Chart type:bar chart',
|
||||
pValue: '4.63e-14',
|
||||
impact: 'High',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -10,7 +10,15 @@ export interface TestData {
|
|||
isSavedSearch?: boolean;
|
||||
sourceIndexOrSavedSearch: string;
|
||||
rowsPerPage?: 10 | 25 | 50;
|
||||
brushTargetTimestamp: number;
|
||||
expected: {
|
||||
totalDocCountFormatted: string;
|
||||
analysisTable: Array<{
|
||||
fieldName: string;
|
||||
fieldValue: string;
|
||||
logRate: string;
|
||||
pValue: string;
|
||||
impact: string;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import expect from '@kbn/expect';
|
|||
import type { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function ExplainLogRateSpikesProvider({ getService }: FtrProviderContext) {
|
||||
const browser = getService('browser');
|
||||
const elasticChart = getService('elasticChart');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
|
||||
|
@ -36,29 +38,130 @@ export function ExplainLogRateSpikesProvider({ getService }: FtrProviderContext)
|
|||
});
|
||||
},
|
||||
|
||||
async assertTotalDocCountHeaderExist() {
|
||||
async assertTotalDocCountHeaderExists() {
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await testSubjects.existOrFail(`aiopsTotalDocCountHeader`);
|
||||
});
|
||||
},
|
||||
|
||||
async assertTotalDocCountChartExist() {
|
||||
async assertTotalDocCountChartExists() {
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await testSubjects.existOrFail(`aiopsDocumentCountChart`);
|
||||
});
|
||||
},
|
||||
|
||||
async assertSearchPanelExist() {
|
||||
async assertSearchPanelExists() {
|
||||
await testSubjects.existOrFail(`aiopsSearchPanel`);
|
||||
},
|
||||
|
||||
async assertNoWindowParametersEmptyPromptExist() {
|
||||
async assertNoWindowParametersEmptyPromptExists() {
|
||||
await testSubjects.existOrFail(`aiopsNoWindowParametersEmptyPrompt`);
|
||||
},
|
||||
|
||||
async assertNoResultsFoundEmptyPromptExists() {
|
||||
await testSubjects.existOrFail(`aiopsNoResultsFoundEmptyPrompt`);
|
||||
},
|
||||
|
||||
async clickDocumentCountChart() {
|
||||
await elasticChart.waitForRenderComplete();
|
||||
const el = await elasticChart.getCanvas();
|
||||
|
||||
await browser.getActions().move({ x: 0, y: 0, origin: el._webElement }).click().perform();
|
||||
|
||||
await this.assertHistogramBrushesExist();
|
||||
},
|
||||
|
||||
async clickRerunAnalysisButton(shouldRerun: boolean) {
|
||||
await testSubjects.clickWhenNotDisabled(
|
||||
`aiopsRerunAnalysisButton${shouldRerun ? ' shouldRerun' : ''}`
|
||||
);
|
||||
|
||||
await retry.tryForTime(30 * 1000, async () => {
|
||||
await testSubjects.existOrFail(
|
||||
`aiopsRerunAnalysisButton${!shouldRerun ? ' shouldRerun' : ''}`
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
async assertHistogramBrushesExist() {
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await testSubjects.existOrFail(`aiopsHistogramBrushes`);
|
||||
// As part of the interface for the histogram brushes, the button to clear the selection should be present
|
||||
await testSubjects.existOrFail(`aiopsClearSelectionBadge`);
|
||||
});
|
||||
},
|
||||
|
||||
async assertAnalysisSectionExists() {
|
||||
await retry.tryForTime(5000, async () => {
|
||||
await testSubjects.existOrFail(`aiopsExplainLogRateSpikesAnalysis`);
|
||||
});
|
||||
},
|
||||
|
||||
async assertRerunAnalysisButtonExists(shouldRerun: boolean) {
|
||||
await testSubjects.existOrFail(
|
||||
`aiopsRerunAnalysisButton${shouldRerun ? ' shouldRerun' : ''}`
|
||||
);
|
||||
},
|
||||
|
||||
async assertProgressTitle(expectedProgressTitle: string) {
|
||||
await testSubjects.existOrFail('aiopProgressTitle');
|
||||
const currentProgressTitle = await testSubjects.getVisibleText('aiopProgressTitle');
|
||||
expect(currentProgressTitle).to.be(expectedProgressTitle);
|
||||
},
|
||||
|
||||
async navigateToIndexPatternSelection() {
|
||||
await testSubjects.click('mlMainTab explainLogRateSpikes');
|
||||
await testSubjects.existOrFail('mlPageSourceSelection');
|
||||
},
|
||||
|
||||
async getBrushSelectionWidth(selector: string) {
|
||||
const brush = await testSubjects.find(selector);
|
||||
const brushSelection = (await brush.findAllByClassName('selection'))[0];
|
||||
const brushSelectionRect = await brushSelection._webElement.getRect();
|
||||
return brushSelectionRect.width;
|
||||
},
|
||||
|
||||
async getPxForTimestamp(timestamp: number) {
|
||||
await elasticChart.waitForRenderComplete('aiopsDocumentCountChart');
|
||||
const chartDebugData = await elasticChart.getChartDebugData('aiopsDocumentCountChart');
|
||||
|
||||
// Select the wrapper element to access its 'width' later one for the calculations.
|
||||
const dualBrushWrapper = await testSubjects.find('aiopsDualBrush');
|
||||
const dualBrushWrapperRect = await dualBrushWrapper._webElement.getRect();
|
||||
|
||||
// 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);
|
||||
|
||||
// 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.
|
||||
const targetPx = Math.round(
|
||||
(targetDeviationBarIndex / barsCount) * dualBrushWrapperRect.width
|
||||
);
|
||||
|
||||
// The pixel width of the interval of an individual bar of the histogram.
|
||||
// Can be used as a helper to calculate the offset from the target pixel location
|
||||
// to the next histogram bar.
|
||||
const intervalPx = Math.round((1 / barsCount) * dualBrushWrapperRect.width);
|
||||
|
||||
return { targetPx, intervalPx };
|
||||
},
|
||||
|
||||
async adjustBrushHandler(selector: string, handlerClassName: string, targetPx: number) {
|
||||
const brush = await testSubjects.find(selector);
|
||||
const dualBrushWrapper = await testSubjects.find('aiopsDualBrush');
|
||||
const dualBrushWrapperRect = await dualBrushWrapper._webElement.getRect();
|
||||
|
||||
const handle = (await brush.findAllByClassName(handlerClassName))[0];
|
||||
const handleRect = await handle._webElement.getRect();
|
||||
const handlePx = handleRect.x - dualBrushWrapperRect.x;
|
||||
const dragAndDropOffsetPx = targetPx - handlePx;
|
||||
|
||||
await browser.dragAndDrop(
|
||||
{ location: handle, offset: { x: 0, y: 0 } },
|
||||
{ location: handle, offset: { x: dragAndDropOffsetPx, y: 0 } }
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function ExplainLogRateSpikesAnalysisTableProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return new (class AnalysisTable {
|
||||
public async assertSpikeAnalysisTableExists() {
|
||||
await testSubjects.existOrFail(`aiopsSpikeAnalysisTable`);
|
||||
}
|
||||
|
||||
public async parseAnalysisTable() {
|
||||
const table = await testSubjects.find('~aiopsSpikeAnalysisTable');
|
||||
const $ = await table.parseDomContent();
|
||||
const rows = [];
|
||||
|
||||
for (const tr of $.findTestSubjects('~aiopsSpikeAnalysisTableRow').toArray()) {
|
||||
const $tr = $(tr);
|
||||
|
||||
const rowObject: {
|
||||
fieldName: string;
|
||||
fieldValue: string;
|
||||
logRate: string;
|
||||
pValue: string;
|
||||
impact: string;
|
||||
} = {
|
||||
fieldName: $tr
|
||||
.findTestSubject('aiopsSpikeAnalysisTableColumnFieldName')
|
||||
.find('.euiTableCellContent')
|
||||
.text()
|
||||
.trim(),
|
||||
fieldValue: $tr
|
||||
.findTestSubject('aiopsSpikeAnalysisTableColumnFieldValue')
|
||||
.find('.euiTableCellContent')
|
||||
.text()
|
||||
.trim(),
|
||||
logRate: $tr
|
||||
.findTestSubject('aiopsSpikeAnalysisTableColumnLogRate')
|
||||
.find('.euiTableCellContent')
|
||||
.text()
|
||||
.trim(),
|
||||
pValue: $tr
|
||||
.findTestSubject('aiopsSpikeAnalysisTableColumnPValue')
|
||||
.find('.euiTableCellContent')
|
||||
.text()
|
||||
.trim(),
|
||||
impact: $tr
|
||||
.findTestSubject('aiopsSpikeAnalysisTableColumnImpact')
|
||||
.find('.euiTableCellContent')
|
||||
.text()
|
||||
.trim(),
|
||||
};
|
||||
|
||||
rows.push(rowObject);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
})();
|
||||
}
|
|
@ -8,11 +8,14 @@
|
|||
import type { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
import { ExplainLogRateSpikesProvider } from './explain_log_rate_spikes';
|
||||
import { ExplainLogRateSpikesAnalysisTableProvider } from './explain_log_rate_spikes_analysis_table';
|
||||
|
||||
export function AiopsProvider(context: FtrProviderContext) {
|
||||
const explainLogRateSpikes = ExplainLogRateSpikesProvider(context);
|
||||
const explainLogRateSpikesAnalysisTable = ExplainLogRateSpikesAnalysisTableProvider(context);
|
||||
|
||||
return {
|
||||
explainLogRateSpikes,
|
||||
explainLogRateSpikesAnalysisTable,
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue