Click a summarized card → its full summary (summaryBody) + chat dock (chatReveal)
expand in place via HTMX (GET /v/{id}/expand → expandedCard), collapse back via
GET /v/{id}/card → compact VideoCard. Same <li id>, outerHTML swap — the existing
list-fragment pattern. The card title carries href=/v/{id} as the no-JS fallback
(detail page stays for no-JS + deep links); only summarized cards expand. Reuses
summaryBody + chatReveal so the expanded card never drifts from the detail page.
BDD: inline_expand.feature un-pended + mapped. TDD: 6 handler/fragment tests
(expand/collapse fragments, chat dock, summarized-only, no-JS href, shared body).
Minimal CSS only — the TUI/charm restyle is #17.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
108 lines
4.2 KiB
Go
108 lines
4.2 KiB
Go
package web_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestExpandReturnsSummaryBodyFragment: GET /v/{id}/expand returns the full
|
|
// summary as an in-place card fragment (not a full page) — ADR-031.
|
|
func TestExpandReturnsSummaryBodyFragment(t *testing.T) {
|
|
ctx := context.Background()
|
|
app := newApp(t)
|
|
p := rawPool(t)
|
|
resetDB(t, p)
|
|
require.NoError(t, deliver(ctx, app, videoX, "the full summary text"))
|
|
seedVideo(t, p, videoX, "X Title", "https://x", time.Time{})
|
|
|
|
html := body(t, do(t, app, httptest.NewRequest(http.MethodGet, "/v/"+videoX+"/expand", nil)))
|
|
require.Contains(t, html, "the full summary text")
|
|
require.Contains(t, html, "Takeaways")
|
|
require.Contains(t, html, "highlight one")
|
|
require.Contains(t, html, "card-expanded", "rendered as the expanded card")
|
|
require.Contains(t, html, "/v/"+videoX+"/card", "carries a collapse affordance")
|
|
require.NotContains(t, html, "<html", "fragment, not a full page")
|
|
}
|
|
|
|
// TestCollapseReturnsCompactCard: GET /v/{id}/card returns the compact card with
|
|
// the expand affordance — the collapse target.
|
|
func TestCollapseReturnsCompactCard(t *testing.T) {
|
|
ctx := context.Background()
|
|
app := newApp(t)
|
|
p := rawPool(t)
|
|
resetDB(t, p)
|
|
require.NoError(t, deliver(ctx, app, videoX, "summary text"))
|
|
seedVideo(t, p, videoX, "X Title", "https://x", time.Time{})
|
|
|
|
html := body(t, do(t, app, httptest.NewRequest(http.MethodGet, "/v/"+videoX+"/card", nil)))
|
|
require.Contains(t, html, `class="card"`, "compact card")
|
|
require.Contains(t, html, "/v/"+videoX+"/expand", "compact card offers expand")
|
|
require.NotContains(t, html, "card-expanded")
|
|
require.NotContains(t, html, "<html", "fragment, not a full page")
|
|
}
|
|
|
|
// TestExpandedCardOffersChatDock: with chat enabled, the expanded card includes
|
|
// the deeper-dive chat affordance.
|
|
func TestExpandedCardOffersChatDock(t *testing.T) {
|
|
ctx := context.Background()
|
|
app := newChatApp(t, &fakeChatter{models: []string{"m"}}, nil)
|
|
p := rawPool(t)
|
|
resetDB(t, p)
|
|
require.NoError(t, deliver(ctx, app, videoX, "summary text"))
|
|
seedVideo(t, p, videoX, "X Title", "https://x", time.Time{})
|
|
|
|
html := body(t, do(t, app, httptest.NewRequest(http.MethodGet, "/v/"+videoX+"/expand", nil)))
|
|
require.Contains(t, html, "/v/"+videoX+"/chat", "expanded card wires the chat dock")
|
|
}
|
|
|
|
// TestCompactCardExpandOnlyWhenSummarized: a not-yet-summarized card shows its
|
|
// summarize footer and no expand affordance.
|
|
func TestCompactCardExpandOnlyWhenSummarized(t *testing.T) {
|
|
app := newApp(t)
|
|
p := rawPool(t)
|
|
resetDB(t, p)
|
|
seedVideo(t, p, videoX, "Pending Title", "https://x", time.Time{}) // no summary
|
|
|
|
html := body(t, do(t, app, httptest.NewRequest(http.MethodGet, "/v/"+videoX+"/card", nil)))
|
|
require.Contains(t, html, "Not summarized")
|
|
require.NotContains(t, html, "/v/"+videoX+"/expand", "pending card offers no expand")
|
|
}
|
|
|
|
// TestCompactCardHasNoJSDetailFallback: the expand affordance carries an href to
|
|
// the detail page, so JS-off users still reach the full summary.
|
|
func TestCompactCardHasNoJSDetailFallback(t *testing.T) {
|
|
ctx := context.Background()
|
|
app := newApp(t)
|
|
p := rawPool(t)
|
|
resetDB(t, p)
|
|
require.NoError(t, deliver(ctx, app, videoX, "summary text"))
|
|
seedVideo(t, p, videoX, "X Title", "https://x", time.Time{})
|
|
|
|
html := body(t, do(t, app, httptest.NewRequest(http.MethodGet, "/v/"+videoX+"/card", nil)))
|
|
require.Contains(t, html, `href="/v/`+videoX+`"`, "no-JS fallback to the detail page")
|
|
require.Contains(t, html, "/v/"+videoX+"/expand", "and the HTMX expand for JS users")
|
|
}
|
|
|
|
// TestDetailAndExpandShareSummaryBody: the detail page and the expanded card render
|
|
// the same summary body (one shared fragment, no drift).
|
|
func TestDetailAndExpandShareSummaryBody(t *testing.T) {
|
|
ctx := context.Background()
|
|
app := newApp(t)
|
|
p := rawPool(t)
|
|
resetDB(t, p)
|
|
require.NoError(t, deliver(ctx, app, videoX, "shared summary text"))
|
|
seedVideo(t, p, videoX, "X Title", "https://x", time.Time{})
|
|
|
|
detail := body(t, do(t, app, httptest.NewRequest(http.MethodGet, "/v/"+videoX, nil)))
|
|
expand := body(t, do(t, app, httptest.NewRequest(http.MethodGet, "/v/"+videoX+"/expand", nil)))
|
|
for _, want := range []string{"shared summary text", "Takeaways", "highlight one"} {
|
|
require.Contains(t, detail, want)
|
|
require.Contains(t, expand, want)
|
|
}
|
|
}
|