[ML] Add basic navigation tests for ML pages to functional UI tests (#39525)

This PR adds functional UI tests to navigate through the machine learning pages
with some basic verification for existing elements.
This commit is contained in:
Robert Oskamp 2019-06-25 20:06:05 +02:00 committed by GitHub
parent 1d09f5a067
commit 82d83ee5f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 184 additions and 20 deletions

View file

@ -3,42 +3,42 @@
<!-- Tabs -->
<div data-transclude-slot="bottomRow">
<div ng-if="showTabs" class="kuiLocalTabs" role="tablist">
<a kbn-href="#/jobs" class="kuiLocalTab" role="tab"
<a kbn-href="#/jobs" class="kuiLocalTab" role="tab" data-test-subj="mlTabJobManagement"
ng-class="{'kuiLocalTab-isSelected': isActiveTab('jobs'), 'disabled-nav-link': disableLinks}">
<span
i18n-id="xpack.ml.navMenu.jobManagementTabLinkText"
i18n-default-message="Job Management"
></span>
</a>
<a kbn-href="#/explorer" class="kuiLocalTab" role="tab"
<a kbn-href="#/explorer" class="kuiLocalTab" role="tab" data-test-subj="mlTabAnomalyExplorer"
ng-class="{'kuiLocalTab-isSelected': isActiveTab('explorer'), 'disabled-nav-link': disableLinks}">
<span
i18n-id="xpack.ml.navMenu.anomalyExplorerTabLinkText"
i18n-default-message="Anomaly Explorer"
></span>
</a>
<a kbn-href="#/timeseriesexplorer" class="kuiLocalTab" role="tab"
<a kbn-href="#/timeseriesexplorer" class="kuiLocalTab" role="tab" data-test-subj="mlTabSingleMetricViewer"
ng-class="{'kuiLocalTab-isSelected': isActiveTab('timeseriesexplorer'), 'disabled-nav-link': disableLinks}">
<span
i18n-id="xpack.ml.navMenu.singleMetricViewerTabLinkText"
i18n-default-message="Single Metric Viewer"
></span>
</a>
<a kbn-href="#/data_frames" class="kuiLocalTab" role="tab"
<a kbn-href="#/data_frames" class="kuiLocalTab" role="tab" data-test-subj="mlTabDataFrames"
ng-class="{'kuiLocalTab-isSelected': isActiveTab('data_frame')}">
<span
i18n-id="xpack.ml.navMenu.dataFrameTabLinkText"
i18n-default-message="Data Frames"
></span>
</a>
<a kbn-href="#/datavisualizer" class="kuiLocalTab" role="tab"
<a kbn-href="#/datavisualizer" class="kuiLocalTab" role="tab" data-test-subj="mlTabDataVisualizer"
ng-class="{'kuiLocalTab-isSelected': isActiveTab('datavisualizer')}">
<span
i18n-id="xpack.ml.navMenu.dataVisualizerTabLinkText"
i18n-default-message="Data Visualizer"
></span>
</a>
<a kbn-href="#/settings" class="kuiLocalTab" role="tab"
<a kbn-href="#/settings" class="kuiLocalTab" role="tab" data-test-subj="mlTabSettings"
ng-class="{'kuiLocalTab-isSelected': isActiveTab('settings'), 'disabled-nav-link': disableLinks}">
<span
i18n-id="xpack.ml.navMenu.settingsTabLinkText"

View file

@ -2,6 +2,7 @@
exports[`Data Frame: Job List <Page /> Minimal initialization 1`] = `
<EuiPage
data-test-subj="mlPageDataFrame"
restrictWidth={false}
>
<EuiPageBody

View file

@ -14,6 +14,7 @@ exports[`Data Frame: Job List <DataFrameJobList /> Minimal initialization 1`] =
</EuiButtonEmpty>,
]
}
data-test-subj="mlNoDataFrameJobsFound"
iconColor="subdued"
title={
<h2>

View file

@ -68,6 +68,7 @@ export const DataFrameJobList: SFC = () => {
Create your first data frame job
</EuiButtonEmpty>,
]}
data-test-subj="mlNoDataFrameJobsFound"
/>
);
}

View file

@ -25,7 +25,7 @@ import { CreateJobButton } from './components/create_job_button';
import { DataFrameJobList } from './components/job_list';
export const Page: SFC = () => (
<EuiPage>
<EuiPage data-test-subj="mlPageDataFrame">
<EuiPageBody>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>

View file

@ -121,6 +121,7 @@ export const DatavisualizerSelector = injectI18n(function (props) {
/>
</EuiButton>
}
data-test-subj="mlDataVisualizerCardImportData"
/>
</EuiFlexItem>
<EuiFlexItem>
@ -149,6 +150,7 @@ export const DatavisualizerSelector = injectI18n(function (props) {
/>
</EuiButton>
}
data-test-subj="mlDataVisualizerCardIndexData"
/>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -19,7 +19,7 @@ import uiRoutes from 'ui/routes';
const template = `
<div class="euiSpacer euiSpacer--s" />
<ml-nav-menu name="datavisualizer" />
<datavisualizer-selector />
<datavisualizer-selector data-test-subj="mlPageDataVisualizerSelector" />
`;
uiRoutes

View file

@ -18,6 +18,7 @@ exports[`ExplorerNoInfluencersFound snapshot 1`] = `
/>
</EuiButton>
}
data-test-subj="mlNoJobsFound"
iconColor="subdued"
iconType="alert"
title={

View file

@ -29,5 +29,6 @@ export const ExplorerNoJobsFound = () => (
/>
</EuiButton>
}
data-test-subj="mlNoJobsFound"
/>
);

