diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx index 33bb78c51e013..0af6030df28b1 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx @@ -5,6 +5,7 @@ */ import React, { useEffect, useState, Fragment, FC } from 'react'; +import { Router } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { CoreStart } from 'kibana/public'; @@ -20,6 +21,8 @@ import { EuiTitle, } from '@elastic/eui'; +import { ManagementAppMountParams } from '../../../../../../../../../src/plugins/management/public/'; + import { checkGetManagementMlJobsResolver } from '../../../../capabilities/check_capabilities'; import { KibanaContextProvider } from '../../../../../../../../../src/plugins/kibana_react/public'; @@ -30,6 +33,7 @@ import { DataFrameAnalyticsList } from '../../../../data_frame_analytics/pages/a import { AccessDeniedPage } from '../access_denied_page'; interface Tab { + 'data-test-subj': string; id: string; name: string; content: any; @@ -38,6 +42,7 @@ interface Tab { function getTabs(isMlEnabledInSpace: boolean): Tab[] { return [ { + 'data-test-subj': 'mlStackManagementJobsListAnomalyDetectionTab', id: 'anomaly_detection_jobs', name: i18n.translate('xpack.ml.management.jobsList.anomalyDetectionTab', { defaultMessage: 'Anomaly detection', @@ -50,6 +55,7 @@ function getTabs(isMlEnabledInSpace: boolean): Tab[] { ), }, { + 'data-test-subj': 'mlStackManagementJobsListAnalyticsTab', id: 'analytics_jobs', name: i18n.translate('xpack.ml.management.jobsList.analyticsTab', { defaultMessage: 'Analytics', @@ -67,7 +73,10 @@ function getTabs(isMlEnabledInSpace: boolean): Tab[] { ]; } -export const JobsListPage: FC<{ coreStart: CoreStart }> = ({ coreStart }) => { +export const JobsListPage: FC<{ + coreStart: CoreStart; + history: ManagementAppMountParams['history']; +}> = ({ coreStart, history }) => { const [initialized, setInitialized] = useState(false); const [accessDenied, setAccessDenied] = useState(false); const [isMlEnabledInSpace, setIsMlEnabledInSpace] = useState(false); @@ -128,46 +137,51 @@ export const JobsListPage: FC<{ coreStart: CoreStart }> = ({ coreStart }) => { return ( - - - - -

- {i18n.translate('xpack.ml.management.jobsList.jobsListTitle', { - defaultMessage: 'Machine Learning Jobs', - })} -

-
- - - {currentTabId === 'anomaly_detection_jobs' - ? anomalyDetectionDocsLabel - : analyticsDocsLabel} - - -
-
- - - - {i18n.translate('xpack.ml.management.jobsList.jobsListTagline', { - defaultMessage: 'View machine learning analytics and anomaly detection jobs.', - })} - - - - {renderTabs()} -
+ + + + + +

+ {i18n.translate('xpack.ml.management.jobsList.jobsListTitle', { + defaultMessage: 'Machine Learning Jobs', + })} +

+
+ + + {currentTabId === 'anomaly_detection_jobs' + ? anomalyDetectionDocsLabel + : analyticsDocsLabel} + + +
+
+ + + + {i18n.translate('xpack.ml.management.jobsList.jobsListTagline', { + defaultMessage: 'View machine learning analytics and anomaly detection jobs.', + })} + + + + {renderTabs()} +
+
); diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/index.ts b/x-pack/plugins/ml/public/application/management/jobs_list/index.ts index 81190a412abc0..afea5a573b8b5 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/index.ts +++ b/x-pack/plugins/ml/public/application/management/jobs_list/index.ts @@ -14,8 +14,12 @@ import { getJobsListBreadcrumbs } from '../breadcrumbs'; import { setDependencyCache, clearCache } from '../../util/dependency_cache'; import './_index.scss'; -const renderApp = (element: HTMLElement, coreStart: CoreStart) => { - ReactDOM.render(React.createElement(JobsListPage, { coreStart }), element); +const renderApp = ( + element: HTMLElement, + history: ManagementAppMountParams['history'], + coreStart: CoreStart +) => { + ReactDOM.render(React.createElement(JobsListPage, { coreStart, history }), element); return () => { unmountComponentAtNode(element); clearCache(); @@ -37,5 +41,5 @@ export async function mountApp( params.setBreadcrumbs(getJobsListBreadcrumbs()); - return renderApp(params.element, coreStart); + return renderApp(params.element, params.history, coreStart); } diff --git a/x-pack/test/functional/apps/ml/pages.ts b/x-pack/test/functional/apps/ml/pages.ts index e2c80c8dab558..3691e6b1afcdc 100644 --- a/x-pack/test/functional/apps/ml/pages.ts +++ b/x-pack/test/functional/apps/ml/pages.ts @@ -53,5 +53,17 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataVisualizer.assertDataVisualizerImportDataCardExists(); await ml.dataVisualizer.assertDataVisualizerIndexDataCardExists(); }); + + it('should load the stack management with the ML menu item being present', async () => { + await ml.navigation.navigateToStackManagement(); + }); + + it('should load the jobs list page in stack management', async () => { + await ml.navigation.navigateToStackManagementJobsListPage(); + }); + + it('should load the analytics jobs list page in stack management', async () => { + await ml.navigation.navigateToStackManagementJobsListPageAnalyticsTab(); + }); }); } diff --git a/x-pack/test/functional/services/ml/navigation.ts b/x-pack/test/functional/services/ml/navigation.ts index 9b67a369f055f..f52197d4b2256 100644 --- a/x-pack/test/functional/services/ml/navigation.ts +++ b/x-pack/test/functional/services/ml/navigation.ts @@ -23,6 +23,13 @@ export function MachineLearningNavigationProvider({ }); }, + async navigateToStackManagement() { + await retry.tryForTime(60 * 1000, async () => { + await PageObjects.common.navigateToApp('management'); + await testSubjects.existOrFail('jobsListLink', { timeout: 2000 }); + }); + }, + async assertTabsExist(tabTypeSubject: string, areaSubjects: string[]) { await retry.tryForTime(10000, async () => { const allTabs = await testSubjects.findAll(`~${tabTypeSubject}`, 3); @@ -76,5 +83,25 @@ export function MachineLearningNavigationProvider({ async navigateToSettings() { await this.navigateToArea('~mlMainTab & ~settings', 'mlPageSettings'); }, + + async navigateToStackManagementJobsListPage() { + // clicks the jobsListLink and loads the jobs list page + await testSubjects.click('jobsListLink'); + await retry.tryForTime(60 * 1000, async () => { + // verify that the overall page is present + await testSubjects.existOrFail('mlPageStackManagementJobsList'); + // verify that the default tab with the anomaly detection jobs list got loaded + await testSubjects.existOrFail('ml-jobs-list'); + }); + }, + + async navigateToStackManagementJobsListPageAnalyticsTab() { + // clicks the `Analytics` tab and loads the analytics list page + await testSubjects.click('mlStackManagementJobsListAnalyticsTab'); + await retry.tryForTime(60 * 1000, async () => { + // verify that the empty prompt for analytics jobs list got loaded + await testSubjects.existOrFail('mlNoDataFrameAnalyticsFound'); + }); + }, }; }