127 lines
5.1 KiB
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())
|
|
}
|
|
}
|