diff --git a/example/main.go b/example/main.go index 03cf272..fa398e2 100644 --- a/example/main.go +++ b/example/main.go @@ -10,9 +10,14 @@ import ( func main() { r := ron.New() + htmlRender := ron.HTMLRender() + r.Renderer = htmlRender + r.GET("/", helloWorld) r.GET("/json", helloWorldJSON) r.POST("/another", anotherHelloWorld) + r.GET("/html", helloWorldHTML) + r.GET("/component", componentHTML) slog.Info("Server is running at http://localhost:8080") http.ListenAndServe(":8080", r) @@ -27,5 +32,16 @@ func anotherHelloWorld(c *ron.Context) { } func helloWorldJSON(c *ron.Context) { - c.JSON(200, ron.D{"message": "hello world"}) + c.JSON(200, ron.Data{"message": "hello world"}) +} + +func helloWorldHTML(c *ron.Context) { + c.HTML(200, "page.index.gohtml", ron.Data{ + "title": "hello world", + "message": "hello world from html", + }) +} + +func componentHTML(c *ron.Context) { + c.HTML(200, "component.list.gohtml", ron.Data{}) } diff --git a/example/templates/page/component.list.gohtml b/example/templates/page/component.list.gohtml new file mode 100644 index 0000000..342f35f --- /dev/null +++ b/example/templates/page/component.list.gohtml @@ -0,0 +1,4 @@ +
layout.base.gohtml
{{ .Data.foo }}
{{ block \"base/content\" . }}{{ end }}{{ end }}")) + f.Close() + f, _ = os.Create("templates/layout.another.gohtml") + f.Write([]byte("{{ define \"layout/another\" }}layout.another.gohtml
{{ .Data.bar }}
{{ block \"base/content\" . }}{{ end }}{{ end }}")) + f.Close() + f, _ = os.Create("templates/fragment.button.gohtml") + f.Close() + f, _ = os.Create("templates/component.list.gohtml") + f.Close() + f, _ = os.Create("templates/page.index.gohtml") + f.Write([]byte("{{ template \"layout/base\" .}}{{ define \"base/content\" }}page.index.gohtml
{{ .Data.bar }}
{{ end }}")) + f.Close() + f, _ = os.Create("templates/page.another.gohtml") + f.Write([]byte("{{ template \"layout/another\" .}}{{ define \"base/content\" }}page.another.gohtml
{{ .Data.foo }}
{{ end }}")) + f.Close() + + render := DefaultHTMLRender() + return render +} + +func Test_findHTMLFiles(t *testing.T) { + render := createDummyFilesAndRender() + if render == nil { + t.Errorf("Error: %v", render) + return + } + defer os.RemoveAll("templates") + + expected := []string{ + "templates\\layout.base.gohtml", + "templates\\layout.another.gohtml", + "templates\\fragment.button.gohtml", + "templates\\component.list.gohtml", + "templates\\page.index.gohtml", + "templates\\page.another.gohtml", + } + actual, err := render.findHTMLFiles() + if err != nil { + t.Errorf("Error: %v", err) + } + + expectedAny := testhelpers.StringSliceToAnySlice(expected) + actualAny := testhelpers.StringSliceToAnySlice(actual) + + if testhelpers.CheckSlicesEquality(expectedAny, actualAny) == false { + t.Errorf("Expected: %v, Actual: %v", expected, actual) + } +} + +func Test_createTemplateCache(t *testing.T) { + render := createDummyFilesAndRender() + if render == nil { + t.Errorf("Error: %v", render) + } + defer os.RemoveAll("templates") + + tc, err := render.createTemplateCache() + if err != nil || len(tc) != 3 { + t.Errorf("Error: %v", err) + } + + templateNames := []string{ + "component.list.gohtml", + "page.index.gohtml", + "page.another.gohtml", + } + + for _, templateName := range templateNames { + if _, ok := tc[templateName]; ok == false { + t.Errorf("Error: %v", err) + } + } +} + +func Test_TemplateDefault(t *testing.T) { + tests := []struct { + name string + expected string + }{ + {"index", "layout.base.gohtml
Foo
page.index.gohtml
Bar
"}, + {"another", "layout.another.gohtml
Bar
page.another.gohtml
Foo
"}, + } + + render := createDummyFilesAndRender() + if render == nil { + t.Errorf("Error: %v", render) + } + defer os.RemoveAll("templates") + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rr := httptest.NewRecorder() + render.Template(rr, "page."+tt.name+".gohtml", &TemplateData{ + Data: Data{ + "foo": "Foo", + "bar": "Bar", + }}) + + if rr.Body.String() != tt.expected { + t.Errorf("Expected: %v, Actual: %v", tt.expected, rr.Body.String()) + } + }) + } +} diff --git a/testhelpers/testhelpers.go b/testhelpers/testhelpers.go new file mode 100644 index 0000000..1fc8a9c --- /dev/null +++ b/testhelpers/testhelpers.go @@ -0,0 +1,33 @@ +package testhelpers + +func CheckSlicesEquality(a []any, b []any) bool { + if len(a) != len(b) { + return false + } + + aMap := make(map[any]int) + bMap := make(map[any]int) + + for _, v := range a { + aMap[v]++ + } + for _, v := range b { + bMap[v]++ + } + + for k, v := range aMap { + if bMap[k] != v { + return false + } + } + + return true +} + +func StringSliceToAnySlice(s []string) []any { + var result []any + for _, v := range s { + result = append(result, v) + } + return result +} diff --git a/testhelpers/testhelpers_test.go b/testhelpers/testhelpers_test.go new file mode 100644 index 0000000..bc9df4e --- /dev/null +++ b/testhelpers/testhelpers_test.go @@ -0,0 +1,70 @@ +package testhelpers + +import ( + "testing" +) + +func Test_CheckSlicesEquality(t *testing.T) { + tests := []struct { + name string + ok bool + sliceA []any + sliceB []any + }{ + { + name: "Integers", + ok: true, + sliceA: []any{2, 3, 1}, + sliceB: []any{1, 2, 3}, + }, + { + name: "Strings", + ok: true, + sliceA: []any{"x", "y", "z"}, + sliceB: []any{"z", "y", "x"}, + }, + { + name: "Integers 2", + ok: true, + sliceA: []any{1, 2, 3}, + sliceB: []any{1, 2, 3}, + }, + { + name: "Different lengths", + ok: false, + sliceA: []any{1, 2, 3}, + sliceB: []any{1, 2, 3, 4}, + }, + { + name: "Different lengths 2", + ok: false, + sliceA: []any{1, 2, 3, 4}, + sliceB: []any{1, 2, 3}, + }, + { + name: "Different types", + ok: false, + sliceA: []any{1, 2, 3}, + sliceB: []any{"1", "2", "3"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if result := CheckSlicesEquality(tt.sliceA, tt.sliceB); result != tt.ok { + t.Errorf("CheckSlicesEquality() = %v, want %v", result, tt.ok) + } + }) + } + +} + +func Test_StringSliceToAnySlice(t *testing.T) { + expected := []any{"a", "b", "c"} + actual := StringSliceToAnySlice([]string{"a", "b", "c"}) + + if !CheckSlicesEquality(expected, actual) { + t.Errorf("StringSliceToAnySlice() = %v, want %v", actual, expected) + } + +}