/* eslint-disable sonarjs/no-duplicate-string */
import { fireEvent, render, screen } from '@testing-library/react';
import { Button } from 'antd';
import ROUTES from 'constants/routes';
import { server } from 'mocks-server/server';
import { rest } from 'msw';
import ContextMenu, { useCoordinates } from 'periscope/components/ContextMenu';
import MockQueryClientProvider from 'providers/test/MockQueryClientProvider';
import React from 'react';
import { Provider } from 'react-redux';
import { MemoryRouter } from 'react-router-dom';
import store from 'store';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import useTableContextMenu from '../useTableContextMenu';
import {
MOCK_AGGREGATE_DATA,
MOCK_COORDINATES,
MOCK_FILTER_DATA,
MOCK_KEY_SUGGESTIONS_RESPONSE,
// MOCK_KEY_SUGGESTIONS_SEARCH_RESPONSE,
MOCK_QUERY,
} from './mockTableData';
// Mock the necessary hooks and dependencies
const mockSafeNavigate = jest.fn();
const mockRedirectWithQueryBuilderData = jest.fn();
jest.mock('hooks/useSafeNavigate', () => ({
useSafeNavigate: (): any => ({
safeNavigate: mockSafeNavigate,
}),
}));
jest.mock('hooks/queryBuilder/useQueryBuilder', () => ({
useQueryBuilder: (): any => ({
redirectWithQueryBuilderData: mockRedirectWithQueryBuilderData,
}),
}));
jest.mock('container/GridCardLayout/useResolveQuery', () => ({
__esModule: true,
default: (): any => ({
getUpdatedQuery: jest.fn().mockResolvedValue({}),
isLoading: false,
}),
}));
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: (): { pathname: string } => ({
pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.DASHBOARD}/`,
}),
}));
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useSelector: (): any => ({
globalTime: {
selectedTime: {
startTime: 1713734400000,
endTime: 1713738000000,
},
maxTime: 1713738000000,
minTime: 1713734400000,
},
}),
}));
jest.mock('container/QueryTable/Drilldown/useDashboardVarConfig', () => ({
__esModule: true,
default: (): any => ({
dashbaordVariablesConfig: {
items: <>items>,
},
// contextItems: <>>,
}),
}));
function MockTableDrilldown(): JSX.Element {
const {
coordinates,
popoverPosition,
clickedData,
onClose,
onClick,
subMenu,
setSubMenu,
} = useCoordinates();
const { menuItemsConfig } = useTableContextMenu({
widgetId: 'test-widget',
query: MOCK_QUERY as Query,
clickedData,
onClose,
coordinates,
subMenu,
setSubMenu,
});
const handleClick = (type: 'aggregate' | 'filter'): void => {
// Simulate the same flow as handleColumnClick in QueryTable
onClick(
MOCK_COORDINATES,
type === 'aggregate' ? MOCK_AGGREGATE_DATA : MOCK_FILTER_DATA,
);
};
return (
);
}
const renderWithProviders = (
component: React.ReactElement,
): ReturnType =>
render(
{component},
);
describe('TableDrilldown Breakout Functionality', () => {
beforeEach((): void => {
jest.clearAllMocks();
// Mock the substitute_vars API that's causing network errors
server.use(
rest.post('*/api/v5/substitute_vars', (req, res, ctx) =>
res(ctx.status(200), ctx.json({ status: 'success', data: {} })),
),
);
});
it('should show breakout options when "Breakout by" is clicked', async (): Promise => {
// Mock the MSW server to intercept the keySuggestions API call
server.use(
rest.get('*/fields/keys', (req, res, ctx) =>
res(ctx.status(200), ctx.json(MOCK_KEY_SUGGESTIONS_RESPONSE)),
),
);
renderWithProviders();
// Find and click the aggregate button to show context menu
const aggregateButton = screen.getByRole('button', { name: /aggregate/i });
fireEvent.click(aggregateButton);
// Find and click "Breakout by" option
const breakoutOption = screen.getByText(/Breakout by/);
fireEvent.click(breakoutOption);
// Wait for the breakout options to load and verify they are displayed
await screen.findByText('Breakout by');
// Check that the search input is displayed
expect(
screen.getByPlaceholderText('Search breakout options...'),
).toBeInTheDocument();
// Wait for the API call to complete and options to load
// Check what's actually being rendered instead of waiting for specific text
await screen.findByText('deployment.environment');
// Check that the breakout options are loaded and displayed
// Based on the test output, these are the actual options being rendered
expect(screen.getByText('deployment.environment')).toBeInTheDocument();
expect(screen.getByText('http.method')).toBeInTheDocument();
expect(screen.getByText('http.status_code')).toBeInTheDocument();
// Verify that the breakout header is displayed
expect(screen.getByText('Breakout by')).toBeInTheDocument();
});
it('should add selected breakout option to groupBy and redirect with correct query', async (): Promise => {
// Mock the MSW server to intercept the keySuggestions API call
server.use(
rest.get('*/fields/keys', (req, res, ctx) =>
res(ctx.status(200), ctx.json(MOCK_KEY_SUGGESTIONS_RESPONSE)),
),
);
renderWithProviders();
// Navigate to breakout options
const aggregateButton = screen.getByRole('button', { name: /aggregate/i });
fireEvent.click(aggregateButton);
const breakoutOption = screen.getByText(/Breakout by/);
fireEvent.click(breakoutOption);
// Wait for breakout options to load
await screen.findByText('deployment.environment');
// Click on a breakout option (e.g., deployment.environment)
const breakoutOptionItem = screen.getByText('deployment.environment');
fireEvent.click(breakoutOptionItem);
// Verify redirectWithQueryBuilderData was called
expect(mockRedirectWithQueryBuilderData).toHaveBeenCalledTimes(1);
const [
query,
queryParams,
,
newTab,
] = mockRedirectWithQueryBuilderData.mock.calls[0];
// Check that the query contains the correct structure
expect(query.builder).toBeDefined();
expect(query.builder.queryData).toBeDefined();
// Find the query data for the aggregate query (queryName: 'A')
const aggregateQueryData = query.builder.queryData.find(
(item: any) => item.queryName === 'A',
);
expect(aggregateQueryData).toBeDefined();
// Verify that the groupBy has been updated to only contain the selected breakout option
expect(aggregateQueryData.groupBy).toHaveLength(1);
expect(aggregateQueryData.groupBy[0].key).toEqual('deployment.environment');
// Verify that orderBy has been cleared (as per getBreakoutQuery logic)
expect(aggregateQueryData.orderBy).toEqual([]);
// Verify that the legend has been updated (check the actual value being returned)
// The legend logic in getBreakoutQuery: legend: item.legend && groupBy.key ? `{{${groupBy.key}}}` : ''
// Since the original legend might be empty, the result could be empty string
expect(aggregateQueryData.legend).toBeDefined();
// Check that the queryParams contain the expandedWidgetId
expect(queryParams).toEqual({
expandedWidgetId: 'test-widget',
graphType: 'graph',
});
// Check that newTab is true
expect(newTab).toBe(true);
// Verify that the original filters are preserved and new filters are added
expect(aggregateQueryData.filter.expression).toContain(
"service.name in $service.name AND trace_id EXISTS AND deployment.environment = '$env'",
);
// The new filter from the clicked data should also be present
expect(aggregateQueryData.filter.expression).toContain(
"service.name = 'adservice' AND trace_id = 'df2cfb0e57bb8736207689851478cd50'",
);
});
});