1import { describe, expect, it } from "vite-plus/test";
2
3import {
4 DEFAULT_BASE_FONT_SIZE,
5 normalizeBaseFontSize,
6 resolveAppearance,
7 resolveAppearancePreferences,
8 resolveMarkdownFontSizes,
9 resolveMobileCodeSurface,
10 resolveNativeMarkdownTypography,
11 resolveTextScaleVariables,
12 stepTerminalFontSize,
13} from "./appearancePreferences";
14
15describe("appearancePreferences", () => {
16 it("resolves defaults for empty stored preferences", () => {
17 expect(resolveAppearancePreferences({})).toEqual({
18 baseFontSize: DEFAULT_BASE_FONT_SIZE,
19 terminalFontSize: null,
20 codeFontSize: null,
21 codeWordBreak: false,
22 });
23 });
24
25 it("migrates the legacy markdownFontSize key to baseFontSize", () => {
26 expect(resolveAppearancePreferences({ markdownFontSize: 18 }).baseFontSize).toBe(18);
27 expect(
28 resolveAppearancePreferences({ baseFontSize: 16, markdownFontSize: 18 }).baseFontSize,
29 ).toBe(16);
30 });
31
32 it("keeps explicit overrides and treats missing values as automatic", () => {
33 const preferences = resolveAppearancePreferences({ terminalFontSize: 12, codeFontSize: 14 });
34 expect(preferences.terminalFontSize).toBe(12);
35 expect(preferences.codeFontSize).toBe(14);
36 expect(resolveAppearancePreferences({ terminalFontSize: null }).terminalFontSize).toBe(null);
37 });
38
39 it("derives terminal and code sizes from the base size when not overridden", () => {
40 const appearance = resolveAppearance(resolveAppearancePreferences({ baseFontSize: 15 }));
41 expect(appearance.terminalFontSize).toBe(10);
42 expect(appearance.codeFontSize).toBe(11);
43 expect(appearance.isTerminalFontSizeCustom).toBe(false);
44 expect(appearance.isCodeFontSizeCustom).toBe(false);
45
46 const scaled = resolveAppearance(resolveAppearancePreferences({ baseFontSize: 22 }));
47 expect(scaled.terminalFontSize).toBe(14);
48 expect(scaled.codeFontSize).toBe(17);
49 });
50
51 it("applies explicit overrides over derived values", () => {
52 const appearance = resolveAppearance(
53 resolveAppearancePreferences({ baseFontSize: 22, terminalFontSize: 8, codeFontSize: 9 }),
54 );
55 expect(appearance.terminalFontSize).toBe(8);
56 expect(appearance.codeFontSize).toBe(9);
57 expect(appearance.isTerminalFontSizeCustom).toBe(true);
58 expect(appearance.isCodeFontSizeCustom).toBe(true);
59 });
60
61 it("clamps base and code font sizes", () => {
62 expect(normalizeBaseFontSize(4)).toBe(11);
63 expect(normalizeBaseFontSize(30)).toBe(22);
64 expect(resolveAppearancePreferences({ codeFontSize: 4 }).codeFontSize).toBe(8);
65 expect(resolveAppearancePreferences({ codeFontSize: 30 }).codeFontSize).toBe(18);
66 });
67
68 it("steps terminal font size within bounds", () => {
69 expect(stepTerminalFontSize(6, -1)).toBe(6);
70 });
71
72 it("scales markdown typography from the base size", () => {
73 expect(resolveMarkdownFontSizes(15)).toMatchObject({
74 m: 15,
75 h1: 20,
76 bodyLineHeight: 22,
77 codeBlockFontSize: 12,
78 codeBlockLineHeight: 18,
79 });
80 });
81
82 it("scales code surface geometry from the code font size", () => {
83 expect(resolveMobileCodeSurface(11)).toMatchObject({
84 fontSize: 11,
85 rowHeight: 20,
86 });
87 });
88
89 it("keeps explicit code word break enabled", () => {
90 expect(resolveAppearancePreferences({ codeWordBreak: true }).codeWordBreak).toBe(true);
91 });
92
93 it("preserves the no-wrap default unless wrapping is explicitly enabled", () => {
94 expect(resolveAppearancePreferences(undefined).codeWordBreak).toBe(false);
95 expect(resolveAppearancePreferences({}).codeWordBreak).toBe(false);
96 expect(resolveAppearancePreferences({ codeWordBreak: null }).codeWordBreak).toBe(false);
97 expect(resolveAppearancePreferences({ codeWordBreak: false }).codeWordBreak).toBe(false);
98 });
99
100 it("returns the authored text scale at the 16pt default", () => {
101 expect(DEFAULT_BASE_FONT_SIZE).toBe(16);
102
103 const variables = resolveTextScaleVariables(DEFAULT_BASE_FONT_SIZE);
104 expect(variables["--text-base"]).toBe(16);
105 expect(variables["--text-base--line-height"]).toBe(23);
106 expect(variables["--text-sm"]).toBe(14);
107 expect(variables["--text-sm--line-height"]).toBe(19);
108 expect(variables["--text-lg"]).toBe(18);
109 expect(variables["--text-3xl"]).toBe(30);
110 });
111
112 it("scales every text variable proportionally with the base size", () => {
113 const smallerVariables = resolveTextScaleVariables(15);
114 expect(smallerVariables["--text-base"]).toBe(15);
115 expect(smallerVariables["--text-sm"]).toBe(13);
116
117 const variables = resolveTextScaleVariables(20);
118 expect(variables["--text-base"]).toBe(20);
119 expect(variables["--text-base--line-height"]).toBe(29);
120 expect(variables["--text-sm"]).toBe(18);
121 expect(variables["--text-xs"]).toBe(16);
122 expect(variables["--text-lg"]).toBe(23);
123
124 const smaller = resolveTextScaleVariables(11);
125 expect(smaller["--text-base"]).toBe(11);
126 expect(smaller["--text-3xs"]).toBeGreaterThanOrEqual(8);
127 expect(smaller["--text-3xs--line-height"]).toBeGreaterThanOrEqual(10);
128 });
129
130 it("derives native markdown typography from the base size", () => {
131 expect(resolveNativeMarkdownTypography(22)).toEqual({
132 fontSize: 22,
133 lineHeight: 32,
134 headingFontSizes: [29, 26, 23, 21, 21, 21],
135 });
136 });
137});