~bigbes/sr-ht-ecore

ref: 3bd158fbb23241df31897f9ba47afac832ee2e1d sr-ht-ecore/chrome/sections_test.go -rw-r--r-- 4.3 KiB
3bd158fb — Eugene Blikh mcphttp: pin Flush with a flush that precedes any write 2 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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")
}