feat(04-02): i3bar widget with per-session colors, workspace prefix, wait type and duration
- One i3bar block per session (individual colors: red/green/gray) - Workspace prefix: 10-vmux[W], 3-auth[? 3m] - Wait type markers: ⚡ permission, ? question, ! generic - Wait duration: [⚡ 30s], [? 3m], [? 1h15m] - Standalone mode only (separate bar, no i3status wrapping) - Removed isTerminal and i3status wrapping code
This commit is contained in:
168
i3bar_test.go
168
i3bar_test.go
@@ -2,24 +2,51 @@ package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var i3barTestNow = time.Date(2026, 3, 24, 12, 0, 0, 0, time.UTC)
|
||||
|
||||
func TestFormatI3BarBlocks_MixedStates(t *testing.T) {
|
||||
waiting := i3barTestNow.Add(-3 * time.Minute)
|
||||
sessions := []SessionInfo{
|
||||
{Cwd: "/home/pierre/Code/auth", State: "Needs Input"},
|
||||
{Cwd: "/home/pierre/Code/portal", State: "Working"},
|
||||
{Cwd: "/home/pierre/Code/neia", State: "Idle"},
|
||||
{Cwd: "/home/pierre/Code/auth", Workspace: "3", State: "Needs Input", WaitType: "question", WaitingSince: &waiting},
|
||||
{Cwd: "/home/pierre/Code/portal", Workspace: "2", State: "Working"},
|
||||
{Cwd: "/home/pierre/Code/neia", Workspace: "5", State: "Idle"},
|
||||
}
|
||||
blocks := formatI3BarBlocks(sessions)
|
||||
if len(blocks) != 1 {
|
||||
t.Fatalf("expected 1 block, got %d", len(blocks))
|
||||
blocks := formatI3BarBlocks(sessions, i3barTestNow)
|
||||
if len(blocks) != 3 {
|
||||
t.Fatalf("expected 3 blocks, got %d", len(blocks))
|
||||
}
|
||||
want := "vmux: auth[!] portal[W] neia[I]"
|
||||
if blocks[0].FullText != want {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||
if blocks[0].FullText != "3-auth[? 3m]" || blocks[0].Color != "#ff0000" {
|
||||
t.Errorf("block 0: %q %q", blocks[0].FullText, blocks[0].Color)
|
||||
}
|
||||
if blocks[0].Color != "#ff0000" {
|
||||
t.Errorf("color = %q, want #ff0000", blocks[0].Color)
|
||||
if blocks[1].FullText != "2-portal[W]" || blocks[1].Color != "#00ff00" {
|
||||
t.Errorf("block 1: %q %q", blocks[1].FullText, blocks[1].Color)
|
||||
}
|
||||
if blocks[2].FullText != "5-neia[I]" || blocks[2].Color != "#888888" {
|
||||
t.Errorf("block 2: %q %q", blocks[2].FullText, blocks[2].Color)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatI3BarBlocks_PermissionMarker(t *testing.T) {
|
||||
waiting := i3barTestNow.Add(-30 * time.Second)
|
||||
sessions := []SessionInfo{
|
||||
{Cwd: "/a/proj", Workspace: "1", State: "Needs Input", WaitType: "permission", WaitingSince: &waiting},
|
||||
}
|
||||
blocks := formatI3BarBlocks(sessions, i3barTestNow)
|
||||
if blocks[0].FullText != "1-proj[⚡ 30s]" {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, "1-proj[⚡ 30s]")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatI3BarBlocks_NeedsInputNoWaitType(t *testing.T) {
|
||||
sessions := []SessionInfo{
|
||||
{Cwd: "/a/proj", State: "Needs Input"},
|
||||
}
|
||||
blocks := formatI3BarBlocks(sessions, i3barTestNow)
|
||||
if blocks[0].FullText != "proj[!]" {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, "proj[!]")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,48 +54,38 @@ func TestFormatI3BarBlocks_AllWorking(t *testing.T) {
|
||||
sessions := []SessionInfo{
|
||||
{Cwd: "/a/b/one", State: "Working"},
|
||||
{Cwd: "/a/b/two", State: "Working"},
|
||||
{Cwd: "/a/b/three", State: "Working"},
|
||||
}
|
||||
blocks := formatI3BarBlocks(sessions)
|
||||
want := "vmux: all working (3)"
|
||||
if blocks[0].FullText != want {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||
blocks := formatI3BarBlocks(sessions, i3barTestNow)
|
||||
if len(blocks) != 2 {
|
||||
t.Fatalf("expected 2 blocks, got %d", len(blocks))
|
||||
}
|
||||
if blocks[0].Color != "#00ff00" {
|
||||
t.Errorf("color = %q, want #00ff00", blocks[0].Color)
|
||||
if blocks[0].FullText != "one[W]" || blocks[0].Color != "#00ff00" {
|
||||
t.Errorf("block 0: %q %q", blocks[0].FullText, blocks[0].Color)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatI3BarBlocks_NoSessions(t *testing.T) {
|
||||
blocks := formatI3BarBlocks(nil)
|
||||
want := "vmux: no sessions"
|
||||
if blocks[0].FullText != want {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||
}
|
||||
if blocks[0].Color != "#00ff00" {
|
||||
t.Errorf("color = %q, want #00ff00", blocks[0].Color)
|
||||
blocks := formatI3BarBlocks(nil, i3barTestNow)
|
||||
if len(blocks) != 1 || blocks[0].FullText != "vmux: no sessions" {
|
||||
t.Errorf("full_text = %q", blocks[0].FullText)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatI3BarBlocks_ColorRed(t *testing.T) {
|
||||
func TestFormatI3BarBlocks_PerSessionColor(t *testing.T) {
|
||||
sessions := []SessionInfo{
|
||||
{Cwd: "/a/b/x", State: "Working"},
|
||||
{Cwd: "/a/b/y", State: "Needs Input"},
|
||||
{Cwd: "/a/x", State: "Working"},
|
||||
{Cwd: "/a/y", State: "Needs Input"},
|
||||
{Cwd: "/a/z", State: "Idle"},
|
||||
}
|
||||
blocks := formatI3BarBlocks(sessions)
|
||||
if blocks[0].Color != "#ff0000" {
|
||||
t.Errorf("color = %q, want #ff0000", blocks[0].Color)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatI3BarBlocks_ColorGreen(t *testing.T) {
|
||||
sessions := []SessionInfo{
|
||||
{Cwd: "/a/b/x", State: "Working"},
|
||||
{Cwd: "/a/b/y", State: "Idle"},
|
||||
}
|
||||
blocks := formatI3BarBlocks(sessions)
|
||||
blocks := formatI3BarBlocks(sessions, i3barTestNow)
|
||||
if blocks[0].Color != "#00ff00" {
|
||||
t.Errorf("color = %q, want #00ff00", blocks[0].Color)
|
||||
t.Errorf("Working color = %q, want #00ff00", blocks[0].Color)
|
||||
}
|
||||
if blocks[1].Color != "#ff0000" {
|
||||
t.Errorf("Needs Input color = %q, want #ff0000", blocks[1].Color)
|
||||
}
|
||||
if blocks[2].Color != "#888888" {
|
||||
t.Errorf("Idle color = %q, want #888888", blocks[2].Color)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,54 +93,61 @@ func TestFormatI3BarBlocks_UsesLabel(t *testing.T) {
|
||||
sessions := []SessionInfo{
|
||||
{Cwd: "/home/pierre/Code/auth-service", Label: "auth", State: "Working"},
|
||||
}
|
||||
blocks := formatI3BarBlocks(sessions)
|
||||
want := "vmux: all working (1)"
|
||||
if blocks[0].FullText != want {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||
blocks := formatI3BarBlocks(sessions, i3barTestNow)
|
||||
if blocks[0].FullText != "auth[W]" {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, "auth[W]")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatI3BarBlocks_UsesLabelInMixed(t *testing.T) {
|
||||
func TestFormatI3BarBlocks_WorkspacePrefix(t *testing.T) {
|
||||
sessions := []SessionInfo{
|
||||
{Cwd: "/home/pierre/Code/auth-service", Label: "auth", State: "Needs Input"},
|
||||
{Cwd: "/home/pierre/Code/vibe/vmux", Workspace: "10", State: "Working"},
|
||||
}
|
||||
blocks := formatI3BarBlocks(sessions)
|
||||
want := "vmux: auth[!]"
|
||||
if blocks[0].FullText != want {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||
blocks := formatI3BarBlocks(sessions, i3barTestNow)
|
||||
if blocks[0].FullText != "10-vmux[W]" {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, "10-vmux[W]")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatI3BarBlocks_UsesCwdBase(t *testing.T) {
|
||||
func TestFormatI3BarBlocks_LongWaitDuration(t *testing.T) {
|
||||
waiting := i3barTestNow.Add(-75 * time.Minute)
|
||||
sessions := []SessionInfo{
|
||||
{Cwd: "/home/pierre/Code/vibe/vmux", State: "Needs Input"},
|
||||
{Cwd: "/a/proj", Workspace: "2", State: "Needs Input", WaitType: "question", WaitingSince: &waiting},
|
||||
}
|
||||
blocks := formatI3BarBlocks(sessions)
|
||||
want := "vmux: vmux[!]"
|
||||
if blocks[0].FullText != want {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||
blocks := formatI3BarBlocks(sessions, i3barTestNow)
|
||||
if blocks[0].FullText != "2-proj[? 1h15m]" {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, "2-proj[? 1h15m]")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatI3BarBlocks_NeedsInputMarker(t *testing.T) {
|
||||
sessions := []SessionInfo{
|
||||
{Cwd: "/a/proj", State: "Needs Input"},
|
||||
func TestShortDuration(t *testing.T) {
|
||||
tests := []struct {
|
||||
d time.Duration
|
||||
want string
|
||||
}{
|
||||
{10 * time.Second, "10s"},
|
||||
{59 * time.Second, "59s"},
|
||||
{1 * time.Minute, "1m"},
|
||||
{5 * time.Minute, "5m"},
|
||||
{65 * time.Minute, "1h5m"},
|
||||
{120 * time.Minute, "2h0m"},
|
||||
}
|
||||
blocks := formatI3BarBlocks(sessions)
|
||||
want := "vmux: proj[!]"
|
||||
if blocks[0].FullText != want {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||
for _, tt := range tests {
|
||||
got := shortDuration(tt.d)
|
||||
if got != tt.want {
|
||||
t.Errorf("shortDuration(%v) = %q, want %q", tt.d, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatI3BarBlocks_IdleMarker(t *testing.T) {
|
||||
sessions := []SessionInfo{
|
||||
{Cwd: "/a/proj", State: "Idle"},
|
||||
{Cwd: "/a/other", State: "Needs Input"},
|
||||
func TestWaitTypeMarker(t *testing.T) {
|
||||
if waitTypeMarker("permission") != "⚡" {
|
||||
t.Error("permission should be ⚡")
|
||||
}
|
||||
blocks := formatI3BarBlocks(sessions)
|
||||
want := "vmux: proj[I] other[!]"
|
||||
if blocks[0].FullText != want {
|
||||
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||
if waitTypeMarker("question") != "?" {
|
||||
t.Error("question should be ?")
|
||||
}
|
||||
if waitTypeMarker("") != "!" {
|
||||
t.Error("empty should be !")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user