Changed: DB Params

This commit is contained in:
2025-03-20 12:35:13 +01:00
parent 8640a12439
commit b71b3d12ca
822 changed files with 134218 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
package main
import (
"fmt"
"log"
"net/http"
"github.com/a-h/templ"
)
func main() {
// Use a template that doesn't take parameters.
http.Handle("/", templ.Handler(home()))
// Use a template that accesses data or handles form posts.
http.Handle("/posts", NewPostsHandler())
// Start the server.
fmt.Println("listening on http://localhost:8000")
if err := http.ListenAndServe("localhost:8000", nil); err != nil {
log.Printf("error listening: %v", err)
}
}
func NewPostsHandler() PostsHandler {
// Replace this in-memory function with a call to a database.
postsGetter := func() (posts []Post, err error) {
return []Post{{Name: "templ", Author: "author"}}, nil
}
return PostsHandler{
GetPosts: postsGetter,
Log: log.Default(),
}
}
type PostsHandler struct {
Log *log.Logger
GetPosts func() ([]Post, error)
}
func (ph PostsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ps, err := ph.GetPosts()
if err != nil {
ph.Log.Printf("failed to get posts: %v", err)
http.Error(w, "failed to retrieve posts", http.StatusInternalServerError)
return
}
templ.Handler(posts(ps)).ServeHTTP(w, r)
}
type Post struct {
Name string
Author string
}

View File

@@ -0,0 +1,64 @@
package main
import (
"fmt"
"time"
)
templ headerTemplate(name string) {
<header data-testid="headerTemplate">
<h1>{ name }</h1>
</header>
}
templ footerTemplate() {
<footer data-testid="footerTemplate">
<div>&copy; { fmt.Sprintf("%d", time.Now().Year()) }</div>
</footer>
}
templ navTemplate() {
<nav data-testid="navTemplate">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/posts">Posts</a></li>
</ul>
</nav>
}
templ layout(name string) {
<html>
<head><title>{ name }</title></head>
<body>
@headerTemplate(name)
@navTemplate()
<main>
{ children... }
</main>
</body>
@footerTemplate()
</html>
}
templ postsTemplate(posts []Post) {
<div data-testid="postsTemplate">
for _, p := range posts {
<div data-testid="postsTemplatePost">
<div data-testid="postsTemplatePostName">{ p.Name }</div>
<div data-testid="postsTemplatePostAuthor">{ p.Author }</div>
</div>
}
</div>
}
templ home() {
@layout("Home") {
<div data-testid="homeTemplate">Welcome to my website.</div>
}
}
templ posts(posts []Post) {
@layout("Posts") {
@postsTemplate(posts)
}
}

View File

