package chrome
import (
"html/template"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// testSections is the row artifacts declares, which is the shape that motivated
// the type: three sections with a path of their own, and a first one standing
// for the service root plus the listing rows reached from it.
func testSections() []Section {
return []Section{
{Name: "channels", Href: "/", Paths: []string{"/"}, Prefixes: []string{"/~"}},
{Name: "images", Href: "/images", Paths: []string{"/images"}},
{Name: "cache", Href: "/cache", Paths: []string{"/cache"}},
{Name: "mirrors", Href: "/mirrors", Paths: []string{"/mirrors"}},
}
}
// renderSections executes a layout invoking the section partial, the way a
// service's own layout does.
func renderSections(t *testing.T, v any) string {
t.Helper()
tpl := MustAttach(template.New("layout"))
tpl, err := tpl.Parse(`<nav>{{template "srht-nav" .}}</nav>{{template "srht-sections" .}}`)
require.NoError(t, err)
var b strings.Builder
require.NoError(t, tpl.Execute(&b, v))
return b.String()
}
// activeNames is the tabs a path lights up, which for a well-formed row is
// never more than one.
func activeNames(tabs []SectionTab) []string {
var names []string
for _, tab := range tabs {
if tab.Active {
names = append(names, tab.Name)
}
}
return names
}
func TestSectionRowRendersUpstreamMarkup(t *testing.T) {
svc := NewService(testConf(), "diff.sr.ht")
svc.Sections = testSections()
page := svc.Page(httptest.NewRequest("GET", "/cache", nil), "t", "alice")
out := renderSections(t, page)
// The classes are the whole reason the row needs no stylesheet of its own:
// both come from core.sr.ht's nav.scss through every service's base import.
assert.Contains(t, out, `<div class="header-tabbed">`)
assert.Contains(t, out, `<ul class="nav nav-tabs">`)
assert.Contains(t, out, `<a class="nav-link active" href="/cache">cache</a>`)
assert.Contains(t, out, `<a class="nav-link" href="/images">images</a>`)
}
// The row is what the switcher cannot be: the switcher's active element names
// the service, which is true of every page it serves.
func TestActiveTabFollowsTheRequestPath(t *testing.T) {
svc := NewService(testConf(), "diff.sr.ht")
svc.Sections = testSections()
for _, tc := range []struct {
path string
active string // "" when no tab lights up
}{
{path: "/", active: "channels"},
// A listing row belongs to the section whose listing carries it.
{path: "/~alice/main", active: "channels"},
{path: "/images", active: "images"},
{path: "/images/alice/tool", active: "images"},
{path: "/mirrors/alpine/rules", active: "mirrors"},
// The prefix matches as text; the segment boundary is what keeps the
// tab dark.
{path: "/mirrorsomething"},
// A path in no section - the 404 - lights nothing rather than falling
// back to the first tab.
{path: "/nowhere"},
} {
t.Run(tc.path, func(t *testing.T) {
page := svc.Page(httptest.NewRequest("GET", tc.path, nil), "t", "alice")
if tc.active == "" {
assert.Empty(t, activeNames(page.Tabs))
return
}
assert.Equal(t, []string{tc.active}, activeNames(page.Tabs))
})
}
}
// The root must not swallow every path under it: it is "/" as a segment, and
// "/" is the prefix of everything.
func TestServiceRootMatchesOnlyItself(t *testing.T) {
root := Section{Name: "overview", Href: "/", Paths: []string{"/"}}
assert.True(t, root.matches("/"))
assert.False(t, root.matches("/images"))
assert.False(t, root.matches("/images/alice/tool"))
}
// A service that declared no sections renders exactly what it did before the
// field existed, which is every service on the instance but one.
func TestServiceWithoutSectionsRendersNoRow(t *testing.T) {
svc := NewService(testConf(), "diff.sr.ht")
page := svc.Page(httptest.NewRequest("GET", "/", nil), "t", "alice")
assert.Empty(t, page.Tabs)
assert.NotContains(t, renderSections(t, page), "header-tabbed")
}
// Chrome for a reader with a session, the rule the switcher already follows.
func TestAnonymousViewerGetsNoSectionRow(t *testing.T) {
svc := NewService(testConf(), "diff.sr.ht")
svc.Sections = testSections()
page := svc.Page(httptest.NewRequest("GET", "/cache", nil), "t", "")
assert.Empty(t, page.Tabs)
assert.NotContains(t, renderSections(t, page), "header-tabbed")
}