feat: split admin surface into bookmarkable pages (#138)
This commit is contained in:
+47
-15
@@ -640,10 +640,9 @@ type fakeLanes struct{ status latest.Status }
|
||||
|
||||
func (f fakeLanes) LaneStatus() latest.Status { return f.status }
|
||||
|
||||
// The roster moved off the reading page onto its own address: the owner gets a
|
||||
// link, everyone else gets nothing, and the page itself lists every Reader with
|
||||
// the counters and the two controls.
|
||||
func TestAdminPageCarriesRosterAndOwnerLink(t *testing.T) {
|
||||
// Every admin address carries the same navigation, while the roster only lives
|
||||
// on its own page and the other pages keep their shells independent.
|
||||
func TestAdminPagesCarrySharedNavigation(t *testing.T) {
|
||||
router, st, _ := oauthWebTestServer(t)
|
||||
theirCookie := signInCookie(t, router)
|
||||
ownerCookie := sessionCookie(t, st)
|
||||
@@ -654,12 +653,11 @@ func TestAdminPageCarriesRosterAndOwnerLink(t *testing.T) {
|
||||
router.ServeHTTP(rr, req)
|
||||
body := rr.Body.String()
|
||||
if strings.Contains(body, `id="readers"`) {
|
||||
t.Error("the reading page still carries the roster; it belongs on /admin")
|
||||
t.Error("the reading page still carries the roster; it belongs on /admin/readers")
|
||||
}
|
||||
if !strings.Contains(body, `href="/admin"`) {
|
||||
t.Error("the owner's reading page offers no link to the admin page")
|
||||
}
|
||||
|
||||
req = httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.AddCookie(theirCookie)
|
||||
rr = httptest.NewRecorder()
|
||||
@@ -668,21 +666,55 @@ func TestAdminPageCarriesRosterAndOwnerLink(t *testing.T) {
|
||||
t.Error("a non-owner was offered the admin link")
|
||||
}
|
||||
|
||||
req = httptest.NewRequest(http.MethodGet, "/admin", nil)
|
||||
for _, page := range []struct {
|
||||
path string
|
||||
name string
|
||||
}{
|
||||
{"/admin", "Overview"},
|
||||
{"/admin/lanes", "Lanes"},
|
||||
{"/admin/readers", "Readers"},
|
||||
{"/admin/series", "Series"},
|
||||
} {
|
||||
t.Run(page.name, func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, page.path, nil)
|
||||
req.AddCookie(ownerCookie)
|
||||
rr := httptest.NewRecorder()
|
||||
router.ServeHTTP(rr, req)
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("GET %s status = %d, want 200", page.path, rr.Code)
|
||||
}
|
||||
body := rr.Body.String()
|
||||
if !strings.Contains(body, `class="topbar-actions"`) {
|
||||
t.Errorf("%s has no topbar action cluster:\n%s", page.path, body)
|
||||
}
|
||||
if !strings.Contains(body, `aria-label="Admin pages"`) {
|
||||
t.Errorf("%s has no admin navigation:\n%s", page.path, body)
|
||||
}
|
||||
if strings.Count(body, `aria-current="page"`) != 1 {
|
||||
t.Errorf("%s has %d active admin tabs, want 1:\n%s", page.path, strings.Count(body, `aria-current="page"`), body)
|
||||
}
|
||||
if !strings.Contains(body, page.name) {
|
||||
t.Errorf("%s does not name its active page %q:\n%s", page.path, page.name, body)
|
||||
}
|
||||
if !strings.Contains(body, `href="/static/admin.css"`) {
|
||||
t.Errorf("%s does not load the admin foundation stylesheet", page.path)
|
||||
}
|
||||
if page.name == "Lanes" && strings.Count(body, `hx-trigger="every 30s"`) != 1 {
|
||||
t.Errorf("%s has %d Lane timers, want exactly 1", page.path, strings.Count(body, `hx-trigger="every 30s"`))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
req = httptest.NewRequest(http.MethodGet, "/admin/readers", nil)
|
||||
req.AddCookie(ownerCookie)
|
||||
rr = httptest.NewRecorder()
|
||||
router.ServeHTTP(rr, req)
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("GET /admin status = %d, want 200", rr.Code)
|
||||
}
|
||||
body = rr.Body.String()
|
||||
for _, want := range []string{`id="readers"`, testOwnerID, "Revoke sessions", "Clear marks", "confirmed"} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Errorf("admin page lacks %q:\n%s", want, body)
|
||||
t.Errorf("readers page lacks %q:\n%s", want, body)
|
||||
}
|
||||
}
|
||||
// Exactly one revocable row: the other Reader's. The owner's own row carries
|
||||
// the same session count and no button.
|
||||
if n := strings.Count(body, "/revoke"); n != 1 {
|
||||
t.Fatalf("roster has %d revoke controls, want 1 (the owner's own row must have none):\n%s", n, body)
|
||||
}
|
||||
@@ -845,7 +877,7 @@ func TestAdminPageWithoutAPollerSaysSo(t *testing.T) {
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
router, st, _ := oauthWebTestServer(t, tc.lanes...)
|
||||
req := httptest.NewRequest(http.MethodGet, "/admin", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/admin/lanes", nil)
|
||||
req.AddCookie(sessionCookie(t, st))
|
||||
rr := httptest.NewRecorder()
|
||||
router.ServeHTTP(rr, req)
|
||||
@@ -881,7 +913,7 @@ func TestOwnerClearsReaderMarks(t *testing.T) {
|
||||
t.Fatalf("mark reader: %v", err)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/admin", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/admin/readers", nil)
|
||||
req.AddCookie(cookie)
|
||||
rr := httptest.NewRecorder()
|
||||
router.ServeHTTP(rr, req)
|
||||
|
||||
Reference in New Issue
Block a user