114 lines
3.5 KiB
Go
114 lines
3.5 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package prompt
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.stormux.org/storm/fenrir/internal/screen"
|
|
)
|
|
|
|
func snapshot(lines ...string) screen.Snapshot {
|
|
result := screen.Snapshot{Terminal: 1, Active: true, Rows: len(lines), Cols: 100, Y: len(lines) - 1}
|
|
for _, line := range lines {
|
|
cells := make([]rune, result.Cols)
|
|
for index := range cells {
|
|
cells[index] = ' '
|
|
}
|
|
copy(cells, []rune(line))
|
|
result.Cells = append(result.Cells, cells...)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func TestRecognizesFreshPromptsAndCompletion(t *testing.T) {
|
|
for _, line := range []string{
|
|
"$", "#", ">", "bash-5.3$", "[Username@Host ~/code] $", "Username@Host:~$",
|
|
"Proceed? [Y/n]", "Continue? (Yes/No)", "Is this ok [y/N]:", ":: Proceed? [Y/n]",
|
|
"Password:", "[sudo] password for Username:", "Username's password:",
|
|
"Please enter passphrase for key:", "Press any key to continue...", "Are you sure? [yes/no]",
|
|
"Please confirm:", "CUSTOM READY", "Device 42 ready:",
|
|
"Worked for 3m 35s • 8:05 PM", "Worked for 19s • 7:46 PM", "Worked for 1h 2m 3s • 12:00 AM",
|
|
} {
|
|
t.Run(line, func(t *testing.T) {
|
|
detector, err := New([]string{"CUSTOM READY"}, []string{`^Device [0-9]+ ready:$`})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := detector.Observe(snapshot("working")); got != "" {
|
|
t.Fatal(got)
|
|
}
|
|
if got := detector.Observe(snapshot(" " + line)); got != line {
|
|
t.Fatal(got)
|
|
}
|
|
if got := detector.Observe(snapshot(" " + line)); got != "" {
|
|
t.Fatal("stale line restored", got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRejectsFalseAndIncompletePrompts(t *testing.T) {
|
|
for _, line := range []string{
|
|
"Price is $", "C# language", "Downloading 20%", "$ echo hello", "Password: secret",
|
|
"Please enter passphrase: secret", "Worked for 19s", "Worked for 19s • 7:4",
|
|
"Worked for 19s • 17:46 PM", "Worked for 19s • 7:99 PM", "Worked for 19s • 7:46 PM more",
|
|
"quoted Worked for 19s • 7:46 PM", "Worked for 3m nonsense • 8:05 PM",
|
|
} {
|
|
t.Run(line, func(t *testing.T) {
|
|
detector, _ := New(nil, nil)
|
|
detector.Observe(snapshot("working"))
|
|
if got := detector.Observe(snapshot(line)); got != "" {
|
|
t.Fatal(got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSplitCompletionAboveCursorAndOldScroll(t *testing.T) {
|
|
detector, _ := New(nil, nil)
|
|
detector.Observe(snapshot("old", "working", "input"))
|
|
if got := detector.Observe(snapshot("old", "Worked for 19s • 7:4", "input")); got != "" {
|
|
t.Fatal(got)
|
|
}
|
|
line := "Worked for 19s • 7:46 PM"
|
|
if got := detector.Observe(snapshot("old", line, "input")); got != line {
|
|
t.Fatal(got)
|
|
}
|
|
if got := detector.Observe(snapshot(line, "input", "still working")); got != "" {
|
|
t.Fatal("scrolled history restored", got)
|
|
}
|
|
// A shell prompt elsewhere on the screen is not the active prompt.
|
|
if got := detector.Observe(snapshot(line, "$", "still working")); got != "" {
|
|
t.Fatal(got)
|
|
}
|
|
}
|
|
|
|
func TestSessionChangesOnlyEstablishBaseline(t *testing.T) {
|
|
for _, mode := range []string{"terminal", "generation", "resize", "inactive"} {
|
|
t.Run(mode, func(t *testing.T) {
|
|
detector, _ := New(nil, nil)
|
|
detector.Observe(snapshot("working"))
|
|
next := snapshot("$")
|
|
switch mode {
|
|
case "terminal":
|
|
next.Terminal++
|
|
case "generation":
|
|
next.Generation++
|
|
case "resize":
|
|
next = snapshot("", "$")
|
|
case "inactive":
|
|
next.Active = false
|
|
}
|
|
if got := detector.Observe(next); got != "" {
|
|
t.Fatal(got)
|
|
}
|
|
next.Active = true
|
|
if got := detector.Observe(next); got != "" {
|
|
t.Fatal(got)
|
|
}
|
|
})
|
|
}
|
|
}
|