1import {
2 DEFAULT_SERVER_SETTINGS,
3 DEFAULT_UNIFIED_SETTINGS,
4 ProviderDriverKind,
5 ProviderInstanceId,
6 type ProviderInstanceConfig,
7} from "@t3tools/contracts";
8import { getBackgroundActivityPresetSettings } from "@t3tools/shared/backgroundActivitySettings";
9import * as Duration from "effect/Duration";
10import { describe, expect, it } from "vite-plus/test";
11import {
12 backgroundActivitySharedPolicySettings,
13 buildProviderInstanceUpdatePatch,
14 formatDiagnosticsDescription,
15 getChangedBrowserSettingLabels,
16 getChangedTypographySettingLabels,
17 hasChangedBackgroundActivitySettings,
18 isProjectGroupingEnabled,
19 projectGroupingModeFromToggle,
20 resolveBackgroundActivityProfileOption,
21} from "./SettingsPanels.logic";
22
23describe("typography settings restore", () => {
24 it("detects family and size changes by font row", () => {
25 expect(getChangedTypographySettingLabels(DEFAULT_UNIFIED_SETTINGS)).toEqual([]);
26 expect(
27 getChangedTypographySettingLabels({
28 ...DEFAULT_UNIFIED_SETTINGS,
29 fontSizeInterface: 18,
30 fontFamilyCode: "Fira Code",
31 }),
32 ).toEqual(["Interface font", "Code font"]);
33 });
34});
35
36describe("background activity settings restore", () => {
37 it("detects legacy interval values even when the structured setting is at its default", () => {
38 expect(
39 hasChangedBackgroundActivitySettings({
40 backgroundActivity: DEFAULT_UNIFIED_SETTINGS.backgroundActivity,
41 backgroundActivityProfile: DEFAULT_UNIFIED_SETTINGS.backgroundActivityProfile,
42 automaticGitFetchInterval: Duration.seconds(45),
43 providerHealthRefreshInterval: DEFAULT_UNIFIED_SETTINGS.providerHealthRefreshInterval,
44 }),
45 ).toBe(true);
46 expect(
47 hasChangedBackgroundActivitySettings({
48 backgroundActivity: DEFAULT_UNIFIED_SETTINGS.backgroundActivity,
49 backgroundActivityProfile: DEFAULT_UNIFIED_SETTINGS.backgroundActivityProfile,
50 automaticGitFetchInterval: DEFAULT_UNIFIED_SETTINGS.automaticGitFetchInterval,
51 providerHealthRefreshInterval: Duration.minutes(7),
52 }),
53 ).toBe(true);
54 expect(hasChangedBackgroundActivitySettings(DEFAULT_UNIFIED_SETTINGS)).toBe(false);
55 });
56
57 it("detects a legacy profile override so restoring defaults clears it", () => {
58 expect(
59 hasChangedBackgroundActivitySettings({
60 ...DEFAULT_UNIFIED_SETTINGS,
61 backgroundActivityProfile: "performance",
62 }),
63 ).toBe(true);
64 });
65
66 it("shows the effective legacy preset and marks custom legacy intervals as advanced", () => {
67 const performance = getBackgroundActivityPresetSettings("performance");
68 expect(
69 resolveBackgroundActivityProfileOption({
70 ...DEFAULT_UNIFIED_SETTINGS,
71 backgroundActivity: DEFAULT_UNIFIED_SETTINGS.backgroundActivity,
72 backgroundActivityProfile: "performance",
73 automaticGitFetchInterval: performance.automaticGitFetchInterval,
74 providerHealthRefreshInterval: performance.providerHealthRefreshInterval,
75 }),
76 ).toBe("performance");
77
78 expect(
79 resolveBackgroundActivityProfileOption({
80 ...DEFAULT_UNIFIED_SETTINGS,
81 backgroundActivity: DEFAULT_UNIFIED_SETTINGS.backgroundActivity,
82 backgroundActivityProfile: "performance",
83 automaticGitFetchInterval: Duration.seconds(45),
84 providerHealthRefreshInterval: Duration.minutes(7),
85 }),
86 ).toBe("advanced");
87 });
88
89 it("preserves advanced overrides when the shared policy changes", () => {
90 const automaticGitFetchInterval = Duration.seconds(42);
91 expect(
92 backgroundActivitySharedPolicySettings(
93 {
94 ...DEFAULT_UNIFIED_SETTINGS,
95 backgroundActivity: {
96 schemaVersion: 1,
97 profile: "custom",
98 baseProfile: "balanced",
99 overrides: {
100 automaticGitFetchInterval,
101 pauseWhenOnBattery: true,
102 },
103 },
104 },
105 "performance",
106 ),
107 ).toEqual({
108 schemaVersion: 1,
109 profile: "custom",
110 baseProfile: "performance",
111 overrides: {
112 automaticGitFetchInterval,
113 pauseWhenOnBattery: true,
114 },
115 });
116 });
117
118 it("materializes legacy advanced overrides before changing the shared policy", () => {
119 const automaticGitFetchInterval = Duration.seconds(42);
120 expect(
121 backgroundActivitySharedPolicySettings(
122 {
123 ...DEFAULT_UNIFIED_SETTINGS,
124 automaticGitFetchInterval,
125 },
126 "battery-saver",
127 ),
128 ).toEqual({
129 schemaVersion: 1,
130 profile: "custom",
131 baseProfile: "battery-saver",
132 overrides: {
133 automaticGitFetchInterval,
134 },
135 });
136 });
137});
138
139describe("project grouping toggle", () => {
140 it("enables repository grouping and disables into separate projects", () => {
141 expect(isProjectGroupingEnabled("repository")).toBe(true);
142 expect(isProjectGroupingEnabled("repository_path")).toBe(true);
143 expect(isProjectGroupingEnabled("separate")).toBe(false);
144 expect(projectGroupingModeFromToggle(true)).toBe("repository");
145 expect(projectGroupingModeFromToggle(false)).toBe("separate");
146 });
147
148 it("restores repository path grouping when the toggle is cycled", () => {
149 expect(projectGroupingModeFromToggle(false, "repository_path")).toBe("separate");
150 expect(projectGroupingModeFromToggle(true, "repository_path")).toBe("repository_path");
151 });
152});
153
154describe("formatDiagnosticsDescription", () => {
155 it("collapses trace and metric URLs that share the same OTEL base path", () => {
156 expect(
157 formatDiagnosticsDescription({
158 localTracingEnabled: true,
159 otlpTracesEnabled: true,
160 otlpTracesUrl: "http://localhost:4318/v1/traces",
161 otlpMetricsEnabled: true,
162 otlpMetricsUrl: "http://localhost:4318/v1/metrics",
163 }),
164 ).toBe("Local trace file. Exporting OTEL to http://localhost:4318/v1/{traces,metrics}.");
165 });
166
167 it("keeps separate trace and metric URLs when their base paths differ", () => {
168 expect(
169 formatDiagnosticsDescription({
170 localTracingEnabled: true,
171 otlpTracesEnabled: true,
172 otlpTracesUrl: "http://localhost:4318/v1/traces",
173 otlpMetricsEnabled: true,
174 otlpMetricsUrl: "http://localhost:9000/v1/metrics",
175 }),
176 ).toBe(
177 "Local trace file. Exporting OTEL traces to http://localhost:4318/v1/traces and metrics to http://localhost:9000/v1/metrics.",
178 );
179 });
180
181 it("omits OTEL text when no exporter is enabled", () => {
182 expect(
183 formatDiagnosticsDescription({
184 localTracingEnabled: true,
185 otlpTracesEnabled: false,
186 otlpMetricsEnabled: false,
187 }),
188 ).toBe("Local trace file.");
189 });
190});
191
192describe("buildProviderInstanceUpdatePatch", () => {
193 it("replaces one instance and keeps the others", () => {
194 const instanceId = ProviderInstanceId.make("codex");
195 const otherId = ProviderInstanceId.make("codex_personal");
196 const other = {
197 driver: ProviderDriverKind.make("codex"),
198 config: { homePath: "/Users/example/.codex-personal" },
199 } satisfies ProviderInstanceConfig;
200 const nextInstance = {
201 driver: ProviderDriverKind.make("codex"),
202 enabled: true,
203 config: { binaryPath: "/opt/t3/codex" },
204 } satisfies ProviderInstanceConfig;
205
206 const patch = buildProviderInstanceUpdatePatch({
207 settings: { providerInstances: { [otherId]: other } },
208 instanceId,
209 instance: nextInstance,
210 });
211
212 expect(patch.providerInstances).toEqual({ [otherId]: other, [instanceId]: nextInstance });
213 expect(patch).not.toHaveProperty("textGenerationModelSelection");
214 });
215});
216
217describe("getChangedBrowserSettingLabels", () => {
218 it("reports nothing for the defaults", () => {
219 expect(getChangedBrowserSettingLabels(DEFAULT_UNIFIED_SETTINGS)).toEqual([]);
220 });
221
222 it("treats a structurally equal viewport as unchanged", () => {
223 // The viewport is a tagged union, so identity comparison would report a
224 // freshly decoded copy of the default as dirty and offer to "restore" it.
225 expect(
226 getChangedBrowserSettingLabels({
227 ...DEFAULT_UNIFIED_SETTINGS,
228 browserDefaultViewport: { ...DEFAULT_UNIFIED_SETTINGS.browserDefaultViewport },
229 }),
230 ).toEqual([]);
231 });
232
233 it("labels each browser default that differs", () => {
234 expect(
235 getChangedBrowserSettingLabels({
236 ...DEFAULT_UNIFIED_SETTINGS,
237 browserDefaultViewport: { _tag: "freeform", width: 900, height: 600 },
238 browserDefaultZoomFactor: 1.5,
239 browserDefaultAppearance: "dark",
240 browserRecordingFrameRate: 60,
241 browserRecordingShowKeyPresses: true,
242 browserRecordingShowMousePresses: true,
243 browserLinkTarget: "app",
244 browserAutoShowFloatingPreview: !DEFAULT_UNIFIED_SETTINGS.browserAutoShowFloatingPreview,
245 }),
246 ).toEqual([
247 "Browser viewport",
248 "Browser zoom",
249 "Browser appearance",
250 "Recording frame rate",
251 "Recording key presses",
252 "Recording mouse presses",
253 "Open links in",
254 "Floating preview",
255 ]);
256 });
257});