mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-20 18:19:05 +08:00
feat: added tests for preferences framework alongside some minor bugs improvements
This commit is contained in:
parent
6f5c4bf904
commit
34cd2c1b40
@ -0,0 +1,150 @@
|
||||
/* eslint-disable sonarjs/no-identical-functions */
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { FormattingOptions, Preferences } from 'providers/preferences/types';
|
||||
import { MemoryRouter, Route, Switch } from 'react-router-dom';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import {
|
||||
PreferenceContextProvider,
|
||||
usePreferenceContext,
|
||||
} from '../context/PreferenceContextProvider';
|
||||
|
||||
// Mock the usePreferenceSync hook
|
||||
jest.mock('../sync/usePreferenceSync', () => ({
|
||||
usePreferenceSync: jest.fn().mockReturnValue({
|
||||
preferences: {
|
||||
columns: [] as BaseAutocompleteData[],
|
||||
formatting: {
|
||||
maxLines: 2,
|
||||
format: 'table',
|
||||
fontSize: 'small',
|
||||
version: 1,
|
||||
} as FormattingOptions,
|
||||
} as Preferences,
|
||||
loading: false,
|
||||
error: null,
|
||||
updateColumns: jest.fn(),
|
||||
updateFormatting: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
// Test component that consumes the context
|
||||
function TestConsumer(): JSX.Element {
|
||||
const context = usePreferenceContext();
|
||||
return (
|
||||
<div>
|
||||
<div data-testid="mode">{context.mode}</div>
|
||||
<div data-testid="dataSource">{context.dataSource}</div>
|
||||
<div data-testid="loading">{String(context.loading)}</div>
|
||||
<div data-testid="error">{String(context.error)}</div>
|
||||
<div data-testid="savedViewId">{context.savedViewId || 'no-view-id'}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
describe('PreferenceContextProvider', () => {
|
||||
it('should provide context with direct mode when no viewKey is present', () => {
|
||||
render(
|
||||
<MemoryRouter initialEntries={['/logs']}>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/logs"
|
||||
component={(): JSX.Element => (
|
||||
<PreferenceContextProvider>
|
||||
<TestConsumer />
|
||||
</PreferenceContextProvider>
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('mode')).toHaveTextContent('direct');
|
||||
expect(screen.getByTestId('dataSource')).toHaveTextContent('logs');
|
||||
expect(screen.getByTestId('loading')).toHaveTextContent('false');
|
||||
expect(screen.getByTestId('error')).toHaveTextContent('null');
|
||||
expect(screen.getByTestId('savedViewId')).toHaveTextContent('no-view-id');
|
||||
});
|
||||
|
||||
it('should provide context with savedView mode when viewKey is present', () => {
|
||||
render(
|
||||
<MemoryRouter initialEntries={['/logs?viewKey="test-view-id"']}>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/logs"
|
||||
component={(): JSX.Element => (
|
||||
<PreferenceContextProvider>
|
||||
<TestConsumer />
|
||||
</PreferenceContextProvider>
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('mode')).toHaveTextContent('savedView');
|
||||
expect(screen.getByTestId('dataSource')).toHaveTextContent('logs');
|
||||
expect(screen.getByTestId('savedViewId')).toHaveTextContent('test-view-id');
|
||||
});
|
||||
|
||||
it('should set traces dataSource when pathname includes traces', () => {
|
||||
render(
|
||||
<MemoryRouter initialEntries={['/traces']}>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/traces"
|
||||
component={(): JSX.Element => (
|
||||
<PreferenceContextProvider>
|
||||
<TestConsumer />
|
||||
</PreferenceContextProvider>
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('dataSource')).toHaveTextContent('traces');
|
||||
});
|
||||
|
||||
it('should handle invalid viewKey JSON gracefully', () => {
|
||||
// Mock console.error to avoid test output clutter
|
||||
const originalConsoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
|
||||
render(
|
||||
<MemoryRouter initialEntries={['/logs?viewKey=invalid-json']}>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/logs"
|
||||
component={(): JSX.Element => (
|
||||
<PreferenceContextProvider>
|
||||
<TestConsumer />
|
||||
</PreferenceContextProvider>
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('mode')).toHaveTextContent('direct');
|
||||
expect(console.error).toHaveBeenCalled();
|
||||
|
||||
// Restore console.error
|
||||
console.error = originalConsoleError;
|
||||
});
|
||||
|
||||
it('should throw error when usePreferenceContext is used outside provider', () => {
|
||||
// Suppress the error output for this test
|
||||
const originalConsoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
|
||||
expect(() => {
|
||||
render(<TestConsumer />);
|
||||
}).toThrow(
|
||||
'usePreferenceContext must be used within PreferenceContextProvider',
|
||||
);
|
||||
|
||||
// Restore console.error
|
||||
console.error = originalConsoleError;
|
||||
});
|
||||
});
|
@ -0,0 +1,162 @@
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { LogViewMode } from 'container/LogsTable';
|
||||
import { defaultLogsSelectedColumns } from 'container/OptionsMenu/constants';
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import { FormattingOptions } from 'providers/preferences/types';
|
||||
import {
|
||||
BaseAutocompleteData,
|
||||
DataTypes,
|
||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import logsLoaderConfig from '../configs/logsLoaderConfig';
|
||||
|
||||
// Mock localStorage
|
||||
const mockLocalStorage: Record<string, string> = {};
|
||||
|
||||
jest.mock('api/browser/localstorage/get', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn((key: string) => mockLocalStorage[key] || null),
|
||||
}));
|
||||
|
||||
describe('logsLoaderConfig', () => {
|
||||
// Save original location object
|
||||
const originalWindowLocation = window.location;
|
||||
let mockedLocation: Partial<Location>;
|
||||
|
||||
beforeEach(() => {
|
||||
// Setup a mocked location object
|
||||
mockedLocation = {
|
||||
...originalWindowLocation,
|
||||
search: '',
|
||||
};
|
||||
|
||||
// Mock the window.location property
|
||||
Object.defineProperty(window, 'location', {
|
||||
configurable: true,
|
||||
value: mockedLocation,
|
||||
writable: true,
|
||||
});
|
||||
|
||||
// Clear mocked localStorage
|
||||
Object.keys(mockLocalStorage).forEach((key) => {
|
||||
delete mockLocalStorage[key];
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore original location
|
||||
Object.defineProperty(window, 'location', {
|
||||
configurable: true,
|
||||
value: originalWindowLocation,
|
||||
writable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should have priority order: local, url, default', () => {
|
||||
expect(logsLoaderConfig.priority).toEqual(['local', 'url', 'default']);
|
||||
});
|
||||
|
||||
it('should load from localStorage when available', async () => {
|
||||
const mockColumns: BaseAutocompleteData[] = [
|
||||
{
|
||||
key: 'test-column',
|
||||
type: 'tag',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
];
|
||||
|
||||
// Set up localStorage mock data with the correct key from LOCALSTORAGE enum
|
||||
mockLocalStorage[LOCALSTORAGE.LOGS_LIST_OPTIONS] = JSON.stringify({
|
||||
selectColumns: mockColumns,
|
||||
maxLines: 10,
|
||||
format: 'json',
|
||||
fontSize: 'large',
|
||||
version: 2,
|
||||
});
|
||||
|
||||
const result = await logsLoaderConfig.local();
|
||||
|
||||
expect(result).toEqual({
|
||||
columns: mockColumns,
|
||||
formatting: {
|
||||
maxLines: 10,
|
||||
format: 'json' as LogViewMode,
|
||||
fontSize: 'large' as FontSize,
|
||||
version: 2,
|
||||
} as FormattingOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle invalid localStorage data gracefully', async () => {
|
||||
// Set up invalid localStorage mock data
|
||||
mockLocalStorage[LOCALSTORAGE.LOGS_LIST_OPTIONS] = 'invalid-json';
|
||||
|
||||
const result = await logsLoaderConfig.local();
|
||||
|
||||
expect(result).toEqual({
|
||||
columns: [] as BaseAutocompleteData[],
|
||||
formatting: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should load from URL when available', async () => {
|
||||
const mockColumns: BaseAutocompleteData[] = [
|
||||
{
|
||||
key: 'url-column',
|
||||
type: 'tag',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
];
|
||||
|
||||
// Set up URL search params
|
||||
mockedLocation.search = `?options=${encodeURIComponent(
|
||||
JSON.stringify({
|
||||
selectColumns: mockColumns,
|
||||
maxLines: 5,
|
||||
format: 'raw',
|
||||
fontSize: 'medium',
|
||||
version: 1,
|
||||
}),
|
||||
)}`;
|
||||
|
||||
const result = await logsLoaderConfig.url();
|
||||
|
||||
expect(result).toEqual({
|
||||
columns: mockColumns,
|
||||
formatting: {
|
||||
maxLines: 5,
|
||||
format: 'raw' as LogViewMode,
|
||||
fontSize: 'medium' as FontSize,
|
||||
version: 1,
|
||||
} as FormattingOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle invalid URL data gracefully', async () => {
|
||||
// Set up invalid URL search params
|
||||
mockedLocation.search = '?options=invalid-json';
|
||||
|
||||
const result = await logsLoaderConfig.url();
|
||||
|
||||
expect(result).toEqual({
|
||||
columns: [] as BaseAutocompleteData[],
|
||||
formatting: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should provide default values when no other source is available', async () => {
|
||||
const result = await logsLoaderConfig.default();
|
||||
|
||||
expect(result).toEqual({
|
||||
columns: defaultLogsSelectedColumns as BaseAutocompleteData[],
|
||||
formatting: {
|
||||
maxLines: 2,
|
||||
format: 'table' as LogViewMode,
|
||||
fontSize: 'small' as FontSize,
|
||||
version: 1,
|
||||
} as FormattingOptions,
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,251 @@
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { LogViewMode } from 'container/LogsTable';
|
||||
import { defaultOptionsQuery } from 'container/OptionsMenu/constants';
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import { FormattingOptions, PreferenceMode } from 'providers/preferences/types';
|
||||
import {
|
||||
BaseAutocompleteData,
|
||||
DataTypes,
|
||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import getLogsUpdaterConfig from '../configs/logsUpdaterConfig';
|
||||
|
||||
// Mock localStorage
|
||||
const mockLocalStorage: Record<string, string> = {};
|
||||
|
||||
jest.mock('api/browser/localstorage/set', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn((key: string, value: string) => {
|
||||
mockLocalStorage[key] = value;
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock localStorage.getItem
|
||||
Object.defineProperty(window, 'localStorage', {
|
||||
value: {
|
||||
getItem: jest.fn((key: string) => mockLocalStorage[key] || null),
|
||||
setItem: jest.fn((key: string, value: string) => {
|
||||
mockLocalStorage[key] = value;
|
||||
}),
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
|
||||
describe('logsUpdaterConfig', () => {
|
||||
// Mock redirectWithOptionsData and setSavedViewPreferences
|
||||
const redirectWithOptionsData = jest.fn();
|
||||
const setSavedViewPreferences = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
// Clear mocked localStorage
|
||||
Object.keys(mockLocalStorage).forEach((key) => {
|
||||
delete mockLocalStorage[key];
|
||||
});
|
||||
});
|
||||
|
||||
it('should update columns in localStorage for direct mode', () => {
|
||||
const logsUpdater = getLogsUpdaterConfig(
|
||||
redirectWithOptionsData,
|
||||
setSavedViewPreferences,
|
||||
);
|
||||
|
||||
const newColumns: BaseAutocompleteData[] = [
|
||||
{
|
||||
key: 'new-column',
|
||||
type: 'tag',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
];
|
||||
|
||||
// Set initial localStorage data
|
||||
mockLocalStorage[LOCALSTORAGE.LOGS_LIST_OPTIONS] = JSON.stringify({
|
||||
selectColumns: [
|
||||
{
|
||||
key: 'old-column',
|
||||
type: 'tag',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
],
|
||||
maxLines: 2,
|
||||
});
|
||||
|
||||
logsUpdater.updateColumns(newColumns, 'direct' as PreferenceMode);
|
||||
|
||||
// Should update URL
|
||||
expect(redirectWithOptionsData).toHaveBeenCalledWith({
|
||||
...defaultOptionsQuery,
|
||||
selectColumns: newColumns,
|
||||
});
|
||||
|
||||
// Should update localStorage
|
||||
const storedData = JSON.parse(
|
||||
mockLocalStorage[LOCALSTORAGE.LOGS_LIST_OPTIONS],
|
||||
);
|
||||
expect(storedData.selectColumns).toEqual(newColumns);
|
||||
expect(storedData.maxLines).toBe(2); // Should preserve other fields
|
||||
|
||||
// Should not update saved view preferences
|
||||
expect(setSavedViewPreferences).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should update columns in savedViewPreferences for savedView mode', () => {
|
||||
const logsUpdater = getLogsUpdaterConfig(
|
||||
redirectWithOptionsData,
|
||||
setSavedViewPreferences,
|
||||
);
|
||||
|
||||
const newColumns: BaseAutocompleteData[] = [
|
||||
{
|
||||
key: 'new-column',
|
||||
type: 'tag',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
];
|
||||
|
||||
logsUpdater.updateColumns(newColumns, 'savedView' as PreferenceMode);
|
||||
|
||||
// Should not update URL in savedView mode
|
||||
expect(redirectWithOptionsData).not.toHaveBeenCalled();
|
||||
|
||||
// Should not update localStorage in savedView mode
|
||||
expect(mockLocalStorage[LOCALSTORAGE.LOGS_LIST_OPTIONS]).toBeUndefined();
|
||||
|
||||
// Should update saved view preferences
|
||||
expect(setSavedViewPreferences).toHaveBeenCalledWith({
|
||||
columns: newColumns,
|
||||
formatting: {
|
||||
maxLines: 2,
|
||||
format: 'table',
|
||||
fontSize: 'small',
|
||||
version: 1,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should update formatting options in localStorage for direct mode', () => {
|
||||
const logsUpdater = getLogsUpdaterConfig(
|
||||
redirectWithOptionsData,
|
||||
setSavedViewPreferences,
|
||||
);
|
||||
|
||||
const newFormatting: FormattingOptions = {
|
||||
maxLines: 5,
|
||||
format: 'json' as LogViewMode,
|
||||
fontSize: 'large' as FontSize,
|
||||
version: 1,
|
||||
};
|
||||
|
||||
// Set initial localStorage data
|
||||
mockLocalStorage[LOCALSTORAGE.LOGS_LIST_OPTIONS] = JSON.stringify({
|
||||
selectColumns: [
|
||||
{
|
||||
key: 'column',
|
||||
type: 'tag',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
],
|
||||
maxLines: 2,
|
||||
format: 'table',
|
||||
});
|
||||
|
||||
logsUpdater.updateFormatting(newFormatting, 'direct' as PreferenceMode);
|
||||
|
||||
// Should always update URL for both modes
|
||||
expect(redirectWithOptionsData).toHaveBeenCalledWith({
|
||||
...defaultOptionsQuery,
|
||||
...newFormatting,
|
||||
});
|
||||
|
||||
// Should update localStorage in direct mode
|
||||
const storedData = JSON.parse(
|
||||
mockLocalStorage[LOCALSTORAGE.LOGS_LIST_OPTIONS],
|
||||
);
|
||||
expect(storedData.maxLines).toBe(5);
|
||||
expect(storedData.format).toBe('json');
|
||||
expect(storedData.fontSize).toBe('large');
|
||||
expect(storedData.version).toBe(1);
|
||||
expect(storedData.selectColumns).toEqual([
|
||||
{
|
||||
key: 'column',
|
||||
type: 'tag',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
]); // Should preserve columns
|
||||
});
|
||||
|
||||
it('should not update localStorage for savedView mode in updateFormatting', () => {
|
||||
const logsUpdater = getLogsUpdaterConfig(
|
||||
redirectWithOptionsData,
|
||||
setSavedViewPreferences,
|
||||
);
|
||||
|
||||
const newFormatting: FormattingOptions = {
|
||||
maxLines: 5,
|
||||
format: 'json' as LogViewMode,
|
||||
fontSize: 'large' as FontSize,
|
||||
version: 1,
|
||||
};
|
||||
|
||||
// Set initial localStorage data
|
||||
mockLocalStorage[LOCALSTORAGE.LOGS_LIST_OPTIONS] = JSON.stringify({
|
||||
selectColumns: [
|
||||
{
|
||||
key: 'column',
|
||||
type: 'tag',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
],
|
||||
maxLines: 2,
|
||||
format: 'table',
|
||||
});
|
||||
|
||||
logsUpdater.updateFormatting(newFormatting, 'savedView' as PreferenceMode);
|
||||
|
||||
// Should always update URL for both modes
|
||||
expect(redirectWithOptionsData).toHaveBeenCalledWith({
|
||||
...defaultOptionsQuery,
|
||||
...newFormatting,
|
||||
});
|
||||
|
||||
// Should not override localStorage in savedView mode
|
||||
const storedData = JSON.parse(
|
||||
mockLocalStorage[LOCALSTORAGE.LOGS_LIST_OPTIONS],
|
||||
);
|
||||
expect(storedData.maxLines).toBe(2); // Should remain the same
|
||||
expect(storedData.format).toBe('table'); // Should remain the same
|
||||
});
|
||||
|
||||
it('should initialize localStorage if it does not exist', () => {
|
||||
const logsUpdater = getLogsUpdaterConfig(
|
||||
redirectWithOptionsData,
|
||||
setSavedViewPreferences,
|
||||
);
|
||||
|
||||
const newFormatting: FormattingOptions = {
|
||||
maxLines: 5,
|
||||
format: 'json' as LogViewMode,
|
||||
fontSize: 'large' as FontSize,
|
||||
version: 1,
|
||||
};
|
||||
|
||||
// No initial localStorage data
|
||||
|
||||
logsUpdater.updateFormatting(newFormatting, 'direct' as PreferenceMode);
|
||||
|
||||
// Should create localStorage entry
|
||||
const storedData = JSON.parse(
|
||||
mockLocalStorage[LOCALSTORAGE.LOGS_LIST_OPTIONS],
|
||||
);
|
||||
expect(storedData.maxLines).toBe(5);
|
||||
expect(storedData.format).toBe('json');
|
||||
expect(storedData.fontSize).toBe('large');
|
||||
expect(storedData.version).toBe(1);
|
||||
});
|
||||
});
|
@ -0,0 +1,139 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import logsLoaderConfig from '../configs/logsLoaderConfig';
|
||||
import { usePreferenceLoader } from '../loader/usePreferenceLoader';
|
||||
|
||||
// Mock the config loaders
|
||||
jest.mock('../configs/logsLoaderConfig', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
priority: ['local', 'url', 'default'],
|
||||
local: jest.fn().mockResolvedValue({
|
||||
columns: [{ name: 'local-column' }],
|
||||
formatting: { maxLines: 5, format: 'table', fontSize: 'medium', version: 1 },
|
||||
}),
|
||||
url: jest.fn().mockResolvedValue({
|
||||
columns: [{ name: 'url-column' }],
|
||||
formatting: { maxLines: 3, format: 'table', fontSize: 'small', version: 1 },
|
||||
}),
|
||||
default: jest.fn().mockResolvedValue({
|
||||
columns: [{ name: 'default-column' }],
|
||||
formatting: { maxLines: 2, format: 'table', fontSize: 'small', version: 1 },
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('../configs/tracesLoaderConfig', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
priority: ['local', 'url', 'default'],
|
||||
local: jest.fn().mockResolvedValue({
|
||||
columns: [{ name: 'local-trace-column' }],
|
||||
}),
|
||||
url: jest.fn().mockResolvedValue({
|
||||
columns: [{ name: 'url-trace-column' }],
|
||||
}),
|
||||
default: jest.fn().mockResolvedValue({
|
||||
columns: [{ name: 'default-trace-column' }],
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('usePreferenceLoader', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should load logs preferences based on priority order', async () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePreferenceLoader({ dataSource: DataSource.LOGS, reSync: 0 }),
|
||||
);
|
||||
|
||||
// Initially it should be loading
|
||||
expect(result.current.loading).toBe(true);
|
||||
expect(result.current.preferences).toBe(null);
|
||||
expect(result.current.error).toBe(null);
|
||||
|
||||
// Wait for the loader to complete
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
// Should have loaded from local storage (highest priority)
|
||||
expect(result.current.preferences).toEqual({
|
||||
columns: [{ name: 'local-column' }],
|
||||
formatting: { maxLines: 5, format: 'table', fontSize: 'medium', version: 1 },
|
||||
});
|
||||
expect(result.current.error).toBe(null);
|
||||
});
|
||||
|
||||
it('should load traces preferences', async () => {
|
||||
const { result } = renderHook(() =>
|
||||
usePreferenceLoader({ dataSource: DataSource.TRACES, reSync: 0 }),
|
||||
);
|
||||
|
||||
// Wait for the loader to complete
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
// Should have loaded trace columns
|
||||
expect(result.current.preferences).toEqual({
|
||||
columns: [{ name: 'local-trace-column' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('should re-load preferences when reSync changes', async () => {
|
||||
const { result, rerender } = renderHook(
|
||||
({ dataSource, reSync }) => usePreferenceLoader({ dataSource, reSync }),
|
||||
{ initialProps: { dataSource: DataSource.LOGS, reSync: 0 } },
|
||||
);
|
||||
|
||||
// Wait for the first load to complete
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
// Trigger a reSync
|
||||
rerender({ dataSource: DataSource.LOGS, reSync: 1 });
|
||||
|
||||
// Should start loading again
|
||||
expect(result.current.loading).toBe(true);
|
||||
|
||||
// Wait for the second load to complete
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
// Should have reloaded from local storage
|
||||
expect(result.current.preferences).toEqual({
|
||||
columns: [{ name: 'local-column' }],
|
||||
formatting: { maxLines: 5, format: 'table', fontSize: 'medium', version: 1 },
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle errors during loading', async () => {
|
||||
// Mock an error in the loader using jest.spyOn
|
||||
const localSpy = jest.spyOn(logsLoaderConfig, 'local');
|
||||
localSpy.mockRejectedValueOnce(new Error('Loading failed'));
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
usePreferenceLoader({ dataSource: DataSource.LOGS, reSync: 0 }),
|
||||
);
|
||||
|
||||
// Wait for the loader to complete
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
// Should have set the error
|
||||
expect(result.current.error).toBeInstanceOf(Error);
|
||||
expect(result.current.error?.message).toBe('Loading failed');
|
||||
expect(result.current.preferences).toBe(null);
|
||||
|
||||
// Restore original implementation
|
||||
localSpy.mockRestore();
|
||||
});
|
||||
});
|
@ -0,0 +1,212 @@
|
||||
/* eslint-disable sonarjs/no-identical-functions */
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { LogViewMode } from 'container/LogsTable';
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import { FormattingOptions, PreferenceMode } from 'providers/preferences/types';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import {
|
||||
BaseAutocompleteData,
|
||||
DataTypes,
|
||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { usePreferenceUpdater } from '../updater/usePreferenceUpdater';
|
||||
|
||||
// Mock the config updaters
|
||||
const mockUpdateColumns = jest.fn();
|
||||
const mockUpdateFormatting = jest.fn();
|
||||
|
||||
jest.mock('../configs/logsUpdaterConfig', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn().mockImplementation(() => ({
|
||||
updateColumns: mockUpdateColumns,
|
||||
updateFormatting: mockUpdateFormatting,
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock('../configs/tracesUpdaterConfig', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn().mockImplementation(() => ({
|
||||
updateColumns: mockUpdateColumns,
|
||||
updateFormatting: mockUpdateFormatting,
|
||||
})),
|
||||
}));
|
||||
|
||||
// Mock the URL query hook
|
||||
jest.mock('hooks/useUrlQueryData', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn().mockReturnValue({
|
||||
redirectWithQuery: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('usePreferenceUpdater', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return updateColumns and updateFormatting functions', () => {
|
||||
const setReSync = jest.fn();
|
||||
const setSavedViewPreferences = jest.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
usePreferenceUpdater({
|
||||
dataSource: DataSource.LOGS,
|
||||
mode: 'direct' as PreferenceMode,
|
||||
setReSync,
|
||||
setSavedViewPreferences,
|
||||
}),
|
||||
);
|
||||
|
||||
// Should return the update functions
|
||||
expect(typeof result.current.updateColumns).toBe('function');
|
||||
expect(typeof result.current.updateFormatting).toBe('function');
|
||||
});
|
||||
|
||||
it('should call the logs updater for updateColumns with logs dataSource', () => {
|
||||
const setReSync = jest.fn();
|
||||
const setSavedViewPreferences = jest.fn();
|
||||
const newColumns: BaseAutocompleteData[] = [
|
||||
{
|
||||
key: 'new-column',
|
||||
type: 'tag',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
];
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
usePreferenceUpdater({
|
||||
dataSource: DataSource.LOGS,
|
||||
mode: 'direct' as PreferenceMode,
|
||||
setReSync,
|
||||
setSavedViewPreferences,
|
||||
}),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.updateColumns(newColumns);
|
||||
});
|
||||
|
||||
// Should call the logs updater
|
||||
expect(mockUpdateColumns).toHaveBeenCalledWith(newColumns, 'direct');
|
||||
expect(setReSync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call the logs updater for updateFormatting with logs dataSource', () => {
|
||||
const setReSync = jest.fn();
|
||||
const setSavedViewPreferences = jest.fn();
|
||||
const newFormatting: FormattingOptions = {
|
||||
maxLines: 10,
|
||||
format: 'table' as LogViewMode,
|
||||
fontSize: 'large' as FontSize,
|
||||
version: 1,
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
usePreferenceUpdater({
|
||||
dataSource: DataSource.LOGS,
|
||||
mode: 'direct' as PreferenceMode,
|
||||
setReSync,
|
||||
setSavedViewPreferences,
|
||||
}),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.updateFormatting(newFormatting);
|
||||
});
|
||||
|
||||
// Should call the logs updater
|
||||
expect(mockUpdateFormatting).toHaveBeenCalledWith(newFormatting, 'direct');
|
||||
expect(setReSync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call the traces updater for updateColumns with traces dataSource', () => {
|
||||
const setReSync = jest.fn();
|
||||
const setSavedViewPreferences = jest.fn();
|
||||
const newColumns: BaseAutocompleteData[] = [
|
||||
{
|
||||
key: 'new-trace-column',
|
||||
type: 'tag',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
];
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
usePreferenceUpdater({
|
||||
dataSource: DataSource.TRACES,
|
||||
mode: 'direct' as PreferenceMode,
|
||||
setReSync,
|
||||
setSavedViewPreferences,
|
||||
}),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.updateColumns(newColumns);
|
||||
});
|
||||
|
||||
// Should call the traces updater
|
||||
expect(mockUpdateColumns).toHaveBeenCalledWith(newColumns, 'direct');
|
||||
expect(setReSync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call the traces updater for updateFormatting with traces dataSource', () => {
|
||||
const setReSync = jest.fn();
|
||||
const setSavedViewPreferences = jest.fn();
|
||||
const newFormatting: FormattingOptions = {
|
||||
maxLines: 10,
|
||||
format: 'table' as LogViewMode,
|
||||
fontSize: 'large' as FontSize,
|
||||
version: 1,
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
usePreferenceUpdater({
|
||||
dataSource: DataSource.TRACES,
|
||||
mode: 'direct' as PreferenceMode,
|
||||
setReSync,
|
||||
setSavedViewPreferences,
|
||||
}),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.updateFormatting(newFormatting);
|
||||
});
|
||||
|
||||
// Should call the traces updater
|
||||
expect(mockUpdateFormatting).toHaveBeenCalledWith(newFormatting, 'direct');
|
||||
expect(setReSync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should increment reSync counter when updates are called', () => {
|
||||
const setReSync = jest.fn();
|
||||
const setSavedViewPreferences = jest.fn();
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
usePreferenceUpdater({
|
||||
dataSource: DataSource.LOGS,
|
||||
mode: 'direct' as PreferenceMode,
|
||||
setReSync,
|
||||
setSavedViewPreferences,
|
||||
}),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.updateColumns([
|
||||
{
|
||||
key: 'column',
|
||||
type: 'tag',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
expect(setReSync).toHaveBeenCalledWith(expect.any(Function));
|
||||
|
||||
// Simulate the setReSync callback to ensure it increments
|
||||
const incrementFn = setReSync.mock.calls[0][0];
|
||||
expect(incrementFn(1)).toBe(2);
|
||||
});
|
||||
});
|
@ -37,10 +37,6 @@ export function usePreferenceSync({
|
||||
)?.extraData;
|
||||
|
||||
const parsedExtraData = JSON.parse(extraData || '{}');
|
||||
console.log('uncaught extraData', {
|
||||
extraData,
|
||||
parsedExtraData,
|
||||
});
|
||||
let columns: BaseAutocompleteData[] = [];
|
||||
let formatting: FormattingOptions | undefined;
|
||||
if (dataSource === DataSource.LOGS) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user