View file

@ -1,6 +1,6 @@
<ml-nav-menu name="explorer"></ml-nav-menu>
<ml-chart-tooltip></ml-chart-tooltip>
<div class="ml-explorer" ng-controller="MlExplorerController">
<div class="ml-explorer" ng-controller="MlExplorerController" data-test-subj="mlPageAnomalyExplorer" >
<ml-job-selector-react-wrapper />

View file

@ -115,7 +115,7 @@ export const JobStatsBar = ({ jobsSummaryList }) => {
const stats = Object.keys(jobStats).map(k => jobStats[k]);
return (
<div className="jobs-stats-bar">
<div className="jobs-stats-bar" data-test-subj="mlJobStatsBar">
{
stats.filter(s => (s.show)).map(s => <Stat key={s.label} stat={s} />)
}

View file

@ -20,7 +20,7 @@ import { loadNewJobDefaults } from 'plugins/ml/jobs/new_job/utils/new_job_defaul
import uiRoutes from 'ui/routes';
const template = `<ml-nav-menu name="jobs" /><jobs-page />`;
const template = `<ml-nav-menu name="jobs" /><jobs-page data-test-subj="mlPageJobManagement" />`;
uiRoutes
.when('/jobs/?', {

View file

@ -3,6 +3,7 @@
exports[`Settings Renders settings page 1`] = `
<EuiPage
className="mlSettingsPage"
data-test-subj="mlPageSettings"
restrictWidth={false}
>
<EuiPageBody
@ -35,7 +36,7 @@ exports[`Settings Renders settings page 1`] = `
>
<EuiButtonEmpty
color="primary"
data-testid="ml_calendar_mng_button"
data-test-subj="ml_calendar_mng_button"
href="undefined/app/ml#/settings/calendars_list"
iconSide="left"
isDisabled={false}
@ -54,7 +55,7 @@ exports[`Settings Renders settings page 1`] = `
>
<EuiButtonEmpty
color="primary"
data-testid="ml_filter_lists_button"
data-test-subj="ml_filter_lists_button"
href="undefined/app/ml#/settings/filter_lists"
iconSide="left"
isDisabled={false}

View file

@ -29,7 +29,7 @@ export function Settings({
canGetCalendars
}) {
return (
<EuiPage className="mlSettingsPage">
<EuiPage className="mlSettingsPage" data-test-subj="mlPageSettings">
<EuiPageBody className="mlSettingsPage__body">
<EuiPageContent
className="mlSettingsPage__content"
@ -49,7 +49,7 @@ export function Settings({
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-testid="ml_calendar_mng_button"
data-test-subj="ml_calendar_mng_button"
size="l"
color="primary"
href={`${chrome.getBasePath()}/app/ml#/settings/calendars_list`}
@ -64,7 +64,7 @@ export function Settings({
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-testid="ml_filter_lists_button"
data-test-subj="ml_filter_lists_button"
size="l"
color="primary"
href={`${chrome.getBasePath()}/app/ml#/settings/filter_lists`}

View file

@ -31,7 +31,7 @@ describe('Settings', () => {
<Settings canGetFilters={false} canGetCalendars={true}/>
);
const button = wrapper.find('[data-testid="ml_filter_lists_button"]');
const button = wrapper.find('[data-test-subj="ml_filter_lists_button"]');
const filterButton = button.find('EuiButtonEmpty');
expect(filterButton.prop('isDisabled')).toBe(true);
});
@ -41,7 +41,7 @@ describe('Settings', () => {
<Settings canGetFilters={true} canGetCalendars={false} />
);
const button = wrapper.find('[data-testid="ml_calendar_mng_button"]');
const button = wrapper.find('[data-test-subj="ml_calendar_mng_button"]');
const calendarButton = button.find('EuiButtonEmpty');
expect(calendarButton.prop('isDisabled')).toBe(true);
});

View file

@ -1,10 +1,10 @@
<ml-nav-menu name="timeseriesexplorer"></ml-nav-menu>
<ml-chart-tooltip></ml-chart-tooltip>
<div class="ml-time-series-explorer" ng-controller="MlTimeSeriesExplorerController">
<div class="ml-time-series-explorer" ng-controller="MlTimeSeriesExplorerController" data-test-subj="mlPageSingleMetricViewer" >
<ml-job-selector-react-wrapper timeseriesonly="true" singleselection="true" />
<div class="no-results-container" ng-if="jobs.length === 0 && loading === false">
<div class="no-results-container" ng-if="jobs.length === 0 && loading === false" data-test-subj="mlNoSingleMetricJobsFound">
<div class="no-results">
<div
i18n-id="xpack.ml.timeSeriesExplorer.noSingleMetricJobsFoundLabel"

View file

@ -11,5 +11,6 @@ export default function({ loadTestFile }: KibanaFunctionalTestDefaultProviders)
this.tags('ciGroup3');
loadTestFile(require.resolve('./feature_controls'));
loadTestFile(require.resolve('./pages'));
});
}

View file

@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers';
// eslint-disable-next-line import/no-default-export
export default function({ getService }: KibanaFunctionalTestDefaultProviders) {
const esArchiver = getService('esArchiver');
const ml = getService('ml');
describe('page navigation', () => {
before(async () => {
await esArchiver.load('empty_kibana');
});
after(async () => {
await esArchiver.unload('empty_kibana');
});
it('loads the home page', async () => {
await ml.navigateTo();
});
it('loads the job management page', async () => {
await ml.navigateToJobManagement();
await ml.assertJobStatsBarExists();
await ml.assertJobTableExists();
await ml.assertCreateNewJobButtonExists();
});
it('loads the anomaly explorer page', async () => {
await ml.navigateToAnomalyExplorert();
await ml.assertAnomalyExplorerEmptyListMessageExists();
});
it('loads the single metric viewer page', async () => {
await ml.navigateToSingleMetricViewer();
await ml.assertSingleMetricViewerEmptyListMessageExsist();
});
it('loads the data frame page', async () => {
await ml.navigateToDataFrames();
await ml.assertDataFrameEmptyListMessageExists();
});
it('loads the data visualizer page', async () => {
await ml.navigateToDataVisualizer();
await ml.assertDataVisualizerImportDataCardExists();
await ml.assertDataVisualizerIndexDataCardExists();
});
it('loads the settings page', async () => {
await ml.navigateToSettings();
await ml.assertSettingsCalendarLinkExists();
await ml.assertSettingsFilterlistLinkExists();
});
});
}

View file

@ -60,6 +60,7 @@ import {
UptimeProvider,
InfraSourceConfigurationFlyoutProvider,
InfraLogStreamProvider,
MachineLearningProvider,
} from './services';
import {
@ -150,6 +151,7 @@ export default async function ({ readConfigFile }) {
rollup: RollupPageProvider,
infraSourceConfigurationFlyout: InfraSourceConfigurationFlyoutProvider,
infraLogStream: InfraLogStreamProvider,
ml: MachineLearningProvider,
},
// just like services, PageObjects are defined as a map of

View file

@ -14,3 +14,4 @@ export { UserMenuProvider } from './user_menu';
export { UptimeProvider } from './uptime';
export { InfraSourceConfigurationFlyoutProvider } from './infra_source_configuration_flyout';
export { InfraLogStreamProvider } from './infra_log_stream';
export { MachineLearningProvider } from './machine_learning';

View file

@ -0,0 +1,91 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { KibanaFunctionalTestDefaultProviders } from '../../types/providers';
export function MachineLearningProvider({
getService,
getPageObjects,
}: KibanaFunctionalTestDefaultProviders) {
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['common']);
return {
async navigateTo() {
return await PageObjects.common.navigateToApp('ml');
},
async navigateToJobManagement() {
await testSubjects.click('mlTabJobManagement');
await testSubjects.exists('mlPageJobManagement');
},
async navigateToAnomalyExplorert() {
await testSubjects.click('mlTabAnomalyExplorer');
await testSubjects.exists('mlPageAnomalyExplorer');
},
async navigateToSingleMetricViewer() {
await testSubjects.click('mlTabSingleMetricViewer');
await testSubjects.exists('mlPageSingleMetricViewer');
},
async navigateToDataFrames() {
await testSubjects.click('mlTabDataFrames');
await testSubjects.exists('mlPageDataFrame');
},
async navigateToDataVisualizer() {
await testSubjects.click('mlTabDataVisualizer');
await testSubjects.exists('mlPageDataVisualizerSelector');
},
async navigateToSettings() {
await testSubjects.click('mlTabSettings');
await testSubjects.exists('mlPageSettings');
},
async assertJobTableExists() {
await testSubjects.existOrFail('mlJobListTable');
},
async assertCreateNewJobButtonExists() {
await testSubjects.existOrFail('mlCreateNewJobButton');
},
async assertJobStatsBarExists() {
await testSubjects.existOrFail('mlJobStatsBar');
},
async assertAnomalyExplorerEmptyListMessageExists() {
await testSubjects.existOrFail('mlNoJobsFound');
},
async assertSingleMetricViewerEmptyListMessageExsist() {
await testSubjects.existOrFail('mlNoSingleMetricJobsFound');
},
async assertDataFrameEmptyListMessageExists() {
await testSubjects.existOrFail('mlNoDataFrameJobsFound');
},
async assertDataVisualizerImportDataCardExists() {
await testSubjects.existOrFail('mlDataVisualizerCardImportData');
},
async assertDataVisualizerIndexDataCardExists() {
await testSubjects.existOrFail('mlDataVisualizerCardIndexData');
},
async assertSettingsCalendarLinkExists() {
await testSubjects.existOrFail('ml_calendar_mng_button');
},
async assertSettingsFilterlistLinkExists() {
await testSubjects.existOrFail('ml_filter_lists_button');
},
};
}