// SPDX-License-Identifier: AGPL-3.0-or-later package web import ( "context" "fmt" "os" "regexp" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "bnema.dev/ermite/internal/domain" ) // hunkOf is a modified file with one hunk of the lines given as "+text", // "-text" and " text". func hunkOf(name string, lines ...string) domain.FileDiff { var ls []domain.Line o, n := 0, 0 for _, s := range lines { switch s[0] { case '+': n++ ls = append(ls, line(domain.LineAddition, 0, n, s[1:])) case '-': o++ ls = append(ls, line(domain.LineDeletion, o, 0, s[1:])) default: o++ n++ ls = append(ls, line(domain.LineContext, o, n, s[1:])) } } return domain.FileDiff{ Status: domain.FileModified, OldPath: name, NewPath: name, Hunks: []domain.Hunk{{OldStart: 1, OldLines: o, NewStart: 1, NewLines: n, Lines: ls}}, } } // rendered is the HTML of the code cell of every line of the first hunk. func rendered(t *testing.T, f DiffFile) []string { t.Helper() var out []string for _, l := range f.Hunks[0].Lines { var b strings.Builder require.NoError(t, codeLine(l).Render(context.Background(), &b)) out = append(out, b.String()) } return out } // uncachedFiles is the files of a diff coloured with no cache: every call // lexes. func uncachedFiles(ctx context.Context, files []domain.FileDiff) []DiffFile { return (*hlCache)(nil).files(ctx, files) } func highlighted(t *testing.T, fd domain.FileDiff) DiffFile { t.Helper() return uncachedFiles(context.Background(), []domain.FileDiff{fd})[0] } // A block comment that spans lines is a comment on each of them: the hunk is // lexed as a sequence, not line by line. func TestHighlightMultiLineComment(t *testing.T) { t.Parallel() f := highlighted(t, hunkOf("a.go", " package a", "+/* first", "+ second */", "+var x = 1", )) got := rendered(t, f) assert.Equal(t, `package a`, got[0]) assert.Equal(t, `/* first`, got[1]) assert.Equal(t, ` second <b> */`, got[2]) assert.Contains(t, got[3], `var`) } // Each side is lexed on its own: a deleted line has the colours it had in // the old file, whatever the added lines around it open. func TestHighlightSidesApart(t *testing.T) { t.Parallel() f := highlighted(t, hunkOf("a.go", "-x := 1 // old", "-y := 2", "+/* new", "+y := 2 */", )) got := rendered(t, f) assert.NotContains(t, got[1], `class="cm"`, "the old side has no comment open") assert.Equal(t, `/* new`, got[2]) assert.Equal(t, `y := 2 */`, got[3]) assert.Contains(t, got[0], `// old`) } // Whatever is coloured, the text of the line is untouched and escaped, // invisible characters are markers, and the CR of a CRLF line is dropped. func TestHighlightKeepsText(t *testing.T) { t.Parallel() texts := []string{ `s := "" // & "quoted"`, "if x { // \u202eadmin \u200b", "\tfunc() {}\r", "", `x := '<' + "é世"`, } var in []string for _, s := range texts { in = append(in, "+"+s) } f := highlighted(t, hunkOf("a.go", in...)) plain := diffFiles([]domain.FileDiff{hunkOf("a.go", in...)})[0] for i, l := range f.Hunks[0].Lines { var got, want strings.Builder for _, s := range l.Segs { got.WriteString(s.Text) } for _, s := range plain.Hunks[0].Lines[i].Segs { want.WriteString(s.Text) } assert.Equal(t, want.String(), got.String(), "line %d", i) } for i, h := range rendered(t, f) { assert.NotContains(t, h, "