mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-11 23:18:59 +08:00
chore: added trace explorer test (#5531)
* feat: added trace filter test cases * feat: added trace filter test cases - initial render * feat: added test cases - query sync, filter section behaviour etc * feat: deleted mock-data files * feat: added test cases of undefined filters and items * feat: deleted tsconfig * feat: added clear and rest btn test cases for traces filters * feat: added collapse and uncollapse test for traces filters * chore: added trace explorer tests
This commit is contained in:
parent
ab4a8dfbea
commit
a20794040a
@ -501,6 +501,7 @@ function ExplorerOptions({
|
||||
shape="circle"
|
||||
onClick={hideToolbar}
|
||||
icon={<PanelBottomClose size={16} />}
|
||||
data-testid="hide-toolbar"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@ -530,6 +531,7 @@ function ExplorerOptions({
|
||||
icon={<Check size={16} />}
|
||||
onClick={onSaveHandler}
|
||||
disabled={isSaveViewLoading}
|
||||
data-testid="save-view-btn"
|
||||
>
|
||||
Save this view
|
||||
</Button>,
|
||||
|
@ -65,6 +65,7 @@ function ExplorerOptionsHideArea({
|
||||
// style={{ alignSelf: 'center', marginRight: 'calc(10% - 20px)' }}
|
||||
className="explorer-show-btn"
|
||||
onClick={handleShowExplorerOption}
|
||||
data-testid="show-explorer-option"
|
||||
>
|
||||
<div className="menu-bar" />
|
||||
</Button>
|
||||
|
@ -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"}',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -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',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
@ -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),
|
||||
|
@ -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 <div>MockUplot</div>;
|
||||
},
|
||||
);
|
||||
|
||||
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();
|
||||
}
|
||||
jest.mock('container/OptionsMenu/useOptionsMenu', () => ({
|
||||
__esModule: true,
|
||||
default: (): any => optionMenuReturn,
|
||||
}));
|
||||
|
||||
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<void> {
|
||||
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',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
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(<TracesExplorer />);
|
||||
|
||||
// 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(<TracesExplorer />);
|
||||
|
||||
// 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(
|
||||
<QueryBuilderContext.Provider value={{ ...qbProviderValue }}>
|
||||
<TracesExplorer />
|
||||
</QueryBuilderContext.Provider>,
|
||||
);
|
||||
|
||||
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(
|
||||
<QueryBuilderContext.Provider
|
||||
value={{ ...qbProviderValue, panelType: PANEL_TYPES.TABLE }}
|
||||
>
|
||||
<TracesExplorer />
|
||||
</QueryBuilderContext.Provider>,
|
||||
);
|
||||
|
||||
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(
|
||||
<QueryBuilderContext.Provider
|
||||
value={{ ...qbProviderValue, panelType: PANEL_TYPES.TRACE }}
|
||||
>
|
||||
<TracesExplorer />
|
||||
</QueryBuilderContext.Provider>,
|
||||
);
|
||||
|
||||
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(<TracesExplorer />);
|
||||
|
||||
// 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(<TracesExplorer />);
|
||||
|
||||
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(<TracesExplorer />);
|
||||
|
||||
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(<TracesExplorer />);
|
||||
|
||||
const createAlertBtn = getByText('Create an Alert');
|
||||
expect(createAlertBtn).toBeInTheDocument();
|
||||
fireEvent.click(createAlertBtn);
|
||||
|
||||
expect(historyPush).toHaveBeenCalledWith(
|
||||
expect.stringContaining(`${ROUTES.ALERTS_NEW}`),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
261
frontend/src/pages/TracesExplorer/__test__/testUtils.ts
Normal file
261
frontend/src/pages/TracesExplorer/__test__/testUtils.ts
Normal file
@ -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<void> {
|
||||
for (const val of values) {
|
||||
const sectionContent = await screen.findByText(val);
|
||||
await waitFor(() => expect(sectionContent).toBeInTheDocument());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user