Files
sm-seo-plugin/seo_tab_test.go

127 lines
5.1 KiB
Go

package seo_test
import (
"net/http"
"net/url"
"strings"
"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)
}
}
// TestSEOTabPermission checks D-15 on the SEO tab: an administrator
// without golem15.seo.manage gets no SEO fields, and a posted seo_title
// changes nothing.
func TestSEOTabPermission(t *testing.T) {
env := bootEnv(t)
super := env.admin(t, true)
editor := env.admin(t, false, pagesPermission)
for _, f := range env.formSchema(t, editor, "acme/pages/pages") {
if strings.HasPrefix(f.Name, "seo_") {
t.Fatalf("editor without golem15.seo.manage sees %s", f.Name)
}
}
id := env.createPage(t, editor, map[string]any{"title": "About", "slug": "about", "published": true, "seo_title": map[string]any{"en": "Sneaky"}})
var count int64
if err := env.db.Raw(`SELECT COUNT(*) FROM golem15_seo_entries`).Scan(&count).Error; err != nil || count != 0 {
t.Fatalf("entries after an editor create = %d err=%v, want 0", count, err)
}
env.expect(t, http.StatusOK, http.MethodPut, adminAPI("/acme/pages/pages/"+id), super, map[string]any{
"title": "About", "slug": "about", "published": true, "seo_title": map[string]any{"en": "Custom"},
})
env.expect(t, http.StatusOK, http.MethodPut, adminAPI("/acme/pages/pages/"+id), editor, map[string]any{
"title": "About us", "slug": "about", "published": true, "seo_title": map[string]any{"en": "Hacked"},
})
got := env.resolve(t, "/pages/about")
if seoBody, _ := got["seo"].(map[string]any); seoBody["title"] != "Custom" {
t.Fatalf("resolve = %v, want the title set with the permission", got)
}
show := env.expect(t, http.StatusOK, http.MethodGet, adminAPI("/acme/pages/pages/"+id), editor, nil)
if strings.Contains(show.Body.String(), "seo_title") {
t.Fatalf("editor show carries SEO values: %s", show.Body.String())
}
}