package model import ( "strings" "testing" "github.com/bjarneo/cliamp/playlist" ) func TestTerminalTitleValuesForTrack(t *testing.T) { t.Run("stream title is parsed into logical song fields", func(t *testing.T) { values := terminalTitleValuesForTrack( playlist.Track{Stream: true, Path: "https://radio.example.test/stream"}, "Artist - Song", true, false, ) if values.stateIcon != "▶" { t.Fatalf("stateIcon = %q, want ▶", values.stateIcon) } if values.title != "Song" || values.artist != "Artist" { t.Fatalf("parsed values = title %q artist %q", values.title, values.artist) } if values.metadata != "Song - Artist" { t.Fatalf("metadata = %q, want %q", values.metadata, "Song - Artist") } if values.streamTitle != "Artist - Song" { t.Fatalf("streamTitle = %q, want raw value", values.streamTitle) } }) t.Run("non parsable stream title falls back to raw title", func(t *testing.T) { values := terminalTitleValuesForTrack( playlist.Track{Stream: true}, "NTS Live", true, false, ) if values.title != "NTS Live" || values.artist != "" { t.Fatalf("values = title %q artist %q", values.title, values.artist) } if values.metadata != "NTS Live" { t.Fatalf("metadata = %q, want %q", values.metadata, "NTS Live") } }) t.Run("stopped clears track metadata", func(t *testing.T) { values := terminalTitleValuesForTrack( playlist.Track{Title: "Angel", Artist: "Massive Attack", Path: "/music/angel.flac"}, "", false, false, ) if values.stateIcon != "" { t.Fatalf("stateIcon = %q, want empty for stopped", values.stateIcon) } if values.metadata != "" || values.title != "" || values.artist != "" || values.path != "" { t.Fatalf("stopped values should be empty, got %+v", values) } }) } func TestRenderTerminalTitle(t *testing.T) { track := playlist.Track{Title: "Angel", Artist: "Massive Attack"} t.Run("playing", func(t *testing.T) { got := renderTerminalTitle(terminalTitleValuesForTrack(track, "", true, false)) want := "▶ Angel - Massive Attack | cliamp" if got != want { t.Fatalf("render(playing) = %q, want %q", got, want) } }) t.Run("paused", func(t *testing.T) { got := renderTerminalTitle(terminalTitleValuesForTrack(track, "", true, true)) want := "⏸ Angel - Massive Attack | cliamp" if got != want { t.Fatalf("render(paused) = %q, want %q", got, want) } }) t.Run("stopped", func(t *testing.T) { got := renderTerminalTitle(terminalTitleValuesForTrack(track, "", false, false)) if got != baseTerminalTitle { t.Fatalf("render(stopped) = %q, want %q", got, baseTerminalTitle) } }) } func TestTerminalTitleIntroSequence(t *testing.T) { state := initialTerminalTitleState() frames := []string{currentTerminalTitle(state, 0, terminalTitleStateValues(false, false))} for state.introActive { advanceTerminalTitleState(&state, 0) title := currentTerminalTitle(state, 0, terminalTitleStateValues(false, false)) if title != frames[len(frames)-1] { frames = append(frames, title) } } if got, want := frames[0], strings.Repeat(" ", titleIntroViewportDefault-4)+"It r"; got != want { t.Fatalf("first intro frame = %q, want %q", got, want) } if got, wantSuffix := frames[1], "It rea"; !strings.HasSuffix(got, wantSuffix) { t.Fatalf("second intro frame = %q, want suffix %q", got, wantSuffix) } if got, want := frames[len(frames)-2], strings.Repeat(" ", titleIntroViewportDefault); got != want { t.Fatalf("last intro frame = %q, want %q", got, want) } if got := frames[len(frames)-1]; got != baseTerminalTitle { t.Fatalf("post-intro title = %q, want %q", got, baseTerminalTitle) } } func TestCurrentTerminalTitleSanitizesRenderedTitle(t *testing.T) { tests := []struct { name string values terminalTitleValues want string }{ { name: "drops control bytes", values: terminalTitleValues{ stateIcon: "▶", metadata: "Song\a\x1b[31m - Artist\r\nName", }, want: "▶ Song[31m - Artist Name | cliamp", }, { name: "collapses control whitespace", values: terminalTitleValues{ metadata: "Song\r\n\tArtist", }, want: "Song Artist | cliamp", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := currentTerminalTitle(terminalTitleState{}, 0, tt.values); got != tt.want { t.Fatalf("currentTerminalTitle() = %q, want %q", got, tt.want) } }) } } func TestTitleIntroViewportForWidth(t *testing.T) { tests := []struct { width int introLen int want int }{ {width: 0, introLen: len(defaultTerminalTitleIntroRunes), want: titleIntroViewportDefault}, {width: 40, introLen: len(defaultTerminalTitleIntroRunes), want: titleIntroViewportMin}, {width: 80, introLen: len(defaultTerminalTitleIntroRunes), want: 26}, {width: 160, introLen: len(defaultTerminalTitleIntroRunes), want: len(defaultTerminalTitleIntroRunes)}, } for _, tt := range tests { if got := titleIntroViewportForWidth(tt.width, tt.introLen); got != tt.want { t.Fatalf("titleIntroViewportForWidth(%d, %d) = %d, want %d", tt.width, tt.introLen, got, tt.want) } } }