Compare commits
24 Commits
fix/span-l
...
enhancemen
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aea42ba5ca | ||
|
|
455ba0549f | ||
|
|
5f2c302551 | ||
|
|
15c2dc700a | ||
|
|
02fa0dbc32 | ||
|
|
e0948033c8 | ||
|
|
a1115ac65b | ||
|
|
9bcb88c747 | ||
|
|
367bf7b4b5 | ||
|
|
59b68057b8 | ||
|
|
fa1b2ddf7c | ||
|
|
642a0e5656 | ||
|
|
cb99ee1ac1 | ||
|
|
7616cb89e4 | ||
|
|
bf780c7445 | ||
|
|
61062dfd8d | ||
|
|
5b7af9651c | ||
|
|
b9012f6150 | ||
|
|
7ab81780b3 | ||
|
|
a16f51457f | ||
|
|
38a38b5645 | ||
|
|
bb04bc5044 | ||
|
|
58736f40dc | ||
|
|
91154249d6 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -106,6 +106,7 @@ downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
!frontend/src/lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
|
||||
@@ -1,63 +1,43 @@
|
||||
version: "2"
|
||||
linters:
|
||||
default: none
|
||||
default: standard
|
||||
enable:
|
||||
- bodyclose
|
||||
- depguard
|
||||
- errcheck
|
||||
- forbidigo
|
||||
- govet
|
||||
- iface
|
||||
- ineffassign
|
||||
- misspell
|
||||
- nilnil
|
||||
- sloglint
|
||||
- depguard
|
||||
- iface
|
||||
- unparam
|
||||
- unused
|
||||
settings:
|
||||
depguard:
|
||||
rules:
|
||||
noerrors:
|
||||
deny:
|
||||
- pkg: errors
|
||||
desc: Do not use errors package. Use github.com/SigNoz/signoz/pkg/errors instead.
|
||||
nozap:
|
||||
deny:
|
||||
- pkg: go.uber.org/zap
|
||||
desc: Do not use zap logger. Use slog instead.
|
||||
forbidigo:
|
||||
forbid:
|
||||
- pattern: fmt.Errorf
|
||||
- pattern: ^(fmt\.Print.*|print|println)$
|
||||
iface:
|
||||
enable:
|
||||
- identical
|
||||
sloglint:
|
||||
no-mixed-args: true
|
||||
kv-only: true
|
||||
no-global: all
|
||||
context: all
|
||||
static-msg: true
|
||||
key-naming-case: snake
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- pkg/query-service
|
||||
- ee/query-service
|
||||
- scripts/
|
||||
- tmp/
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
- forbidigo
|
||||
|
||||
linters-settings:
|
||||
sloglint:
|
||||
no-mixed-args: true
|
||||
kv-only: true
|
||||
no-global: all
|
||||
context: all
|
||||
static-msg: true
|
||||
msg-style: lowercased
|
||||
key-naming-case: snake
|
||||
depguard:
|
||||
rules:
|
||||
nozap:
|
||||
deny:
|
||||
- pkg: "go.uber.org/zap"
|
||||
desc: "Do not use zap logger. Use slog instead."
|
||||
noerrors:
|
||||
deny:
|
||||
- pkg: "errors"
|
||||
desc: "Do not use errors package. Use github.com/SigNoz/signoz/pkg/errors instead."
|
||||
iface:
|
||||
enable:
|
||||
- identical
|
||||
forbidigo:
|
||||
forbid:
|
||||
- fmt.Errorf
|
||||
- ^(fmt\.Print.*|print|println)$
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- "pkg/query-service"
|
||||
- "ee/query-service"
|
||||
- "scripts/"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// Mock for useSafeNavigate hook to avoid React Router version conflicts in tests
|
||||
export { isEventObject } from '../src/utils/isEventObject';
|
||||
|
||||
interface SafeNavigateOptions {
|
||||
replace?: boolean;
|
||||
state?: unknown;
|
||||
|
||||
@@ -23,11 +23,15 @@ const mockAlerts = [mockAlert1, mockAlert2];
|
||||
const mockDashboards = [mockDashboard1, mockDashboard2];
|
||||
|
||||
const mockSafeNavigate = jest.fn();
|
||||
jest.mock('hooks/useSafeNavigate', () => ({
|
||||
useSafeNavigate: (): any => ({
|
||||
safeNavigate: mockSafeNavigate,
|
||||
}),
|
||||
}));
|
||||
jest.mock('hooks/useSafeNavigate', () => {
|
||||
const actual = jest.requireActual('hooks/useSafeNavigate');
|
||||
return {
|
||||
...actual,
|
||||
useSafeNavigate: (): any => ({
|
||||
safeNavigate: mockSafeNavigate,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const mockSetQuery = jest.fn();
|
||||
const mockUrlQuery = {
|
||||
|
||||
@@ -112,6 +112,7 @@ export const useSpanContextLogs = ({
|
||||
traceId,
|
||||
spanId,
|
||||
timeRange,
|
||||
isDrawerOpen = true,
|
||||
}: UseSpanContextLogsProps): UseSpanContextLogsReturn => {
|
||||
const [allLogs, setAllLogs] = useState<ILog[]>([]);
|
||||
const [spanLogIds, setSpanLogIds] = useState<Set<string>>(new Set());
|
||||
@@ -292,7 +293,7 @@ export const useSpanContextLogs = ({
|
||||
],
|
||||
queryFn: () =>
|
||||
GetMetricQueryRange(traceOnlyQueryPayload as any, ENTITY_VERSION_V5),
|
||||
enabled: !!traceOnlyQueryPayload,
|
||||
enabled: isDrawerOpen && !!traceOnlyQueryPayload && spanLogs.length === 0,
|
||||
staleTime: FIVE_MINUTES_IN_MS,
|
||||
});
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ function SpanRelatedSignals({
|
||||
startTime: traceStartTime - FIVE_MINUTES_IN_MS,
|
||||
endTime: traceEndTime + FIVE_MINUTES_IN_MS,
|
||||
},
|
||||
isDrawerOpen: isOpen,
|
||||
});
|
||||
|
||||
const handleTabChange = useCallback((e: RadioChangeEvent): void => {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { cloneDeep, isEqual } from 'lodash-es';
|
||||
import { useCallback } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom-v5-compat';
|
||||
import {
|
||||
Location,
|
||||
NavigateFunction,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
} from 'react-router-dom-v5-compat';
|
||||
import { isEventObject } from 'utils/isEventObject';
|
||||
|
||||
// state uses 'any' because react-router's NavigateOptions interface uses it
|
||||
interface NavigateOptions {
|
||||
replace?: boolean;
|
||||
state?: any;
|
||||
@@ -83,6 +90,74 @@ const isDefaultNavigation = (currentUrl: URL, targetUrl: URL): boolean => {
|
||||
|
||||
return newKeys.length > 0;
|
||||
};
|
||||
|
||||
// Helper function to extract options from arguments
|
||||
const extractOptions = (
|
||||
optionsOrEvent?:
|
||||
| NavigateOptions
|
||||
| React.MouseEvent
|
||||
| MouseEvent
|
||||
| KeyboardEvent,
|
||||
options?: NavigateOptions,
|
||||
): NavigateOptions => {
|
||||
const isEvent = isEventObject(optionsOrEvent);
|
||||
const actualOptions = isEvent ? options : (optionsOrEvent as NavigateOptions);
|
||||
|
||||
const shouldOpenInNewTab =
|
||||
isEvent && (optionsOrEvent.metaKey || optionsOrEvent.ctrlKey);
|
||||
|
||||
return {
|
||||
...actualOptions,
|
||||
newTab: shouldOpenInNewTab || actualOptions?.newTab,
|
||||
};
|
||||
};
|
||||
|
||||
// Helper function to create target URL
|
||||
const createTargetUrl = (
|
||||
to: string | SafeNavigateParams,
|
||||
location: Location,
|
||||
): URL => {
|
||||
if (typeof to === 'string') {
|
||||
return new URL(to, window.location.origin);
|
||||
}
|
||||
return new URL(
|
||||
`${to.pathname || location.pathname}${to.search || ''}`,
|
||||
window.location.origin,
|
||||
);
|
||||
};
|
||||
|
||||
// Helper function to handle new tab navigation
|
||||
const handleNewTabNavigation = (
|
||||
to: string | SafeNavigateParams,
|
||||
location: Location,
|
||||
): void => {
|
||||
const targetPath =
|
||||
typeof to === 'string'
|
||||
? to
|
||||
: `${to.pathname || location.pathname}${to.search || ''}`;
|
||||
window.open(targetPath, '_blank');
|
||||
};
|
||||
|
||||
// Helper function to perform navigation
|
||||
const performNavigation = (
|
||||
to: string | SafeNavigateParams,
|
||||
navigationOptions: NavigateOptions,
|
||||
navigate: NavigateFunction,
|
||||
location: Location,
|
||||
): void => {
|
||||
if (typeof to === 'string') {
|
||||
navigate(to, navigationOptions);
|
||||
} else {
|
||||
navigate(
|
||||
{
|
||||
pathname: to.pathname || location.pathname,
|
||||
search: to.search,
|
||||
},
|
||||
navigationOptions,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const useSafeNavigate = (
|
||||
{ preventSameUrlNavigation }: UseSafeNavigateProps = {
|
||||
preventSameUrlNavigation: true,
|
||||
@@ -90,6 +165,11 @@ export const useSafeNavigate = (
|
||||
): {
|
||||
safeNavigate: (
|
||||
to: string | SafeNavigateParams,
|
||||
optionsOrEvent?:
|
||||
| NavigateOptions
|
||||
| React.MouseEvent
|
||||
| MouseEvent
|
||||
| KeyboardEvent,
|
||||
options?: NavigateOptions,
|
||||
) => void;
|
||||
} => {
|
||||
@@ -97,30 +177,25 @@ export const useSafeNavigate = (
|
||||
const location = useLocation();
|
||||
|
||||
const safeNavigate = useCallback(
|
||||
(to: string | SafeNavigateParams, options?: NavigateOptions) => {
|
||||
(
|
||||
to: string | SafeNavigateParams,
|
||||
optionsOrEvent?:
|
||||
| NavigateOptions
|
||||
| React.MouseEvent
|
||||
| MouseEvent
|
||||
| KeyboardEvent,
|
||||
options?: NavigateOptions,
|
||||
) => {
|
||||
const finalOptions = extractOptions(optionsOrEvent, options);
|
||||
const currentUrl = new URL(
|
||||
`${location.pathname}${location.search}`,
|
||||
window.location.origin,
|
||||
);
|
||||
const targetUrl = createTargetUrl(to, location);
|
||||
|
||||
let targetUrl: URL;
|
||||
|
||||
if (typeof to === 'string') {
|
||||
targetUrl = new URL(to, window.location.origin);
|
||||
} else {
|
||||
targetUrl = new URL(
|
||||
`${to.pathname || location.pathname}${to.search || ''}`,
|
||||
window.location.origin,
|
||||
);
|
||||
}
|
||||
|
||||
// If newTab is true, open in new tab and return early
|
||||
if (options?.newTab) {
|
||||
const targetPath =
|
||||
typeof to === 'string'
|
||||
? to
|
||||
: `${to.pathname || location.pathname}${to.search || ''}`;
|
||||
window.open(targetPath, '_blank');
|
||||
// Handle new tab navigation
|
||||
if (finalOptions?.newTab) {
|
||||
handleNewTabNavigation(to, location);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -132,23 +207,13 @@ export const useSafeNavigate = (
|
||||
}
|
||||
|
||||
const navigationOptions = {
|
||||
...options,
|
||||
replace: isDefaultParamsNavigation || options?.replace,
|
||||
...finalOptions,
|
||||
replace: isDefaultParamsNavigation || finalOptions?.replace,
|
||||
};
|
||||
|
||||
if (typeof to === 'string') {
|
||||
navigate(to, navigationOptions);
|
||||
} else {
|
||||
navigate(
|
||||
{
|
||||
pathname: to.pathname || location.pathname,
|
||||
search: to.search,
|
||||
},
|
||||
navigationOptions,
|
||||
);
|
||||
}
|
||||
performNavigation(to, navigationOptions, navigate, location);
|
||||
},
|
||||
[navigate, location.pathname, location.search, preventSameUrlNavigation],
|
||||
[navigate, location, preventSameUrlNavigation],
|
||||
);
|
||||
|
||||
return { safeNavigate };
|
||||
|
||||
634
frontend/src/lib/__tests__/history.test.ts
Normal file
634
frontend/src/lib/__tests__/history.test.ts
Normal file
@@ -0,0 +1,634 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { LocationDescriptorObject } from 'history';
|
||||
|
||||
import history from '../history';
|
||||
|
||||
jest.mock('history', () => {
|
||||
const actualHistory = jest.requireActual('history');
|
||||
const mockPush = jest.fn();
|
||||
const mockReplace = jest.fn();
|
||||
const mockGo = jest.fn();
|
||||
const mockGoBack = jest.fn();
|
||||
const mockGoForward = jest.fn();
|
||||
const mockBlock = jest.fn(() => jest.fn());
|
||||
const mockListen = jest.fn(() => jest.fn());
|
||||
const mockCreateHref = jest.fn((location) => {
|
||||
if (typeof location === 'string') return location;
|
||||
return actualHistory.createPath(location);
|
||||
});
|
||||
|
||||
const baseHistory = {
|
||||
length: 2,
|
||||
action: 'PUSH' as const,
|
||||
location: {
|
||||
pathname: '/current-path',
|
||||
search: '?existing=param',
|
||||
hash: '#section',
|
||||
state: { existing: 'state' },
|
||||
key: 'test-key',
|
||||
},
|
||||
push: mockPush,
|
||||
replace: mockReplace,
|
||||
go: mockGo,
|
||||
goBack: mockGoBack,
|
||||
goForward: mockGoForward,
|
||||
block: mockBlock,
|
||||
listen: mockListen,
|
||||
createHref: mockCreateHref,
|
||||
};
|
||||
|
||||
return {
|
||||
...actualHistory,
|
||||
createBrowserHistory: jest.fn(() => baseHistory),
|
||||
};
|
||||
});
|
||||
|
||||
interface TestUser {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
interface TestState {
|
||||
from?: string;
|
||||
user?: TestUser;
|
||||
timestamp?: number;
|
||||
}
|
||||
|
||||
describe('Enhanced History Methods', () => {
|
||||
let mockWindowOpen: jest.SpyInstance;
|
||||
let originalPush: jest.MockedFunction<typeof history.push>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
mockWindowOpen = jest.spyOn(window, 'open').mockImplementation(() => null);
|
||||
|
||||
originalPush = history.originalPush as jest.MockedFunction<
|
||||
typeof history.push
|
||||
>;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockWindowOpen.mockRestore();
|
||||
});
|
||||
|
||||
describe('history.push() - String Path Navigation', () => {
|
||||
it('should handle simple string path navigation', () => {
|
||||
history.push('/dashboard');
|
||||
|
||||
expect(originalPush).toHaveBeenCalledTimes(1);
|
||||
expect(originalPush).toHaveBeenCalledWith('/dashboard', undefined);
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle string path with state', () => {
|
||||
const testState: TestState = { from: 'home', timestamp: Date.now() };
|
||||
|
||||
history.push('/dashboard', testState);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith('/dashboard', testState);
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle string path with query parameters', () => {
|
||||
history.push('/logs?filter=error&timeRange=24h');
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(
|
||||
'/logs?filter=error&timeRange=24h',
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle string path with hash', () => {
|
||||
history.push('/docs#installation');
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith('/docs#installation', undefined);
|
||||
});
|
||||
|
||||
it('should handle complex URL with all components', () => {
|
||||
const complexUrl = '/api/traces?service=backend&status=error#span-details';
|
||||
const state: TestState = {
|
||||
user: { id: 1, name: 'John', email: 'john@test.com' },
|
||||
};
|
||||
|
||||
history.push(complexUrl, state);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(complexUrl, state);
|
||||
});
|
||||
});
|
||||
|
||||
describe('history.push() - Location Object Navigation', () => {
|
||||
it('should handle location object with only pathname', () => {
|
||||
const location: LocationDescriptorObject = {
|
||||
pathname: '/metrics',
|
||||
};
|
||||
|
||||
history.push(location);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(location, undefined);
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle location object with pathname and search', () => {
|
||||
const location: LocationDescriptorObject = {
|
||||
pathname: '/logs',
|
||||
search: '?filter=error&severity=high',
|
||||
};
|
||||
|
||||
history.push(location);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(location, undefined);
|
||||
});
|
||||
|
||||
it('should handle location object with all properties', () => {
|
||||
const location: LocationDescriptorObject<TestState> = {
|
||||
pathname: '/traces',
|
||||
search: '?service=api-server&duration=slow',
|
||||
hash: '#span-123',
|
||||
state: { from: 'dashboard', timestamp: Date.now() },
|
||||
key: 'unique-key',
|
||||
};
|
||||
|
||||
history.push(location);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(location, undefined);
|
||||
});
|
||||
|
||||
it('should handle location object with state passed separately', () => {
|
||||
const location: LocationDescriptorObject = {
|
||||
pathname: '/alerts',
|
||||
search: '?type=critical',
|
||||
};
|
||||
const separateState: TestState = { from: 'monitoring' };
|
||||
|
||||
history.push(location, separateState);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(location, separateState);
|
||||
});
|
||||
|
||||
it('should handle empty location object', () => {
|
||||
const location: LocationDescriptorObject = {};
|
||||
|
||||
history.push(location);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(location, undefined);
|
||||
});
|
||||
|
||||
it('should preserve current pathname when updating search', () => {
|
||||
const location: LocationDescriptorObject = {
|
||||
pathname: history.location.pathname,
|
||||
search: '?newParam=value',
|
||||
};
|
||||
|
||||
history.push(location);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(location, undefined);
|
||||
expect(originalPush.mock.calls[0][0]).toHaveProperty(
|
||||
'pathname',
|
||||
'/current-path',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('history.push() - Event Handling (Cmd/Ctrl+Click)', () => {
|
||||
describe('MouseEvent handling', () => {
|
||||
it('should open in new tab when metaKey is pressed with string path', () => {
|
||||
const event = new MouseEvent('click', { metaKey: true });
|
||||
|
||||
history.push('/dashboard', event);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith('/dashboard', '_blank');
|
||||
expect(originalPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should open in new tab when ctrlKey is pressed with string path', () => {
|
||||
const event = new MouseEvent('click', { ctrlKey: true });
|
||||
|
||||
history.push('/metrics', event);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith('/metrics', '_blank');
|
||||
expect(originalPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should open in new tab when both metaKey and ctrlKey are pressed', () => {
|
||||
const event = new MouseEvent('click', { metaKey: true, ctrlKey: true });
|
||||
|
||||
history.push('/logs', event);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith('/logs', '_blank');
|
||||
expect(originalPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle normal click without meta/ctrl keys', () => {
|
||||
const event = new MouseEvent('click', { metaKey: false, ctrlKey: false });
|
||||
const state: TestState = { from: 'nav' };
|
||||
|
||||
history.push('/alerts', event, state);
|
||||
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled();
|
||||
expect(originalPush).toHaveBeenCalledWith('/alerts', state);
|
||||
});
|
||||
});
|
||||
|
||||
describe('KeyboardEvent handling', () => {
|
||||
it('should open in new tab when metaKey is pressed with keyboard event', () => {
|
||||
const event = new KeyboardEvent('keydown', { metaKey: true });
|
||||
|
||||
history.push('/traces', event);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith('/traces', '_blank');
|
||||
expect(originalPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should open in new tab when ctrlKey is pressed with keyboard event', () => {
|
||||
const event = new KeyboardEvent('keydown', { ctrlKey: true });
|
||||
|
||||
history.push('/pipelines', event);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith('/pipelines', '_blank');
|
||||
expect(originalPush).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('React SyntheticEvent handling', () => {
|
||||
it('should handle React MouseEvent with metaKey', () => {
|
||||
const nativeEvent = new MouseEvent('click', { metaKey: true });
|
||||
const reactEvent = {
|
||||
nativeEvent,
|
||||
metaKey: true,
|
||||
ctrlKey: false,
|
||||
} as React.MouseEvent;
|
||||
|
||||
history.push('/dashboard', reactEvent);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith('/dashboard', '_blank');
|
||||
expect(originalPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle React MouseEvent with ctrlKey', () => {
|
||||
const nativeEvent = new MouseEvent('click', { ctrlKey: true });
|
||||
const reactEvent = {
|
||||
nativeEvent,
|
||||
metaKey: false,
|
||||
ctrlKey: true,
|
||||
} as React.MouseEvent;
|
||||
|
||||
history.push('/logs', reactEvent);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith('/logs', '_blank');
|
||||
expect(originalPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle React MouseEvent without modifier keys', () => {
|
||||
const nativeEvent = new MouseEvent('click');
|
||||
const reactEvent = {
|
||||
nativeEvent,
|
||||
metaKey: false,
|
||||
ctrlKey: false,
|
||||
} as React.MouseEvent;
|
||||
|
||||
history.push('/metrics', reactEvent);
|
||||
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled();
|
||||
expect(originalPush).toHaveBeenCalledWith('/metrics', undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Location Object with Event handling', () => {
|
||||
it('should open location object URL in new tab with metaKey', () => {
|
||||
const location: LocationDescriptorObject = {
|
||||
pathname: '/traces',
|
||||
search: '?service=backend',
|
||||
hash: '#span-details',
|
||||
};
|
||||
const event = new MouseEvent('click', { metaKey: true });
|
||||
|
||||
history.push(location, event);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith(
|
||||
'/traces?service=backend#span-details',
|
||||
'_blank',
|
||||
);
|
||||
expect(originalPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should open location object URL in new tab with ctrlKey', () => {
|
||||
const location: LocationDescriptorObject = {
|
||||
pathname: '/alerts',
|
||||
search: '?status=firing',
|
||||
};
|
||||
const event = new MouseEvent('click', { ctrlKey: true });
|
||||
|
||||
history.push(location, event);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith(
|
||||
'/alerts?status=firing',
|
||||
'_blank',
|
||||
);
|
||||
expect(originalPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle location object with normal navigation', () => {
|
||||
const location: LocationDescriptorObject = {
|
||||
pathname: '/dashboard',
|
||||
search: '?tab=overview',
|
||||
};
|
||||
const event = new MouseEvent('click', { metaKey: false, ctrlKey: false });
|
||||
const state: TestState = { from: 'home' };
|
||||
|
||||
history.push(location, event, state);
|
||||
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled();
|
||||
expect(originalPush).toHaveBeenCalledWith(location, state);
|
||||
});
|
||||
|
||||
it('should handle complex location object with all properties in new tab', () => {
|
||||
const location: LocationDescriptorObject<TestState> = {
|
||||
pathname: '/api/v1/traces',
|
||||
search: '?limit=100&offset=0&service=auth',
|
||||
hash: '#result-section',
|
||||
state: { from: 'explorer' }, // State is ignored in new tab
|
||||
};
|
||||
const event = new MouseEvent('click', { metaKey: true });
|
||||
|
||||
history.push(location, event);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith(
|
||||
'/api/v1/traces?limit=100&offset=0&service=auth#result-section',
|
||||
'_blank',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('history.push() - Edge Cases and Error Scenarios', () => {
|
||||
it('should handle undefined as second parameter', () => {
|
||||
history.push('/dashboard', undefined);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith('/dashboard', undefined);
|
||||
});
|
||||
|
||||
it('should handle null as second parameter', () => {
|
||||
history.push('/logs', null);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith('/logs', null);
|
||||
});
|
||||
|
||||
it('should handle empty string path', () => {
|
||||
history.push('');
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith('', undefined);
|
||||
});
|
||||
|
||||
it('should handle root path', () => {
|
||||
history.push('/');
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith('/', undefined);
|
||||
});
|
||||
|
||||
it('should handle relative paths', () => {
|
||||
history.push('../parent');
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith('../parent', undefined);
|
||||
});
|
||||
|
||||
it('should handle special characters in path', () => {
|
||||
const specialPath = '/path/with spaces/and#special?chars=@$%';
|
||||
|
||||
history.push(specialPath);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(specialPath, undefined);
|
||||
});
|
||||
|
||||
it('should handle location object with undefined values', () => {
|
||||
const location: LocationDescriptorObject = {
|
||||
pathname: undefined,
|
||||
search: undefined,
|
||||
hash: undefined,
|
||||
state: undefined,
|
||||
};
|
||||
|
||||
history.push(location);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(location, undefined);
|
||||
});
|
||||
|
||||
it('should handle very long URLs', () => {
|
||||
const longParam = 'x'.repeat(1000);
|
||||
const longUrl = `/path?param=${longParam}`;
|
||||
|
||||
history.push(longUrl);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(longUrl, undefined);
|
||||
});
|
||||
|
||||
it('should handle object that looks like an event but isnt', () => {
|
||||
const fakeEvent = {
|
||||
metaKey: 'not-a-boolean', // Invalid type but still truthy values
|
||||
ctrlKey: 'not-a-boolean',
|
||||
};
|
||||
|
||||
history.push('/dashboard', fakeEvent as any);
|
||||
|
||||
// The implementation checks if metaKey/ctrlKey exist and are truthy values
|
||||
// Since these are truthy strings, it will be treated as an event
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith('/dashboard', '_blank');
|
||||
expect(originalPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle event-like object with falsy values', () => {
|
||||
const fakeEventFalsy = {
|
||||
metaKey: false,
|
||||
ctrlKey: false,
|
||||
};
|
||||
|
||||
history.push('/dashboard', fakeEventFalsy as any);
|
||||
|
||||
// The object is detected as an event (has metaKey/ctrlKey properties)
|
||||
// but since both are false, it doesn't open in new tab
|
||||
// When treated as event, third param (state) is undefined
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled();
|
||||
expect(originalPush).toHaveBeenCalledWith('/dashboard', undefined);
|
||||
});
|
||||
|
||||
it('should handle partial event-like objects', () => {
|
||||
const partialEvent = { metaKey: true }; // Has metaKey but not instanceof MouseEvent
|
||||
|
||||
history.push('/logs', partialEvent as any);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith('/logs', '_blank');
|
||||
expect(originalPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle object without event properties as state', () => {
|
||||
const regularObject = {
|
||||
someData: 'value',
|
||||
anotherProp: 123,
|
||||
// No metaKey or ctrlKey properties
|
||||
};
|
||||
|
||||
history.push('/page', regularObject);
|
||||
|
||||
// Object without metaKey/ctrlKey is treated as state, not event
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled();
|
||||
expect(originalPush).toHaveBeenCalledWith('/page', regularObject);
|
||||
});
|
||||
});
|
||||
|
||||
describe('history.push() - State Handling', () => {
|
||||
it('should pass state with string path', () => {
|
||||
const complexState: TestState = {
|
||||
from: 'dashboard',
|
||||
user: { id: 123, name: 'Test User', email: 'test@example.com' },
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
history.push('/profile', complexState);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith('/profile', complexState);
|
||||
});
|
||||
|
||||
it('should handle state with location object', () => {
|
||||
const location: LocationDescriptorObject<TestState> = {
|
||||
pathname: '/settings',
|
||||
state: { from: 'profile' },
|
||||
};
|
||||
const additionalState: TestState = { timestamp: Date.now() };
|
||||
|
||||
history.push(location, additionalState);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(location, additionalState);
|
||||
});
|
||||
|
||||
it('should handle state with event and string path', () => {
|
||||
const event = new MouseEvent('click', { metaKey: false });
|
||||
const state: TestState = { from: 'nav' };
|
||||
|
||||
history.push('/dashboard', event, state);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith('/dashboard', state);
|
||||
});
|
||||
|
||||
it('should handle state with event and location object', () => {
|
||||
const location: LocationDescriptorObject = {
|
||||
pathname: '/logs',
|
||||
};
|
||||
const event = new MouseEvent('click', { metaKey: false });
|
||||
const state: TestState = { from: 'sidebar' };
|
||||
|
||||
history.push(location, event, state);
|
||||
|
||||
expect(originalPush).toHaveBeenCalledWith(location, state);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Other History Methods', () => {
|
||||
it('should have working replace method', () => {
|
||||
// replace should exist and be callable
|
||||
expect(history.replace).toBeDefined();
|
||||
expect(typeof history.replace).toBe('function');
|
||||
|
||||
history.replace('/new-path');
|
||||
|
||||
const mockReplace = (history as any).replace as jest.MockedFunction<
|
||||
typeof history.replace
|
||||
>;
|
||||
expect(mockReplace).toHaveBeenCalledWith('/new-path');
|
||||
});
|
||||
|
||||
it('should have working go method', () => {
|
||||
expect(history.go).toBeDefined();
|
||||
expect(typeof history.go).toBe('function');
|
||||
|
||||
history.go(-2);
|
||||
|
||||
const mockGo = (history as any).go as jest.MockedFunction<typeof history.go>;
|
||||
expect(mockGo).toHaveBeenCalledWith(-2);
|
||||
});
|
||||
|
||||
it('should have working goBack method', () => {
|
||||
expect(history.goBack).toBeDefined();
|
||||
expect(typeof history.goBack).toBe('function');
|
||||
|
||||
history.goBack();
|
||||
|
||||
const mockGoBack = (history as any).goBack as jest.MockedFunction<
|
||||
typeof history.goBack
|
||||
>;
|
||||
expect(mockGoBack).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have working goForward method', () => {
|
||||
expect(history.goForward).toBeDefined();
|
||||
expect(typeof history.goForward).toBe('function');
|
||||
|
||||
history.goForward();
|
||||
|
||||
const mockGoForward = (history as any).goForward as jest.MockedFunction<
|
||||
typeof history.goForward
|
||||
>;
|
||||
expect(mockGoForward).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have working block method', () => {
|
||||
expect(history.block).toBeDefined();
|
||||
expect(typeof history.block).toBe('function');
|
||||
|
||||
const unblock = history.block('Are you sure?');
|
||||
|
||||
expect(typeof unblock).toBe('function');
|
||||
const mockBlock = (history as any).block as jest.MockedFunction<
|
||||
typeof history.block
|
||||
>;
|
||||
expect(mockBlock).toHaveBeenCalledWith('Are you sure?');
|
||||
});
|
||||
|
||||
it('should have working listen method', () => {
|
||||
expect(history.listen).toBeDefined();
|
||||
expect(typeof history.listen).toBe('function');
|
||||
|
||||
const listener = jest.fn();
|
||||
|
||||
const unlisten = history.listen(listener);
|
||||
|
||||
expect(typeof unlisten).toBe('function');
|
||||
const mockListen = (history as any).listen as jest.MockedFunction<
|
||||
typeof history.listen
|
||||
>;
|
||||
expect(mockListen).toHaveBeenCalledWith(listener);
|
||||
});
|
||||
|
||||
it('should have working createHref method', () => {
|
||||
expect(history.createHref).toBeDefined();
|
||||
expect(typeof history.createHref).toBe('function');
|
||||
|
||||
const location: LocationDescriptorObject = {
|
||||
pathname: '/test',
|
||||
search: '?query=value',
|
||||
};
|
||||
|
||||
const href = history.createHref(location);
|
||||
|
||||
expect(href).toBe('/test?query=value');
|
||||
});
|
||||
|
||||
it('should have accessible location property', () => {
|
||||
expect(history.location).toBeDefined();
|
||||
expect(history.location.pathname).toBe('/current-path');
|
||||
expect(history.location.search).toBe('?existing=param');
|
||||
expect(history.location.hash).toBe('#section');
|
||||
expect(history.location.state).toEqual({ existing: 'state' });
|
||||
});
|
||||
|
||||
it('should have accessible length property', () => {
|
||||
expect(history.length).toBeDefined();
|
||||
expect(history.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should have accessible action property', () => {
|
||||
expect(history.action).toBeDefined();
|
||||
expect(history.action).toBe('PUSH');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,58 @@
|
||||
import { createBrowserHistory } from 'history';
|
||||
import {
|
||||
createBrowserHistory,
|
||||
createPath,
|
||||
History,
|
||||
LocationDescriptorObject,
|
||||
LocationState,
|
||||
} from 'history';
|
||||
import { isEventObject } from 'utils/isEventObject';
|
||||
|
||||
export default createBrowserHistory();
|
||||
// Create the base history instance
|
||||
const baseHistory = createBrowserHistory();
|
||||
|
||||
type PathOrLocation = string | LocationDescriptorObject<LocationState>;
|
||||
|
||||
// Extend the History interface to include enhanced push method
|
||||
interface EnhancedHistory extends History {
|
||||
push: {
|
||||
(path: PathOrLocation, state?: any): void;
|
||||
(
|
||||
path: PathOrLocation,
|
||||
event?: React.MouseEvent | MouseEvent | KeyboardEvent,
|
||||
state?: any,
|
||||
): void;
|
||||
};
|
||||
originalPush: History['push'];
|
||||
}
|
||||
|
||||
// Create enhanced history with overridden push method
|
||||
const history = baseHistory as EnhancedHistory;
|
||||
|
||||
// Store the original push method
|
||||
history.originalPush = baseHistory.push;
|
||||
|
||||
// Override push to handle meta/ctrl key events and location objects
|
||||
history.push = function (
|
||||
path: PathOrLocation,
|
||||
eventOrState?: React.MouseEvent | MouseEvent | KeyboardEvent | any,
|
||||
state?: any,
|
||||
): void {
|
||||
// Check if second argument is an event object
|
||||
const isEvent = isEventObject(eventOrState);
|
||||
|
||||
// If it's an event and meta/ctrl key is pressed, open in new tab
|
||||
if (isEvent && (eventOrState.metaKey || eventOrState.ctrlKey)) {
|
||||
// Convert location object to URL string using createPath from history
|
||||
const url = typeof path === 'string' ? path : createPath(path);
|
||||
window.open(url, '_blank');
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, use normal navigation
|
||||
// The original push method already handles both strings and location objects
|
||||
// If eventOrState is not an event, treat it as state
|
||||
const actualState = isEvent ? state : eventOrState;
|
||||
history.originalPush(path, actualState);
|
||||
};
|
||||
|
||||
export default history;
|
||||
|
||||
21
frontend/src/utils/isEventObject.ts
Normal file
21
frontend/src/utils/isEventObject.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
// Event types that have metaKey/ctrlKey properties
|
||||
type EventWithModifiers =
|
||||
| MouseEvent
|
||||
| KeyboardEvent
|
||||
| React.MouseEvent<any, MouseEvent>
|
||||
| React.KeyboardEvent<any>;
|
||||
|
||||
// Helper function to determine if an argument is an event - Also used in utils/history.ts
|
||||
export const isEventObject = (arg: unknown): arg is EventWithModifiers => {
|
||||
if (!arg || typeof arg !== 'object') return false;
|
||||
|
||||
return (
|
||||
arg instanceof MouseEvent ||
|
||||
arg instanceof KeyboardEvent ||
|
||||
('nativeEvent' in arg &&
|
||||
(arg.nativeEvent instanceof MouseEvent ||
|
||||
arg.nativeEvent instanceof KeyboardEvent)) ||
|
||||
'metaKey' in arg ||
|
||||
'ctrlKey' in arg
|
||||
);
|
||||
};
|
||||
@@ -2,6 +2,7 @@ package alertmanagerbatcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log/slog"
|
||||
"testing"
|
||||
|
||||
@@ -10,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func TestBatcherWithOneAlertAndDefaultConfigs(t *testing.T) {
|
||||
batcher := New(slog.New(slog.DiscardHandler), NewConfig())
|
||||
batcher := New(slog.New(slog.NewTextHandler(io.Discard, nil)), NewConfig())
|
||||
_ = batcher.Start(context.Background())
|
||||
|
||||
batcher.Add(context.Background(), &alertmanagertypes.PostableAlert{Alert: alertmanagertypes.AlertModel{
|
||||
@@ -24,7 +25,7 @@ func TestBatcherWithOneAlertAndDefaultConfigs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBatcherWithBatchSize(t *testing.T) {
|
||||
batcher := New(slog.New(slog.DiscardHandler), Config{Size: 2, Capacity: 4})
|
||||
batcher := New(slog.New(slog.NewTextHandler(io.Discard, nil)), Config{Size: 2, Capacity: 4})
|
||||
_ = batcher.Start(context.Background())
|
||||
|
||||
var alerts alertmanagertypes.PostableAlerts
|
||||
@@ -44,7 +45,7 @@ func TestBatcherWithBatchSize(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBatcherWithCClosed(t *testing.T) {
|
||||
batcher := New(slog.New(slog.DiscardHandler), Config{Size: 2, Capacity: 4})
|
||||
batcher := New(slog.New(slog.NewTextHandler(io.Discard, nil)), Config{Size: 2, Capacity: 4})
|
||||
_ = batcher.Start(context.Background())
|
||||
|
||||
var alerts alertmanagertypes.PostableAlerts
|
||||
|
||||
@@ -2,14 +2,14 @@ package alertmanagerserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/SigNoz/signoz/pkg/types/alertmanagertypes/alertmanagertypestest"
|
||||
"github.com/prometheus/alertmanager/dispatch"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/alertmanagertypes/alertmanagertypestest"
|
||||
"github.com/prometheus/alertmanager/dispatch"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager/nfroutingstore/nfroutingstoretest"
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager/rulebasednotification"
|
||||
@@ -89,7 +89,7 @@ func TestEndToEndAlertManagerFlow(t *testing.T) {
|
||||
srvCfg := NewConfig()
|
||||
stateStore := alertmanagertypestest.NewStateStore()
|
||||
registry := prometheus.NewRegistry()
|
||||
logger := slog.New(slog.DiscardHandler)
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
server, err := New(context.Background(), logger, registry, srvCfg, orgID, stateStore, notificationManager)
|
||||
require.NoError(t, err)
|
||||
amConfig, err := alertmanagertypes.NewDefaultConfig(srvCfg.Global, srvCfg.Route, orgID)
|
||||
|
||||
@@ -3,6 +3,7 @@ package alertmanagerserver
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -25,7 +26,7 @@ import (
|
||||
|
||||
func TestServerSetConfigAndStop(t *testing.T) {
|
||||
notificationManager := nfmanagertest.NewMock()
|
||||
server, err := New(context.Background(), slog.New(slog.DiscardHandler), prometheus.NewRegistry(), NewConfig(), "1", alertmanagertypestest.NewStateStore(), notificationManager)
|
||||
server, err := New(context.Background(), slog.New(slog.NewTextHandler(io.Discard, nil)), prometheus.NewRegistry(), NewConfig(), "1", alertmanagertypestest.NewStateStore(), notificationManager)
|
||||
require.NoError(t, err)
|
||||
|
||||
amConfig, err := alertmanagertypes.NewDefaultConfig(alertmanagertypes.GlobalConfig{}, alertmanagertypes.RouteConfig{GroupInterval: 1 * time.Minute, RepeatInterval: 1 * time.Minute, GroupWait: 1 * time.Minute}, "1")
|
||||
@@ -37,7 +38,7 @@ func TestServerSetConfigAndStop(t *testing.T) {
|
||||
|
||||
func TestServerTestReceiverTypeWebhook(t *testing.T) {
|
||||
notificationManager := nfmanagertest.NewMock()
|
||||
server, err := New(context.Background(), slog.New(slog.DiscardHandler), prometheus.NewRegistry(), NewConfig(), "1", alertmanagertypestest.NewStateStore(), notificationManager)
|
||||
server, err := New(context.Background(), slog.New(slog.NewTextHandler(io.Discard, nil)), prometheus.NewRegistry(), NewConfig(), "1", alertmanagertypestest.NewStateStore(), notificationManager)
|
||||
require.NoError(t, err)
|
||||
|
||||
amConfig, err := alertmanagertypes.NewDefaultConfig(alertmanagertypes.GlobalConfig{}, alertmanagertypes.RouteConfig{GroupInterval: 1 * time.Minute, RepeatInterval: 1 * time.Minute, GroupWait: 1 * time.Minute}, "1")
|
||||
@@ -85,7 +86,7 @@ func TestServerPutAlerts(t *testing.T) {
|
||||
srvCfg := NewConfig()
|
||||
srvCfg.Route.GroupInterval = 1 * time.Second
|
||||
notificationManager := nfmanagertest.NewMock()
|
||||
server, err := New(context.Background(), slog.New(slog.DiscardHandler), prometheus.NewRegistry(), srvCfg, "1", stateStore, notificationManager)
|
||||
server, err := New(context.Background(), slog.New(slog.NewTextHandler(io.Discard, nil)), prometheus.NewRegistry(), srvCfg, "1", stateStore, notificationManager)
|
||||
require.NoError(t, err)
|
||||
|
||||
amConfig, err := alertmanagertypes.NewDefaultConfig(srvCfg.Global, srvCfg.Route, "1")
|
||||
@@ -133,7 +134,7 @@ func TestServerTestAlert(t *testing.T) {
|
||||
srvCfg := NewConfig()
|
||||
srvCfg.Route.GroupInterval = 1 * time.Second
|
||||
notificationManager := nfmanagertest.NewMock()
|
||||
server, err := New(context.Background(), slog.New(slog.DiscardHandler), prometheus.NewRegistry(), srvCfg, "1", stateStore, notificationManager)
|
||||
server, err := New(context.Background(), slog.New(slog.NewTextHandler(io.Discard, nil)), prometheus.NewRegistry(), srvCfg, "1", stateStore, notificationManager)
|
||||
require.NoError(t, err)
|
||||
|
||||
amConfig, err := alertmanagertypes.NewDefaultConfig(srvCfg.Global, srvCfg.Route, "1")
|
||||
@@ -238,7 +239,7 @@ func TestServerTestAlertContinuesOnFailure(t *testing.T) {
|
||||
srvCfg := NewConfig()
|
||||
srvCfg.Route.GroupInterval = 1 * time.Second
|
||||
notificationManager := nfmanagertest.NewMock()
|
||||
server, err := New(context.Background(), slog.New(slog.DiscardHandler), prometheus.NewRegistry(), srvCfg, "1", stateStore, notificationManager)
|
||||
server, err := New(context.Background(), slog.New(slog.NewTextHandler(io.Discard, nil)), prometheus.NewRegistry(), srvCfg, "1", stateStore, notificationManager)
|
||||
require.NoError(t, err)
|
||||
|
||||
amConfig, err := alertmanagertypes.NewDefaultConfig(srvCfg.Global, srvCfg.Route, "1")
|
||||
|
||||
@@ -2,6 +2,7 @@ package factory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -32,7 +33,7 @@ func TestRegistryWith2Services(t *testing.T) {
|
||||
s1 := newTestService(t)
|
||||
s2 := newTestService(t)
|
||||
|
||||
registry, err := NewRegistry(slog.New(slog.DiscardHandler), NewNamedService(MustNewName("s1"), s1), NewNamedService(MustNewName("s2"), s2))
|
||||
registry, err := NewRegistry(slog.New(slog.NewTextHandler(io.Discard, nil)), NewNamedService(MustNewName("s1"), s1), NewNamedService(MustNewName("s2"), s2))
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -53,7 +54,7 @@ func TestRegistryWith2ServicesWithoutWait(t *testing.T) {
|
||||
s1 := newTestService(t)
|
||||
s2 := newTestService(t)
|
||||
|
||||
registry, err := NewRegistry(slog.New(slog.DiscardHandler), NewNamedService(MustNewName("s1"), s1), NewNamedService(MustNewName("s2"), s2))
|
||||
registry, err := NewRegistry(slog.New(slog.NewTextHandler(io.Discard, nil)), NewNamedService(MustNewName("s1"), s1), NewNamedService(MustNewName("s2"), s2))
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -16,7 +17,7 @@ func TestTimeout(t *testing.T) {
|
||||
writeTimeout := 6 * time.Second
|
||||
defaultTimeout := 2 * time.Second
|
||||
maxTimeout := 4 * time.Second
|
||||
m := NewTimeout(slog.New(slog.DiscardHandler), []string{"/excluded"}, defaultTimeout, maxTimeout)
|
||||
m := NewTimeout(slog.New(slog.NewTextHandler(io.Discard, nil)), []string{"/excluded"}, defaultTimeout, maxTimeout)
|
||||
|
||||
listener, err := net.Listen("tcp", "localhost:0")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package instrumentationtest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log/slog"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
@@ -20,7 +21,7 @@ type noopInstrumentation struct {
|
||||
|
||||
func New() instrumentation.Instrumentation {
|
||||
return &noopInstrumentation{
|
||||
logger: slog.New(slog.DiscardHandler),
|
||||
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
||||
meterProvider: noopmetric.NewMeterProvider(),
|
||||
tracerProvider: nooptrace.NewTracerProvider(),
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package signoz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log/slog"
|
||||
"testing"
|
||||
|
||||
@@ -12,7 +13,7 @@ import (
|
||||
// This is a test to ensure that all fields of config implement the factory.Config interface and are valid with
|
||||
// their default values.
|
||||
func TestValidateConfig(t *testing.T) {
|
||||
logger := slog.New(slog.DiscardHandler)
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
_, err := NewConfig(context.Background(), logger, configtest.NewResolverConfig(), DeprecatedFlags{})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user