[8.16] [ML] Memory usage: Functional tests (#197415) (#197808)

# Backport

This will backport the following commits from `main` to `8.16`:
- [[ML] Memory usage: Functional tests
(#197415)](https://github.com/elastic/kibana/pull/197415)

<!--- Backport version: 9.4.3 -->

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

<!--BACKPORT [{"author":{"name":"Robert
Jaszczurek","email":"92210485+rbrtj@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-25T11:39:38Z","message":"[ML]
Memory usage: Functional tests (#197415)\n\n## Summary\r\n\r\nFunctional
tests for `memory usage` page in
ML.","sha":"479280c0b74747056634421c9b9e05f94a3c7e07","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["non-issue",":ml","release_note:skip","v9.0.0","Team:ML","v8.16.0","backport:version","v8.17.0"],"title":"[ML]
Memory usage: Functional
tests","number":197415,"url":"https://github.com/elastic/kibana/pull/197415","mergeCommit":{"message":"[ML]
Memory usage: Functional tests (#197415)\n\n## Summary\r\n\r\nFunctional
tests for `memory usage` page in
ML.","sha":"479280c0b74747056634421c9b9e05f94a3c7e07"}},"sourceBranch":"main","suggestedTargetBranches":["8.16","8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/197415","number":197415,"mergeCommit":{"message":"[ML]
Memory usage: Functional tests (#197415)\n\n## Summary\r\n\r\nFunctional
tests for `memory usage` page in
ML.","sha":"479280c0b74747056634421c9b9e05f94a3c7e07"}},{"branch":"8.16","label":"v8.16.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.x","label":"v8.17.0","branchLabelMappingKey":"^v8.17.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Robert Jaszczurek <92210485+rbrtj@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2024-10-26 00:23:13 +11:00 committed by GitHub
parent b8d7ad76f1
commit af2cd0c47d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 251 additions and 5 deletions

View file

@ -249,6 +249,7 @@ enabled:
- x-pack/test/functional/apps/ml/permissions/config.ts
- x-pack/test/functional/apps/ml/short_tests/config.ts
- x-pack/test/functional/apps/ml/stack_management_jobs/config.ts
- x-pack/test/functional/apps/ml/memory_usage/config.ts
- x-pack/test/functional/apps/monitoring/config.ts
- x-pack/test/functional/apps/painless_lab/config.ts
- x-pack/test/functional/apps/remote_clusters/config.ts

View file

@ -161,7 +161,7 @@ export const JobMemoryTreeMap: FC<Props> = ({ node, type, height }) => {
options={typeOptions}
selectedOptions={selectedOptions ?? []}
onChange={setSelectedOptions}
isClearable={false}
data-test-subj="mlJobTreeMapComboBox"
/>
<EuiSpacer size="s" />

View file

@ -50,16 +50,18 @@ export const MemoryUsagePage: FC = () => {
{showNodeInfo ? (
<>
<EuiTabs>
<EuiTabs data-test-subj="mlMemoryUsageTabs">
<EuiTab
isSelected={selectedTab === TAB.NODES}
onClick={() => setSelectedTab(TAB.NODES)}
data-test-subj="mlMemoryUsageTab-nodes"
>
<FormattedMessage id="xpack.ml.memoryUsage.nodesTab" defaultMessage="Nodes" />
</EuiTab>
<EuiTab
isSelected={selectedTab === TAB.MEMORY_USAGE}
onClick={() => setSelectedTab(TAB.MEMORY_USAGE)}
data-test-subj="mlMemoryUsageTab-memory-usage"
>
<FormattedMessage id="xpack.ml.memoryUsage.memoryTab" defaultMessage="Memory usage" />
</EuiTab>

View file

@ -64,6 +64,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
<EuiTab
isSelected={selectedTab === TAB.DETAILS}
onClick={() => setSelectedTab(TAB.DETAILS)}
data-test-subj="mlNodesOverviewPanelDetailsTab"
>
<FormattedMessage
id="xpack.ml.trainedModels.nodesList.expandedRow.detailsTabTitle"
@ -73,6 +74,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
<EuiTab
isSelected={selectedTab === TAB.MEMORY_USAGE}
onClick={() => setSelectedTab(TAB.MEMORY_USAGE)}
data-test-subj="mlNodesOverviewPanelMemoryTab"
>
<FormattedMessage
id="xpack.ml.trainedModels.nodesList.expandedRow.memoryTabTitle"
@ -85,7 +87,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
<>
<EuiSpacer size="s" />
<EuiFlexGrid columns={2} gutterSize={'s'}>
<EuiFlexItem>
<EuiFlexItem data-test-subj="mlNodesTableRowDetailsPanel">
<EuiPanel hasShadow={false}>
<EuiTitle size={'xs'}>
<h5>
@ -104,7 +106,7 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => {
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexItem data-test-subj="mlNodesTableRowDetailsAttributesPanel">
<EuiPanel hasShadow={false}>
<EuiTitle size={'xs'}>
<h5>

View file

@ -185,6 +185,7 @@ export const NodesList: FC<NodesListProps> = ({ compactView = false }) => {
},
box: {
incremental: true,
'data-test-subj': 'mlNodesTableSearchInput',
},
};

View file

@ -39,6 +39,7 @@ export const nodesListRouteFactory = (
},
],
enableDatePicker: true,
'data-test-subj': 'mlPageMemoryUsage',
});
const PageWrapper: FC = () => {

View file

@ -0,0 +1,20 @@
/*
* 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 { FtrConfigProviderContext } from '@kbn/test';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js'));
return {
...functionalConfig.getAll(),
testFiles: [require.resolve('.')],
junit: {
reportName: 'Chrome X-Pack UI Functional Tests - ML memory_usage',
},
};
}

View file

@ -0,0 +1,33 @@
/*
* 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 default function ({ getService, loadTestFile }: FtrProviderContext) {
const ml = getService('ml');
describe('machine learning - overview page', function () {
this.tags(['skipFirefox']);
before(async () => {
await ml.securityCommon.createMlRoles();
await ml.securityCommon.createMlUsers();
await ml.securityUI.loginAsMlPowerUser();
});
after(async () => {
await ml.securityUI.logout();
await ml.securityCommon.cleanMlUsers();
await ml.securityCommon.cleanMlRoles();
await ml.testResources.resetKibanaTimeZone();
});
loadTestFile(require.resolve('./memory_usage_page'));
});
}

View file

@ -0,0 +1,72 @@
/*
* 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 default function ({ getService }: FtrProviderContext) {
const ml = getService('ml');
const esArchiver = getService('esArchiver');
const jobId = 'sample_job';
describe('ML memory usage page', function () {
this.tags(['ml']);
before(async () => {
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote');
const jobConfig = ml.commonConfig.getADFqSingleMetricJobConfig(jobId);
// Create and open AD job
await ml.api.createAnomalyDetectionJob(jobConfig);
await ml.api.openAnomalyDetectionJob(jobId);
await ml.navigation.navigateToMl();
await ml.navigation.navigateToMemoryUsage();
});
after(async () => {
await ml.api.closeAnomalyDetectionJob(jobId);
await ml.api.cleanMlIndices();
});
it('opens page with nodes tab selected', async () => {
await ml.memoryUsage.assertMemoryUsageTabIsSelected('nodes');
});
it('allows sorting', async () => {
await ml.memoryUsage.sortColumn('tableHeaderCell_name_1');
await ml.memoryUsage.assertColumnIsSorted('tableHeaderCell_name_1', 'descending');
});
it('allows searching for a node', async () => {
await ml.memoryUsage.searchForNode('ftr');
await ml.memoryUsage.assertRowCount(1);
});
it('expands node details and displays memory usage details', async () => {
await ml.memoryUsage.expandRow();
await ml.memoryUsage.assertNodeExpandedDetailsPanelsExist();
await ml.memoryUsage.selectNodeExpandedRowTab('mlNodesOverviewPanelMemoryTab');
await ml.memoryUsage.assertChartItemsSelectedByDefault();
await ml.memoryUsage.assertTreeChartExists();
});
it('clears selected chart items', async () => {
await ml.memoryUsage.clearSelectedChartItems();
await ml.memoryUsage.assertEmptyTreeChartExists();
});
it('selects memory usage tab and displays chart', async () => {
await ml.memoryUsage.selectTab('memory-usage');
await ml.memoryUsage.assertTreeChartExists();
await ml.memoryUsage.clearSelectedChartItems();
await ml.memoryUsage.assertEmptyTreeChartExists();
});
});
}

View file

@ -69,6 +69,8 @@ import { MlTableServiceProvider } from './common_table_service';
import { MachineLearningFieldStatsFlyoutProvider } from './field_stats_flyout';
import { MachineLearningDataDriftProvider } from './data_drift';
import { TrainedModelsFlyoutProvider } from './add_trained_models_flyout';
import { MachineLearningMemoryUsageProvider } from './memory_usage';
export function MachineLearningProvider(context: FtrProviderContext) {
const commonAPI = MachineLearningCommonAPIProvider(context);
const commonUI = MachineLearningCommonUIProvider(context);
@ -178,7 +180,7 @@ export function MachineLearningProvider(context: FtrProviderContext) {
const deployDFAModelFlyout = DeployDFAModelFlyoutProvider(context, commonUI);
const mlNodesPanel = MlNodesPanelProvider(context);
const notifications = NotificationsProvider(context, commonUI, tableService);
const memoryUsage = MachineLearningMemoryUsageProvider(context);
const cases = MachineLearningCasesProvider(context, swimLane, anomalyCharts);
return {
@ -244,5 +246,6 @@ export function MachineLearningProvider(context: FtrProviderContext) {
trainedModelsFlyout,
deployDFAModelFlyout,
trainedModelsTable,
memoryUsage,
};
}

View file

@ -0,0 +1,107 @@
/*
* 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 { FtrProviderContext } from '../../ftr_provider_context';
export function MachineLearningMemoryUsageProvider({ getService }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const comboBox = getService('comboBox');
return {
async assertNodeExpandedDetailsPanelsExist() {
await testSubjects.existOrFail('mlNodesTableRowDetailsPanel');
await testSubjects.existOrFail('mlNodesTableRowDetailsAttributesPanel');
},
async assertTabIsSelected(tabName: string) {
await testSubjects.existOrFail(`mlNodesOverviewPanel ${tabName}Tab`);
},
async selectTab(tabName: string) {
await testSubjects.click(`mlMemoryUsageTab-${tabName}`);
},
async assertMemoryUsageTabsExist() {
await testSubjects.existOrFail('mlMemoryUsageTabs');
},
async assertMemoryUsageTabIsSelected(tabName: string) {
const isSelected = await testSubjects.getAttribute(
`mlMemoryUsageTab-${tabName}`,
'aria-selected'
);
expect(isSelected).to.eql('true');
},
async assertRowCount(expectedCount: number) {
const rowCount = await this.getRowCount();
expect(rowCount).to.eql(expectedCount);
},
async getAllRows() {
return await testSubjects.findAll('~mlNodesTableRow');
},
async expandRow() {
await testSubjects.click('mlNodesTableRowDetailsToggle');
},
async getRowCount() {
const rows = await this.getAllRows();
return rows.length;
},
async assertColumnHeaderExists(columnName: string) {
await testSubjects.existOrFail(columnName);
},
async assertColumnIsSorted(columnName: string, sortDirection: 'ascending' | 'descending') {
const sorted = await testSubjects.getAttribute(columnName, 'aria-sort');
expect(sorted).to.eql(sortDirection);
},
async sortColumn(columnName: string) {
await this.assertColumnHeaderExists(columnName);
await testSubjects.click(columnName);
},
async assertSearchBarExists() {
await testSubjects.existOrFail('mlNodesTableSearchInput');
},
async searchForNode(nodeId: string) {
await this.assertSearchBarExists();
await testSubjects.setValue('mlNodesTableSearchInput', nodeId);
},
async selectNodeExpandedRowTab(tabName: string) {
await testSubjects.click(tabName);
},
async clearSelectedChartItems() {
await comboBox.clear('~mlJobTreeMap > mlJobTreeMapComboBox');
},
async getSelectedChartItems() {
return await comboBox.getComboBoxSelectedOptions('~mlJobTreeMap > comboBoxInput');
},
async assertChartItemsSelectedByDefault() {
const selectedOptions = await this.getSelectedChartItems();
expect(selectedOptions.length).to.be.greaterThan(0);
},
async assertTreeChartExists() {
await testSubjects.existOrFail('mlJobTreeMap withData');
},
async assertEmptyTreeChartExists() {
await testSubjects.existOrFail('mlJobTreeMap empty');
},
};
}

View file

@ -154,6 +154,10 @@ export function MachineLearningNavigationProvider({
await this.navigateToArea('~mlMainTab & ~notifications', 'mlPageNotifications');
},
async navigateToMemoryUsage() {
await this.navigateToArea('~mlMainTab & ~nodesOverview', 'mlPageMemoryUsage');
},
async navigateToAnomalyDetection() {
await this.navigateToArea('~mlMainTab & ~anomalyDetection', 'mlPageJobManagement');
},