import * as React from 'react'; import { createElement } from 'react'; import { createRoot, Root } from 'react-dom/client'; import { MemoryRouter } from 'react-router-dom'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import InterfaceSettings from '../interface-settings'; import useFeedViewSettingsStore from '../../../../stores/use-feed-view-settings-store'; (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; const act = (React as { act?: (cb: () => void | Promise) => void | Promise }).act as (cb: () => void | Promise) => void | Promise; vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string) => key, i18n: { changeLanguage: vi.fn(), language: 'en' }, }), })); vi.mock('../../../../hooks/use-theme', () => ({ default: () => ['yotsuba', vi.fn()], })); vi.mock('../../../../stores/use-expanded-media-store', () => ({ default: () => ({ fitExpandedImagesToScreen: false, setFitExpandedImagesToScreen: vi.fn() }), })); vi.mock('../../../../stores/use-special-theme-store', () => ({ default: () => ({ isEnabled: false, setIsEnabled: vi.fn() }), })); vi.mock('../../../../lib/utils/time-utils', () => ({ isChristmas: () => false, })); vi.mock('../../version', () => ({ default: () => null, })); /** Minimal component that subscribes to feed view settings (like Board) to verify re-renders. */ const BoardModeIndicator = () => { const enableInfiniteScroll = useFeedViewSettingsStore((state) => state.enableInfiniteScroll); return {enableInfiniteScroll ? 'infinite' : 'pagination'}; }; const STORAGE_KEY = 'feed-view-settings-store'; let root: Root; let container: HTMLDivElement; const render = (children: React.ReactNode) => { act(() => { root.render(createElement(MemoryRouter, {}, children)); }); }; describe('InterfaceSettings', () => { let setItemSpy: ReturnType; beforeEach(() => { vi.clearAllMocks(); localStorage.removeItem(STORAGE_KEY); useFeedViewSettingsStore.getState().setEnableInfiniteScroll(false); setItemSpy = vi.spyOn(Storage.prototype, 'setItem'); container = document.createElement('div'); document.body.appendChild(container); root = createRoot(container); }); afterEach(() => { act(() => root.unmount()); container.remove(); setItemSpy.mockRestore(); }); it('renders enable_infinite_scroll checkbox unchecked by default', () => { render(createElement(InterfaceSettings)); const label = Array.from(container.querySelectorAll('label')).find((l) => l.textContent?.toLowerCase().includes('enable_infinite_scroll')); expect(label).toBeTruthy(); const checkbox = label?.querySelector('input[type="checkbox"]'); expect(checkbox).toBeTruthy(); expect(checkbox?.checked).toBe(false); }); it('toggling checkbox updates persisted state and re-renders board mode', async () => { render(createElement(React.Fragment, {}, createElement(InterfaceSettings), createElement(BoardModeIndicator))); const label = Array.from(container.querySelectorAll('label')).find((l) => l.textContent?.toLowerCase().includes('enable_infinite_scroll')); const checkbox = label?.querySelector('input[type="checkbox"]'); expect(checkbox).toBeTruthy(); expect(container.querySelector('[data-testid="board-mode"]')?.textContent).toBe('pagination'); await act(async () => { checkbox?.click(); }); expect(useFeedViewSettingsStore.getState().enableInfiniteScroll).toBe(true); expect(checkbox?.checked).toBe(true); expect(container.querySelector('[data-testid="board-mode"]')?.textContent).toBe('infinite'); expect(setItemSpy).toHaveBeenCalledWith(STORAGE_KEY, expect.stringContaining('"enableInfiniteScroll":true')); }); });