Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Writer not strings #34

Merged
merged 15 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion boolsetter.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ func makeBoolSetter(v interface{}) BoolSetter {
case *atomic.Value:
return atomicSetter{v}
}
panic(fmt.Errorf("expected jaws.BoolGetter or bool, not %T", v))
panic(fmt.Errorf("expected jaws.BoolSetter or bool, not %T", v))
}
15 changes: 7 additions & 8 deletions clickhandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package jaws

import (
"testing"
"time"

"github.com/linkdata/jaws/what"
)
Expand All @@ -22,8 +21,7 @@ func (tje *testJawsClick) JawsClick(e *Element, name string) (err error) {
var _ ClickHandler = (*testJawsClick)(nil)

func Test_clickHandlerWapper_JawsEvent(t *testing.T) {
tmr := time.NewTimer(testTimeout)
defer tmr.Stop()
th := newTestHelper(t)
nextJid = 0
rq := newTestRequest()
defer rq.Close()
Expand All @@ -34,23 +32,24 @@ func Test_clickHandlerWapper_JawsEvent(t *testing.T) {
}

want := `<div id="Jid.1">inner</div>`
if got := string(rq.Div("inner", tjc)); got != want {
rq.Div("inner", tjc)
if got := rq.BodyString(); got != want {
t.Errorf("Request.Div() = %q, want %q", got, want)
}

rq.inCh <- wsMsg{Data: "text", Jid: 1, What: what.Input}
select {
case <-tmr.C:
t.Error("timeout")
case <-th.C:
th.Timeout()
case s := <-rq.outCh:
t.Errorf("%q", s)
default:
}

rq.inCh <- wsMsg{Data: "adam", Jid: 1, What: what.Click}
select {
case <-tmr.C:
t.Error("timeout")
case <-th.C:
th.Timeout()
case name := <-tjc.clickCh:
if name != "adam" {
t.Error(name)
Expand Down
48 changes: 39 additions & 9 deletions element.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,57 @@ import (

// An Element is an instance of a *Request, an UI object and a Jid.
type Element struct {
ui UI // (read-only) the UI object
jid jid.Jid // (read-only) JaWS ID, unique to this Element within it's Request
*Request // (read-only) the Request the Element belongs to
ui UI // (read-only) the UI object
jid jid.Jid // (read-only) JaWS ID, unique to this Element within it's Request
rq *Request // (read-only) the Request the Element belongs to
// internals
updating bool // about to have Update() called
wsQueue []wsMsg // changes queued
handlers []EventHandler // custom event handlers registered, if any
}

func (e *Element) String() string {
return fmt.Sprintf("Element{%T, id=%q, Tags: %v}", e.ui, e.jid, e.Request.TagsOf(e))
return fmt.Sprintf("Element{%T, id=%q, Tags: %v}", e.ui, e.jid, e.rq.TagsOf(e))
}

// Jaws returns the Jaws the Element belongs to.
func (e *Element) Jaws() *Jaws {
return e.rq.Jaws
}

// Request returns the Request the Element belongs to.
func (e *Element) Request() *Request {
return e.rq
}

// Session returns the Elements's Session, or nil.
func (e *Element) Session() *Session {
return e.rq.Session()
}

// Get calls Session().Get()
func (e *Element) Get(key string) (val interface{}) {
return e.Session().Get(key)
}

// Set calls Session().Get()
func (e *Element) Set(key string, val interface{}) {
e.Session().Set(key, val)
}

// Dirty calls Request().Dirty()
func (e *Element) Dirty(tags ...interface{}) {
e.rq.Dirty(tags...)
}

// Tag adds the given tags to the Element.
func (e *Element) Tag(tags ...interface{}) {
e.Request.Tag(e, tags...)
e.rq.Tag(e, tags...)
}

// HasTag returns true if this Element has the given tag.
func (e *Element) HasTag(tag interface{}) bool {
return e.Request.HasTag(e, tag)
return e.rq.HasTag(e, tag)
}

// Jid returns the JaWS ID for this Element, unique within it's Request.
Expand All @@ -46,8 +76,8 @@ func (e *Element) Ui() UI {
}

// Render calls Request.JawsRender() for this Element.
func (e *Element) Render(w io.Writer, params []interface{}) {
e.Request.JawsRender(e, w, params)
func (e *Element) Render(w io.Writer, params []interface{}) error {
return e.rq.JawsRender(e, w, params)
}

func (e *Element) queue(wht what.What, data string) {
Expand All @@ -58,7 +88,7 @@ func (e *Element) queue(wht what.What, data string) {
What: wht,
})
} else {
e.Request.cancelFn(ErrWebsocketQueueOverflow)
e.rq.cancel(ErrWebsocketQueueOverflow)
}
}

Expand Down
40 changes: 27 additions & 13 deletions element_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type testUi struct {
getCalled int32
setCalled int32
s string
renderFn func(e *Element, w io.Writer, params []any)
renderFn func(e *Element, w io.Writer, params []any) error
updateFn func(e *Element)
}

Expand All @@ -39,12 +39,13 @@ func (tss *testUi) JawsSetString(e *Element, s string) error {
return nil
}

func (tss *testUi) JawsRender(e *Element, w io.Writer, params []any) {
func (tss *testUi) JawsRender(e *Element, w io.Writer, params []any) (err error) {
e.Tag(tss)
atomic.AddInt32(&tss.renderCalled, 1)
if tss.renderFn != nil {
tss.renderFn(e, w, params)
err = tss.renderFn(e, w, params)
}
return
}

func (tss *testUi) JawsUpdate(e *Element) {
Expand All @@ -54,8 +55,22 @@ func (tss *testUi) JawsUpdate(e *Element) {
}
}

func TestElement_helpers(t *testing.T) {
is := newTestHelper(t)
rq := newTestRequest()
defer rq.Close()

tss := &testUi{}
e := rq.NewElement(tss)
is.Equal(e.Jaws(), rq.jw.Jaws)
is.Equal(e.Request(), rq.Request)
is.Equal(e.Session(), nil)
e.Set("foo", "bar") // no session, so no effect
is.Equal(e.Get("foo"), nil)
}

func TestElement_Tag(t *testing.T) {
is := testHelper{t}
is := newTestHelper(t)
rq := newTestRequest()
defer rq.Close()

Expand All @@ -68,7 +83,7 @@ func TestElement_Tag(t *testing.T) {
}

func TestElement_Queued(t *testing.T) {
is := testHelper{t}
th := newTestHelper(t)
rq := newTestRequest()
defer rq.Close()

Expand All @@ -85,7 +100,7 @@ func TestElement_Queued(t *testing.T) {
e.Order([]jid.Jid{1, 2})
replaceHtml := template.HTML(fmt.Sprintf("<div id=\"%s\"></div>", e.Jid().String()))
e.Replace(replaceHtml)
is.Equal(e.wsQueue, []wsMsg{
th.Equal(e.wsQueue, []wsMsg{
{
Data: "hidden\n",
Jid: e.jid,
Expand Down Expand Up @@ -141,26 +156,25 @@ func TestElement_Queued(t *testing.T) {
}

pendingRq := rq.Jaws.NewRequest(httptest.NewRequest(http.MethodGet, "/", nil))
pendingRq.UI(tss)
RequestWriter{pendingRq, httptest.NewRecorder()}.UI(tss)

rq.UI(tss)
rq.Jaws.Dirty(tss)
rq.Dirty(tss)
tmr := time.NewTimer(testTimeout)
for atomic.LoadInt32(&tss.updateCalled) < 1 {
select {
case <-tmr.C:
is.Fail()
case <-th.C:
th.Timeout()
default:
time.Sleep(time.Millisecond)
}
}
is.Equal(tss.updateCalled, int32(1))
is.Equal(tss.renderCalled, int32(2))
th.Equal(tss.updateCalled, int32(1))
th.Equal(tss.renderCalled, int32(2))
}

func TestElement_ReplacePanicsOnMissingId(t *testing.T) {
is := testHelper{t}
is := newTestHelper(t)
rq := newTestRequest()
defer rq.Close()
defer func() {
Expand Down
19 changes: 0 additions & 19 deletions html.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"html/template"
"io"
"strconv"
"strings"

"github.com/linkdata/jaws/jid"
)
Expand Down Expand Up @@ -70,12 +69,6 @@ func WriteHtmlInput(w io.Writer, jid jid.Jid, typ, val string, attrs ...string)
return
}

func HtmlInput(jid jid.Jid, typ, val string, attrs ...string) template.HTML {
var sb strings.Builder
_ = WriteHtmlInput(&sb, jid, typ, val, attrs...)
return template.HTML(sb.String()) // #nosec G203
}

func WriteHtmlInner(w io.Writer, jid jid.Jid, tag, typ string, inner template.HTML, attrs ...string) (err error) {
need := 1 + len(tag)*2 + jidPrealloc + 8 + len(typ) + 1 + 1 + getAttrsLen(attrs) + 1 + len(inner) + 2 + 1
b := make([]byte, 0, need)
Expand All @@ -96,12 +89,6 @@ func WriteHtmlInner(w io.Writer, jid jid.Jid, tag, typ string, inner template.HT
return
}

func HtmlInner(jid jid.Jid, tag, typ string, inner template.HTML, attrs ...string) template.HTML {
var sb strings.Builder
_ = WriteHtmlInner(&sb, jid, tag, typ, inner, attrs...)
return template.HTML(sb.String()) // #nosec G203
}

func WriteHtmlSelect(w io.Writer, jid jid.Jid, nba *NamedBoolArray, attrs ...string) (err error) {
need := 12 + jidPrealloc + 2 + getAttrsLen(attrs) + 2 + 10
nba.ReadLocked(func(nba []*NamedBool) {
Expand Down Expand Up @@ -132,9 +119,3 @@ func WriteHtmlSelect(w io.Writer, jid jid.Jid, nba *NamedBoolArray, attrs ...str
_, err = w.Write(b)
return
}

func HtmlSelect(jid jid.Jid, nba *NamedBoolArray, attrs ...string) template.HTML {
var sb strings.Builder
_ = WriteHtmlSelect(&sb, jid, nba, attrs...)
return template.HTML(sb.String()) // #nosec G203
}
25 changes: 19 additions & 6 deletions html_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package jaws

import (
"html/template"
"strings"
"testing"

"github.com/linkdata/jaws/jid"
Expand All @@ -17,7 +18,7 @@ func TestHtmlInput(t *testing.T) {
tests := []struct {
name string
args args
want template.HTML
want string
}{
{
name: "HtmlInput no attrs",
Expand Down Expand Up @@ -61,7 +62,11 @@ func TestHtmlInput(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := HtmlInput(tt.args.jid, tt.args.typ, tt.args.val, tt.args.attrs...); got != tt.want {
var sb strings.Builder
if err := WriteHtmlInput(&sb, tt.args.jid, tt.args.typ, tt.args.val, tt.args.attrs...); err != nil {
t.Fatal(err)
}
if got := sb.String(); got != tt.want {
t.Errorf("HtmlInput() = %v, want %v", got, tt.want)
}
})
Expand All @@ -79,7 +84,7 @@ func TestHtmlInner(t *testing.T) {
tests := []struct {
name string
args args
want template.HTML
want string
}{
{
name: "HtmlInner no attrs",
Expand Down Expand Up @@ -115,7 +120,11 @@ func TestHtmlInner(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := HtmlInner(tt.args.jid, tt.args.tag, tt.args.typ, tt.args.inner, tt.args.attrs...); got != tt.want {
var sb strings.Builder
if err := WriteHtmlInner(&sb, tt.args.jid, tt.args.tag, tt.args.typ, tt.args.inner, tt.args.attrs...); err != nil {
t.Fatal(err)
}
if got := sb.String(); got != tt.want {
t.Errorf("HtmlInner() = %v, want %v", got, tt.want)
}
})
Expand All @@ -131,7 +140,7 @@ func TestHtmlSelect(t *testing.T) {
tests := []struct {
name string
args args
want template.HTML
want string
}{
{
name: "HtmlSelect empty NamedBoolArray and one attr",
Expand Down Expand Up @@ -166,7 +175,11 @@ func TestHtmlSelect(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := HtmlSelect(tt.args.jid, tt.args.val, tt.args.attrs...); got != tt.want {
var sb strings.Builder
if err := WriteHtmlSelect(&sb, tt.args.jid, tt.args.val, tt.args.attrs...); err != nil {
t.Fatal(err)
}
if got := sb.String(); got != tt.want {
t.Errorf("HtmlSelect() = %v, want %v", got, tt.want)
}
})
Expand Down
Loading
Loading