- fields.yaml trigger (action disable|hide, field, condition checked|unchecked) compiled to FormField.Trigger - boot errors for malformed triggers, controlling fields that are not a switch or checkbox, and triggers in repeater rows, settings and relation forms - Administrators permissions overrides are disabled while Superuser is on (D-06) - SPA: trigger.ts (triggerState, applyTriggers), FORM_TRIGGER_NOTES, the trigger note in FormField, FormView tabs/banner/payload after triggers, PreviewView hide only - regenerated admin OpenAPI and schema.d.ts; rebuilt modules/boardwalk/dist; docs and README
108 lines
5.0 KiB
Go
108 lines
5.0 KiB
Go
package cabana
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"testing/fstest"
|
|
)
|
|
|
|
type triggerController struct{}
|
|
|
|
func (triggerController) ID() string { return "acme.trig.items" }
|
|
func (triggerController) ModelName() string { return "Item" }
|
|
func (triggerController) ConfigDir() string { return "controllers/items" }
|
|
|
|
func compileTriggerForm(fields string) (*FormSchema, error) {
|
|
fsys := fstest.MapFS{
|
|
"controllers/items/config_form.yaml": &fstest.MapFile{Data: []byte("form: ~/plugins/acme/trig/models/item/fields.yaml\nmodelClass: Item\n")},
|
|
"models/item/fields.yaml": &fstest.MapFile{Data: []byte(fields)},
|
|
}
|
|
return CompileForm("acme.trig", triggerController{}, fsys)
|
|
}
|
|
|
|
func TestFieldTriggerSmoke(t *testing.T) {
|
|
base := "fields:\n is_admin:\n type: switch\n title:\n type: text\n"
|
|
valid := base + " notes:\n type: textarea\n trigger:\n action: disable\n field: is_admin\n condition: checked\n"
|
|
|
|
t.Run("a valid trigger compiles and reaches the schema", func(t *testing.T) {
|
|
schema, err := compileTriggerForm(valid)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
view, err := schema.Localize(context.Background(), nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var got *FieldTrigger
|
|
for _, field := range view.Fields {
|
|
if field.Name == "notes" {
|
|
got = field.Trigger
|
|
}
|
|
}
|
|
if got == nil || *got != (FieldTrigger{Action: "disable", Field: "is_admin", Condition: "checked"}) {
|
|
t.Fatalf("notes trigger = %+v", got)
|
|
}
|
|
if got == schema.Fields[2].Trigger {
|
|
t.Fatal("the localized view shares the cached trigger")
|
|
}
|
|
})
|
|
|
|
trig := func(body string) string { return base + " notes:\n type: textarea\n trigger:\n" + body }
|
|
for _, tc := range []struct{ name, fields, want string }{
|
|
{"unknown action", trig(" action: show\n field: is_admin\n condition: checked\n"), "action show is not supported"},
|
|
{"unknown condition", trig(" action: hide\n field: is_admin\n condition: value[1]\n"), "condition value[1] is not supported"},
|
|
{"extra sub-key", trig(" action: hide\n field: is_admin\n condition: checked\n value: 1\n"), "unknown key value"},
|
|
{"missing field", trig(" action: hide\n condition: checked\n"), "field is required"},
|
|
{"not a mapping", base + " notes:\n type: textarea\n trigger: is_admin\n", "trigger must be a mapping"},
|
|
{"self reference", base + " notes:\n type: checkbox\n trigger:\n action: hide\n field: notes\n condition: checked\n", "names the field itself"},
|
|
{"unknown controlling field", trig(" action: hide\n field: active\n condition: checked\n"), "trigger field active is not a field of this form"},
|
|
{"text controlling field", trig(" action: hide\n field: title\n condition: unchecked\n"), "trigger field title must be a switch or checkbox"},
|
|
{"in a repeater row", base + " rows:\n type: repeater\n form:\n fields:\n flag:\n type: checkbox\n notes:\n type: text\n trigger:\n action: hide\n field: flag\n condition: checked\n", "trigger is not supported in a repeater row"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := compileTriggerForm(tc.fields)
|
|
if err == nil {
|
|
t.Fatalf("compiled; want an error containing %q", tc.want)
|
|
}
|
|
msg := err.Error()
|
|
if !strings.Contains(msg, tc.want) || !strings.Contains(msg, "trigger") || !strings.Contains(msg, "notes") {
|
|
t.Fatalf("err = %v, want it to name notes, trigger and %q", err, tc.want)
|
|
}
|
|
if !strings.Contains(msg, "acme.trig.items") {
|
|
t.Fatalf("err = %v, want the controller named", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
t.Run("settings and relation forms refuse trigger", func(t *testing.T) {
|
|
field := FormField{Name: "notes", Type: "text", Trigger: &FieldTrigger{Action: "hide", Field: "flag", Condition: "checked"}}
|
|
if err := settingsFormOnly(field); err == nil || !strings.Contains(err.Error(), "trigger") {
|
|
t.Fatalf("settingsFormOnly = %v", err)
|
|
}
|
|
if err := controllerFormOnly(field); err == nil || !strings.Contains(err.Error(), "trigger") {
|
|
t.Fatalf("controllerFormOnly = %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("the Administrators form disables overrides while Superuser is on", func(t *testing.T) {
|
|
reg, err := compileWithScreens(t, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cc, ok := reg.Get(AdministratorsControllerID)
|
|
if !ok || cc.Form == nil {
|
|
t.Fatal("no administrators form")
|
|
}
|
|
for _, field := range cc.Form.Fields {
|
|
if field.Name == usersPermissionsField {
|
|
if field.Trigger == nil || *field.Trigger != (FieldTrigger{Action: "disable", Field: usersSuperuserField, Condition: "checked"}) {
|
|
t.Fatalf("permissions trigger = %+v", field.Trigger)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
t.Fatal("administrators form has no permissions field")
|
|
})
|
|
}
|