package web_test
import (
"bytes"
"context"
"io/ioutil"
"mime/multipart"
"net/http/httptest"
"strings"
"testing"
. "github.com/qor5/web"
"github.com/qor5/web/multipartestutils"
h "github.com/theplant/htmlgo"
"github.com/theplant/htmltestingutils"
"github.com/theplant/testingutils"
"goji.io"
"goji.io/pat"
)
type User struct {
Name string
Address *Address
}
type Address struct {
Zipcode string
City string
}
func runEvent(
eventFunc EventFunc,
renderChanger func(ctx *EventContext, pr *PageResponse),
eventFormChanger func(builder *multipartestutils.Builder),
) (indexResp *bytes.Buffer, eventResp *bytes.Buffer) {
pb := New()
var f = func(ctx *EventContext) (r EventResponse, err error) {
r.Reload = true
return
}
if eventFunc != nil {
f = eventFunc
}
var p = pb.Page(func(ctx *EventContext) (pr PageResponse, err error) {
if renderChanger != nil {
renderChanger(ctx, &pr)
} else {
pr.Body = h.H1("Hello")
}
return
}).EventFunc("call", f)
r := httptest.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
p.ServeHTTP(w, r)
indexResp = w.Body
builder := multipartestutils.NewMultipartBuilder().
EventFunc("call")
if eventFormChanger != nil {
eventFormChanger(builder)
}
r = builder.BuildEventFuncRequest()
w = httptest.NewRecorder()
p.ServeHTTP(w, r)
eventResp = w.Body
return
}
func TestFileUpload(t *testing.T) {
type mystate struct {
File1 []*multipart.FileHeader `form:"-"`
}
var uploadFile = func(ctx *EventContext) (r EventResponse, err error) {
s := &mystate{}
ctx.MustUnmarshalForm(s)
ctx.Flash = s
r.Reload = true
return
}
pb := New()
p := pb.Page(func(ctx *EventContext) (pr PageResponse, err error) {
s := &mystate{}
if ctx.Flash != nil {
s = ctx.Flash.(*mystate)
}
var data []byte
if len(s.File1) > 0 {
var mf multipart.File
mf, err = s.File1[0].Open()
if err != nil {
panic(err)
}
data, err = ioutil.ReadAll(mf)
if err != nil {
panic(err)
}
}
pr.Body = h.H1(string(data))
return
}).EventFunc("uploadFile", uploadFile)
b := multipartestutils.NewMultipartBuilder().
EventFunc("uploadFile").
AddReader("File1", "myfile.txt", strings.NewReader("Hello"))
r := b.BuildEventFuncRequest()
w := httptest.NewRecorder()
p.ServeHTTP(w, r)
diff := testingutils.PrettyJsonDiff(`
{
"body": "\n\u003ch1\u003eHello\u003c/h1\u003e\n",
"reload": true,
"pushState": null
}
`, w.Body.String())
if len(diff) > 0 {
t.Error(diff)
}
}
type DummyComp struct {
}
func (dc *DummyComp) MarshalHTML(ctx context.Context) (r []byte, err error) {
r = []byte("
hello
")
return
}
var eventCases = []struct {
name string
eventFunc EventFunc
renderChanger func(ctx *EventContext, pr *PageResponse)
eventFormChanger func(b *multipartestutils.Builder)
expectedIndexResp string
expectedEventResp string
}{
{
name: "run event reload states",
renderChanger: func(ctx *EventContext, pr *PageResponse) {
s := &User{
Address: &Address{},
}
if ctx.Flash != nil {
s = ctx.Flash.(*User)
}
pr.Body = h.Text(s.Name + " " + s.Address.City)
s.Name = "Felix"
},
eventFunc: func(ctx *EventContext) (r EventResponse, err error) {
s := &User{}
ctx.MustUnmarshalForm(s)
r.Reload = true
s.Name = "Felix1"
s.Address = &Address{City: "Hangzhou"}
ctx.Flash = s
return
},
expectedEventResp: `{
"body": "Felix1 Hangzhou",
"reload": true,
"pushState": null
}
`,
},
{
name: "render body in event func",
eventFunc: func(ctx *EventContext) (r EventResponse, err error) {
r.Body = h.Div(
h.H1("hello"),
)
return
},
expectedEventResp: `{
"body": "\n\u003cdiv\u003e\n\u003ch1\u003ehello\u003c/h1\u003e\n\u003c/div\u003e\n",
"pushState": null
}`,
},
{
name: "case 1",
renderChanger: func(ctx *EventContext, pr *PageResponse) {
pr.Body = h.RawHTML("Hello
")
},
expectedEventResp: `
{
"body": "\u003ch1\u003eHello\u003c/h1\u003e",
"reload": true,
"pushState": null
}
`,
},
{
name: "case 2",
renderChanger: func(ctx *EventContext, pr *PageResponse) {
ctx.Injector.TailHTMLComponent("mainjs", h.RawHTML(""), false)
pr.Body = &DummyComp{}
},
expectedEventResp: `{
"body": "\u003cdiv\u003ehello\u003c/div\u003e",
"reload": true,
"pushState": null
}`,
expectedIndexResp: `
`,
},
}
func TestEvents(t *testing.T) {
for _, c := range eventCases {
t.Run(c.name, func(t *testing.T) {
indexResp, eventResp := runEvent(c.eventFunc, c.renderChanger, c.eventFormChanger)
var diff string
if len(c.expectedIndexResp) > 0 {
diff = testingutils.PrettyJsonDiff(c.expectedIndexResp, indexResp)
if len(diff) > 0 {
t.Error(c.name, diff)
}
}
if len(c.expectedEventResp) > 0 {
diff = testingutils.PrettyJsonDiff(c.expectedEventResp, eventResp.String())
if len(diff) > 0 {
t.Error(c.name, diff)
}
}
})
}
}
var mountCases = []struct {
name string
method string
path string
bodyFunc func(b *multipartestutils.Builder)
expected string
}{
{
name: "with param get",
method: "GET",
path: "/home/topics/xgb123",
bodyFunc: nil,
expected: `
`,
},
{
name: "with param post",
method: "POST",
path: "/home/topics/xgb123",
bodyFunc: func(b *multipartestutils.Builder) {
b.EventFunc("bookmark")
},
expected: `{"body":"\n\u003ch1\u003exgb123 bookmarked\u003c/h1\u003e\n","pushState":null}`,
},
}
func TestMultiplePagesAndEvents(t *testing.T) {
var topicIndex = func(ctx *EventContext) (r PageResponse, err error) {
r.Body = h.H1("Hello Topic List")
return
}
var bookmark = func(ctx *EventContext) (r EventResponse, err error) {
topicId := pat.Param(ctx.R, "topicID")
r.Body = h.H1(topicId + " bookmarked")
return
}
var topicDetail = func(ctx *EventContext) (r PageResponse, err error) {
// remove to test global event func with web.New().RegisterEventFunc
// ctx.Hub.RegisterEventFunc("bookmark", bookmark)
topicId := pat.Param(ctx.R, "topicID")
r.Body = h.Div(
h.A().Href("#").Text(topicId).
Attr("v-on:click", Plaid().EventFunc("bookmark").Go()),
h.A().Href("#").Text("hello").
Attr("v-on:blur", Plaid().
BeforeScript("alert(1)").
FieldValue("Text1", Var("$event")).
EventFunc("doIt").
Go(),
),
)
return
}
pb := New()
pb.RegisterEventFunc("bookmark", bookmark)
mux := goji.NewMux()
mux.Handle(pat.New("/home/topics/:topicID"), pb.Page(topicDetail))
mux.Handle(pat.New("/home/topics"), pb.Page(topicIndex))
for _, c := range mountCases {
t.Run(c.name, func(t *testing.T) {
r := httptest.NewRequest(c.method, c.path, nil)
if c.bodyFunc != nil {
b := multipartestutils.NewMultipartBuilder().
PageURL(c.path)
c.bodyFunc(b)
r = b.BuildEventFuncRequest()
}
w := httptest.NewRecorder()
mux.ServeHTTP(w, r)
selector := "#app div"
if c.bodyFunc != nil {
selector = "*"
}
diff := htmltestingutils.PrettyHtmlDiff(w.Body, selector, c.expected)
if len(diff) > 0 {
t.Error(c.name, diff)
}
})
}
}
func TestEventFuncsOnPageAndBuilder(t *testing.T) {
w := httptest.NewRecorder()
r := multipartestutils.NewMultipartBuilder().
EventFunc("g1").BuildEventFuncRequest()
b := New().EventFuncs(
"g1", func(ctx *EventContext) (r EventResponse, err error) {
r.Body = h.H2("G1")
return
},
)
b.Page(func(ctx *EventContext) (r PageResponse, err error) {
r.Body = h.H1("Page")
return
}).EventFuncs(
"e1", func(ctx *EventContext) (r EventResponse, err error) {
r.Body = h.H2("E1")
return
},
).ServeHTTP(w, r)
if !strings.Contains(w.Body.String(), "G1") {
t.Errorf("wrong response %s", w.Body.String())
}
}
func TestEmbed(t *testing.T) {
pack := JSComponentsPack()
if len(pack) == 0 {
t.Fatal("No embed string")
}
}