diff --git a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx index 249e441c2..0523391f2 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/utils.tsx +++ b/dashboards-observability/public/components/custom_panels/helpers/utils.tsx @@ -166,7 +166,6 @@ export const fetchVisualizationById = async ( setIsError(`Could not locate saved visualization id:${savedVisualizationId}`); console.error('Issue in fetching the saved Visualization by Id', err); }); - return savedVisualization; }; diff --git a/dashboards-observability/public/components/metrics/helpers/__tests__/utils.test.tsx b/dashboards-observability/public/components/metrics/helpers/__tests__/utils.test.tsx new file mode 100644 index 000000000..fa599cc0a --- /dev/null +++ b/dashboards-observability/public/components/metrics/helpers/__tests__/utils.test.tsx @@ -0,0 +1,100 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { configure } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import { DurationRange } from '@elastic/eui/src/components/date_picker/types'; +import { + createPrometheusMetricById, + getNewVizDimensions, + mergeLayoutAndMetrics, + onTimeChange, + sortMetricLayout, + updateMetricsWithSelections, +} from '../utils'; +import { + samplePanelVisualizations1, + samplenewDimensions1, + samplenewDimensions2, + samplePanelVisualizations2, + sampleMetricsVisualizations, + samplePrometheusVisualizationComponent, + samplePrometheusVisualizationId, + sampleLayout, + sampleVisualizationsList, + sampleMergedVisualizations, + samplePrometheusSampleUpdateWithSelections, + sampleSavedMetric, + sampleSavedMetricUpdate, +} from '../../../../../test/metrics_contants'; +import _ from 'lodash'; + +describe('Utils helper functions', () => { + configure({ adapter: new Adapter() }); + + it('validates onTimeChange function', () => { + const setRecentlyUsedRanges = jest.fn((x) => x); + const setStart = jest.fn(); + const setEnd = jest.fn(); + const recentlyUsedRanges: DurationRange[] = []; + onTimeChange( + '2022-01-30T18:44:40.577Z', + '2022-02-25T19:18:33.075Z', + recentlyUsedRanges, + setRecentlyUsedRanges, + setStart, + setEnd + ); + expect(setRecentlyUsedRanges).toHaveBeenCalledWith([ + { start: '2022-01-30T18:44:40.577Z', end: '2022-02-25T19:18:33.075Z' }, + ]); + expect(setStart).toHaveBeenCalledWith('2022-01-30T18:44:40.577Z'); + expect(setEnd).toHaveBeenCalledWith('2022-02-25T19:18:33.075Z'); + }); + + it('validates getNewVizDimensions function', () => { + expect(getNewVizDimensions([])).toMatchObject({ + x: 0, + y: 0, + w: 12, + h: 2, + }); + + expect(getNewVizDimensions(samplePanelVisualizations1)).toStrictEqual(samplenewDimensions1); + expect(getNewVizDimensions(samplePanelVisualizations2)).toStrictEqual(samplenewDimensions2); + }); + + it('validates sortMetricLayout function', () => { + expect(sortMetricLayout(_.shuffle(sampleMetricsVisualizations))).toStrictEqual( + sampleMetricsVisualizations + ); + + expect(sortMetricLayout(_.shuffle(sampleMetricsVisualizations))).toStrictEqual( + sampleMetricsVisualizations + ); + }); + + it('validates mergeLayoutAndMetrics function', () => { + expect(mergeLayoutAndMetrics(sampleLayout, sampleVisualizationsList)).toStrictEqual( + sampleMergedVisualizations + ); + }); + + it('validates createPrometheusMetricById function', () => { + expect(createPrometheusMetricById(samplePrometheusVisualizationId)).toStrictEqual( + samplePrometheusVisualizationComponent + ); + }); + + it('validates updateMetricsWithSelections function', () => { + expect( + updateMetricsWithSelections(samplePrometheusVisualizationComponent, 'now-1d', 'now', '1h') + ).toStrictEqual(samplePrometheusSampleUpdateWithSelections); + + expect(updateMetricsWithSelections(sampleSavedMetric, 'now-30m', 'now', '1m')).toStrictEqual( + sampleSavedMetricUpdate + ); + }); +}); diff --git a/dashboards-observability/public/components/metrics/redux/slices/metrics_slice.ts b/dashboards-observability/public/components/metrics/redux/slices/metrics_slice.ts index 7d3c9e6b0..60ec7821f 100644 --- a/dashboards-observability/public/components/metrics/redux/slices/metrics_slice.ts +++ b/dashboards-observability/public/components/metrics/redux/slices/metrics_slice.ts @@ -8,7 +8,12 @@ import { PPL_PROMETHEUS_CATALOG_REQUEST, REDUX_SLICE_METRICS, } from '../../../../../common/constants/metrics'; -import { pplServiceRequestor, getVisualizations, getNewVizDimensions } from '../../helpers/utils'; +import { + pplServiceRequestor, + getVisualizations, + getNewVizDimensions, + sortMetricLayout, +} from '../../helpers/utils'; import PPLService from '../../../../services/requests/ppl'; import { MetricType } from '../../../../../common/types/metrics'; @@ -78,11 +83,7 @@ const updateLayoutBySelection = (state: any, newMetric: any) => { }; const updateLayoutByDeSelection = (state: any, newMetric: any) => { - const sortedMetricsLayout = state.metricsLayout.sort((a: MetricType, b: MetricType) => { - if (a.y > b.y) return 1; - if (a.y < b.y) return -1; - else return 0; - }); + const sortedMetricsLayout = sortMetricLayout(state.metricsLayout); let newMetricsLayout = [] as MetricType[]; let heightSubtract = 0; @@ -98,6 +99,15 @@ const updateLayoutByDeSelection = (state: any, newMetric: any) => { state.metricsLayout = newMetricsLayout; }; +const filterDeletedLayoutIds = (state: any, payload: any) => { + let deletedMetricIds: string[] = []; + const payloadIds = payload.map((metric: any) => metric.id); + state.metricsLayout.map((metricLayout: MetricType) => { + if (!payloadIds.includes(metricLayout.id)) deletedMetricIds.push(metricLayout.id); + }); + deletedMetricIds.map((metricId: string) => updateLayoutByDeSelection(state, { id: metricId })); +}; + export const metricSlice = createSlice({ name: REDUX_SLICE_METRICS, initialState, @@ -117,6 +127,7 @@ export const metricSlice = createSlice({ extraReducers: (builder) => { builder.addCase(loadMetrics.fulfilled, (state, { payload }) => { state.metrics = payload; + filterDeletedLayoutIds(state, payload); }); }, }); diff --git a/dashboards-observability/public/components/metrics/sidebar/__tests__/__snapshots__/searchbar.test.tsx.snap b/dashboards-observability/public/components/metrics/sidebar/__tests__/__snapshots__/searchbar.test.tsx.snap new file mode 100644 index 000000000..0dd14e663 --- /dev/null +++ b/dashboards-observability/public/components/metrics/sidebar/__tests__/__snapshots__/searchbar.test.tsx.snap @@ -0,0 +1,323 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Search Bar Component Search Side Bar Component with available metrics 1`] = ` + +
+ + +
+ +
+ + + +
+
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +`; + +exports[`Search Bar Component Search Side Bar Component with no available metrics 1`] = ` + +
+ + +
+ +
+ + + +
+
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +`; diff --git a/dashboards-observability/public/components/metrics/sidebar/__tests__/__snapshots__/sidebar.test.tsx.snap b/dashboards-observability/public/components/metrics/sidebar/__tests__/__snapshots__/sidebar.test.tsx.snap new file mode 100644 index 000000000..9cee54555 --- /dev/null +++ b/dashboards-observability/public/components/metrics/sidebar/__tests__/__snapshots__/sidebar.test.tsx.snap @@ -0,0 +1,427 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Side Bar Component renders Side Bar Component 1`] = ` + + + + + +
+ + + Recently Created Metrics + + + } + id="recentlyCreatedMetricsSelector" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + paddingSize="xs" + > +
+
+ +
+
+ +
+
+
    +
+
+
+
+
+
+ +
+ + + + Selected Metrics + + + } + id="selectedMetricsSelector" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + paddingSize="xs" + > +
+
+ +
+
+ +
+
+
    +
+
+
+
+
+
+ +
+ + + + Available Metrics + + + } + id="availableMetricsSelector" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + paddingSize="xs" + > +
+
+ +
+
+ +
+
+
    +
+
+
+
+
+
+
+
+
+
+
+
+`; diff --git a/dashboards-observability/public/components/metrics/sidebar/__tests__/searchbar.test.tsx b/dashboards-observability/public/components/metrics/sidebar/__tests__/searchbar.test.tsx new file mode 100644 index 000000000..de824be1a --- /dev/null +++ b/dashboards-observability/public/components/metrics/sidebar/__tests__/searchbar.test.tsx @@ -0,0 +1,45 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { configure, mount } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import React from 'react'; +import { waitFor } from '@testing-library/react'; +import { SearchBar } from '../search_bar'; +import { sampleAllAvailableMetrics } from '../../../../../test/metrics_contants'; + +describe('Search Bar Component', () => { + configure({ adapter: new Adapter() }); + + it('Search Side Bar Component with no available metrics', async () => { + const allAvailableMetrics: any = []; + const handleAddMetric = jest.fn(); + + const wrapper = mount( + + ); + + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); + + it('Search Side Bar Component with available metrics', async () => { + const allAvailableMetrics = sampleAllAvailableMetrics; + const handleAddMetric = jest.fn(); + + const wrapper = mount( + + ); + + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); +}); diff --git a/dashboards-observability/public/components/metrics/sidebar/__tests__/sidebar.test.tsx b/dashboards-observability/public/components/metrics/sidebar/__tests__/sidebar.test.tsx new file mode 100644 index 000000000..1cf39ca1b --- /dev/null +++ b/dashboards-observability/public/components/metrics/sidebar/__tests__/sidebar.test.tsx @@ -0,0 +1,40 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { configure, mount } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import React from 'react'; +import { waitFor } from '@testing-library/react'; +import httpClientMock from '../../../../../test/__mocks__/httpClientMock'; +import PPLService from '../../../../services/requests/ppl'; +import { applyMiddleware, createStore } from '@reduxjs/toolkit'; +import rootReducer from '../../../../framework/redux/reducers'; +import { Provider } from 'react-redux'; +import { Sidebar } from '../sidebar'; +import thunk from 'redux-thunk'; + +describe('Side Bar Component', () => { + configure({ adapter: new Adapter() }); + const store = createStore(rootReducer, applyMiddleware(thunk)); + + it('renders Side Bar Component', async () => { + httpClientMock.get = jest.fn(); + + const http = httpClientMock; + const pplService = new PPLService(httpClientMock); + + const wrapper = mount( + + + + ); + + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); +}); diff --git a/dashboards-observability/public/components/metrics/sidebar/search_bar.tsx b/dashboards-observability/public/components/metrics/sidebar/search_bar.tsx index 9a6781776..9f3a7dafd 100644 --- a/dashboards-observability/public/components/metrics/sidebar/search_bar.tsx +++ b/dashboards-observability/public/components/metrics/sidebar/search_bar.tsx @@ -4,7 +4,7 @@ */ import { EuiGlobalToastList, EuiSearchBar, EuiToast } from '@elastic/eui'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { pplServiceRequestor } from '../helpers/utils'; interface ISearchBarProps { diff --git a/dashboards-observability/public/components/metrics/top_menu/__tests__/__snapshots__/metrics_export_panel.test.tsx.snap b/dashboards-observability/public/components/metrics/top_menu/__tests__/__snapshots__/metrics_export_panel.test.tsx.snap new file mode 100644 index 000000000..c13ac42f8 --- /dev/null +++ b/dashboards-observability/public/components/metrics/top_menu/__tests__/__snapshots__/metrics_export_panel.test.tsx.snap @@ -0,0 +1,315 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Export Metrics Panel Component renders Export Metrics Panel Component 1`] = ` + + +
+ +
+
+ + + +
+
+ +
+ + +
+
+
+

+ Select dashboards/applications +

+ +
+ +
+
+ +
+ +
+ + + +
+
+
+
+ + +
+ + +
+ Search existing dashboards or applications by name +
+
+
+
+ +
+ + +`; diff --git a/dashboards-observability/public/components/metrics/top_menu/__tests__/__snapshots__/top_menu.test.tsx.snap b/dashboards-observability/public/components/metrics/top_menu/__tests__/__snapshots__/top_menu.test.tsx.snap new file mode 100644 index 000000000..31818772d --- /dev/null +++ b/dashboards-observability/public/components/metrics/top_menu/__tests__/__snapshots__/top_menu.test.tsx.snap @@ -0,0 +1,3934 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Metrics Top Menu Component renders Top Menu Component when disabled in edit mode 1`] = ` + + + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + + +
+
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+
+ + } + aria-label="resolutionField" + className="resolutionSelectText" + disabled={true} + isInvalid={false} + onChange={[Function]} + prepend="Span Interval" + value={1} + > + + } + fullWidth={false} + prepend="Span Interval" + > +
+ + + +
+ + + + +
+ + +
+
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + +
+ +
+ + } + > +
+ + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + +
+
+
+
+
+ } + iconType={false} + isCustom={true} + startDateControl={
} + > +
+ +
+ + +
+
+ +
+ + +
+ + + } + delay="regular" + position="bottom" + > + + + + + + + + + +
+
+
+ + +
+ + +
+ + Save + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + + + +
+
+
+
+
+
+ +
+ +
+
+ +
+ +
+ + + + + +
+
+ +
+ + + + + +
+
+
+
+
+
+`; + +exports[`Metrics Top Menu Component renders Top Menu Component when disabled with no metric visualizations 1`] = ` + + + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + + +
+
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+
+ + } + aria-label="resolutionField" + className="resolutionSelectText" + disabled={true} + isInvalid={false} + onChange={[Function]} + prepend="Span Interval" + value={1} + > + + } + fullWidth={false} + prepend="Span Interval" + > +
+ + + +
+ + + + +
+ + +
+
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + +
+ +
+ + } + > +
+ + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + +
+
+
+
+
+ } + iconType={false} + isCustom={true} + startDateControl={
} + > +
+ +
+ + +
+
+ +
+ + +
+ + + } + delay="regular" + position="bottom" + > + + + + + + + + + +
+
+
+ + +
+ + +
+ + Save + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + + + +
+
+
+
+
+
+ +
+ +
+
+ +
+ +
+ + + + + +
+
+
+
+
+
+`; + +exports[`Metrics Top Menu Component renders Top Menu Component when enabled 1`] = ` + + + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + + +
+
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+
+ + } + aria-label="resolutionField" + className="resolutionSelectText" + disabled={false} + isInvalid={false} + onChange={[Function]} + prepend="Span Interval" + value={1} + > + + } + fullWidth={false} + prepend="Span Interval" + > +
+ + + +
+ + + + +
+ + +
+
+ + + + +
+ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + +
+ +
+ + } + > +
+ + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + +
+
+
+
+
+ } + iconType={false} + isCustom={true} + startDateControl={
} + > +
+ +
+ + +
+
+ +
+ + +
+ + + + + + + + + + + +
+
+
+ + +
+ + +
+ + Save + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + + + +
+
+
+
+
+
+ +
+ +
+
+ +
+ +
+ + + + + +
+
+
+
+
+
+`; diff --git a/dashboards-observability/public/components/metrics/top_menu/__tests__/metrics_export_panel.test.tsx b/dashboards-observability/public/components/metrics/top_menu/__tests__/metrics_export_panel.test.tsx new file mode 100644 index 000000000..00496f1be --- /dev/null +++ b/dashboards-observability/public/components/metrics/top_menu/__tests__/metrics_export_panel.test.tsx @@ -0,0 +1,63 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { configure, mount } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import React from 'react'; +import { waitFor } from '@testing-library/react'; +import httpClientMock from '../../../../../test/__mocks__/httpClientMock'; +import { + samplePanelOptions, + sampleSortedMetricsLayout, + sampleVisualizationById, +} from '../../../../../test/metrics_contants'; +import { createStore } from '@reduxjs/toolkit'; +import rootReducer from '../../../../framework/redux/reducers'; +import { Provider } from 'react-redux'; +import { HttpResponse } from '../../../../../../../src/core/public'; +import { MetricsExportPanel } from '../metrics_export_panel'; +import { EuiComboBoxOptionOption } from '@elastic/eui'; + +describe('Export Metrics Panel Component', () => { + configure({ adapter: new Adapter() }); + const store = createStore(rootReducer); + + it('renders Export Metrics Panel Component', async () => { + let httpFlag = 1; + httpClientMock.get = jest.fn(() => { + if (httpFlag == 1) { + httpFlag += 1; + return Promise.resolve((samplePanelOptions as unknown) as HttpResponse); + } else { + return Promise.resolve((sampleVisualizationById as unknown) as HttpResponse); + } + }); + + const http = httpClientMock; + const visualizationsMetaData: any = []; + const setVisualizationsMetaData = jest.fn(); + const sortedMetricsLayout = sampleSortedMetricsLayout; + const selectedPanelOptions: EuiComboBoxOptionOption[] = []; + const setSelectedPanelOptions = jest.fn(); + + const wrapper = mount( + + + + ); + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); +}); diff --git a/dashboards-observability/public/components/metrics/top_menu/__tests__/top_menu.test.tsx b/dashboards-observability/public/components/metrics/top_menu/__tests__/top_menu.test.tsx new file mode 100644 index 000000000..b34591b62 --- /dev/null +++ b/dashboards-observability/public/components/metrics/top_menu/__tests__/top_menu.test.tsx @@ -0,0 +1,183 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { configure, mount } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import React from 'react'; +import { waitFor } from '@testing-library/react'; +import httpClientMock from '../../../../../test/__mocks__/httpClientMock'; +import { sampleMetric, sampleMetricsVisualizations } from '../../../../../test/metrics_contants'; +import { createStore } from '@reduxjs/toolkit'; +import rootReducer from '../../../../framework/redux/reducers'; +import { Provider } from 'react-redux'; +import { HttpResponse } from '../../../../../../../src/core/public'; +import { TopMenu } from '../top_menu'; +import { DurationRange } from '@elastic/eui/src/components/date_picker/types'; +import SavedObjects from '../../../../services/saved_objects/event_analytics/saved_objects'; +import { MetricType } from '../../../../../common/types/metrics'; + +describe('Metrics Top Menu Component', () => { + configure({ adapter: new Adapter() }); + const store = createStore(rootReducer); + + it('renders Top Menu Component when enabled', async () => { + httpClientMock.get = jest.fn(() => Promise.resolve((sampleMetric as unknown) as HttpResponse)); + + const http = httpClientMock; + const IsTopPanelDisabled = false; + const startTime = 'now-1d'; + const endTime = 'now'; + const onDatePickerChange = jest.fn(); + const recentlyUsedRanges: DurationRange[] = []; + const editMode = false; + const setEditMode = jest.fn(); + const setEditActionType = jest.fn(); + const panelVisualizations = sampleMetricsVisualizations; + const setPanelVisualizations = jest.fn(); + const resolutionValue = 'h'; + const setResolutionValue = jest.fn(); + const spanValue = 1; + const setSpanValue = jest.fn(); + const resolutionSelectId = 'select_123'; + const savedObjects = new SavedObjects(httpClientMock); + const setToast = jest.fn(); + + const wrapper = mount( + + + + ); + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); + + it('renders Top Menu Component when disabled with no metric visualizations', async () => { + httpClientMock.get = jest.fn(); + + const http = httpClientMock; + const IsTopPanelDisabled = true; + const startTime = 'now-1d'; + const endTime = 'now'; + const onDatePickerChange = jest.fn(); + const recentlyUsedRanges: DurationRange[] = []; + const editMode = false; + const setEditMode = jest.fn(); + const setEditActionType = jest.fn(); + const panelVisualizations: MetricType[] = []; + const setPanelVisualizations = jest.fn(); + const resolutionValue = 'h'; + const setResolutionValue = jest.fn(); + const spanValue = 1; + const setSpanValue = jest.fn(); + const resolutionSelectId = 'select_123'; + const savedObjects = new SavedObjects(httpClientMock); + const setToast = jest.fn(); + + const wrapper = mount( + + + + ); + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); + + it('renders Top Menu Component when disabled in edit mode', async () => { + httpClientMock.get = jest.fn(() => Promise.resolve((sampleMetric as unknown) as HttpResponse)); + + const http = httpClientMock; + const IsTopPanelDisabled = true; + const startTime = 'now-1d'; + const endTime = 'now'; + const onDatePickerChange = jest.fn(); + const recentlyUsedRanges: DurationRange[] = []; + const editMode = true; + const setEditMode = jest.fn(); + const setEditActionType = jest.fn(); + const panelVisualizations = sampleMetricsVisualizations; + const setPanelVisualizations = jest.fn(); + const resolutionValue = 'h'; + const setResolutionValue = jest.fn(); + const spanValue = 1; + const setSpanValue = jest.fn(); + const resolutionSelectId = 'select_123'; + const savedObjects = new SavedObjects(httpClientMock); + const setToast = jest.fn(); + + const wrapper = mount( + + + + ); + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); +}); diff --git a/dashboards-observability/public/components/metrics/top_menu/top_menu.tsx b/dashboards-observability/public/components/metrics/top_menu/top_menu.tsx index f6f4e14cb..c8adb9977 100644 --- a/dashboards-observability/public/components/metrics/top_menu/top_menu.tsx +++ b/dashboards-observability/public/components/metrics/top_menu/top_menu.tsx @@ -17,7 +17,7 @@ import { import { DurationRange } from '@elastic/eui/src/components/date_picker/types'; import { useDispatch, useSelector } from 'react-redux'; import { uiSettingsService } from '../../../../common/utils'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { MetricType } from '../../../../common/types/metrics'; import { resolutionOptions } from '../../../../common/constants/metrics'; import './top_menu.scss'; diff --git a/dashboards-observability/public/components/metrics/view/__tests__/__snapshots__/empty_view.test.tsx.snap b/dashboards-observability/public/components/metrics/view/__tests__/__snapshots__/empty_view.test.tsx.snap new file mode 100644 index 000000000..c9427636e --- /dev/null +++ b/dashboards-observability/public/components/metrics/view/__tests__/__snapshots__/empty_view.test.tsx.snap @@ -0,0 +1,86 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Empty View Component renders empty view container without metrics 1`] = ` + +
+ +
+
+ +
+ + + + + + +
+ +

+ No Metrics Selected +

+ +
+ + +
+ +
+ Select a metric from the left sidepanel to view results. +
+
+
+
+
+ +
+
+ +
+ +`; diff --git a/dashboards-observability/public/components/metrics/view/__tests__/__snapshots__/metrics_grid.test.tsx.snap b/dashboards-observability/public/components/metrics/view/__tests__/__snapshots__/metrics_grid.test.tsx.snap new file mode 100644 index 000000000..e03336e60 --- /dev/null +++ b/dashboards-observability/public/components/metrics/view/__tests__/__snapshots__/metrics_grid.test.tsx.snap @@ -0,0 +1,1198 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Metrics Grid Component renders Metrics Grid Component 1`] = ` + + + + + +
+ +
+ + +
+
+ +
+ +
+ +
+ + +
+ + +
+
+
+
+ +
+ + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + +
+
+
+
+
+
+
+
+
+ + + + + + + + +
+
+
+
+
+
+ +
+ + +
+
+ +
+ +
+ +
+ + +
+ + +
+
+
+
+ +
+ + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + +
+
+
+
+
+
+
+
+
+ + + + + + + + +
+
+
+
+
+
+ +
+ + +
+
+ +
+ +
+ +
+ + +
+ prometheus.process_resident_memory_bytes +
+
+
+
+
+
+
+ +
+ + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + +
+
+
+
+
+
+
+
+
+ + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+`; diff --git a/dashboards-observability/public/components/metrics/view/__tests__/empty_view.test.tsx b/dashboards-observability/public/components/metrics/view/__tests__/empty_view.test.tsx new file mode 100644 index 000000000..d27a946ca --- /dev/null +++ b/dashboards-observability/public/components/metrics/view/__tests__/empty_view.test.tsx @@ -0,0 +1,23 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { configure, mount } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import React from 'react'; +import { EmptyMetricsView } from '../empty_view'; +import { waitFor } from '@testing-library/react'; + +describe('Empty View Component', () => { + configure({ adapter: new Adapter() }); + + it('renders empty view container without metrics', async () => { + const wrapper = mount(); + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); +}); diff --git a/dashboards-observability/public/components/metrics/view/__tests__/metrics_grid.test.tsx b/dashboards-observability/public/components/metrics/view/__tests__/metrics_grid.test.tsx new file mode 100644 index 000000000..2cf11a501 --- /dev/null +++ b/dashboards-observability/public/components/metrics/view/__tests__/metrics_grid.test.tsx @@ -0,0 +1,64 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { configure, mount } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import React from 'react'; +import { waitFor } from '@testing-library/react'; +import { MetricsGrid } from '../metrics_grid'; +import httpClientMock from '../../../../../test/__mocks__/httpClientMock'; +import { coreStartMock } from '../../../../../test/__mocks__/coreMocks'; +import PPLService from '../../../../services/requests/ppl'; +import { sampleMetric, sampleMetricsVisualizations } from '../../../../../test/metrics_contants'; +import { createStore } from '@reduxjs/toolkit'; +import rootReducer from '../../../../framework/redux/reducers'; +import { Provider } from 'react-redux'; +import { HttpResponse } from '../../../../../../../src/core/public'; + +describe('Metrics Grid Component', () => { + configure({ adapter: new Adapter() }); + const store = createStore(rootReducer); + + it('renders Metrics Grid Component', async () => { + httpClientMock.get = jest.fn(() => Promise.resolve((sampleMetric as unknown) as HttpResponse)); + + const http = httpClientMock; + const core = coreStartMock; + const panelVisualizations = sampleMetricsVisualizations; + const setPanelVisualizations = jest.fn(); + const editMode = false; + const pplService = new PPLService(httpClientMock); + const startTime = 'now-30m'; + const endTime = 'now'; + const onEditClick = jest.fn(); + const onRefresh = true; + const editActionType = 'save'; + const spanParam = '1h'; + + const wrapper = mount( + + + + ); + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); +}); diff --git a/dashboards-observability/test/metrics_contants.ts b/dashboards-observability/test/metrics_contants.ts new file mode 100644 index 000000000..2e8c08da6 --- /dev/null +++ b/dashboards-observability/test/metrics_contants.ts @@ -0,0 +1,331 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const sampleMetricsVisualizations = [ + { + h: 2, + id: 'Y4muP4QBiaYaSxpXk7r8', + metricType: 'savedCustomMetric', + savedVisualizationId: 'Y4muP4QBiaYaSxpXk7r8', + w: 12, + x: 0, + y: 0, + }, + { + h: 2, + id: 'tomAP4QBiaYaSxpXALls', + metricType: 'savedCustomMetric', + savedVisualizationId: 'tomAP4QBiaYaSxpXALls', + w: 12, + x: 0, + y: 2, + }, + { + h: 2, + id: 'prometheus.process_resident_memory_bytes', + metricType: 'prometheusMetric', + savedVisualizationId: 'prometheus.process_resident_memory_bytes', + w: 12, + x: 0, + y: 4, + }, +]; + +export const sampleMetric = { + name: 'new metric', + description: '', + query: 'source = opensearch_dashboards_sample_data_logs | stats count() by span(timestamp,1h)', + type: 'line', + selected_date_range: { + start: 'now-1d', + end: 'now', + text: '', + }, + selected_timestamp: { + name: 'timestamp', + type: 'timestamp', + }, + selected_fields: { + text: '', + tokens: [], + }, + user_configs: '{}', + sub_type: 'metric', + units_of_measure: 'kilometers (k)', +}; + +export const sampleSortedMetricsLayout = [ + { + h: 2, + id: 'Y4muP4QBiaYaSxpXk7r8', + metricType: 'savedCustomMetric', + savedVisualizationId: 'Y4muP4QBiaYaSxpXk7r8', + w: 12, + x: 0, + y: 0, + }, + { + h: 2, + id: 'prometheus.process_resident_memory_bytes', + metricType: 'prometheusMetric', + savedVisualizationId: 'prometheus.process_resident_memory_bytes', + w: 12, + x: 0, + y: 2, + }, +]; + +export const samplePanelOptions = [ + { + dateCreated: 1667512665139, + dateModified: 1667513726084, + id: 'xImAP4QBiaYaSxpXBLkz', + name: '[Logs] Web traffic Panel', + }, + { + dateCreated: 1667512677437, + dateModified: 1667525552909, + id: 'zYmAP4QBiaYaSxpXNLk9', + name: 'panel1', + }, +]; + +export const sampleVisualizationById = { + id: 'Y4muP4QBiaYaSxpXk7r8', + name: 'new metric', + query: 'source = opensearch_dashboards_sample_data_logs | stats count() by span(timestamp,1h)', + type: 'line', + timeField: 'timestamp', + selected_date_range: {}, + selected_fields: {}, + user_configs: {}, + sub_type: 'metric', + units_of_measure: 'kilometers (k)', +}; + +export const sampleAllAvailableMetrics = [ + { + id: 'HIlAQYQBiaYaSxpXJ73K', + name: '[Prometheus Metric] prometheus.process_resident_memory_bytes', + catalog: 'CUSTOM_METRICS', + type: 'line', + recentlyCreated: true, + }, + { + id: 'Y4muP4QBiaYaSxpXk7r8', + name: 'new metric', + catalog: 'CUSTOM_METRICS', + type: 'line', + recentlyCreated: false, + }, + { + id: 'HolAQYQBiaYaSxpXKL0T', + name: '[Prometheus Metric] prometheus.go_memstats_heap_sys_bytes', + catalog: 'CUSTOM_METRICS', + type: 'line', + recentlyCreated: true, + }, + { + id: 'tomAP4QBiaYaSxpXALls', + name: '[Logs] Average ram usage per day by windows os ', + catalog: 'CUSTOM_METRICS', + type: 'line', + recentlyCreated: false, + }, + { + id: 'prometheus.process_resident_memory_bytes', + name: 'prometheus.process_resident_memory_bytes', + catalog: 'prometheus', + type: 'gauge', + recentlyCreated: false, + }, +]; + +export const samplePanelVisualizations1 = [ + { + id: 'Y4muP4QBiaYaSxpXk7r8', + savedVisualizationId: 'Y4muP4QBiaYaSxpXk7r8', + x: 0, + y: 0, + h: 2, + w: 12, + metricType: 'savedCustomMetric', + }, +]; + +export const samplenewDimensions1 = { + x: 0, + y: 2, + w: 12, + h: 2, +}; + +export const samplePanelVisualizations2 = [ + { + id: 'Y4muP4QBiaYaSxpXk7r8', + savedVisualizationId: 'Y4muP4QBiaYaSxpXk7r8', + x: 0, + y: 0, + h: 2, + w: 12, + metricType: 'savedCustomMetric', + }, + { + id: 'tomAP4QBiaYaSxpXALls', + savedVisualizationId: 'tomAP4QBiaYaSxpXALls', + x: 0, + y: 2, + h: 2, + w: 12, + metricType: 'savedCustomMetric', + }, +]; + +export const samplenewDimensions2 = { + x: 0, + y: 4, + w: 12, + h: 2, +}; + +export const samplePrometheusVisualizationId = 'prometheus.process_resident_memory_bytes'; + +export const samplePrometheusVisualizationComponent = { + name: '[Prometheus Metric] prometheus.process_resident_memory_bytes', + description: '', + query: + 'source = prometheus.process_resident_memory_bytes | stats avg(@value) by span(@timestamp,1h)', + type: 'line', + timeField: '@timestamp', + selected_fields: { + text: '', + tokens: [], + }, + sub_type: 'metric', + units_of_measure: 'hours (h)', + user_configs: {}, +}; + +export const sampleVisualizationsList = [ + { + id: 'panel_viz_ed409e13-4759-4e0f-9bc1-6ae32999318e', + savedVisualizationId: 'savedCustomMetric', + x: 0, + y: 0, + w: 6, + h: 4, + }, + { + id: 'panel_viz_f59ad102-943e-48d9-9c0a-3df7055070a3', + savedVisualizationId: 'prometheusMetric', + x: 0, + y: 4, + w: 6, + h: 4, + }, +]; + +export const sampleLayout = [ + { i: 'panel_viz_ed409e13-4759-4e0f-9bc1-6ae32999318e', x: 0, y: 0, w: 3, h: 2 }, + { i: 'panel_viz_f59ad102-943e-48d9-9c0a-3df7055070a3', x: 3, y: 0, w: 6, h: 4 }, +]; + +export const sampleMergedVisualizations = [ + { + id: 'panel_viz_ed409e13-4759-4e0f-9bc1-6ae32999318e', + savedVisualizationId: 'savedCustomMetric', + x: 0, + y: 0, + w: 3, + h: 2, + }, + { + id: 'panel_viz_f59ad102-943e-48d9-9c0a-3df7055070a3', + savedVisualizationId: 'prometheusMetric', + x: 3, + y: 0, + w: 6, + h: 4, + }, +]; + +export const samplePrometheusSampleUpdateWithSelections = { + dateRange: ['now-1d', 'now'], + description: '', + fields: [], + name: '[Prometheus Metric] prometheus.process_resident_memory_bytes', + query: + 'source = prometheus.process_resident_memory_bytes | stats avg(@value) by span(@timestamp,1h)', + subType: 'metric', + timestamp: '@timestamp', + type: 'line', + unitsOfMeasure: 'hours (h)', + userConfigs: '{}', +}; + +export const sampleSavedMetric = { + id: 'tomAP4QBiaYaSxpXALls', + name: '[Logs] Average ram usage per day by windows os ', + query: + "source = opensearch_dashboards_sample_data_logs | where match(machine.os,'win') | stats avg(machine.ram) by span(timestamp,1h)", + type: 'line', + timeField: 'timestamp', + selected_date_range: { + start: 'now-1d', + end: 'now', + text: '', + }, + selected_fields: { + text: '', + tokens: [], + }, + user_configs: { + dataConfig: { + series: [ + { + label: 'machine.ram', + name: 'machine.ram', + aggregation: 'avg', + customLabel: '', + }, + ], + dimensions: [], + span: { + time_field: [ + { + name: 'timestamp', + type: 'timestamp', + label: 'timestamp', + }, + ], + unit: [ + { + text: 'Day', + value: 'd', + label: 'Day', + }, + ], + interval: '1', + }, + }, + }, + sub_type: 'metric', + units_of_measure: 'hours (h)', +}; + +export const sampleSavedMetricUpdate = { + dateRange: ['now-30m', 'now'], + description: undefined, + fields: [], + name: '[Logs] Average ram usage per day by windows os ', + query: + "source = opensearch_dashboards_sample_data_logs | where match(machine.os,'win') | stats avg(machine.ram) by span(timestamp,1m)", + subType: 'metric', + timestamp: 'timestamp', + type: 'line', + unitsOfMeasure: 'hours (h)', + userConfigs: + '{"dataConfig":{"series":[{"label":"machine.ram","name":"machine.ram","aggregation":"avg","customLabel":""}],"dimensions":[],"span":{"time_field":[{"name":"timestamp","type":"timestamp","label":"timestamp"}],"unit":[{"text":"Day","value":"d","label":"Day"}],"interval":"1"}}}', +};