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: `
hello
`, }, } 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: `
xgb123 hello
`, }, { 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") } }