diff --git a/common/constants.ts b/common/constants.ts index 2186516..c685599 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -8,6 +8,6 @@ export const LATENCY = 'Latency'; export const CPU_TIME = 'CPU Time'; export const MEMORY_USAGE = 'Memory Usage'; export const INDICES = 'Indices'; -export const SEARCH_TYPE = 'Search type'; -export const NODE_ID = 'Coordinator node ID'; -export const TOTAL_SHARDS = 'Total shards'; +export const SEARCH_TYPE = 'Search Type'; +export const NODE_ID = 'Coordinator Node ID'; +export const TOTAL_SHARDS = 'Total Shards'; diff --git a/public/components/__snapshots__/app.test.tsx.snap b/public/components/__snapshots__/app.test.tsx.snap index 3571697..36c6673 100644 --- a/public/components/__snapshots__/app.test.tsx.snap +++ b/public/components/__snapshots__/app.test.tsx.snap @@ -172,10 +172,10 @@ exports[` spec renders the component 1`] = ` > - Search type + Search Type @@ -215,10 +215,10 @@ exports[` spec renders the component 1`] = ` > - Coordinator node ID + Coordinator Node ID @@ -566,9 +566,9 @@ exports[` spec renders the component 1`] = ` > - Search type + Search Type @@ -591,9 +591,9 @@ exports[` spec renders the component 1`] = ` > - Coordinator node ID + Coordinator Node ID @@ -616,9 +616,9 @@ exports[` spec renders the component 1`] = ` > - Total shards + Total Shards diff --git a/public/pages/Configuration/Configuration.test.tsx b/public/pages/Configuration/Configuration.test.tsx new file mode 100644 index 0000000..1a3948b --- /dev/null +++ b/public/pages/Configuration/Configuration.test.tsx @@ -0,0 +1,128 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import { MemoryRouter } from 'react-router-dom'; +import Configuration from './Configuration'; + +const mockConfigInfo = jest.fn(); +const mockCoreStart = { + chrome: { + setBreadcrumbs: jest.fn(), + }, +}; + +const defaultLatencySettings = { + isEnabled: true, + currTopN: '5', + currWindowSize: '10', + currTimeUnit: 'MINUTES', +}; +const defaultCpuSettings = { + isEnabled: false, + currTopN: '10', + currWindowSize: '1', + currTimeUnit: 'HOURS', +}; +const defaultMemorySettings = { + isEnabled: false, + currTopN: '15', + currWindowSize: '2', + currTimeUnit: 'HOURS', +}; + +const renderConfiguration = (overrides = {}) => { + render( + + + + ); +}; + +const getWindowSizeConfigurations = () => screen.getAllByRole('combobox'); +const getTopNSizeConfiguration = () => screen.getByRole('spinbutton'); +const getEnableToggle = () => screen.getByRole('switch'); + +describe('Configuration Component', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders with default settings', () => { + renderConfiguration(); + // main header + expect( + screen.getByRole('heading', { name: /Top n queries monitoring configuration settings/i }) + ).toBeInTheDocument(); + // section headers + expect(screen.getByRole('heading', { name: /Metric Type/i })).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: /Enabled/i })).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: /Value of N/i })).toBeInTheDocument(); + expect(screen.getByRole('heading', { name: /Window size/i })).toBeInTheDocument(); + // Check values for window size configurations + const selectBoxes = getWindowSizeConfigurations(); + expect(selectBoxes[0]).toHaveValue('latency'); + expect(selectBoxes[1]).toHaveValue('10'); + expect(selectBoxes[2]).toHaveValue('MINUTES'); + // Check the value for top n size configurations + expect(getTopNSizeConfiguration()).toHaveValue(5); + // Check the value for enabled switch + const enableBox = getEnableToggle(); + expect(enableBox).toBeInTheDocument(); + expect(enableBox).toBeChecked(); + }); + + it('updates state when toggling metrics and enables Save button when changes are made', () => { + renderConfiguration(); + // before toggling the metric + expect(getWindowSizeConfigurations()[0]).toHaveValue('latency'); + expect(getEnableToggle()).toBeChecked(); + // toggle the metric + fireEvent.change(getWindowSizeConfigurations()[0], { target: { value: 'cpu' } }); + // after toggling the metric + expect(getWindowSizeConfigurations()[0]).toHaveValue('cpu'); + // the enabled box should be disabled by default based on our configuration + const cpuEnableBox = getEnableToggle(); + expect(cpuEnableBox).toBeInTheDocument(); + expect(cpuEnableBox).not.toBeChecked(); + + fireEvent.click(getEnableToggle()); + expect(getEnableToggle()).toBeChecked(); + expect(screen.getByText('Save')).toBeEnabled(); + }); + + it('validates topNSize and windowSize inputs and disables Save button for invalid input', () => { + renderConfiguration(); + fireEvent.change(getTopNSizeConfiguration(), { target: { value: '101' } }); + expect(screen.queryByText('Save')).not.toBeInTheDocument(); + fireEvent.change(getWindowSizeConfigurations()[1], { target: { value: '999' } }); + expect(screen.queryByText('Save')).not.toBeInTheDocument(); + }); + + it('calls configInfo and navigates on Save button click', async () => { + renderConfiguration(); + fireEvent.change(getTopNSizeConfiguration(), { target: { value: '7' } }); + fireEvent.click(screen.getByText('Save')); + await waitFor(() => { + expect(mockConfigInfo).toHaveBeenCalledWith(false, true, 'latency', '7', '10', 'MINUTES'); + }); + }); + + it('resets state on Cancel button click', async () => { + renderConfiguration(); + fireEvent.change(getTopNSizeConfiguration(), { target: { value: '7' } }); + fireEvent.click(screen.getByText('Cancel')); + expect(getTopNSizeConfiguration()).toHaveValue(5); // Resets to initial value + }); +}); diff --git a/public/pages/QueryInsights/QueryInsights.test.tsx b/public/pages/QueryInsights/QueryInsights.test.tsx new file mode 100644 index 0000000..a8af693 --- /dev/null +++ b/public/pages/QueryInsights/QueryInsights.test.tsx @@ -0,0 +1,106 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import QueryInsights from './QueryInsights'; +import { MemoryRouter } from 'react-router-dom'; + +// Mock functions and data +const mockOnTimeChange = jest.fn(); +const mockCore = { + chrome: { + setBreadcrumbs: jest.fn(), + }, +}; + +// TODO: change to use MockQueries once https://github.com/opensearch-project/query-insights-dashboards/pull/21/files is merged +const sampleQueries = [ + { + timestamp: 1633046400000, + measurements: {}, + indices: ['index1', 'index2'], + search_type: 'QUERY_THEN_FETCH', + node_id: 'node_1', + total_shards: 5, + source: {}, + labels: {}, + phase_latency_map: {}, + task_resource_usages: [], + }, + { + timestamp: 1633132800000, + measurements: {}, + indices: ['index3'], + search_type: 'DFS_QUERY_THEN_FETCH', + node_id: 'node_2', + total_shards: 3, + source: {}, + labels: {}, + phase_latency_map: {}, + task_resource_usages: [], + }, +]; + +const renderQueryInsights = () => { + render( + + + + ); +}; + +describe('QueryInsights Component', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders the table with the correct columns and data', () => { + renderQueryInsights(); + + // Check that the table and columns render correctly + expect(document.querySelector('span[title="Timestamp"]')).toBeInTheDocument(); + expect(document.querySelector('span[title="Latency"]')).toBeInTheDocument(); + expect(document.querySelector('span[title="CPU Time"]')).toBeInTheDocument(); + expect(document.querySelector('span[title="Memory Usage"]')).toBeInTheDocument(); + expect(document.querySelector('span[title="Indices"]')).toBeInTheDocument(); + expect(document.querySelector('span[title="Search Type"]')).toBeInTheDocument(); + expect(document.querySelector('span[title="Coordinator Node ID"]')).toBeInTheDocument(); + expect(document.querySelector('span[title="Total Shards"]')).toBeInTheDocument(); + // TODO add tests for the values + }); + + it('calls setBreadcrumbs on mount', () => { + renderQueryInsights(); + expect(mockCore.chrome.setBreadcrumbs).toHaveBeenCalledWith([ + { + text: 'Query insights', + href: '/queryInsights', + onClick: expect.any(Function), + }, + ]); + }); + + it('triggers onTimeChange when the date picker changes', () => { + renderQueryInsights(); + + // Find the date picker update button + const updateButton = screen.getByRole('button', { name: /Refresh/i }); + fireEvent.click(updateButton); + + // Verify the onTimeChange callback is triggered + expect(mockOnTimeChange).toHaveBeenCalled(); + }); +}); diff --git a/types/types.ts b/types/types.ts index 01f2f59..7d249cc 100644 --- a/types/types.ts +++ b/types/types.ts @@ -3,8 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ISearchSource } from 'src/plugins/data/public'; - export interface SearchQueryRecord { timestamp: number; measurements: { @@ -14,7 +12,7 @@ export interface SearchQueryRecord { }; total_shards: number; node_id: string; - source: ISearchSource; + source: Record; labels: Record; search_type: string; indices: string[];