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(); + }); +});