diff options
| author | grm <grm@eyesin.space> | 2026-09-17 20:15:14 +0300 |
|---|---|---|
| committer | grm <grm@eyesin.space> | 2026-09-17 20:15:14 +0300 |
| commit | 69912c4ac04ada3286c09400ce523d5d3ab4cfb4 (patch) | |
| tree | 409c59ce65e1612d587aa698bb5e25c2cf7bfc0b /internal/web/web_test.go | |
| parent | 616aa3a934951260d5daa81ab3d0f93fbc94ebee (diff) | |
| download | blogspace-69912c4ac04ada3286c09400ce523d5d3ab4cfb4.tar.gz blogspace-69912c4ac04ada3286c09400ce523d5d3ab4cfb4.tar.bz2 blogspace-69912c4ac04ada3286c09400ce523d5d3ab4cfb4.zip | |
Add a header rule beside the title or logo, and plain menu links
gizmolab's header is a logo with a striped rule filling the rest of the
row, and a menu of plain underlined links under it; nothing in the Design
tab could draw either. The rule (line, dots or stripes) is a theme option
drawn in the header text colour beside the title and logo modules — on
both sides when the header is centred — as table cells, so it is as tall
as the brand and old browsers lay it out the same; it is hidden on phones
so a long title can wrap. The menu's link style gains "plain, underlined"
next to bold and uppercase.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
Diffstat (limited to 'internal/web/web_test.go')
| -rw-r--r-- | internal/web/web_test.go | 54 |
1 files changed, 50 insertions, 4 deletions
diff --git a/internal/web/web_test.go b/internal/web/web_test.go index fcd1e41..914c991 100644 --- a/internal/web/web_test.go +++ b/internal/web/web_test.go @@ -82,8 +82,8 @@ func TestRootRoutePrecedence(t *testing.T) { } func TestThemeNormalizeAndCSS(t *testing.T) { - th := ParseTheme([]byte(`{"bg_color":"red;}body{display:none","font":"comic","bg_image":"../etc","logo":"../etc","link_hover":"blue","heading_font":"wingdings","favicon":"x","logo_size":"huge","left_width":5,"right_width":90}`)) - if th.BgColor != DefaultTheme().BgColor || th.Font != "sans" || th.BgImage != "" || th.Logo != "" || th.LinkHover != "" || th.HeadingFont != "same" || th.Favicon != "" || th.LogoSize != "medium" { + th := ParseTheme([]byte(`{"bg_color":"red;}body{display:none","font":"comic","bg_image":"../etc","logo":"../etc","link_hover":"blue","heading_font":"wingdings","favicon":"x","logo_size":"huge","left_width":5,"right_width":90,"header_rule":"neon\"><script>"}`)) + if th.BgColor != DefaultTheme().BgColor || th.Font != "sans" || th.BgImage != "" || th.Logo != "" || th.LinkHover != "" || th.HeadingFont != "same" || th.Favicon != "" || th.LogoSize != "medium" || th.HeaderRule != "none" || th.RuleClass() != "" { t.Errorf("normalize: %+v", th) } if th.LeftWidth != 15 || th.RightWidth != 40 { @@ -101,9 +101,9 @@ func TestThemeNormalizeAndCSS(t *testing.T) { } f := url.Values{"bg_color": {"#ABCDEF"}, "bg_image": {"not-a-uuid"}, "logo_size": {"large"}, "link_hover": {"#ff0000"}, "nav_hover": {"#00ff00"}, "nav_hover_custom": {"on"}, "nav_style": {"uppercase"}, "nav_align": {"center"}, - "link_underline": {"hover"}, "heading_font": {"serif"}, "content_style": {"flat"}, "content_padding": {"roomy"}, "date_format": {"iso"}} + "link_underline": {"hover"}, "heading_font": {"serif"}, "content_style": {"flat"}, "content_padding": {"roomy"}, "date_format": {"iso"}, "header_rule": {"stripes"}} th = ThemeFromForm(DefaultTheme(), f) - if th.BgColor != "#abcdef" || th.BgImage != "" || th.LogoSize != "large" { + if th.BgColor != "#abcdef" || th.BgImage != "" || th.LogoSize != "large" || th.RuleClass() != "ruled rule-stripes" { t.Errorf("from form: %+v", th) } if th.LinkHover != "" || th.NavHover != "#00ff00" || th.ShowDates || th.DateFormat != "iso" { @@ -123,6 +123,52 @@ func TestThemeNormalizeAndCSS(t *testing.T) { if css := DefaultTheme().CSS(); strings.Contains(css, "padding:") || strings.Contains(css, "a:hover") { t.Errorf("default css should not set padding or hover: %s", css) } + // Plain menu links: regular weight and underlined, instead of blog.css's bold. + css = ThemeFromForm(DefaultTheme(), url.Values{"nav_style": {"plain"}}).CSS() + if !strings.Contains(css, ".site-nav a { color:#222222; font-weight:normal; text-decoration:underline; }") { + t.Errorf("plain nav css: %s", css) + } +} + +// The header rule is drawn beside the title and logo blocks — after them, and +// before them too when the header is centred — and not at all by default. +func TestHeaderRule(t *testing.T) { + tpl, err := newTemplates(false).get("en", "blog/page.html") + if err != nil { + t.Fatal(err) + } + logo := uuid.New().String() + render := func(kind string, th Theme) string { + var b strings.Builder + root := view{Blog: &store.Blog{Title: "Gizmo", Tagline: "a hacklab"}, Data: map[string]any{"theme": th, "layout": Layout{}}} + if err := tpl.ExecuteTemplate(&b, "module", map[string]any{"m": store.Module{Area: "header", Kind: kind}, "root": root}); err != nil { + t.Fatal(err) + } + return b.String() + } + plain := DefaultTheme() + plain.Logo = logo + for _, kind := range []string{"title", "logo"} { + if out := render(kind, plain); strings.Contains(out, "header-rule") || strings.Contains(out, "ruled") { + t.Errorf("%s without a rule: %s", kind, out) + } + } + left := plain + left.HeaderRule = "stripes" + out := render("title", left) + if !strings.Contains(out, `class="wrap header-text ruled rule-stripes"`) || strings.Count(out, `<div class="header-rule"></div>`) != 1 || !strings.HasSuffix(strings.TrimSpace(out), `</div><div class="header-rule"></div></div>`) { + t.Errorf("left-aligned rule: %s", out) + } + if !strings.Contains(out, `<p class="tagline">a hacklab</p>`) { + t.Errorf("tagline lost: %s", out) + } + centred := left + centred.HeaderAlign, centred.HeaderRule = "center", "line" + out = render("logo", centred) + if !strings.Contains(out, `class="wrap header-logo ruled rule-line"`) || strings.Count(out, `<div class="header-rule"></div>`) != 2 || + !strings.Contains(out, `<div class="header-rule"></div><div class="brand"><a href="/"><img class="logo" src="/media/`+logo+`"`) { + t.Errorf("centred rule: %s", out) + } } func TestLayoutTheme(t *testing.T) { |
