1package systemviews
2
3import (
4 "testing"
5
6 "github.com/stretchr/testify/assert"
7)
8
9func TestRenderAppFrame_ReturnsFragment(t *testing.T) {
10 t.Parallel()
11
12 page := renderedPage{
13 route: RouteHistory,
14 subtitle: "History",
15 body: `<ul><li>hello</li></ul>`,
16 }
17 theme := shellTheme{RootClass: "sv-dark"}
18
19 out := renderAppFrame(page, theme)
20
21 // Must be a fragment — no full document tags.
22 assert.NotContains(t, out, "<html")
23 assert.NotContains(t, out, "<head")
24 assert.NotContains(t, out, "<body")
25
26 // Root div carries theme class.
27 assert.Contains(t, out, `class="sv-app sv-dark"`)
28
29 // Route and document title are on the root element.
30 assert.Contains(t, out, `data-route="history"`)
31 assert.Contains(t, out, `data-page-title="History"`)
32
33 // Shell wrapper present.
34 assert.Contains(t, out, `class="sv-shell"`)
35
36 // Body content passed through.
37 assert.Contains(t, out, `<ul><li>hello</li></ul>`)
38
39 // Subtitle rendered.
40 assert.Contains(t, out, "History")
41}
42
43func TestRenderAppFrame_DefaultThemeClass(t *testing.T) {
44 t.Parallel()
45
46 page := renderedPage{
47 route: RouteFavorites,
48 subtitle: "Favorites",
49 body: `<p>content</p>`,
50 }
51 // Empty theme — should still get sv-app with sv-dark default.
52 out := renderAppFrame(page, shellTheme{})
53
54 assert.Contains(t, out, `class="sv-app sv-dark"`)
55 assert.Contains(t, out, `data-route="favorites"`)
56 assert.Contains(t, out, `data-page-title="Favorites"`)
57}
58
59func TestRenderAppFrame_DocumentTitleUsesExplicitTitle(t *testing.T) {
60 t.Parallel()
61
62 page := renderedPage{
63 route: RouteHistory,
64 title: "History — 12 entries",
65 subtitle: "Recent visits",
66 body: `<p>content</p>`,
67 }
68
69 out := renderAppFrame(page, shellTheme{})
70
71 assert.Contains(t, out, `data-page-title="History — 12 entries"`)
72 assert.Contains(t, out, "Recent visits")
73}
74
75func TestRenderAppFrame_InlineVarsApplied(t *testing.T) {
76 t.Parallel()
77
78 page := renderedPage{
79 route: RouteConfig,
80 subtitle: "Config",
81 body: `<p>settings</p>`,
82 }
83 theme := shellTheme{
84 RootClass: "sv-light",
85 InlineVars: "--sv-background: #fff;",
86 }
87
88 out := renderAppFrame(page, theme)
89
90 assert.Contains(t, out, `style="--sv-background: #fff;"`)
91 assert.Contains(t, out, `class="sv-app sv-light"`)
92}
93
94func TestListHTMLWrapsRows(t *testing.T) {
95 t.Parallel()
96
97 out := listHTML(`<li class="sv-list-row">entry</li>`, "Nothing here")
98
99 assert.Contains(t, out, `class="sv-list"`)
100 assert.Contains(t, out, `class="sv-list-row"`)
101 assert.Contains(t, out, "entry")
102}
103
104func TestListHTMLEmptyState(t *testing.T) {
105 t.Parallel()
106
107 out := listHTML("", "Nothing here")
108
109 assert.Contains(t, out, `class="sv-empty"`)
110 assert.Contains(t, out, "Nothing here")
111}
112
113func TestLinkHTMLSanitizesHref(t *testing.T) {
114 t.Parallel()
115
116 out := linkHTML("javascript:alert(1)", "Bad")
117
118 assert.Contains(t, out, `class="sv-link"`)
119 assert.Contains(t, out, `href="#"`)
120 assert.Contains(t, out, "Bad")
121}
122
123func TestKVHTMLRendersPairs(t *testing.T) {
124 t.Parallel()
125
126 out := kvHTML([]kvPair{{Label: "Engine", Value: "webkit"}})
127
128 assert.Contains(t, out, `class="sv-kv"`)
129 assert.Contains(t, out, "Engine")
130 assert.Contains(t, out, "webkit")
131}
132
133func TestKVHTMLEmptySlice(t *testing.T) {
134 t.Parallel()
135
136 out := kvHTML(nil)
137
138 assert.Contains(t, out, `class="sv-empty"`)
139 assert.Contains(t, out, "No configuration details")
140}
141
142func TestKVHTMLAllEmptyRows(t *testing.T) {
143 t.Parallel()
144
145 out := kvHTML([]kvPair{{}, {Label: " ", Value: ""}})
146
147 assert.Contains(t, out, `class="sv-empty"`)
148 assert.Contains(t, out, "No configuration details")
149}
150
151func TestErrorStateHTML(t *testing.T) {
152 t.Parallel()
153
154 out := errorStateHTML("boom")
155
156 assert.Contains(t, out, `class="sv-error"`)
157 assert.Contains(t, out, `role="alert"`)
158 assert.Contains(t, out, "boom")
159}
160
161func TestSectionHTML(t *testing.T) {
162 t.Parallel()
163
164 out := sectionHTML("test-section", "Test Title", "<p>inner</p>")
165 assert.Contains(t, out, `class="sv-section test-section"`)
166 assert.Contains(t, out, "Test Title")
167 assert.Contains(t, out, "<p>inner</p>")
168}
169
170func TestEmptyStateHTML(t *testing.T) {
171 t.Parallel()
172
173 out := emptyStateHTML("Nothing here")
174 assert.Contains(t, out, `class="sv-empty"`)
175 assert.Contains(t, out, "Nothing here")
176}