discordo/internal/ui/chat/memberstree/model_test.go

157 lines
4.7 KiB
Go

package memberstree
import (
"encoding/json"
"path/filepath"
"slices"
"testing"
"github.com/ayn2op/arikawa/v3/discord"
"github.com/ayn2op/arikawa/v3/gateway"
"github.com/ayn2op/arikawa/v3/state"
"github.com/ayn2op/discordo/internal/config"
"github.com/ayn2op/ningen/v3"
"github.com/ayn2op/tview/tree"
)
const guildID, channelID = 1, 2
func newTestModel(t *testing.T) Model {
t.Helper()
cfg, err := config.Load(filepath.Join(t.TempDir(), "config.toml"))
if err != nil {
t.Fatal(err)
}
return NewModel(cfg, ningen.FromState(state.New("")))
}
// setGuildMembers stores a guild channel whose member list holds items, given as JSON.
func setGuildMembers(t *testing.T, m Model, items string) *discord.Channel {
t.Helper()
channel := &discord.Channel{ID: channelID, GuildID: guildID, Type: discord.GuildText}
if err := m.state.ChannelSet(channel, false); err != nil {
t.Fatal(err)
}
if err := m.state.RoleSet(guildID, &discord.Role{ID: guildID, Permissions: discord.PermissionViewChannel}, false); err != nil {
t.Fatal(err)
}
if err := m.state.RoleSet(guildID, &discord.Role{ID: 3, Name: "Mods"}, false); err != nil {
t.Fatal(err)
}
event := &gateway.GuildMemberListUpdateEvent{ID: "everyone", GuildID: guildID}
event.Ops = []gateway.GuildMemberListOp{{Op: "SYNC", Range: [2]int{0, 99}}}
if err := json.Unmarshal([]byte(items), &event.Ops[0].Items); err != nil {
t.Fatal(err)
}
m.state.State.Call(event)
return channel
}
// lines returns the text of the groups and, indented, their members.
func lines(m Model) []string {
var lines []string
for _, group := range m.root.Children() {
lines = append(lines, nodeText(group))
for _, member := range group.Children() {
lines = append(lines, " "+nodeText(member))
}
}
return lines
}
func nodeText(node *tree.Node) string {
var s string
for _, span := range node.Spans() {
s += span.Text
}
return s
}
func TestModelShown(t *testing.T) {
for _, tt := range []struct {
name string
channel *discord.Channel
want bool
}{
{"none", nil, false},
{"guild channel", &discord.Channel{GuildID: guildID, Type: discord.GuildText}, true},
{"thread", &discord.Channel{GuildID: guildID, Type: discord.GuildPublicThread}, false},
{"group dm", &discord.Channel{Type: discord.GroupDM}, true},
{"dm", &discord.Channel{Type: discord.DirectMessage}, false},
} {
t.Run(tt.name, func(t *testing.T) {
m := newTestModel(t)
m.channel = tt.channel
if got := m.Shown(); got != tt.want {
t.Fatalf("Shown() = %v, want %v", got, tt.want)
}
})
}
}
func TestModelRebuild(t *testing.T) {
t.Run("guild members", func(t *testing.T) {
m := newTestModel(t)
m.channel = setGuildMembers(t, m, `[
{"group": {"id": "3", "count": 1}},
{"member": {"user": {"id": "10", "username": "a"}, "nick": "Alice"}},
{"group": {"id": "online", "count": 2}},
{"member": {"user": {"id": "11", "username": "b"}}},
{},
{"member": {"user": {"id": "12", "username": "c"}}}
]`)
m.rebuild()
// The member after the unloaded item belongs to a group that has yet to arrive.
want := []string{"Mods — 1", " ● Alice", "Online — 2", " ● b", "Members", " ● c"}
if got := lines(m); !slices.Equal(got, want) {
t.Fatalf("lines = %q, want %q", got, want)
}
})
t.Run("group dm recipients", func(t *testing.T) {
m := newTestModel(t)
if err := m.state.MyselfSet(discord.User{ID: 13, Username: "me"}, false); err != nil {
t.Fatal(err)
}
if err := m.state.PresenceSet(discord.NullGuildID, &discord.Presence{User: discord.User{ID: 11}, Status: discord.OnlineStatus}, false); err != nil {
t.Fatal(err)
}
m.channel = &discord.Channel{Type: discord.GroupDM, DMRecipients: []discord.User{
{ID: 10, Username: "a"},
{ID: 11, Username: "c"},
{ID: 12, Username: "B"},
}}
m.rebuild()
// Online members come first, then members by name, including the current user.
want := []string{"Members — 4", " ● c", " ● a", " ● B", " ● me"}
if got := lines(m); !slices.Equal(got, want) {
t.Fatalf("lines = %q, want %q", got, want)
}
})
t.Run("keeps selection and collapsed groups", func(t *testing.T) {
m := newTestModel(t)
m.channel = setGuildMembers(t, m, `[
{"group": {"id": "online", "count": 1}},
{"member": {"user": {"id": "10", "username": "a"}}},
{"group": {"id": "offline", "count": 1}},
{"member": {"user": {"id": "11", "username": "b"}}}
]`)
m.rebuild()
online, offline := m.root.Children()[0], m.root.Children()[1]
selected := online.Children()[0]
m.current = selected
offline.Collapse()
m.rebuild()
if got := m.current; got != selected || m.root.PathTo(got) == nil {
t.Fatal("the selected member was not kept in the tree")
}
if m.root.Children()[1].Expanded() {
t.Fatal("the collapsed group was expanded")
}
})
}