mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 06:19:03 +08:00
chore: added trace filter test cases (#5451)
* 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
This commit is contained in:
parent
a2492b0135
commit
89fd3e4f55
@ -1,5 +1,4 @@
|
|||||||
import { Typography } from 'antd';
|
import { Card, Typography } from 'antd';
|
||||||
import Card from 'antd/es/card/Card';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const Container = styled(Card)`
|
export const Container = styled(Card)`
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import './TraceDetails.styles.scss';
|
import './TraceDetails.styles.scss';
|
||||||
|
|
||||||
import { FilterOutlined } from '@ant-design/icons';
|
import { FilterOutlined } from '@ant-design/icons';
|
||||||
import { Button, Col, Typography } from 'antd';
|
import { Button, Col, Layout, Typography } from 'antd';
|
||||||
import Sider from 'antd/es/layout/Sider';
|
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import {
|
import {
|
||||||
StyledCol,
|
StyledCol,
|
||||||
@ -42,6 +41,8 @@ import {
|
|||||||
INTERVAL_UNITS,
|
INTERVAL_UNITS,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
|
const { Sider } = Layout;
|
||||||
|
|
||||||
function TraceDetail({ response }: TraceDetailProps): JSX.Element {
|
function TraceDetail({ response }: TraceDetailProps): JSX.Element {
|
||||||
const spanServiceColors = useMemo(
|
const spanServiceColors = useMemo(
|
||||||
() => spanServiceNameToColorMapping(response[0].events),
|
() => spanServiceNameToColorMapping(response[0].events),
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Col } from 'antd';
|
import { Card, Col } from 'antd';
|
||||||
import Card from 'antd/es/card/Card';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const Container = styled(Card)`
|
export const Container = styled(Card)`
|
||||||
|
81
frontend/src/mocks-server/__mockdata__/explorer_views.ts
Normal file
81
frontend/src/mocks-server/__mockdata__/explorer_views.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
export const explorerView = {
|
||||||
|
status: 'success',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
uuid: 'test-uuid-1',
|
||||||
|
name: 'Table View',
|
||||||
|
category: '',
|
||||||
|
createdAt: '2023-08-29T18:04:10.906310033Z',
|
||||||
|
createdBy: 'test-user-1',
|
||||||
|
updatedAt: '2024-01-29T10:42:47.346331133Z',
|
||||||
|
updatedBy: 'test-user-1',
|
||||||
|
sourcePage: 'traces',
|
||||||
|
tags: [''],
|
||||||
|
compositeQuery: {
|
||||||
|
builderQueries: {
|
||||||
|
A: {
|
||||||
|
queryName: 'A',
|
||||||
|
stepInterval: 60,
|
||||||
|
dataSource: 'traces',
|
||||||
|
aggregateOperator: 'count',
|
||||||
|
aggregateAttribute: {
|
||||||
|
key: 'component',
|
||||||
|
dataType: 'string',
|
||||||
|
type: 'tag',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
op: 'AND',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: {
|
||||||
|
key: 'component',
|
||||||
|
dataType: 'string',
|
||||||
|
type: 'tag',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
},
|
||||||
|
value: 'test-component',
|
||||||
|
op: '!=',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
groupBy: [
|
||||||
|
{
|
||||||
|
key: 'component',
|
||||||
|
dataType: 'string',
|
||||||
|
type: 'tag',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'client-uuid',
|
||||||
|
dataType: 'string',
|
||||||
|
type: 'resource',
|
||||||
|
isColumn: false,
|
||||||
|
isJSON: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
expression: 'A',
|
||||||
|
disabled: false,
|
||||||
|
limit: 0,
|
||||||
|
offset: 0,
|
||||||
|
pageSize: 0,
|
||||||
|
orderBy: [
|
||||||
|
{
|
||||||
|
columnName: 'timestamp',
|
||||||
|
order: 'desc',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
reduceTo: 'sum',
|
||||||
|
ShiftBy: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
panelType: 'table',
|
||||||
|
queryType: 'builder',
|
||||||
|
},
|
||||||
|
extraData: '{"color":"#00ffd0"}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
@ -2,6 +2,7 @@ import { rest } from 'msw';
|
|||||||
|
|
||||||
import { billingSuccessResponse } from './__mockdata__/billing';
|
import { billingSuccessResponse } from './__mockdata__/billing';
|
||||||
import { dashboardSuccessResponse } from './__mockdata__/dashboards';
|
import { dashboardSuccessResponse } from './__mockdata__/dashboards';
|
||||||
|
import { explorerView } from './__mockdata__/explorer_views';
|
||||||
import { inviteUser } from './__mockdata__/invite_user';
|
import { inviteUser } from './__mockdata__/invite_user';
|
||||||
import { licensesSuccessResponse } from './__mockdata__/licenses';
|
import { licensesSuccessResponse } from './__mockdata__/licenses';
|
||||||
import { membersResponse } from './__mockdata__/members';
|
import { membersResponse } from './__mockdata__/members';
|
||||||
@ -55,6 +56,51 @@ export const handlers = [
|
|||||||
const metricName = req.url.searchParams.get('metricName');
|
const metricName = req.url.searchParams.get('metricName');
|
||||||
const tagKey = req.url.searchParams.get('tagKey');
|
const tagKey = req.url.searchParams.get('tagKey');
|
||||||
|
|
||||||
|
const attributeKey = req.url.searchParams.get('attributeKey');
|
||||||
|
|
||||||
|
if (attributeKey === 'serviceName') {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
status: 'success',
|
||||||
|
data: {
|
||||||
|
stringAttributeValues: [
|
||||||
|
'customer',
|
||||||
|
'demo-app',
|
||||||
|
'driver',
|
||||||
|
'frontend',
|
||||||
|
'mysql',
|
||||||
|
'redis',
|
||||||
|
'route',
|
||||||
|
'go-grpc-otel-server',
|
||||||
|
'test',
|
||||||
|
],
|
||||||
|
numberAttributeValues: null,
|
||||||
|
boolAttributeValues: null,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributeKey === 'name') {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
status: 'success',
|
||||||
|
data: {
|
||||||
|
stringAttributeValues: [
|
||||||
|
'HTTP GET',
|
||||||
|
'HTTP GET /customer',
|
||||||
|
'HTTP GET /dispatch',
|
||||||
|
'HTTP GET /route',
|
||||||
|
],
|
||||||
|
numberAttributeValues: null,
|
||||||
|
boolAttributeValues: null,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
metricName === 'signoz_calls_total' &&
|
metricName === 'signoz_calls_total' &&
|
||||||
tagKey === 'resource_signoz_collector_id'
|
tagKey === 'resource_signoz_collector_id'
|
||||||
@ -102,4 +148,31 @@ export const handlers = [
|
|||||||
rest.post('http://localhost/api/v1/invite', (_, res, ctx) =>
|
rest.post('http://localhost/api/v1/invite', (_, res, ctx) =>
|
||||||
res(ctx.status(200), ctx.json(inviteUser)),
|
res(ctx.status(200), ctx.json(inviteUser)),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
rest.get(
|
||||||
|
'http://localhost/api/v3/autocomplete/aggregate_attributes',
|
||||||
|
(req, res, ctx) =>
|
||||||
|
res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
status: 'success',
|
||||||
|
data: { attributeKeys: null },
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
rest.get('http://localhost/api/v1/explorer/views', (req, res, ctx) =>
|
||||||
|
res(ctx.status(200), ctx.json(explorerView)),
|
||||||
|
),
|
||||||
|
|
||||||
|
rest.post('http://localhost/api/v1/event', (req, res, ctx) =>
|
||||||
|
res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
payload: 'Event Processed Successfully',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
@ -109,6 +109,7 @@ export function DurationSection(props: DurationProps): JSX.Element {
|
|||||||
className="min-max-input"
|
className="min-max-input"
|
||||||
onChange={onChangeMinHandler}
|
onChange={onChangeMinHandler}
|
||||||
value={preMin}
|
value={preMin}
|
||||||
|
data-testid="min-input"
|
||||||
addonAfter="ms"
|
addonAfter="ms"
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
@ -118,6 +119,7 @@ export function DurationSection(props: DurationProps): JSX.Element {
|
|||||||
className="min-max-input"
|
className="min-max-input"
|
||||||
onChange={onChangeMaxHandler}
|
onChange={onChangeMaxHandler}
|
||||||
value={preMax}
|
value={preMax}
|
||||||
|
data-testid="max-input"
|
||||||
addonAfter="ms"
|
addonAfter="ms"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -224,13 +224,18 @@ export function Filter(props: FilterProps): JSX.Element {
|
|||||||
<Button
|
<Button
|
||||||
onClick={(): void => handleRun({ resetAll: true })}
|
onClick={(): void => handleRun({ resetAll: true })}
|
||||||
className="sync-icon"
|
className="sync-icon"
|
||||||
|
data-testid="reset-filters"
|
||||||
>
|
>
|
||||||
<SyncOutlined />
|
<SyncOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Tooltip title="Collapse" placement="right">
|
<Tooltip title="Collapse" placement="right">
|
||||||
<Button onClick={(): void => setOpen(false)} className="arrow-icon">
|
<Button
|
||||||
|
onClick={(): void => setOpen(false)}
|
||||||
|
className="arrow-icon"
|
||||||
|
data-testid="toggle-filter-panel"
|
||||||
|
>
|
||||||
<VerticalAlignTopOutlined rotate={270} />
|
<VerticalAlignTopOutlined rotate={270} />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
@ -64,7 +64,7 @@ export function Section(props: SectionProps): JSX.Element {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Divider plain className="divider" />
|
<Divider plain className="divider" />
|
||||||
<div className="section-body-header">
|
<div className="section-body-header" data-testid={`collapse-${panelName}`}>
|
||||||
<Collapse
|
<Collapse
|
||||||
bordered={false}
|
bordered={false}
|
||||||
className="collapseContainer"
|
className="collapseContainer"
|
||||||
@ -96,7 +96,11 @@ export function Section(props: SectionProps): JSX.Element {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Button type="link" onClick={onClearHandler}>
|
<Button
|
||||||
|
type="link"
|
||||||
|
onClick={onClearHandler}
|
||||||
|
data-testid={`collapse-${panelName}-clearBtn`}
|
||||||
|
>
|
||||||
Clear All
|
Clear All
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -145,6 +145,7 @@ export function SectionBody(props: SectionBodyProps): JSX.Element {
|
|||||||
key={`${type}-${item}`}
|
key={`${type}-${item}`}
|
||||||
onChange={(e): void => onCheckHandler(e, item)}
|
onChange={(e): void => onCheckHandler(e, item)}
|
||||||
checked={checkboxMatcher(item)}
|
checked={checkboxMatcher(item)}
|
||||||
|
data-testid={`${type}-${item}`}
|
||||||
>
|
>
|
||||||
<div className="checkbox-label">
|
<div className="checkbox-label">
|
||||||
<div className={labelClassname(item)} />
|
<div className={labelClassname(item)} />
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
/* eslint-disable sonarjs/no-duplicate-string */
|
/* eslint-disable sonarjs/no-duplicate-string */
|
||||||
/* eslint-disable no-restricted-syntax */
|
/* eslint-disable no-restricted-syntax */
|
||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
import {
|
import {
|
||||||
initialQueriesMap,
|
initialQueriesMap,
|
||||||
initialQueryBuilderFormValues,
|
initialQueryBuilderFormValues,
|
||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import * as compositeQueryHook from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
import * as compositeQueryHook from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||||
import { render } from 'tests/test-utils';
|
import { QueryBuilderContext } from 'providers/QueryBuilder';
|
||||||
|
import { fireEvent, render, screen, waitFor, within } from 'tests/test-utils';
|
||||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
|
import TracesExplorer from '..';
|
||||||
import { Filter } from '../Filter/Filter';
|
import { Filter } from '../Filter/Filter';
|
||||||
import { AllTraceFilterKeyValue } from '../Filter/filterUtils';
|
import { AllTraceFilterKeyValue } from '../Filter/filterUtils';
|
||||||
|
|
||||||
@ -37,6 +40,48 @@ jest.mock('uplot', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock(
|
||||||
|
'container/TopNav/DateTimeSelectionV2/index.tsx',
|
||||||
|
() =>
|
||||||
|
function MockDateTimeSelection(): JSX.Element {
|
||||||
|
return <div>MockDateTimeSelection</div>;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
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<void> {
|
||||||
|
for (const val of values) {
|
||||||
|
const sectionContent = await screen.findByText(val);
|
||||||
|
await waitFor(() => expect(sectionContent).toBeInTheDocument());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const redirectWithQueryBuilderData = jest.fn();
|
||||||
|
|
||||||
const compositeQuery: Query = {
|
const compositeQuery: Query = {
|
||||||
...initialQueriesMap.traces,
|
...initialQueriesMap.traces,
|
||||||
builder: {
|
builder: {
|
||||||
@ -81,6 +126,157 @@ const compositeQuery: Query = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe('TracesExplorer - ', () => {
|
describe('TracesExplorer - ', () => {
|
||||||
|
// 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 () => {
|
||||||
|
const { getByText, getByTestId } = render(<Filter setOpen={jest.fn()} />);
|
||||||
|
|
||||||
|
Object.values(AllTraceFilterKeyValue).forEach((filter) => {
|
||||||
|
expect(getByText(filter)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check default state of duration slider
|
||||||
|
const minDuration = getByTestId('min-input') as HTMLInputElement;
|
||||||
|
const maxDuration = getByTestId('max-input') as HTMLInputElement;
|
||||||
|
expect(minDuration).toHaveValue(null);
|
||||||
|
expect(minDuration).toHaveProperty('placeholder', '0');
|
||||||
|
expect(maxDuration).toHaveValue(null);
|
||||||
|
expect(maxDuration).toHaveProperty('placeholder', '100000000');
|
||||||
|
|
||||||
|
// Check which all filter section are opened by default
|
||||||
|
defaultOpenSections.forEach((section) =>
|
||||||
|
checkIfSectionIsOpen(getByTestId, section),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check which all filter section are closed by default
|
||||||
|
defaultClosedSections.forEach((section) =>
|
||||||
|
checkIfSectionIsNotOpen(getByTestId, section),
|
||||||
|
);
|
||||||
|
|
||||||
|
// check for the status section content
|
||||||
|
await checkForSectionContent(['Ok', 'Error']);
|
||||||
|
|
||||||
|
// check for the service name section content from API response
|
||||||
|
await checkForSectionContent([
|
||||||
|
'customer',
|
||||||
|
'demo-app',
|
||||||
|
'driver',
|
||||||
|
'frontend',
|
||||||
|
'mysql',
|
||||||
|
'redis',
|
||||||
|
'route',
|
||||||
|
'go-grpc-otel-server',
|
||||||
|
'test',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// test the filter panel actions like opening and closing the sections, etc.
|
||||||
|
it('filter panel actions', async () => {
|
||||||
|
const { getByTestId } = render(<Filter setOpen={jest.fn()} />);
|
||||||
|
|
||||||
|
// Check if the section is closed
|
||||||
|
checkIfSectionIsNotOpen(getByTestId, 'name');
|
||||||
|
// Open the section
|
||||||
|
const name = getByTestId('collapse-name');
|
||||||
|
expect(name).toBeInTheDocument();
|
||||||
|
|
||||||
|
userEvent.click(within(name).getByText(AllTraceFilterKeyValue.name));
|
||||||
|
await waitFor(() => checkIfSectionIsOpen(getByTestId, 'name'));
|
||||||
|
|
||||||
|
await checkForSectionContent([
|
||||||
|
'HTTP GET',
|
||||||
|
'HTTP GET /customer',
|
||||||
|
'HTTP GET /dispatch',
|
||||||
|
'HTTP GET /route',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Close the section
|
||||||
|
userEvent.click(within(name).getByText(AllTraceFilterKeyValue.name));
|
||||||
|
await waitFor(() => checkIfSectionIsNotOpen(getByTestId, 'name'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checking filters should update the query', async () => {
|
||||||
|
const { getByText } = render(
|
||||||
|
<QueryBuilderContext.Provider
|
||||||
|
value={
|
||||||
|
{
|
||||||
|
currentQuery: {
|
||||||
|
...initialQueriesMap.traces,
|
||||||
|
builder: {
|
||||||
|
...initialQueriesMap.traces.builder,
|
||||||
|
queryData: [initialQueryBuilderFormValues],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
redirectWithQueryBuilderData,
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Filter setOpen={jest.fn()} />
|
||||||
|
</QueryBuilderContext.Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const okCheckbox = getByText('Ok');
|
||||||
|
fireEvent.click(okCheckbox);
|
||||||
|
expect(
|
||||||
|
redirectWithQueryBuilderData.mock.calls[
|
||||||
|
redirectWithQueryBuilderData.mock.calls.length - 1
|
||||||
|
][0].builder.queryData[0].filters.items,
|
||||||
|
).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
key: {
|
||||||
|
id: expect.any(String),
|
||||||
|
key: 'hasError',
|
||||||
|
type: 'tag',
|
||||||
|
dataType: 'bool',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
},
|
||||||
|
op: 'in',
|
||||||
|
value: ['false'],
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if the query is updated when the error checkbox is clicked
|
||||||
|
const errorCheckbox = getByText('Error');
|
||||||
|
fireEvent.click(errorCheckbox);
|
||||||
|
expect(
|
||||||
|
redirectWithQueryBuilderData.mock.calls[
|
||||||
|
redirectWithQueryBuilderData.mock.calls.length - 1
|
||||||
|
][0].builder.queryData[0].filters.items,
|
||||||
|
).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
key: {
|
||||||
|
id: expect.any(String),
|
||||||
|
key: 'hasError',
|
||||||
|
type: 'tag',
|
||||||
|
dataType: 'bool',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
},
|
||||||
|
op: 'in',
|
||||||
|
value: ['false', 'true'],
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the trace filter with the given query', async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(compositeQueryHook, 'useGetCompositeQueryParam')
|
||||||
|
.mockReturnValue(compositeQuery);
|
||||||
|
|
||||||
|
const { findByText, getByTestId } = render(<Filter setOpen={jest.fn()} />);
|
||||||
|
|
||||||
|
// check if the default query is applied - composite query has filters - serviceName : demo-app and name : HTTP GET /customer
|
||||||
|
expect(await findByText('demo-app')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('serviceName-demo-app')).toBeChecked();
|
||||||
|
expect(await findByText('HTTP GET /customer')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('name-HTTP GET /customer')).toBeChecked();
|
||||||
|
});
|
||||||
|
|
||||||
it('test edge cases of undefined filters', async () => {
|
it('test edge cases of undefined filters', async () => {
|
||||||
jest.spyOn(compositeQueryHook, 'useGetCompositeQueryParam').mockReturnValue({
|
jest.spyOn(compositeQueryHook, 'useGetCompositeQueryParam').mockReturnValue({
|
||||||
...compositeQuery,
|
...compositeQuery,
|
||||||
@ -98,7 +294,6 @@ describe('TracesExplorer - ', () => {
|
|||||||
|
|
||||||
const { getByText } = render(<Filter setOpen={jest.fn()} />);
|
const { getByText } = render(<Filter setOpen={jest.fn()} />);
|
||||||
|
|
||||||
// we should have all the filters
|
|
||||||
Object.values(AllTraceFilterKeyValue).forEach((filter) => {
|
Object.values(AllTraceFilterKeyValue).forEach((filter) => {
|
||||||
expect(getByText(filter)).toBeInTheDocument();
|
expect(getByText(filter)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
@ -124,9 +319,141 @@ describe('TracesExplorer - ', () => {
|
|||||||
|
|
||||||
const { getByText } = render(<Filter setOpen={jest.fn()} />);
|
const { getByText } = render(<Filter setOpen={jest.fn()} />);
|
||||||
|
|
||||||
// we should have all the filters
|
|
||||||
Object.values(AllTraceFilterKeyValue).forEach((filter) => {
|
Object.values(AllTraceFilterKeyValue).forEach((filter) => {
|
||||||
expect(getByText(filter)).toBeInTheDocument();
|
expect(getByText(filter)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should clear filter on clear & reset button click', async () => {
|
||||||
|
const { getByText, getByTestId } = render(
|
||||||
|
<QueryBuilderContext.Provider
|
||||||
|
value={
|
||||||
|
{
|
||||||
|
currentQuery: {
|
||||||
|
...initialQueriesMap.traces,
|
||||||
|
builder: {
|
||||||
|
...initialQueriesMap.traces.builder,
|
||||||
|
queryData: [initialQueryBuilderFormValues],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
redirectWithQueryBuilderData,
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Filter setOpen={jest.fn()} />
|
||||||
|
</QueryBuilderContext.Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
// check for the status section content
|
||||||
|
await checkForSectionContent(['Ok', 'Error']);
|
||||||
|
|
||||||
|
// check for the service name section content from API response
|
||||||
|
await checkForSectionContent([
|
||||||
|
'customer',
|
||||||
|
'demo-app',
|
||||||
|
'driver',
|
||||||
|
'frontend',
|
||||||
|
'mysql',
|
||||||
|
'redis',
|
||||||
|
'route',
|
||||||
|
'go-grpc-otel-server',
|
||||||
|
'test',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const okCheckbox = getByText('Ok');
|
||||||
|
fireEvent.click(okCheckbox);
|
||||||
|
|
||||||
|
const frontendCheckbox = getByText('frontend');
|
||||||
|
fireEvent.click(frontendCheckbox);
|
||||||
|
|
||||||
|
// check if checked and present in query
|
||||||
|
expect(
|
||||||
|
redirectWithQueryBuilderData.mock.calls[
|
||||||
|
redirectWithQueryBuilderData.mock.calls.length - 1
|
||||||
|
][0].builder.queryData[0].filters.items,
|
||||||
|
).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
key: {
|
||||||
|
id: expect.any(String),
|
||||||
|
key: 'hasError',
|
||||||
|
type: 'tag',
|
||||||
|
dataType: 'bool',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
},
|
||||||
|
op: 'in',
|
||||||
|
value: ['false'],
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
key: {
|
||||||
|
key: 'serviceName',
|
||||||
|
dataType: 'string',
|
||||||
|
type: 'tag',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
id: expect.any(String),
|
||||||
|
},
|
||||||
|
op: 'in',
|
||||||
|
value: ['frontend'],
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const clearButton = getByTestId('collapse-serviceName-clearBtn');
|
||||||
|
expect(clearButton).toBeInTheDocument();
|
||||||
|
fireEvent.click(clearButton);
|
||||||
|
|
||||||
|
// check if cleared and not present in query
|
||||||
|
expect(
|
||||||
|
redirectWithQueryBuilderData.mock.calls[
|
||||||
|
redirectWithQueryBuilderData.mock.calls.length - 1
|
||||||
|
][0].builder.queryData[0].filters.items,
|
||||||
|
).not.toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
key: {
|
||||||
|
key: 'serviceName',
|
||||||
|
dataType: 'string',
|
||||||
|
type: 'tag',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
id: expect.any(String),
|
||||||
|
},
|
||||||
|
op: 'in',
|
||||||
|
value: ['frontend'],
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
// check if reset button is present
|
||||||
|
const resetButton = getByTestId('reset-filters');
|
||||||
|
expect(resetButton).toBeInTheDocument();
|
||||||
|
fireEvent.click(resetButton);
|
||||||
|
|
||||||
|
// check if reset id done
|
||||||
|
expect(
|
||||||
|
redirectWithQueryBuilderData.mock.calls[
|
||||||
|
redirectWithQueryBuilderData.mock.calls.length - 1
|
||||||
|
][0].builder.queryData[0].filters.items,
|
||||||
|
).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filter panel should collapse & uncollapsed', async () => {
|
||||||
|
const { getByText, getByTestId } = render(<TracesExplorer />);
|
||||||
|
|
||||||
|
Object.values(AllTraceFilterKeyValue).forEach((filter) => {
|
||||||
|
expect(getByText(filter)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter panel should collapse
|
||||||
|
const collapseButton = getByTestId('toggle-filter-panel');
|
||||||
|
expect(collapseButton).toBeInTheDocument();
|
||||||
|
fireEvent.click(collapseButton);
|
||||||
|
|
||||||
|
// uncollapse btn should be present
|
||||||
|
expect(
|
||||||
|
await screen.findByTestId('filter-uncollapse-btn'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -251,6 +251,7 @@ function TracesExplorer(): JSX.Element {
|
|||||||
<Button
|
<Button
|
||||||
onClick={(): void => setOpen(!isOpen)}
|
onClick={(): void => setOpen(!isOpen)}
|
||||||
className="filter-outlined-btn"
|
className="filter-outlined-btn"
|
||||||
|
data-testid="filter-uncollapse-btn"
|
||||||
>
|
>
|
||||||
<FilterOutlined />
|
<FilterOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user