package seo_test import ( "net/http" "net/url" "testing" seo "git.golem15.com/golem15/sm-seo-plugin" ) // seoTabLabel is the English label of seo.TabSEO. const seoTabLabel = "SEO" func urlQueryEscape(s string) string { return url.QueryEscape(s) } // seoField returns the schema field name, or fails. func findField(t *testing.T, fields []schemaField, name string) schemaField { t.Helper() for _, f := range fields { if f.Name == name { return f } } t.Fatalf("schema has no field %q: %+v", name, fields) return schemaField{} } // TestSEOTabTracer is the plan's tracer: an editor sets a record's SEO // title in the SEO tab the plugin adds to a content type it does not know, // the value lands in the plugin's own table, and the public resolve answer // returns it. func TestSEOTabTracer(t *testing.T) { env := bootEnv(t) token := env.admin(t, true) fields := env.formSchema(t, token, "acme/pages/pages") // The schema serves tab labels translated: golem15.seo::lang.tab.seo // is "SEO" in English. if f := findField(t, fields, "seo_title"); f.Tab != seoTabLabel { t.Fatalf("seo_title tab = %q, want %q (%s)", f.Tab, seoTabLabel, seo.TabSEO) } if last := fields[len(fields)-1]; last.Tab != seoTabLabel { t.Fatalf("last field %+v, want the SEO tab last", last) } id := env.createPage(t, token, map[string]any{"title": "About us", "slug": "about", "excerpt": "Who we are", "published": true}) got := env.resolve(t, "/pages/about") seoBody, _ := got["seo"].(map[string]any) if got["action"] != "ok" || seoBody["title"] != "About us" { t.Fatalf("resolve before the SEO title = %v, want the content title", got) } env.expect(t, http.StatusOK, http.MethodPut, adminAPI("/acme/pages/pages/"+id), token, map[string]any{ "title": "About us", "slug": "about", "excerpt": "Who we are", "published": true, "seo_title": map[string]any{"en": "Custom"}, }) got = env.resolve(t, "/pages/about") seoBody, _ = got["seo"].(map[string]any) if got["action"] != "ok" || seoBody["title"] != "Custom" { t.Fatalf("resolve = %v, want seo.title Custom", got) } if got["url"] != siteURL+"/pages/about" || seoBody["canonical"] != siteURL+"/pages/about" { t.Fatalf("url/canonical = %v / %v, want the configured site URL, never the request Host", got["url"], seoBody["canonical"]) } var stored string if err := env.db.Raw(`SELECT title FROM golem15_seo_entries WHERE model_type = ? AND record_id = ? AND locale = 'en'`, pagesModel, id).Scan(&stored).Error; err != nil || stored != "Custom" { t.Fatalf("stored entry title = %q err=%v", stored, err) } var seoColumns int64 if err := env.db.Raw(`SELECT COUNT(*) FROM information_schema.columns WHERE table_name = ? AND column_name LIKE 'seo%'`, pagesModel).Scan(&seoColumns).Error; err != nil || seoColumns != 0 { t.Fatalf("owner table SEO columns = %d err=%v, want none", seoColumns, err) } show := env.expect(t, http.StatusOK, http.MethodGet, adminAPI("/acme/pages/pages/"+id), token, nil) if !jsonContains(show.Body.String(), `"seo_title":{"en":"Custom"}`) { t.Fatalf("show body lacks the stored SEO title: %s", show.Body.String()) } got = env.resolve(t, "/pl/pages/about") seoBody, _ = got["seo"].(map[string]any) if got["locale"] != "pl" || seoBody["title"] != "About us" { t.Fatalf("pl resolve = %v, want the content title (no pl SEO title)", got) } if got := env.resolve(t, "/pages/missing"); got["action"] != "not_found" { t.Fatalf("unknown path = %v, want not_found", got) } }