diff --git a/packages/vest/docs/result.md b/packages/vest/docs/result.md index ec1d78519..7f11480af 100644 --- a/packages/vest/docs/result.md +++ b/packages/vest/docs/result.md @@ -65,15 +65,20 @@ Along with these values, the result object exposes the following methods: `isValid` returns whether the validation suite as a whole is valid or not. -A suite is considered valid if both conditions are met: +A suite is considered valid if the following conditions are met: - There are no errors (`hasErrors() === false`) in the suite - warnings are not counted as errors. - All non optional fields have passing tests. +- There are no pending async tests. ```js -resultObject.isValid(); +result.isValid(); + +suite.get().isValid(); ``` +?> **Note** when `isValid` equals `false`, it does not necessarily mean that the form is inValid, but that it might not be valid _yet_. For example, if not all the fields are filled, the form is simply not valid, even though it may not be strictly invalid. + ## `hasErrors` and `hasWarnings` functions If you only need to know if a certain field has validation errors or warnings but don't really care which they are, you can use `hasErrors` or `hasWarnings` functions. diff --git a/packages/vest/src/core/produce/__tests__/isValid.test.js b/packages/vest/src/core/produce/__tests__/isValid.test.js index aae6c0290..28975b697 100644 --- a/packages/vest/src/core/produce/__tests__/isValid.test.js +++ b/packages/vest/src/core/produce/__tests__/isValid.test.js @@ -1,3 +1,5 @@ +import wait from 'wait'; + import vest, { test, optional } from 'vest'; describe('isValid', () => { @@ -74,6 +76,102 @@ describe('isValid', () => { }); }); + describe('When the suite has an async optional test', () => { + let suite; + + beforeEach(() => { + suite = vest.create(() => { + optional('field_1'); + test('field_1', async () => { + await wait(300); + return true; + }); + }); + }); + + describe('When test is pending', () => { + it('Should return false', () => { + suite(); + expect(suite.get().isValid()).toBe(false); + }); + }); + describe('When test is passing', () => { + it('Should return true', async () => { + suite(); + await wait(300); + expect(suite.get().isValid()).toBe(true); + }); + }); + }); + + describe('When the suite has warning async tests', () => { + let suite; + + beforeEach(() => { + suite = vest.create(() => { + test('field_1', async () => { + vest.warn(); + await wait(300); + return true; + }); + + test('field_1', () => { + return true; + }); + }); + }); + + it('Should return true', () => { + expect(suite().isValid()).toBe(true); + }); + }); + + describe('When the suite has async non-optional tests', () => { + let suite; + + beforeEach(() => { + suite = vest.create(only => { + vest.only(only); + optional('field_2'); + test('field_1', async () => { + await wait(300); + return true; + }); + test('field_2', () => { + return true; + }); + }); + }); + + describe('When test is pending', () => { + it('Should return `false`', () => { + const result = suite(); + + expect(result.isValid()).toBe(false); + }); + }); + + describe('When async test is passing', () => { + it('Should return `true`', () => { + return new Promise(done => { + const result = suite().done(() => { + expect(result.isValid()).toBe(true); + done(); + }); + }); + }); + }); + + describe('When test is lagging', () => { + it('Should return `false`', () => { + suite(); + const result = suite('field_2'); + + expect(result.isValid()).toBe(false); + }); + }); + }); + describe('When a all required fields are passing', () => { let suite; diff --git a/packages/vest/src/core/produce/produce.js b/packages/vest/src/core/produce/produce.js index 2148a5db9..c0d8ba33e 100644 --- a/packages/vest/src/core/produce/produce.js +++ b/packages/vest/src/core/produce/produce.js @@ -6,6 +6,7 @@ import getFailuresByGroup from 'getFailuresByGroup'; import hasFailures from 'hasFailures'; import hasFailuresByGroup from 'hasFailuresByGroup'; import hasRemainingTests from 'hasRemainingTests'; +import { isNotEmpty } from 'isEmpty'; import isFunction from 'isFunction'; import { SEVERITY_GROUP_ERROR, SEVERITY_GROUP_WARN } from 'resultKeys'; import { HAS_WARNINGS, HAS_ERRORS } from 'sharedKeys'; @@ -13,6 +14,7 @@ import { useTestCallbacks, useTestObjects, useOptionalFields, + usePending, } from 'stateHooks'; import withArgs from 'withArgs'; @@ -119,6 +121,16 @@ function isValid() { const [optionalFields] = useOptionalFields(); + const [{ pending, lagging }] = usePending(); + + if ( + isNotEmpty( + pending.concat(lagging).filter(testObject => !testObject.isWarning) + ) + ) { + return false; + } + for (const test in result.tests) { if (!optionalFields[test] && result.tests[test].testCount === 0) { return false;