package web
import (
"strings"
"testing"
)
// The composer offers two views of one draft: Rich, reading its blocks
// from this repository's markup-blocks and its pickers' lists from
// ref.suggest, and Markdown, the textarea the form posts. There is no third Preview: Rich renders through the same
// pipeline. Without script the tab bar stays hidden and the textarea is
// the composer.
func TestTheComposerOffersRichAndMarkdown(t *testing.T) {
var b strings.Builder
if err := noHooks.template(forumAnnouncePage).ExecuteTemplate(&b, "content", pageData{Repo: "alpha", Composer: true}); err != nil {
t.Fatal(err)
}
s := b.String()
if !strings.Contains(s, `data-hook="composer-rich"`) || !strings.Contains(s, `data-blocks="/r/alpha/markup-blocks"`) {
t.Error("FAIL: the composer has no Rich view reading this repository's blocks")
}
if !strings.Contains(s, `data-suggest="/r/alpha/ref.suggest"`) {
t.Error("FAIL: the Rich view's pickers do not ask this repository")
}
if !strings.Contains(s, `data-hook="composer-write"`) || strings.Contains(s, `data-tab="preview"`) {
t.Error("FAIL: the composer's tabs are not Rich and Markdown")
}
if !strings.Contains(s, `<div class="f-tabs" hidden>`) || !strings.Contains(s, `name="body"`) {
t.Error("FAIL: without script the composer is not the plain textarea")
}
}
// "/" opens the search everywhere except where somebody is typing, and
// the Rich composer is a place where somebody is typing: it is an
// editable element, not an input, so the shortcut has to ask.
func TestSlashSearchLeavesTheRichComposerAlone(t *testing.T) {
shortcuts := strings.Count(baseHTML, `evt.key == '/'`)
guarded := strings.Count(baseHTML, `evt.key == '/' && !/INPUT|TEXTAREA|SELECT/.test(document.activeElement.tagName) && !document.activeElement.isContentEditable`)
if shortcuts == 0 || guarded != shortcuts {
t.Errorf("FAIL: %d of %d search shortcuts skip an editable element", guarded, shortcuts)
}
}
// Whoever may edit a post (forum.get hands them its source: the author,
// whoever answers for an agent author, a moderator) sees Edit beside
// Reply, and the edit page is the composer holding the post's text.
func TestAnAuthorEditsTheirPostInTheComposer(t *testing.T) {
data := pageData{Repo: "alpha", ForumRoot: "abc123", ForumViewer: "lou", CanWrite: true,
ForumPosts: []forumPostRow{
{Hash: "aaaaaaaaaaaa1111", User: "lou-bot", HTML: "<p>my agent's</p>", Source: "my agent's", Date: "2026-09-16T09:00:00Z",
Edits: 1, EditedBy: "lou"},
{Hash: "bbbbbbbbbbbb2222", User: "mia", HTML: "<p>hers</p>", Date: "2026-09-16T09:05:00Z"},
}}
var b strings.Builder
if err := noHooks.template(forumThreadPage).ExecuteTemplate(&b, "forum-posts", data); err != nil {
t.Fatal(err)
}
if n := strings.Count(b.String(), `data-hook="post-edit"`); n != 1 {
t.Errorf("FAIL: %d Edit buttons, want one, on the post the viewer may edit", n)
}
if !strings.Contains(b.String(), `/r/alpha/forum/abc123/edit/aaaaaaaaaaaa1111`) {
t.Error("FAIL: Edit does not lead to the post the viewer may edit")
}
if !strings.Contains(b.String(), "edited by lou") {
t.Error("FAIL: an edit by someone other than the author does not say who made it")
}
var e strings.Builder
edit := pageData{Repo: "alpha", ForumRoot: "abc123",
ForumPosts: []forumPostRow{{Hash: "aaaaaaaaaaaa1111", User: "lou", Source: "the **text**"}}}
if err := noHooks.template(forumEditPage).ExecuteTemplate(&e, "content", edit); err != nil {
t.Fatal(err)
}
if !strings.Contains(e.String(), `data-hook="composer-rich"`) || !strings.Contains(e.String(), `name="body"`) ||
!strings.Contains(e.String(), "the **text**") {
t.Error("FAIL: the edit page is not the composer holding the post's text")
}
}