@@ -0,0 +1,358 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package main
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"fmt"
"time"
)
func headerTemplate(name string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<header data-testid=\"headerTemplate\"><h1>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templ/examples/blog/posts.templ`, Line: 10, Col: 12}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</h1></header>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func footerTemplate() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<footer data-testid=\"footerTemplate\"><div>&copy; ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", time.Now().Year()))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templ/examples/blog/posts.templ`, Line: 16, Col: 52}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div></footer>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func navTemplate() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<nav data-testid=\"navTemplate\"><ul><li><a href=\"/\">Home</a></li><li><a href=\"/posts\">Posts</a></li></ul></nav>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func layout(name string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
if templ_7745c5c3_Var6 == nil {
templ_7745c5c3_Var6 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<html><head><title>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templ/examples/blog/posts.templ`, Line: 31, Col: 21}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</title></head><body>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = headerTemplate(name).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = navTemplate().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<main>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var6.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</main></body>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = footerTemplate().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func postsTemplate(posts []Post) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
if templ_7745c5c3_Var8 == nil {
templ_7745c5c3_Var8 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div data-testid=\"postsTemplate\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, p := range posts {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div data-testid=\"postsTemplatePost\"><div data-testid=\"postsTemplatePostName\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(p.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templ/examples/blog/posts.templ`, Line: 47, Col: 53}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div><div data-testid=\"postsTemplatePostAuthor\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(p.Author)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templ/examples/blog/posts.templ`, Line: 48, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func home() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var11 := templ.GetChildren(ctx)
if templ_7745c5c3_Var11 == nil {
templ_7745c5c3_Var11 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var12 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<div data-testid=\"homeTemplate\">Welcome to my website.</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = layout("Home").Render(templ.WithChildren(ctx, templ_7745c5c3_Var12), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func posts(posts []Post) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var13 := templ.GetChildren(ctx)
if templ_7745c5c3_Var13 == nil {
templ_7745c5c3_Var13 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var14 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = postsTemplate(posts).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = layout("Posts").Render(templ.WithChildren(ctx, templ_7745c5c3_Var14), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -0,0 +1,210 @@
package main
import (
"context"
"errors"
"fmt"
"io"
"log"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/PuerkitoBio/goquery"
)
func TestHeader(t *testing.T) {
// Pipe the rendered template into goquery.
r, w := io.Pipe()
go func() {
_ = headerTemplate("Posts").Render(context.Background(), w)
_ = w.Close()
}()
doc, err := goquery.NewDocumentFromReader(r)
if err != nil {
t.Fatalf("failed to read template: %v", err)
}
// Expect the component to include a testid.
if doc.Find(`[data-testid="headerTemplate"]`).Length() == 0 {
t.Error("expected data-testid attribute to be rendered, but it wasn't")
}
// Expect the page name to be set correctly.
expectedPageName := "Posts"
if actualPageName := doc.Find("h1").Text(); actualPageName != expectedPageName {
t.Errorf("expected page name %q, got %q", expectedPageName, actualPageName)
}
}
func TestFooter(t *testing.T) {
// Pipe the rendered template into goquery.
r, w := io.Pipe()
go func() {
_ = footerTemplate().Render(context.Background(), w)
_ = w.Close()
}()
doc, err := goquery.NewDocumentFromReader(r)
if err != nil {
t.Fatalf("failed to read template: %v", err)
}
// Expect the component to include a testid.
if doc.Find(`[data-testid="footerTemplate"]`).Length() == 0 {
t.Error("expected data-testid attribute to be rendered, but it wasn't")
}
// Expect the copyright notice to include the current year.
expectedCopyrightNotice := fmt.Sprintf("© %d", time.Now().Year())
if actualCopyrightNotice := doc.Find("div").Text(); actualCopyrightNotice != expectedCopyrightNotice {
t.Errorf("expected copyright notice %q, got %q", expectedCopyrightNotice, actualCopyrightNotice)
}
}
func TestNav(t *testing.T) {
r, w := io.Pipe()
go func() {
_ = navTemplate().Render(context.Background(), w)
_ = w.Close()
}()
doc, err := goquery.NewDocumentFromReader(r)
if err != nil {
t.Fatalf("failed to read template: %v", err)
}
// Expect the component to include a testid.
if doc.Find(`[data-testid="navTemplate"]`).Length() == 0 {
t.Error("expected data-testid attribute to be rendered, but it wasn't")
}
}
func TestHome(t *testing.T) {
r, w := io.Pipe()
go func() {
_ = home().Render(context.Background(), w)
_ = w.Close()
}()
doc, err := goquery.NewDocumentFromReader(r)
if err != nil {
t.Fatalf("failed to read template: %v", err)
}
// Expect the page title to be set correctly.
expectedTitle := "Home"
if actualTitle := doc.Find("title").Text(); actualTitle != expectedTitle {
t.Errorf("expected title name %q, got %q", expectedTitle, actualTitle)
}
// Expect the header to be rendered.
if doc.Find(`[data-testid="headerTemplate"]`).Length() == 0 {
t.Error("expected data-testid attribute to be rendered, but it wasn't")
}
// Expect the navigation to be rendered.
if doc.Find(`[data-testid="navTemplate"]`).Length() == 0 {
t.Error("expected nav to be rendered, but it wasn't")
}
// Expect the home template be rendered.
if doc.Find(`[data-testid="homeTemplate"]`).Length() == 0 {
t.Error("expected homeTemplate to be rendered, but it wasn't")
}
}
func TestPosts(t *testing.T) {
testPosts := []Post{
{Name: "Name1", Author: "Author1"},
{Name: "Name2", Author: "Author2"},
}
r, w := io.Pipe()
go func() {
_ = posts(testPosts).Render(context.Background(), w)
_ = w.Close()
}()
doc, err := goquery.NewDocumentFromReader(r)
if err != nil {
t.Fatalf("failed to read template: %v", err)
}
// Assert.
// Expect the page title to be set correctly.
expectedTitle := "Posts"
if actualTitle := doc.Find("title").Text(); actualTitle != expectedTitle {
t.Errorf("expected title name %q, got %q", expectedTitle, actualTitle)
}
// Expect the header to be rendered.
if doc.Find(`[data-testid="headerTemplate"]`).Length() == 0 {
t.Error("expected data-testid attribute to be rendered, but it wasn't")
}
// Expect the navigation to be rendered.
if doc.Find(`[data-testid="navTemplate"]`).Length() == 0 {
t.Error("expected nav to be rendered, but it wasn't")
}
// Expect the posts to be rendered.
if doc.Find(`[data-testid="postsTemplate"]`).Length() == 0 {
t.Error("expected posts to be rendered, but it wasn't")
}
// Expect both posts to be rendered.
if actualPostCount := doc.Find(`[data-testid="postsTemplatePost"]`).Length(); actualPostCount != len(testPosts) {
t.Fatalf("expected %d posts to be rendered, found %d", len(testPosts), actualPostCount)
}
// Expect the posts to contain the author name.
doc.Find(`[data-testid="postsTemplatePost"]`).Each(func(index int, sel *goquery.Selection) {
expectedName := testPosts[index].Name
if actualName := sel.Find(`[data-testid="postsTemplatePostName"]`).Text(); actualName != expectedName {
t.Errorf("expected name %q, got %q", actualName, expectedName)
}
expectedAuthor := testPosts[index].Author
if actualAuthor := sel.Find(`[data-testid="postsTemplatePostAuthor"]`).Text(); actualAuthor != expectedAuthor {
t.Errorf("expected author %q, got %q", actualAuthor, expectedAuthor)
}
})
}
func TestPostsHandler(t *testing.T) {
tests := []struct {
name string
postGetter func() (posts []Post, err error)
expectedStatus int
assert func(doc *goquery.Document)
}{
{
name: "database errors result in a 500 error",
postGetter: func() (posts []Post, err error) {
return nil, errors.New("database error")
},
expectedStatus: http.StatusInternalServerError,
assert: func(doc *goquery.Document) {
expected := "failed to retrieve posts\n"
if actual := doc.Text(); actual != expected {
t.Errorf("expected error message %q, got %q", expected, actual)
}
},
},
{
name: "database success renders the posts",
postGetter: func() (posts []Post, err error) {
return []Post{
{Name: "Name1", Author: "Author1"},
{Name: "Name2", Author: "Author2"},
}, nil
},
expectedStatus: http.StatusInternalServerError,
assert: func(doc *goquery.Document) {
if doc.Find(`[data-testid="postsTemplate"]`).Length() == 0 {
t.Error("expected posts to be rendered, but it wasn't")
}
},
},
}
for _, test := range tests {
// Arrange.
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/posts", nil)
ph := NewPostsHandler()
ph.Log = log.New(io.Discard, "", 0) // Suppress logging.
ph.GetPosts = test.postGetter
// Act.
ph.ServeHTTP(w, r)
doc, err := goquery.NewDocumentFromReader(w.Result().Body)
if err != nil {
t.Fatalf("failed to read template: %v", err)
}
// Assert.
test.assert(doc)
}
}