github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/edit/prompt_test.go (about)

     1  package edit
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"src.elv.sh/pkg/cli/clitest"
     9  	"src.elv.sh/pkg/cli/term"
    10  	"src.elv.sh/pkg/testutil"
    11  	"src.elv.sh/pkg/ui"
    12  )
    13  
    14  func TestPrompt_ValueOutput(t *testing.T) {
    15  	f := setup(rc(`edit:prompt = { put '#'; num 13; styled '> ' red }`))
    16  	defer f.Cleanup()
    17  
    18  	f.TestTTY(t,
    19  		"#13> ", Styles,
    20  		"   !!", term.DotHere)
    21  }
    22  
    23  func TestPrompt_ByteOutput(t *testing.T) {
    24  	f := setup(rc(`edit:prompt = { print 'bytes> ' }`))
    25  	defer f.Cleanup()
    26  
    27  	f.TestTTY(t, "bytes> ", term.DotHere)
    28  }
    29  
    30  func TestPrompt_ParsesSGRInByteOutput(t *testing.T) {
    31  	f := setup(rc(`edit:prompt = { print "\033[31mred\033[m> " }`))
    32  	defer f.Cleanup()
    33  
    34  	f.TestTTY(t,
    35  		"red> ", Styles,
    36  		"!!!  ", term.DotHere)
    37  }
    38  
    39  func TestPrompt_NotifiesInvalidValueOutput(t *testing.T) {
    40  	f := setup(rc(`edit:prompt = { put good [bad] good2 }`))
    41  	defer f.Cleanup()
    42  
    43  	f.TestTTY(t, "goodgood2", term.DotHere)
    44  	f.TestTTYNotes(t, "invalid output type from prompt: list")
    45  }
    46  
    47  func TestPrompt_NotifiesException(t *testing.T) {
    48  	f := setup(rc(`edit:prompt = { fail ERROR }`))
    49  	defer f.Cleanup()
    50  
    51  	f.TestTTYNotes(t,
    52  		"[prompt error] ERROR\n",
    53  		`see stack trace with "show $edit:exceptions[0]"`)
    54  	evals(f.Evaler, `excs = (count $edit:exceptions)`)
    55  	testGlobal(t, f.Evaler, "excs", 1)
    56  }
    57  
    58  func TestRPrompt(t *testing.T) {
    59  	f := setup(rc(`edit:rprompt = { put 'RRR' }`))
    60  	defer f.Cleanup()
    61  
    62  	f.TestTTY(t, "~> ", term.DotHere,
    63  		strings.Repeat(" ", clitest.FakeTTYWidth-6)+"RRR")
    64  }
    65  
    66  func TestPromptEagerness(t *testing.T) {
    67  	f := setup(rc(
    68  		`i = 0`,
    69  		`edit:prompt = { i = (+ $i 1); put $i'> ' }`,
    70  		`edit:-prompt-eagerness = 10`))
    71  	defer f.Cleanup()
    72  
    73  	f.TestTTY(t, "1> ", term.DotHere)
    74  	// With eagerness = 10, any key press will cause the prompt to be
    75  	// recomputed.
    76  	f.TTYCtrl.Inject(term.K(ui.Backspace))
    77  	f.TestTTY(t, "2> ", term.DotHere)
    78  }
    79  
    80  func TestPromptStaleThreshold(t *testing.T) {
    81  	f := setup(rc(
    82  		`pipe = (pipe)`,
    83  		`edit:prompt = { nop (slurp < $pipe); put '> ' }`,
    84  		`edit:prompt-stale-threshold = `+scaledMsAsSec(50)))
    85  	defer f.Cleanup()
    86  
    87  	f.TestTTY(t,
    88  		"???> ", Styles,
    89  		"+++++", term.DotHere)
    90  
    91  	evals(f.Evaler, `pwclose $pipe`)
    92  	f.TestTTY(t, "> ", term.DotHere)
    93  	evals(f.Evaler, `prclose $pipe`)
    94  }
    95  
    96  func TestPromptStaleTransform(t *testing.T) {
    97  	f := setup(rc(
    98  		`pipe = (pipe)`,
    99  		`edit:prompt = { nop (slurp < $pipe); put '> ' }`,
   100  		`edit:prompt-stale-threshold = `+scaledMsAsSec(50),
   101  		`edit:prompt-stale-transform = [a]{ put S; put $a; put S }`))
   102  	defer f.Cleanup()
   103  
   104  	f.TestTTY(t, "S???> S", term.DotHere)
   105  	evals(f.Evaler, `pwclose $pipe`)
   106  	evals(f.Evaler, `prclose $pipe`)
   107  }
   108  
   109  func TestPromptStaleTransform_Exception(t *testing.T) {
   110  	f := setup(rc(
   111  		`pipe = (pipe)`,
   112  		`edit:prompt = { nop (slurp < $pipe); put '> ' }`,
   113  		`edit:prompt-stale-threshold = `+scaledMsAsSec(50),
   114  		`edit:prompt-stale-transform = [_]{ fail ERROR }`))
   115  	defer f.Cleanup()
   116  
   117  	f.TestTTYNotes(t,
   118  		"[prompt stale transform error] ERROR\n",
   119  		`see stack trace with "show $edit:exceptions[0]"`)
   120  	evals(f.Evaler, `excs = (count $edit:exceptions)`)
   121  	testGlobal(t, f.Evaler, "excs", 1)
   122  }
   123  
   124  func TestRPromptPersistent_True(t *testing.T) {
   125  	testRPromptPersistent(t, `edit:rprompt-persistent = $true`,
   126  		"~> "+strings.Repeat(" ", clitest.FakeTTYWidth-6)+"RRR",
   127  		"\n", term.DotHere,
   128  	)
   129  }
   130  
   131  func TestRPromptPersistent_False(t *testing.T) {
   132  	testRPromptPersistent(t, `edit:rprompt-persistent = $false`,
   133  		"~> ", // no rprompt
   134  		"\n", term.DotHere,
   135  	)
   136  }
   137  
   138  func testRPromptPersistent(t *testing.T, code string, finalBuf ...interface{}) {
   139  	f := setup(rc(`edit:rprompt = { put RRR }`, code))
   140  	defer f.Cleanup()
   141  
   142  	// Make sure that the UI has stabilized before hitting Enter.
   143  	f.TestTTY(t,
   144  		"~> ", term.DotHere,
   145  		strings.Repeat(" ", clitest.FakeTTYWidth-6), "RRR",
   146  	)
   147  
   148  	f.TTYCtrl.Inject(term.K('\n'))
   149  	f.TestTTY(t, finalBuf...)
   150  }
   151  
   152  func TestDefaultPromptForNonRoot(t *testing.T) {
   153  	f := setup(assign("edit:prompt", getDefaultPrompt(false)))
   154  	defer f.Cleanup()
   155  
   156  	f.TestTTY(t, "~> ", term.DotHere)
   157  }
   158  
   159  func TestDefaultPromptForRoot(t *testing.T) {
   160  	f := setup(assign("edit:prompt", getDefaultPrompt(true)))
   161  	defer f.Cleanup()
   162  
   163  	f.TestTTY(t,
   164  		"~# ", Styles,
   165  		" !!", term.DotHere)
   166  }
   167  
   168  func TestDefaultRPrompt(t *testing.T) {
   169  	f := setup(assign("edit:rprompt", getDefaultRPrompt("elf", "host")))
   170  	defer f.Cleanup()
   171  
   172  	f.TestTTY(t,
   173  		"~> ", term.DotHere, strings.Repeat(" ", 39),
   174  		"elf@host", Styles,
   175  		"++++++++")
   176  }
   177  
   178  func scaledMsAsSec(ms int) string {
   179  	return fmt.Sprint(testutil.ScaledMs(ms).Seconds())
   180  }