diff --git a/src/plugins/discover/public/__mocks__/es_hits.ts b/src/plugins/discover/public/__mocks__/es_hits.ts new file mode 100644 index 000000000000..e282bdbd1ca9 --- /dev/null +++ b/src/plugins/discover/public/__mocks__/es_hits.ts @@ -0,0 +1,55 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +export const esHits = [ + { + _index: 'i', + _id: '1', + _score: 1, + _type: '_doc', + _source: { date: '2020-20-01T12:12:12.123', message: 'test1', bytes: 20 }, + }, + { + _index: 'i', + _id: '2', + _score: 1, + _type: '_doc', + _source: { date: '2020-20-01T12:12:12.124', name: 'test2', extension: 'jpg' }, + }, + { + _index: 'i', + _id: '3', + _score: 1, + _type: '_doc', + _source: { date: '2020-20-01T12:12:12.124', name: 'test3', extension: 'gif', bytes: 50 }, + }, + { + _index: 'i', + _id: '4', + _score: 1, + _type: '_doc', + _source: { date: '2020-20-01T12:12:12.125', name: 'test4', extension: 'png', bytes: 50 }, + }, + { + _index: 'i', + _id: '5', + _score: 1, + _type: '_doc', + _source: { date: '2020-20-01T12:12:12.128', name: 'test5', extension: 'doc', bytes: 50 }, + }, +]; diff --git a/src/plugins/discover/public/__mocks__/index_pattern.ts b/src/plugins/discover/public/__mocks__/index_pattern.ts index 696079ec72a7..706118cb7135 100644 --- a/src/plugins/discover/public/__mocks__/index_pattern.ts +++ b/src/plugins/discover/public/__mocks__/index_pattern.ts @@ -17,8 +17,9 @@ * under the License. */ -import { IndexPattern, indexPatterns } from '../kibana_services'; import { IIndexPatternFieldList } from '../../../data/common/index_patterns/fields'; +import { IndexPattern } from '../../../data/common'; +import { indexPatterns } from '../../../data/public'; const fields = [ { @@ -67,8 +68,10 @@ const indexPattern = ({ getComputedFields: () => ({}), getSourceFiltering: () => ({}), getFieldByName: () => ({}), + timeFieldName: '', } as unknown) as IndexPattern; indexPattern.flattenHit = indexPatterns.flattenHitWrapper(indexPattern, indexPattern.metaFields); +indexPattern.isTimeBased = () => !!indexPattern.timeFieldName; export const indexPatternMock = indexPattern; diff --git a/src/plugins/discover/public/__mocks__/index_pattern_with_timefield.ts b/src/plugins/discover/public/__mocks__/index_pattern_with_timefield.ts new file mode 100644 index 000000000000..c898ab111254 --- /dev/null +++ b/src/plugins/discover/public/__mocks__/index_pattern_with_timefield.ts @@ -0,0 +1,83 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IIndexPatternFieldList } from '../../../data/common/index_patterns/fields'; +import { IndexPattern } from '../../../data/common'; +import { indexPatterns } from '../../../data/public'; + +const fields = [ + { + name: '_index', + type: 'string', + scripted: false, + filterable: true, + }, + { + name: 'timestamp', + type: 'date', + scripted: false, + filterable: true, + }, + { + name: 'message', + type: 'string', + scripted: false, + filterable: false, + }, + { + name: 'extension', + type: 'string', + scripted: false, + filterable: true, + }, + { + name: 'bytes', + type: 'number', + scripted: false, + filterable: true, + }, + { + name: 'scripted', + type: 'number', + scripted: true, + filterable: false, + }, +] as IIndexPatternFieldList; + +fields.getByName = (name: string) => { + return fields.find((field) => field.name === name); +}; + +const indexPattern = ({ + id: 'index-pattern-with-timefield-id', + title: 'index-pattern-without-timefield', + metaFields: ['_index', '_score'], + flattenHit: undefined, + formatHit: jest.fn((hit) => hit._source), + fields, + getComputedFields: () => ({}), + getSourceFiltering: () => ({}), + getFieldByName: () => ({}), + timeFieldName: 'timestamp', +} as unknown) as IndexPattern; + +indexPattern.flattenHit = indexPatterns.flattenHitWrapper(indexPattern, indexPattern.metaFields); +indexPattern.isTimeBased = () => !!indexPattern.timeFieldName; + +export const indexPatternWithTimefieldMock = indexPattern; diff --git a/src/plugins/discover/public/__mocks__/ui_settings.ts b/src/plugins/discover/public/__mocks__/ui_settings.ts new file mode 100644 index 000000000000..8454907e9d6c --- /dev/null +++ b/src/plugins/discover/public/__mocks__/ui_settings.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { IUiSettingsClient } from 'kibana/public'; +import { SAMPLE_SIZE_SETTING } from '../../common'; + +export const uiSettingsMock = ({ + get: (key: string) => { + if (key === SAMPLE_SIZE_SETTING) { + return 10; + } + }, +} as unknown) as IUiSettingsClient; diff --git a/src/plugins/discover/public/application/components/discover_legacy.test.tsx b/src/plugins/discover/public/application/components/discover_legacy.test.tsx new file mode 100644 index 000000000000..e2f4ba7ab6e2 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_legacy.test.tsx @@ -0,0 +1,130 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { shallowWithIntl } from '@kbn/test/jest'; +import { DiscoverLegacy } from './discover_legacy'; +import { inspectorPluginMock } from '../../../../inspector/public/mocks'; +import { esHits } from '../../__mocks__/es_hits'; +import { indexPatternMock } from '../../__mocks__/index_pattern'; +import { getTopNavLinks } from './top_nav/get_top_nav_links'; +import { DiscoverServices } from '../../build_services'; +import { GetStateReturn } from '../angular/discover_state'; +import { savedSearchMock } from '../../__mocks__/saved_search'; +import { createSearchSourceMock } from '../../../../data/common/search/search_source/mocks'; +import { dataPluginMock } from '../../../../data/public/mocks'; +import { createFilterManagerMock } from '../../../../data/public/query/filter_manager/filter_manager.mock'; +import { uiSettingsMock } from '../../__mocks__/ui_settings'; +import { IndexPattern, IndexPatternAttributes } from '../../../../data/common/index_patterns'; +import { SavedObject } from '../../../../../core/types'; +import { navigationPluginMock } from '../../../../navigation/public/mocks'; +import { indexPatternWithTimefieldMock } from '../../__mocks__/index_pattern_with_timefield'; +import { calcFieldCounts } from '../helpers/calc_field_counts'; + +const mockNavigation = navigationPluginMock.createStartContract(); + +jest.mock('../../kibana_services', () => { + return { + getServices: () => ({ + metadata: { + branch: 'test', + }, + capabilities: { + discover: { + save: true, + }, + }, + navigation: mockNavigation, + }), + }; +}); + +function getProps(indexPattern: IndexPattern) { + const searchSourceMock = createSearchSourceMock({}); + const state = ({} as unknown) as GetStateReturn; + const services = ({ + capabilities: { + discover: { + save: true, + }, + }, + } as unknown) as DiscoverServices; + + return { + addColumn: jest.fn(), + fetch: jest.fn(), + fetchCounter: 0, + fetchError: undefined, + fieldCounts: calcFieldCounts({}, esHits, indexPattern), + hits: esHits.length, + indexPattern, + minimumVisibleRows: 10, + onAddFilter: jest.fn(), + onChangeInterval: jest.fn(), + onMoveColumn: jest.fn(), + onRemoveColumn: jest.fn(), + onSetColumns: jest.fn(), + onSkipBottomButtonClick: jest.fn(), + onSort: jest.fn(), + opts: { + config: uiSettingsMock, + data: dataPluginMock.createStartContract(), + fixedScroll: jest.fn(), + filterManager: createFilterManagerMock(), + indexPatternList: (indexPattern as unknown) as Array<SavedObject<IndexPatternAttributes>>, + sampleSize: 10, + savedSearch: savedSearchMock, + setHeaderActionMenu: jest.fn(), + timefield: indexPattern.timeFieldName || '', + setAppState: jest.fn(), + }, + resetQuery: jest.fn(), + resultState: 'ready', + rows: esHits, + searchSource: searchSourceMock, + setIndexPattern: jest.fn(), + showSaveQuery: true, + state: { columns: [] }, + timefilterUpdateHandler: jest.fn(), + topNavMenu: getTopNavLinks({ + getFieldCounts: jest.fn(), + indexPattern, + inspectorAdapters: inspectorPluginMock, + navigateTo: jest.fn(), + savedSearch: savedSearchMock, + services, + state, + }), + updateQuery: jest.fn(), + updateSavedQueryId: jest.fn(), + }; +} + +describe('Descover legacy component', () => { + test('selected index pattern without time field displays no chart toggle', () => { + const component = shallowWithIntl(<DiscoverLegacy {...getProps(indexPatternMock)} />); + expect(component.find('[data-test-subj="discoverChartToggle"]').length).toBe(0); + }); + test('selected index pattern with time field displays chart toggle', () => { + const component = shallowWithIntl( + <DiscoverLegacy {...getProps(indexPatternWithTimefieldMock)} /> + ); + expect(component.find('[data-test-subj="discoverChartToggle"]').length).toBe(1); + }); +}); diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx index 56f8fa46a9f6..d228be66990b 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.tsx @@ -66,9 +66,9 @@ export interface DiscoverProps { addColumn: (column: string) => void; fetch: () => void; fetchCounter: number; - fetchError: Error; + fetchError?: Error; fieldCounts: Record<string, number>; - histogramData: Chart; + histogramData?: Chart; hits: number; indexPattern: IndexPattern; minimumVisibleRows: number; @@ -266,23 +266,26 @@ export function DiscoverLegacy({ /> </EuiFlexItem> )} - <EuiFlexItem className="dscResultCount__toggle" grow={false}> - <EuiButtonEmpty - size="xs" - iconType={toggleOn ? 'eyeClosed' : 'eye'} - onClick={() => { - toggleChart(!toggleOn); - }} - > - {toggleOn - ? i18n.translate('discover.hideChart', { - defaultMessage: 'Hide chart', - }) - : i18n.translate('discover.showChart', { - defaultMessage: 'Show chart', - })} - </EuiButtonEmpty> - </EuiFlexItem> + {opts.timefield && ( + <EuiFlexItem className="dscResultCount__toggle" grow={false}> + <EuiButtonEmpty + size="xs" + iconType={toggleOn ? 'eyeClosed' : 'eye'} + onClick={() => { + toggleChart(!toggleOn); + }} + data-test-subj="discoverChartToggle" + > + {toggleOn + ? i18n.translate('discover.hideChart', { + defaultMessage: 'Hide chart', + }) + : i18n.translate('discover.showChart', { + defaultMessage: 'Show chart', + })} + </EuiButtonEmpty> + </EuiFlexItem> + )} </EuiFlexGroup> <SkipBottomButton onClick={onSkipBottomButtonClick} /> </EuiFlexItem> @@ -297,7 +300,7 @@ export function DiscoverLegacy({ )} className="dscTimechart" > - {opts.chartAggConfigs && rows.length !== 0 && ( + {opts.chartAggConfigs && rows.length !== 0 && histogramData && ( <div className="dscHistogram" data-test-subj="discoverChart"> <DiscoverHistogram chartData={histogramData}