diff --git a/frontend/src/providers/preferences/__tests__/PreferenceContextProvider.test.tsx b/frontend/src/providers/preferences/__tests__/PreferenceContextProvider.test.tsx
new file mode 100644
index 0000000000..2f5d4eab4f
--- /dev/null
+++ b/frontend/src/providers/preferences/__tests__/PreferenceContextProvider.test.tsx
@@ -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 (
+
+
{context.mode}
+
{context.dataSource}
+
{String(context.loading)}
+
{String(context.error)}
+
{context.savedViewId || 'no-view-id'}
+
+ );
+}
+
+describe('PreferenceContextProvider', () => {
+ it('should provide context with direct mode when no viewKey is present', () => {
+ render(
+
+
+ (
+
+
+
+ )}
+ />
+
+ ,
+ );
+
+ 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(
+
+
+ (
+
+
+
+ )}
+ />
+
+ ,
+ );
+
+ 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(
+
+
+ (
+
+
+
+ )}
+ />
+
+ ,
+ );
+
+ 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(
+
+
+ (
+
+
+
+ )}
+ />
+
+ ,
+ );
+
+ 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();
+ }).toThrow(
+ 'usePreferenceContext must be used within PreferenceContextProvider',
+ );
+
+ // Restore console.error
+ console.error = originalConsoleError;
+ });
+});
diff --git a/frontend/src/providers/preferences/__tests__/logsLoaderConfig.test.ts b/frontend/src/providers/preferences/__tests__/logsLoaderConfig.test.ts
new file mode 100644
index 0000000000..b6ab1b18af
--- /dev/null
+++ b/frontend/src/providers/preferences/__tests__/logsLoaderConfig.test.ts
@@ -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 = {};
+
+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;
+
+ 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,
+ });
+ });
+});
diff --git a/frontend/src/providers/preferences/__tests__/logsUpdaterConfig.test.ts b/frontend/src/providers/preferences/__tests__/logsUpdaterConfig.test.ts
new file mode 100644
index 0000000000..20488c093e
--- /dev/null
+++ b/frontend/src/providers/preferences/__tests__/logsUpdaterConfig.test.ts
@@ -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 = {};
+
+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);
+ });
+});
diff --git a/frontend/src/providers/preferences/__tests__/usePreferenceLoader.test.tsx b/frontend/src/providers/preferences/__tests__/usePreferenceLoader.test.tsx
new file mode 100644
index 0000000000..24b9af78b9
--- /dev/null
+++ b/frontend/src/providers/preferences/__tests__/usePreferenceLoader.test.tsx
@@ -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();
+ });
+});
diff --git a/frontend/src/providers/preferences/__tests__/usePreferenceUpdater.test.tsx b/frontend/src/providers/preferences/__tests__/usePreferenceUpdater.test.tsx
new file mode 100644
index 0000000000..fa29b94aa6
--- /dev/null
+++ b/frontend/src/providers/preferences/__tests__/usePreferenceUpdater.test.tsx
@@ -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);
+ });
+});
diff --git a/frontend/src/providers/preferences/sync/usePreferenceSync.ts b/frontend/src/providers/preferences/sync/usePreferenceSync.ts
index 75908c6c56..4934d8df12 100644
--- a/frontend/src/providers/preferences/sync/usePreferenceSync.ts
+++ b/frontend/src/providers/preferences/sync/usePreferenceSync.ts
@@ -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) {