diff --git a/jaws.go b/jaws.go
index 0601930..04d55cf 100644
--- a/jaws.go
+++ b/jaws.go
@@ -3,6 +3,7 @@ package jaws
import (
"html/template"
"io"
+ "net/http"
"sync"
"time"
@@ -49,6 +50,7 @@ type (
With = pkg.With
Session = pkg.Session
Tag = pkg.Tag
+ TestRequest = pkg.TestRequest
)
var (
@@ -236,3 +238,7 @@ func NewUiText(vp Setter[string]) *UiText {
func NewUiTr(innerHTML HTMLGetter) *UiTr {
return pkg.NewUiTr(innerHTML)
}
+
+func NewTestRequest(jw *Jaws, hr *http.Request) (tr *TestRequest) {
+ return pkg.NewTestRequest(jw, hr)
+}
diff --git a/jaws/clickhandler_test.go b/jaws/clickhandler_test.go
index 29552c2..268efea 100644
--- a/jaws/clickhandler_test.go
+++ b/jaws/clickhandler_test.go
@@ -23,7 +23,7 @@ var _ ClickHandler = (*testJawsClick)(nil)
func Test_clickHandlerWapper_JawsEvent(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
tjc := &testJawsClick{
@@ -37,16 +37,16 @@ func Test_clickHandlerWapper_JawsEvent(t *testing.T) {
t.Errorf("Request.Div() = %q, want %q", got, want)
}
- rq.inCh <- wsMsg{Data: "text", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "text", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
- case s := <-rq.outCh:
+ case s := <-rq.OutCh:
t.Errorf("%q", s)
default:
}
- rq.inCh <- wsMsg{Data: "adam", Jid: 1, What: what.Click}
+ rq.InCh <- wsMsg{Data: "adam", Jid: 1, What: what.Click}
select {
case <-th.C:
th.Timeout()
diff --git a/jaws/element_test.go b/jaws/element_test.go
index dbd01c2..9cfb5eb 100644
--- a/jaws/element_test.go
+++ b/jaws/element_test.go
@@ -66,12 +66,12 @@ func (tss *testUi) JawsUpdate(e *Element) {
func TestElement_helpers(t *testing.T) {
is := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
tss := &testUi{}
e := rq.NewElement(tss)
- is.Equal(e.Jaws, rq.jw.Jaws)
+ is.Equal(e.Jaws, rq.Jaws)
is.Equal(e.Request, rq.Request)
is.Equal(e.Session(), nil)
e.Set("foo", "bar") // no session, so no effect
@@ -80,7 +80,7 @@ func TestElement_helpers(t *testing.T) {
func TestElement_Tag(t *testing.T) {
is := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
tss := &testUi{}
@@ -96,7 +96,7 @@ func TestElement_Tag(t *testing.T) {
func TestElement_Queued(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
tss := &testUi{
@@ -181,13 +181,12 @@ func TestElement_Queued(t *testing.T) {
time.Sleep(time.Millisecond)
}
}
- th.Equal(tss.updateCalled, int32(1))
th.Equal(tss.renderCalled, int32(2))
}
func TestElement_ReplacePanicsOnMissingId(t *testing.T) {
is := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
defer func() {
if x := recover(); x == nil {
@@ -202,7 +201,7 @@ func TestElement_ReplacePanicsOnMissingId(t *testing.T) {
func TestElement_maybeDirty(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
tss := &testUi{s: "foo"}
e := rq.NewElement(tss)
@@ -231,7 +230,7 @@ var _ ClickHandler = testClickHandler{}
func TestElement_ApplyGetter(t *testing.T) {
is := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
tss := &testUi{s: "foo"}
@@ -250,7 +249,7 @@ func TestElement_ApplyGetter(t *testing.T) {
func TestElement_JawsInit(t *testing.T) {
is := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
tss := &testUi{s: "foo"}
diff --git a/jaws/handler_test.go b/jaws/handler_test.go
index c89c94b..1c4d7a6 100644
--- a/jaws/handler_test.go
+++ b/jaws/handler_test.go
@@ -8,7 +8,7 @@ import (
func TestHandler_ServeHTTP(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
dot := Tag("123")
diff --git a/jaws/jawsevent_test.go b/jaws/jawsevent_test.go
index 0a191aa..4af7a00 100644
--- a/jaws/jawsevent_test.go
+++ b/jaws/jawsevent_test.go
@@ -58,7 +58,7 @@ var _ UI = (*testJawsEvent)(nil)
func Test_JawsEvent_ClickUnhandled(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
msgCh := make(chan string, 1)
@@ -68,7 +68,7 @@ func Test_JawsEvent_ClickUnhandled(t *testing.T) {
id := rq.Register(zomgItem, je, "attr1", []string{"attr2"}, template.HTMLAttr("attr3"), []template.HTMLAttr{"attr4"})
je.clickerr = ErrEventUnhandled
- rq.inCh <- wsMsg{Data: "name", Jid: id, What: what.Click}
+ rq.InCh <- wsMsg{Data: "name", Jid: id, What: what.Click}
select {
case <-th.C:
th.Timeout()
@@ -82,7 +82,7 @@ func Test_JawsEvent_ClickUnhandled(t *testing.T) {
func Test_JawsEvent_AllUnhandled(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
msgCh := make(chan string, 1)
@@ -93,7 +93,7 @@ func Test_JawsEvent_AllUnhandled(t *testing.T) {
je.clickerr = ErrEventUnhandled
je.eventerr = ErrEventUnhandled
- rq.inCh <- wsMsg{Data: "name", Jid: id, What: what.Click}
+ rq.InCh <- wsMsg{Data: "name", Jid: id, What: what.Click}
select {
case <-th.C:
th.Timeout()
@@ -126,7 +126,7 @@ func (t *testJawsEventHandler) JawsEvent(e *Element, wht what.What, val string)
func Test_JawsEvent_ExtraHandler(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
msgCh := make(chan string, 1)
@@ -139,7 +139,7 @@ func Test_JawsEvent_ExtraHandler(t *testing.T) {
th.NoErr(je.JawsRender(elem, &sb, nil))
th.Equal(sb.String(), "
tjEH
")
- rq.inCh <- wsMsg{Data: "name", Jid: 1, What: what.Click}
+ rq.InCh <- wsMsg{Data: "name", Jid: 1, What: what.Click}
select {
case <-th.C:
th.Timeout()
diff --git a/jaws/jawsjaws_test.go b/jaws/jawsjaws_test.go
index baea00d..75d36df 100644
--- a/jaws/jawsjaws_test.go
+++ b/jaws/jawsjaws_test.go
@@ -178,6 +178,8 @@ func TestJaws_BroadcastFullClosesChannel(t *testing.T) {
go func() {
select {
+ case <-t.Context().Done():
+ close(failCh)
case <-th.C:
close(failCh)
case <-subCh2:
@@ -521,18 +523,20 @@ func TestJaws_GenerateHeadHTML(t *testing.T) {
func TestJaws_TemplateLookuper(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ tj := newTestJaws()
+ defer tj.Close()
+ rq := NewTestRequest(tj.Jaws, nil)
defer rq.Close()
th.Equal(rq.Jaws.LookupTemplate("nosuchtemplate"), nil)
- th.Equal(rq.Jaws.LookupTemplate("testtemplate"), rq.jw.testtmpl)
- rq.Jaws.RemoveTemplateLookuper(rq.jw.testtmpl)
+ th.Equal(rq.Jaws.LookupTemplate("testtemplate"), tj.testtmpl)
+ rq.Jaws.RemoveTemplateLookuper(tj.testtmpl)
th.Equal(rq.Jaws.LookupTemplate("testtemplate"), nil)
}
func TestJaws_JsCall(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
tss := &testUi{}
@@ -548,7 +552,7 @@ func TestJaws_JsCall(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
got := msg.Format()
th.Equal(got, "Call\tJid.1\tsomefn=1.3\n")
}
diff --git a/jaws/jsvar_test.go b/jaws/jsvar_test.go
index d3afd51..96c1681 100644
--- a/jaws/jsvar_test.go
+++ b/jaws/jsvar_test.go
@@ -43,11 +43,11 @@ func (tl *testLocker) Unlock() {
func Test_JsVar_JawsRender(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
nextJid = 0
- rq.jw.AddTemplateLookuper(template.Must(template.New("jsvartemplate").Parse(`{{$.JsVar "` + varname + `" .Dot}}`)))
+ rq.Jaws.AddTemplateLookuper(template.Must(template.New("jsvartemplate").Parse(`{{$.JsVar "` + varname + `" .Dot}}`)))
var mu deadlock.RWMutex
var val valtype
@@ -88,7 +88,7 @@ func Test_JsVar_Update(t *testing.T) {
var val valtype
dot := NewJsVar(&mu, &val)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
elem := rq.NewElement(dot)
@@ -107,7 +107,7 @@ func Test_JsVar_Update(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case gotMsg := <-rq.outCh:
+ case gotMsg := <-rq.OutCh:
wantMsg := wsMsg{
Data: "={\"String\":\"x\",\"Number\":2}",
Jid: 1,
@@ -166,7 +166,7 @@ func Test_JsVar_Event(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case rq1.inCh <- wsMsg{Jid: 1, What: what.Set, Data: "={\"String\":\"y\",\"Number\":3}"}:
+ case rq1.InCh <- wsMsg{Jid: 1, What: what.Set, Data: "={\"String\":\"y\",\"Number\":3}"}:
}
select {
@@ -180,7 +180,7 @@ func Test_JsVar_Event(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq1.outCh:
+ case msg := <-rq1.OutCh:
s := msg.Format()
after, found := strings.CutPrefix(s, "Set\tJid.1\t=")
th.Equal(found, true)
@@ -197,7 +197,7 @@ func Test_JsVar_Event(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq2.outCh:
+ case msg := <-rq2.OutCh:
s := msg.Format()
after, found := strings.CutPrefix(s, "Set\tJid.2\t=")
th.Equal(found, true)
@@ -214,13 +214,13 @@ func Test_JsVar_Event(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case rq1.inCh <- wsMsg{Jid: 1, What: what.Set, Data: "=1"}:
+ case rq1.InCh <- wsMsg{Jid: 1, What: what.Set, Data: "=1"}:
}
select {
case <-th.C:
th.Timeout()
- case msg := <-rq1.outCh:
+ case msg := <-rq1.OutCh:
s := msg.Format()
if !strings.Contains(s, "jq: expected") {
th.Error(s)
@@ -230,7 +230,7 @@ func Test_JsVar_Event(t *testing.T) {
func Test_JsVar_PanicsOnWrongType(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
defer func() {
if x := recover(); x == nil {
@@ -255,7 +255,7 @@ var _ JsVarMaker = &testJsVarMaker{}
func Test_JsVar_JsVarMaker(t *testing.T) {
nextJid = 0
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
err := rq.JsVar("foo", &testJsVarMaker{})
th.NoErr(err)
@@ -286,7 +286,7 @@ var _ SetPather = &testJsVarPathSetter{}
func Test_JsVar_PathSetter_SetPather(t *testing.T) {
nextJid = 0
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
var mu deadlock.Mutex
@@ -302,7 +302,7 @@ func Test_JsVar_PathSetter_SetPather(t *testing.T) {
func Test_JsVar_Unchanged(t *testing.T) {
nextJid = 0
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
var mu deadlock.Mutex
diff --git a/jaws/namedbool_test.go b/jaws/namedbool_test.go
index 60c62db..5190768 100644
--- a/jaws/namedbool_test.go
+++ b/jaws/namedbool_test.go
@@ -12,7 +12,7 @@ func TestNamedBool(t *testing.T) {
nba.Add("1", "one")
nb := nba.data[0]
- rq := newTestRequest()
+ rq := newTestRequest(t)
e := rq.NewElement(NewUiCheckbox(nb))
defer rq.Close()
diff --git a/jaws/namedboolarray_test.go b/jaws/namedboolarray_test.go
index af690e6..6db85b9 100644
--- a/jaws/namedboolarray_test.go
+++ b/jaws/namedboolarray_test.go
@@ -78,7 +78,7 @@ func Test_NamedBoolArray(t *testing.T) {
(nba.data)[1].Set(true)
is.Equal(nba.IsChecked("2"), true)
- rq := newTestRequest()
+ rq := newTestRequest(t)
e := rq.NewElement(NewUiSelect(nba))
defer rq.Close()
diff --git a/jaws/request.go b/jaws/request.go
index 6526492..fc94b5a 100644
--- a/jaws/request.go
+++ b/jaws/request.go
@@ -395,6 +395,13 @@ func (rq *Request) NewElement(ui UI) *Element {
return rq.newElementLocked(ui)
}
+func (rq *Request) GetElementByJid(jid Jid) (e *Element) {
+ rq.mu.RLock()
+ defer rq.mu.RUnlock()
+ e = rq.getElementByJidLocked(jid)
+ return
+}
+
func (rq *Request) getElementByJidLocked(jid Jid) (elem *Element) {
for _, e := range rq.elems {
if e.Jid() == jid {
diff --git a/jaws/request_test.go b/jaws/request_test.go
index d214973..3d1c916 100644
--- a/jaws/request_test.go
+++ b/jaws/request_test.go
@@ -5,12 +5,14 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"strconv"
"strings"
"sync/atomic"
"testing"
"time"
+ "github.com/linkdata/deadlock"
"github.com/linkdata/jaws/jid"
"github.com/linkdata/jaws/what"
)
@@ -29,7 +31,7 @@ func fillWsCh(ch chan wsMsg) {
func TestRequest_Registrations(t *testing.T) {
is := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
x := &testUi{}
@@ -90,19 +92,19 @@ document.getElementById("Jid.1")?.classList?.remove("cls");
func TestRequest_SendArrivesOk(t *testing.T) {
is := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
x := &testUi{}
jid := rq.Register(x)
- elem := rq.getElementByJid(jid)
+ elem := rq.GetElementByJid(jid)
is.True(elem != nil)
- rq.jw.Broadcast(Message{Dest: x, What: what.Inner, Data: "bar"})
+ rq.Jaws.Broadcast(Message{Dest: x, What: what.Inner, Data: "bar"})
select {
case <-time.NewTimer(time.Hour).C:
is.Error("timeout")
- case msg := <-rq.outCh:
- elem := rq.getElementByJid(jid)
+ case msg := <-rq.OutCh:
+ elem := rq.GetElementByJid(jid)
is.True(elem != nil)
if elem != nil {
is.Equal(msg, wsMsg{Jid: elem.jid, Data: "bar", What: what.Inner})
@@ -111,7 +113,7 @@ func TestRequest_SendArrivesOk(t *testing.T) {
}
func TestRequest_SetContext(t *testing.T) {
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
type testKey string
rq.SetContext(func(oldctx context.Context) (newctx context.Context) {
@@ -124,23 +126,23 @@ func TestRequest_SetContext(t *testing.T) {
func TestRequest_OutboundRespectsContextDone(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
var callCount int32
x := &testUi{}
rq.Register(x, func(e *Element, evt what.What, val string) error {
atomic.AddInt32(&callCount, 1)
- rq.cancel()
+ rq.cancel(nil)
return errors.New(val)
})
- fillWsCh(rq.outCh)
- rq.jw.Broadcast(Message{Dest: x, What: what.Hook, Data: "bar"})
+ fillWsCh(rq.OutCh)
+ rq.Jaws.Broadcast(Message{Dest: x, What: what.Hook, Data: "bar"})
select {
case <-th.C:
th.Equal(int(atomic.LoadInt32(&callCount)), 0)
th.Timeout()
- case <-rq.jw.Done():
+ case <-rq.Jaws.Done():
th.Fatal("jaws done too soon")
case <-rq.ctx.Done():
}
@@ -148,7 +150,7 @@ func TestRequest_OutboundRespectsContextDone(t *testing.T) {
th.Equal(int(atomic.LoadInt32(&callCount)), 1)
select {
- case <-rq.jw.Done():
+ case <-rq.Jaws.Done():
th.Fatal("jaws done too soon")
default:
}
@@ -156,7 +158,7 @@ func TestRequest_OutboundRespectsContextDone(t *testing.T) {
func TestRequest_Trigger(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
gotFooCall := make(chan struct{})
gotEndCall := make(chan struct{})
@@ -176,11 +178,11 @@ func TestRequest_Trigger(t *testing.T) {
})
// broadcasts from ourselves should not invoke fn
- rq.jw.Broadcast(Message{Dest: endItem, What: what.Input, Data: ""}) // to know when to stop
+ rq.Jaws.Broadcast(Message{Dest: endItem, What: what.Input, Data: ""}) // to know when to stop
select {
case <-th.C:
th.Timeout()
- case s := <-rq.outCh:
+ case s := <-rq.OutCh:
th.Fatal(s)
case <-gotFooCall:
th.Fatal("gotFooCall")
@@ -188,21 +190,21 @@ func TestRequest_Trigger(t *testing.T) {
}
// global broadcast should invoke fn
- rq.jw.Broadcast(Message{Dest: fooItem, What: what.Input, Data: "bar"})
+ rq.Jaws.Broadcast(Message{Dest: fooItem, What: what.Input, Data: "bar"})
select {
case <-th.C:
th.Timeout()
- case s := <-rq.outCh:
+ case s := <-rq.OutCh:
th.Fatal(s)
case <-gotFooCall:
}
// fn returning error should send an danger alert message
- rq.jw.Broadcast(Message{Dest: errItem, What: what.Input, Data: "omg"})
+ rq.Jaws.Broadcast(Message{Dest: errItem, What: what.Input, Data: "omg"})
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
th.Equal(msg.Format(), (&wsMsg{
Data: "danger\nomg",
Jid: jid.Jid(0),
@@ -213,7 +215,7 @@ func TestRequest_Trigger(t *testing.T) {
func TestRequest_EventFnQueue(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
// calls to slow event functions queue up and are executed in order
@@ -224,120 +226,153 @@ func TestRequest_EventFnQueue(t *testing.T) {
rq.Register(sleepItem, func(e *Element, evt what.What, val string) error {
count := int(atomic.AddInt32(&callCount, 1))
if val != strconv.Itoa(count) {
- t.Logf("val=%s, count=%d, cap=%d", val, count, cap(rq.outCh))
+ t.Logf("val=%s, count=%d, cap=%d", val, count, cap(rq.OutCh))
th.Fail()
}
if count == 1 {
close(firstDoneCh)
}
for atomic.LoadInt32(&sleepDone) == 0 {
- time.Sleep(time.Millisecond)
+ select {
+ case <-t.Context().Done():
+ return nil
+ default:
+ time.Sleep(time.Millisecond)
+ }
}
return nil
})
- for i := 0; i < cap(rq.outCh); i++ {
- rq.jw.Broadcast(Message{Dest: sleepItem, What: what.Input, Data: strconv.Itoa(i + 1)})
+ for i := 0; i < cap(rq.OutCh); i++ {
+ rq.Jaws.Broadcast(Message{Dest: sleepItem, What: what.Input, Data: strconv.Itoa(i + 1)})
}
select {
case <-th.C:
th.Timeout()
- case <-rq.doneCh:
+ case <-rq.DoneCh:
th.Fatal("doneCh")
case <-firstDoneCh:
}
th.Equal(atomic.LoadInt32(&callCount), int32(1))
atomic.StoreInt32(&sleepDone, 1)
- th.Equal(rq.panicVal, nil)
+ th.Equal(rq.PanicVal, nil)
- for int(atomic.LoadInt32(&callCount)) < cap(rq.outCh) {
+ for int(atomic.LoadInt32(&callCount)) < cap(rq.OutCh) {
select {
case <-th.C:
- t.Logf("callCount=%d, cap=%d", atomic.LoadInt32(&callCount), cap(rq.outCh))
- th.Equal(rq.panicVal, nil)
+ t.Logf("callCount=%d, cap=%d", atomic.LoadInt32(&callCount), cap(rq.OutCh))
+ th.Equal(rq.PanicVal, nil)
th.Timeout()
default:
time.Sleep(time.Millisecond)
}
}
- th.Equal(atomic.LoadInt32(&callCount), int32(cap(rq.outCh)))
+ th.Equal(atomic.LoadInt32(&callCount), int32(cap(rq.OutCh)))
}
func TestRequest_EventFnQueueOverflowPanicsWithNoLogger(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
+ var log bytes.Buffer
+ rq.Jaws.Logger = slog.New(slog.NewTextHandler(&log, nil))
var wait int32
bombItem := &testUi{}
rq.Register(bombItem, func(e *Element, evt what.What, val string) error {
- time.Sleep(time.Millisecond * time.Duration(atomic.AddInt32(&wait, 1)))
+ delay := 1 << atomic.AddInt32(&wait, 1)
+ select {
+ case <-t.Context().Done():
+ case <-time.NewTimer(time.Millisecond * time.Duration(min(1000, delay))).C:
+ }
return nil
})
- rq.expectPanic = true
- rq.jw.Logger = nil
+ rq.ExpectPanic = true
+ rq.Jaws.Logger = nil
jid := jidForTag(rq.Request, bombItem)
for {
select {
- case <-rq.doneCh:
- th.True(rq.panicked)
- th.True(strings.Contains(rq.panicVal.(error).Error(), "eventCallCh is full sending"))
+ case <-rq.DoneCh:
+ if t.Context().Err() == nil {
+ th.True(rq.Panicked)
+ txt := fmt.Sprint(rq.PanicVal)
+ if !strings.Contains(txt, "eventCallCh is full sending") {
+ t.Log(log.String())
+ t.Errorf("unexpected panic value %q", txt)
+ }
+ } else {
+ t.Log(log.String())
+ t.Error("test timed out before event channel full")
+ }
return
case <-th.C:
th.Timeout()
- case rq.inCh <- wsMsg{Jid: jid, What: what.Input}:
+ case rq.InCh <- wsMsg{Jid: jid, What: what.Input}:
}
}
}
func TestRequest_IgnoresIncomingMsgsDuringShutdown(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
+ var log bytes.Buffer
+ rq.Jaws.Logger = slog.New(slog.NewTextHandler(&log, nil))
+
+ waitms := 1000
+ if deadlock.Debug {
+ waitms *= 10
+ }
var spewState int32
var callCount int32
spewItem := &testUi{}
rq.Register(spewItem, func(e *Element, evt what.What, val string) error {
atomic.AddInt32(&callCount, 1)
- if len(rq.outCh) < cap(rq.outCh) {
- rq.jw.Broadcast(Message{Dest: spewItem, What: what.Input})
- } else {
- atomic.StoreInt32(&spewState, 1)
- for atomic.LoadInt32(&spewState) == 1 {
+ if len(rq.OutCh) < cap(rq.OutCh) {
+ rq.Jaws.Broadcast(Message{Dest: spewItem, What: what.Input})
+ return errors.New("chunks")
+ }
+ atomic.StoreInt32(&spewState, 1)
+ for atomic.LoadInt32(&spewState) == 1 {
+ select {
+ case <-t.Context().Done():
+ atomic.StoreInt32(&spewState, 3)
+ default:
time.Sleep(time.Millisecond)
}
}
+ atomic.StoreInt32(&spewState, 3)
return errors.New("chunks")
})
fooItem := &testUi{}
rq.Register(fooItem)
- rq.jw.Broadcast(Message{Dest: spewItem, What: what.Input})
+ rq.Jaws.Broadcast(Message{Dest: spewItem, What: what.Input})
// wait for the event fn to be in hold state
waited := 0
- for waited < 1000 && atomic.LoadInt32(&spewState) == 0 {
+ for waited < waitms && atomic.LoadInt32(&spewState) == 0 {
time.Sleep(time.Millisecond)
waited++
}
th.Equal(atomic.LoadInt32(&spewState), int32(1))
- th.Equal(cap(rq.outCh), len(rq.outCh))
- th.True(waited < 1000)
+ th.Equal(cap(rq.OutCh), len(rq.OutCh))
+ th.True(waited < waitms)
- rq.cancel()
+ rq.cancel(nil)
// rq should now be in shutdown phase draining channels
// while waiting for the event fn to return
- for i := 0; i < cap(rq.outCh)*2; i++ {
+ for i := 0; i < cap(rq.OutCh)*2; i++ {
select {
- case <-rq.doneCh:
+ case <-rq.DoneCh:
th.Fatal()
case <-th.C:
th.Timeout()
@@ -345,8 +380,8 @@ func TestRequest_IgnoresIncomingMsgsDuringShutdown(t *testing.T) {
rq.Jaws.Broadcast(Message{Dest: rq})
}
select {
- case rq.inCh <- wsMsg{}:
- case <-rq.doneCh:
+ case rq.InCh <- wsMsg{}:
+ case <-rq.DoneCh:
th.Fatal()
case <-th.C:
th.Timeout()
@@ -357,14 +392,17 @@ func TestRequest_IgnoresIncomingMsgsDuringShutdown(t *testing.T) {
atomic.StoreInt32(&spewState, 2)
select {
- case <-rq.doneCh:
+ case <-rq.DoneCh:
+ th.True(atomic.LoadInt32(&spewState) == 3)
th.True(atomic.LoadInt32(&callCount) > 1)
case <-th.C:
+ t.Logf("timeout callcount %v, spewState %v", atomic.LoadInt32(&callCount), atomic.LoadInt32(&spewState))
+ t.Log(log.String())
th.Timeout()
}
// log data should contain message that we were unable to deliver error
- th.True(strings.Contains(rq.jw.log.String(), "outboundMsgCh full sending event"))
+ th.True(strings.Contains(log.String(), "outboundMsgCh full sending event"))
}
func TestRequest_Alert(t *testing.T) {
@@ -378,14 +416,14 @@ func TestRequest_Alert(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq1.outCh:
+ case msg := <-rq1.OutCh:
s := msg.Format()
if s != "Alert\t\t\"info\\n\\nnot\\tescaped\"\n" {
t.Errorf("%q", s)
}
}
select {
- case s := <-rq2.outCh:
+ case s := <-rq2.OutCh:
t.Errorf("%q", s)
default:
}
@@ -402,14 +440,14 @@ func TestRequest_Redirect(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq1.outCh:
+ case msg := <-rq1.OutCh:
s := msg.Format()
if s != "Redirect\t\t\"some-url\"\n" {
t.Errorf("%q", s)
}
}
select {
- case s := <-rq2.outCh:
+ case s := <-rq2.OutCh:
t.Errorf("%q", s)
default:
}
@@ -424,7 +462,7 @@ func TestRequest_AlertError(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Alert\t\t\"danger\\n<html>\\nshould-be-escaped\"\n" {
t.Errorf("%q", s)
@@ -466,7 +504,7 @@ func TestRequest_DeleteByTag(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq1.outCh:
+ case msg := <-rq1.OutCh:
s := msg.Format()
if s != "Delete\tJid.1\t\"\"\n" {
t.Errorf("%q", s)
@@ -476,7 +514,7 @@ func TestRequest_DeleteByTag(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq1.outCh:
+ case msg := <-rq1.OutCh:
s := msg.Format()
if s != "Delete\tJid.3\t\"\"\n" {
t.Errorf("%q", s)
@@ -486,7 +524,7 @@ func TestRequest_DeleteByTag(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq2.outCh:
+ case msg := <-rq2.OutCh:
s := msg.Format()
if s != "Delete\tJid.4\t\"\"\n" {
t.Errorf("%q", s)
@@ -496,7 +534,7 @@ func TestRequest_DeleteByTag(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq2.outCh:
+ case msg := <-rq2.OutCh:
s := msg.Format()
if s != "Delete\tJid.6\t\"\"\n" {
t.Errorf("%q", s)
@@ -519,7 +557,7 @@ func TestRequest_HTMLIdBroadcast(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq1.outCh:
+ case msg := <-rq1.OutCh:
s := msg.Format()
if s != "Inner\tfooId\t\"inner\"\n" {
t.Errorf("%q", s)
@@ -528,7 +566,7 @@ func TestRequest_HTMLIdBroadcast(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq2.outCh:
+ case msg := <-rq2.OutCh:
s := msg.Format()
if s != "Inner\tfooId\t\"inner\"\n" {
t.Errorf("%q", s)
@@ -545,7 +583,7 @@ func jidForTag(rq *Request, tag any) jid.Jid {
func TestRequest_ConnectFn(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
th.Equal(rq.GetConnectFn(), nil)
@@ -561,7 +599,7 @@ func TestRequest_ConnectFn(t *testing.T) {
func TestRequest_Dirty(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
tss1 := &testUi{s: "foo1"}
@@ -588,8 +626,10 @@ func TestRequest_Dirty(t *testing.T) {
func TestRequest_UpdatePanicLogs(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
+ var log bytes.Buffer
+ rq.Jaws.Logger = slog.New(slog.NewTextHandler(&log, nil))
tss := &testUi{
updateFn: func(e *Element) {
@@ -600,9 +640,9 @@ func TestRequest_UpdatePanicLogs(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case <-rq.doneCh:
+ case <-rq.DoneCh:
}
- if s := rq.jw.log.String(); !strings.Contains(s, "wildpanic") {
+ if s := log.String(); !strings.Contains(s, "wildpanic") {
t.Error(s)
}
}
@@ -610,7 +650,7 @@ func TestRequest_UpdatePanicLogs(t *testing.T) {
func TestRequest_IncomingRemove(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
tss := newTestSetter("")
@@ -619,17 +659,17 @@ func TestRequest_IncomingRemove(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case rq.inCh <- wsMsg{What: what.Remove, Data: "Jid.1"}:
+ case rq.InCh <- wsMsg{What: what.Remove, Data: "Jid.1"}:
}
- elem := rq.getElementByJid(1)
+ elem := rq.GetElementByJid(1)
for elem != nil {
select {
case <-th.C:
th.Timeout()
default:
time.Sleep(time.Millisecond)
- elem = rq.getElementByJid(1)
+ elem = rq.GetElementByJid(1)
}
}
}
@@ -637,7 +677,7 @@ func TestRequest_IncomingRemove(t *testing.T) {
func TestRequest_IncomingClick(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
tjc1 := &testJawsClick{
@@ -656,7 +696,7 @@ func TestRequest_IncomingClick(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case rq.inCh <- wsMsg{What: what.Click, Data: "name\tJid.1\tJid.2"}:
+ case rq.InCh <- wsMsg{What: what.Click, Data: "name\tJid.1\tJid.2"}:
}
select {
@@ -676,7 +716,7 @@ func TestRequest_IncomingClick(t *testing.T) {
func TestRequest_CustomErrors(t *testing.T) {
th := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
cause := newErrNoWebSocketRequest(rq.Request)
err := newErrPendingCancelledLocked(rq.Request, cause)
diff --git a/jaws/sessioner_test.go b/jaws/sessioner_test.go
index 7bf0149..6721718 100644
--- a/jaws/sessioner_test.go
+++ b/jaws/sessioner_test.go
@@ -8,7 +8,7 @@ import (
func TestJaws_Session(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
dot := Tag("123")
diff --git a/jaws/template_test.go b/jaws/template_test.go
index a0a056c..111de17 100644
--- a/jaws/template_test.go
+++ b/jaws/template_test.go
@@ -7,7 +7,7 @@ import (
)
func TestTemplate_Missing(t *testing.T) {
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
err := rq.Template("missingtemplate", nil, nil)
@@ -21,7 +21,7 @@ func TestTemplate_Missing(t *testing.T) {
func TestTemplate_String(t *testing.T) {
is := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
dot := 123
@@ -31,7 +31,7 @@ func TestTemplate_String(t *testing.T) {
}
func TestTemplate_Calls_Dot_Updater(t *testing.T) {
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
dot := &testUi{}
tmpl := NewTemplate("testtemplate", dot)
diff --git a/jaws/testhelper_test.go b/jaws/testhelper_test.go
index f02ae4a..3b1746b 100644
--- a/jaws/testhelper_test.go
+++ b/jaws/testhelper_test.go
@@ -1,20 +1,70 @@
package jaws
import (
+ "bytes"
+ "fmt"
"reflect"
+ "regexp"
+ "runtime"
+ "sort"
"testing"
"time"
+
+ "github.com/linkdata/deadlock"
)
+func printGoroutineOrigins(t *testing.T) {
+ t.Helper()
+ buf := make([]byte, 1<<20)
+ n := runtime.Stack(buf, true)
+ buf = buf[:n]
+
+ lines := bytes.Split(buf, []byte("\n"))
+ re := regexp.MustCompile(`\t(.*?):(\d+) \+0x`)
+ counts := make(map[string]int)
+
+ for _, line := range lines {
+ m := re.FindSubmatch(line)
+ if len(m) == 3 {
+ loc := fmt.Sprintf("%s:%s", m[1], m[2])
+ counts[loc]++
+ }
+ }
+
+ // Convert to slice for sorting
+ type pair struct {
+ loc string
+ count int
+ }
+ var items []pair
+ for k, v := range counts {
+ if v > 1 { // omit entries with only one goroutine
+ items = append(items, pair{k, v})
+ }
+ }
+
+ sort.Slice(items, func(i, j int) bool {
+ return items[i].count > items[j].count
+ })
+
+ for _, item := range items {
+ t.Logf("%-50s %4d goroutines\n", item.loc, item.count)
+ }
+}
+
type testHelper struct {
*time.Timer
*testing.T
}
func newTestHelper(t *testing.T) (th *testHelper) {
+ seconds := 3
+ if deadlock.Debug {
+ seconds *= 10
+ }
th = &testHelper{
T: t,
- Timer: time.NewTimer(time.Second * 3),
+ Timer: time.NewTimer(time.Second * time.Duration(seconds)),
}
t.Cleanup(th.Cleanup)
return
@@ -47,7 +97,8 @@ func (th *testHelper) NoErr(err error) {
func (th *testHelper) Timeout() {
th.Helper()
- th.Fatal("timeout")
+ printGoroutineOrigins(th.T)
+ th.Fatalf("timeout")
}
func Test_testHelper(t *testing.T) {
diff --git a/jaws/testjaws_test.go b/jaws/testjaws_test.go
index 91f44b4..46f4d63 100644
--- a/jaws/testjaws_test.go
+++ b/jaws/testjaws_test.go
@@ -2,12 +2,10 @@ package jaws
import (
"bytes"
- "context"
"html/template"
"log/slog"
"net/http"
- "net/http/httptest"
- "strings"
+ "testing"
"time"
)
@@ -37,98 +35,15 @@ func newTestJaws() (tj *testJaws) {
return
}
-type testRequest struct {
- hr *http.Request
- rr *httptest.ResponseRecorder
- jw *testJaws
- readyCh chan struct{}
- doneCh chan struct{}
- inCh chan wsMsg
- outCh chan wsMsg
- bcastCh chan Message
- ctx context.Context
- cancel context.CancelFunc
- expectPanic bool
- panicked bool
- panicVal any
- *Request
- RequestWriter
+func (tj *testJaws) newRequest(hr *http.Request) (tr *TestRequest) {
+ return NewTestRequest(tj.Jaws, hr)
}
-func (tj *testJaws) newRequest(hr *http.Request) (tr *testRequest) {
- if hr == nil {
- hr = httptest.NewRequest(http.MethodGet, "/", nil)
- }
- ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
- hr = hr.WithContext(ctx)
- rr := httptest.NewRecorder()
- rr.Body = &bytes.Buffer{}
- rq := tj.NewRequest(hr)
- if rq == nil || tj.UseRequest(rq.JawsKey, hr) != rq {
- panic("failed to create or use jaws.Request")
- }
- bcastCh := tj.subscribe(rq, 64)
- for i := 0; i <= cap(tj.subCh); i++ {
- tj.subCh <- subscription{} // ensure subscription is processed
- }
-
- tr = &testRequest{
- hr: hr,
- rr: rr,
- jw: tj,
- readyCh: make(chan struct{}),
- doneCh: make(chan struct{}),
- inCh: make(chan wsMsg),
- outCh: make(chan wsMsg, cap(bcastCh)),
- bcastCh: bcastCh,
- ctx: ctx,
- cancel: cancel,
- Request: rq,
- RequestWriter: rq.Writer(rr),
- }
-
- go func() {
- defer func() {
- if tr.expectPanic {
- if tr.panicVal = recover(); tr.panicVal != nil {
- tr.panicked = true
- }
- }
- close(tr.doneCh)
- }()
- close(tr.readyCh)
- tr.process(tr.bcastCh, tr.inCh, tr.outCh) // usubs from bcase, closes outCh
- tr.jw.recycle(tr.Request)
- }()
-
- return
-}
-
-func (tr *testRequest) BodyString() string {
- return tr.rr.Body.String()
-}
-
-func (tr *testRequest) BodyHTML() template.HTML {
- return template.HTML(strings.TrimSpace(tr.BodyString()))
-}
-
-func (tr *testRequest) Close() {
- tr.cancel()
- tr.jw.Close()
-}
-
-func (tr *testRequest) Write(buf []byte) (int, error) {
- return tr.rr.Write(buf)
-}
-
-func (tr *testRequest) getElementByJid(jid Jid) (e *Element) {
- tr.Request.mu.RLock()
- e = tr.Request.getElementByJidLocked(jid)
- tr.Request.mu.RUnlock()
- return
-}
-
-func newTestRequest() (tr *testRequest) {
+func newTestRequest(t *testing.T) (tr *TestRequest) {
tj := newTestJaws()
- return tj.newRequest(nil)
+ if t != nil {
+ t.Helper()
+ t.Cleanup(tj.Close)
+ }
+ return NewTestRequest(tj.Jaws, nil)
}
diff --git a/jaws/testpage_test.go b/jaws/testpage_test.go
index f0888e3..82eef84 100644
--- a/jaws/testpage_test.go
+++ b/jaws/testpage_test.go
@@ -72,9 +72,9 @@ type testPage struct {
TheDot any
}
-func newTestPage(tr *testRequest) *testPage {
+func newTestPage(tr *TestRequest) *testPage {
testDate, _ := time.Parse(ISO8601, "1901-02-03")
- tr.jw.AddTemplateLookuper(template.Must(template.New("nested").Parse(testPageNestedTmplText)))
+ tr.Jaws.AddTemplateLookuper(template.Must(template.New("nested").Parse(testPageNestedTmplText)))
tmpl := template.Must(template.New("normal").Parse(testPageTmplText))
tp := &testPage{
diff --git a/jaws/testrequest.go b/jaws/testrequest.go
new file mode 100644
index 0000000..4dbf659
--- /dev/null
+++ b/jaws/testrequest.go
@@ -0,0 +1,81 @@
+package jaws
+
+import (
+ "bytes"
+ "html/template"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+)
+
+type TestRequest struct {
+ *Request
+ *httptest.ResponseRecorder
+ RequestWriter
+ ReadyCh chan struct{}
+ DoneCh chan struct{}
+ InCh chan wsMsg
+ OutCh chan wsMsg
+ BcastCh chan Message
+ ExpectPanic bool
+ Panicked bool
+ PanicVal any
+}
+
+// NewTestRequest creates a TestRequest for use when testing.
+// Passing nil for hr will create a "GET /" request with no body.
+//
+// If NewRequest() or UseRequest() fails, it returns nil.
+func NewTestRequest(jw *Jaws, hr *http.Request) (tr *TestRequest) {
+ if hr == nil {
+ hr = httptest.NewRequest(http.MethodGet, "/", nil)
+ }
+ rr := httptest.NewRecorder()
+ rr.Body = &bytes.Buffer{}
+ rq := jw.NewRequest(hr)
+ if rq != nil && jw.UseRequest(rq.JawsKey, hr) == rq {
+ bcastCh := jw.subscribe(rq, 64)
+ for i := 0; i <= cap(jw.subCh); i++ {
+ jw.subCh <- subscription{} // ensure subscription is processed
+ }
+
+ tr = &TestRequest{
+ ReadyCh: make(chan struct{}),
+ DoneCh: make(chan struct{}),
+ InCh: make(chan wsMsg),
+ OutCh: make(chan wsMsg, cap(bcastCh)),
+ BcastCh: bcastCh,
+ Request: rq,
+ RequestWriter: rq.Writer(rr),
+ ResponseRecorder: rr,
+ }
+
+ go func() {
+ defer func() {
+ if tr.ExpectPanic {
+ if tr.PanicVal = recover(); tr.PanicVal != nil {
+ tr.Panicked = true
+ }
+ }
+ close(tr.DoneCh)
+ }()
+ close(tr.ReadyCh)
+ tr.process(tr.BcastCh, tr.InCh, tr.OutCh) // unsubs from bcast, closes outCh
+ jw.recycle(tr.Request)
+ }()
+ }
+
+ return
+}
+
+func (tr *TestRequest) Close() {
+ close(tr.InCh)
+}
+
+func (tr *TestRequest) BodyString() string {
+ return strings.TrimSpace(tr.Body.String())
+}
+
+func (tr *TestRequest) BodyHTML() template.HTML {
+ return template.HTML(tr.BodyString()) /* #nosec G203 */
+}
diff --git a/jaws/ui_test.go b/jaws/ui_test.go
index 494268f..cc79a5a 100644
--- a/jaws/ui_test.go
+++ b/jaws/ui_test.go
@@ -37,7 +37,7 @@ func TestRequest_NewElement_DebugPanicsIfNotComparable(t *testing.T) {
}()
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
rq.NewElement(notHashableUI)
@@ -51,7 +51,7 @@ func (testStringer) String() string { return "foo" }
func TestRequest_JawsRender_DebugOutput(t *testing.T) {
is := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
rq.Jaws.Debug = true
@@ -72,7 +72,7 @@ func TestRequest_JawsRender_DebugOutput(t *testing.T) {
func TestRequest_InsideTemplate(t *testing.T) {
var buf bytes.Buffer
- tr := newTestRequest()
+ tr := newTestRequest(t)
defer tr.Close()
tp := newTestPage(tr)
err := tp.render(&buf)
@@ -85,7 +85,7 @@ func TestRequest_InsideTemplate(t *testing.T) {
}
func BenchmarkPageRender(b *testing.B) {
- tr := newTestRequest()
+ tr := newTestRequest(nil)
defer tr.Close()
tp := newTestPage(tr)
@@ -97,7 +97,7 @@ func BenchmarkPageRender(b *testing.B) {
}
func BenchmarkPageUpdate(b *testing.B) {
- tr := newTestRequest()
+ tr := newTestRequest(nil)
defer tr.Close()
tp := newTestPage(tr)
var buf bytes.Buffer
diff --git a/jaws/uia_test.go b/jaws/uia_test.go
index 801d2ba..d1fbd2c 100644
--- a/jaws/uia_test.go
+++ b/jaws/uia_test.go
@@ -52,7 +52,7 @@ func TestRequest_A(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
rq.A(tt.args.innerHTML, tt.args.params...)
if got := rq.BodyHTML(); !reflect.DeepEqual(got, tt.want) {
diff --git a/jaws/uibutton_test.go b/jaws/uibutton_test.go
index 594cc29..fb2e780 100644
--- a/jaws/uibutton_test.go
+++ b/jaws/uibutton_test.go
@@ -6,7 +6,7 @@ import (
func TestRequest_Button(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
want := ``
rq.Button("inner")
diff --git a/jaws/uicheckbox_test.go b/jaws/uicheckbox_test.go
index 46edd29..b537889 100644
--- a/jaws/uicheckbox_test.go
+++ b/jaws/uicheckbox_test.go
@@ -10,7 +10,7 @@ import (
func TestRequest_Checkbox(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
ts := newTestSetter(true)
@@ -21,7 +21,7 @@ func TestRequest_Checkbox(t *testing.T) {
}
val := false
- rq.inCh <- wsMsg{Data: "false", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "false", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
@@ -31,7 +31,7 @@ func TestRequest_Checkbox(t *testing.T) {
t.Error(ts.Get(), "!=", val)
}
select {
- case s := <-rq.outCh:
+ case s := <-rq.OutCh:
t.Errorf("%q", s)
default:
}
@@ -42,7 +42,7 @@ func TestRequest_Checkbox(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Value\tJid.1\t\"true\"\n" {
t.Errorf("%q", s)
@@ -55,11 +55,11 @@ func TestRequest_Checkbox(t *testing.T) {
t.Error("SetCount", ts.SetCount())
}
- rq.inCh <- wsMsg{Data: "omg", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "omg", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Alert\t\t\"danger\\nstrconv.ParseBool: parsing "omg": invalid syntax\"\n" {
t.Errorf("wrong Alert: %q", s)
@@ -67,11 +67,11 @@ func TestRequest_Checkbox(t *testing.T) {
}
ts.err = errors.New("meh")
- rq.inCh <- wsMsg{Data: "true", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "true", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Alert\t\t\"danger\\nmeh\"\n" {
t.Errorf("wrong Alert: %q", s)
diff --git a/jaws/uicontainer_test.go b/jaws/uicontainer_test.go
index 0bd3d01..4573052 100644
--- a/jaws/uicontainer_test.go
+++ b/jaws/uicontainer_test.go
@@ -58,7 +58,7 @@ func TestRequest_Container(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
err := rq.Container("div", tt.args.c, tt.args.params...)
if err != nil {
diff --git a/jaws/uidate_test.go b/jaws/uidate_test.go
index c861411..de30cab 100644
--- a/jaws/uidate_test.go
+++ b/jaws/uidate_test.go
@@ -12,7 +12,7 @@ import (
func TestRequest_Date(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
ts := newTestSetter(time.Now())
@@ -23,7 +23,7 @@ func TestRequest_Date(t *testing.T) {
}
val, _ := time.Parse(ISO8601, "1970-02-03")
- rq.inCh <- wsMsg{Data: val.Format(ISO8601), Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: val.Format(ISO8601), Jid: 1, What: what.Input}
tmr := time.NewTimer(testTimeout)
defer tmr.Stop()
select {
@@ -35,7 +35,7 @@ func TestRequest_Date(t *testing.T) {
t.Error(ts.Get(), "!=", val)
}
select {
- case s := <-rq.outCh:
+ case s := <-rq.OutCh:
t.Errorf("%q", s)
default:
}
@@ -46,7 +46,7 @@ func TestRequest_Date(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != fmt.Sprintf("Value\tJid.1\t\"%s\"\n", val.Format(ISO8601)) {
t.Error("wrong Value")
@@ -59,11 +59,11 @@ func TestRequest_Date(t *testing.T) {
t.Error("SetCount", ts.SetCount())
}
- rq.inCh <- wsMsg{Data: "omg", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "omg", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Alert\t\t\"danger\\nparsing time "omg" as "2006-01-02": cannot parse "omg" as "2006"\"\n" {
t.Errorf("wrong Alert: %q", s)
@@ -71,11 +71,11 @@ func TestRequest_Date(t *testing.T) {
}
ts.err = errors.New("meh")
- rq.inCh <- wsMsg{Data: val.Format(ISO8601), Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: val.Format(ISO8601), Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Alert\t\t\"danger\\nmeh\"\n" {
t.Errorf("wrong Alert: %q", s)
diff --git a/jaws/uidiv_test.go b/jaws/uidiv_test.go
index 48c4fb7..95ddc02 100644
--- a/jaws/uidiv_test.go
+++ b/jaws/uidiv_test.go
@@ -6,7 +6,7 @@ import (
func TestRequest_Div(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
want := `inner
`
rq.Div("inner")
diff --git a/jaws/uiimg_test.go b/jaws/uiimg_test.go
index edeecf3..341c7cf 100644
--- a/jaws/uiimg_test.go
+++ b/jaws/uiimg_test.go
@@ -8,7 +8,7 @@ import (
func TestRequest_Img(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
ts := newTestSetter("image.png")
@@ -25,7 +25,7 @@ func TestRequest_Img(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "SAttr\tJid.1\t\"src\\nimage2.jpg\"\n" {
t.Error(strconv.Quote(s))
diff --git a/jaws/uilabel_test.go b/jaws/uilabel_test.go
index 0bc0fa8..db60b72 100644
--- a/jaws/uilabel_test.go
+++ b/jaws/uilabel_test.go
@@ -6,7 +6,7 @@ import (
func TestRequest_Label(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
want := ``
rq.Label("inner")
diff --git a/jaws/uili_test.go b/jaws/uili_test.go
index 0c1c675..979f143 100644
--- a/jaws/uili_test.go
+++ b/jaws/uili_test.go
@@ -6,7 +6,7 @@ import (
func TestRequest_Li(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
want := `inner`
rq.Li("inner")
diff --git a/jaws/uinumber_test.go b/jaws/uinumber_test.go
index e45cd92..a93a675 100644
--- a/jaws/uinumber_test.go
+++ b/jaws/uinumber_test.go
@@ -11,7 +11,7 @@ import (
func TestRequest_Number(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
ts := newTestSetter(float64(1.2))
@@ -22,7 +22,7 @@ func TestRequest_Number(t *testing.T) {
}
val := float64(2.3)
- rq.inCh <- wsMsg{Data: fmt.Sprint(val), Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: fmt.Sprint(val), Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
@@ -32,7 +32,7 @@ func TestRequest_Number(t *testing.T) {
t.Error(ts.Get(), "!=", val)
}
select {
- case s := <-rq.outCh:
+ case s := <-rq.OutCh:
t.Errorf("%q", s)
default:
}
@@ -43,7 +43,7 @@ func TestRequest_Number(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != fmt.Sprintf("Value\tJid.1\t\"%v\"\n", val) {
t.Error("wrong Value")
@@ -56,11 +56,11 @@ func TestRequest_Number(t *testing.T) {
t.Error("SetCount", ts.SetCount())
}
- rq.inCh <- wsMsg{Data: "omg", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "omg", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Alert\t\t\"danger\\nstrconv.ParseFloat: parsing "omg": invalid syntax\"\n" {
t.Errorf("wrong Alert: %q", s)
@@ -68,11 +68,11 @@ func TestRequest_Number(t *testing.T) {
}
ts.err = errors.New("meh")
- rq.inCh <- wsMsg{Data: fmt.Sprint(val), Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: fmt.Sprint(val), Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Alert\t\t\"danger\\nmeh\"\n" {
t.Errorf("wrong Alert: %q", s)
diff --git a/jaws/uioption_test.go b/jaws/uioption_test.go
index 48f5c89..363e5e3 100644
--- a/jaws/uioption_test.go
+++ b/jaws/uioption_test.go
@@ -8,7 +8,7 @@ import (
func TestUiOption(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
nba := NewNamedBoolArray()
@@ -30,7 +30,7 @@ func TestUiOption(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "RAttr\tJid.1\t\"selected\"\n" {
t.Errorf("%q", s)
@@ -42,7 +42,7 @@ func TestUiOption(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "SAttr\tJid.1\t\"selected\\n\"\n" {
t.Errorf("%q", s)
diff --git a/jaws/uipassword_test.go b/jaws/uipassword_test.go
index f8b9ea3..8e42e7d 100644
--- a/jaws/uipassword_test.go
+++ b/jaws/uipassword_test.go
@@ -6,7 +6,7 @@ import (
func TestRequest_Password(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
ts := newTestSetter("")
want := ``
diff --git a/jaws/uiradio_test.go b/jaws/uiradio_test.go
index 1e0c715..c0c4039 100644
--- a/jaws/uiradio_test.go
+++ b/jaws/uiradio_test.go
@@ -6,7 +6,7 @@ import (
func TestRequest_Radio(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
ts := newTestSetter(true)
diff --git a/jaws/uiradiogroup_test.go b/jaws/uiradiogroup_test.go
index d088e9d..50e9c17 100644
--- a/jaws/uiradiogroup_test.go
+++ b/jaws/uiradiogroup_test.go
@@ -6,7 +6,7 @@ import (
func TestRequest_RadioGroup(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
nba := NewNamedBoolArray()
diff --git a/jaws/uirange_test.go b/jaws/uirange_test.go
index 59e9e9a..8e93bee 100644
--- a/jaws/uirange_test.go
+++ b/jaws/uirange_test.go
@@ -10,7 +10,7 @@ import (
func TestRequest_Range(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
ts := newTestSetter(float64(1))
@@ -19,7 +19,7 @@ func TestRequest_Range(t *testing.T) {
if got := rq.BodyString(); got != want {
t.Errorf("Request.Range() = %q, want %q", got, want)
}
- rq.inCh <- wsMsg{Data: "2.1", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "2.1", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
@@ -29,7 +29,7 @@ func TestRequest_Range(t *testing.T) {
t.Error(ts.Get())
}
select {
- case s := <-rq.outCh:
+ case s := <-rq.OutCh:
t.Errorf("%q", s)
default:
}
@@ -38,7 +38,7 @@ func TestRequest_Range(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Value\tJid.1\t\"2.3\"\n" {
t.Error(s)
@@ -52,11 +52,11 @@ func TestRequest_Range(t *testing.T) {
}
ts.err = errors.New("meh")
- rq.inCh <- wsMsg{Data: "3.4", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "3.4", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Alert\t\t\"danger\\nmeh\"\n" {
t.Errorf("wrong Alert: %q", s)
diff --git a/jaws/uiregister_test.go b/jaws/uiregister_test.go
index d29faf3..96242d9 100644
--- a/jaws/uiregister_test.go
+++ b/jaws/uiregister_test.go
@@ -8,12 +8,12 @@ import (
func TestRequestWriter_Register(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
item := &testUi{}
jid := rq.Register(item)
th.Equal(jid, Jid(1))
th.Equal(atomic.LoadInt32(&item.updateCalled), int32(1))
- e := rq.getElementByJid(jid)
+ e := rq.GetElementByJid(jid)
th.NoErr(e.JawsRender(nil, nil))
}
diff --git a/jaws/uiselect_test.go b/jaws/uiselect_test.go
index aab2df2..521fcde 100644
--- a/jaws/uiselect_test.go
+++ b/jaws/uiselect_test.go
@@ -32,7 +32,7 @@ func (ts *testNamedBoolArray) JawsSet(e *Element, val string) (err error) {
func TestRequest_Select(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
a := &testNamedBoolArray{
@@ -56,7 +56,7 @@ func TestRequest_Select(t *testing.T) {
t.Error("2 is checked")
}
- rq.inCh <- wsMsg{Data: "2", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "2", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
@@ -71,9 +71,13 @@ func TestRequest_Select(t *testing.T) {
}
select {
- case s := <-rq.outCh:
- t.Errorf("%q", s)
- default:
+ case <-th.C:
+ th.Timeout()
+ case msg := <-rq.OutCh:
+ s := msg.Format()
+ if s != "Value\tJid.1\t\"2\"\n" {
+ t.Errorf("wrong Value %q", s)
+ }
}
a.Set("2", false)
@@ -82,10 +86,10 @@ func TestRequest_Select(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Value\tJid.1\t\"\"\n" {
- t.Error("wrong Value")
+ t.Errorf("wrong Value %q", s)
}
}
@@ -96,15 +100,23 @@ func TestRequest_Select(t *testing.T) {
t.Error("2 is checked")
}
+ a.mu.Lock()
a.err = errors.New("meh")
- rq.inCh <- wsMsg{Data: "1", Jid: 1, What: what.Input}
+ a.mu.Unlock()
+ rq.InCh <- wsMsg{Data: "1", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Alert\t\t\"danger\\nmeh\"\n" {
t.Errorf("wrong Alert: %q", s)
+ select {
+ case msg := <-rq.OutCh:
+ s := msg.Format()
+ t.Errorf("queued msg: %q", s)
+ default:
+ }
}
}
diff --git a/jaws/uispan_test.go b/jaws/uispan_test.go
index 41d8ca6..c16551a 100644
--- a/jaws/uispan_test.go
+++ b/jaws/uispan_test.go
@@ -6,7 +6,7 @@ import (
func TestRequest_Span(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
want := `inner`
rq.Span("inner")
diff --git a/jaws/uitbody_test.go b/jaws/uitbody_test.go
index cbd2db7..b64d4fc 100644
--- a/jaws/uitbody_test.go
+++ b/jaws/uitbody_test.go
@@ -6,7 +6,7 @@ import (
func TestRequest_Tbody(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
want := ``
rq.Tbody(&testContainer{})
diff --git a/jaws/uitd_test.go b/jaws/uitd_test.go
index 0bc350b..952effc 100644
--- a/jaws/uitd_test.go
+++ b/jaws/uitd_test.go
@@ -6,7 +6,7 @@ import (
func TestRequest_Td(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
want := `inner | `
rq.Td("inner")
diff --git a/jaws/uitemplate_test.go b/jaws/uitemplate_test.go
index e614c31..a8c209c 100644
--- a/jaws/uitemplate_test.go
+++ b/jaws/uitemplate_test.go
@@ -1,7 +1,9 @@
package jaws
import (
+ "bytes"
"html/template"
+ "log/slog"
"reflect"
"strings"
"testing"
@@ -16,15 +18,17 @@ func TestRequest_TemplateMissingJid(t *testing.T) {
t.Skip("debug tag not set")
}
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
- rq.jw.AddTemplateLookuper(template.Must(template.New("badtesttemplate").Parse(`{{with $.Dot}}{{.}}
{{end}}`)))
+ var log bytes.Buffer
+ rq.Jaws.Logger = slog.New(slog.NewTextHandler(&log, nil))
+ rq.Jaws.AddTemplateLookuper(template.Must(template.New("badtesttemplate").Parse(`{{with $.Dot}}{{.}}
{{end}}`)))
if e := rq.Template("badtesttemplate", nil, nil); e != nil {
t.Error(e)
}
- if !strings.Contains(rq.jw.log.String(), "WARN") || !strings.Contains(rq.jw.log.String(), "badtesttemplate") {
+ if !strings.Contains(log.String(), "WARN") || !strings.Contains(log.String(), "badtesttemplate") {
t.Error("expected WARN in the log")
- t.Log(rq.jw.log.String())
+ t.Log(log.String())
}
}
@@ -33,15 +37,17 @@ func TestRequest_TemplateJidInsideIf(t *testing.T) {
t.Skip("debug tag not set")
}
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
- rq.jw.AddTemplateLookuper(template.Must(template.New("iftesttemplate").Parse(`{{with $.Dot}}{{if true}}{{.}}
{{end}}{{end}}`)))
+ var log bytes.Buffer
+ rq.Jaws.Logger = slog.New(slog.NewTextHandler(&log, nil))
+ rq.Jaws.AddTemplateLookuper(template.Must(template.New("iftesttemplate").Parse(`{{with $.Dot}}{{if true}}{{.}}
{{end}}{{end}}`)))
if e := rq.Template("iftesttemplate", nil, nil); e != nil {
t.Error(e)
}
- if strings.Contains(rq.jw.log.String(), "WARN") && strings.Contains(rq.jw.log.String(), "iftesttemplate") {
+ if strings.Contains(log.String(), "WARN") && strings.Contains(log.String(), "iftesttemplate") {
t.Error("found WARN in the log")
- t.Log(rq.jw.log.String())
+ t.Log(log.String())
}
}
@@ -50,15 +56,17 @@ func TestRequest_TemplateMissingJidButHasHTMLTag(t *testing.T) {
t.Skip("debug tag not set")
}
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
- rq.jw.AddTemplateLookuper(template.Must(template.New("badtesttemplate").Parse(`{{with $.Dot}}{{.}}
{{end}}`)))
+ var log bytes.Buffer
+ rq.Jaws.Logger = slog.New(slog.NewTextHandler(&log, nil))
+ rq.Jaws.AddTemplateLookuper(template.Must(template.New("badtesttemplate").Parse(`{{with $.Dot}}{{.}}
{{end}}`)))
if e := rq.Template("badtesttemplate", nil, nil); e != nil {
t.Error(e)
}
- if strings.Contains(rq.jw.log.String(), "WARN") {
+ if strings.Contains(log.String(), "WARN") {
t.Error("expected no WARN in the log")
- t.Log(rq.jw.log.String())
+ t.Log(log.String())
}
}
@@ -106,7 +114,7 @@ func TestRequest_Template(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
if tt.errtxt != "" {
defer func() {
@@ -155,15 +163,15 @@ var _ ClickHandler = &templateDot{}
func TestRequest_Template_Event(t *testing.T) {
is := newTestHelper(t)
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
dot := &templateDot{clickedCh: make(chan struct{})}
rq.Template("testtemplate", dot)
- rq.jw.Broadcast(Message{
+ rq.Jaws.Broadcast(Message{
Dest: dot,
What: what.Update,
})
- rq.jw.Broadcast(Message{
+ rq.Jaws.Broadcast(Message{
Dest: dot,
What: what.Click,
Data: "foo",
diff --git a/jaws/uitext_test.go b/jaws/uitext_test.go
index 0170ce6..1726381 100644
--- a/jaws/uitext_test.go
+++ b/jaws/uitext_test.go
@@ -10,7 +10,7 @@ import (
func TestRequest_Text(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
ss := newTestSetter("foo")
@@ -19,7 +19,7 @@ func TestRequest_Text(t *testing.T) {
if got := rq.BodyString(); got != want {
t.Errorf("Request.Text() = %q, want %q", got, want)
}
- rq.inCh <- wsMsg{Data: "bar", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "bar", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
@@ -29,7 +29,7 @@ func TestRequest_Text(t *testing.T) {
t.Error(ss.Get())
}
select {
- case s := <-rq.outCh:
+ case s := <-rq.OutCh:
t.Errorf("%q", s)
default:
}
@@ -38,7 +38,7 @@ func TestRequest_Text(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Value\tJid.1\t\"quux\"\n" {
t.Error("wrong Value")
@@ -52,11 +52,11 @@ func TestRequest_Text(t *testing.T) {
}
ss.err = errors.New("meh")
- rq.inCh <- wsMsg{Data: "omg", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "omg", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Alert\t\t\"danger\\nmeh\"\n" {
t.Errorf("wrong Alert: %q", s)
diff --git a/jaws/uitextarea_test.go b/jaws/uitextarea_test.go
index 0c78540..fe4ef77 100644
--- a/jaws/uitextarea_test.go
+++ b/jaws/uitextarea_test.go
@@ -9,7 +9,7 @@ import (
func TestRequest_Textarea(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
ss := newTestSetter("foo")
@@ -18,7 +18,7 @@ func TestRequest_Textarea(t *testing.T) {
if got := rq.BodyString(); got != want {
t.Errorf("Request.Textarea() = %q, want %q", got, want)
}
- rq.inCh <- wsMsg{Data: "bar", Jid: 1, What: what.Input}
+ rq.InCh <- wsMsg{Data: "bar", Jid: 1, What: what.Input}
select {
case <-th.C:
th.Timeout()
@@ -28,7 +28,7 @@ func TestRequest_Textarea(t *testing.T) {
t.Fail()
}
select {
- case s := <-rq.outCh:
+ case s := <-rq.OutCh:
t.Errorf("%q", s)
default:
}
@@ -37,7 +37,7 @@ func TestRequest_Textarea(t *testing.T) {
select {
case <-th.C:
th.Timeout()
- case msg := <-rq.outCh:
+ case msg := <-rq.OutCh:
s := msg.Format()
if s != "Value\tJid.1\t\"quux\"\n" {
t.Fail()
diff --git a/jaws/uitr_test.go b/jaws/uitr_test.go
index 3e87fad..1be3f27 100644
--- a/jaws/uitr_test.go
+++ b/jaws/uitr_test.go
@@ -6,7 +6,7 @@ import (
func TestRequest_Tr(t *testing.T) {
nextJid = 0
- rq := newTestRequest()
+ rq := newTestRequest(t)
defer rq.Close()
want := `inner
`
rq.Tr("inner")
diff --git a/jaws_test.go b/jaws_test.go
index a5602e7..b6e47eb 100644
--- a/jaws_test.go
+++ b/jaws_test.go
@@ -93,45 +93,49 @@ func maybeFatal(t *testing.T, err error) {
}
}
-func TestNewTemplate(t *testing.T) {
- jw, err := jaws.New()
- maybeFatal(t, err)
- defer jw.Close()
-
- jw.AddTemplateLookuper(template.Must(template.New("nested").Parse(testPageNestedTmplText)))
- jw.AddTemplateLookuper(template.Must(template.New("normal").Parse(testPageTmplText)))
-
- hr := httptest.NewRequest(http.MethodGet, "/", nil)
- rq := jw.NewRequest(hr)
- jw.UseRequest(rq.JawsKey, hr)
- var sb strings.Builder
- rqwr := rq.Writer(&sb)
-
- var mu sync.RWMutex
- vbool := true
- vtime, _ := time.Parse(jaws.ISO8601, "1901-02-03")
- vnumber := float64(1.2)
- vstring := "bar"
- nba := jaws.NewNamedBoolArray()
-
- tp := &testPage{
- TheBool: jaws.Bind(&mu, &vbool),
- TheContainer: &testContainer{},
- TheTime: jaws.Bind(&mu, &vtime),
- TheNumber: jaws.Bind(&mu, &vnumber),
- TheString: jaws.Bind(&mu, &vstring),
- TheSelector: nba,
- TheDot: jaws.Tag("dot"),
- }
+var onlyOnce sync.Once
- tmpl := jaws.NewTemplate("normal", tp)
- elem := rq.NewElement(tmpl)
- err = tmpl.JawsRender(elem, rqwr, nil)
- maybeFatal(t, err)
-
- if sb.String() != testPageWant {
- t.Errorf("\n got: %q\nwant: %q\n", sb.String(), testPageWant)
- }
+func TestNewTemplate(t *testing.T) {
+ onlyOnce.Do(func() {
+ jw, err := jaws.New()
+ maybeFatal(t, err)
+ defer jw.Close()
+
+ jw.AddTemplateLookuper(template.Must(template.New("nested").Parse(testPageNestedTmplText)))
+ jw.AddTemplateLookuper(template.Must(template.New("normal").Parse(testPageTmplText)))
+
+ hr := httptest.NewRequest(http.MethodGet, "/", nil)
+ rq := jw.NewRequest(hr)
+ jw.UseRequest(rq.JawsKey, hr)
+ var sb strings.Builder
+ rqwr := rq.Writer(&sb)
+
+ var mu sync.RWMutex
+ vbool := true
+ vtime, _ := time.Parse(jaws.ISO8601, "1901-02-03")
+ vnumber := float64(1.2)
+ vstring := "bar"
+ nba := jaws.NewNamedBoolArray()
+
+ tp := &testPage{
+ TheBool: jaws.Bind(&mu, &vbool),
+ TheContainer: &testContainer{},
+ TheTime: jaws.Bind(&mu, &vtime),
+ TheNumber: jaws.Bind(&mu, &vnumber),
+ TheString: jaws.Bind(&mu, &vstring),
+ TheSelector: nba,
+ TheDot: jaws.Tag("dot"),
+ }
+
+ tmpl := jaws.NewTemplate("normal", tp)
+ elem := rq.NewElement(tmpl)
+ err = tmpl.JawsRender(elem, rqwr, nil)
+ maybeFatal(t, err)
+
+ if s := sb.String(); s != testPageWant {
+ t.Errorf("\n got: %q\nwant: %q\n", s, testPageWant)
+ }
+ })
}
func TestJsVar(t *testing.T) {
@@ -191,5 +195,16 @@ func TestNewUi(t *testing.T) {
jaws.NewUiTd(htmlGetter)
jaws.NewUiText(jaws.Bind(&mu, &vstring))
jaws.NewUiTr(htmlGetter)
+}
+func TestNewTestRequest(t *testing.T) {
+ jw, err := jaws.New()
+ maybeFatal(t, err)
+ defer jw.Close()
+ go jw.Serve()
+ if tr := jaws.NewTestRequest(jw, nil); tr == nil {
+ t.Fatal("got nil")
+ } else {
+ tr.Close()
+ }
}
diff --git a/jawstree/tree.go b/jawstree/tree.go
index 18a2cf4..4c4fa96 100644
--- a/jawstree/tree.go
+++ b/jawstree/tree.go
@@ -3,6 +3,7 @@ package jawstree
import (
"fmt"
"io"
+ "strconv"
"github.com/linkdata/jaws"
)
@@ -37,3 +38,13 @@ func (t *Tree) JawsRender(e *jaws.Element, w io.Writer, params []any) (err error
}
return
}
+
+func (t *Tree) JawsUpdate(elem *jaws.Element) {
+ var b []byte
+ b = append(b, `{"tree":`...)
+ b = strconv.AppendQuote(b, t.id)
+ b = append(b, `,"data":`...)
+ b = t.JsVar.Ptr.marshalJSON(b)
+ b = append(b, `}`...)
+ elem.Jaws.JsCall(t.Tag, "jawstreeSet", string(b))
+}
diff --git a/jawstree/tree_test.go b/jawstree/tree_test.go
index d51685b..cb42f54 100644
--- a/jawstree/tree_test.go
+++ b/jawstree/tree_test.go
@@ -3,7 +3,6 @@ package jawstree
import (
"encoding/json"
"net/http"
- "net/http/httptest"
"os"
"reflect"
"strings"
@@ -25,11 +24,12 @@ func TestTree(t *testing.T) {
maybeError(t, err)
defer jw.Close()
- err = jw.Setup(http.DefaultServeMux.Handle, "/", Setup)
+ mux := http.NewServeMux()
+ err = jw.Setup(mux.Handle, "/", Setup)
maybeError(t, err)
- rq := jw.NewRequest(httptest.NewRequest("GET", "/", nil))
- rq = rq.Jaws.UseRequest(rq.JawsKey, httptest.NewRequest("GET", "/", nil))
+ go jw.Serve()
+ rq := jaws.NewTestRequest(jw, nil)
root, err := os.OpenRoot(".")
maybeError(t, err)
@@ -78,5 +78,26 @@ func TestTree(t *testing.T) {
t.Fatal("selection mismatch")
}
+ changed[0].Disabled = true
+ tree.JawsUpdate(elem)
+ select {
+ case <-t.Context().Done():
+ case msg := <-rq.OutCh:
+ if s := string(rootnode.marshalJSON(nil)); !strings.Contains(msg.Data, s) {
+ t.Log(msg.Data)
+ t.Error("msg data did not contain our JSON")
+ }
+ if !strings.Contains(msg.Data, `"selectable":false`) {
+ t.Error("msg data did not contain selectable:false")
+ }
+ }
+
rootnode.JawsPathSet(elem, changed[0].ID+".selected", "false")
+ select {
+ case <-t.Context().Done():
+ case msg := <-rq.OutCh:
+ if s := "jawstreeSetPath={\"tree\":\"tree\",\"id\":\"children.1.children.1\",\"set\":false}"; msg.Data != s {
+ t.Errorf("unexpected data: %q", msg.Data)
+ }
+ }
}