[6.8] [ML] Functional tests - Basic page navigation (#66431)

This PR adds basic page navigation tests for the ML app in the 6.8 branch.
This commit is contained in:
Robert Oskamp 2020-05-14 10:43:25 +02:00 committed by GitHub
parent 93e09d23e6
commit 5a6577f5f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 192 additions and 21 deletions

View file

@ -14,35 +14,35 @@
<!-- 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="#/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

@ -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

@ -15,7 +15,10 @@ import { checkFindFileStructurePrivilege } from 'plugins/ml/privilege/check_priv
import uiRoutes from 'ui/routes';
const template = `<ml-nav-menu name="datavisualizer" /><datavisualizer-selector class="ml-datavisualizer-selector"/>`;
const template = `
<ml-nav-menu name="datavisualizer" />
<datavisualizer-selector class="ml-datavisualizer-selector" data-test-subj="mlPageDataVisualizerSelector"/>
`;
uiRoutes
.when('/datavisualizer', {

View file

@ -17,6 +17,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">
<navbar ng-show="jobs.length > 0 && chrome.getVisible()">
<job-select-button></job-select-button>
</navbar>

View file

@ -255,6 +255,7 @@ class JobsListUI extends Component {
return (
<EuiBasicTable
data-test-subj="mlJobListTable"
loading={loading === true}
noItemsMessage={loading ?
intl.formatMessage({

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

@ -24,6 +24,7 @@ export function NewJobButton() {
const buttonEnabled = (checkPermission('canCreateJob') && mlNodesAvailable());
return (
<EuiButton
data-test-subj="mlCreateNewJobButton"
onClick={newJob}
size="s"
disabled={(buttonEnabled === false)}

View file

@ -19,7 +19,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
@ -45,7 +46,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}
@ -65,7 +66,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,6 +1,6 @@
<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">
<navbar ng-show="jobs.length > 0 && chrome.getVisible()">
<job-select-button
timeseriesonly="true"
@ -8,7 +8,7 @@
</job-select-button>
</navbar>
<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"
@ -189,7 +189,7 @@
i18n-id="xpack.ml.timeSeriesExplorer.annotationsTitle"
i18n-default-message="Annotations"
></span>
<ml-annotation-table
annotations="focusAnnotationData"
drill-down="false"

View file

@ -0,0 +1,16 @@
/*
* 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';
// tslint:disable:no-default-export
export default function({ loadTestFile }: KibanaFunctionalTestDefaultProviders) {
describe('machine learning', function() {
this.tags(['ciGroup3', 'mlqa']);
loadTestFile(require.resolve('./pages'));
});
}

View file

@ -0,0 +1,56 @@
/*
* 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';
// tslint:disable: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 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

@ -54,6 +54,7 @@ import {
AceEditorProvider,
GrokDebuggerProvider,
UptimeProvider,
MachineLearningProvider,
} from './services';
@ -87,7 +88,8 @@ export default async function ({ readConfigFile }) {
resolve(__dirname, './apps/maps'),
resolve(__dirname, './apps/status_page'),
resolve(__dirname, './apps/upgrade_assistant'),
resolve(__dirname, './apps/uptime')
resolve(__dirname, './apps/uptime'),
resolve(__dirname, './apps/ml'),
],
// define the name and providers for services that should be
@ -124,6 +126,7 @@ export default async function ({ readConfigFile }) {
grokDebugger: GrokDebuggerProvider,
uptime: UptimeProvider,
rollup: RollupPageProvider,
ml: MachineLearningProvider,
},
// just like services, PageObjects are defined as a map of
@ -213,7 +216,10 @@ export default async function ({ readConfigFile }) {
rollupJob: {
pathname: '/app/kibana',
hash: '/management/elasticsearch/rollup_jobs/'
}
},
ml: {
pathname: '/app/ml'
},
},
// choose where esArchiver should load archives from

View file

@ -11,3 +11,4 @@ export { RandomProvider } from './random';
export { AceEditorProvider } from './ace_editor';
export { GrokDebuggerProvider } from './grok_debugger';
export { UptimeProvider } from './uptime';
export { MachineLearningProvider } from './ml';

View file

@ -0,0 +1,82 @@
/*
* 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 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 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');
},
};
}