Skip to content

Commit 1a40b0f

Browse files
committed
errors.Is for ErrEventUnhandled
1 parent c13f768 commit 1a40b0f

7 files changed

Lines changed: 354 additions & 9 deletions

File tree

eventhandler.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package jaws
22

33
import (
4+
"errors"
45
"fmt"
56
"reflect"
67

@@ -65,12 +66,12 @@ func callEventHandler(obj any, e *Element, wht what.What, val string) (err error
6566
}
6667
if wht == what.Click {
6768
if h, ok := obj.(ClickHandler); ok {
68-
if err = h.JawsClick(e, clk); err != ErrEventUnhandled {
69+
if err = h.JawsClick(e, clk); !errors.Is(err, ErrEventUnhandled) {
6970
return
7071
}
7172
}
7273
} else if h, ok := obj.(ContextMenuHandler); ok {
73-
if err = h.JawsContextMenu(e, clk); err != ErrEventUnhandled {
74+
if err = h.JawsContextMenu(e, clk); !errors.Is(err, ErrEventUnhandled) {
7475
return
7576
}
7677
}
@@ -82,9 +83,9 @@ func callEventHandler(obj any, e *Element, wht what.What, val string) (err error
8283
}
8384

8485
func callEventHandlers(ui any, e *Element, wht what.What, val string) (err error) {
85-
if err = callEventHandler(ui, e, wht, val); err == ErrEventUnhandled {
86+
if err = callEventHandler(ui, e, wht, val); errors.Is(err, ErrEventUnhandled) {
8687
for _, h := range e.handlers {
87-
if err = callEventHandler(h, e, wht, val); err != ErrEventUnhandled {
88+
if err = callEventHandler(h, e, wht, val); !errors.Is(err, ErrEventUnhandled) {
8889
return
8990
}
9091
}

eventhandler_test.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ func Test_CallEventHandlers_ClickDispatchCombinations(t *testing.T) {
175175
rq := newTestRequest(t)
176176
defer rq.Close()
177177
elem := rq.NewElement(testDivWidget{inner: "x"})
178+
wrappedUnhandled := fmt.Errorf("wrapped: %w", ErrEventUnhandled)
178179

179180
tests := []struct {
180181
name string
@@ -200,6 +201,14 @@ func Test_CallEventHandlers_ClickDispatchCombinations(t *testing.T) {
200201
wantClicks: 1,
201202
wantEvents: 0,
202203
},
204+
{
205+
name: "click-only returns wrapped ErrEventUnhandled",
206+
make: func(rec *clickEventComboRecorder) any { return clickOnlyComboHandler{rec: rec} },
207+
clickRet: wrappedUnhandled,
208+
wantErr: ErrEventUnhandled,
209+
wantClicks: 1,
210+
wantEvents: 0,
211+
},
203212
{
204213
name: "event-only returns nil",
205214
make: func(rec *clickEventComboRecorder) any { return eventOnlyComboHandler{rec: rec} },
@@ -215,6 +224,14 @@ func Test_CallEventHandlers_ClickDispatchCombinations(t *testing.T) {
215224
wantClicks: 0,
216225
wantEvents: 1,
217226
},
227+
{
228+
name: "event-only returns wrapped ErrEventUnhandled",
229+
make: func(rec *clickEventComboRecorder) any { return eventOnlyComboHandler{rec: rec} },
230+
eventRet: wrappedUnhandled,
231+
wantErr: ErrEventUnhandled,
232+
wantClicks: 0,
233+
wantEvents: 1,
234+
},
218235
{
219236
name: "dual returns nil from click and nil from event",
220237
make: func(rec *clickEventComboRecorder) any { return dualComboHandler{rec: rec} },
@@ -238,6 +255,14 @@ func Test_CallEventHandlers_ClickDispatchCombinations(t *testing.T) {
238255
wantClicks: 1,
239256
wantEvents: 1,
240257
},
258+
{
259+
name: "dual returns wrapped ErrEventUnhandled from click and nil from event",
260+
make: func(rec *clickEventComboRecorder) any { return dualComboHandler{rec: rec} },
261+
clickRet: wrappedUnhandled,
262+
wantErr: nil,
263+
wantClicks: 1,
264+
wantEvents: 1,
265+
},
241266
{
242267
name: "dual returns ErrEventUnhandled from click and ErrEventUnhandled from event",
243268
make: func(rec *clickEventComboRecorder) any { return dualComboHandler{rec: rec} },
@@ -247,6 +272,15 @@ func Test_CallEventHandlers_ClickDispatchCombinations(t *testing.T) {
247272
wantClicks: 1,
248273
wantEvents: 1,
249274
},
275+
{
276+
name: "dual returns ErrEventUnhandled from click and wrapped ErrEventUnhandled from event",
277+
make: func(rec *clickEventComboRecorder) any { return dualComboHandler{rec: rec} },
278+
clickRet: ErrEventUnhandled,
279+
eventRet: wrappedUnhandled,
280+
wantErr: ErrEventUnhandled,
281+
wantClicks: 1,
282+
wantEvents: 1,
283+
},
250284
}
251285

252286
for _, tt := range tests {
@@ -258,7 +292,11 @@ func Test_CallEventHandlers_ClickDispatchCombinations(t *testing.T) {
258292
handler := tt.make(rec)
259293

260294
err := CallEventHandlers(handler, elem, what.Click, "1 2 5 name")
261-
if err != tt.wantErr {
295+
if tt.wantErr == nil {
296+
if err != nil {
297+
t.Fatalf("err = %v, want nil", err)
298+
}
299+
} else if !errors.Is(err, tt.wantErr) {
262300
t.Fatalf("err = %v, want %v", err, tt.wantErr)
263301
}
264302
if rec.clickCalls != tt.wantClicks {

lib/bind/bind_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,43 @@ func TestBind_Hook_Clicked_bindingHook_fallsThroughUnhandled(t *testing.T) {
391391
}
392392
}
393393

394+
func TestBind_Hook_Clicked_bindingHook_fallsThroughWrappedUnhandled(t *testing.T) {
395+
var mu deadlock.Mutex
396+
var val string
397+
398+
order := []int{}
399+
clickCalls1 := 0
400+
clickCalls2 := 0
401+
clickBind2 := New(&mu, &val).
402+
Clicked(func(Binder[string], *jaws.Element, jaws.Click) error {
403+
clickCalls1++
404+
order = append(order, 1)
405+
return errors.Join(jaws.ErrEventUnhandled, io.EOF)
406+
}).
407+
Clicked(func(Binder[string], *jaws.Element, jaws.Click) error {
408+
clickCalls2++
409+
order = append(order, 2)
410+
return nil
411+
})
412+
413+
handler, ok := clickBind2.(jaws.ClickHandler)
414+
if !ok {
415+
t.Fatalf("%T does not implement ClickHandler", clickBind2)
416+
}
417+
if err := handler.JawsClick(nil, jaws.Click{Name: "two"}); err != nil {
418+
t.Fatal(err)
419+
}
420+
if clickCalls1 != 1 {
421+
t.Error(clickCalls1)
422+
}
423+
if clickCalls2 != 1 {
424+
t.Error(clickCalls2)
425+
}
426+
if !reflect.DeepEqual(order, []int{1, 2}) {
427+
t.Error(order)
428+
}
429+
}
430+
394431
func TestBind_Hook_ContextMenu_binding(t *testing.T) {
395432
var mu deadlock.Mutex
396433
var val string
@@ -463,6 +500,43 @@ func TestBind_Hook_ContextMenu_bindingHook_fallsThroughUnhandled(t *testing.T) {
463500
}
464501
}
465502

503+
func TestBind_Hook_ContextMenu_bindingHook_fallsThroughWrappedUnhandled(t *testing.T) {
504+
var mu deadlock.Mutex
505+
var val string
506+
507+
order := []int{}
508+
menuCalls1 := 0
509+
menuCalls2 := 0
510+
menuBind2 := New(&mu, &val).
511+
ContextMenu(func(Binder[string], *jaws.Element, jaws.Click) error {
512+
menuCalls1++
513+
order = append(order, 1)
514+
return errors.Join(jaws.ErrEventUnhandled, io.EOF)
515+
}).
516+
ContextMenu(func(Binder[string], *jaws.Element, jaws.Click) error {
517+
menuCalls2++
518+
order = append(order, 2)
519+
return nil
520+
})
521+
522+
handler, ok := menuBind2.(jaws.ContextMenuHandler)
523+
if !ok {
524+
t.Fatalf("%T does not implement ContextMenuHandler", menuBind2)
525+
}
526+
if err := handler.JawsContextMenu(nil, jaws.Click{Name: "two"}); err != nil {
527+
t.Fatal(err)
528+
}
529+
if menuCalls1 != 1 {
530+
t.Error(menuCalls1)
531+
}
532+
if menuCalls2 != 1 {
533+
t.Error(menuCalls2)
534+
}
535+
if !reflect.DeepEqual(order, []int{1, 2}) {
536+
t.Error(order)
537+
}
538+
}
539+
466540
func TestBind_Click_defaultUnhandled(t *testing.T) {
467541
var mu deadlock.Mutex
468542
var val string

lib/bind/bindinghook.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package bind
22

33
import (
4+
"errors"
45
"fmt"
56
"html/template"
67

@@ -66,13 +67,13 @@ func callChain[T comparable](binder Binder[T], elem *jaws.Element, kind callChai
6667
}
6768
}
6869
case callChainClicked:
69-
if err == jaws.ErrEventUnhandled {
70+
if errors.Is(err, jaws.ErrEventUnhandled) {
7071
if fn, ok := bh.hook.(BindClickedHook[T]); ok {
7172
err = fn(bh, elem, param.(jaws.Click))
7273
}
7374
}
7475
case callChainContextMenu:
75-
if err == jaws.ErrEventUnhandled {
76+
if errors.Is(err, jaws.ErrEventUnhandled) {
7677
if fn, ok := bh.hook.(BindContextMenuHook[T]); ok {
7778
err = fn(bh, elem, param.(jaws.Click))
7879
}

lib/ui/object_test.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package ui
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/linkdata/jaws"
9+
)
10+
11+
type testObjectStringer struct {
12+
s string
13+
}
14+
15+
func (s testObjectStringer) String() string {
16+
return s.s
17+
}
18+
19+
func TestObject_NewForwardsHTMLAndTag(t *testing.T) {
20+
_, rq := newCoreRequest(t)
21+
elem := rq.NewElement(NewSpan(testHTMLGetter("x")))
22+
inner := testObjectStringer{s: "<b>x</b>"}
23+
obj := New(inner)
24+
25+
if got, want := string(obj.JawsGetHTML(elem)), "&lt;b&gt;x&lt;/b&gt;"; got != want {
26+
t.Fatalf("want %q got %q", want, got)
27+
}
28+
if got, want := obj.JawsGetTag(rq), any(inner); got != want {
29+
t.Fatalf("want tag %#v got %#v", want, got)
30+
}
31+
}
32+
33+
func TestObject_Click_DefaultUnhandled(t *testing.T) {
34+
obj := New("x")
35+
if err := obj.JawsClick(nil, jaws.Click{Name: "ignored"}); err != jaws.ErrEventUnhandled {
36+
t.Fatalf("want ErrEventUnhandled got %v", err)
37+
}
38+
}
39+
40+
func TestObject_ContextMenu_DefaultUnhandled(t *testing.T) {
41+
obj := New("x")
42+
if err := obj.JawsContextMenu(nil, jaws.Click{Name: "ignored"}); err != jaws.ErrEventUnhandled {
43+
t.Fatalf("want ErrEventUnhandled got %v", err)
44+
}
45+
}
46+
47+
func TestObject_Clicked_FallthroughOrder(t *testing.T) {
48+
obj := New("x")
49+
order := []int{}
50+
gotObj := []Object{}
51+
gotElem := []*jaws.Element{}
52+
gotClick := []jaws.Click{}
53+
54+
obj = obj.Clicked(func(got Object, elem *jaws.Element, click jaws.Click) error {
55+
order = append(order, 1)
56+
gotObj = append(gotObj, got)
57+
gotElem = append(gotElem, elem)
58+
gotClick = append(gotClick, click)
59+
return jaws.ErrEventUnhandled
60+
}).Clicked(func(got Object, elem *jaws.Element, click jaws.Click) error {
61+
order = append(order, 2)
62+
gotObj = append(gotObj, got)
63+
gotElem = append(gotElem, elem)
64+
gotClick = append(gotClick, click)
65+
return nil
66+
})
67+
68+
elem := &jaws.Element{}
69+
click := jaws.Click{Name: "save", X: 1, Y: 2}
70+
if err := obj.JawsClick(elem, click); err != nil {
71+
t.Fatalf("want nil got %v", err)
72+
}
73+
if len(order) != 2 || order[0] != 1 || order[1] != 2 {
74+
t.Fatalf("unexpected order %v", order)
75+
}
76+
if gotObj[0] == gotObj[1] {
77+
t.Fatalf("expected distinct hook objects")
78+
}
79+
if gotElem[0] != elem || gotElem[1] != elem {
80+
t.Fatalf("unexpected elem forwarding %#v", gotElem)
81+
}
82+
if gotClick[0] != click || gotClick[1] != click {
83+
t.Fatalf("unexpected click forwarding %#v", gotClick)
84+
}
85+
}
86+
87+
func TestObject_Clicked_StopsOnHandled(t *testing.T) {
88+
called1 := 0
89+
called2 := 0
90+
obj := New("x").
91+
Clicked(func(Object, *jaws.Element, jaws.Click) error {
92+
called1++
93+
return nil
94+
}).
95+
Clicked(func(Object, *jaws.Element, jaws.Click) error {
96+
called2++
97+
return nil
98+
})
99+
100+
if err := obj.JawsClick(nil, jaws.Click{Name: "save"}); err != nil {
101+
t.Fatalf("want nil got %v", err)
102+
}
103+
if called1 != 1 {
104+
t.Fatalf("want first called once, got %d", called1)
105+
}
106+
if called2 != 0 {
107+
t.Fatalf("want second not called, got %d", called2)
108+
}
109+
}
110+
111+
func TestObject_ContextMenu_FallthroughOrder(t *testing.T) {
112+
obj := New("x")
113+
order := []int{}
114+
115+
obj = obj.ContextMenu(func(Object, *jaws.Element, jaws.Click) error {
116+
order = append(order, 1)
117+
return jaws.ErrEventUnhandled
118+
}).ContextMenu(func(Object, *jaws.Element, jaws.Click) error {
119+
order = append(order, 2)
120+
return nil
121+
})
122+
123+
if err := obj.JawsContextMenu(nil, jaws.Click{Name: "menu"}); err != nil {
124+
t.Fatalf("want nil got %v", err)
125+
}
126+
if len(order) != 2 || order[0] != 1 || order[1] != 2 {
127+
t.Fatalf("unexpected order %v", order)
128+
}
129+
}
130+
131+
func TestObject_EventUnhandledCanBeWrapped(t *testing.T) {
132+
wrapped := fmt.Errorf("wrapped: %w", jaws.ErrEventUnhandled)
133+
134+
obj := New("x").Clicked(func(Object, *jaws.Element, jaws.Click) error {
135+
return wrapped
136+
})
137+
if err := obj.JawsClick(nil, jaws.Click{Name: "click"}); !errors.Is(err, jaws.ErrEventUnhandled) {
138+
t.Fatalf("want ErrEventUnhandled chain got %v", err)
139+
} else if err != wrapped {
140+
t.Fatalf("want wrapped unhandled got %v", err)
141+
}
142+
143+
obj = New("x").ContextMenu(func(Object, *jaws.Element, jaws.Click) error {
144+
return wrapped
145+
})
146+
if err := obj.JawsContextMenu(nil, jaws.Click{Name: "menu"}); !errors.Is(err, jaws.ErrEventUnhandled) {
147+
t.Fatalf("want ErrEventUnhandled chain got %v", err)
148+
} else if err != wrapped {
149+
t.Fatalf("want wrapped unhandled got %v", err)
150+
}
151+
}

request.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -723,11 +723,11 @@ func (rq *Request) callAllEventHandlers(id Jid, wht what.What, val string) (err
723723
rq.mu.RUnlock()
724724

725725
for _, e := range elems {
726-
if err = CallEventHandlers(e.Ui(), e, wht, val); err != ErrEventUnhandled {
726+
if err = CallEventHandlers(e.Ui(), e, wht, val); !errors.Is(err, ErrEventUnhandled) {
727727
return
728728
}
729729
}
730-
if err == ErrEventUnhandled {
730+
if errors.Is(err, ErrEventUnhandled) {
731731
err = nil
732732
}
733733
return

0 commit comments

Comments
 (0)