Dialogue fixes and tests.
This commit is contained in:
1 parent
702d9737bd
commit
4c9a267897
5 files changed
+274
-9
No files matched your search
@@ -0,0 +1,148 @@
|
||||
// Fenrir console screen reader.
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package reader
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"git.stormux.org/storm/fenrir/internal/command"
|
||||
"git.stormux.org/storm/fenrir/internal/presentation"
|
||||
"git.stormux.org/storm/fenrir/internal/screen"
|
||||
)
|
||||
|
||||
func dialog_snapshot(lines map[int]string, x, y int) screen.Snapshot {
|
||||
snapshot := screen.Snapshot{Terminal: 1, Active: true, Rows: 25, Cols: 80, X: x, Y: y}
|
||||
snapshot.Cells = make([]rune, snapshot.Rows*snapshot.Cols)
|
||||
for index := range snapshot.Cells {
|
||||
snapshot.Cells[index] = ' '
|
||||
}
|
||||
for row, text := range lines {
|
||||
copy(snapshot.Line(row), []rune(text))
|
||||
}
|
||||
return snapshot
|
||||
}
|
||||
|
||||
func TestDialogBodyWithUnchangedCursorRow(t *testing.T) {
|
||||
// These are the two split redraws captured during the I38 audit: the
|
||||
// button row is already present before the dialog body finishes drawing.
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
before, after screen.Snapshot
|
||||
want []presentation.Message
|
||||
}{
|
||||
{
|
||||
name: "saved preferences body",
|
||||
before: dialog_snapshot(map[int]string{
|
||||
8: "I38", 14: "< OK >",
|
||||
}, 0, 19),
|
||||
after: dialog_snapshot(map[int]string{
|
||||
8: "I38", 9: "Loaded saved preferences from", 10: "/tmp/I38_preferences.conf",
|
||||
12: "Missing or invalid values will be prompted.", 14: "< OK >",
|
||||
}, 2, 14),
|
||||
want: []presentation.Message{
|
||||
{Text: "Loaded saved preferences from"}, {Text: "/tmp/I38_preferences.conf"},
|
||||
{Text: "Missing or invalid values will be prompted."}, {Text: "< OK >"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "last warning line",
|
||||
before: dialog_snapshot(map[int]string{
|
||||
0: "I38", 1: "I38Lock is not available with the current screen reader setup.",
|
||||
2: "Requires i38lock and either Cthulhu or Orca 50 or newer.", 22: "< OK >",
|
||||
}, 2, 3),
|
||||
after: dialog_snapshot(map[int]string{
|
||||
0: "I38", 1: "I38Lock is not available with the current screen reader setup.",
|
||||
2: "Requires i38lock and either Cthulhu or Orca 50 or newer.",
|
||||
3: "Please choose another screen lock method.", 22: "< OK >",
|
||||
}, 2, 22),
|
||||
want: []presentation.Message{
|
||||
{Text: "Please choose another screen lock method."}, {Text: "< OK >"},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
r := New()
|
||||
r.Update(testCase.before)
|
||||
if got := r.Update(testCase.after); !reflect.DeepEqual(got, testCase.want) {
|
||||
t.Fatalf("got %v, want %v", got, testCase.want)
|
||||
}
|
||||
if got := r.Update(testCase.after); len(got) != 0 {
|
||||
t.Fatal("unchanged dialog repeated speech", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCursorFeedbackPreservesConcurrentOutput(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
before, after screen.Snapshot
|
||||
want []presentation.Message
|
||||
}{
|
||||
{
|
||||
"output below focus", fixture("first", "focus", ""), fixture("first", "focus", "new output"),
|
||||
[]presentation.Message{{Text: "new output"}, {Text: "focus"}},
|
||||
},
|
||||
{
|
||||
"scroll with fixed focus", fixture("old", "keep one", "keep two", "last", "focus"),
|
||||
fixture("keep one", "keep two", "last", "new output", "focus"),
|
||||
[]presentation.Message{{Text: "new output"}, {Text: "focus"}},
|
||||
},
|
||||
{
|
||||
"changed focused row", fixture("first", "old focus"), fixture("first", "new focus"),
|
||||
[]presentation.Message{{Text: "new"}},
|
||||
},
|
||||
{
|
||||
"cleared body", fixture("old body", "focus"), fixture("", "focus"),
|
||||
[]presentation.Message{{Text: "focus"}},
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
testCase.after.Y = 1
|
||||
if testCase.name == "scroll with fixed focus" {
|
||||
testCase.after.Y = 4
|
||||
}
|
||||
if got := changes(testCase.before, testCase.after, true); !reflect.DeepEqual(got, testCase.want) {
|
||||
t.Fatalf("got %v, want %v", got, testCase.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialogRedrawRespectsReadingModes(t *testing.T) {
|
||||
for _, mode := range []string{"cursor", "cursor off", "table", "highlight", "review", "automatic off"} {
|
||||
t.Run(mode, func(t *testing.T) {
|
||||
r := New()
|
||||
before := fixture("I38", "", "< OK >")
|
||||
r.Update(before)
|
||||
switch mode {
|
||||
case "cursor off":
|
||||
r.Configure(true, false)
|
||||
case "table", "highlight":
|
||||
r.Configure_tracking(mode)
|
||||
case "review":
|
||||
r.Command(command.CurrentLine)
|
||||
case "automatic off":
|
||||
r.Configure(false, true)
|
||||
}
|
||||
after := fixture("I38", "New dialog body", "< OK >")
|
||||
after.Y = 2
|
||||
var want []presentation.Message
|
||||
if mode != "automatic off" {
|
||||
want = []presentation.Message{{Text: "New dialog body"}}
|
||||
if mode == "cursor" {
|
||||
want = append(want, presentation.Message{Text: "< OK >"})
|
||||
}
|
||||
}
|
||||
if got := r.Update(after); !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("got %v, want %v", got, want)
|
||||
}
|
||||
if mode == "review" && (!r.review.Active() || r.Command(command.CurrentLine).Text != "I38") {
|
||||
t.Fatal("passive dialog redraw displaced review")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user