diff --git a/go/validate.go b/go/validate.go index fc406eb..d951416 100644 --- a/go/validate.go +++ b/go/validate.go @@ -602,8 +602,21 @@ func isVisibilityRuleSatisfied(rule VisibilityRule, values url.Values) bool { conditionMet = strings.HasSuffix(fieldValue, condition.Comparison.Value) case "EqualsField": // the value stored in Comparison.Value is the name of another field - comparisonValue := values.Get(condition.Comparison.Value) - conditionMet = fieldValue == comparisonValue + // Check if ANY value from fieldName matches ANY value from the other field + fieldValues := values[condition.FieldName] + otherFieldValues := values[condition.Comparison.Value] + conditionMet = false + for _, fv := range fieldValues { + for _, ofv := range otherFieldValues { + if fv == ofv { + conditionMet = true + break + } + } + if conditionMet { + break + } + } case "GreaterThan": // Try to parse comparison value as float64 comparisonValue, comparisonErr := strconv.ParseFloat(condition.Comparison.Value, 64) diff --git a/go/validate_test.go b/go/validate_test.go index a37c73f..37e1674 100644 --- a/go/validate_test.go +++ b/go/validate_test.go @@ -1531,6 +1531,122 @@ func TestVisibilityRules(t *testing.T) { }, expectedError: ErrRequiredFieldMissing, }, + { + name: "EqualsField with multi-value fields - any value matches (Elm behavior)", + formFields: ` + [ + { + "name": "skills", + "type": { + "type": "ChooseMultiple", + "choices": ["Go", "Elm", "JavaScript", "Python"] + }, + "label": "Your Skills", + "presence": "Required" + }, + { + "name": "preferred_skills", + "type": { + "type": "ChooseMultiple", + "choices": ["Go", "Elm", "JavaScript", "Python"] + }, + "label": "Preferred Skills", + "presence": "Required" + }, + { + "type": { + "type": "ShortText", + "inputType": "text", + "attributes": { + "type": "text", + "class": "size-0-invisible", + "value": "form-invalid", + "pattern": "form-ok" + } + }, + "label": "Skills Match Indicator", + "presence": "Required", + "visibilityRule": [ + { + "type": "HideWhen", + "conditions": [ + { + "type": "Field", + "fieldName": "skills", + "comparison": { + "type": "EqualsField", + "value": "preferred_skills" + } + } + ] + } + ] + } + ]`, + values: url.Values{ + "skills": []string{"Go", "Elm"}, + "preferred_skills": []string{"Python", "Elm", "JavaScript"}, + }, + expectedError: nil, // Should pass - "Elm" is in both lists, so condition is met, field is hidden + }, + { + name: "EqualsField with multi-value fields - no overlap", + formFields: ` + [ + { + "name": "skills", + "type": { + "type": "ChooseMultiple", + "choices": ["Go", "Elm", "JavaScript", "Python"] + }, + "label": "Your Skills", + "presence": "Required" + }, + { + "name": "preferred_skills", + "type": { + "type": "ChooseMultiple", + "choices": ["Go", "Elm", "JavaScript", "Python"] + }, + "label": "Preferred Skills", + "presence": "Required" + }, + { + "type": { + "type": "ShortText", + "inputType": "text", + "attributes": { + "type": "text", + "class": "size-0-invisible", + "value": "form-invalid", + "pattern": "form-ok" + } + }, + "label": "Skills Match Indicator", + "presence": "Required", + "visibilityRule": [ + { + "type": "HideWhen", + "conditions": [ + { + "type": "Field", + "fieldName": "skills", + "comparison": { + "type": "EqualsField", + "value": "preferred_skills" + } + } + ] + } + ] + } + ]`, + values: url.Values{ + "skills": []string{"Go", "Elm"}, + "preferred_skills": []string{"Python", "JavaScript"}, + }, + expectedError: ErrRequiredFieldMissing, // Should fail - no overlap, so field is visible but missing value + }, } for _, tt := range scenarios { diff --git a/tests/MainTest.elm b/tests/MainTest.elm index 3064830..a3dc273 100644 --- a/tests/MainTest.elm +++ b/tests/MainTest.elm @@ -612,6 +612,186 @@ suite = -- Comparison is as-is: whitespace and duplicates are not normalized Main.evaluateCondition values (Main.Field "a" (Main.EqualsField "b")) |> Expect.equal True + , test "EqualsField with multi-value fields - any value matches (integration test)" <| + \_ -> + let + -- Mirror the Go test: ChooseMultiple fields with EqualsField visibility rule + formFields = + Array.fromList + [ { label = "Your Skills" + , name = Just "skills" + , presence = Main.Required + , description = Main.AttributeNotNeeded + , type_ = + Main.ChooseMultiple + { choices = + [ { label = "Go", value = "Go" } + , { label = "Elm", value = "Elm" } + , { label = "JavaScript", value = "JavaScript" } + , { label = "Python", value = "Python" } + ] + , filter = Nothing + , maxAllowed = Nothing + , minRequired = Nothing + } + , visibilityRule = [] + } + , { label = "Preferred Skills" + , name = Just "preferred_skills" + , presence = Main.Required + , description = Main.AttributeNotNeeded + , type_ = + Main.ChooseMultiple + { choices = + [ { label = "Go", value = "Go" } + , { label = "Elm", value = "Elm" } + , { label = "JavaScript", value = "JavaScript" } + , { label = "Python", value = "Python" } + ] + , filter = Nothing + , maxAllowed = Nothing + , minRequired = Nothing + } + , visibilityRule = [] + } + , { label = "Skills Match Indicator" + , name = Nothing + , presence = Main.Required + , description = Main.AttributeNotNeeded + , type_ = + Main.ShortText + (Main.fromRawCustomElement + { inputType = "text" + , inputTag = "input" + , attributes = + Dict.fromList + [ ( "type", "text" ) + , ( "class", "size-0-invisible" ) + , ( "value", "form-invalid" ) + , ( "pattern", "form-ok" ) + ] + } + ) + , visibilityRule = + [ Main.HideWhen + [ Main.Field "skills" (Main.EqualsField "preferred_skills") ] + ] + } + ] + + -- Values with overlap: "Elm" is in both lists + values = + Dict.fromList + [ ( "skills", [ "Go", "Elm" ] ) + , ( "preferred_skills", [ "Python", "Elm", "JavaScript" ] ) + ] + in + Expect.all + [ \_ -> + -- The EqualsField condition should be met (overlap exists) + Main.evaluateCondition values (Main.Field "skills" (Main.EqualsField "preferred_skills")) + |> Expect.equal True + , \_ -> + -- The third field should be hidden (HideWhen condition met) + case Array.get 2 formFields of + Just field -> + Main.isVisibilityRuleSatisfied field.visibilityRule values + |> Expect.equal False + + Nothing -> + Expect.fail "Field 2 not found" + ] + () + , test "EqualsField with multi-value fields - no overlap (integration test)" <| + \_ -> + let + -- Same form structure as above + formFields = + Array.fromList + [ { label = "Your Skills" + , name = Just "skills" + , presence = Main.Required + , description = Main.AttributeNotNeeded + , type_ = + Main.ChooseMultiple + { choices = + [ { label = "Go", value = "Go" } + , { label = "Elm", value = "Elm" } + , { label = "JavaScript", value = "JavaScript" } + , { label = "Python", value = "Python" } + ] + , filter = Nothing + , maxAllowed = Nothing + , minRequired = Nothing + } + , visibilityRule = [] + } + , { label = "Preferred Skills" + , name = Just "preferred_skills" + , presence = Main.Required + , description = Main.AttributeNotNeeded + , type_ = + Main.ChooseMultiple + { choices = + [ { label = "Go", value = "Go" } + , { label = "Elm", value = "Elm" } + , { label = "JavaScript", value = "JavaScript" } + , { label = "Python", value = "Python" } + ] + , filter = Nothing + , maxAllowed = Nothing + , minRequired = Nothing + } + , visibilityRule = [] + } + , { label = "Skills Match Indicator" + , name = Nothing + , presence = Main.Required + , description = Main.AttributeNotNeeded + , type_ = + Main.ShortText + (Main.fromRawCustomElement + { inputType = "text" + , inputTag = "input" + , attributes = + Dict.fromList + [ ( "type", "text" ) + , ( "class", "size-0-invisible" ) + , ( "value", "form-invalid" ) + , ( "pattern", "form-ok" ) + ] + } + ) + , visibilityRule = + [ Main.HideWhen + [ Main.Field "skills" (Main.EqualsField "preferred_skills") ] + ] + } + ] + + -- Values with NO overlap + values = + Dict.fromList + [ ( "skills", [ "Go", "Elm" ] ) + , ( "preferred_skills", [ "Python", "JavaScript" ] ) + ] + in + Expect.all + [ \_ -> + -- The EqualsField condition should NOT be met (no overlap) + Main.evaluateCondition values (Main.Field "skills" (Main.EqualsField "preferred_skills")) + |> Expect.equal False + , \_ -> + -- The third field should be visible (HideWhen condition not met) + case Array.get 2 formFields of + Just field -> + Main.isVisibilityRuleSatisfied field.visibilityRule values + |> Expect.equal True + + Nothing -> + Expect.fail "Field 2 not found" + ] + () ] , describe "list attribute handling" [ test "fromRawCustomElement removes list attribute" <|