Skip to content

Commit fb25e85

Browse files
committed
test: cover dashboard settings clone defaults and equality semantics
Drives cloneDashboardSettingsData/dashboardSettingsDataEqual with the real resolveMenuLayoutMode and a normalizer matching the dashboard-settings default semantics: - a sparse legacy settings object clones with every optional field at its documented default, statusline fields copied (not aliased), and the layout mode + unselected-rows flag derived together with an explicit menuLayoutMode winning over the legacy boolean - equality treats absent optional fields as their defaults, flags any single-field difference, compares layout through the resolver (the legacy boolean spelling equals the explicit expanded-rows mode), and compares statusline fields through the normalizer with order being significant https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB
1 parent 6ede089 commit fb25e85

1 file changed

Lines changed: 187 additions & 0 deletions

File tree

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import { describe, expect, it } from "vitest";
2+
import {
3+
cloneDashboardSettingsData,
4+
dashboardSettingsDataEqual,
5+
} from "../lib/codex-manager/dashboard-settings-data.js";
6+
import {
7+
DEFAULT_DASHBOARD_DISPLAY_SETTINGS,
8+
type DashboardDisplaySettings,
9+
type DashboardStatuslineField,
10+
} from "../lib/dashboard-settings.js";
11+
import { resolveMenuLayoutMode } from "../lib/codex-manager/settings-hub.js";
12+
13+
const DEFAULT_FIELDS: DashboardStatuslineField[] = [
14+
"last-used",
15+
"limits",
16+
"status",
17+
];
18+
19+
// The real production deps: the actual layout-mode resolver, and a
20+
// normalizer matching dashboard-settings semantics (default on absence).
21+
const deps = {
22+
resolveMenuLayoutMode,
23+
normalizeStatuslineFields: (
24+
fields: DashboardDisplaySettings["menuStatuslineFields"],
25+
) => fields ?? DEFAULT_FIELDS,
26+
};
27+
28+
function settings(
29+
overrides: Partial<DashboardDisplaySettings> = {},
30+
): DashboardDisplaySettings {
31+
return { ...DEFAULT_DASHBOARD_DISPLAY_SETTINGS, ...overrides };
32+
}
33+
34+
// A sparse settings object as it comes from an older settings.json that
35+
// predates most optional fields.
36+
const SPARSE: DashboardDisplaySettings = {
37+
showPerAccountRows: true,
38+
showQuotaDetails: false,
39+
showForecastReasons: true,
40+
showRecommendations: false,
41+
showLiveProbeNotes: true,
42+
};
43+
44+
describe("cloneDashboardSettingsData", () => {
45+
it("fills every optional field with its documented default", () => {
46+
const clone = cloneDashboardSettingsData(SPARSE, deps);
47+
48+
expect(clone).toMatchObject({
49+
actionAutoReturnMs: 2_000,
50+
actionPauseOnKey: true,
51+
menuAutoFetchLimits: true,
52+
menuSortEnabled: true,
53+
menuSortMode: "ready-first",
54+
menuSortPinCurrent: false,
55+
menuSortQuickSwitchVisibleRow: true,
56+
uiThemePreset: "green",
57+
uiAccentColor: "green",
58+
menuShowQuotaSummary: true,
59+
menuShowFetchStatus: true,
60+
menuQuotaTtlMs: 5 * 60_000,
61+
menuFocusStyle: "row-invert",
62+
menuHighlightCurrentRow: true,
63+
menuLayoutMode: "compact-details",
64+
menuShowDetailsForUnselectedRows: false,
65+
menuStatuslineFields: DEFAULT_FIELDS,
66+
});
67+
// The required flags pass through unchanged.
68+
expect(clone.showQuotaDetails).toBe(false);
69+
expect(clone.showRecommendations).toBe(false);
70+
});
71+
72+
it("derives layout mode and the unselected-rows flag together", () => {
73+
const expanded = cloneDashboardSettingsData(
74+
{ ...SPARSE, menuShowDetailsForUnselectedRows: true },
75+
deps,
76+
);
77+
expect(expanded.menuLayoutMode).toBe("expanded-rows");
78+
expect(expanded.menuShowDetailsForUnselectedRows).toBe(true);
79+
80+
// An explicit layout mode wins over the legacy boolean.
81+
const compact = cloneDashboardSettingsData(
82+
{
83+
...SPARSE,
84+
menuLayoutMode: "compact-details",
85+
menuShowDetailsForUnselectedRows: true,
86+
},
87+
deps,
88+
);
89+
expect(compact.menuLayoutMode).toBe("compact-details");
90+
expect(compact.menuShowDetailsForUnselectedRows).toBe(false);
91+
});
92+
93+
it("copies the statusline fields instead of aliasing the input array", () => {
94+
const fields: DashboardStatuslineField[] = ["limits"];
95+
const clone = cloneDashboardSettingsData(
96+
{ ...SPARSE, menuStatuslineFields: fields },
97+
deps,
98+
);
99+
100+
expect(clone.menuStatuslineFields).toEqual(["limits"]);
101+
expect(clone.menuStatuslineFields).not.toBe(fields);
102+
});
103+
});
104+
105+
describe("dashboardSettingsDataEqual", () => {
106+
it("treats absent optional fields as equal to their defaults", () => {
107+
expect(
108+
dashboardSettingsDataEqual(
109+
SPARSE,
110+
cloneDashboardSettingsData(SPARSE, deps),
111+
deps,
112+
),
113+
).toBe(true);
114+
// A fully-defaulted object equals the sparse one when the required
115+
// flags agree.
116+
expect(
117+
dashboardSettingsDataEqual(
118+
settings({ showQuotaDetails: false, showRecommendations: false }),
119+
SPARSE,
120+
deps,
121+
),
122+
).toBe(true);
123+
});
124+
125+
it("detects a difference in any single field", () => {
126+
expect(
127+
dashboardSettingsDataEqual(
128+
settings(),
129+
settings({ menuQuotaTtlMs: 60_000 }),
130+
deps,
131+
),
132+
).toBe(false);
133+
expect(
134+
dashboardSettingsDataEqual(
135+
settings(),
136+
settings({ uiThemePreset: "mono" }),
137+
deps,
138+
),
139+
).toBe(false);
140+
expect(
141+
dashboardSettingsDataEqual(
142+
settings(),
143+
settings({ showQuotaDetails: false }),
144+
deps,
145+
),
146+
).toBe(false);
147+
});
148+
149+
it("compares layout through the resolver, not the raw fields", () => {
150+
// expanded-rows expressed via the legacy boolean equals the explicit
151+
// layout-mode spelling (no explicit menuLayoutMode on the left, since
152+
// an explicit mode would win over the boolean).
153+
expect(
154+
dashboardSettingsDataEqual(
155+
{ ...SPARSE, menuShowDetailsForUnselectedRows: true },
156+
{ ...SPARSE, menuLayoutMode: "expanded-rows" },
157+
deps,
158+
),
159+
).toBe(true);
160+
expect(
161+
dashboardSettingsDataEqual(
162+
settings({ menuLayoutMode: "expanded-rows" }),
163+
settings({ menuLayoutMode: "compact-details" }),
164+
deps,
165+
),
166+
).toBe(false);
167+
});
168+
169+
it("compares statusline fields through the normalizer", () => {
170+
// Absent fields equal the normalized default list...
171+
expect(
172+
dashboardSettingsDataEqual(
173+
settings({ menuStatuslineFields: undefined }),
174+
settings({ menuStatuslineFields: DEFAULT_FIELDS }),
175+
deps,
176+
),
177+
).toBe(true);
178+
// ...but a different order is a real difference.
179+
expect(
180+
dashboardSettingsDataEqual(
181+
settings({ menuStatuslineFields: ["limits", "status", "last-used"] }),
182+
settings({ menuStatuslineFields: DEFAULT_FIELDS }),
183+
deps,
184+
),
185+
).toBe(false);
186+
});
187+
});

0 commit comments

Comments
 (0)