test(17-06): framework and admin SPA unit tests for the blog companions
- signpost: activation refusals, host overrides, D-02 pattern layers incl. PatternFrom, ReferenceOptions depths - seaglass: removal report, bluemonday bypass corpus, Check - pact Option.Depth omitempty and the menu item type contracts; party activates menu item types after Register and before the first Boot - cabana: repeater compile and decode, dependsOn compile and recheck, richeditor keys and notices, reorder and singleton boot checks (Docker-free); repeater save, dependent recheck, richeditor save, reorder and singleton routes on Postgres; conformance cases for the documented 404, 409 and 422 answers of the new routes - admin: richeditor value contract, link popover, notices and chunk failure; repeater bounds, remove and reorder, collapsed rows; dependent dropdown states and last request wins; reorder announcements, partition switch and tree rules; SingletonForm spec - gate: required names in the go, admin and postgres stages
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package party
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"git.golem15.com/golem15/summercms/modules/pact"
|
||||
"git.golem15.com/golem15/summercms/modules/phrasebook"
|
||||
"git.golem15.com/golem15/summercms/modules/postcard"
|
||||
"git.golem15.com/golem15/summercms/modules/signpost"
|
||||
)
|
||||
|
||||
type tracePlugin struct {
|
||||
@@ -346,7 +348,6 @@ func TestActivatePublishesActivatedOrderAndRefusesSecond(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestRegisterNilIsNoop(t *testing.T) {
|
||||
Register(nil)
|
||||
}
|
||||
@@ -527,12 +528,12 @@ type mailPlugin struct {
|
||||
sawMailer *bool
|
||||
}
|
||||
|
||||
func (p *mailPlugin) ID() string { return p.id }
|
||||
func (p *mailPlugin) Requires() []string { return nil }
|
||||
func (p *mailPlugin) Register(*backpack.App) error { return nil }
|
||||
func (p *mailPlugin) MailTemplatesFS() fs.FS { return p.fsys }
|
||||
func (p *mailPlugin) MailTemplates() []string { return p.templates }
|
||||
func (p *mailPlugin) MailLayouts() map[string]string { return p.layouts }
|
||||
func (p *mailPlugin) ID() string { return p.id }
|
||||
func (p *mailPlugin) Requires() []string { return nil }
|
||||
func (p *mailPlugin) Register(*backpack.App) error { return nil }
|
||||
func (p *mailPlugin) MailTemplatesFS() fs.FS { return p.fsys }
|
||||
func (p *mailPlugin) MailTemplates() []string { return p.templates }
|
||||
func (p *mailPlugin) MailLayouts() map[string]string { return p.layouts }
|
||||
|
||||
func (p *mailPlugin) Boot(app *backpack.App) error {
|
||||
if p.sawMailer == nil {
|
||||
@@ -551,7 +552,7 @@ func TestActivatePublishesMailerBeforeBoot(t *testing.T) {
|
||||
templates: []string{"golem15.hello::mail.hello"},
|
||||
layouts: map[string]string{"hello": "golem15.hello::mail.layouts.hello"},
|
||||
fsys: fstest.MapFS{
|
||||
"views/mail/hello.htm": {Data: []byte("subject = \"Hi\"\nlayout = \"hello\"\n==\nHi.\n")},
|
||||
"views/mail/hello.htm": {Data: []byte("subject = \"Hi\"\nlayout = \"hello\"\n==\nHi.\n")},
|
||||
"views/mail/layouts/hello.htm": {Data: []byte("name = \"Hello\"\n==\n{{ .Content }}\n==\n<div>{{ .Content }}</div>\n")},
|
||||
},
|
||||
}
|
||||
@@ -580,3 +581,80 @@ func TestActivateMissingMailTemplateFailsBootWithName(t *testing.T) {
|
||||
t.Fatalf("error %q should name the boot plugin and missing template", msg)
|
||||
}
|
||||
}
|
||||
|
||||
// menuTypesPlugin registers one menu item type and records whether the
|
||||
// signpost registry was published at Register and at Boot.
|
||||
type menuTypesPlugin struct {
|
||||
id string
|
||||
key string
|
||||
log *[]string
|
||||
atRegister *bool
|
||||
atBoot *bool
|
||||
typesAtBoot *int
|
||||
}
|
||||
|
||||
func (p *menuTypesPlugin) ID() string { return p.id }
|
||||
func (p *menuTypesPlugin) Requires() []string { return nil }
|
||||
|
||||
func (p *menuTypesPlugin) MenuItemTypes() []pact.MenuItemType {
|
||||
*p.log = append(*p.log, p.id+":types")
|
||||
return []pact.MenuItemType{{Key: p.key, DefaultPattern: "/:slug", Resolve: func(context.Context, pact.MenuItemResolve) (*pact.ResolvedMenuItem, error) {
|
||||
return &pact.ResolvedMenuItem{}, nil
|
||||
}}}
|
||||
}
|
||||
|
||||
func (p *menuTypesPlugin) Register(app *backpack.App) error {
|
||||
*p.log = append(*p.log, p.id+":register")
|
||||
_, ok := app.Lookup[*signpost.Registry]()
|
||||
*p.atRegister = ok
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *menuTypesPlugin) Boot(app *backpack.App) error {
|
||||
*p.log = append(*p.log, p.id+":boot")
|
||||
reg, ok := app.Lookup[*signpost.Registry]()
|
||||
*p.atBoot = ok
|
||||
if ok {
|
||||
*p.typesAtBoot = len(reg.Types())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestActivateMenuItemTypesOrder pins the party hook: menu item types are
|
||||
// collected after every Register and before the first Boot, so a Boot sees
|
||||
// the full registry; a duplicate key fails activation naming both plugins and
|
||||
// no plugin boots.
|
||||
func TestActivateMenuItemTypesOrder(t *testing.T) {
|
||||
var log []string
|
||||
var reg1, boot1, reg2, boot2 bool
|
||||
var n1, n2 int
|
||||
a := &menuTypesPlugin{id: "acme.a", key: "acme-a", log: &log, atRegister: ®1, atBoot: &boot1, typesAtBoot: &n1}
|
||||
b := &menuTypesPlugin{id: "acme.b", key: "acme-b", log: &log, atRegister: ®2, atBoot: &boot2, typesAtBoot: &n2}
|
||||
app := backpack.New(&compass.Config{})
|
||||
if _, err := activate([]Plugin{a, b}, app, []string{"acme.a", "acme.b"}); err != nil {
|
||||
t.Fatalf("activate: %v", err)
|
||||
}
|
||||
if reg1 || reg2 {
|
||||
t.Fatal("the registry existed during Register")
|
||||
}
|
||||
if !boot1 || !boot2 || n1 != 2 || n2 != 2 {
|
||||
t.Fatalf("Boot saw registry %v/%v with %d/%d types, want both and 2", boot1, boot2, n1, n2)
|
||||
}
|
||||
want := "acme.a:register,acme.b:register,acme.a:types,acme.b:types,acme.a:boot,acme.b:boot"
|
||||
if got := strings.Join(log, ","); got != want {
|
||||
t.Fatalf("order = %s\nwant %s", got, want)
|
||||
}
|
||||
|
||||
log = nil
|
||||
dupA := &menuTypesPlugin{id: "acme.a", key: "acme-same", log: &log, atRegister: ®1, atBoot: &boot1, typesAtBoot: &n1}
|
||||
dupB := &menuTypesPlugin{id: "acme.b", key: "acme-same", log: &log, atRegister: ®2, atBoot: &boot2, typesAtBoot: &n2}
|
||||
_, err := activate([]Plugin{dupA, dupB}, backpack.New(&compass.Config{}), []string{"acme.a", "acme.b"})
|
||||
if err == nil || !strings.Contains(err.Error(), "party: menu item types") || !strings.Contains(err.Error(), "acme.a") || !strings.Contains(err.Error(), "acme.b") {
|
||||
t.Fatalf("duplicate error = %v", err)
|
||||
}
|
||||
for _, entry := range log {
|
||||
if strings.HasSuffix(entry, ":boot") {
|
||||
t.Fatalf("a plugin booted after the duplicate: %v", log)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user