1import { DEFAULT_UNIFIED_SETTINGS, type UnifiedSettings } from "@t3tools/contracts";
2import { beforeEach, describe, expect, it, vi } from "vite-plus/test";
3
4import { reactHookHarness as hooks } from "../../test/reactHookHarness";
5
6const state = vi.hoisted(() => ({
7 settings: null as UnifiedSettings | null,
8 update: vi.fn(),
9 confirm: vi.fn(),
10}));
11
12vi.mock("react", async (importOriginal) => {
13 const actual = await importOriginal<typeof import("react")>();
14 const { reactHookHarness } = await import("../../test/reactHookHarness");
15 return {
16 ...actual,
17 useCallback: reactHookHarness.useCallback,
18 useMemo: reactHookHarness.useMemo,
19 };
20});
21
22vi.mock("react/compiler-runtime", async () => {
23 const { reactHookHarness } = await import("../../test/reactHookHarness");
24 return { c: reactHookHarness.useMemoCache };
25});
26
27vi.mock("../../hooks/useTheme", async (importOriginal) => ({
28 ...(await importOriginal<typeof import("../../hooks/useTheme")>()),
29 useTheme: () => ({
30 theme: "system",
31 followSystem: true,
32 themeHalves: null,
33 setTheme: vi.fn(),
34 setFollowSystem: vi.fn(),
35 setThemeHalf: vi.fn(),
36 clearThemeHalves: vi.fn(),
37 }),
38 readThemePreference: () => "system",
39 readThemeHalves: () => null,
40 readAppearanceModePreference: () => "system",
41}));
42
43vi.mock("./useScopedSettings", async (importOriginal) => ({
44 ...(await importOriginal<typeof import("./useScopedSettings")>()),
45 useScopedSettings: () => state.settings,
46 useUpdateScopedSettings: () => state.update,
47}));
48
49vi.mock("../../localApi", async (importOriginal) => ({
50 ...(await importOriginal<typeof import("../../localApi")>()),
51 readLocalApi: () => ({ dialogs: { confirm: state.confirm } }),
52}));
53
54import { useSettingsRestore } from "./SettingsPanels";
55
56beforeEach(() => {
57 hooks.reset();
58 vi.clearAllMocks();
59 state.settings = { ...DEFAULT_UNIFIED_SETTINGS };
60 state.confirm.mockResolvedValue(true);
61});
62
63describe("restoring V2 settings", () => {
64 it.each([
65 ["persistComposerContextStrip", "Composer context"],
66 ["autoResumeLimitedThreads", "Auto-resume limited threads"],
67 ["snoozeLimitedThreads", "Snooze limited threads"],
68 ] as const)("restores %s when it is the only changed setting", async (key, label) => {
69 state.settings = { ...DEFAULT_UNIFIED_SETTINGS, [key]: true };
70 hooks.beginRender();
71 const restore = useSettingsRestore();
72
73 expect(restore.changedSettingLabels).toEqual([label]);
74 await restore.restoreDefaults();
75
76 expect(state.confirm.mock.calls[0]?.[0]).toContain(label);
77 expect(state.update).toHaveBeenCalledOnce();
78 expect(state.update.mock.calls[0]?.[0][key]).toBe(DEFAULT_UNIFIED_SETTINGS[key]);
79 });
80
81 it("does not reset settings after cancellation", async () => {
82 state.settings = { ...DEFAULT_UNIFIED_SETTINGS, autoResumeLimitedThreads: true };
83 state.confirm.mockResolvedValue(false);
84 hooks.beginRender();
85
86 await useSettingsRestore().restoreDefaults();
87
88 expect(state.confirm).toHaveBeenCalledOnce();
89 expect(state.update).not.toHaveBeenCalled();
90 });
91});