Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion conf/example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ cloudintegration:
version: v0.0.8

##################### Trace Detail #####################
tracedetail:
traces:
waterfall:
# Number of spans returned per request when the trace is too large to show all at once.
span_page_size: 500
Expand Down
28 changes: 0 additions & 28 deletions frontend/__mocks__/useSafeNavigate.ts

This file was deleted.

6 changes: 5 additions & 1 deletion frontend/jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Config } from '@jest/types';

const USE_SAFE_NAVIGATE_MOCK_PATH = '<rootDir>/__mocks__/useSafeNavigate.ts';
const USE_SAFE_NAVIGATE_MOCK_PATH =
'<rootDir>/src/__tests__/safeNavigateMock.ts';
const LOG_EVENT_MOCK_PATH = '<rootDir>/src/__tests__/logEventMock.ts';

const config: Config.InitialOptions = {
silent: true,
Expand All @@ -22,6 +24,8 @@ const config: Config.InitialOptions = {
'^hooks/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
'^src/hooks/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
'^.*/useSafeNavigate$': USE_SAFE_NAVIGATE_MOCK_PATH,
'^api/common/logEvent$': LOG_EVENT_MOCK_PATH,
'^src/api/common/logEvent$': LOG_EVENT_MOCK_PATH,
'^constants/env$': '<rootDir>/__mocks__/env.ts',
'^src/constants/env$': '<rootDir>/__mocks__/env.ts',
'^@signozhq/icons$': '<rootDir>/__mocks__/signozhqIconsMock.tsx',
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/__tests__/logEventMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Shared mock for `api/common/logEvent`.
// Wired into jest.config.ts moduleNameMapper, so any import of
// `api/common/logEvent` in test code resolves to this file.
// Tests can import `logEventMock` to assert analytics calls — Jest's
// `clearMocks: true` resets call history between tests.

export const logEventMock: jest.MockedFunction<
(eventName: string, attributes?: Record<string, unknown>) => void
> = jest.fn();

export default logEventMock;
29 changes: 29 additions & 0 deletions frontend/src/__tests__/safeNavigateMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Shared mock for `hooks/useSafeNavigate`.
// Wired into jest.config.ts moduleNameMapper, so any import of
// `hooks/useSafeNavigate` in test code resolves to this file.
// Tests can import `safeNavigateMock` to assert navigation calls — Jest's
// `clearMocks: true` resets call history between tests.

interface SafeNavigateOptions {
replace?: boolean;
state?: unknown;
newTab?: boolean;
}

interface SafeNavigateTo {
pathname?: string;
search?: string;
hash?: string;
}

type SafeNavigateToType = string | SafeNavigateTo;

export const safeNavigateMock: jest.MockedFunction<
(to: SafeNavigateToType, options?: SafeNavigateOptions) => void
> = jest.fn();

export const useSafeNavigate = (): {
safeNavigate: typeof safeNavigateMock;
} => ({
safeNavigate: safeNavigateMock,
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,12 @@ import { useLocation } from 'react-router-dom';
import { toast } from '@signozhq/ui/sonner';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import logEvent from 'api/common/logEvent';
import { logEventMock } from '__tests__/logEventMock';
import { handleContactSupport } from 'container/Integrations/utils';
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';

import FeedbackModal from '../FeedbackModal';

jest.mock('api/common/logEvent', () => ({
__esModule: true,
default: jest.fn(() => Promise.resolve()),
}));

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: jest.fn(),
Expand All @@ -35,7 +30,6 @@ jest.mock('container/Integrations/utils', () => ({
handleContactSupport: jest.fn(),
}));

const mockLogEvent = logEvent as jest.MockedFunction<typeof logEvent>;
const mockUseLocation = useLocation as jest.Mock;
const mockUseGetTenantLicense = useGetTenantLicense as jest.Mock;
const mockHandleContactSupport = handleContactSupport as jest.Mock;
Expand All @@ -50,6 +44,7 @@ const mockLocation = {
describe('FeedbackModal', () => {
beforeEach(() => {
jest.clearAllMocks();
logEventMock.mockReturnValue(Promise.resolve() as never);
mockUseLocation.mockReturnValue(mockLocation);
mockUseGetTenantLicense.mockReturnValue({
isCloudUser: false,
Expand Down Expand Up @@ -116,7 +111,7 @@ describe('FeedbackModal', () => {
await user.type(textarea, testFeedback);
await user.click(submitButton);

expect(mockLogEvent).toHaveBeenCalledWith('Feedback: Submitted', {
expect(logEventMock).toHaveBeenCalledWith('Feedback: Submitted', {
data: testFeedback,
type: 'feedback',
page: mockLocation.pathname,
Expand Down Expand Up @@ -149,7 +144,7 @@ describe('FeedbackModal', () => {
await user.type(textarea, testFeedback);
await user.click(submitButton);

expect(mockLogEvent).toHaveBeenCalledWith('Feedback: Submitted', {
expect(logEventMock).toHaveBeenCalledWith('Feedback: Submitted', {
data: testFeedback,
type: 'reportBug',
page: mockLocation.pathname,
Expand Down Expand Up @@ -182,7 +177,7 @@ describe('FeedbackModal', () => {
await user.type(textarea, testFeedback);
await user.click(submitButton);

expect(mockLogEvent).toHaveBeenCalledWith('Feedback: Submitted', {
expect(logEventMock).toHaveBeenCalledWith('Feedback: Submitted', {
data: testFeedback,
type: 'featureRequest',
page: mockLocation.pathname,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@
import { useLocation } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import logEvent from 'api/common/logEvent';
import { logEventMock } from '__tests__/logEventMock';
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';

import HeaderRightSection from '../HeaderRightSection';

jest.mock('api/common/logEvent', () => ({
__esModule: true,
default: jest.fn(),
}));

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: jest.fn(),
Expand Down Expand Up @@ -50,7 +45,6 @@ jest.mock('hooks/useIsAIAssistantEnabled', () => ({
useIsAIAssistantEnabled: (): boolean => false,
}));

const mockLogEvent = logEvent as jest.Mock;
const mockUseLocation = useLocation as jest.Mock;
const mockUseGetTenantLicense = useGetTenantLicense as jest.Mock;

Expand Down Expand Up @@ -120,7 +114,7 @@ describe('HeaderRightSection', () => {

await user.click(feedbackButton!);

expect(mockLogEvent).toHaveBeenCalledWith('Feedback: Clicked', {
expect(logEventMock).toHaveBeenCalledWith('Feedback: Clicked', {
page: mockLocation.pathname,
});
expect(screen.getByTestId('feedback-modal')).toBeInTheDocument();
Expand All @@ -133,7 +127,7 @@ describe('HeaderRightSection', () => {
const shareButton = screen.getByRole('button', { name: /share/i });
await user.click(shareButton);

expect(mockLogEvent).toHaveBeenCalledWith('Share: Clicked', {
expect(logEventMock).toHaveBeenCalledWith('Share: Clicked', {
page: mockLocation.pathname,
});
expect(screen.getByTestId('share-modal')).toBeInTheDocument();
Expand All @@ -150,7 +144,7 @@ describe('HeaderRightSection', () => {

await user.click(announcementsButton!);

expect(mockLogEvent).toHaveBeenCalledWith('Announcements: Clicked', {
expect(logEventMock).toHaveBeenCalledWith('Announcements: Clicked', {
page: mockLocation.pathname,
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,13 @@ import { matchPath, useLocation } from 'react-router-dom';
import { useCopyToClipboard } from 'react-use';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import logEvent from 'api/common/logEvent';
import { logEventMock } from '__tests__/logEventMock';
import ROUTES from 'constants/routes';
import useUrlQuery from 'hooks/useUrlQuery';
import GetMinMax from 'lib/getMinMax';

import ShareURLModal from '../ShareURLModal';

jest.mock('api/common/logEvent', () => ({
__esModule: true,
default: jest.fn(),
}));

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: jest.fn(),
Expand Down Expand Up @@ -53,7 +48,6 @@ Object.defineProperty(window, 'location', {
writable: true,
});

const mockLogEvent = logEvent as jest.Mock;
const mockUseLocation = useLocation as jest.Mock;
const mockUseUrlQuery = useUrlQuery as jest.Mock;
const mockUseSelector = useSelector as jest.Mock;
Expand Down Expand Up @@ -125,7 +119,7 @@ describe('ShareURLModal', () => {
await user.click(copyButton);

expect(mockHandleCopyToClipboard).toHaveBeenCalled();
expect(mockLogEvent).toHaveBeenCalledWith('Share: Copy link clicked', {
expect(logEventMock).toHaveBeenCalledWith('Share: Copy link clicked', {
page: TEST_PATH,
URL: expect.any(String),
});
Expand Down
47 changes: 47 additions & 0 deletions frontend/src/components/QueryBuilderV2/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,53 @@ export const removeKeysFromExpression = (
return result?.text ?? '';
};

const escapeRegExp = (value: string): string =>
value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

export const createVariablePlaceholderRegExp = (
variableName: string,
): RegExp => {
const escapedName = escapeRegExp(variableName);
// (?![\w.]) prevents $env from matching inside $environment or $env.attr
return new RegExp(
`(\\$${escapedName}(?![\\w.])|\\{\\{\\s*\\.?${escapedName}\\s*\\}\\}|\\[\\[\\s*${escapedName}\\s*\\]\\])`,
'g',
);
};

const matchesVariablePlaceholder = (
text: string,
variableName: string,
): boolean => createVariablePlaceholderRegExp(variableName).test(text);

export const removeVariableFromExpression = (
expression: string | undefined,
variableName: string,
): string => {
if (!expression) {
return '';
}

const queryPairs = extractQueryPairs(expression);

const keysToRemove = queryPairs
.filter((pair) => {
const singleValue = pair.value?.toString() ?? '';
const listValues = (pair.valueList ?? []).join(' ');
return (
matchesVariablePlaceholder(singleValue, variableName) ||
matchesVariablePlaceholder(listValues, variableName)
);
})
.map((pair) => pair.key);

if (keysToRemove.length === 0) {
return expression;
}

return removeKeysFromExpression(expression, keysToRemove, `$${variableName}`);
};

/**
* Convert old having format to new having format
* @param having - Array of old having objects with columnName, op, and value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ jest.mock('react-query', (): unknown => {
});

// mock other side-effecty modules
jest.mock('api/common/logEvent', () => jest.fn());
jest.mock('api/browser/localstorage/set', () => jest.fn());
jest.mock('utils/error', () => ({ showErrorNotification: jest.fn() }));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { QueryClient, QueryClientProvider } from 'react-query';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import logEvent from 'api/common/logEvent';
import { logEventMock } from '__tests__/logEventMock';
import { GlobalShortcuts } from 'constants/shortcuts/globalShortcuts';
import { USER_PREFERENCES } from 'constants/userPreferences';
import {
Expand All @@ -24,8 +24,6 @@ jest.mock('providers/cmdKProvider', () => ({
}),
}));

jest.mock('api/common/logEvent', () => jest.fn());

// Mock the AppContext
const mockUpdateUserPreferenceInContext = jest.fn();

Expand Down Expand Up @@ -139,7 +137,7 @@ describe('Sidebar Toggle Shortcut', () => {
it('should log the toggle event with correct parameters', async () => {
const user = userEvent.setup();
const mockHandleShortcut = jest.fn(() => {
logEvent('Global Shortcut: Sidebar Toggle', {
logEventMock('Global Shortcut: Sidebar Toggle', {
previousState: false,
newState: true,
});
Expand All @@ -155,10 +153,13 @@ describe('Sidebar Toggle Shortcut', () => {

await user.keyboard(SHIFT_B_KEYBOARD_SHORTCUT);

expect(logEvent).toHaveBeenCalledWith('Global Shortcut: Sidebar Toggle', {
previousState: false,
newState: true,
});
expect(logEventMock).toHaveBeenCalledWith(
'Global Shortcut: Sidebar Toggle',
{
previousState: false,
newState: true,
},
);
});

it('should update user preference in context', async () => {
Expand Down
Loading
Loading