package journal_test import ( "encoding/json" "net/http" "strings" "testing" "git.golem15.com/golem15/sm-journal-plugin/controllers" "git.golem15.com/golem15/sm-journal-plugin/models" ) const journalAPIPrefix = "/_journal/api/v1" // setH1ToH2 switches the Journal H1 setting on the settings row. func setH1ToH2(t *testing.T, env *adminEnv, on bool) { t.Helper() if err := env.gdb.Exec(`UPDATE golem15_journal_settings SET content_h1_to_h2 = ? WHERE id = 1`, on).Error; err != nil { t.Fatal(err) } } // publishPost publishes a post with a past published_at. func publishPost(t *testing.T, env *adminEnv, id uint) { t.Helper() if err := env.gdb.Exec(`UPDATE golem15_journal_posts SET published = TRUE, published_at = NOW() - INTERVAL '1 hour' WHERE id = ?`, id).Error; err != nil { t.Fatal(err) } } // showH1Flag is GET /_journal/api/v1/posts/{slug}'s content_h1_to_h2. func showH1Flag(t *testing.T, env *adminEnv, slug string) any { t.Helper() rec := env.expect(t, http.StatusOK, http.MethodGet, journalAPIPrefix+"/posts/"+slug, "", nil) var body struct { Data map[string]any `json:"data"` } if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { t.Fatalf("show body %s: %v", rec.Body.String(), err) } flag, ok := body.Data["content_h1_to_h2"] if !ok { t.Fatalf("show answer has no content_h1_to_h2: %s", rec.Body.String()) } return flag } // TestHeadingConversionTracer is the 33-02 tracer: an editor saves a post // whose content holds an H1 through the admin and gets an H2 back with the // h1_converted notice, and the public post answer reports the setting. // With the setting off the same save keeps the H1, adds no notice, and the // answer reports false. func TestHeadingConversionTracer(t *testing.T) { env := newPostsEnv(t) token := env.admin(t, controllers.PermissionAccessPosts, controllers.PermissionAccessPublish, controllers.PermissionAccessOtherPosts, ) const submitted = "
x
" save := func(t *testing.T, slug string) (uint, []adminNotice) { t.Helper() rec := env.expect(t, http.StatusCreated, http.MethodPost, adminAPI(adminPostsPath), token, map[string]any{ "title": map[string]any{"en": "Heading " + slug, "pl": "Nagłówek " + slug}, "slug": map[string]any{"en": slug, "pl": slug + "-pl"}, "content": map[string]any{"en": submitted, "pl": submitted}, }) return adminCreatedID(t, rec), adminNotices(t, rec) } t.Run("setting on", func(t *testing.T) { setH1ToH2(t, env, true) id, notices := save(t, "heading-on") assertHostContent(t, env, id, "on", "x
", "x
") pl := translatedAttrs(t, env, id, "pl") if pl["content"] != "x
" || pl["content_html"] != "x
" { t.Fatalf("pl content = %#v, content_html = %#v, want the H2", pl["content"], pl["content_html"]) } for _, locale := range []string{"en", "pl"} { found := false for _, n := range notices { if n.Field == "content" && n.Locale == locale && n.Message == "1 H1 heading was converted to H2, because the page shows the post title as the H1." { found = true } } if !found { t.Fatalf("%s notices = %#v, want the h1_converted phrase with count 1", locale, notices) } } publishPost(t, env, id) if flag := showH1Flag(t, env, "heading-on"); flag != true { t.Fatalf("content_h1_to_h2 = %#v, want true", flag) } }) t.Run("setting off", func(t *testing.T) { setH1ToH2(t, env, false) t.Cleanup(func() { setH1ToH2(t, env, true) }) id, notices := save(t, "heading-off") assertHostContent(t, env, id, "off", submitted, submitted) for _, n := range notices { if strings.Contains(n.Message, "H1") { t.Fatalf("setting off: notice %#v", n) } } if notices != nil { t.Fatalf("setting off: clean save carries meta.notices %#v", notices) } publishPost(t, env, id) if flag := showH1Flag(t, env, "heading-off"); flag != false { t.Fatalf("content_h1_to_h2 = %#v, want false", flag) } // D-50: switching the setting never rewrites stored content. setH1ToH2(t, env, true) var stored models.Post if err := env.gdb.First(&stored, id).Error; err != nil { t.Fatal(err) } if stored.Content != submitted || stored.ContentHTML != submitted { t.Fatalf("switching the setting on rewrote content: %q / %q", stored.Content, stored.ContentHTML) } }) }