1package ui
2
3import (
4 "reflect"
5 "testing"
6
7 "github.com/stretchr/testify/mock"
8)
9
10// newOrderedIME returns an IME mock that records Enable and Disable in order
11// and fails on an enable before a disable or a double disable.
12func newOrderedIME(t *testing.T) (*MockIME, *[]string) {
13 t.Helper()
14 ime := NewMockIME(t)
15 calls := new([]string)
16 active := false
17 ime.EXPECT().Enable().Run(func() {
18 if active {
19 t.Error("IME enable before disable")
20 }
21 active = true
22 *calls = append(*calls, "enable")
23 }).Maybe()
24 ime.EXPECT().Disable().Run(func() {
25 if !active {
26 t.Error("IME double disable")
27 }
28 active = false
29 *calls = append(*calls, "disable")
30 }).Maybe()
31 ime.EXPECT().Surrounding(mock.Anything, mock.Anything, mock.Anything).Maybe()
32 ime.EXPECT().CursorRect(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Maybe()
33 ime.EXPECT().ContentType(mock.Anything, mock.Anything).Maybe()
34 return ime, calls
35}
36
37func TestPasswordEditorNeverSendsSecretToIME(t *testing.T) {
38 r := newRuntime()
39 ime := NewMockIME(t)
40 var surrounding []string
41 password := false
42 ime.EXPECT().Enable().Maybe()
43 ime.EXPECT().Disable().Maybe()
44 ime.EXPECT().CursorRect(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Maybe()
45 ime.EXPECT().Surrounding(mock.Anything, mock.Anything, mock.Anything).Run(func(text string, _, _ int) {
46 surrounding = append(surrounding, text)
47 }).Maybe()
48 ime.EXPECT().ContentType(mock.Anything, mock.Anything).Run(func(p, _ bool) { password = p }).Maybe()
49 r.ime = ime
50 value := "hunter2"
51 view := func(f *Frame) { f.Root().Input("secret", &value, Key("secret"), Password(true)) }
52 r.Build(view)
53 r.route(key("Tab"))
54 r.Build(view)
55 if !password {
56 t.Fatal("password content type not sent")
57 }
58 if len(surrounding) == 0 {
59 t.Fatal("no surrounding text update")
60 }
61 for _, s := range surrounding {
62 if s != "" {
63 t.Fatalf("surrounding text leaked %q", s)
64 }
65 }
66}
67
68func TestIMEFocusTransferDisablesBeforeEnable(t *testing.T) {
69 r := newRuntime()
70 ime, calls := newOrderedIME(t)
71 r.ime = ime
72 a, b := "one", "two"
73 view := func(f *Frame) { root := f.Root(); root.Input("a", &a, Key("a")); root.Textarea("b", &b, Key("b")) }
74 r.Build(view)
75 r.route(key("Tab"))
76 r.Build(view)
77 r.route(key("Tab"))
78 r.Build(view)
79 r.route(key("Tab"))
80 r.Build(view)
81 if want := []string{"enable", "disable", "enable", "disable", "enable"}; !reflect.DeepEqual(*calls, want) {
82 t.Fatalf("focus transition calls %v want %v", *calls, want)
83 }
84 if !r.edits[idKey(r.Target(0))].IMEActive() || r.edits[idKey(r.Target(1))].IMEActive() {
85 t.Fatal("wrong active editor")
86 }
87}