1package systemviews
2
3import (
4 "testing"
5
6 "github.com/bnema/dumber/internal/application/dto"
7 "github.com/stretchr/testify/assert"
8)
9
10func TestBuildInlineVarsRejectsUnsafeCSSValues(t *testing.T) {
11 t.Parallel()
12
13 vars := buildInlineVars(dto.WebUIAppearanceConfig{
14 SansFont: "Inter; color:red",
15 SerifFont: "Georgia",
16 MonospaceFont: "JetBrains Mono",
17 LightPalette: dto.ColorPalette{
18 Background: "#ffffff; color:red",
19 Surface: "rgb(1 2 3 / 50%)",
20 Text: "transparent",
21 Accent: "definitelynotacolor",
22 Border: "#ABCDEF80",
23 },
24 }, dto.ColorPalette{
25 Background: "#ffffff; color:red",
26 Surface: "rgb(1 2 3 / 50%)",
27 Text: "transparent",
28 Accent: "definitelynotacolor",
29 Border: "#ABCDEF80",
30 })
31
32 assert.NotContains(t, vars, "color:red")
33 assert.NotContains(t, vars, "--sv-background")
34 assert.NotContains(t, vars, "--sv-font-sans")
35 assert.Contains(t, vars, "--sv-surface: rgb(1 2 3 / 50%);")
36 assert.Contains(t, vars, "--sv-text: transparent;")
37 assert.NotContains(t, vars, "definitelynotacolor")
38 assert.Contains(t, vars, "--sv-border: #ABCDEF80;")
39 assert.Contains(t, vars, "--sv-font-serif: Georgia;")
40 assert.Contains(t, vars, "--sv-font-mono: JetBrains Mono;")
41}
42
43func TestSanitizeCSSFontFamilyQuotes(t *testing.T) {
44 t.Parallel()
45
46 tests := []struct {
47 name string
48 value string
49 want bool
50 }{
51 {name: "unquoted spaces", value: "JetBrains Mono", want: true},
52 {name: "single quoted token", value: "'JetBrains Mono'", want: true},
53 {name: "double quoted token", value: "\"JetBrains Mono\"", want: true},
54 {name: "unquoted single quote", value: "JetBrains' Mono", want: false},
55 {name: "unquoted double quote", value: "JetBrains\" Mono", want: false},
56 {name: "internal quote", value: "'JetBrains' Mono'", want: false},
57 {name: "breakout", value: "'JetBrains Mono'; color:red", want: false},
58 }
59
60 for _, tt := range tests {
61 t.Run(tt.name, func(t *testing.T) {
62 t.Parallel()
63
64 _, got := sanitizeCSSFontFamily(tt.value)
65 assert.Equal(t, tt.want, got)
66 })
67 }
68}
69
70func TestResolveShellTheme(t *testing.T) {
71 orig := currentPrefersDarkImpl
72 t.Cleanup(func() {
73 currentPrefersDarkImpl = orig
74 })
75
76 appearance := dto.WebUIAppearanceConfig{
77 SansFont: "Inter",
78 SerifFont: "Georgia",
79 MonospaceFont: "JetBrains Mono",
80 DefaultFontSize: 16,
81 ColorScheme: "default",
82 LightPalette: dto.ColorPalette{
83 Background: "#ffffff",
84 Surface: "#f8f8f8",
85 SurfaceVariant: "#eeeeee",
86 Text: "#111111",
87 Muted: "#666666",
88 Accent: "#0055ff",
89 Border: "#dddddd",
90 },
91 DarkPalette: dto.ColorPalette{
92 Background: "#111111",
93 Surface: "#1a1a1a",
94 SurfaceVariant: "#2a2a2a",
95 Text: "#f5f5f5",
96 Muted: "#a0a0a0",
97 Accent: "#66aaff",
98 Border: "#333333",
99 },
100 }
101
102 tests := []struct {
103 name string
104 colorScheme string
105 prefersDark bool
106 wantClass string
107 wantBackground string
108 wantSurfaceVariant string
109 }{
110 {name: "prefer-dark", colorScheme: "prefer-dark", prefersDark: false, wantClass: "sv-dark", wantBackground: "#111111", wantSurfaceVariant: "#2a2a2a"},
111 {name: "prefer-light", colorScheme: "prefer-light", prefersDark: true, wantClass: "sv-light", wantBackground: "#ffffff", wantSurfaceVariant: "#eeeeee"},
112 {name: "default prefers dark", colorScheme: "default", prefersDark: true, wantClass: "sv-dark", wantBackground: "#111111", wantSurfaceVariant: "#2a2a2a"},
113 {name: "default prefers light", colorScheme: "default", prefersDark: false, wantClass: "sv-light", wantBackground: "#ffffff", wantSurfaceVariant: "#eeeeee"},
114 }
115
116 for _, tt := range tests {
117 t.Run(tt.name, func(t *testing.T) {
118 currentPrefersDarkImpl = func() bool { return tt.prefersDark }
119 ttAppearance := appearance
120 ttAppearance.ColorScheme = tt.colorScheme
121
122 got := resolveShellTheme(ttAppearance)
123
124 assert.Equal(t, tt.wantClass, got.RootClass)
125 assert.Contains(t, got.InlineVars, "--sv-background")
126 assert.Contains(t, got.InlineVars, "--sv-font-sans")
127 assert.Contains(t, got.InlineVars, tt.wantBackground)
128 assert.Contains(t, got.InlineVars, tt.wantSurfaceVariant)
129 })
130 }
131}