Files
bookhoard/templates/admin_archived_render_test.go
T
John O'Keefe f1e4b2a603
Release / build-and-push (push) Successful in 3m15s
fix(admin): un-dead the archived page's purge and delete buttons
Two independent dead-ends, confirmed on prod's /admin/archived
(2026-10): Purge All rendered but clicking it did nothing at all, and
per-row Delete never even showed its confirm dialog.

Purge All is Alpine-bound (@click="purgeArchivedItems()"), but this
page's <body> lacked x-data="admin" — the only admin page without it.
Alpine evaluates directives only inside an x-data tree, so the
attribute was inert: the admin component (and its method, registered
in admin.ts) never instantiated here. The body now carries
x-data="admin", matching admin.templ and admin_library.templ.

Per-item Delete was a second, subtler dead-end: admin.ts's API helpers
guard with `if (!token) return;` BEFORE the confirm dialog, and the
reporting session had no localStorage token — SSR pages run on the
cookie session, so the web UI works while every Bearer-
authenticated admin action (purge, delete, restore, merge, scan-all,
cancel-scan) silently no-ops with zero feedback. Those guards now go
through requireToken, which toasts "Session expired — sign out and
back in, then retry" when the token is absent. The two passive
page-load probes (loadWatchStatus, checkActiveScans) deliberately keep
the silent return: a toast there would fire on every visit.

Pinned by TestAdminArchivedPageHasAlpineScope: renders the page and
asserts the body scope and the action buttons coexist in one render.

After deploy: hard-refresh prod, click Purge All (confirm appears);
if the token toast fires on any admin action, log out and back in once
to stash a fresh API token.
2026-10-28 09:37:14 -04:00

48 lines
1.7 KiB
Go

package templates
import (
"context"
"strings"
"testing"
"time"
)
// The archived-items admin page binds its buttons through the page-wide
// Alpine component: Purge All is @click="purgeArchivedItems()" and the
// per-row Delete/Restore/Merge buttons rely on listeners main.ts binds at
// module load. The body tag therefore MUST carry x-data="admin" — the
// other admin pages have it, and when this page's body lost it the purge
// button rendered as an inert attribute (Alpine only evaluates directives
// inside an x-data tree; the click did literally nothing).
//
// Regression: /admin/archived rendered <body class="theme-…"> with no
// Alpine scope, so "Purge All Archived" was dead (prod report 2026-10).
func TestAdminArchivedPageHasAlpineScope(t *testing.T) {
archivedAt := time.Date(2026, 10, 28, 4, 38, 0, 0, time.UTC)
var b strings.Builder
err := AdminArchived(User{ID: "u1", Timezone: "UTC"}, []ArchivedItem{{
ID: "m1",
Title: "Lost Book",
Author: "An Author",
LibraryName: "Audiobooks",
FilePath: "Author/Book/file.m4b",
MissingScans: 2,
ArchivedAt: &archivedAt,
Status: "Archived - pending purge",
}}, 0).Render(context.Background(), &b)
if err != nil {
t.Fatalf("render: %v", err)
}
out := b.String()
if !strings.Contains(out, `<body x-data="admin"`) {
t.Error("archived page body missing x-data=\"admin\" — the Alpine-bound purge button would be dead")
}
// The action buttons the page depends on must be present in the same
// render, so the pin cannot pass on an empty shell.
for _, want := range []string{"purgeArchivedItems()", `data-delete-archived="m1"`} {
if !strings.Contains(out, want) {
t.Errorf("archived page missing %q", want)
}
}
}