package seo_test import ( "encoding/json" "net/http" "strings" "testing" ) // tabsOf returns the schema's tab labels in first-appearance order. func tabsOf(fields []schemaField) []string { var out []string seen := map[string]bool{} for _, f := range fields { if !seen[f.Tab] { seen[f.Tab] = true out = append(out, f.Tab) } } return out } // TestSettingsTypeTabs checks the D-104 per-type tabs on the settings // singleton: one tab per content type in registration order between // General and Indexing, their values in golem15_seo_type_settings with the // D-21 defaults, template checks, and the inject switch hiding the SEO tab. func TestSettingsTypeTabs(t *testing.T) { t.Run("two content types", func(t *testing.T) { env := bootEnv(t) token := env.admin(t, true) fields := env.formSchema(t, token, "golem15/seo/settings") if got, want := strings.Join(tabsOf(fields), " | "), "General | Page | Tags | Indexing | Addresses | Verification"; got != want { t.Fatalf("settings tabs = %q, want %q", got, want) } findField(t, fields, "type_acme_page_inject") findField(t, fields, "type_acme_tag_robots") show := env.expect(t, http.StatusOK, http.MethodGet, adminAPI("/golem15/seo/settings/1"), token, nil) var body struct { Data map[string]any `json:"data"` } if err := json.Unmarshal(show.Body.Bytes(), &body); err != nil { t.Fatal(err) } if body.Data["type_acme_page_inject"] != true || body.Data["type_acme_tag_robots"] != "noindex,follow" || body.Data["type_acme_page_robots"] != "" { t.Fatalf("per-type defaults = %v", body.Data) } rec := env.call(t, http.MethodPut, adminAPI("/golem15/seo/settings/1"), token, map[string]any{ "type_acme_page_title_template": "%title% - %nope%", }) if rec.Code != http.StatusUnprocessableEntity || !strings.Contains(rec.Body.String(), "type_acme_page_title_template") || !strings.Contains(rec.Body.String(), "%nope%") { t.Fatalf("unknown placeholder = %d %s, want 422 on the type field naming %%nope%%", rec.Code, rec.Body.String()) } id := env.createPage(t, token, map[string]any{"title": "About", "slug": "about", "published": true, "seo_title": map[string]any{"en": "Stored"}}) env.expect(t, http.StatusOK, http.MethodPut, adminAPI("/golem15/seo/settings/1"), token, map[string]any{ "type_acme_page_inject": false, "type_acme_page_title_template": "%title% · Pages", }) var row struct { Inject bool TitleTemplate string } if err := env.db.Raw(`SELECT inject, title_template FROM golem15_seo_type_settings WHERE type_key = ?`, pagesTypeKey).Scan(&row).Error; err != nil || row.Inject || row.TitleTemplate != "%title% · Pages" { t.Fatalf("type settings row = %+v err=%v", row, err) } for _, f := range env.formSchema(t, token, "acme/pages/pages") { if strings.HasPrefix(f.Name, "seo_") { t.Fatalf("SEO tab field %s shown with inject off", f.Name) } } got := env.resolve(t, "/pages/about") if seoBody, _ := got["seo"].(map[string]any); seoBody["title"] != "Stored" { t.Fatalf("resolve with the tab off = %v, want the stored title (id %s)", got, id) } }) t.Run("no content types", func(t *testing.T) { env := bootPlugins(t, []string{"golem15.seo"}) token := env.admin(t, true) if got, want := strings.Join(tabsOf(env.formSchema(t, token, "golem15/seo/settings")), " | "), "General | Indexing | Addresses | Verification"; got != want { t.Fatalf("settings tabs = %q, want %q", got, want) } }) }