diff --git a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx index f34a4470a9..44378e602b 100644 --- a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx +++ b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx @@ -501,6 +501,7 @@ function ExplorerOptions({ shape="circle" onClick={hideToolbar} icon={} + data-testid="hide-toolbar" /> @@ -530,6 +531,7 @@ function ExplorerOptions({ icon={} onClick={onSaveHandler} disabled={isSaveViewLoading} + data-testid="save-view-btn" > Save this view , diff --git a/frontend/src/container/ExplorerOptions/ExplorerOptionsHideArea.tsx b/frontend/src/container/ExplorerOptions/ExplorerOptionsHideArea.tsx index a420c25ecc..efdaef1cd1 100644 --- a/frontend/src/container/ExplorerOptions/ExplorerOptionsHideArea.tsx +++ b/frontend/src/container/ExplorerOptions/ExplorerOptionsHideArea.tsx @@ -65,6 +65,7 @@ function ExplorerOptionsHideArea({ // style={{ alignSelf: 'center', marginRight: 'calc(10% - 20px)' }} className="explorer-show-btn" onClick={handleShowExplorerOption} + data-testid="show-explorer-option" >
diff --git a/frontend/src/mocks-server/__mockdata__/explorer_views.ts b/frontend/src/mocks-server/__mockdata__/explorer_views.ts index ae88071e55..b51eb2ee9c 100644 --- a/frontend/src/mocks-server/__mockdata__/explorer_views.ts +++ b/frontend/src/mocks-server/__mockdata__/explorer_views.ts @@ -77,5 +77,67 @@ export const explorerView = { }, extraData: '{"color":"#00ffd0"}', }, + { + uuid: '58b010b6-8be9-40d1-8d25-f73b5f7314ad', + name: 'success traces list view', + category: '', + createdAt: '2023-08-30T13:00:40.958011925Z', + createdBy: 'test-email', + updatedAt: '2024-04-29T13:09:06.175537361Z', + updatedBy: 'test-email', + sourcePage: 'traces', + tags: [''], + compositeQuery: { + builderQueries: { + A: { + queryName: 'A', + stepInterval: 60, + dataSource: 'traces', + aggregateOperator: 'noop', + aggregateAttribute: { + key: '', + dataType: '', + type: '', + isColumn: false, + isJSON: false, + }, + filters: { + op: 'AND', + items: [ + { + key: { + key: 'responseStatusCode', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + }, + value: '200', + op: '=', + }, + ], + }, + expression: 'A', + disabled: false, + limit: 0, + offset: 0, + pageSize: 0, + orderBy: [ + { + columnName: 'timestamp', + order: 'desc', + }, + ], + reduceTo: 'sum', + timeAggregation: 'rate', + spaceAggregation: 'sum', + ShiftBy: 0, + }, + }, + panelType: 'list', + queryType: 'builder', + }, + extraData: '{"color":"#bdff9d"}', + }, ], }; diff --git a/frontend/src/mocks-server/__mockdata__/query_range.ts b/frontend/src/mocks-server/__mockdata__/query_range.ts index 69ff7bfb66..ef55283a96 100644 --- a/frontend/src/mocks-server/__mockdata__/query_range.ts +++ b/frontend/src/mocks-server/__mockdata__/query_range.ts @@ -1,3 +1,4 @@ +/* eslint-disable sonarjs/no-duplicate-string */ import { PANEL_TYPES } from 'constants/queryBuilder'; import { QueryRangePayload } from 'types/api/metrics/getQueryRange'; import { EQueryType } from 'types/common/dashboard'; @@ -77,3 +78,245 @@ export const queryRangeSuccessResponse: QueryRangePayload = { start: 0, step: 0, }; + +export const queryRangeForTimeSeries = { + status: 'success', + data: { + resultType: '', + result: [ + { + queryName: 'A', + series: [ + { + labels: {}, + labelsArray: null, + values: [ + { + timestamp: 1721378340000, + value: '3074', + }, + { + timestamp: 1721378100000, + value: '2983', + }, + { + timestamp: 1721378040000, + value: '2978', + }, + { + timestamp: 1721378160000, + value: '2940', + }, + { + timestamp: 1721377980000, + value: '2904', + }, + { + timestamp: 1721378280000, + value: '2874', + }, + { + timestamp: 1721378220000, + value: '2667', + }, + ], + }, + ], + }, + ], + }, +}; + +export const queryRangeForListView = { + status: 'success', + data: { + resultType: '', + result: [ + { + queryName: 'A', + list: [ + { + timestamp: '2024-07-19T08:39:59.949129915Z', + data: { + dbName: '', + durationNano: 790949390, + httpMethod: '', + name: 'authenticate_check_db', + responseStatusCode: '', + serviceName: 'demo-app', + spanID: '5704353737b6778e', + statusCode: 0, + traceID: 'a364a8e15af3e9a8c866e0528db8b637', + }, + }, + { + timestamp: '2024-07-19T08:39:59.506524482Z', + data: { + dbName: '', + durationNano: 1375203118, + httpMethod: '', + name: 'check cart in cache', + responseStatusCode: '', + serviceName: 'demo-app', + spanID: '2134bb1165c928aa', + statusCode: 0, + traceID: '7b565bc351bac2a12c004d92d3a809b1', + }, + }, + { + timestamp: '2024-07-19T08:39:58.735245Z', + data: { + dbName: '', + durationNano: 55306000, + httpMethod: 'GET', + name: 'HTTP GET', + responseStatusCode: '200', + serviceName: 'frontend', + spanID: '772c4d29dd9076ac', + statusCode: 0, + traceID: '0000000000000000344ded1387b08a7e', + }, + }, + ], + }, + ], + }, +}; + +export const queryRangeForTableView = { + status: 'success', + data: { + resultType: '', + result: [ + { + queryName: 'A', + series: [ + { + labels: {}, + labelsArray: null, + values: [ + { + timestamp: 1721583834000, + value: '87798', + }, + ], + }, + ], + }, + ], + }, +}; + +export const queryRangeForTraceView = { + status: 'success', + data: { + resultType: '', + result: [ + { + queryName: 'A', + list: [ + { + timestamp: '0001-01-01T00:00:00Z', + data: { + span_count: 8, + 'subQuery.durationNano': 7245231266, + 'subQuery.name': 'home', + 'subQuery.serviceName': 'demo-app', + traceID: '5765b60ba7cc4ddafe8bdaa9c1b4b246', + }, + }, + { + timestamp: '0001-01-01T00:00:00Z', + data: { + span_count: 8, + 'subQuery.durationNano': 7218609120, + 'subQuery.name': 'home', + 'subQuery.serviceName': 'demo-app', + traceID: '1593c896d96cc6b2478bb95dcc01e3f5', + }, + }, + { + timestamp: '0001-01-01T00:00:00Z', + data: { + span_count: 8, + 'subQuery.durationNano': 7217156051, + 'subQuery.name': 'home', + 'subQuery.serviceName': 'demo-app', + traceID: 'dcd145ed13937795c5e2ee8618ec7e32', + }, + }, + { + timestamp: '0001-01-01T00:00:00Z', + data: { + span_count: 8, + 'subQuery.durationNano': 7054152134, + 'subQuery.name': 'home', + 'subQuery.serviceName': 'demo-app', + traceID: 'd9ceed0a6b23ed4b3bff664e2b303382', + }, + }, + { + timestamp: '0001-01-01T00:00:00Z', + data: { + span_count: 8, + 'subQuery.durationNano': 7052324178, + 'subQuery.name': 'home', + 'subQuery.serviceName': 'demo-app', + traceID: 'f76f1acc10a9149121c2bf715d1f92c5', + }, + }, + { + timestamp: '0001-01-01T00:00:00Z', + data: { + span_count: 8, + 'subQuery.durationNano': 6998186102, + 'subQuery.name': 'home', + 'subQuery.serviceName': 'demo-app', + traceID: '1e3acf6649147117836cfdde66e2bde5', + }, + }, + { + timestamp: '0001-01-01T00:00:00Z', + data: { + span_count: 8, + 'subQuery.durationNano': 6898849195, + 'subQuery.name': 'home', + 'subQuery.serviceName': 'demo-app', + traceID: '035b210595493adcef4c7f297a427bb0', + }, + }, + { + timestamp: '0001-01-01T00:00:00Z', + data: { + span_count: 8, + 'subQuery.durationNano': 6829435795, + 'subQuery.name': 'home', + 'subQuery.serviceName': 'demo-app', + traceID: '4ae4d4d082fc6d7a20d90ae0b1d0fff1', + }, + }, + { + timestamp: '0001-01-01T00:00:00Z', + data: { + span_count: 8, + 'subQuery.durationNano': 6790765891, + 'subQuery.name': 'home', + 'subQuery.serviceName': 'demo-app', + traceID: '7975c032b430ac63479e5d578c1f0edd', + }, + }, + { + timestamp: '0001-01-01T00:00:00Z', + data: { + span_count: 8, + 'subQuery.durationNano': 6786616927, + 'subQuery.name': 'home', + 'subQuery.serviceName': 'demo-app', + traceID: 'ce9d3e5d66dbdd41d46d519b615cce52', + }, + }, + ], + }, + ], + }, +}; diff --git a/frontend/src/mocks-server/handlers.ts b/frontend/src/mocks-server/handlers.ts index 87287a0d1a..22978717a5 100644 --- a/frontend/src/mocks-server/handlers.ts +++ b/frontend/src/mocks-server/handlers.ts @@ -210,6 +210,16 @@ export const handlers = [ res(ctx.status(200), ctx.json(explorerView)), ), + rest.post('http://localhost/api/v1/explorer/views', (req, res, ctx) => + res( + ctx.status(200), + ctx.json({ + status: 'success', + data: '7731ece1-3fa3-4ed4-8b1c-58b4c28723b2', + }), + ), + ), + rest.post('http://localhost/api/v1/event', (req, res, ctx) => res( ctx.status(200), diff --git a/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx b/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx index 1dcaaaa4cf..0622a365c2 100644 --- a/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx +++ b/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx @@ -1,27 +1,56 @@ /* eslint-disable sonarjs/no-duplicate-string */ -/* eslint-disable no-restricted-syntax */ -/* eslint-disable no-await-in-loop */ import userEvent from '@testing-library/user-event'; import { initialQueriesMap, initialQueryBuilderFormValues, + PANEL_TYPES, } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; import * as compositeQueryHook from 'hooks/queryBuilder/useGetCompositeQueryParam'; +import { + queryRangeForListView, + queryRangeForTableView, + queryRangeForTimeSeries, + queryRangeForTraceView, +} from 'mocks-server/__mockdata__/query_range'; +import { server } from 'mocks-server/server'; +import { rest } from 'msw'; import { QueryBuilderContext } from 'providers/QueryBuilder'; -import { fireEvent, render, screen, waitFor, within } from 'tests/test-utils'; -import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; -import { Query } from 'types/api/queryBuilder/queryBuilderData'; +import { + act, + fireEvent, + render, + screen, + waitFor, + within, +} from 'tests/test-utils'; import TracesExplorer from '..'; import { Filter } from '../Filter/Filter'; import { AllTraceFilterKeyValue } from '../Filter/filterUtils'; +import { + checkForSectionContent, + checkIfSectionIsNotOpen, + checkIfSectionIsOpen, + compositeQuery, + defaultClosedSections, + defaultOpenSections, + optionMenuReturn, + qbProviderValue, + redirectWithQueryBuilderData, +} from './testUtils'; + +const historyPush = jest.fn(); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useLocation: (): { pathname: string } => ({ pathname: `${process.env.FRONTEND_API_ENDPOINT}${ROUTES.TRACES_EXPLORER}/`, }), + useHistory: (): any => ({ + ...jest.requireActual('react-router-dom').useHistory(), + push: historyPush, + }), })); jest.mock('uplot', () => { @@ -40,6 +69,25 @@ jest.mock('uplot', () => { }; }); +jest.mock( + 'components/Uplot/Uplot', + () => + function MockUplot(): JSX.Element { + return
MockUplot
; + }, +); + +const successNotification = jest.fn(); +jest.mock('hooks/useNotifications', () => ({ + __esModule: true, + useNotifications: jest.fn(() => ({ + notifications: { + success: successNotification, + error: jest.fn(), + }, + })), +})); + jest.mock( 'container/TopNav/DateTimeSelectionV2/index.tsx', () => @@ -48,84 +96,12 @@ jest.mock( }, ); -function checkIfSectionIsOpen( - getByTestId: (testId: string) => HTMLElement, - panelName: string, -): void { - const section = getByTestId(`collapse-${panelName}`); - expect(section.querySelector('.ant-collapse-item-active')).not.toBeNull(); -} - -function checkIfSectionIsNotOpen( - getByTestId: (testId: string) => HTMLElement, - panelName: string, -): void { - const section = getByTestId(`collapse-${panelName}`); - expect(section.querySelector('.ant-collapse-item-active')).toBeNull(); -} - -const defaultOpenSections = ['hasError', 'durationNano', 'serviceName']; - -const defaultClosedSections = Object.keys(AllTraceFilterKeyValue).filter( - (section) => - ![...defaultOpenSections, 'durationNanoMin', 'durationNanoMax'].includes( - section, - ), -); - -async function checkForSectionContent(values: string[]): Promise { - for (const val of values) { - const sectionContent = await screen.findByText(val); - await waitFor(() => expect(sectionContent).toBeInTheDocument()); - } -} - -const redirectWithQueryBuilderData = jest.fn(); - -const compositeQuery: Query = { - ...initialQueriesMap.traces, - builder: { - ...initialQueriesMap.traces.builder, - queryData: [ - { - ...initialQueryBuilderFormValues, - filters: { - items: [ - { - id: '95564eb1', - key: { - key: 'name', - dataType: DataTypes.String, - type: 'tag', - isColumn: true, - isJSON: false, - id: 'name--string--tag--true', - }, - op: 'in', - value: ['HTTP GET /customer'], - }, - { - id: '3337951c', - key: { - key: 'serviceName', - dataType: DataTypes.String, - type: 'tag', - isColumn: true, - isJSON: false, - id: 'serviceName--string--tag--true', - }, - op: 'in', - value: ['demo-app'], - }, - ], - op: 'AND', - }, - }, - ], - }, -}; +jest.mock('container/OptionsMenu/useOptionsMenu', () => ({ + __esModule: true, + default: (): any => optionMenuReturn, +})); -describe('TracesExplorer - ', () => { +describe('TracesExplorer - Filters', () => { // Initial filter panel rendering // Test the initial state like which filters section are opened, default state of duration slider, etc. it('should render the Trace filter', async () => { @@ -457,3 +433,255 @@ describe('TracesExplorer - ', () => { ).toBeInTheDocument(); }); }); + +const handleExplorerTabChangeTest = jest.fn(); +jest.mock('hooks/useHandleExplorerTabChange', () => ({ + useHandleExplorerTabChange: jest.fn(() => ({ + handleExplorerTabChange: handleExplorerTabChangeTest, + })), +})); + +describe('TracesExplorer - ', () => { + it('should render the traces explorer page', async () => { + server.use( + rest.post('http://localhost/api/v3/query_range', (req, res, ctx) => + res(ctx.status(200), ctx.json(queryRangeForTimeSeries)), + ), + ); + const { findByText, getByText } = render(); + + // assert mocked date time selection + expect(await findByText('MockDateTimeSelection')).toBeInTheDocument(); + + // assert stage&Btn + expect(getByText('Stage & Run Query')).toBeInTheDocument(); + + // assert QB - will not write tests for QB as that would be covererd in QB tests separately + expect( + getByText( + 'Search Filter : select options from suggested values, for IN/NOT IN operators - press "Enter" after selecting options', + ), + ).toBeInTheDocument(); + expect(getByText('AGGREGATION INTERVAL')).toBeInTheDocument(); + expect(getByText('Metrics name')).toBeInTheDocument(); + expect(getByText('WHERE')).toBeInTheDocument(); + expect(getByText('Legend Format')).toBeInTheDocument(); + + // assert timeseries chart mock + expect(await screen.findByText('MockUplot')).toBeInTheDocument(); + }); + + it('check tab navigation', async () => { + const { getByText } = render(); + + // switch to list view + const listViewBtn = getByText('List View'); + expect(listViewBtn).toBeInTheDocument(); + fireEvent.click(listViewBtn); + + expect(handleExplorerTabChangeTest).toBeCalledWith(PANEL_TYPES.LIST); + + // switch to traces view + const tracesBtn = getByText('Traces'); + expect(tracesBtn).toBeInTheDocument(); + fireEvent.click(tracesBtn); + + expect(handleExplorerTabChangeTest).toBeCalledWith(PANEL_TYPES.TRACE); + + // switch to Table view + const TableBtn = getByText('Table View'); + expect(TableBtn).toBeInTheDocument(); + fireEvent.click(TableBtn); + + expect(handleExplorerTabChangeTest).toBeCalledWith(PANEL_TYPES.TABLE); + }); + + it('trace explorer - list view', async () => { + server.use( + rest.post('http://localhost/api/v3/query_range', (req, res, ctx) => + res(ctx.status(200), ctx.json(queryRangeForListView)), + ), + ); + + const { getByText } = render( + + + , + ); + + expect(await screen.findByText('Timestamp')).toBeInTheDocument(); + expect(getByText('options_menu.options')).toBeInTheDocument(); + + // test if pagination is there + expect(getByText('Previous')).toBeInTheDocument(); + expect(getByText('Next')).toBeInTheDocument(); + + // column interaction is covered in E2E tests as its a complex interaction + }); + + it('trace explorer - table view', async () => { + server.use( + rest.post('http://localhost/api/v3/query_range', (req, res, ctx) => + res(ctx.status(200), ctx.json(queryRangeForTableView)), + ), + ); + render( + + + , + ); + + expect(await screen.findByText('count')).toBeInTheDocument(); + expect(screen.getByText('87798.00')).toBeInTheDocument(); + }); + + it('trace explorer - trace view', async () => { + server.use( + rest.post('http://localhost/api/v3/query_range', (req, res, ctx) => + res(ctx.status(200), ctx.json(queryRangeForTraceView)), + ), + ); + const { getByText, getAllByText } = render( + + + , + ); + + expect(await screen.findByText('Root Service Name')).toBeInTheDocument(); + + // assert table headers + expect(getByText('Root Operation Name')).toBeInTheDocument(); + expect(getByText('Root Duration (in ms)')).toBeInTheDocument(); + expect(getByText('TraceID')).toBeInTheDocument(); + expect(getByText('No of Spans')).toBeInTheDocument(); + + // assert row values + ['demo-app', 'home', '8'].forEach((val) => + expect(getAllByText(val)[0]).toBeInTheDocument(), + ); + expect(getByText('7245.23ms')).toBeInTheDocument(); + + // assert traceId and redirection to trace details + const traceId = getByText('5765b60ba7cc4ddafe8bdaa9c1b4b246'); + fireEvent.click(traceId); + + // assert redirection - should go to /trace/:traceId + expect(window.location.href).toEqual( + 'http://localhost/trace/5765b60ba7cc4ddafe8bdaa9c1b4b246', + ); + }); + + it('test for explorer options', async () => { + const { getByText, getByTestId } = render(); + + // assert explorer options - action btns + [ + 'Save this view', + 'Create an Alert', + 'Add to Dashboard', + 'Select a view', + ].forEach((val) => expect(getByText(val)).toBeInTheDocument()); + + const hideExplorerOption = getByTestId('hide-toolbar'); + expect(hideExplorerOption).toBeInTheDocument(); + fireEvent.click(hideExplorerOption); + + // explorer options should hide and show btn should be present + expect(await screen.findByTestId('show-explorer-option')).toBeInTheDocument(); + expect(screen.queryByTestId('hide-toolbar')).toBeNull(); + + // show explorer options + const showExplorerOption = screen.getByTestId('show-explorer-option'); + expect(showExplorerOption).toBeInTheDocument(); + fireEvent.click(showExplorerOption); + + // explorer options should show and hide btn should be present + expect(await screen.findByTestId('hide-toolbar')).toBeInTheDocument(); + }); + + it('select a view options - assert and save this view', async () => { + const { container } = render(); + + await act(async () => { + fireEvent.mouseDown( + container.querySelector( + '.view-options .ant-select-selection-search-input', + ) as HTMLElement, + ); + }); + + const viewListOptions = await screen.findByRole('listbox'); + expect(viewListOptions).toBeInTheDocument(); + + expect( + within(viewListOptions).getByText('success traces list view'), + ).toBeInTheDocument(); + + expect(within(viewListOptions).getByText('Table View')).toBeInTheDocument(); + + // save this view + fireEvent.click(screen.getByText('Save this view')); + + const saveViewModalInput = await screen.findByPlaceholderText( + 'e.g. External http method view', + ); + expect(saveViewModalInput).toBeInTheDocument(); + + const saveViewModal = document.querySelector( + '.ant-modal-content', + ) as HTMLElement; + expect(saveViewModal).toBeInTheDocument(); + + await act(async () => + fireEvent.change(saveViewModalInput, { target: { value: 'test view' } }), + ); + + expect(saveViewModalInput).toHaveValue('test view'); + await act(async () => { + fireEvent.click(within(saveViewModal).getByTestId('save-view-btn')); + }); + + expect(successNotification).toHaveBeenCalledWith({ + message: 'View Saved Successfully', + }); + }); + + it('create a dashboard btn assert', async () => { + const { getByText } = render(); + + const createDashboardBtn = getByText('Add to Dashboard'); + expect(createDashboardBtn).toBeInTheDocument(); + fireEvent.click(createDashboardBtn); + + expect(await screen.findByText('Export Panel')).toBeInTheDocument(); + const createDashboardModal = document.querySelector( + '.ant-modal-content', + ) as HTMLElement; + expect(createDashboardModal).toBeInTheDocument(); + + // assert modal content + expect( + within(createDashboardModal).getByText('Select Dashboard'), + ).toBeInTheDocument(); + + expect( + within(createDashboardModal).getByText('New Dashboard'), + ).toBeInTheDocument(); + }); + + it('create an alert btn assert', async () => { + const { getByText } = render(); + + const createAlertBtn = getByText('Create an Alert'); + expect(createAlertBtn).toBeInTheDocument(); + fireEvent.click(createAlertBtn); + + expect(historyPush).toHaveBeenCalledWith( + expect.stringContaining(`${ROUTES.ALERTS_NEW}`), + ); + }); +}); diff --git a/frontend/src/pages/TracesExplorer/__test__/testUtils.ts b/frontend/src/pages/TracesExplorer/__test__/testUtils.ts new file mode 100644 index 0000000000..80d96c9cf3 --- /dev/null +++ b/frontend/src/pages/TracesExplorer/__test__/testUtils.ts @@ -0,0 +1,261 @@ +/* eslint-disable sonarjs/no-duplicate-string */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable no-await-in-loop */ +import { + initialQueriesMap, + initialQueryBuilderFormValues, + PANEL_TYPES, +} from 'constants/queryBuilder'; +import { noop } from 'lodash-es'; +import { screen, waitFor } from 'tests/test-utils'; +import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; + +import { AllTraceFilterKeyValue } from '../Filter/filterUtils'; + +export const optionMenuReturn = { + options: { + selectColumns: [ + { + key: 'serviceName', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'serviceName--string--tag--true', + }, + { + key: 'name', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'name--string--tag--true', + }, + { + key: 'durationNano', + dataType: 'float64', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'durationNano--float64--tag--true', + }, + { + key: 'httpMethod', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'httpMethod--string--tag--true', + }, + { + key: 'responseStatusCode', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'responseStatusCode--string--tag--true', + }, + { + key: 'statusCode', + dataType: 'float64', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'statusCode--float64--tag--true', + }, + { + key: 'dbName', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'dbName--string--tag--true', + }, + ], + maxLines: 2, + format: 'list', + }, + handleOptionsChange: jest.fn(), + config: { + addColumn: { + isFetching: false, + value: [ + { + key: 'serviceName', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'serviceName--string--tag--true', + }, + { + key: 'name', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'name--string--tag--true', + }, + { + key: 'durationNano', + dataType: 'float64', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'durationNano--float64--tag--true', + }, + { + key: 'httpMethod', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'httpMethod--string--tag--true', + }, + { + key: 'responseStatusCode', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'responseStatusCode--string--tag--true', + }, + { + key: 'statusCode', + dataType: 'float64', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'statusCode--float64--tag--true', + }, + { + key: 'dbName', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'dbName--string--tag--true', + }, + ], + options: [], + }, + format: { + value: 'list', + }, + maxLines: { + value: 2, + }, + }, +}; + +export const compositeQuery: Query = { + ...initialQueriesMap.traces, + builder: { + ...initialQueriesMap.traces.builder, + queryData: [ + { + ...initialQueryBuilderFormValues, + filters: { + items: [ + { + id: '95564eb1', + key: { + key: 'name', + dataType: DataTypes.String, + type: 'tag', + isColumn: true, + isJSON: false, + id: 'name--string--tag--true', + }, + op: 'in', + value: ['HTTP GET /customer'], + }, + { + id: '3337951c', + key: { + key: 'serviceName', + dataType: DataTypes.String, + type: 'tag', + isColumn: true, + isJSON: false, + id: 'serviceName--string--tag--true', + }, + op: 'in', + value: ['demo-app'], + }, + ], + op: 'AND', + }, + }, + ], + }, +}; + +export const redirectWithQueryBuilderData = jest.fn(); + +export const qbProviderValue = { + currentQuery: { + ...initialQueriesMap.traces, + builder: { + ...initialQueriesMap.traces.builder, + queryData: [initialQueryBuilderFormValues], + }, + }, + redirectWithQueryBuilderData, + panelType: PANEL_TYPES.LIST, + setSupersetQuery: jest.fn(), + supersetQuery: initialQueriesMap.traces, + stagedQuery: initialQueriesMap.traces, + initialDataSource: null, + isEnabledQuery: false, + handleSetQueryData: noop, + handleSetFormulaData: noop, + handleSetQueryItemData: noop, + handleSetConfig: noop, + removeQueryBuilderEntityByIndex: noop, + removeQueryTypeItemByIndex: noop, + addNewBuilderQuery: noop, + cloneQuery: noop, + addNewFormula: noop, + addNewQueryItem: noop, + handleRunQuery: noop, + resetQuery: noop, + updateAllQueriesOperators: (): Query => initialQueriesMap.traces, + updateQueriesData: (): Query => initialQueriesMap.traces, + initQueryBuilderData: noop, + handleOnUnitsChange: noop, + isStagedQueryUpdated: (): boolean => false, +} as any; + +export function checkIfSectionIsOpen( + getByTestId: (testId: string) => HTMLElement, + panelName: string, +): void { + const section = getByTestId(`collapse-${panelName}`); + expect(section.querySelector('.ant-collapse-item-active')).not.toBeNull(); +} + +export function checkIfSectionIsNotOpen( + getByTestId: (testId: string) => HTMLElement, + panelName: string, +): void { + const section = getByTestId(`collapse-${panelName}`); + expect(section.querySelector('.ant-collapse-item-active')).toBeNull(); +} + +export const defaultOpenSections = ['hasError', 'durationNano', 'serviceName']; + +export const defaultClosedSections = Object.keys(AllTraceFilterKeyValue).filter( + (section) => + ![...defaultOpenSections, 'durationNanoMin', 'durationNanoMax'].includes( + section, + ), +); + +export async function checkForSectionContent(values: string[]): Promise { + for (const val of values) { + const sectionContent = await screen.findByText(val); + await waitFor(() => expect(sectionContent).toBeInTheDocument()); + } +}