diff --git a/x-pack/plugins/rollup/__jest__/utils/testbed.js b/x-pack/plugins/rollup/__jest__/utils/testbed.js index 95ef83dd1b928..dab5d1e7d93ba 100644 --- a/x-pack/plugins/rollup/__jest__/utils/testbed.js +++ b/x-pack/plugins/rollup/__jest__/utils/testbed.js @@ -9,7 +9,7 @@ import { Provider } from 'react-redux'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; // eslint-disable-line import/no-unresolved import { findTestSubject as findTestSubjectHelper } from '@elastic/eui/lib/test'; -const registerTestSubjExists = component => testSubject => Boolean(findTestSubjectHelper(component, testSubject).length); +const registerTestSubjExists = component => (testSubject, count = 1) => findTestSubjectHelper(component, testSubject).length === count; export const registerTestBed = (Component, defaultProps, store = {}) => (props) => { const component = mountWithIntl( diff --git a/x-pack/plugins/rollup/fixtures/job.js b/x-pack/plugins/rollup/fixtures/job.js index ba9352dd36404..b8aab4da6bd9d 100644 --- a/x-pack/plugins/rollup/fixtures/job.js +++ b/x-pack/plugins/rollup/fixtures/job.js @@ -10,34 +10,38 @@ const initialValues = { dateHistogramField: 'timestamp', dateHistogramInterval: '24h', dateHistogramTimeZone: 'UTC', - documentsProcessed: 0, + documentsProcessed: 10, histogram: [ { name: 'DistanceMiles' }, { name: 'FlightTimeMin' }, ], id: 'test', indexPattern: 'kibana*', + json: { + foo: 'bar', + }, metrics: [ { name: 'dayOfWeek', - types: ['avg', 'min'] + types: ['avg', 'max', 'min'] }, { name: 'distanceKilometers', types: ['avg', 'max'] } ], - pagesProcessed: 0, + pagesProcessed: 3, rollupCron: '0 0 0 ? * 7', rollupDelay: '1d', rollupIndex: 'my_rollup_index', - rollupsIndexed: 0, + rollupsIndexed: 2, status: 'stopped', terms: [ + { name: 'Dest' }, { name: 'Carrier' }, { name: 'DestCountry' }, ], - triggerCount: 0, + triggerCount: 7, }; export const getJob = (values = { id: getRandomString() }) => ({ ...initialValues, ...values }); diff --git a/x-pack/plugins/rollup/public/crud_app/sections/components/field_list/field_list.js b/x-pack/plugins/rollup/public/crud_app/sections/components/field_list/field_list.js index 912b945a0d7e0..e0c1fdf2222b2 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/components/field_list/field_list.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/components/field_list/field_list.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import { @@ -64,17 +64,15 @@ export const FieldList = ({ }; return ( - - - + ); }; diff --git a/x-pack/plugins/rollup/public/crud_app/sections/components/job_details/tabs/tab_summary.js b/x-pack/plugins/rollup/public/crud_app/sections/components/job_details/tabs/tab_summary.js index 16d65f295d9d5..04d064639c257 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/components/job_details/tabs/tab_summary.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/components/job_details/tabs/tab_summary.js @@ -43,11 +43,17 @@ export class TabSummary extends Component { } = stats; return ( - +
-

+

- + - + {documentsProcessed} - + - + {pagesProcessed} @@ -92,34 +100,36 @@ export class TabSummary extends Component { - + - + {rollupsIndexed} - + - + {triggerCount} - +

); } @@ -138,154 +148,186 @@ export class TabSummary extends Component { return ( - -

- -

-
- - +
+ +

+ +

+
+ + + + + + + + + + + + {indexPattern} + + + + + + + - - - - - - - - {indexPattern} - - + + {rollupIndex} + + + - - - - + + + + {' '} + + )} + /> + + + {rollupCron} + + - - {rollupIndex} - - - + + + + - - - - {' '} - + {rollupDelay || ( )} - /> - - - - {rollupCron} - - - - - - - - - - {rollupDelay || ( + + + + + + +
+ +
+ +

+ +

+
+ + + + + + + - )} - - - - - - - - -

