Files
Gardomatic/internal/web/journal_test.go
T
kleiax 904d14b64c
CI / test (push) Canceled after 0s
Initial commit
2026-09-12 22:22:17 +02:00

100 lines
5.9 KiB
Go

package web
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"gardomatic.kleiax.de/lib/client"
)
func TestJournalTemplateRendersMetadataMarkdownAndMedia(t *testing.T) {
app := newTestApplication(t)
garden := client.Garden{ID: 3, Name: "Hinterhof", Role: "owner"}
entry := client.JournalEntry{ID: 7, GardenID: 3, AuthorName: "Ada", Title: "Ernte", Body: "**Drei** Tomaten.\n\n<script>alert(1)</script>", CreatedAt: time.Date(2026, 9, 5, 14, 30, 0, 0, time.Local), Tags: []string{"tomaten"}, Attachments: []client.JournalAttachment{{ID: 9, EntryID: 7, FileName: "foto.jpg", MediaType: "image/jpeg", Size: 2048}}}
response := httptest.NewRecorder()
app.render(response, http.StatusOK, "journal.tmpl", &templateData{commonTemplateData: commonTemplateData{Garden: &garden}, journalTemplateData: journalTemplateData{JournalEntries: []client.JournalEntry{entry}}})
body := response.Body.String()
for _, want := range []string{"<strong>Drei</strong>", "05.09.2026 · 14:30 Uhr", "Ada", "tomaten", "/g/3/journal/attachments/7/9", "Tagebuch", "data-card-href='/g/3/journal/edit/7'"} {
if !strings.Contains(body, want) {
t.Errorf("journal output missing %q: %s", want, body)
}
}
if strings.Contains(body, "<script>alert(1)</script>") {
t.Fatalf("raw HTML was not escaped: %s", body)
}
if strings.Contains(body, ">Bearbeiten</a>") {
t.Fatalf("journal tile still exposes a separate edit link: %s", body)
}
}
func TestJournalFormOffersBothEditorModesAndCaptureInputs(t *testing.T) {
app := newTestApplication(t)
garden := client.Garden{ID: 3, Name: "Hinterhof", Role: "owner"}
response := httptest.NewRecorder()
app.render(response, http.StatusOK, "journal_form.tmpl", &templateData{commonTemplateData: commonTemplateData{Garden: &garden, Form: journalForm{Body: "# Notiz", Errors: map[string]string{}}, UseJournalEditor: true}})
body := response.Body.String()
for _, want := range []string{"toastui-editor-3.2.2.min.js", "data-journal-editor", "data-journal-body", "data-image-camera", "data-video-record", "data-video-pause", "data-video-apply", "Mediendateien auswählen", "Audio aufnehmen", "data-audio-dialog", "data-audio-preview", "data-audio-pause", "data-audio-apply", "data-recording-indicator", "name='embedded_images'", "data-tag-editor", "data-tag-suggestions"} {
if !strings.Contains(body, want) {
t.Errorf("journal form missing %q: %s", want, body)
}
}
if strings.Contains(body, "data-image-preview") || strings.Contains(body, "data-image-file") {
t.Fatalf("photo capture should expose one button, not a persistent preview/file picker: %s", body)
}
}
func TestPinboardReusesEntryAndPhotoComponents(t *testing.T) {
app := newTestApplication(t)
garden := client.Garden{ID: 3, Name: "Hinterhof", Role: "owner"}
entry := client.JournalEntry{ID: 8, GardenID: 3, EntryType: client.JournalEntryTypePinboard, AuthorName: "Ada", Title: "Sitzecke", Body: "Bank unter den Apfelbaum", CreatedAt: time.Date(2026, 9, 6, 10, 0, 0, 0, time.Local), Attachments: []client.JournalAttachment{{ID: 11, EntryID: 8, FileName: "idee.jpg", MediaType: "image/jpeg", Size: 1024}}}
listResponse := httptest.NewRecorder()
app.render(listResponse, http.StatusOK, "journal.tmpl", &templateData{commonTemplateData: commonTemplateData{Garden: &garden, ContentKind: client.JournalEntryTypePinboard}, journalTemplateData: journalTemplateData{JournalEntries: []client.JournalEntry{entry}}})
listBody := listResponse.Body.String()
for _, want := range []string{"Pinnwand", "pinboard-list", "data-card-href='/g/3/pinboard/edit/8'", "/g/3/pinboard/attachments/8/11", "Bank unter den Apfelbaum"} {
if !strings.Contains(listBody, want) {
t.Errorf("pinboard output missing %q: %s", want, listBody)
}
}
formResponse := httptest.NewRecorder()
app.render(formResponse, http.StatusOK, "journal_form.tmpl", &templateData{commonTemplateData: commonTemplateData{Garden: &garden, ContentKind: client.JournalEntryTypePinboard, Form: journalForm{DateTime: "2026-09-06T10:00", Errors: map[string]string{}}, UseJournalEditor: true}, mediaTemplateData: mediaTemplateData{Images: []client.Image{{ID: 4, FileName: "vorhanden.jpg"}}}})
formBody := formResponse.Body.String()
for _, want := range []string{"Neue Notiz", "Fotos auswählen", "Foto mit Gerät aufnehmen", "Aus Bilderdatenbank", "href='/g/3/pinboard'"} {
if !strings.Contains(formBody, want) {
t.Errorf("pinboard form missing %q: %s", want, formBody)
}
}
if strings.Contains(formBody, "name='title' value='' maxlength='500' required") {
t.Fatalf("pinboard title is still required: %s", formBody)
}
if strings.Contains(formBody, "data-video-record") || strings.Contains(formBody, "data-audio-record") {
t.Fatalf("simple pinboard form exposes journal recording controls: %s", formBody)
}
}
func TestPinboardSearchCardLinksBackToPinboard(t *testing.T) {
card := gardenEntrySearchCard(3, client.JournalEntry{ID: 8, EntryType: client.JournalEntryTypePinboard, Title: "Sitzecke"})
if card.Type != "Pinnwand" || card.URL != "/g/3/pinboard#entry-8" {
t.Fatalf("pinboard search card: %+v", card)
}
}
func TestDashboardShowsJournalEntryCount(t *testing.T) {
app := newTestApplication(t)
garden := client.Garden{ID: 3, Name: "Hinterhof", Role: "owner"}
entries := []client.JournalEntry{{ID: 1}, {ID: 2}, {ID: 3}}
response := httptest.NewRecorder()
app.render(response, http.StatusOK, "dashboard.tmpl", &templateData{commonTemplateData: commonTemplateData{Garden: &garden}, journalTemplateData: journalTemplateData{JournalEntries: entries, PinboardEntries: []client.JournalEntry{{ID: 4}}}})
body := response.Body.String()
if !strings.Contains(body, "href='/g/3/journal'") || !strings.Contains(body, "<span class='summary-number'>3</span><h3>Tagebuch</h3>") {
t.Fatalf("dashboard output missing journal count: %s", body)
}
if !strings.Contains(body, "href='/g/3/pinboard'") || !strings.Contains(body, "<span class='summary-number'>1</span><h3>Pinnwand</h3>") {
t.Fatalf("dashboard output missing pinboard count: %s", body)
}
}