135 lines
4 KiB
Go
135 lines
4 KiB
Go
package chat
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ayn2op/arikawa/v3/discord"
|
|
"github.com/ayn2op/arikawa/v3/gateway"
|
|
"github.com/ayn2op/discordo/internal/config"
|
|
"github.com/ayn2op/discordo/internal/ui/chat/emojipicker"
|
|
"github.com/ayn2op/discordo/internal/ui/chat/messageslist"
|
|
"github.com/ayn2op/tview"
|
|
"github.com/gdamore/tcell/v3"
|
|
)
|
|
|
|
func newTestModel(t *testing.T) *Model {
|
|
t.Helper()
|
|
cfg, err := config.Load(filepath.Join(t.TempDir(), "config.toml"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m := NewModel(cfg, "")
|
|
return &m
|
|
}
|
|
|
|
func TestModelHandle(t *testing.T) {
|
|
t.Run("typing in the channels picker", func(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.openPicker()
|
|
area := tview.Rectangle{Width: 120, Height: 40}
|
|
// The picker's action passes through the panes below it, which must leave it alone.
|
|
msg := m.View().Handle(tcell.NewEventKey(tcell.KeyRune, "a", tcell.ModNone), area)
|
|
if msg == nil {
|
|
t.Fatal("typing produced no message")
|
|
}
|
|
if _, ok := msg.(paneMsg); ok {
|
|
t.Fatalf("the picker's action was marked for a pane: %v", msg)
|
|
}
|
|
*m, _ = m.Update(msg)
|
|
})
|
|
}
|
|
|
|
func TestModelUpdate(t *testing.T) {
|
|
t.Run("typing expires unless extended", func(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.addTyper(1)
|
|
m.addTyper(1)
|
|
*m, _ = m.Update(typingExpiredMsg{userID: 1})
|
|
if _, ok := m.typers[1]; !ok {
|
|
t.Fatal("an extended typing indicator expired early")
|
|
}
|
|
m.typers[1] = time.Now().Add(-time.Second)
|
|
*m, _ = m.Update(typingExpiredMsg{userID: 1})
|
|
if _, ok := m.typers[1]; ok {
|
|
t.Fatal("the typing indicator did not expire")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestModelAddMessageOrNotify(t *testing.T) {
|
|
for _, tt := range []struct {
|
|
name string
|
|
focused bool
|
|
whenUnfocused bool
|
|
want bool
|
|
}{
|
|
{"focused", true, true, false},
|
|
{"unfocused", false, true, true},
|
|
{"unfocused but disabled", false, false, false},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.cfg.Notifications.WhenUnfocused = tt.whenUnfocused
|
|
// Loading an avatar is a command too.
|
|
m.cfg.UI.MessagesList.Avatars.Preview = false
|
|
m.setSelectedChannel(&discord.Channel{ID: 1})
|
|
*m, _ = m.Update(tcell.NewEventFocus(tt.focused))
|
|
cmd := m.addMessageOrNotify(&gateway.MessageCreateEvent{ID: 1, ChannelID: 1})
|
|
if got := cmd != nil; got != tt.want {
|
|
t.Fatalf("notified = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestModelCycleFocus(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.setSelectedChannel(&discord.Channel{ID: 1, GuildID: 2, Type: discord.GuildText})
|
|
m.focused = paneGuildsTree
|
|
|
|
// The composer is disabled without permission to send messages, so it is skipped.
|
|
for _, want := range []pane{paneMessagesList, paneMembersTree, paneGuildsTree} {
|
|
if m.cycleFocus(1); m.focused != want {
|
|
t.Fatalf("focused = %v, want %v", m.focused, want)
|
|
}
|
|
}
|
|
for _, want := range []pane{paneMembersTree, paneMessagesList, paneGuildsTree} {
|
|
if m.cycleFocus(-1); m.focused != want {
|
|
t.Fatalf("focused = %v, want %v", m.focused, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestModelReactions(t *testing.T) {
|
|
m := newTestModel(t)
|
|
channel := discord.Channel{ID: 1, GuildID: 2, Type: discord.GuildText}
|
|
m.setSelectedChannel(&channel)
|
|
|
|
msg := discord.Message{ID: 10, ChannelID: 1}
|
|
*m, _ = m.Update(messageslist.ReactMsg{Message: msg})
|
|
if m.overlay != overlayEmojiPicker {
|
|
t.Fatalf("overlay = %v, want overlayEmojiPicker", m.overlay)
|
|
}
|
|
if m.reactingMessage == nil || m.reactingMessage.ID != 10 {
|
|
t.Fatalf("reactingMessage = %v, want ID 10", m.reactingMessage)
|
|
}
|
|
|
|
// Cancel closes overlay
|
|
*m, _ = m.Update(emojipicker.CancelMsg{})
|
|
if m.overlay != overlayNone {
|
|
t.Fatalf("overlay after cancel = %v, want overlayNone", m.overlay)
|
|
}
|
|
|
|
// Select triggers react command and closes overlay
|
|
*m, _ = m.Update(messageslist.ReactMsg{Message: msg})
|
|
var cmd tview.Cmd
|
|
*m, cmd = m.Update(emojipicker.SelectedMsg{Emoji: discord.APIEmoji("😄")})
|
|
if m.overlay != overlayNone {
|
|
t.Fatalf("overlay after select = %v, want overlayNone", m.overlay)
|
|
}
|
|
if cmd == nil {
|
|
t.Fatal("expected non-nil command for reaction action")
|
|
}
|
|
}
|