Skip to content
quarry Artifact [45452fa7d8]
Login

Artifact [45452fa7d8]

Artifact 45452fa7d873801f4f01c4fca23b5f9a0af0b900c58031ee986474dea2d97340:


package web

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func bridgeSection(t *testing.T, p *chatBridgePanel) string {
	t.Helper()
	var b strings.Builder
	d := pageData{Repo: "alpha", RepoAdmin: true, ChatBridge: p, Roster: &rosterPanel{Rows: []rosterRow{
		{Name: "dan", Caps: "a", Role: "Admin", Chats: true},
		{Name: "eve", Caps: "o", Role: "Reader"},
	}}}
	if err := noHooks.template(settingsPage).ExecuteTemplate(&b, "content", d); err != nil {
		t.Fatal(err)
	}
	s := b.String()
	i := strings.Index(s, `data-hook="chat-bridge"`)
	if i < 0 {
		t.Fatal("FAIL: no chat bridge section")
	}
	s = s[i:]
	return s[:strings.Index(s, "</section>")]
}

// Open to anyone signed in, the section offers the close first, lists
// the category among the readers, and offers no bridge it would refuse.
func TestAnOpenChannelOffersTheCloseNotTheBridge(t *testing.T) {
	s := bridgeSection(t, &chatBridgePanel{OpenTo: []string{"anonymous"}})
	if !strings.Contains(s, `data-hook="chat-bridge-open"`) || !strings.Contains(s, `data-hook="chat-bridge-close"`) {
		t.Error("FAIL: an open channel does not say so and offer to close it")
	}
	if !strings.Contains(s, `<fieldset disabled>`) {
		t.Error("FAIL: the bridge form is usable on an open channel")
	}
	if !strings.Contains(s, `href="/r/alpha/people/dan"`) || strings.Contains(s, `href="/r/alpha/people/eve"`) {
		t.Error("FAIL: the readers are not exactly the people holding chat")
	}
}

// Closed to named people, the form is live; bridged, it gives way to
// the bridge's state and Detach; stopped, the section says so.
func TestTheBridgeSectionFollowsTheBridge(t *testing.T) {
	ready := bridgeSection(t, &chatBridgePanel{})
	if !strings.Contains(ready, `data-hook="chat-bridge-form"`) || strings.Contains(ready, "disabled") ||
		strings.Contains(ready, `data-hook="chat-bridge-close"`) {
		t.Error("FAIL: a closed channel does not offer a live bridge form, or offers to close it")
	}
	on := &chatBridgePanel{Bridged: true, State: "connected"}
	on.Bridge.URL, on.Bridge.User, on.Bridge.At = "https://fossil.example/r", "bot", "2026-10-26T10:00:00Z"
	s := bridgeSection(t, on)
	if !strings.Contains(s, `data-hook="chat-bridge-state"`) || !strings.Contains(s, `value="detach"`) ||
		strings.Contains(s, `data-hook="chat-bridge-form"`) {
		t.Error("FAIL: a bridged channel does not show its state and Detach in place of the form")
	}
	stop := &chatBridgePanel{Bridged: true, State: "stopped", OpenTo: []string{"anonymous"}}
	stop.Bridge.Stopped = "2026-10-26T10:00:00Z"
	s = bridgeSection(t, stop)
	if !strings.Contains(s, `data-hook="chat-bridge-stopped"`) || !strings.Contains(s, `data-hook="chat-bridge-close"`) {
		t.Error("FAIL: a stopped bridge does not say so and offer to close the channel")
	}
}

// The section in each state, as whole settings pages for screenshots.
// Off unless QUARRY_SHOT_BRIDGE names a directory.
func TestWriteChatBridgeShots(t *testing.T) {
	dir := os.Getenv("QUARRY_SHOT_BRIDGE")
	if dir == "" {
		t.Skip("QUARRY_SHOT_BRIDGE unset")
	}
	if err := os.WriteFile(filepath.Join(dir, "quarry.css"), []byte(sheet), 0o644); err != nil {
		t.Fatal(err)
	}
	roster := &rosterPanel{Rows: []rosterRow{
		{Name: "dmestas", Caps: "a", Role: "Admin", Chats: true},
		{Name: "lou", Caps: "x", Role: "Maintainer", Chats: true},
		{Name: "calionauta", Caps: "i", Role: "Developer", Chats: true},
		{Name: "mia", Team: "craft-design-group", Chats: true},
	}}
	on := &chatBridgePanel{Bridged: true, State: "connected"}
	on.Bridge.URL, on.Bridge.User, on.Bridge.At = "https://fossil.craftdesign.group/zit", "zeitforge-bridge", "2026-10-26T09:56:00Z"
	stop := &chatBridgePanel{Bridged: true, State: "stopped", OpenTo: []string{"anonymous"}}
	stop.Bridge.URL, stop.Bridge.User, stop.Bridge.Stopped = on.Bridge.URL, on.Bridge.User, "2026-10-26T09:02:00Z"
	for name, p := range map[string]*chatBridgePanel{
		"open": {OpenTo: []string{"anonymous"}}, "ready": {}, "connected": on, "stopped": stop,
	} {
		var b strings.Builder
		d := pageData{Title: "Settings", HideH1: true, Repo: "quarry", RepoAdmin: true, SheetURL: "quarry.css",
			ChatBridge: p, Roster: roster}
		if err := noHooks.template(settingsPage).Execute(&b, d); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(filepath.Join(dir, name+".html"), []byte(b.String()), 0o644); err != nil {
			t.Fatal(err)
		}
	}
}