Skip to content
quarry Artifact [95e1996614]
Login

Artifact [95e1996614]

Artifact 95e19966147c6d5eb94465d30ccc7ebcb54f8df390c1465eb916749aaebd99e6:


package web

import (
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"
)

// The selection column is drawn only for a viewer who holds both
// letters chat.promote needs, and the boxes reach the promote form
// through the form attribute — a form cannot be nested in the composer.
func TestChatSelectionRendersOnlyForPromoters(t *testing.T) {
	msgs := []ChatMessage{
		{Seq: 4, Kind: "msg", User: "dan", Text: "a", At: "2026-08-29T14:05:00Z"},
		{Seq: 5, Kind: "msg", User: "eve", Text: "b", At: "2026-08-29T14:06:00Z"},
	}
	with := chatContent(t, pageData{Repo: "alpha", ChatMsgs: msgs,
		ChatViewer: "dan", ChatPromote: true})
	for _, want := range []string{
		`class="chat-pick"`,
		`name="seq" value="4"`,
		`name="seq" value="5"`,
		`form="chat-promote"`,
		`id="chat-promote"`,
		`action="/r/alpha/chat/promote"`,
		`class="chat-mark chat-promote"`,
	} {
		if !strings.Contains(with, want) {
			t.Errorf("the promoter's page is missing %q", want)
		}
	}
	without := chatContent(t, pageData{Repo: "alpha", ChatMsgs: msgs, ChatViewer: "dan"})
	for _, gone := range []string{`class="chat-pick"`, `id="chat-promote"`} {
		if strings.Contains(without, gone) {
			t.Errorf("a viewer who cannot promote was offered %q", gone)
		}
	}
}

// A deleted message and a robot line are not conversation anybody can
// promote, so neither gets a box.
func TestChatSelectionSkipsWhatCannotBePromoted(t *testing.T) {
	body := chatContent(t, pageData{Repo: "alpha", ChatPromote: true, ChatViewer: "dan",
		ChatMsgs: []ChatMessage{
			{Seq: 1, Kind: "msg", User: "dan", Text: "keep", At: "2026-08-29T14:05:00Z"},
			{Seq: 2, Kind: "msg", User: "dan", Deleted: true, At: "2026-08-29T14:06:00Z"},
			{Seq: 3, Kind: "robot", User: "robot", Text: "check-in abc", At: "2026-08-29T14:07:00Z"},
		}})
	if !strings.Contains(body, `name="seq" value="1"`) {
		t.Error("a live message got no selection box")
	}
	for _, seq := range []string{"2", "3"} {
		if strings.Contains(body, `name="seq" value="`+seq+`"`) {
			t.Errorf("message %s was offered for promotion", seq)
		}
	}
}

// A robot line carries its own mark and takes no author hue: the row
// says "the channel said this", not "a person did".
func TestRobotLineIsMarkedAndUnhued(t *testing.T) {
	row := string(chatMsgHTML(ChatMessage{Seq: 9, Kind: "robot", User: "robot",
		Text: "promoted #4-#6", At: "2026-08-29T14:05:00Z"}, "alpha", "dan", false))
	if !strings.Contains(row, `data-hook="robot-mark"`) {
		t.Errorf("robot line has no mark:\n%s", row)
	}
	if strings.Contains(row, "--uh:") {
		t.Errorf("robot line took an author hue:\n%s", row)
	}
	person := string(chatMsgHTML(ChatMessage{Seq: 10, Kind: "msg", User: "dan",
		Text: "hi", At: "2026-08-29T14:05:00Z"}, "alpha", "dan", false))
	if strings.Contains(person, `data-hook="robot-mark"`) {
		t.Errorf("a person's line was marked as the robot's:\n%s", person)
	}
	if !strings.Contains(person, "--uh:") {
		t.Errorf("a person's line lost its hue:\n%s", person)
	}
}

// The checked boxes' outermost pair is the range, whatever order the
// browser sent them in and whatever it sent alongside them.
func TestChatPickedBoundsTheSelection(t *testing.T) {
	form := url.Values{"seq": {"12", "4", "9", "", "nonsense", "0"}, "title": {"x"}}
	req := httptest.NewRequest("POST", "/r/alpha/chat/promote",
		strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	from, to := chatPicked(req)
	if from != 4 || to != 12 {
		t.Fatalf("chatPicked = %d..%d, want 4..12", from, to)
	}
	empty := httptest.NewRequest("POST", "/r/alpha/chat/promote", strings.NewReader(""))
	empty.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if f, tt := chatPicked(empty); f != 0 || tt != 0 {
		t.Fatalf("an empty selection = %d..%d, want 0..0", f, tt)
	}
}