Skip to content

Commit 608c219

Browse files
authored
feat: add Parallel (#156)
* support parallel * add test case
1 parent 97c74a9 commit 608c219

File tree

3 files changed

+128
-63
lines changed

3 files changed

+128
-63
lines changed

src/Field.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export interface InternalFieldProps {
6666
shouldUpdate?: ShouldUpdate;
6767
trigger?: string;
6868
validateTrigger?: string | string[] | false;
69-
validateFirst?: boolean;
69+
validateFirst?: boolean | 'parallel';
7070
valuePropName?: string;
7171
getValueProps?: (value: StoreValue) => object;
7272
messageVariables?: Record<string, string>;

src/utils/validateUtil.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export function validateRules(
132132
value: StoreValue,
133133
rules: RuleObject[],
134134
options: ValidateOptions,
135-
validateFirst: boolean,
135+
validateFirst: boolean | 'parallel',
136136
messageVariables?: Record<string, string>,
137137
) {
138138
const name = namePath.join('.');
@@ -188,20 +188,40 @@ export function validateRules(
188188
};
189189
});
190190

191-
const rulePromises = filledRules.map(rule =>
192-
validateRule(name, value, rule, options, messageVariables),
193-
);
191+
let summaryPromise: Promise<string[]>;
194192

195-
const summaryPromise: Promise<string[]> = (validateFirst
196-
? finishOnFirstFailed(rulePromises)
197-
: finishOnAllFailed(rulePromises)
198-
).then((errors: string[]): string[] | Promise<string[]> => {
199-
if (!errors.length) {
200-
return [];
201-
}
193+
if (validateFirst === true) {
194+
// >>>>> Validate by serialization
195+
summaryPromise = new Promise(async resolve => {
196+
/* eslint-disable no-await-in-loop */
197+
for (let i = 0; i < filledRules.length; i += 1) {
198+
const errors = await validateRule(name, value, filledRules[i], options, messageVariables);
199+
if (errors.length) {
200+
resolve(errors);
201+
return;
202+
}
203+
}
204+
/* eslint-enable */
202205

203-
return Promise.reject<string[]>(errors);
204-
});
206+
resolve([]);
207+
});
208+
} else {
209+
// >>>>> Validate by parallel
210+
const rulePromises = filledRules.map(rule =>
211+
validateRule(name, value, rule, options, messageVariables),
212+
);
213+
214+
summaryPromise = (validateFirst
215+
? finishOnFirstFailed(rulePromises)
216+
: finishOnAllFailed(rulePromises)
217+
).then((errors: string[]): string[] | Promise<string[]> => {
218+
if (!errors.length) {
219+
return [];
220+
}
221+
222+
return Promise.reject<string[]>(errors);
223+
});
224+
}
205225

206226
// Internal catch error to avoid console error log.
207227
summaryPromise.catch(e => e);

tests/validate.test.js

Lines changed: 94 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -408,59 +408,104 @@ describe('Form.Validate', () => {
408408
errorSpy.mockRestore();
409409
});
410410

411-
it('validateFirst', async () => {
412-
let form;
413-
let canEnd = false;
414-
const onFinish = jest.fn();
411+
describe('validateFirst', () => {
412+
it('work', async () => {
413+
let form;
414+
let canEnd = false;
415+
const onFinish = jest.fn();
415416

416-
const wrapper = mount(
417-
<div>
418-
<Form
419-
ref={instance => {
420-
form = instance;
421-
}}
422-
onFinish={onFinish}
423-
>
424-
<InfoField
425-
name="username"
426-
validateFirst
427-
rules={[
428-
// Follow promise will never end
429-
{ required: true },
430-
{
431-
validator: () =>
432-
new Promise(resolve => {
433-
if (canEnd) {
434-
resolve();
435-
}
436-
}),
437-
},
438-
]}
439-
/>
440-
</Form>
441-
</div>,
442-
);
417+
const wrapper = mount(
418+
<div>
419+
<Form
420+
ref={instance => {
421+
form = instance;
422+
}}
423+
onFinish={onFinish}
424+
>
425+
<InfoField
426+
name="username"
427+
validateFirst
428+
rules={[
429+
// Follow promise will never end
430+
{ required: true },
431+
{
432+
validator: () =>
433+
new Promise(resolve => {
434+
if (canEnd) {
435+
resolve();
436+
}
437+
}),
438+
},
439+
]}
440+
/>
441+
</Form>
442+
</div>,
443+
);
443444

444-
// Not pass
445-
await changeValue(wrapper, '');
446-
matchError(wrapper, true);
447-
expect(form.getFieldError('username')).toEqual(["'username' is required"]);
448-
expect(form.getFieldsError()).toEqual([
449-
{
450-
name: ['username'],
451-
errors: ["'username' is required"],
452-
},
453-
]);
454-
expect(onFinish).not.toHaveBeenCalled();
445+
// Not pass
446+
await changeValue(wrapper, '');
447+
matchError(wrapper, true);
448+
expect(form.getFieldError('username')).toEqual(["'username' is required"]);
449+
expect(form.getFieldsError()).toEqual([
450+
{
451+
name: ['username'],
452+
errors: ["'username' is required"],
453+
},
454+
]);
455+
expect(onFinish).not.toHaveBeenCalled();
455456

456-
// Should pass
457-
canEnd = true;
458-
await changeValue(wrapper, 'test');
459-
wrapper.find('form').simulate('submit');
460-
await timeout();
457+
// Should pass
458+
canEnd = true;
459+
await changeValue(wrapper, 'test');
460+
wrapper.find('form').simulate('submit');
461+
await timeout();
461462

462-
matchError(wrapper, false);
463-
expect(onFinish).toHaveBeenCalledWith({ username: 'test' });
463+
matchError(wrapper, false);
464+
expect(onFinish).toHaveBeenCalledWith({ username: 'test' });
465+
});
466+
467+
[
468+
{ name: 'serialization', first: true, second: false, validateFirst: true },
469+
{ name: 'parallel', first: true, second: true, validateFirst: 'parallel' },
470+
].forEach(({ name, first, second, validateFirst }) => {
471+
it(name, async () => {
472+
let ruleFirst = false;
473+
let ruleSecond = false;
474+
475+
const wrapper = mount(
476+
<Form>
477+
<InfoField
478+
name="username"
479+
validateFirst={validateFirst}
480+
rules={[
481+
{
482+
validator: async () => {
483+
ruleFirst = true;
484+
await timeout();
485+
throw new Error('failed first');
486+
},
487+
},
488+
{
489+
validator: async () => {
490+
ruleSecond = true;
491+
await timeout();
492+
throw new Error('failed second');
493+
},
494+
},
495+
]}
496+
/>
497+
</Form>,
498+
);
499+
500+
await changeValue(wrapper, 'test');
501+
await timeout();
502+
503+
matchError(wrapper, 'failed first');
504+
505+
expect(ruleFirst).toEqual(first);
506+
expect(ruleSecond).toEqual(second);
507+
});
508+
});
464509
});
465510

466511
it('switch to remove errors', async () => {

0 commit comments

Comments
 (0)