Something went wrong. Try again.
changelog generator & diff tool stormlightlabs.github.io/git-storm
changelog changeset markdown golang git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424package diff
import ( "strings" "testing"
"github.com/charmbracelet/lipgloss")
func TestSideBySideFormatter_Format(t *testing.T) { tests := []struct { name string edits []Edit width int expect func(string) bool }{ { name: "empty edits", edits: []Edit{}, width: 80, expect: func(output string) bool { return strings.Contains(output, "No changes") }, }, { name: "equal lines", edits: []Edit{ {Kind: Equal, AIndex: 0, BIndex: 0, Content: "hello world"}, }, width: 80, expect: func(output string) bool { return strings.Contains(output, "hello world") }, }, { name: "insert operation", edits: []Edit{ {Kind: Insert, AIndex: -1, BIndex: 0, Content: "new line"}, }, width: 80, expect: func(output string) bool { return strings.Contains(output, "new line") && strings.Contains(output, SymbolAdd) }, }, { name: "delete operation", edits: []Edit{ {Kind: Delete, AIndex: 0, BIndex: -1, Content: "old line"}, }, width: 80, expect: func(output string) bool { return strings.Contains(output, "old line") && strings.Contains(output, SymbolDeleteLine) }, }, { name: "mixed operations", edits: []Edit{ {Kind: Equal, AIndex: 0, BIndex: 0, Content: "unchanged"}, {Kind: Delete, AIndex: 1, BIndex: -1, Content: "removed"}, {Kind: Insert, AIndex: -1, BIndex: 1, Content: "added"}, {Kind: Equal, AIndex: 2, BIndex: 2, Content: "also unchanged"}, }, width: 100, expect: func(output string) bool { return strings.Contains(output, "unchanged") && strings.Contains(output, "removed") && strings.Contains(output, "added") }, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { formatter := &SideBySideFormatter{ TerminalWidth: tt.width, ShowLineNumbers: true, }
output := formatter.Format(tt.edits)
if !tt.expect(output) { t.Errorf("Format() output did not meet expectations.\nGot:\n%s", output) } }) }}
func TestSideBySideFormatter_CalculatePaneWidth(t *testing.T) { tests := []struct { name string terminalWidth int showLineNumbers bool minExpected int }{ { name: "standard width with line numbers", terminalWidth: 120, showLineNumbers: true, minExpected: 40, }, { name: "narrow terminal", terminalWidth: 60, showLineNumbers: true, minExpected: 20, }, { name: "without line numbers", terminalWidth: 100, showLineNumbers: false, minExpected: 40, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { formatter := &SideBySideFormatter{ TerminalWidth: tt.terminalWidth, ShowLineNumbers: tt.showLineNumbers, }
paneWidth := formatter.calculatePaneWidth()
if paneWidth < tt.minExpected { t.Errorf("calculatePaneWidth() = %d, expected at least %d", paneWidth, tt.minExpected) }
usedWidth := gutterWidth if tt.showLineNumbers { usedWidth += 2 * lineNumWidth } totalWidth := usedWidth + (2 * paneWidth) if totalWidth > tt.terminalWidth { t.Errorf("Total width %d exceeds terminal width %d (paneWidth=%d)", totalWidth, tt.terminalWidth, paneWidth) } }) }}
func TestPadToWidth(t *testing.T) { formatter := &SideBySideFormatter{}
tests := []struct { name string input string targetWidth int }{ { name: "short string gets padded", input: "hello", targetWidth: 10, }, { name: "exact width unchanged", input: "hello world", targetWidth: 11, }, { name: "long string gets truncated", input: "this is a very long string that exceeds the target width", targetWidth: 20, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := formatter.padToWidth(tt.input, tt.targetWidth)
resultWidth := lipgloss.Width(result) if resultWidth != tt.targetWidth { t.Errorf("padToWidth() width = %d, expected exactly %d", resultWidth, tt.targetWidth) } }) }}
func TestSideBySideFormatter_TruncateContent(t *testing.T) { formatter := &SideBySideFormatter{}
tests := []struct { name string content string maxWidth int expected string }{ { name: "short content", content: "hello", maxWidth: 10, expected: "hello", }, { name: "exact fit", content: "hello world", maxWidth: 11, expected: "hello world", }, { name: "needs truncation", content: "hello world this is a long line", maxWidth: 10, expected: "hello w...", }, { name: "very small width", content: "hello", maxWidth: 3, expected: "hel", }, { name: "trailing whitespace removed", content: "hello ", maxWidth: 10, expected: "hello", }, { name: "very long line", content: "github.com/charmbracelet/x/ansi v0.10.3 h1:3WoV9XN8uMEnFRZZ+vBPRy59TaI", maxWidth: 40, expected: "", }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := formatter.truncateContent(tt.content, tt.maxWidth)
displayWidth := lipgloss.Width(result) if displayWidth > tt.maxWidth { t.Errorf("truncateContent() display width = %d, exceeds max %d", displayWidth, tt.maxWidth) }
if tt.expected != "" && result != tt.expected { t.Errorf("truncateContent() = %q, expected %q", result, tt.expected) }
if lipgloss.Width(tt.content) > tt.maxWidth && tt.maxWidth > 3 { if !strings.HasSuffix(result, "...") { t.Errorf("truncateContent() should end with '...' for long content") } } }) }}
func TestSideBySideFormatter_RenderEdit(t *testing.T) { formatter := &SideBySideFormatter{ TerminalWidth: 100, ShowLineNumbers: true, } paneWidth := 40
tests := []struct { name string edit Edit expect func(left, right string) bool }{ { name: "equal edit shows on both sides", edit: Edit{Kind: Equal, AIndex: 0, BIndex: 0, Content: "same"}, expect: func(left, right string) bool { return strings.Contains(left, "same") && strings.Contains(right, "same") }, }, { name: "delete shows only on left", edit: Edit{Kind: Delete, AIndex: 0, BIndex: -1, Content: "removed"}, expect: func(left, right string) bool { return strings.Contains(left, "removed") && !strings.Contains(right, "removed") }, }, { name: "insert shows only on right", edit: Edit{Kind: Insert, AIndex: -1, BIndex: 0, Content: "added"}, expect: func(left, right string) bool { return !strings.Contains(left, "added") && strings.Contains(right, "added") }, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { left, right := formatter.renderEdit(tt.edit, paneWidth)
if !tt.expect(left, right) { t.Errorf("renderEdit() failed expectations.\nLeft: %q\nRight: %q", left, right) } }) }}
func TestUnifiedFormatter_Format(t *testing.T) { tests := []struct { name string edits []Edit width int expect func(string) bool }{ { name: "empty edits", edits: []Edit{}, width: 80, expect: func(output string) bool { return strings.Contains(output, "No changes") }, }, { name: "equal lines", edits: []Edit{ {Kind: Equal, AIndex: 0, BIndex: 0, Content: "hello world"}, }, width: 80, expect: func(output string) bool { return strings.Contains(output, " hello world") }, }, { name: "insert operation", edits: []Edit{ {Kind: Insert, AIndex: -1, BIndex: 0, Content: "new line"}, }, width: 80, expect: func(output string) bool { return strings.Contains(output, "+new line") }, }, { name: "delete operation", edits: []Edit{ {Kind: Delete, AIndex: 0, BIndex: -1, Content: "old line"}, }, width: 80, expect: func(output string) bool { return strings.Contains(output, "-old line") }, }, { name: "replace operation", edits: []Edit{ {Kind: Replace, AIndex: 0, BIndex: 0, Content: "old content", NewContent: "new content"}, }, width: 100, expect: func(output string) bool { return strings.Contains(output, "-old content") && strings.Contains(output, "+new content") }, }, { name: "mixed operations", edits: []Edit{ {Kind: Equal, AIndex: 0, BIndex: 0, Content: "unchanged"}, {Kind: Delete, AIndex: 1, BIndex: -1, Content: "removed"}, {Kind: Insert, AIndex: -1, BIndex: 1, Content: "added"}, {Kind: Equal, AIndex: 2, BIndex: 2, Content: "also unchanged"}, }, width: 100, expect: func(output string) bool { return strings.Contains(output, " unchanged") && strings.Contains(output, "-removed") && strings.Contains(output, "+added") && strings.Contains(output, " also unchanged") }, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { formatter := &UnifiedFormatter{ TerminalWidth: tt.width, ShowLineNumbers: true, }
output := formatter.Format(tt.edits)
if !tt.expect(output) { t.Errorf("Format() output did not meet expectations.\nGot:\n%s", output) } }) }}
func TestUnifiedFormatter_CalculateContentWidth(t *testing.T) { tests := []struct { name string terminalWidth int showLineNumbers bool minExpected int }{ { name: "standard width with line numbers", terminalWidth: 120, showLineNumbers: true, minExpected: 40, }, { name: "narrow terminal", terminalWidth: 60, showLineNumbers: true, minExpected: 40, }, { name: "without line numbers", terminalWidth: 100, showLineNumbers: false, minExpected: 40, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { formatter := &UnifiedFormatter{ TerminalWidth: tt.terminalWidth, ShowLineNumbers: tt.showLineNumbers, }
contentWidth := formatter.calculateContentWidth()
if contentWidth < tt.minExpected { t.Errorf("calculateContentWidth() = %d, expected at least %d", contentWidth, tt.minExpected) } }) }}