1package ui
2
3import (
4 "github.com/bnema/nefergui/internal/css"
5 "github.com/bnema/nefergui/internal/edit"
6 "testing"
7)
8
9func TestRadioSliderAndEditingIdentity(t *testing.T) {
10 r := newRuntime()
11 r.styles = css.Compile(css.UA(), css.Parse(`input,radio,slider {height:20px}`))
12 var choice string
13 v := 3.0
14 name := "e\u0301"
15 order := []string{"a", "b"}
16 view := func(f *Frame) {
17 root := f.Root()
18 for _, key := range order {
19 root.Input(key, &name, Key(key))
20 }
21 root.Radio("One", "one", &choice, Key("r1"))
22 root.Radio("Two", "two", &choice, Key("r2"))
23 root.Slider("level", &v, 0, 10, 1, Key("s"))
24 }
25 r.Build(view)
26 r.Queue(inputEvent{Target: r.Target(3), Kind: "activate"})
27 r.Build(view)
28 if choice != "two" {
29 t.Fatal(choice)
30 }
31 r.route(key("Tab"))
32 r.Build(view)
33 for !r.state.focus.same(r.Target(4)) {
34 r.route(key("Tab"))
35 r.Build(view)
36 if r.state.focus.same(r.Target(0)) {
37 t.Fatalf("slider skipped: %v", func() []string {
38 var ids []string
39 for _, e := range r.focusOrder() {
40 ids = append(ids, idKey(e.identity))
41 }
42 return ids
43 }())
44 }
45 }
46 if !r.state.focus.same(r.Target(4)) {
47 t.Fatalf("slider focus: focus=%s order=%v", idKey(r.state.focus), func() []string {
48 var ids []string
49 for _, e := range r.focusOrder() {
50 ids = append(ids, idKey(e.identity))
51 }
52 return ids
53 }())
54 }
55 r.route(key("Right"))
56 r.Build(view)
57 if v != 4 {
58 t.Fatal(v)
59 }
60 r.Queue(inputEvent{Target: r.Target(0), Kind: "edit-key", Text: "backspace"})
61 r.Build(view)
62 if name != "" {
63 t.Fatal("grapheme delete", name)
64 }
65 order = []string{"b", "a"}
66 r.Redraw()
67 r.Build(view)
68 if r.edits[idKey(r.Target(0))] == r.edits[idKey(r.Target(1))] {
69 t.Fatal("state aliased")
70 }
71}
72func TestDisabledEditorRejectsFocusAndText(t *testing.T) {
73 r := newRuntime()
74 r.styles = css.Compile(css.UA(), css.Parse(`input {height:20px}`))
75 value := "keep"
76 view := func(f *Frame) { f.Root().Input("name", &value, Key("name"), Disabled(true)) }
77 r.Build(view)
78 r.route(key("Tab"))
79 r.Build(view)
80 if r.state.focus != nil {
81 t.Fatalf("disabled input focused: %s", idKey(r.state.focus))
82 }
83 r.route(platformInput{Kind: "key", Key: keyEvent{Name: "q", Text: "x", Pressed: true}})
84 r.Build(view)
85 if value != "keep" {
86 t.Fatalf("disabled input edited: %q", value)
87 }
88}
89
90func TestEditFocusIMEAndClipboard(t *testing.T) {
91 r := newRuntime()
92 r.styles = css.Compile(css.UA(), css.Parse(`input {height:20px}`))
93 var value string
94 view := func(f *Frame) { f.Root().Input("secret", &value, Key("secret"), Password(true)) }
95 r.Build(view)
96 r.clipboard = NewMockClipboard(t) // no clipboard access expected
97 r.route(key("Tab"))
98 r.Build(view)
99 r.route(platformInput{Kind: "key", Key: keyEvent{Name: "q", Text: "x", Pressed: true}})
100 r.Build(view)
101 if value != "x" || r.committed.children[0].text != "•" {
102 t.Fatalf("value=%q display=%q focus=%s", value, r.committed.children[0].text, idKey(r.state.focus))
103 }
104 r.route(platformInput{Kind: "ime-done", IME: edit.IMEBatch{Preedit: "候補"}})
105 r.Build(view)
106 if value != "x" {
107 t.Fatal(value)
108 }
109 r.route(platformInput{Kind: "focus-out"})
110 r.Build(view)
111 if r.edits[idKey(r.Target(0))].Preedit != "" {
112 t.Fatal("preedit survives blur")
113 }
114}