- -

-
- - - - - - - - - - - - {dateHistogramField} - - - - - - - + + + + {dateHistogramField} + + + + + + + - - {dateHistogramTimeZone} - - - + + {dateHistogramTimeZone} + + + - - - - {' '} - - )} - /> - - - - {dateHistogramInterval} - - - - + + + + {' '} + + )} + /> + + + + {dateHistogramInterval} + + + + +
{this.renderStats()}
diff --git a/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.js b/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.js index b080c61d430be..85ff06bb016de 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.js @@ -37,7 +37,7 @@ import { tabToHumanizedMap, } from '../../components'; -const JOB_DETAILS_TABS = [ +export const JOB_DETAILS_TABS = [ JOB_DETAILS_TAB_SUMMARY, JOB_DETAILS_TAB_TERMS, JOB_DETAILS_TAB_HISTOGRAM, @@ -50,6 +50,7 @@ export class DetailPanelUi extends Component { isOpen: PropTypes.bool.isRequired, isLoading: PropTypes.bool, job: PropTypes.object, + jobId: PropTypes.string, panelType: PropTypes.oneOf(JOB_DETAILS_TABS), closeDetailPanel: PropTypes.func.isRequired, openDetailPanel: PropTypes.func.isRequired, @@ -137,7 +138,7 @@ export class DetailPanelUi extends Component { return ( - + + + - +

{jobId}

diff --git a/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js b/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js new file mode 100644 index 0000000000000..0470a1ff47019 --- /dev/null +++ b/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js @@ -0,0 +1,335 @@ +/* + * 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 { registerTestBed } from '../../../../../__jest__/utils'; +import { getJob } from '../../../../../fixtures'; +import { rollupJobsStore } from '../../../store'; +import { DetailPanel } from './detail_panel'; +import { + JOB_DETAILS_TAB_SUMMARY, + JOB_DETAILS_TAB_TERMS, + JOB_DETAILS_TAB_HISTOGRAM, + JOB_DETAILS_TAB_METRICS, + JOB_DETAILS_TAB_JSON, + tabToHumanizedMap, +} from '../../components'; + +const defaultJob = getJob(); + +const defaultProps = { + isOpen: true, + isLoading: false, + job: defaultJob, + jobId: defaultJob.id, + panelType: JOB_DETAILS_TAB_SUMMARY, + closeDetailPanel: jest.fn(), + openDetailPanel: jest.fn(), +}; + +const initTestBed = registerTestBed(DetailPanel, defaultProps, rollupJobsStore); + +describe('', () => { + describe('layout', () => { + let component; + let findTestSubject; + let testSubjectExists; + + beforeEach(() => { + ({ component, findTestSubject } = initTestBed()); + }); + + it('should have the title set to the current Job "id"', () => { + const { job } = defaultProps; + const title = findTestSubject('rollupJobDetailsFlyoutTitle'); + expect(title.length).toBe(1); + expect(title.text()).toEqual(job.id); + }); + + it('should have children if it\'s open', () => { + expect(component.find('DetailPanelUi').children().length).toBeTruthy(); + }); + + it('should *not* have children if it\s closed', () => { + ({ component } = initTestBed({ isOpen: false })); + expect(component.find('DetailPanelUi').children().length).toBeFalsy(); + }); + + it('should show a loading when the job is loading', () => { + ({ component, findTestSubject, testSubjectExists } = initTestBed({ isLoading: true })); + const loading = findTestSubject('rollupJobDetailLoading'); + expect(loading.length).toBeTruthy(); + expect(loading.text()).toEqual('Loading rollup job...'); + + // Make sure the title and the tabs are visible + expect(testSubjectExists('detailPanelTabSelected')).toBeTruthy(); + expect(testSubjectExists('rollupJobDetailsFlyoutTitle')).toBeTruthy(); + }); + + it('should display a message when no job is provided', () => { + ({ component, findTestSubject } = initTestBed({ job: undefined })); + expect(findTestSubject('rollupJobDetailJobNotFound').text()).toEqual('Rollup job not found'); + }); + }); + + describe('tabs', () => { + const tabActive = JOB_DETAILS_TAB_SUMMARY; + const { component } = initTestBed({ panelType: tabActive }); + const tabs = component.find('EuiTab'); + const getTab = (id) => { + const found = tabs.findWhere((tab) => { + return tab.text() === tabToHumanizedMap[id].props.defaultMessage; + }); + return found.first(); + }; + + it('should have 5 tabs visible', () => { + const tabsLabel = tabs.map(tab => tab.text()); + + expect(tabsLabel).toEqual(['Summary', 'Terms', 'Histogram', 'Metrics', 'JSON']); + }); + + it('should set default selected tab to the "panelType" prop provided', () => { + const tab = getTab(tabActive); + expect(tab.props().isSelected).toEqual(true); + }); + + it('should select the tab when clicking on it', () => { + const { job, openDetailPanel } = defaultProps; + const termsTab = getTab(JOB_DETAILS_TAB_TERMS); + + termsTab.simulate('click'); + + expect(openDetailPanel.mock.calls.length).toBe(1); + expect(openDetailPanel.mock.calls[0][0]).toEqual({ + jobId: job.id, + panelType: JOB_DETAILS_TAB_TERMS + }); + }); + }); + + describe('job detail', () => { + describe('summary tab content', () => { + // Init testBed on the SUMMARY tab + const panelType = JOB_DETAILS_TAB_SUMMARY; + const { findTestSubject } = initTestBed({ panelType }); + + it('should have a "Logistics", "Date histogram" and "Stats" section', () => { + const expectedSections = ['Logistics', 'DateHistogram', 'Stats']; + const sectionsFound = expectedSections.reduce((sectionsFound, section) => { + if (findTestSubject(`rollupJobDetailSummary${section}Section`).length) { + sectionsFound.push(section); + } + return sectionsFound; + }, []); + + expect(sectionsFound).toEqual(expectedSections); + }); + + describe('Logistics section', () => { + const LOGISTICS_SUBSECTIONS = ['IndexPattern', 'RollupIndex', 'Cron', 'Delay']; + + it('should have "Index pattern", "Rollup index", "Cron" and "Delay" subsections', () => { + const logisticsSubsectionsTitles = LOGISTICS_SUBSECTIONS.reduce((subSections, subSection) => { + if (findTestSubject(`rollupJobDetailLogistics${subSection}Title`)) { + subSections.push(subSection); + } + return subSections; + }, []); + expect(logisticsSubsectionsTitles).toEqual(LOGISTICS_SUBSECTIONS); + }); + + it('should set the correct job value for each of the subsection', () => { + LOGISTICS_SUBSECTIONS.forEach((subSection) => { + const wrapper = findTestSubject(`rollupJobDetailLogistics${subSection}Description`); + expect(wrapper.length).toBe(1); + const description = wrapper.text(); + + switch(subSection) { + case 'IndexPattern': + expect(description).toEqual(defaultJob.indexPattern); + break; + case 'Cron': + expect(description).toEqual(defaultJob.rollupCron); + break; + case 'Delay': + expect(description).toEqual(defaultJob.rollupDelay); + break; + case 'RollupIndex': + expect(description).toEqual(defaultJob.rollupIndex); + break; + default: + // Should never get here... if it does a section is missing in the constant + throw(new Error('Should not get here. The constant LOGISTICS_SUBSECTIONS is probably missing a new subsection')); + } + }); + }); + }); + + describe('Date histogram section', () => { + const DATE_HISTOGRAMS_SUBSECTIONS = ['TimeField', 'Timezone', 'Interval']; + + it('should have "Time field", "Timezone", "Interval" subsections', () => { + const dateHistogramSubsections = DATE_HISTOGRAMS_SUBSECTIONS.reduce((subSections, subSection) => { + if (findTestSubject(`rollupJobDetailDateHistogram${subSection}Title`)) { + subSections.push(subSection); + } + return subSections; + }, []); + expect(dateHistogramSubsections).toEqual(DATE_HISTOGRAMS_SUBSECTIONS); + }); + + it('should set the correct job value for each of the subsection', () => { + DATE_HISTOGRAMS_SUBSECTIONS.forEach((subSection) => { + const wrapper = findTestSubject(`rollupJobDetailDateHistogram${subSection}Description`); + expect(wrapper.length).toBe(1); + const description = wrapper.text(); + + switch(subSection) { + case 'TimeField': + expect(description).toEqual(defaultJob.dateHistogramField); + break; + case 'Interval': + expect(description).toEqual(defaultJob.dateHistogramInterval); + break; + case 'Timezone': + expect(description).toEqual(defaultJob.dateHistogramTimeZone); + break; + default: + // Should never get here... if it does a section is missing in the constant + throw(new Error('Should not get here. The constant DATE_HISTOGRAMS_SUBSECTIONS is probably missing a new subsection')); + } + }); + }); + }); + + describe('Stats section', () => { + const STATS_SUBSECTIONS = ['DocumentsProcessed', 'PagesProcessed', 'RollupsIndexed', 'TriggerCount']; + + it('should have "Documents processed", "Pages processed", "Rollups indexed" and "Trigger count" subsections', () => { + const statsSubSections = STATS_SUBSECTIONS.reduce((subSections, subSection) => { + if (findTestSubject(`rollupJobDetailStats${subSection}Title`)) { + subSections.push(subSection); + } + return subSections; + }, []); + expect(statsSubSections).toEqual(STATS_SUBSECTIONS); + }); + + it('should set the correct job value for each of the subsection', () => { + STATS_SUBSECTIONS.forEach((subSection) => { + const wrapper = findTestSubject(`rollupJobDetailStats${subSection}Description`); + expect(wrapper.length).toBe(1); + const description = wrapper.text(); + + switch(subSection) { + case 'DocumentsProcessed': + expect(description).toEqual(defaultJob.documentsProcessed.toString()); + break; + case 'PagesProcessed': + expect(description).toEqual(defaultJob.pagesProcessed.toString()); + break; + case 'RollupsIndexed': + expect(description).toEqual(defaultJob.rollupsIndexed.toString()); + break; + case 'TriggerCount': + expect(description).toEqual(defaultJob.triggerCount.toString()); + break; + default: + // Should never get here... if it does a section is missing in the constant + throw(new Error('Should not get here. The constant STATS_SUBSECTIONS is probably missing a new subsection')); + } + }); + }); + + it('should display the job status', () => { + const statsSection = findTestSubject('rollupJobDetailSummaryStatsSection'); + expect(statsSection.length).toBe(1); + expect(defaultJob.status).toEqual('stopped'); // make sure status is Stopped + expect(statsSection.find('EuiHealth').text()).toEqual('Stopped'); + }); + }); + }); + + describe('terms tab content', () => { + // Init testBed on the TERMS tab + const panelType = JOB_DETAILS_TAB_TERMS; + const { findTestSubject } = initTestBed({ panelType }); + const tabContent = findTestSubject('rollupJobDetailTabContent'); + const getRowsText = () => ( + tabContent + .find('tr') + .map(row => row.text()) + .slice(1) // we remove the first row as it is the table header + ); + it('should list the Job terms fields', () => { + const rowsText = getRowsText(); + const expected = defaultJob.terms.map(term => term.name); + expect(rowsText).toEqual(expected); + }); + }); + + describe('histogram tab content', () => { + // Init testBed on the HISTOGRAM tab + const panelType = JOB_DETAILS_TAB_HISTOGRAM; + const { findTestSubject } = initTestBed({ panelType }); + const tabContent = findTestSubject('rollupJobDetailTabContent'); + const getRowsText = () => ( + tabContent + .find('tr') + .map(row => row.text()) + .slice(1) // we remove the first row as it is the table header + ); + + it('should list the Job histogram fields', () => { + const rowsText = getRowsText(); + const expected = defaultJob.histogram.map(h => h.name); + expect(rowsText).toEqual(expected); + }); + }); + + describe('metrics tab content', () => { + // Init testBed on the METRICS tab + const panelType = JOB_DETAILS_TAB_METRICS; + const { findTestSubject } = initTestBed({ panelType }); + const tabContent = findTestSubject('rollupJobDetailTabContent'); + const getRows = () => ( + tabContent + .find('tr') + .slice(1) + ); + it('should list the Job metrics fields and their types', () => { + const rows = getRows(); + + rows.forEach((row, i) => { + const metric = defaultJob.metrics[i]; + + row.find('td').forEach((cell, j) => { + if (j === 0) { + // field + expect(cell.text()).toEqual(metric.name); + } else if (j === 1) { + // types + expect(cell.text()).toEqual(metric.types.join(', ')); + } + }); + }); + }); + }); + + describe('JSON tab content', () => { + // Init testBed on the JSON tab + const panelType = JOB_DETAILS_TAB_JSON; + const { findTestSubject } = initTestBed({ panelType }); + const tabContent = findTestSubject('rollupJobDetailTabContent'); + + it('should render the "EuiCodeEditor" with the job "json" data', () => { + const euiCodeEditor = tabContent.find('EuiCodeEditor'); + expect(euiCodeEditor.length).toBeTruthy(); + expect(JSON.parse(euiCodeEditor.props().value)).toEqual(defaultJob.json); + }); + }); + }); +}); diff --git a/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js b/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js index 3c4ef49212469..f32cb5fe0918c 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js @@ -26,7 +26,7 @@ const defaultProps = { loadJobs: () => {}, refreshJobs: () => {}, openDetailPanel: () => {}, - jobs: [], + hasJobs: false, isLoading: false }; @@ -40,14 +40,14 @@ describe('', () => { }); it('should display a loading message when loading the jobs', () => { - const { testSubjectExists } = initTestBed({ isLoading: true }); + const { component, testSubjectExists } = initTestBed({ isLoading: true }); expect(testSubjectExists('jobListLoading')).toBeTruthy(); - expect(testSubjectExists('jobListTable')).toBeFalsy(); + expect(component.find('JobTableUi').length).toBeFalsy(); }); it('should display the when there are jobs', () => { - const { component, testSubjectExists } = initTestBed({ jobs: [{ foo: 'bar' }] }); + const { component, testSubjectExists } = initTestBed({ hasJobs: true }); expect(testSubjectExists('jobListLoading')).toBeFalsy(); expect(component.find('JobTableUi').length).toBeTruthy(); diff --git a/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js b/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js index d1867a485becd..11fe6cfc255bb 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js @@ -61,7 +61,7 @@ describe('', () => { }); it('should set the correct job value in each row cell', () => { - const fieldNames = [ + const unformattedFields = [ 'id', 'indexPattern', 'rollupIndex', @@ -72,10 +72,9 @@ describe('', () => { const job = jobs[0]; const getCellText = (field) => row.find(`[data-test-subj="jobTableCell-${field}"]`).hostNodes().text(); - // Simple fields - fieldNames.forEach((fieldName) => { - const cellText = getCellText(fieldName); - expect(cellText).toEqual(job[fieldName]); + unformattedFields.forEach((field) => { + const cellText = getCellText(field); + expect(cellText).toEqual(job[field]); }); // Status @@ -84,16 +83,8 @@ describe('', () => { expect(cellStatusText).toEqual('Stopped'); // Groups - const expectedJobGroups = ['histogram', 'terms'].reduce((text, field) => { - if (job[field].length) { - return text - ? `${text}, ${field}` - : field.replace(/^\w/, char => char.toUpperCase()); - } - return text; - }, ''); const cellGroupsText = getCellText('groups'); - expect(cellGroupsText).toEqual(expectedJobGroups); + expect(cellGroupsText).toEqual('Histogram, terms'); // Metrics const expectedJobMetrics = job.metrics.reduce((text, { name }) => (