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") }) }