discordo/internal/ui/chat/composer_test.go

111 lines
3.6 KiB
Go

package chat
import (
"maps"
"path/filepath"
"slices"
"testing"
"github.com/ayn2op/arikawa/v3/discord"
"github.com/ayn2op/arikawa/v3/gateway"
"github.com/ayn2op/arikawa/v3/utils/sendpart"
"github.com/ayn2op/discordo/internal/config"
"github.com/gdamore/tcell/v3"
)
func TestComposerEditLastMessage(t *testing.T) {
for _, scenario := range []string{"latest", "old", "draft", "multiline", "reply", "attachment", "editing", "disabled", "no own message", "remapped", "unbound"} {
t.Run(scenario, func(t *testing.T) {
cfg, err := config.Load(filepath.Join(t.TempDir(), "config.toml"))
if err != nil {
t.Fatal(err)
}
m := NewModel(cfg, "")
if err := m.state.Cabinet.MyselfSet(discord.User{ID: 1}, false); err != nil {
t.Fatal(err)
}
m.SetSelectedChannel(&discord.Channel{ID: 1})
c := m.composer
c.SetDisabled(false)
messages := []discord.Message{
{ID: 1, ChannelID: 1, Author: discord.User{ID: 1}, Content: "old message"},
{ID: 2, ChannelID: 1, Author: discord.User{ID: 1}, Content: "latest message"},
{ID: 3, ChannelID: 1, Author: discord.User{ID: 2}, Content: "someone else"},
{ID: 4, ChannelID: 1, Author: discord.User{ID: 1}, Type: discord.ChannelPinnedMessage, Content: "system"},
{ID: 5, ChannelID: 1, Author: discord.User{ID: 1}},
}
want := ""
key := tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone)
switch scenario {
case "latest":
want = "latest message"
case "old":
messages = messages[:1]
want = "old message"
case "draft":
c.SetText("draft", true)
case "multiline":
c.SetText("first line\nsecond line", true)
case "reply":
c.sendMessageData.Reference = &discord.MessageReference{MessageID: 3}
case "attachment":
c.sendMessageData.Files = []sendpart.File{{Name: "file"}}
case "editing":
c.edit = true
case "disabled":
c.SetDisabled(true)
case "no own message":
messages = messages[2:3]
case "remapped":
cfg.Keybinds.Composer.EditLast.SetKeys("ctrl+p")
key = tcell.NewEventKey(tcell.KeyCtrlP, "", tcell.ModNone)
want = "latest message"
case "unbound":
cfg.Keybinds.Composer.EditLast.SetKeys()
}
slices.Reverse(messages)
m.messagesList.setMessages(messages)
before := c.Text()
wasEditing := c.edit
c.Update(key)
if want != "" {
selected, ok := m.messagesList.selectedMessage()
if !c.edit || c.Text() != want || !ok || selected.Content != want {
t.Fatalf("edit = %v, text = %q, selected = %v", c.edit, c.Text(), selected)
}
} else if c.Text() != before || c.edit != wasEditing {
t.Fatalf("composer changed: edit = %v, text = %q", c.edit, c.Text())
}
})
}
}
func TestComposerSearchMember(t *testing.T) {
cfg, err := config.Load(filepath.Join(t.TempDir(), "config.toml"))
if err != nil {
t.Fatal(err)
}
m := NewModel(cfg, "")
m.state.MemberState.SearchLimit = 2
c := m.composer
c.onGuildMembersChunk(&gateway.GuildMembersChunkEvent{
Nonce: memberSearchNonce + "1 ab",
Members: []discord.Member{{}},
})
if cmd := c.searchMember(1, "ab"); cmd != nil {
t.Fatal("cached query returned a network command")
}
if cmd := c.searchMember(1, "abc"); cmd != nil || c.memberSearchCache["1 abc"] != 1 {
t.Fatal("incomplete prefix results were not reused")
}
c.memberSearchCache["1 a"] = 2
c.memberSearchCache["2 a"] = 2
m.onGuildMemberRemove(&gateway.GuildMemberRemoveEvent{
GuildID: 1,
User: discord.User{Username: "abc"},
})
want := map[string]uint{"1 ab": 1, "1 abc": 1, "2 a": 2}
if !maps.Equal(c.memberSearchCache, want) {
t.Fatalf("cache after invalidation = %v, want %v", c.memberSearchCache, want)
}
}