Skip to content

Commit 8a7b63d

Browse files
Martynas ŽilinskasDovydasNavickas
authored andcommitted
Feature/validation (#2)
* Added saving FieldRef component to FormStoreState. * Added form-store tests. * Added GetFieldId test. * Added immutable record for FormError. Added test for field validation with error. * Fixed form-store validate. * Updated async form store tests. * Fixed tslint warnings. * Removed validators from FieldState. * Fixed PR changes.
1 parent 92eb606 commit 8a7b63d

File tree

8 files changed

+203
-41
lines changed

8 files changed

+203
-41
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import { FormStore } from "../../src/stores/form-store";
2+
import { FormError } from "../../src/contracts/error";
3+
4+
describe("Form store", () => {
5+
it("returns state", () => {
6+
const formId = "FORM-ID";
7+
const formStore = new FormStore(formId);
8+
9+
expect(formStore.GetState()).not.toBeUndefined();
10+
});
11+
12+
it("returns fieldId from fieldName and fieldGroupId", () => {
13+
const formId = "FORM-ID";
14+
const fieldName = "FIELD-NAME";
15+
const fieldGroupId = "FIELD-GROUP-ID";
16+
const formStore = new FormStore(formId);
17+
18+
const fieldId = formStore.GetFieldId(fieldName, fieldGroupId);
19+
20+
expect(typeof fieldId).toBe("string");
21+
expect(fieldId.indexOf(fieldName)).not.toBe(-1);
22+
expect(fieldId.indexOf(fieldGroupId)).not.toBe(-1);
23+
});
24+
25+
it("registers a field", () => {
26+
const formId = "FORM-ID";
27+
const fieldId = "FIELD-ID";
28+
const initialValue = "INITIAL-VALUE";
29+
const formStore = new FormStore(formId);
30+
31+
formStore.RegisterField(fieldId, initialValue);
32+
expect(formStore.HasField(fieldId)).toBe(true);
33+
expect(formStore.GetField(fieldId)).not.toBeUndefined();
34+
expect(formStore.GetField(fieldId).InitialValue).toBe(initialValue);
35+
});
36+
37+
it("unregisters a field", () => {
38+
const formId = "FORM-ID";
39+
const fieldId = "FIELD-ID";
40+
const initialValue = "INITIAL-VALUE";
41+
const formStore = new FormStore(formId);
42+
43+
formStore.RegisterField(fieldId, initialValue);
44+
formStore.UnregisterField(fieldId);
45+
expect(formStore.GetField(fieldId)).toBeUndefined();
46+
expect(formStore.HasField(fieldId)).toBe(false);
47+
});
48+
49+
it("has a field", () => {
50+
const formId = "FORM-ID";
51+
const fieldId = "FIELD-ID";
52+
const initialValue = "INITIAL-VALUE";
53+
const formStore = new FormStore(formId);
54+
55+
expect(formStore.HasField(fieldId)).toBe(false);
56+
formStore.RegisterField(fieldId, initialValue);
57+
expect(formStore.HasField(fieldId)).toBe(true);
58+
});
59+
60+
it("get a field", () => {
61+
const formId = "FORM-ID";
62+
const fieldId = "FIELD-ID";
63+
const initialValue = "INITIAL-VALUE";
64+
const formStore = new FormStore(formId);
65+
66+
expect(formStore.GetField(fieldId)).toBeUndefined();
67+
formStore.RegisterField(fieldId, initialValue);
68+
expect(formStore.GetField(fieldId)).not.toBeUndefined();
69+
});
70+
71+
it("value changed", () => {
72+
const formId = "FORM-ID";
73+
const fieldId = "FIELD-ID";
74+
const initialValue = "INITIAL-VALUE";
75+
const nextValue = "NEXT-VALUE";
76+
const formStore = new FormStore(formId);
77+
78+
formStore.RegisterField(fieldId, initialValue);
79+
expect(formStore.GetField(fieldId).Value).toBe(initialValue);
80+
formStore.ValueChanged(fieldId, nextValue);
81+
expect(formStore.GetField(fieldId).Value).toBe(nextValue);
82+
});
83+
84+
it("validate field without error", async (done) => {
85+
const formId = "FORM-ID";
86+
const fieldId = "FIELD-ID";
87+
const initialValue = "INITIAL-VALUE";
88+
const formStore = new FormStore(formId);
89+
90+
formStore.RegisterField(fieldId, initialValue);
91+
const validationPromise = new Promise<void>((resolve, reject) => {
92+
setTimeout(() => {
93+
resolve();
94+
}, 50);
95+
});
96+
97+
formStore.Validate(fieldId, validationPromise);
98+
try {
99+
expect(formStore.GetField(fieldId).Validating).toBe(true);
100+
} catch (error) {
101+
done.fail(error);
102+
}
103+
104+
try {
105+
await validationPromise;
106+
expect(formStore.GetField(fieldId).Validating).toBe(false);
107+
done();
108+
} catch (error) {
109+
done.fail(error);
110+
}
111+
});
112+
113+
it("validate field with error", async (done) => {
114+
const formId = "FORM-ID";
115+
const fieldId = "FIELD-ID";
116+
const initialValue = "INITIAL-VALUE";
117+
const formStore = new FormStore(formId);
118+
const formError: FormError = { Message: "Error Message" };
119+
120+
formStore.RegisterField(fieldId, initialValue);
121+
const validationPromise = new Promise<void>((resolve, reject) => {
122+
setTimeout(() => {
123+
reject(formError);
124+
}, 50);
125+
});
126+
127+
formStore.Validate(fieldId, validationPromise);
128+
try {
129+
expect(formStore.GetField(fieldId).Validating).toBe(true);
130+
} catch (error) {
131+
done.fail(error);
132+
}
133+
134+
try {
135+
await validationPromise;
136+
} catch (err) {
137+
// Validation promise was rejected
138+
// Expects in the subsequent try block
139+
}
140+
141+
try {
142+
const error = formStore.GetField(fieldId).Error;
143+
expect(formStore.GetField(fieldId).Validating).toBe(false);
144+
expect(error).not.toBeUndefined();
145+
expect(error).not.toBeNull();
146+
expect(error!.Message).toBe(formError.Message);
147+
148+
done();
149+
} catch (error) {
150+
done.fail(error);
151+
}
152+
});
153+
});

packages/simplr-forms-core/__tests__/stores/form-stores-handler.test.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,4 @@ describe("Form stores handler", () => {
8484

8585
expect(storesHandler.GetStore(generatedFormId)).toBeTruthy();
8686
});
87-
88-
it("validates field with a promise", () => {
89-
90-
});
9187
});

