- element.test.ts: an untouched value survives Source tab, tab buttons, preview and reattach byte for byte with no summer-change - TestBlogEditorLoad: HTML verbatim, markdown as content_html, FormatHTML fallback, nil and empty values - TestPostsAdminResaveKeepsHTML (Docker host): GET then PUT the served values for an HTML and a markdown post, exact bytes, no notices, then a clean edited save in both locales
139 lines
4.8 KiB
Go
139 lines
4.8 KiB
Go
package classes
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
)
|
|
|
|
// Unit tests of the Blog Editor field types (Phase 16.2 plan 06).
|
|
|
|
// blogOptions are the post content field's options.
|
|
var blogOptions = map[string]any{"htmlColumn": "content_html", "uploadFolder": "posts"}
|
|
|
|
// noTranslated is a Translated reader that fails the test when called.
|
|
func noTranslated(t *testing.T) func(string) (string, bool, error) {
|
|
return func(attribute string) (string, bool, error) {
|
|
t.Errorf("Translated(%q) called for HTML content", attribute)
|
|
return "", false, nil
|
|
}
|
|
}
|
|
|
|
// translatedReader serves one attribute and records what was asked.
|
|
func translatedReader(asked *[]string, value string, found bool, err error) func(string) (string, bool, error) {
|
|
return func(attribute string) (string, bool, error) {
|
|
*asked = append(*asked, attribute)
|
|
return value, found, err
|
|
}
|
|
}
|
|
|
|
func TestBlogEditorLoad(t *testing.T) {
|
|
const markdown = "## Nagłówek\n\nTekst z *kursywą*."
|
|
rendered, err := FormatHTML(markdown)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if rendered == markdown || rendered == "" {
|
|
t.Fatalf("FormatHTML(%q) = %q", markdown, rendered)
|
|
}
|
|
html := "<p>Grzyby</p>"
|
|
empty := ""
|
|
|
|
t.Run("HTML content is served verbatim", func(t *testing.T) {
|
|
for _, stored := range []string{
|
|
"<p>Grzyby rosną.</p>\n<footer class=\"author-footer\"><p>Autor</p></footer>\n",
|
|
"\n \t<p>leading white space</p>",
|
|
"<!-- imported --><p>leading comment</p>",
|
|
"\n<!-- imported -->\n<p>both</p>\n",
|
|
"<style>.x{}</style><p>unsafe markup is served too; Decode strips it on save</p>",
|
|
} {
|
|
got, err := loadBlogHTML(context.Background(), pact.FormFieldValue{
|
|
Options: blogOptions, Locale: "pl", Default: true, Value: stored, Translated: noTranslated(t),
|
|
})
|
|
if err != nil || got != stored {
|
|
t.Fatalf("Load(%q) = %q, %v; want the stored bytes", stored, got, err)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("markdown content is served as that locale's content_html", func(t *testing.T) {
|
|
var asked []string
|
|
got, err := loadBlogHTML(context.Background(), pact.FormFieldValue{
|
|
Options: blogOptions, Locale: "en", Value: markdown,
|
|
Translated: translatedReader(&asked, "<h2>Imported</h2>\n", true, nil),
|
|
})
|
|
if err != nil || got != "<h2>Imported</h2>\n" {
|
|
t.Fatalf("Load = %q, %v", got, err)
|
|
}
|
|
if len(asked) != 1 || asked[0] != "content_html" {
|
|
t.Fatalf("asked %v, want [content_html]", asked)
|
|
}
|
|
})
|
|
|
|
t.Run("an empty or missing content_html falls back to FormatHTML", func(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
translated func(string) (string, bool, error)
|
|
}{
|
|
{"empty", translatedReader(new([]string), "", true, nil)},
|
|
{"missing", translatedReader(new([]string), "", false, nil)},
|
|
{"no writer", nil},
|
|
} {
|
|
got, err := loadBlogHTML(context.Background(), pact.FormFieldValue{
|
|
Options: blogOptions, Locale: "pl", Default: true, Value: markdown, Translated: tc.translated,
|
|
})
|
|
if err != nil || got != rendered {
|
|
t.Fatalf("%s: Load = %q, %v; want %q", tc.name, got, err, rendered)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("a Translated error is the Load error", func(t *testing.T) {
|
|
boom := errors.New("read failed")
|
|
_, err := loadBlogHTML(context.Background(), pact.FormFieldValue{
|
|
Options: blogOptions, Value: markdown, Translated: translatedReader(new([]string), "", false, boom),
|
|
})
|
|
if !errors.Is(err, boom) {
|
|
t.Fatalf("err = %v, want %v", err, boom)
|
|
}
|
|
})
|
|
|
|
t.Run("nil and empty values give the empty string", func(t *testing.T) {
|
|
for _, value := range []any{nil, (*string)(nil), &empty, "", " \n"} {
|
|
got, err := loadBlogHTML(context.Background(), pact.FormFieldValue{Options: blogOptions, Value: value, Translated: noTranslated(t)})
|
|
want := storedText(value)
|
|
if err != nil || got != want {
|
|
t.Fatalf("Load(%#v) = %q, %v; want %q", value, got, err, want)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("a *string is dereferenced", func(t *testing.T) {
|
|
got, err := loadBlogHTML(context.Background(), pact.FormFieldValue{Value: &html})
|
|
if err != nil || got != html {
|
|
t.Fatalf("Load = %q, %v", got, err)
|
|
}
|
|
})
|
|
|
|
t.Run("without htmlColumn every value is served as stored", func(t *testing.T) {
|
|
got, err := loadBlogHTML(context.Background(), pact.FormFieldValue{Options: map[string]any{"uploadFolder": "categories"}, Value: markdown, Translated: noTranslated(t)})
|
|
if err != nil || got != markdown {
|
|
t.Fatalf("Load = %q, %v; want the stored markdown", got, err)
|
|
}
|
|
})
|
|
|
|
t.Run("the registered types use this Load", func(t *testing.T) {
|
|
for _, typ := range BlogEditorTypes(nil) {
|
|
if typ.Load == nil {
|
|
t.Fatalf("%s has no Load", typ.Name)
|
|
}
|
|
got, err := typ.Load(context.Background(), pact.FormFieldValue{Options: blogOptions, Value: "\n<!-- c --><p>x</p>"})
|
|
if err != nil || got != "\n<!-- c --><p>x</p>" {
|
|
t.Fatalf("%s: Load = %q, %v", typ.Name, got, err)
|
|
}
|
|
}
|
|
})
|
|
}
|