// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package review import ( "testing" "git.stormux.org/storm/fenrir/internal/command" "git.stormux.org/storm/fenrir/internal/presentation" "git.stormux.org/storm/fenrir/internal/screen" ) func TestVerticalCharacterRetainsColumnAndDetachedReview(t *testing.T) { var r Review s := fixture("abcdÉ!", "x", "", "wxyzΩ?") s.X = 4 r.Update(s) for _, trial := range []struct { action command.ID want string row int cue presentation.Cue character rune }{ {command.ReviewUp, "start of screen", 0, presentation.Top, 0}, {command.ReviewDown, "space", 1, "", ' '}, {command.ReviewDown, "space", 2, "", ' '}, {command.ReviewDown, "Ω", 3, "", 'Ω'}, {command.ReviewDown, "end of screen", 3, presentation.Bottom, 0}, {command.ReviewUp, "space", 2, "", ' '}, {command.ReviewUp, "space", 1, "", ' '}, {command.ReviewUp, "É", 0, "", 'É'}, } { result := r.Execute(trial.action) if got := result.Text; got != trial.want || r.x != 4 || r.y != trial.row || result.Cue != trial.cue { t.Fatalf("%s: %q at %d,%d cue %q; want %+v", trial.action, got, r.x, r.y, result.Cue, trial) } char := result.Character if (char != nil) != (trial.character != 0) || char != nil && *char != trial.character { t.Fatalf("character metadata: %v; want %q", char, trial.character) } if !r.Active() || r.screen.X != 4 || r.screen.Y != 0 { t.Fatal("vertical review changed application ownership or position") } } // Passive application cursor movement must leave detached review intact. s.X, s.Y = 0, 3 r.Update(s) if got := r.Execute(command.ReviewDown).Text; got != "space" || r.x != 4 || r.y != 1 { t.Fatal("review followed application cursor", got, r.x, r.y) } r.Execute(command.FollowCursor) if got := r.Execute(command.ReviewUp).Text; got != "space" || r.x != 0 || r.y != 2 { t.Fatal("follow-cursor did not reset vertical review", got, r.x, r.y) } } func TestVerticalCharacterScreenEdgesAndSessionReset(t *testing.T) { for _, column := range []int{0, 19} { var r Review s := fixture("", "") s.X = column r.Update(s) if got := r.Execute(command.ReviewDown).Text; got != "space" || r.x != column { t.Fatal("empty screen or edge column", got, r.x) } for _, change := range []string{"generation", "terminal", "resize", "inactive"} { next := fixture("new", "last") switch change { case "generation": next.Generation++ case "terminal": next.Terminal++ case "resize": next = fixture("new", "last", "extra") case "inactive": r.Update(screen.Snapshot{}) if got := r.Execute(command.ReviewUp).Text; got != "no text console" { t.Fatal(got) } } r.Update(next) if got := r.Execute(command.ReviewDown).Text; got != "l" || r.x != 0 || r.y != 1 { t.Fatal("stale vertical position after", change, got, r.x, r.y) } } } } func TestVerticalCharacterTableScopeRetainsAbsoluteColumn(t *testing.T) { var r Review r.Update(fixture("Name|Subject", "é|long word", "wide label|other", "only", "x|", "Name|Subject")) r.Set_table_mode(true) r.Execute(command.CurrentWord) r.Set_mark() r.Execute(command.NextWord) for _, trial := range []struct { action command.ID want string row int }{ {command.ReviewDown, "g", 1}, {command.ReviewDown, "no table data", 2}, // column six belongs to the first cell {command.ReviewDown, "no table data", 3}, // selected cell missing {command.ReviewDown, "blank", 4}, {command.ReviewDown, "S", 5}, {command.ReviewUp, "blank", 4}, {command.ReviewUp, "no table data", 3}, {command.ReviewUp, "no table data", 2}, {command.ReviewUp, "g", 1}, {command.ReviewUp, "S", 0}, } { if got := r.Execute(trial.action).Text; got != trial.want || r.x != 5 || r.y != trial.row || r.tableColumn != 1 { t.Fatalf("%s: %q at %d,%d cell %d; want %+v", trial.action, got, r.x, r.y, r.tableColumn, trial) } } if got := r.Execute(command.CurrentWord).Text; got != "Subject Subject" { t.Fatal("vertical review lost header or selected cell", got) } r.Set_table_mode(false) if got := r.Execute(command.ReviewDown).Text; got != "g" { t.Fatal("ordinary review did not return", got) } }