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[];