- posts describe PublishedAt, their categories by name as Parents, the author (a journal-author reference while author pages are on), the featured and body images, YouTube embeds, the heading outline and TopHeading (2 while the H1 setting is on, else 0) - categories describe their parent, the pillar text's outline and TopHeading; kinds article/collection and the journal_ fixing tools - golem15.journal.author_pages (AuthorPagesKey, default true)
188 lines
8.2 KiB
Go
188 lines
8.2 KiB
Go
package journal_test
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.golem15.com/golem15/sm-journal-plugin/classes"
|
|
"git.golem15.com/golem15/sm-journal-plugin/models"
|
|
translate "git.golem15.com/golem15/sm-translate-plugin/classes"
|
|
"git.golem15.com/golem15/summercms/modules/lagoon/attach"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
"git.golem15.com/golem15/summercms/modules/signpost"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// seedLocales enables en (default) and pl.
|
|
func seedLocales(t *testing.T, gdb *gorm.DB) {
|
|
t.Helper()
|
|
for _, row := range []struct {
|
|
code, name string
|
|
def bool
|
|
order int
|
|
}{{"en", "English", true, 1}, {"pl", "Polski", false, 2}} {
|
|
if err := gdb.Exec(`INSERT INTO golem15_translate_locales (code, name, is_default, is_enabled, sort_order)
|
|
SELECT ?, ?, ?, TRUE, ? WHERE NOT EXISTS (SELECT 1 FROM golem15_translate_locales WHERE code = ?)`,
|
|
row.code, row.name, row.def, row.order, row.code).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// seedAuthor inserts a backend user with an optional author slug.
|
|
func seedAuthor(t *testing.T, gdb *gorm.DB, login, first, last, slug string) uint {
|
|
t.Helper()
|
|
var userID uint
|
|
if err := gdb.Raw(`INSERT INTO backend_users (login, email, password, first_name, last_name, is_activated, is_superuser, created_at, updated_at)
|
|
VALUES (?, ?, 'x', ?, ?, TRUE, FALSE, NOW(), NOW()) RETURNING id`, login, login+"@example.test", first, last).Scan(&userID).Error; err != nil || userID == 0 {
|
|
t.Fatalf("backend user %s: %d %v", login, userID, err)
|
|
}
|
|
if slug != "" {
|
|
if err := gdb.Exec(`UPDATE backend_users SET golem15_bloghub_author_slug = ? WHERE id = ?`, slug, userID).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
return userID
|
|
}
|
|
|
|
func describe(t *testing.T, reg *signpost.Registry, key, recordID string) *pact.ContentRecord {
|
|
t.Helper()
|
|
rec, err := reg.Describe(context.Background(), key, recordID, "")
|
|
if err != nil || rec == nil {
|
|
t.Fatalf("Describe %s %s = %+v, %v", key, recordID, rec, err)
|
|
}
|
|
return rec
|
|
}
|
|
|
|
// TestJournalDescriptorFields (D-93, D-43, D-96, P33-06): posts describe
|
|
// their publication, categories in order, authors, featured and body
|
|
// images, YouTube embeds, heading outline, top heading level, kind and
|
|
// tools; categories their parent, pillar outline, top heading and tools;
|
|
// tags their kind. TopHeading follows the H1 setting.
|
|
func TestJournalDescriptorFields(t *testing.T) {
|
|
requirePostgres(t)
|
|
for _, tc := range []struct {
|
|
name string
|
|
files map[string]string
|
|
authorPages bool
|
|
}{
|
|
{"author pages on", map[string]string{}, true},
|
|
{"author pages off", map[string]string{"golem15.yaml": "journal:\n author_pages: false\n"}, false},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, gdb, reg := bootMenuItemTypes(t, tc.files)
|
|
seedLocales(t, gdb)
|
|
ctx := context.Background()
|
|
alpha := menuCategory(t, gdb, "Alpha", "alpha", nil, 1, 4)
|
|
beta := menuCategory(t, gdb, "Beta", "beta", nil, 5, 6)
|
|
child := menuCategory(t, gdb, "Child", "child", &alpha.ID, 2, 3)
|
|
pillar := "<h1>Pillar</h1><p>x</p><h2>Part</h2>"
|
|
if err := gdb.Model(&models.Category{}).Where("id = ?", alpha.ID).Update("content", pillar).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
author := seedAuthor(t, gdb, "ada", "Ada", "Lovelace", "ada-l")
|
|
past := time.Date(2026, 3, 4, 10, 0, 0, 0, time.UTC)
|
|
body := `<h1>Top</h1><p>x</p><h2> Sub heading </h2>` +
|
|
`<p><img src="/a.jpg" alt="A" width="640" height="480"><img src="/d.jpg" alt=""></p>` +
|
|
`<figure data-embed="youtube" data-id="dQw4w9WgXcQ"></figure><h3><a href="/x">Linked</a></h3>`
|
|
post := models.Post{Title: "Hello", Slug: "hello", Content: body, ContentHTML: body, Published: true, PublishedAt: &past, UserID: &author}
|
|
if err := gdb.Create(&post).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
attachCategory(t, gdb, post, beta)
|
|
attachCategory(t, gdb, post, alpha)
|
|
for field, value := range map[string]string{"slug": "witaj", "title": "Witaj", "content_html": `<h2>PL</h2><img src="/p.jpg" alt="P">`} {
|
|
if err := translate.SetTranslated(ctx, gdb, &post, field, "pl", value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
cover := attach.File{DiskName: "cover.jpg", FileName: "cover.jpg", ContentType: "image/jpeg",
|
|
AttachmentType: post.MorphName(), AttachmentID: id(post.ID), Field: "featured_images"}
|
|
if err := gdb.Create(&cover).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tag := models.Tag{Name: "Mushrooms", Slug: "mushrooms"}
|
|
if err := gdb.Create(&tag).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
typ, ok := reg.ContentType(classes.MenuTypePost)
|
|
wantTools := map[string]string{
|
|
pact.ContentToolEdit: "journal_update_post",
|
|
pact.ContentToolHeadings: "journal_fix_headings",
|
|
pact.ContentToolImageAlt: "journal_set_image_alt",
|
|
}
|
|
if !ok || typ.Content.Kind != pact.ContentKindArticle || !reflect.DeepEqual(typ.Content.Tools, wantTools) {
|
|
t.Fatalf("post descriptor kind %q tools %v", typ.Content.Kind, typ.Content.Tools)
|
|
}
|
|
rec := describe(t, reg, classes.MenuTypePost, id(post.ID))
|
|
if rec.PublishedAt == nil || !rec.PublishedAt.Equal(past) {
|
|
t.Fatalf("PublishedAt = %v", rec.PublishedAt)
|
|
}
|
|
wantParents := []pact.ContentRef{{Type: classes.MenuTypeCategory, ID: id(alpha.ID)}, {Type: classes.MenuTypeCategory, ID: id(beta.ID)}}
|
|
if !reflect.DeepEqual(rec.Parents, wantParents) {
|
|
t.Fatalf("Parents = %+v, want %+v", rec.Parents, wantParents)
|
|
}
|
|
wantRef := pact.ContentRef{}
|
|
if tc.authorPages {
|
|
wantRef = pact.ContentRef{Type: classes.MenuTypeAuthor, ID: id(author)}
|
|
}
|
|
if !reflect.DeepEqual(rec.Authors, []pact.ContentPerson{{Name: "Ada Lovelace", Ref: wantRef}}) {
|
|
t.Fatalf("Authors = %+v", rec.Authors)
|
|
}
|
|
if rec.TopHeading != 2 {
|
|
t.Fatalf("TopHeading = %d, want 2 while the setting is on", rec.TopHeading)
|
|
}
|
|
en, pl := rec.Locales[0], rec.Locales[1]
|
|
wantImages := []pact.ContentImage{
|
|
{URL: cover.URL(), Alt: "Hello"},
|
|
{URL: "/a.jpg", Alt: "A", Width: 640, Height: 480},
|
|
{URL: "/d.jpg", Alt: "", Decorative: true},
|
|
}
|
|
if !reflect.DeepEqual(en.Images, wantImages) {
|
|
t.Fatalf("en Images = %+v, want %+v", en.Images, wantImages)
|
|
}
|
|
if !reflect.DeepEqual(pl.Images, []pact.ContentImage{{URL: cover.URL(), Alt: "Witaj"}, {URL: "/p.jpg", Alt: "P"}}) {
|
|
t.Fatalf("pl Images = %+v", pl.Images)
|
|
}
|
|
if !reflect.DeepEqual(en.Videos, []pact.ContentVideo{{Provider: "youtube", ID: "dQw4w9WgXcQ"}}) || len(pl.Videos) != 0 {
|
|
t.Fatalf("Videos = %+v / %+v", en.Videos, pl.Videos)
|
|
}
|
|
wantHeadings := []pact.ContentHeading{{Level: 1, Text: "Top"}, {Level: 2, Text: "Sub heading"}, {Level: 3, Text: "Linked"}}
|
|
if !reflect.DeepEqual(en.Headings, wantHeadings) || !reflect.DeepEqual(pl.Headings, []pact.ContentHeading{{Level: 2, Text: "PL"}}) {
|
|
t.Fatalf("Headings = %+v / %+v", en.Headings, pl.Headings)
|
|
}
|
|
|
|
catType, _ := reg.ContentType(classes.MenuTypeCategory)
|
|
if catType.Content.Kind != pact.ContentKindCollection || !reflect.DeepEqual(catType.Content.Tools, map[string]string{pact.ContentToolHeadings: "journal_fix_headings"}) {
|
|
t.Fatalf("category descriptor kind %q tools %v", catType.Content.Kind, catType.Content.Tools)
|
|
}
|
|
kid := describe(t, reg, classes.MenuTypeCategory, id(child.ID))
|
|
if !reflect.DeepEqual(kid.Parents, []pact.ContentRef{{Type: classes.MenuTypeCategory, ID: id(alpha.ID)}}) || len(kid.Locales[0].Headings) != 0 {
|
|
t.Fatalf("child category = %+v", kid)
|
|
}
|
|
top := describe(t, reg, classes.MenuTypeCategory, id(alpha.ID))
|
|
if len(top.Parents) != 0 || top.TopHeading != 2 ||
|
|
!reflect.DeepEqual(top.Locales[0].Headings, []pact.ContentHeading{{Level: 1, Text: "Pillar"}, {Level: 2, Text: "Part"}}) {
|
|
t.Fatalf("pillar category = %+v", top)
|
|
}
|
|
tagType, _ := reg.ContentType(classes.MenuTypeTag)
|
|
if tagType.Content.Kind != pact.ContentKindCollection {
|
|
t.Fatalf("tag kind %q", tagType.Content.Kind)
|
|
}
|
|
|
|
if err := gdb.Exec(`UPDATE golem15_journal_settings SET content_h1_to_h2 = FALSE WHERE id = 1`).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := describe(t, reg, classes.MenuTypePost, id(post.ID)).TopHeading; got != 0 {
|
|
t.Fatalf("post TopHeading with the setting off = %d, want 0", got)
|
|
}
|
|
if got := describe(t, reg, classes.MenuTypeCategory, id(alpha.ID)).TopHeading; got != 0 {
|
|
t.Fatalf("category TopHeading with the setting off = %d, want 0", got)
|
|
}
|
|
})
|
|
}
|
|
}
|