packages/simplr-forms-core/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"@types/react": "^15.0.21",
2121
"@types/react-dom": "^0.14.23",
2222
"@types/sinon": "^2.1.2",
23+
"@types/core-js": "0.9.35",
2324
"enzyme": "^2.8.0",
2425
"jest": "^19.0.2",
2526
"jest-enzyme": "^3.0.0",
@@ -31,6 +32,7 @@
3132
},
3233
"dependencies": {
3334
"action-emitter": "^0.2.1",
35+
"core-js": "^2.4.1",
3436
"fbemitter": "^2.1.1",
3537
"flux": "^3.1.2",
3638
"immutable": "^3.8.1",
@@ -43,11 +45,11 @@
4345
"transform": {
4446
".(ts|tsx)": "<rootDir>/node_modules/ts-jest/preprocessor.js"
4547
},
46-
"testRegex": "/__tests__\/.*\\.(test|spec).(ts|tsx|js)$",
48+
"testRegex": "/__tests__/.*\\.(test|spec).(ts|tsx|js)$",
4749
"moduleFileExtensions": [
4850
"ts",
4951
"tsx",
5052
"js"
5153
]
5254
}
53-
}
55+
}

packages/simplr-forms-core/src/contracts/field.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ export interface FieldProps {
2323
export interface FieldState {
2424
InitialValue: FieldValue;
2525
Value: FieldValue;
26-
Error?: FormError;
26+
Error?: FormErrorRecord;
2727
Touched: boolean;
2828
Pristine: boolean;
2929
Validating: boolean;
30-
Validators?: Array<JSX.Element | any>;
3130
FieldsGroup?: {
3231
Id: string;
3332
};
3433
}
3534

3635
export interface FieldStateRecord extends TypedRecord<FieldStateRecord>, FieldState { }
36+
export interface FormErrorRecord extends TypedRecord<FormErrorRecord>, FormError { }
3737

3838
export enum FieldValidationType {
3939
None,

packages/simplr-forms-core/src/stores/form-store.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ import { recordify } from "typed-immutable-record";
33
import { ActionEmitter } from "action-emitter";
44

55
import * as Actions from "./form-store-actions";
6-
import { FieldState, FieldValue, FieldStateRecord } from "../contracts/field";
6+
import {
7+
FieldState,
8+
FieldValue,
9+
FieldStateRecord,
10+
FormErrorRecord
11+
} from "../contracts/field";
712
import { FormState, FormStateRecord } from "../contracts/form";
813
import { FormStoreState, FormStoreStateRecord } from "../contracts/form-store";
914
import { FieldsGroupStateRecord } from "../contracts/fields-group";
10-
import { ResolveError } from "../utils/form-error-helpers";
15+
import { ConstructFormError } from "../utils/form-error-helpers";
16+
import { FormError } from "../contracts/error";
1117

1218
export class FormStore extends ActionEmitter {
1319
constructor(formId: string) {
@@ -55,11 +61,16 @@ export class FormStore extends ActionEmitter {
5561
return fieldName;
5662
}
5763

58-
public RegisterField(fieldId: string, initialValue: FieldValue, fieldsGroupId?: string) {
64+
public RegisterField(
65+
fieldId: string,
66+
initialValue: FieldValue,
67+
fieldsGroupId?: string
68+
) {
5969
// Construct field state
6070
let fieldState = this.GetInitialFieldState();
6171
fieldState.InitialValue = initialValue;
6272
fieldState.Value = initialValue;
73+
6374
if (fieldsGroupId != null) {
6475
fieldState.FieldsGroup = {
6576
Id: fieldsGroupId
@@ -98,7 +109,7 @@ export class FormStore extends ActionEmitter {
98109
});
99110
}
100111

101-
public Validate(fieldId: string, promise: Promise<void>) {
112+
public async Validate(fieldId: string, validationPromise: Promise<void>) {
102113
this.State = this.State.withMutations(state => {
103114
const fieldState = state.Fields.get(fieldId);
104115
state.Fields = state.Fields.set(fieldId, fieldState.merge({
@@ -107,27 +118,28 @@ export class FormStore extends ActionEmitter {
107118
} as FieldState));
108119
});
109120

110-
promise.then(() => {
121+
try {
122+
await validationPromise;
111123
this.State = this.State.withMutations(state => {
112124
const fieldState = state.Fields.get(fieldId);
113125
state.Fields = state.Fields.set(fieldId, fieldState.merge({
114126
Validating: false
115127
} as FieldState));
116128
});
117-
}).catch((error) => {
118-
let resolvedError = ResolveError(error);
119-
if (resolvedError != null) {
120-
this.State = this.State.withMutations(state => {
121-
const fieldState = state.Fields.get(fieldId);
122-
state.Fields = state.Fields.set(fieldId, fieldState.merge({
123-
Validating: false,
124-
Error: resolvedError
125-
} as FieldState));
126-
});
127-
} else {
129+
} catch (error) {
130+
const formError = ConstructFormError(error);
131+
if (formError == null) {
128132
throw Error(error);
129133
}
130-
});
134+
135+
this.State = this.State.withMutations(state => {
136+
const fieldState = state.Fields.get(fieldId);
137+
state.Fields = state.Fields.set(fieldId, fieldState.merge({
138+
Validating: false,
139+
Error: recordify<FormError, FormErrorRecord>(formError!)
140+
} as FieldState));
141+
});
142+
}
131143
}
132144

133145
/**
@@ -162,8 +174,7 @@ export class FormStore extends ActionEmitter {
162174
Pristine: true,
163175
Validating: false,
164176
Error: undefined,
165-
FieldsGroup: undefined,
166-
Validators: undefined
177+
FieldsGroup: undefined
167178
};
168179
}
169180
}

packages/simplr-forms-core/src/stores/form-stores-handler.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ export class FormStoresHandlerClass {
1515

1616
/**
1717
* Returns unique incremental store id.
18-
*
19-
* @returns
20-
*
18+
*
19+
* @returns
20+
*
2121
* @memberOf FormStoresHandlerClass
2222
*/
2323
public NextStoreId() {
@@ -26,9 +26,9 @@ export class FormStoresHandlerClass {
2626

2727
/**
2828
* Returns count of currently registered stores.
29-
*
29+
*
3030
* @readonly
31-
*
31+
*
3232
* @memberOf FormStoresHandlerClass
3333
*/
3434
public get StoresCount() {
Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
import { FormError } from "../contracts/error";
22

3-
function isPromise<T>(value: any): value is Promise<T> {
4-
return value != null && value.then != null && value.catch != null;
5-
}
6-
7-
function isFormError(value: any): value is FormError {
3+
export function IsFormError(value: any): value is FormError {
84
return (value != null && (value as FormError).Message != null);
95
}
106

11-
export function ResolveError(error: any) {
7+
export function ConstructFormError(error: any) {
128
if (typeof error === "string") {
139
return {
1410
Message: error
1511
};
16-
} else if (isFormError(error)) {
12+
} else if (IsFormError(error)) {
1713
return error;
1814
}
1915
}

packages/simplr-forms-core/yarn.lock

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
version "0.22.1"
77
resolved "https://www.myget.org/F/quatrodev-insider/auth/a2ff4e2d-c219-42fd-9515-2989c9cd8417/npm/@types/cheerio/-/@types/cheerio-0.22.1.tgz#740c4cd8c4d3f3074f83b9ab62e711eac2c764ce"
88

9+
10+
version "0.9.35"
11+
resolved "https://registry.yarnpkg.com/@types/core-js/-/core-js-0.9.35.tgz#444064e63711cdcc62ea844d27642f6efc2285f2"
12+
913
"@types/enzyme@^2.7.7":
1014
version "2.7.7"
1115
resolved "https://www.myget.org/F/quatrodev-insider/auth/a2ff4e2d-c219-42fd-9515-2989c9cd8417/npm/@types/enzyme/-/@types/enzyme-2.7.7.tgz#52688e130fe55185f14844919ec559961488f53d"
@@ -537,9 +541,9 @@ core-js@^1.0.0:
537541
version "1.2.7"
538542
resolved "https://www.myget.org/F/quatrodev-insider/auth/a2ff4e2d-c219-42fd-9515-2989c9cd8417/npm/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
539543

540-
core-js@^2.4.0:
544+
core-js@^2.4.0, core-js@^2.4.1:
541545
version "2.4.1"
542-
resolved "https://www.myget.org/F/quatrodev-insider/auth/a2ff4e2d-c219-42fd-9515-2989c9cd8417/npm/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
546+
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
543547

544548
core-util-is@~1.0.0:
545549
version "1.0.2"

0 commit comments

Comments
 (0)