mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-09 18:08:31 -10:00
* Polish transcript rendering Tighten transcript spacing, collapse noisy permission/tool rows, and render tool activity with cleaner action-oriented labels. Improve code and diff rendering with markdown highlighting, cleaner command output blocks, and grouped exploration summaries. Tested: env GOCACHE=/tmp/zero-go-cache go test ./internal/tui Tested: git diff --check * Fix grep exploration rendering on Windows Keep search query text out of path normalization so regex escapes render consistently across platforms. Tested: env GOCACHE=/tmp/zero-go-cache go test ./internal/tui Tested: git diff --check * Address transcript rendering review Cache highlighted streaming markdown so closed code blocks are not re-lexed on every live render. Tighten bare-code detection to avoid highlighting ordinary prose, restore manual permission audit rows, and make exploration details expandable. Also align diff content/header predicates and use display-width truncation for compact tool output. Tested: env GOCACHE=/tmp/zero-go-cache go test ./internal/tui Tested: env GOCACHE=/tmp/zero-go-cache go test -race ./internal/tui Tested: env GOCACHE=/tmp/zero-go-cache go vet ./... Tested: git diff --check
120 lines
3.6 KiB
Go
120 lines
3.6 KiB
Go
package tui
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func feedChunks(d *streamingDecoder, chunks ...string) {
|
|
for _, c := range chunks {
|
|
d.feed(c)
|
|
}
|
|
}
|
|
|
|
func TestStreamingDecoderPathTailAndCount(t *testing.T) {
|
|
d := newStreamingDecoder()
|
|
// kimi-style spacing; content fed in awkward chunks.
|
|
feedChunks(d, `{"path": "a/b.go",`, ` "content": "package main\n`, `\nfunc main() {`, `\n\t}`, `"}`)
|
|
if d.path != "a/b.go" {
|
|
t.Errorf("path = %q, want a/b.go", d.path)
|
|
}
|
|
if !d.hasContent() {
|
|
t.Fatal("expected content")
|
|
}
|
|
want := []string{"package main", "", "func main() {", "\t}"}
|
|
if got := d.tailLines(); !reflect.DeepEqual(got, want) {
|
|
t.Errorf("tail = %#v, want %#v", got, want)
|
|
}
|
|
if got := d.lineTotal(); got != 4 {
|
|
t.Errorf("lineTotal = %d, want 4", got)
|
|
}
|
|
}
|
|
|
|
func TestStreamingDecoderPathAliases(t *testing.T) {
|
|
for _, key := range []string{"file", "file_path", "filepath", "filename"} {
|
|
d := newStreamingDecoder()
|
|
d.feed(`{"` + key + `":"time_test.py","content":"print(1)"}`)
|
|
if d.path != "time_test.py" {
|
|
t.Errorf("%s path = %q, want time_test.py", key, d.path)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStreamingDecoderFindsPathAfterContent(t *testing.T) {
|
|
d := newStreamingDecoder()
|
|
feedChunks(d, `{"content":"from datetime import datetime\n`, `print(datetime.now())",`, `"path":"time_test.py"}`)
|
|
if d.path != "time_test.py" {
|
|
t.Fatalf("late path = %q, want time_test.py", d.path)
|
|
}
|
|
if got := d.lineTotal(); got != 2 {
|
|
t.Fatalf("lineTotal = %d, want 2", got)
|
|
}
|
|
}
|
|
|
|
func TestStreamingDecoderSplitEscape(t *testing.T) {
|
|
d := newStreamingDecoder()
|
|
// The \n and \t escapes are split across feed boundaries (backslash, then n/t).
|
|
feedChunks(d, `{"path":"x","content":"line1\`, `nline2\`, `tend"}`)
|
|
want := []string{"line1", "line2\tend"}
|
|
if got := d.tailLines(); !reflect.DeepEqual(got, want) {
|
|
t.Errorf("split-escape tail = %#v, want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestStreamingDecoderEditFileNewString(t *testing.T) {
|
|
// edit_file's canonical content key is new_string (M13 preserved in the decoder).
|
|
d := newStreamingDecoder()
|
|
d.feed(`{"path":"a.go","old_string":"x","new_string":"replaced\nbody"}`)
|
|
if !d.hasContent() {
|
|
t.Fatal("expected content from new_string")
|
|
}
|
|
if got := d.tailLines(); !reflect.DeepEqual(got, []string{"replaced", "body"}) {
|
|
t.Errorf("new_string tail = %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestStreamingDecoderProgressBeforeContent(t *testing.T) {
|
|
d := newStreamingDecoder()
|
|
d.feed(`{"path":"website/css/styles.css"`)
|
|
if d.hasContent() {
|
|
t.Error("content not started yet")
|
|
}
|
|
if d.rawLen == 0 {
|
|
t.Error("rawLen should track bytes for the KB indicator")
|
|
}
|
|
if d.path != "website/css/styles.css" {
|
|
t.Errorf("path should extract before content: %q", d.path)
|
|
}
|
|
}
|
|
|
|
func TestStreamingDecoderTailIsBounded(t *testing.T) {
|
|
d := newStreamingDecoder()
|
|
var b strings.Builder
|
|
b.WriteString(`{"path":"x","content":"`)
|
|
for i := 0; i < 100; i++ {
|
|
b.WriteString("line\\n") // 100 escaped newlines
|
|
}
|
|
b.WriteString(`"}`)
|
|
d.feed(b.String())
|
|
if got := len(d.tailLines()); got > streamingTailLines {
|
|
t.Errorf("tail not bounded: %d > %d", got, streamingTailLines)
|
|
}
|
|
if d.lineTotal() < 100 {
|
|
t.Errorf("lineTotal should count all lines: %d", d.lineTotal())
|
|
}
|
|
}
|
|
|
|
func TestStreamingDecoderChunkingIsConsistent(t *testing.T) {
|
|
full := `{"path":"a.go","content":"alpha\nbeta\ngamma\ndelta"}`
|
|
whole := newStreamingDecoder()
|
|
whole.feed(full)
|
|
byByte := newStreamingDecoder()
|
|
for _, r := range full {
|
|
byByte.feed(string(r))
|
|
}
|
|
if !reflect.DeepEqual(whole.tailLines(), byByte.tailLines()) || whole.lineTotal() != byByte.lineTotal() {
|
|
t.Errorf("byte-by-byte (%#v) != whole (%#v)", byByte.tailLines(), whole.tailLines())
|
|
}
|
|
}
|