diff --git a/frontend/src/container/LogsExplorerList/index.tsx b/frontend/src/container/LogsExplorerList/index.tsx
index c864aa355a..c4bba5291b 100644
--- a/frontend/src/container/LogsExplorerList/index.tsx
+++ b/frontend/src/container/LogsExplorerList/index.tsx
@@ -173,7 +173,9 @@ function LogsExplorerList({
{!isLoading && !isError && logs.length > 0 && (
<>
- {renderContent}
+
+ {renderContent}
+
handleModeChange(PANEL_TYPES.LIST)}
+ data-testid="logs-list-view"
>
List view
@@ -551,6 +552,7 @@ function LogsExplorerViews({
: 'tab'
}
onClick={(): void => handleModeChange(PANEL_TYPES.TIME_SERIES)}
+ data-testid="time-series-view"
>
Time series
@@ -561,6 +563,7 @@ function LogsExplorerViews({
selectedPanelType === PANEL_TYPES.TABLE ? 'selected_view tab' : 'tab'
}
onClick={(): void => handleModeChange(PANEL_TYPES.TABLE)}
+ data-testid="table-view"
>
Table
diff --git a/frontend/src/container/OptionsMenu/AddColumnField/index.tsx b/frontend/src/container/OptionsMenu/AddColumnField/index.tsx
index 5b5382a016..e6f082e500 100644
--- a/frontend/src/container/OptionsMenu/AddColumnField/index.tsx
+++ b/frontend/src/container/OptionsMenu/AddColumnField/index.tsx
@@ -1,6 +1,5 @@
import { SearchOutlined } from '@ant-design/icons';
-import { Input, Spin } from 'antd';
-import Typography from 'antd/es/typography/Typography';
+import { Input, Spin, Typography } from 'antd';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useTranslation } from 'react-i18next';
diff --git a/frontend/src/container/QueryBuilder/components/ToolbarActions/LeftToolbarActions.tsx b/frontend/src/container/QueryBuilder/components/ToolbarActions/LeftToolbarActions.tsx
index 7485c47563..fc88c7f59a 100644
--- a/frontend/src/container/QueryBuilder/components/ToolbarActions/LeftToolbarActions.tsx
+++ b/frontend/src/container/QueryBuilder/components/ToolbarActions/LeftToolbarActions.tsx
@@ -39,7 +39,7 @@ export default function LeftToolbarActions({
)}
onClick={(): void => onChangeSelectedView(SELECTED_VIEWS.SEARCH)}
>
-
+
@@ -52,7 +52,7 @@ export default function LeftToolbarActions({
)}
onClick={(): void => onChangeSelectedView(SELECTED_VIEWS.QUERY_BUILDER)}
>
-
+
@@ -66,7 +66,7 @@ export default function LeftToolbarActions({
)}
onClick={(): void => onChangeSelectedView(SELECTED_VIEWS.CLICKHOUSE)}
>
-
+
)}
diff --git a/frontend/src/container/TracesExplorer/ListView/utils.tsx b/frontend/src/container/TracesExplorer/ListView/utils.tsx
index a94e26d216..6a28acfba9 100644
--- a/frontend/src/container/TracesExplorer/ListView/utils.tsx
+++ b/frontend/src/container/TracesExplorer/ListView/utils.tsx
@@ -1,6 +1,5 @@
-import { Tag } from 'antd';
+import { Tag, Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
-import Typography from 'antd/es/typography/Typography';
import ROUTES from 'constants/routes';
import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util';
import { formUrlParams } from 'container/TraceDetail/utils';
diff --git a/frontend/src/mocks-server/__mockdata__/logs_query_range.ts b/frontend/src/mocks-server/__mockdata__/logs_query_range.ts
new file mode 100644
index 0000000000..3b67e48945
--- /dev/null
+++ b/frontend/src/mocks-server/__mockdata__/logs_query_range.ts
@@ -0,0 +1,45 @@
+export const logsQueryRangeSuccessResponse = {
+ status: 'success',
+ data: {
+ resultType: '',
+ result: [
+ {
+ queryName: 'A',
+ series: null,
+ list: [
+ {
+ timestamp: '2024-02-15T21:20:22Z',
+ data: {
+ attributes_bool: {},
+ attributes_float64: {},
+ attributes_int64: {},
+ attributes_string: {
+ container_id: 'container_id',
+ container_name: 'container_name',
+ driver: 'driver',
+ eta: '2m0s',
+ location: 'frontend',
+ log_level: 'INFO',
+ message: 'Dispatch successful',
+ service: 'frontend',
+ span_id: 'span_id',
+ trace_id: 'span_id',
+ },
+ body:
+ '2024-02-15T21:20:22.035Z\tINFO\tfrontend\tDispatch successful\t{"service": "frontend", "trace_id": "span_id", "span_id": "span_id", "driver": "driver", "eta": "2m0s"}',
+ id: 'id',
+ resources_string: {
+ 'container.name': 'container_name',
+ },
+ severity_number: 0,
+ severity_text: '',
+ span_id: '',
+ trace_flags: 0,
+ trace_id: '',
+ },
+ },
+ ],
+ },
+ ],
+ },
+};
diff --git a/frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx b/frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx
new file mode 100644
index 0000000000..072bfe98ca
--- /dev/null
+++ b/frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx
@@ -0,0 +1,147 @@
+import { render, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { logsQueryRangeSuccessResponse } from 'mocks-server/__mockdata__/logs_query_range';
+import { server } from 'mocks-server/server';
+import { rest } from 'msw';
+import { QueryBuilderProvider } from 'providers/QueryBuilder';
+import MockQueryClientProvider from 'providers/test/MockQueryClientProvider';
+import { I18nextProvider } from 'react-i18next';
+import { Provider } from 'react-redux';
+import { MemoryRouter } from 'react-router-dom';
+// https://virtuoso.dev/mocking-in-tests/
+import { VirtuosoMockContext } from 'react-virtuoso';
+import i18n from 'ReactI18';
+import store from 'store';
+
+import LogsExplorer from '..';
+
+const queryRangeURL = 'http://localhost/api/v3/query_range';
+// mocking the graph components in this test as this should be handled separately
+jest.mock(
+ 'container/TimeSeriesView/TimeSeriesView',
+ () =>
+ // eslint-disable-next-line func-names, @typescript-eslint/explicit-function-return-type, react/display-name
+ function () {
+ return Time Series Chart
;
+ },
+);
+jest.mock(
+ 'container/LogsExplorerChart',
+ () =>
+ // eslint-disable-next-line func-names, @typescript-eslint/explicit-function-return-type, react/display-name
+ function () {
+ return Histogram Chart
;
+ },
+);
+
+jest.mock('constants/panelTypes', () => ({
+ AVAILABLE_EXPORT_PANEL_TYPES: ['graph', 'table'],
+}));
+
+jest.mock('d3-interpolate', () => ({
+ interpolate: jest.fn(),
+}));
+
+describe('Logs Explorer Tests', () => {
+ test('Logs Explorer default view test without data', async () => {
+ const {
+ getByText,
+ getByRole,
+ queryByText,
+ getByTestId,
+ queryByTestId,
+ } = render(
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ // check the presence of histogram chart
+ expect(getByText('Histogram Chart')).toBeInTheDocument();
+
+ // toggle the chart and check it gets removed from the DOM
+ const histogramToggle = getByRole('switch');
+ await userEvent.click(histogramToggle);
+ expect(queryByText('Histogram Chart')).not.toBeInTheDocument();
+
+ // check the presence of search bar and query builder and absence of clickhouse
+ const searchView = getByTestId('search-view');
+ expect(searchView).toBeInTheDocument();
+ const queryBuilderView = getByTestId('query-builder-view');
+ expect(queryBuilderView).toBeInTheDocument();
+ const clickhouseView = queryByTestId('clickhouse-view');
+ expect(clickhouseView).not.toBeInTheDocument();
+
+ // check the presence of List View / Time Series View / Table View
+ const listView = getByTestId('logs-list-view');
+ const timeSeriesView = getByTestId('time-series-view');
+ const tableView = getByTestId('table-view');
+ expect(listView).toBeInTheDocument();
+ expect(timeSeriesView).toBeInTheDocument();
+ expect(tableView).toBeInTheDocument();
+
+ // check the presence of old logs explorer CTA
+ const oldLogsCTA = getByText('Switch to Old Logs Explorer');
+ expect(oldLogsCTA).toBeInTheDocument();
+ });
+
+ test('Logs Explorer Page should render with data', async () => {
+ // mocking the query range API to return the logs
+ server.use(
+ rest.post(queryRangeURL, (req, res, ctx) =>
+ res(ctx.status(200), ctx.json(logsQueryRangeSuccessResponse)),
+ ),
+ );
+ const { queryByText, queryByTestId } = render(
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ // check for loading state to be not present
+ await waitFor(() =>
+ expect(
+ queryByText(
+ `Just a bit of patience, just a little bit’s enough ⎯ we’re getting your logs!`,
+ ),
+ ).not.toBeInTheDocument(),
+ );
+
+ // check for no data state to not be present
+ await waitFor(() =>
+ expect(queryByText('No logs yet.')).not.toBeInTheDocument(),
+ );
+
+ // check for the data container loaded
+ await waitFor(() =>
+ expect(queryByTestId('logs-list-virtuoso')).toBeInTheDocument(),
+ );
+
+ // check for data being present in the UI
+ expect(
+ queryByText(
+ '2024-02-15T21:20:22.035Z INFO frontend Dispatch successful {"service": "frontend", "trace_id": "span_id", "span_id": "span_id", "driver": "driver", "eta": "2m0s"}',
+ ),
+ ).toBeInTheDocument();
+ });
+});