ealush / vest

Vest ✅ Declarative validations framework

Home Page:https://vestjs.dev/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cross-field validation issue on Any

lrpatterson opened this issue · comments

So I have a min example here: https://svelte.dev/repl/6be8e7e5f37d431b8d559a09a5d822eb?version=3.43.1

I am trying to use any() to validate an input field and/or a group of checkboxes.

I want to validate whether a user has checked at least one of the checkboxes or has entered a value into the "other" field.

What currently happens is each field seems to be validated separately.

image

import vest, { test, enforce } from 'vest'
import any from 'vest/any';




const suite = vest.create('user_form', (data = {}, currentField ) => {

  vest.only(currentField)
	any(
		()=>{
			test("testInput", "this cannot be empty", ()=>{
				enforce(data.InputPayload).isNotEmpty()
			})
		},
		()=>{
			test("one", "this cannot be empty", ()=>{
				let trainingKeys = Object.keys(data['checkBoxPayload']).map(key=>data['checkBoxPayload'][key])
				enforce(trainingKeys.some(e=>e)).isTruthy()

			})
		}
		
	)

	
})

export default suite

Hey @lrpatterson, thanks for this issue. It is a tough one.

There are two things at play here -
One, the intention behind any is different, and it is either intended to prevent execution of further tests once at least one passed, or alternatively, select one assertion among a few within your test - so the fact that using any didn't work makes sense.

The second issue is that your requirement - while valid, is one that Vest cannot be aware of by itself - at the moment Vest has no way of specifying this level of interconnectedness between fields (even though it is planned). It does though have some other ways of specifying fields as optional. which might help in some scenarios, but yours might require a little more tweaking to get right.

Let me have a day or so to fiddle with this and come up with a few alternatives that might work for you.

Along with that - does your form have more fields, or is it just that? How does your suite look like in real life? I am asking in order to understand what are the other variables that are at play here.

@ealush, Thank you for your response and explaination (that was helpful).

I can't share to much of my form but it does have a lot of question on it. Validation for the other fields are great it is just I have two or three questions that need to be validated on an 'or'.

I have updated my reply above with additional test()s so you can get a feel for my data. Basically other than the 'problem' fields I am using a single suite to validate my form (even though I would probably like to try to dynamically adding validation on hide/show fields)

Again I really appreciate and feedback you have. Thanks for the great lib!

OK, just reporting on some progress. What you're seeing here, is pretty much a working version of what you're asking for.

optional

After much experimentation I got to the core of the issue in terms of usability, and there are two factors at play here:

  1. You have a "two-way" dependency between fields - meaning Test A needs to know something about Test B, whilst Test B needs to know the same thing about Test A.
  2. Vest runs tests one after the other (with the exception of async tests), meaning that the test itself can never know anything about a test that comes after it.

When we have this sort of dependency, we need some sort of a "second pass", which is why the "optional api" (that I linked to before) was created. It is there to mark fields that may be left un-run. You have a similar case, but not quite. You might allow a single test to be left un-run, but only if another test did - which lead me to the following understanding:
Even though the optional api works oob for the classic cases, it is mostly an implementation of is-dirty (similar to other libraries), while consumers (such as you) might have different criteria under which they want to omit (yes. omit. not just leave un-run) tests from their run, for example - what if the field is touched but the user cleared it? Or what if we want to allow omitting the field if a different field is present (your case).

So the interface that I am currently testing and got it to work is this:

optional still works the same, along with the existing arguments, you may pass an object that contains the fields, and a function that determines its omission condition - meaning, when true, the test will be omitted from the validation result, regardless of whether the field is valid or not - but, it is your duty to make sure that your omission condition does not conflict with your tests (so you don't omit tests that should actually fail).

Here's how your code looks after the change:
image
As you can see, there's a lot less code required on your end to make this work.

Will such an interface work for you?

If so, just a quick FYI - this will probably be available only in version 4 of vest that will come out soon. Until then, once I finalize this api, I can give you a stable pre-release package version that will have this in it so you are unblocked.

Thanks for reporting this, I think your issue lead to some good progress!

@lrpatterson I added this pull request that details how the new API works and what are its limitations. I would love to hear your input on it before I merge it.

@ealush, man you are the best! I think ommission rather than un-run will work for this type of either/or cross-validation. It is also as you mentioned a very clean way to accomplish this end. Thank you again for your help!