1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2026-present FERS Contributors (see AUTHORS.md).
15 DEFAULT_LOG_DRAWER_WIDTH,
17 DEFAULT_LOG_MAX_LINES,
18 getEffectiveLogDrawerWidth,
23 readStoredLogDrawerWidth,
25} from './fersLogStore';
29 getItem: (key: string) => string | null;
30 removeItem: (key: string) => void;
31 setItem: (key: string, value: string) => void;
34function createStorageMock(): StorageMock {
35 const values = new Map<string, string>();
38 clear: () => values.clear(),
39 getItem: (key) => values.get(key) ?? null,
40 removeItem: (key) => values.delete(key),
41 setItem: (key, value) => {
42 values.set(key, value);
47const localStorageMock = createStorageMock();
48const originalLocalStorage = globalThis.localStorage;
50describe('fers log store', () => {
52 Object.defineProperty(globalThis, 'localStorage', {
53 value: localStorageMock,
59 Object.defineProperty(globalThis, 'localStorage', {
60 value: originalLocalStorage,
66 globalThis.localStorage.clear();
67 useFersLogStore.setState({
70 maxLines: DEFAULT_LOG_MAX_LINES,
71 logLevel: DEFAULT_LOG_LEVEL,
73 drawerWidth: DEFAULT_LOG_DRAWER_WIDTH,
77 test('appends log entries in order', () => {
80 .appendLog({ sequence: 1, level: 'INFO', line: 'first' });
83 .appendLog({ sequence: 2, level: 'ERROR', line: 'second' });
85 expect(useFersLogStore.getState().entries).toEqual([
86 { sequence: 1, level: 'INFO', line: 'first' },
87 { sequence: 2, level: 'ERROR', line: 'second' },
91 test('trims to bounded buffer and records dropped lines', () => {
92 useFersLogStore.setState({ maxLines: 2 });
94 for (let sequence = 1; sequence <= 4; sequence += 1) {
95 useFersLogStore.getState().appendLog({
98 line: `line ${sequence}`,
102 const state = useFersLogStore.getState();
103 expect(state.entries.map((entry) => entry.sequence)).toEqual([3, 4]);
104 expect(state.droppedCount).toBe(2);
107 test('clear resets entries and dropped count', () => {
108 useFersLogStore.setState({
109 entries: [{ sequence: 1, level: 'INFO', line: 'line' }],
113 useFersLogStore.getState().clearLogs();
115 expect(useFersLogStore.getState().entries).toEqual([]);
116 expect(useFersLogStore.getState().droppedCount).toBe(0);
119 test('max lines setting clamps and trims immediately', () => {
120 useFersLogStore.setState({
122 { sequence: 1, level: 'INFO', line: 'one' },
123 { sequence: 2, level: 'INFO', line: 'two' },
128 useFersLogStore.getState().setMaxLines(1);
130 const state = useFersLogStore.getState();
131 expect(state.maxLines).toBe(MIN_LOG_MAX_LINES);
132 expect(state.entries.map((entry) => entry.sequence)).toEqual([1, 2]);
135 test('clampLogMaxLines handles invalid and out-of-range values', () => {
136 expect(clampLogMaxLines(Number.NaN)).toBe(DEFAULT_LOG_MAX_LINES);
137 expect(clampLogMaxLines(1)).toBe(MIN_LOG_MAX_LINES);
138 expect(clampLogMaxLines(MAX_LOG_MAX_LINES + 1)).toBe(MAX_LOG_MAX_LINES);
139 expect(clampLogMaxLines(1234.9)).toBe(1234);
142 test('clampLogDrawerWidth handles invalid and out-of-range values', () => {
143 expect(clampLogDrawerWidth(Number.NaN)).toBe(DEFAULT_LOG_DRAWER_WIDTH);
144 expect(clampLogDrawerWidth(1)).toBe(MIN_LOG_DRAWER_WIDTH);
145 expect(clampLogDrawerWidth(MAX_LOG_DRAWER_WIDTH + 1)).toBe(
148 expect(clampLogDrawerWidth(733.6)).toBe(734);
151 test('log level defaults and updates', () => {
152 expect(useFersLogStore.getState().logLevel).toBe(DEFAULT_LOG_LEVEL);
154 useFersLogStore.getState().setLogLevel('TRACE');
155 expect(useFersLogStore.getState().logLevel).toBe('TRACE');
157 useFersLogStore.getState().setLogLevel('OFF');
158 expect(useFersLogStore.getState().logLevel).toBe('OFF');
161 test('setDrawerWidth updates state and persists width', () => {
162 useFersLogStore.getState().setDrawerWidth(734);
164 expect(useFersLogStore.getState().drawerWidth).toBe(734);
167 globalThis.localStorage.getItem('fers.logViewer.v1') ?? '{}'
169 ).toEqual({ drawerWidth: 734 });
172 test('readStoredLogDrawerWidth hydrates persisted width', () => {
173 globalThis.localStorage.setItem(
175 JSON.stringify({ drawerWidth: 1440 })
178 expect(readStoredLogDrawerWidth()).toBe(MAX_LOG_DRAWER_WIDTH);
181 test('effective width clamps to viewport without mutating preferred width', () => {
182 useFersLogStore.getState().setDrawerWidth(800);
184 const preferredWidth = useFersLogStore.getState().drawerWidth;
185 const effectiveWidth = getEffectiveLogDrawerWidth(preferredWidth, 500);
187 expect(preferredWidth).toBe(800);
188 expect(effectiveWidth).toBe(500);