test(04-02): add i3bar formatting with TDD
- I3BarBlock struct for i3bar protocol JSON - formatI3BarBlocks: compact format D-06/D-07/D-08 - 10 tests covering mixed states, colors, labels, markers Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
52
i3bar.go
Normal file
52
i3bar.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// I3BarBlock represents a single block in the i3bar protocol.
|
||||||
|
type I3BarBlock struct {
|
||||||
|
FullText string `json:"full_text"`
|
||||||
|
ShortText string `json:"short_text,omitempty"`
|
||||||
|
Color string `json:"color"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Markup string `json:"markup,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatI3BarBlocks builds the vmux status block for i3bar from session data.
|
||||||
|
func formatI3BarBlocks(sessions []SessionInfo) []I3BarBlock {
|
||||||
|
if len(sessions) == 0 {
|
||||||
|
return []I3BarBlock{{FullText: "vmux: no sessions", Color: "#00ff00", Name: "vmux"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasWaiting := false
|
||||||
|
parts := make([]string, 0, len(sessions))
|
||||||
|
|
||||||
|
for _, s := range sessions {
|
||||||
|
name := shortName(s)
|
||||||
|
switch s.State {
|
||||||
|
case "Needs Input":
|
||||||
|
parts = append(parts, name+"[!]")
|
||||||
|
hasWaiting = true
|
||||||
|
case "Working":
|
||||||
|
parts = append(parts, name+"[W]")
|
||||||
|
case "Idle":
|
||||||
|
parts = append(parts, name+"[I]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var text string
|
||||||
|
if !hasWaiting {
|
||||||
|
text = fmt.Sprintf("vmux: all working (%d)", len(sessions))
|
||||||
|
} else {
|
||||||
|
text = "vmux: " + strings.Join(parts, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
color := "#00ff00"
|
||||||
|
if hasWaiting {
|
||||||
|
color = "#ff0000"
|
||||||
|
}
|
||||||
|
|
||||||
|
return []I3BarBlock{{FullText: text, Color: color, Name: "vmux"}}
|
||||||
|
}
|
||||||
129
i3bar_test.go
Normal file
129
i3bar_test.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFormatI3BarBlocks_MixedStates(t *testing.T) {
|
||||||
|
sessions := []SessionInfo{
|
||||||
|
{Cwd: "/home/pierre/Code/auth", State: "Needs Input"},
|
||||||
|
{Cwd: "/home/pierre/Code/portal", State: "Working"},
|
||||||
|
{Cwd: "/home/pierre/Code/neia", State: "Idle"},
|
||||||
|
}
|
||||||
|
blocks := formatI3BarBlocks(sessions)
|
||||||
|
if len(blocks) != 1 {
|
||||||
|
t.Fatalf("expected 1 block, 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].Color != "#ff0000" {
|
||||||
|
t.Errorf("color = %q, want #ff0000", blocks[0].Color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
if blocks[0].Color != "#00ff00" {
|
||||||
|
t.Errorf("color = %q, want #00ff00", 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatI3BarBlocks_ColorRed(t *testing.T) {
|
||||||
|
sessions := []SessionInfo{
|
||||||
|
{Cwd: "/a/b/x", State: "Working"},
|
||||||
|
{Cwd: "/a/b/y", State: "Needs Input"},
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
if blocks[0].Color != "#00ff00" {
|
||||||
|
t.Errorf("color = %q, want #00ff00", blocks[0].Color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatI3BarBlocks_UsesLabelInMixed(t *testing.T) {
|
||||||
|
sessions := []SessionInfo{
|
||||||
|
{Cwd: "/home/pierre/Code/auth-service", Label: "auth", State: "Needs Input"},
|
||||||
|
}
|
||||||
|
blocks := formatI3BarBlocks(sessions)
|
||||||
|
want := "vmux: auth[!]"
|
||||||
|
if blocks[0].FullText != want {
|
||||||
|
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatI3BarBlocks_UsesCwdBase(t *testing.T) {
|
||||||
|
sessions := []SessionInfo{
|
||||||
|
{Cwd: "/home/pierre/Code/vibe/vmux", State: "Needs Input"},
|
||||||
|
}
|
||||||
|
blocks := formatI3BarBlocks(sessions)
|
||||||
|
want := "vmux: vmux[!]"
|
||||||
|
if blocks[0].FullText != want {
|
||||||
|
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatI3BarBlocks_NeedsInputMarker(t *testing.T) {
|
||||||
|
sessions := []SessionInfo{
|
||||||
|
{Cwd: "/a/proj", State: "Needs Input"},
|
||||||
|
}
|
||||||
|
blocks := formatI3BarBlocks(sessions)
|
||||||
|
want := "vmux: proj[!]"
|
||||||
|
if blocks[0].FullText != want {
|
||||||
|
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatI3BarBlocks_IdleMarker(t *testing.T) {
|
||||||
|
sessions := []SessionInfo{
|
||||||
|
{Cwd: "/a/proj", State: "Idle"},
|
||||||
|
{Cwd: "/a/other", State: "Needs Input"},
|
||||||
|
}
|
||||||
|
blocks := formatI3BarBlocks(sessions)
|
||||||
|
want := "vmux: proj[I] other[!]"
|
||||||
|
if blocks[0].FullText != want {
|
||||||
|
t.Errorf("full_text = %q, want %q", blocks[0].FullText, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user