functional-models 1.0.6 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -21
- package/features/arrayFields.feature +14 -0
- package/features/stepDefinitions/steps.js +16 -4
- package/package.json +2 -1
- package/src/fields.js +98 -4
- package/src/models.js +22 -16
- package/src/serialization.js +4 -4
- package/src/utils.js +2 -2
- package/src/validation.js +46 -31
- package/test/src/fields.test.js +320 -7
- package/test/src/models.test.js +48 -12
- package/test/src/serialization.test.js +15 -15
- package/test/src/validation.test.js +14 -0
package/README.md
CHANGED
|
@@ -10,29 +10,36 @@ This library empowers the creation of pure JavaScript function based models that
|
|
|
10
10
|
## Example Usage
|
|
11
11
|
|
|
12
12
|
const {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
field,
|
|
14
|
+
constantValueField,
|
|
15
|
+
textField,
|
|
16
|
+
dateField,
|
|
17
|
+
integerField,
|
|
16
18
|
uniqueId,
|
|
19
|
+
createModel,
|
|
20
|
+
validation,
|
|
17
21
|
}= require('functional-models')
|
|
18
22
|
|
|
19
|
-
const Truck = ({
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const Truck = createModel({
|
|
24
|
+
type: constantValueField('truck'),
|
|
25
|
+
id: uniqueId({required: true}),
|
|
26
|
+
make: textField({ maxLength: 20, minLength: 3, required: true}),
|
|
27
|
+
model: textField({ maxLength: 20, minLength: 3, required: true}),
|
|
28
|
+
color: textField({ maxLength: 10, minLength: 3, validators: [
|
|
29
|
+
validation.meetsRegex(/Red/),
|
|
30
|
+
]}),
|
|
31
|
+
year: integerField({ maxValue: 2500, minValue: 1900}),
|
|
32
|
+
lastModified: dateField({ autoNow: true}),
|
|
33
|
+
})
|
|
27
34
|
|
|
28
35
|
|
|
29
36
|
const myTruck = Truck({ make: 'Ford', model: 'F-150', color: 'White', year: 2013})
|
|
30
37
|
|
|
31
|
-
console.log(myTruck.getId()) // a random uuid
|
|
32
|
-
console.log(myTruck.getMake()) // 'Ford'
|
|
33
|
-
console.log(myTruck.getModel()) // 'F-150'
|
|
34
|
-
console.log(myTruck.getColor()) // 'White'
|
|
35
|
-
console.log(myTruck.getYear()) // 2013
|
|
38
|
+
console.log(await myTruck.getId()) // a random uuid
|
|
39
|
+
console.log(await myTruck.getMake()) // 'Ford'
|
|
40
|
+
console.log(await myTruck.getModel()) // 'F-150'
|
|
41
|
+
console.log(await myTruck.getColor()) // 'White'
|
|
42
|
+
console.log(await myTruck.getYear()) // 2013
|
|
36
43
|
|
|
37
44
|
const asJson = await myTruck.functions.toJson()
|
|
38
45
|
console.log(asJson)
|
|
@@ -47,8 +54,19 @@ This library empowers the creation of pure JavaScript function based models that
|
|
|
47
54
|
*/
|
|
48
55
|
|
|
49
56
|
const sameTruck = Truck(asJson)
|
|
50
|
-
console.log(sameTruck.getId()) // same uuid as above
|
|
51
|
-
console.log(sameTruck.getMake()) // 'Ford'
|
|
52
|
-
console.log(sameTruck.getModel()) // 'F-150'
|
|
53
|
-
console.log(sameTruck.getColor()) // 'White'
|
|
54
|
-
console.log(sameTruck.getYear()) // 2013
|
|
57
|
+
console.log(await sameTruck.getId()) // same uuid as above
|
|
58
|
+
console.log(await sameTruck.getMake()) // 'Ford'
|
|
59
|
+
console.log(await sameTruck.getModel()) // 'F-150'
|
|
60
|
+
console.log(await sameTruck.getColor()) // 'White'
|
|
61
|
+
console.log(await sameTruck.getYear()) // 2013
|
|
62
|
+
|
|
63
|
+
// Validation
|
|
64
|
+
const errors = await sameTruck.functions.validate.model() // {}
|
|
65
|
+
|
|
66
|
+
const newTruck = Truck({ make: 'Ford', model: 'F-150', color: 'White', year: 20130})
|
|
67
|
+
const errors2 = await newTruck.functions.validate.model()
|
|
68
|
+
console.log(errors2)
|
|
69
|
+
// {"year": 'Value is too long'}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
@@ -36,3 +36,17 @@ Feature: Array Fields
|
|
|
36
36
|
Then the getArrayField field is called on the model
|
|
37
37
|
Then functions.validate is called
|
|
38
38
|
Then an array of 0 errors is shown
|
|
39
|
+
|
|
40
|
+
Scenario: A model that uses the arrayField with the choice validator should pass validation with no errors.
|
|
41
|
+
Given ArrayModel4 model is used
|
|
42
|
+
When ArrayModelData5 data is inserted
|
|
43
|
+
Then the getArrayField field is called on the model
|
|
44
|
+
Then functions.validate is called
|
|
45
|
+
Then an array of 0 errors is shown
|
|
46
|
+
|
|
47
|
+
Scenario: A model that uses the arrayField with the choice validator should fail validation when one value is outside the choices
|
|
48
|
+
Given ArrayModel4 model is used
|
|
49
|
+
When ArrayModelData6 data is inserted
|
|
50
|
+
Then the getArrayField field is called on the model
|
|
51
|
+
Then functions.validate is called
|
|
52
|
+
Then an array of 1 errors is shown
|
|
@@ -4,23 +4,29 @@ const { Given, When, Then } = require('@cucumber/cucumber')
|
|
|
4
4
|
const { createModel, field, arrayField, validation } = require('../../index')
|
|
5
5
|
|
|
6
6
|
const MODEL_DEFINITIONS = {
|
|
7
|
-
TestModel1: createModel({
|
|
7
|
+
TestModel1: createModel("TestModel1", {
|
|
8
8
|
name: field({ required: true }),
|
|
9
9
|
type: field({ required: true, isString: true }),
|
|
10
10
|
flag: field({ required: true, isNumber: true }),
|
|
11
11
|
}),
|
|
12
|
-
ArrayModel1: createModel({
|
|
12
|
+
ArrayModel1: createModel("ArrayModel1", {
|
|
13
13
|
arrayField: field({
|
|
14
14
|
isArray: true,
|
|
15
15
|
validators: [validation.arrayType(validation.TYPE_PRIMATIVES.integer)],
|
|
16
16
|
}),
|
|
17
17
|
}),
|
|
18
|
-
ArrayModel2: createModel({
|
|
18
|
+
ArrayModel2: createModel("ArrayModel2", {
|
|
19
19
|
arrayField: field({ isArray: true }),
|
|
20
20
|
}),
|
|
21
|
-
ArrayModel3: createModel({
|
|
21
|
+
ArrayModel3: createModel("ArrayModel3", {
|
|
22
22
|
arrayField: arrayField({}),
|
|
23
23
|
}),
|
|
24
|
+
ArrayModel4: createModel("ArrayModel4", {
|
|
25
|
+
arrayField: arrayField({
|
|
26
|
+
choices: [4, 5, 6],
|
|
27
|
+
validators: [validation.arrayType(validation.TYPE_PRIMATIVES.integer)],
|
|
28
|
+
}),
|
|
29
|
+
}),
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
const MODEL_INPUT_VALUES = {
|
|
@@ -46,6 +52,12 @@ const MODEL_INPUT_VALUES = {
|
|
|
46
52
|
ArrayModelData4: {
|
|
47
53
|
arrayField: ['a-string', 1, {}, true],
|
|
48
54
|
},
|
|
55
|
+
ArrayModelData5: {
|
|
56
|
+
arrayField: [4, 5, 5, 5, 6],
|
|
57
|
+
},
|
|
58
|
+
ArrayModelData6: {
|
|
59
|
+
arrayField: [4, 5, 5, 5, 6, 1],
|
|
60
|
+
},
|
|
49
61
|
}
|
|
50
62
|
|
|
51
63
|
const EXPECTED_FIELDS = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "functional-models",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "A library for creating JavaScript function based models.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"nyc": {
|
|
30
30
|
"all": true,
|
|
31
31
|
"exclude": [
|
|
32
|
+
"coverage/",
|
|
32
33
|
"features/stepDefinitions/*",
|
|
33
34
|
"test/*"
|
|
34
35
|
]
|
package/src/fields.js
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
const identity = require('lodash/identity')
|
|
2
|
-
const
|
|
2
|
+
const merge = require('lodash/merge')
|
|
3
|
+
const {
|
|
4
|
+
createFieldValidator,
|
|
5
|
+
emptyValidator,
|
|
6
|
+
maxTextLength,
|
|
7
|
+
minTextLength,
|
|
8
|
+
minNumber,
|
|
9
|
+
maxNumber,
|
|
10
|
+
isType,
|
|
11
|
+
meetsRegex,
|
|
12
|
+
} = require('./validation')
|
|
3
13
|
const { createUuid } = require('./utils')
|
|
4
14
|
const { lazyValue } = require('./lazy')
|
|
5
15
|
|
|
16
|
+
const EMAIL_REGEX =
|
|
17
|
+
/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/u
|
|
18
|
+
|
|
19
|
+
const _getValidatorFromConfigElseEmpty = (config, key, validatorGetter) => {
|
|
20
|
+
if (key in config) {
|
|
21
|
+
return validatorGetter(config[key])
|
|
22
|
+
}
|
|
23
|
+
return emptyValidator
|
|
24
|
+
}
|
|
25
|
+
|
|
6
26
|
const field = (config = {}) => {
|
|
7
27
|
const value = config.value || undefined
|
|
8
28
|
const defaultValue = config.defaultValue || undefined
|
|
@@ -33,9 +53,11 @@ const field = (config = {}) => {
|
|
|
33
53
|
}
|
|
34
54
|
},
|
|
35
55
|
getValidator: valueGetter => {
|
|
36
|
-
|
|
37
|
-
|
|
56
|
+
const validator = createFieldValidator(config)
|
|
57
|
+
const _fieldValidatorWrapper = async () => {
|
|
58
|
+
return validator(await valueGetter())
|
|
38
59
|
}
|
|
60
|
+
return _fieldValidatorWrapper
|
|
39
61
|
},
|
|
40
62
|
}
|
|
41
63
|
}
|
|
@@ -81,7 +103,7 @@ const referenceField = config => {
|
|
|
81
103
|
...objToUse,
|
|
82
104
|
functions: {
|
|
83
105
|
...(objToUse.functions ? objToUse.functions : {}),
|
|
84
|
-
|
|
106
|
+
toObj: _getId,
|
|
85
107
|
},
|
|
86
108
|
}
|
|
87
109
|
}
|
|
@@ -105,10 +127,82 @@ const arrayField = (config = {}) =>
|
|
|
105
127
|
isArray: true,
|
|
106
128
|
})
|
|
107
129
|
|
|
130
|
+
const objectField = (config = {}) =>
|
|
131
|
+
field(
|
|
132
|
+
merge(config, {
|
|
133
|
+
validators: [isType('object')],
|
|
134
|
+
})
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
const textField = (config = {}) =>
|
|
138
|
+
field(
|
|
139
|
+
merge(config, {
|
|
140
|
+
isString: true,
|
|
141
|
+
validators: [
|
|
142
|
+
_getValidatorFromConfigElseEmpty(config, 'maxLength', value =>
|
|
143
|
+
maxTextLength(value)
|
|
144
|
+
),
|
|
145
|
+
_getValidatorFromConfigElseEmpty(config, 'minLength', value =>
|
|
146
|
+
minTextLength(value)
|
|
147
|
+
),
|
|
148
|
+
],
|
|
149
|
+
})
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
const integerField = (config = {}) =>
|
|
153
|
+
field(
|
|
154
|
+
merge(config, {
|
|
155
|
+
isInteger: true,
|
|
156
|
+
validators: [
|
|
157
|
+
_getValidatorFromConfigElseEmpty(config, 'minValue', value =>
|
|
158
|
+
minNumber(value)
|
|
159
|
+
),
|
|
160
|
+
_getValidatorFromConfigElseEmpty(config, 'maxValue', value =>
|
|
161
|
+
maxNumber(value)
|
|
162
|
+
),
|
|
163
|
+
],
|
|
164
|
+
})
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
const numberField = (config = {}) =>
|
|
168
|
+
field(
|
|
169
|
+
merge(config, {
|
|
170
|
+
isNumber: true,
|
|
171
|
+
validators: [
|
|
172
|
+
_getValidatorFromConfigElseEmpty(config, 'minValue', value =>
|
|
173
|
+
minNumber(value)
|
|
174
|
+
),
|
|
175
|
+
_getValidatorFromConfigElseEmpty(config, 'maxValue', value =>
|
|
176
|
+
maxNumber(value)
|
|
177
|
+
),
|
|
178
|
+
],
|
|
179
|
+
})
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
const constantValueField = (value, config = {}) =>
|
|
183
|
+
textField(
|
|
184
|
+
merge(config, {
|
|
185
|
+
value,
|
|
186
|
+
})
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
const emailField = (config = {}) =>
|
|
190
|
+
textField(
|
|
191
|
+
merge(config, {
|
|
192
|
+
validators: [meetsRegex(EMAIL_REGEX)],
|
|
193
|
+
})
|
|
194
|
+
)
|
|
195
|
+
|
|
108
196
|
module.exports = {
|
|
109
197
|
field,
|
|
110
198
|
uniqueId,
|
|
111
199
|
dateField,
|
|
112
200
|
arrayField,
|
|
113
201
|
referenceField,
|
|
202
|
+
integerField,
|
|
203
|
+
textField,
|
|
204
|
+
constantValueField,
|
|
205
|
+
numberField,
|
|
206
|
+
objectField,
|
|
207
|
+
emailField,
|
|
114
208
|
}
|
package/src/models.js
CHANGED
|
@@ -1,29 +1,36 @@
|
|
|
1
1
|
const merge = require('lodash/merge')
|
|
2
|
-
const
|
|
3
|
-
const {
|
|
2
|
+
const pickBy = require('lodash/pickBy')
|
|
3
|
+
const { toObj } = require('./serialization')
|
|
4
4
|
const { createPropertyTitle } = require('./utils')
|
|
5
5
|
const { createModelValidator } = require('./validation')
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const MODEL_DEF_KEYS = ['meta', 'functions']
|
|
9
8
|
const PROTECTED_KEYS = ['model']
|
|
10
9
|
|
|
11
|
-
const createModel = keyToField => {
|
|
10
|
+
const createModel = (modelName, keyToField) => {
|
|
12
11
|
PROTECTED_KEYS.forEach(key => {
|
|
13
12
|
if (key in keyToField) {
|
|
14
13
|
throw new Error(`Cannot use ${key}. This is a protected value.`)
|
|
15
14
|
}
|
|
16
15
|
})
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
const fieldProperties = Object.entries(keyToField).filter(
|
|
17
|
+
([key, _]) => !(key in MODEL_DEF_KEYS)
|
|
18
|
+
)
|
|
19
|
+
const fields = fieldProperties.reduce((acc, [key, field]) => {
|
|
20
|
+
return { ...acc, [key]: field }
|
|
20
21
|
}, {})
|
|
21
|
-
const
|
|
22
|
-
(
|
|
22
|
+
const modelDefProperties = merge(
|
|
23
|
+
pickBy(keyToField, (value, key) => MODEL_DEF_KEYS.includes(key)),
|
|
24
|
+
{
|
|
25
|
+
meta: {
|
|
26
|
+
fields,
|
|
27
|
+
modelName,
|
|
28
|
+
},
|
|
29
|
+
}
|
|
23
30
|
)
|
|
24
31
|
|
|
25
|
-
return instanceValues => {
|
|
26
|
-
const loadedInternals =
|
|
32
|
+
return (instanceValues = {}) => {
|
|
33
|
+
const loadedInternals = fieldProperties.reduce((acc, [key, field]) => {
|
|
27
34
|
const fieldGetter = field.createGetter(instanceValues[key])
|
|
28
35
|
const fieldValidator = field.getValidator(fieldGetter)
|
|
29
36
|
const getFieldKey = createPropertyTitle(key)
|
|
@@ -37,16 +44,15 @@ const createModel = keyToField => {
|
|
|
37
44
|
}
|
|
38
45
|
return merge(acc, fleshedOutField)
|
|
39
46
|
}, {})
|
|
40
|
-
const
|
|
41
|
-
const internalFunctions = {
|
|
47
|
+
const frameworkProperties = {
|
|
42
48
|
functions: {
|
|
43
|
-
|
|
49
|
+
toObj: toObj(loadedInternals),
|
|
44
50
|
validate: {
|
|
45
51
|
model: createModelValidator(loadedInternals),
|
|
46
52
|
},
|
|
47
53
|
},
|
|
48
54
|
}
|
|
49
|
-
return merge(
|
|
55
|
+
return merge(loadedInternals, modelDefProperties, frameworkProperties)
|
|
50
56
|
}
|
|
51
57
|
}
|
|
52
58
|
|
package/src/serialization.js
CHANGED
|
@@ -12,8 +12,8 @@ const _getValue = async value => {
|
|
|
12
12
|
return _getValue(await value())
|
|
13
13
|
}
|
|
14
14
|
// Nested Json
|
|
15
|
-
if (type === 'object' && value.functions && value.functions.
|
|
16
|
-
return _getValue(await value.functions.
|
|
15
|
+
if (type === 'object' && value.functions && value.functions.toObj) {
|
|
16
|
+
return _getValue(await value.functions.toObj())
|
|
17
17
|
}
|
|
18
18
|
// Dates
|
|
19
19
|
if (type === 'object' && value.toISOString) {
|
|
@@ -30,7 +30,7 @@ const _shouldIgnoreKey = key => {
|
|
|
30
30
|
return IGNORABLE_KEYS.includes(key)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const
|
|
33
|
+
const toObj = keyToFunc => async () => {
|
|
34
34
|
return Object.entries(keyToFunc).reduce(async (acc, [key, value]) => {
|
|
35
35
|
const realAcc = await acc
|
|
36
36
|
if (_shouldIgnoreKey(key)) {
|
|
@@ -43,5 +43,5 @@ const toJson = keyToFunc => async () => {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
module.exports = {
|
|
46
|
-
|
|
46
|
+
toObj,
|
|
47
47
|
}
|
package/src/utils.js
CHANGED
|
@@ -6,7 +6,7 @@ const toTitleCase = string => {
|
|
|
6
6
|
return `${string.slice(0, 1).toUpperCase()}${string.slice(1)}`
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const createFieldTitle = key => {
|
|
10
10
|
const goodName = toTitleCase(key)
|
|
11
11
|
return `get${goodName}`
|
|
12
12
|
}
|
|
@@ -37,6 +37,6 @@ const loweredTitleCase = string => {
|
|
|
37
37
|
module.exports = {
|
|
38
38
|
createUuid,
|
|
39
39
|
loweredTitleCase,
|
|
40
|
-
createPropertyTitle,
|
|
40
|
+
createPropertyTitle: createFieldTitle,
|
|
41
41
|
toTitleCase,
|
|
42
42
|
}
|
package/src/validation.js
CHANGED
|
@@ -28,13 +28,7 @@ const isType = type => value => {
|
|
|
28
28
|
return _typeOrError(type, `Must be a ${type}`)(value)
|
|
29
29
|
}
|
|
30
30
|
const isNumber = isType('number')
|
|
31
|
-
const isInteger = _trueOrError(
|
|
32
|
-
const numberError = isNumber(v)
|
|
33
|
-
if (numberError) {
|
|
34
|
-
return false
|
|
35
|
-
}
|
|
36
|
-
return Number.isNaN(parseInt(v, 10)) === false
|
|
37
|
-
}, 'Must be an integer')
|
|
31
|
+
const isInteger = _trueOrError(Number.isInteger, 'Must be an integer')
|
|
38
32
|
|
|
39
33
|
const isBoolean = isType('boolean')
|
|
40
34
|
const isString = isType('string')
|
|
@@ -69,8 +63,15 @@ const meetsRegex =
|
|
|
69
63
|
}
|
|
70
64
|
|
|
71
65
|
const choices = choiceArray => value => {
|
|
72
|
-
if (
|
|
73
|
-
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
const bad = value.find(v => !choiceArray.includes(v))
|
|
68
|
+
if (bad) {
|
|
69
|
+
return `${bad} is not a valid choice`
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
if (choiceArray.includes(value) === false) {
|
|
73
|
+
return `${value} is not a valid choice`
|
|
74
|
+
}
|
|
74
75
|
}
|
|
75
76
|
return undefined
|
|
76
77
|
}
|
|
@@ -129,16 +130,20 @@ const minTextLength = min => value => {
|
|
|
129
130
|
return undefined
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
const aggregateValidator = methodOrMethods =>
|
|
133
|
+
const aggregateValidator = methodOrMethods => {
|
|
133
134
|
const toDo = Array.isArray(methodOrMethods)
|
|
134
135
|
? methodOrMethods
|
|
135
136
|
: [methodOrMethods]
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
|
|
138
|
+
const _aggregativeValidator = async value => {
|
|
139
|
+
const values = await Promise.all(
|
|
140
|
+
toDo.map(method => {
|
|
141
|
+
return method(value)
|
|
142
|
+
})
|
|
143
|
+
)
|
|
144
|
+
return values.filter(x => x)
|
|
145
|
+
}
|
|
146
|
+
return _aggregativeValidator
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
const emptyValidator = () => []
|
|
@@ -153,6 +158,7 @@ const CONFIG_TO_VALIDATE_METHOD = {
|
|
|
153
158
|
isNumber: _boolChoice(isNumber),
|
|
154
159
|
isString: _boolChoice(isString),
|
|
155
160
|
isArray: _boolChoice(isArray),
|
|
161
|
+
choices,
|
|
156
162
|
}
|
|
157
163
|
|
|
158
164
|
const createFieldValidator = config => {
|
|
@@ -164,24 +170,33 @@ const createFieldValidator = config => {
|
|
|
164
170
|
].filter(x => x)
|
|
165
171
|
const validator =
|
|
166
172
|
validators.length > 0 ? aggregateValidator(validators) : emptyValidator
|
|
167
|
-
|
|
173
|
+
const _fieldValidator = async value => {
|
|
168
174
|
const errors = await validator(value)
|
|
169
175
|
return [...new Set(flatMap(errors))]
|
|
170
176
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
keysAndFunctions.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
177
|
+
return _fieldValidator
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const createModelValidator = fields => {
|
|
181
|
+
const _modelValidator = async () => {
|
|
182
|
+
const keysAndFunctions = Object.entries(
|
|
183
|
+
get(fields, 'functions.validate', {})
|
|
184
|
+
)
|
|
185
|
+
const data = await Promise.all(
|
|
186
|
+
keysAndFunctions.map(async ([key, validator]) => {
|
|
187
|
+
if (key === 'model') {
|
|
188
|
+
return [key, []]
|
|
189
|
+
}
|
|
190
|
+
return [key, await validator()]
|
|
191
|
+
})
|
|
192
|
+
)
|
|
193
|
+
return data
|
|
194
|
+
.filter(([_, errors]) => Boolean(errors) && errors.length > 0)
|
|
195
|
+
.reduce((acc, [key, errors]) => {
|
|
196
|
+
return { ...acc, [key]: errors }
|
|
197
|
+
}, {})
|
|
198
|
+
}
|
|
199
|
+
return _modelValidator
|
|
185
200
|
}
|
|
186
201
|
|
|
187
202
|
module.exports = {
|
package/test/src/fields.test.js
CHANGED
|
@@ -5,10 +5,297 @@ const {
|
|
|
5
5
|
dateField,
|
|
6
6
|
referenceField,
|
|
7
7
|
arrayField,
|
|
8
|
+
constantValueField,
|
|
9
|
+
objectField,
|
|
10
|
+
numberField,
|
|
11
|
+
textField,
|
|
12
|
+
integerField,
|
|
13
|
+
emailField,
|
|
8
14
|
} = require('../../src/fields')
|
|
15
|
+
const { TYPE_PRIMATIVES, arrayType } = require('../../src/validation')
|
|
9
16
|
const { createModel } = require('../../src/models')
|
|
10
17
|
|
|
11
18
|
describe('/src/fields.js', () => {
|
|
19
|
+
describe('#emailField()', () => {
|
|
20
|
+
describe('#createGetter()', () => {
|
|
21
|
+
it('should be able to create without a config', () => {
|
|
22
|
+
assert.doesNotThrow(() => {
|
|
23
|
+
emailField()
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
it('should always have the value passed in', async () => {
|
|
27
|
+
const fieldInstance = emailField({})
|
|
28
|
+
const getter = fieldInstance.createGetter('testme@email.com')
|
|
29
|
+
const actual = await getter()
|
|
30
|
+
const expected = 'testme@email.com'
|
|
31
|
+
assert.deepEqual(actual, expected)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
describe('#getValidator()', () => {
|
|
35
|
+
it('should return and validate successful with basic input', async () => {
|
|
36
|
+
const fieldInstance = emailField({})
|
|
37
|
+
const getter = fieldInstance.createGetter('testme@email.com')
|
|
38
|
+
const validator = fieldInstance.getValidator(getter)
|
|
39
|
+
const actual = await validator()
|
|
40
|
+
const expected = 0
|
|
41
|
+
assert.equal(actual.length, expected)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
describe('#constantValueField()', () => {
|
|
46
|
+
describe('#createGetter()', () => {
|
|
47
|
+
it('should always have the value passed in', async () => {
|
|
48
|
+
const fieldInstance = constantValueField('constant')
|
|
49
|
+
const getter = fieldInstance.createGetter('changed')
|
|
50
|
+
const actual = await getter()
|
|
51
|
+
const expected = 'constant'
|
|
52
|
+
assert.deepEqual(actual, expected)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
describe('#getValidator()', () => {
|
|
56
|
+
it('should return and validate successful with basic input', async () => {
|
|
57
|
+
const fieldInstance = constantValueField('constant')
|
|
58
|
+
const getter = fieldInstance.createGetter('changed')
|
|
59
|
+
const validator = fieldInstance.getValidator(getter)
|
|
60
|
+
const actual = await validator()
|
|
61
|
+
const expected = 0
|
|
62
|
+
assert.equal(actual.length, expected)
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
describe('#objectField()', () => {
|
|
67
|
+
describe('#createGetter()', () => {
|
|
68
|
+
it('should be able to create without a config', () => {
|
|
69
|
+
assert.doesNotThrow(() => {
|
|
70
|
+
objectField()
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
it('should be able to get the object passed in', async () => {
|
|
74
|
+
const fieldInstance = objectField({})
|
|
75
|
+
const getter = fieldInstance.createGetter({
|
|
76
|
+
my: 'object',
|
|
77
|
+
complex: { it: 'is' },
|
|
78
|
+
})
|
|
79
|
+
const actual = await getter()
|
|
80
|
+
const expected = { my: 'object', complex: { it: 'is' } }
|
|
81
|
+
assert.deepEqual(actual, expected)
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
describe('#getValidator()', () => {
|
|
85
|
+
it('should return and validate successful with basic input', async () => {
|
|
86
|
+
const fieldInstance = objectField({})
|
|
87
|
+
const getter = fieldInstance.createGetter({
|
|
88
|
+
my: 'object',
|
|
89
|
+
complex: { it: 'is' },
|
|
90
|
+
})
|
|
91
|
+
const validator = fieldInstance.getValidator(getter)
|
|
92
|
+
const actual = await validator()
|
|
93
|
+
const expected = 0
|
|
94
|
+
assert.equal(actual.length, expected)
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe('#numberField()', () => {
|
|
100
|
+
describe('#createGetter()', () => {
|
|
101
|
+
it('should be able to create without a config', () => {
|
|
102
|
+
assert.doesNotThrow(() => {
|
|
103
|
+
numberField()
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
it('should be able to get the number passed in', async () => {
|
|
107
|
+
const fieldInstance = numberField({})
|
|
108
|
+
const getter = fieldInstance.createGetter(5)
|
|
109
|
+
const actual = await getter()
|
|
110
|
+
const expected = 5
|
|
111
|
+
assert.equal(actual, expected)
|
|
112
|
+
})
|
|
113
|
+
it('should be able to get float passed in', async () => {
|
|
114
|
+
const fieldInstance = numberField({})
|
|
115
|
+
const getter = fieldInstance.createGetter(5.123)
|
|
116
|
+
const actual = await getter()
|
|
117
|
+
const expected = 5.123
|
|
118
|
+
assert.equal(actual, expected)
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
describe('#getValidator()', () => {
|
|
122
|
+
it('should return and validate successful with basic input', async () => {
|
|
123
|
+
const fieldInstance = numberField({})
|
|
124
|
+
const getter = fieldInstance.createGetter(5)
|
|
125
|
+
const validator = fieldInstance.getValidator(getter)
|
|
126
|
+
const actual = await validator()
|
|
127
|
+
const expected = 0
|
|
128
|
+
assert.equal(actual.length, expected)
|
|
129
|
+
})
|
|
130
|
+
it('should return and validate successful with a basic float', async () => {
|
|
131
|
+
const fieldInstance = numberField({})
|
|
132
|
+
const getter = fieldInstance.createGetter(5.123)
|
|
133
|
+
const validator = fieldInstance.getValidator(getter)
|
|
134
|
+
const actual = await validator()
|
|
135
|
+
const expected = 0
|
|
136
|
+
assert.equal(actual.length, expected)
|
|
137
|
+
})
|
|
138
|
+
it('should return with errors with a non integer input', async () => {
|
|
139
|
+
const fieldInstance = numberField({})
|
|
140
|
+
const getter = fieldInstance.createGetter('string')
|
|
141
|
+
const validator = fieldInstance.getValidator(getter)
|
|
142
|
+
const actual = await validator()
|
|
143
|
+
const expected = 1
|
|
144
|
+
assert.equal(actual.length, expected)
|
|
145
|
+
})
|
|
146
|
+
it('should return with errors with a value=5 and maxValue=3', async () => {
|
|
147
|
+
const fieldInstance = numberField({ maxValue: 3 })
|
|
148
|
+
const getter = fieldInstance.createGetter(5)
|
|
149
|
+
const validator = fieldInstance.getValidator(getter)
|
|
150
|
+
const actual = await validator()
|
|
151
|
+
const expected = 1
|
|
152
|
+
assert.equal(actual.length, expected)
|
|
153
|
+
})
|
|
154
|
+
it('should return with errors with a value=2 and minValue=3', async () => {
|
|
155
|
+
const fieldInstance = numberField({ minValue: 3 })
|
|
156
|
+
const getter = fieldInstance.createGetter(2)
|
|
157
|
+
const validator = fieldInstance.getValidator(getter)
|
|
158
|
+
const actual = await validator()
|
|
159
|
+
const expected = 1
|
|
160
|
+
assert.equal(actual.length, expected)
|
|
161
|
+
})
|
|
162
|
+
it('should return with no errors with a value=3 and minValue=3 and maxValue=3', async () => {
|
|
163
|
+
const fieldInstance = numberField({ minValue: 3, maxValue: 3 })
|
|
164
|
+
const getter = fieldInstance.createGetter(3)
|
|
165
|
+
const validator = fieldInstance.getValidator(getter)
|
|
166
|
+
const actual = await validator()
|
|
167
|
+
const expected = 0
|
|
168
|
+
assert.equal(actual.length, expected)
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
describe('#integerField()', () => {
|
|
174
|
+
describe('#createGetter()', () => {
|
|
175
|
+
it('should be able to create without a config', () => {
|
|
176
|
+
assert.doesNotThrow(() => {
|
|
177
|
+
integerField()
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
it('should be able to get the number passed in', async () => {
|
|
181
|
+
const fieldInstance = integerField({})
|
|
182
|
+
const getter = fieldInstance.createGetter(5)
|
|
183
|
+
const actual = await getter()
|
|
184
|
+
const expected = 5
|
|
185
|
+
assert.equal(actual, expected)
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
describe('#getValidator()', () => {
|
|
189
|
+
it('should return and validate successful with basic input', async () => {
|
|
190
|
+
const fieldInstance = integerField({})
|
|
191
|
+
const getter = fieldInstance.createGetter(5)
|
|
192
|
+
const validator = fieldInstance.getValidator(getter)
|
|
193
|
+
const actual = await validator()
|
|
194
|
+
const expected = 0
|
|
195
|
+
assert.equal(actual.length, expected)
|
|
196
|
+
})
|
|
197
|
+
it('should return errors with a basic float', async () => {
|
|
198
|
+
const fieldInstance = integerField({})
|
|
199
|
+
const getter = fieldInstance.createGetter(5.123)
|
|
200
|
+
const validator = fieldInstance.getValidator(getter)
|
|
201
|
+
const actual = await validator()
|
|
202
|
+
const expected = 1
|
|
203
|
+
assert.equal(actual.length, expected)
|
|
204
|
+
})
|
|
205
|
+
it('should return with errors with a non integer input', async () => {
|
|
206
|
+
const fieldInstance = integerField({})
|
|
207
|
+
const getter = fieldInstance.createGetter('string')
|
|
208
|
+
const validator = fieldInstance.getValidator(getter)
|
|
209
|
+
const actual = await validator()
|
|
210
|
+
const expected = 1
|
|
211
|
+
assert.equal(actual.length, expected)
|
|
212
|
+
})
|
|
213
|
+
it('should return with errors with a value=5 and maxValue=3', async () => {
|
|
214
|
+
const fieldInstance = integerField({ maxValue: 3 })
|
|
215
|
+
const getter = fieldInstance.createGetter(5)
|
|
216
|
+
const validator = fieldInstance.getValidator(getter)
|
|
217
|
+
const actual = await validator()
|
|
218
|
+
const expected = 1
|
|
219
|
+
assert.equal(actual.length, expected)
|
|
220
|
+
})
|
|
221
|
+
it('should return with errors with a value=2 and minValue=3', async () => {
|
|
222
|
+
const fieldInstance = integerField({ minValue: 3 })
|
|
223
|
+
const getter = fieldInstance.createGetter(2)
|
|
224
|
+
const validator = fieldInstance.getValidator(getter)
|
|
225
|
+
const actual = await validator()
|
|
226
|
+
const expected = 1
|
|
227
|
+
assert.equal(actual.length, expected)
|
|
228
|
+
})
|
|
229
|
+
it('should return with no errors with a value=3 and minValue=3 and maxValue=3', async () => {
|
|
230
|
+
const fieldInstance = integerField({ minValue: 3, maxValue: 3 })
|
|
231
|
+
const getter = fieldInstance.createGetter(3)
|
|
232
|
+
const validator = fieldInstance.getValidator(getter)
|
|
233
|
+
const actual = await validator()
|
|
234
|
+
const expected = 0
|
|
235
|
+
assert.equal(actual.length, expected)
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
describe('#textField()', () => {
|
|
241
|
+
describe('#createGetter()', () => {
|
|
242
|
+
it('should be able to create without a config', () => {
|
|
243
|
+
assert.doesNotThrow(() => {
|
|
244
|
+
textField()
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
it('should be able to get the value passed in', async () => {
|
|
248
|
+
const fieldInstance = textField({})
|
|
249
|
+
const getter = fieldInstance.createGetter('basic input')
|
|
250
|
+
const actual = await getter()
|
|
251
|
+
const expected = 'basic input'
|
|
252
|
+
assert.equal(actual, expected)
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
describe('#getValidator()', () => {
|
|
256
|
+
it('should return and validate successful with basic input', async () => {
|
|
257
|
+
const fieldInstance = textField({})
|
|
258
|
+
const getter = fieldInstance.createGetter('basic input')
|
|
259
|
+
const validator = fieldInstance.getValidator(getter)
|
|
260
|
+
const actual = await validator()
|
|
261
|
+
const expected = 0
|
|
262
|
+
assert.equal(actual.length, expected)
|
|
263
|
+
})
|
|
264
|
+
it('should return with errors with a value=5', async () => {
|
|
265
|
+
const fieldInstance = textField({})
|
|
266
|
+
const getter = fieldInstance.createGetter(5)
|
|
267
|
+
const validator = fieldInstance.getValidator(getter)
|
|
268
|
+
const actual = await validator()
|
|
269
|
+
const expected = 1
|
|
270
|
+
assert.equal(actual.length, expected)
|
|
271
|
+
})
|
|
272
|
+
it('should return with errors with a value="hello" and maxLength=3', async () => {
|
|
273
|
+
const fieldInstance = textField({ maxLength: 3 })
|
|
274
|
+
const getter = fieldInstance.createGetter('hello')
|
|
275
|
+
const validator = fieldInstance.getValidator(getter)
|
|
276
|
+
const actual = await validator()
|
|
277
|
+
const expected = 1
|
|
278
|
+
assert.equal(actual.length, expected)
|
|
279
|
+
})
|
|
280
|
+
it('should return with errors with a value="hello" and minLength=10', async () => {
|
|
281
|
+
const fieldInstance = textField({ minLength: 10 })
|
|
282
|
+
const getter = fieldInstance.createGetter('hello')
|
|
283
|
+
const validator = fieldInstance.getValidator(getter)
|
|
284
|
+
const actual = await validator()
|
|
285
|
+
const expected = 1
|
|
286
|
+
assert.equal(actual.length, expected)
|
|
287
|
+
})
|
|
288
|
+
it('should return with no errors with a value="hello" and minLength=5 and maxLength=5', async () => {
|
|
289
|
+
const fieldInstance = textField({ minLength: 5, maxLength: 5 })
|
|
290
|
+
const getter = fieldInstance.createGetter('hello')
|
|
291
|
+
const validator = fieldInstance.getValidator(getter)
|
|
292
|
+
const actual = await validator()
|
|
293
|
+
const expected = 0
|
|
294
|
+
assert.equal(actual.length, expected)
|
|
295
|
+
})
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
|
|
12
299
|
describe('#arrayField()', () => {
|
|
13
300
|
describe('#createGetter()', () => {
|
|
14
301
|
it('should return an array passed in without issue', async () => {
|
|
@@ -49,6 +336,32 @@ describe('/src/fields.js', () => {
|
|
|
49
336
|
const expected = []
|
|
50
337
|
assert.deepEqual(actual, expected)
|
|
51
338
|
})
|
|
339
|
+
it('should error an array passed in when it doesnt have the right types', async () => {
|
|
340
|
+
const theField = arrayField({
|
|
341
|
+
validators: [arrayType(TYPE_PRIMATIVES.integer)],
|
|
342
|
+
})
|
|
343
|
+
const getter = theField.createGetter([1, 'string', 3])
|
|
344
|
+
const validator = theField.getValidator(getter)
|
|
345
|
+
const actual = await validator()
|
|
346
|
+
const expected = 1
|
|
347
|
+
assert.deepEqual(actual.length, expected)
|
|
348
|
+
})
|
|
349
|
+
it('should validate an array with [4,4,5,5,6,6] when choices are [4,5,6]', async () => {
|
|
350
|
+
const theField = arrayField({ choices: [4, 5, 6] })
|
|
351
|
+
const getter = theField.createGetter([4, 4, 5, 5, 6, 6])
|
|
352
|
+
const validator = theField.getValidator(getter)
|
|
353
|
+
const actual = await validator()
|
|
354
|
+
const expected = []
|
|
355
|
+
assert.deepEqual(actual, expected)
|
|
356
|
+
})
|
|
357
|
+
it('should return errors when an array with [4,4,3,5,5,6,6] when choices are [4,5,6]', async () => {
|
|
358
|
+
const theField = arrayField({ choices: [4, 5, 6] })
|
|
359
|
+
const getter = theField.createGetter([4, 4, 3, 5, 5, 6, 6])
|
|
360
|
+
const validator = theField.getValidator(getter)
|
|
361
|
+
const actual = await validator()
|
|
362
|
+
const expected = 1
|
|
363
|
+
assert.equal(actual.length, expected)
|
|
364
|
+
})
|
|
52
365
|
})
|
|
53
366
|
})
|
|
54
367
|
describe('#field()', () => {
|
|
@@ -168,7 +481,7 @@ describe('/src/fields.js', () => {
|
|
|
168
481
|
assert.deepEqual(actual, expected)
|
|
169
482
|
})
|
|
170
483
|
it('should take the smartObject as a value', async () => {
|
|
171
|
-
const proto = createModel({
|
|
484
|
+
const proto = createModel('name', {
|
|
172
485
|
id: uniqueId({ value: 'obj-id' }),
|
|
173
486
|
})
|
|
174
487
|
const input = [proto({ id: 'obj-id' })]
|
|
@@ -177,23 +490,23 @@ describe('/src/fields.js', () => {
|
|
|
177
490
|
const expected = 'obj-id'
|
|
178
491
|
assert.deepEqual(actual, expected)
|
|
179
492
|
})
|
|
180
|
-
describe('#functions.
|
|
181
|
-
it('should use the getId of the smartObject passed in when
|
|
182
|
-
const proto = createModel({
|
|
493
|
+
describe('#functions.toObj()', () => {
|
|
494
|
+
it('should use the getId of the smartObject passed in when toObj is called', async () => {
|
|
495
|
+
const proto = createModel('name', {
|
|
183
496
|
id: uniqueId({ value: 'obj-id' }),
|
|
184
497
|
})
|
|
185
498
|
const input = [proto({ id: 'obj-id' })]
|
|
186
499
|
const instance = await referenceField({}).createGetter(...input)()
|
|
187
|
-
const actual = await instance.functions.
|
|
500
|
+
const actual = await instance.functions.toObj()
|
|
188
501
|
const expected = 'obj-id'
|
|
189
502
|
assert.deepEqual(actual, expected)
|
|
190
503
|
})
|
|
191
|
-
it('should return "obj-id" when switch-a-roo fetcher is used and
|
|
504
|
+
it('should return "obj-id" when switch-a-roo fetcher is used and toObj is called', async () => {
|
|
192
505
|
const input = ['obj-id']
|
|
193
506
|
const instance = await referenceField({
|
|
194
507
|
fetcher: () => ({ id: 'obj-id', prop: 'switch-a-roo' }),
|
|
195
508
|
}).createGetter(...input)()
|
|
196
|
-
const actual = await instance.functions.
|
|
509
|
+
const actual = await instance.functions.toObj()
|
|
197
510
|
const expected = 'obj-id'
|
|
198
511
|
assert.deepEqual(actual, expected)
|
|
199
512
|
})
|
package/test/src/models.test.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const _ = require('lodash')
|
|
1
2
|
const assert = require('chai').assert
|
|
2
3
|
const { createModel } = require('../../src/models')
|
|
3
4
|
const { field } = require('../../src/fields')
|
|
@@ -5,16 +6,53 @@ const { field } = require('../../src/fields')
|
|
|
5
6
|
describe('/src/models.js', () => {
|
|
6
7
|
describe('#createModel()', () => {
|
|
7
8
|
it('should return a function when called once with valid data', () => {
|
|
8
|
-
const actual = createModel({})
|
|
9
|
+
const actual = createModel('name', {})
|
|
9
10
|
const expected = 'function'
|
|
10
11
|
assert.isFunction(actual)
|
|
11
12
|
})
|
|
12
13
|
describe('#()', () => {
|
|
14
|
+
it('should not throw an exception if nothing is passed into function', () => {
|
|
15
|
+
const input = {
|
|
16
|
+
myField: field({ required: true }),
|
|
17
|
+
}
|
|
18
|
+
const model = createModel('name', input)
|
|
19
|
+
assert.doesNotThrow(() => {
|
|
20
|
+
model()
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
it('should return an object that contains meta.fields.myField', () => {
|
|
24
|
+
const input = {
|
|
25
|
+
myField: field({ required: true }),
|
|
26
|
+
}
|
|
27
|
+
const model = createModel('name', input)
|
|
28
|
+
const instance = model({ myField: 'value' })
|
|
29
|
+
const actual = _.get(instance, 'meta.fields.myField')
|
|
30
|
+
assert.isOk(actual)
|
|
31
|
+
})
|
|
32
|
+
it('should return an object that contains meta.modelName===test-the-name', () => {
|
|
33
|
+
const input = {
|
|
34
|
+
myField: field({ required: true }),
|
|
35
|
+
}
|
|
36
|
+
const model = createModel('test-the-name', input)
|
|
37
|
+
const instance = model({ myField: 'value' })
|
|
38
|
+
const actual = _.get(instance, 'meta.modelName')
|
|
39
|
+
const expected = 'test-the-name'
|
|
40
|
+
assert.deepEqual(actual, expected)
|
|
41
|
+
})
|
|
42
|
+
it('should return an object that contains meta.fields.myField', () => {
|
|
43
|
+
const input = {
|
|
44
|
+
myField: field({ required: true }),
|
|
45
|
+
}
|
|
46
|
+
const model = createModel('name', input)
|
|
47
|
+
const instance = model({ myField: 'value' })
|
|
48
|
+
const actual = _.get(instance, 'meta.fields.myField')
|
|
49
|
+
assert.isOk(actual)
|
|
50
|
+
})
|
|
13
51
|
it('should use the value passed in when field.defaultValue and field.value are not set', async () => {
|
|
14
52
|
const input = {
|
|
15
53
|
myField: field({ required: true }),
|
|
16
54
|
}
|
|
17
|
-
const model = createModel(input)
|
|
55
|
+
const model = createModel('name', input)
|
|
18
56
|
const instance = model({ myField: 'passed-in' })
|
|
19
57
|
const actual = await instance.getMyField()
|
|
20
58
|
const expected = 'passed-in'
|
|
@@ -24,7 +62,7 @@ describe('/src/models.js', () => {
|
|
|
24
62
|
const input = {
|
|
25
63
|
myField: field({ value: 'value', defaultValue: 'default-value' }),
|
|
26
64
|
}
|
|
27
|
-
const model = createModel(input)
|
|
65
|
+
const model = createModel('name', input)
|
|
28
66
|
const instance = model({ myField: 'passed-in' })
|
|
29
67
|
const actual = await instance.getMyField()
|
|
30
68
|
const expected = 'value'
|
|
@@ -34,7 +72,7 @@ describe('/src/models.js', () => {
|
|
|
34
72
|
const input = {
|
|
35
73
|
myField: field({ value: 'value' }),
|
|
36
74
|
}
|
|
37
|
-
const model = createModel(input)
|
|
75
|
+
const model = createModel('name', input)
|
|
38
76
|
const instance = model({ myField: 'passed-in' })
|
|
39
77
|
const actual = await instance.getMyField()
|
|
40
78
|
const expected = 'value'
|
|
@@ -44,7 +82,7 @@ describe('/src/models.js', () => {
|
|
|
44
82
|
const input = {
|
|
45
83
|
myField: field({ defaultValue: 'defaultValue' }),
|
|
46
84
|
}
|
|
47
|
-
const model = createModel(input)
|
|
85
|
+
const model = createModel('name', input)
|
|
48
86
|
const instance = model({})
|
|
49
87
|
const actual = await instance.getMyField()
|
|
50
88
|
const expected = 'defaultValue'
|
|
@@ -54,7 +92,7 @@ describe('/src/models.js', () => {
|
|
|
54
92
|
const input = {
|
|
55
93
|
myField: field({ defaultValue: 'defaultValue' }),
|
|
56
94
|
}
|
|
57
|
-
const model = createModel(input)
|
|
95
|
+
const model = createModel('name', input)
|
|
58
96
|
const instance = model({ myField: null })
|
|
59
97
|
const actual = await instance.getMyField()
|
|
60
98
|
const expected = 'defaultValue'
|
|
@@ -65,9 +103,8 @@ describe('/src/models.js', () => {
|
|
|
65
103
|
id: field({ required: true }),
|
|
66
104
|
type: field(),
|
|
67
105
|
}
|
|
68
|
-
const model = createModel(input)
|
|
106
|
+
const model = createModel('name', input)
|
|
69
107
|
const actual = model({ id: 'my-id', type: 'my-type' })
|
|
70
|
-
console.log(actual)
|
|
71
108
|
assert.isOk(actual.getId)
|
|
72
109
|
assert.isOk(actual.getType)
|
|
73
110
|
})
|
|
@@ -76,21 +113,20 @@ describe('/src/models.js', () => {
|
|
|
76
113
|
id: field({ required: true }),
|
|
77
114
|
type: field(),
|
|
78
115
|
}
|
|
79
|
-
const model = createModel(input)
|
|
116
|
+
const model = createModel('name', input)
|
|
80
117
|
const instance = model({ type: 'my-type' })
|
|
81
118
|
const actual = await instance.functions.validate.model()
|
|
82
119
|
const expected = 1
|
|
83
|
-
console.log(actual)
|
|
84
120
|
assert.equal(Object.values(actual).length, expected)
|
|
85
121
|
})
|
|
86
122
|
})
|
|
87
123
|
it('should return a function when called once with valid data', () => {
|
|
88
|
-
const actual = createModel({})
|
|
124
|
+
const actual = createModel('name', {})
|
|
89
125
|
assert.isFunction(actual)
|
|
90
126
|
})
|
|
91
127
|
it('should throw an exception if a key "model" is passed in', () => {
|
|
92
128
|
assert.throws(() => {
|
|
93
|
-
createModel({ model: 'weeee' })
|
|
129
|
+
createModel('name', { model: 'weeee' })
|
|
94
130
|
})
|
|
95
131
|
})
|
|
96
132
|
})
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const assert = require('chai').assert
|
|
2
|
-
const {
|
|
2
|
+
const { toObj } = require('../../src/serialization')
|
|
3
3
|
|
|
4
4
|
describe('/src/serialization.js', () => {
|
|
5
|
-
describe('#
|
|
5
|
+
describe('#toObj()', () => {
|
|
6
6
|
it('serialize a very basic input of key-value', async () => {
|
|
7
|
-
const actual = await
|
|
7
|
+
const actual = await toObj({
|
|
8
8
|
key: 'value',
|
|
9
9
|
key2: 'value2',
|
|
10
10
|
})()
|
|
@@ -15,7 +15,7 @@ describe('/src/serialization.js', () => {
|
|
|
15
15
|
assert.deepEqual(actual, expected)
|
|
16
16
|
})
|
|
17
17
|
it('should ignore "meta" properties', async () => {
|
|
18
|
-
const actual = await
|
|
18
|
+
const actual = await toObj({
|
|
19
19
|
key: 'value',
|
|
20
20
|
key2: 'value2',
|
|
21
21
|
meta: {
|
|
@@ -29,7 +29,7 @@ describe('/src/serialization.js', () => {
|
|
|
29
29
|
assert.deepEqual(actual, expected)
|
|
30
30
|
})
|
|
31
31
|
it('should ignore "functions" properties', async () => {
|
|
32
|
-
const actual = await
|
|
32
|
+
const actual = await toObj({
|
|
33
33
|
key: 'value',
|
|
34
34
|
key2: 'value2',
|
|
35
35
|
functions: {
|
|
@@ -44,13 +44,13 @@ describe('/src/serialization.js', () => {
|
|
|
44
44
|
}
|
|
45
45
|
assert.deepEqual(actual, expected)
|
|
46
46
|
})
|
|
47
|
-
it('should call "functions.
|
|
48
|
-
const actual = await
|
|
47
|
+
it('should call "functions.toObj" on nested objects', async () => {
|
|
48
|
+
const actual = await toObj({
|
|
49
49
|
key: 'value',
|
|
50
50
|
key2: {
|
|
51
51
|
complex: () => ({ func: 'func' }),
|
|
52
52
|
functions: {
|
|
53
|
-
|
|
53
|
+
toObj: () => ({ func: 'value' }),
|
|
54
54
|
},
|
|
55
55
|
},
|
|
56
56
|
})()
|
|
@@ -62,12 +62,12 @@ describe('/src/serialization.js', () => {
|
|
|
62
62
|
}
|
|
63
63
|
assert.deepEqual(actual, expected)
|
|
64
64
|
})
|
|
65
|
-
it('should call "
|
|
66
|
-
const actual = await
|
|
65
|
+
it('should call "toObj" on very nested objects', async () => {
|
|
66
|
+
const actual = await toObj({
|
|
67
67
|
key: 'value',
|
|
68
68
|
key2: {
|
|
69
69
|
functions: {
|
|
70
|
-
|
|
70
|
+
toObj: () => ({ func: 'value' }),
|
|
71
71
|
},
|
|
72
72
|
},
|
|
73
73
|
})()
|
|
@@ -80,7 +80,7 @@ describe('/src/serialization.js', () => {
|
|
|
80
80
|
assert.deepEqual(actual, expected)
|
|
81
81
|
})
|
|
82
82
|
it('should set an undefined property to null', async () => {
|
|
83
|
-
const actual = await
|
|
83
|
+
const actual = await toObj({
|
|
84
84
|
key: 'value',
|
|
85
85
|
key2: undefined,
|
|
86
86
|
})()
|
|
@@ -91,7 +91,7 @@ describe('/src/serialization.js', () => {
|
|
|
91
91
|
assert.deepEqual(actual, expected)
|
|
92
92
|
})
|
|
93
93
|
it('should get the value of a function', async () => {
|
|
94
|
-
const actual = await
|
|
94
|
+
const actual = await toObj({
|
|
95
95
|
key: 'value',
|
|
96
96
|
key2: () => {
|
|
97
97
|
return 'funcvalue'
|
|
@@ -104,7 +104,7 @@ describe('/src/serialization.js', () => {
|
|
|
104
104
|
assert.deepEqual(actual, expected)
|
|
105
105
|
})
|
|
106
106
|
it('should change property getTheValue to "theValue"', async () => {
|
|
107
|
-
const actual = await
|
|
107
|
+
const actual = await toObj({
|
|
108
108
|
key: 'value',
|
|
109
109
|
getTheValue: () => 'funcvalue',
|
|
110
110
|
})()
|
|
@@ -115,7 +115,7 @@ describe('/src/serialization.js', () => {
|
|
|
115
115
|
assert.deepEqual(actual, expected)
|
|
116
116
|
})
|
|
117
117
|
it('should return "2021-09-16T21:51:56.039Z" for the set date.', async () => {
|
|
118
|
-
const actual = await
|
|
118
|
+
const actual = await toObj({
|
|
119
119
|
myDate: new Date('2021-09-16T21:51:56.039Z'),
|
|
120
120
|
})()
|
|
121
121
|
const expected = {
|
|
@@ -258,6 +258,20 @@ describe('/src/validation.js', () => {
|
|
|
258
258
|
sinon.assert.calledOnce(fields.functions.validate.id)
|
|
259
259
|
sinon.assert.calledOnce(fields.functions.validate.type)
|
|
260
260
|
})
|
|
261
|
+
it('should not run a validate.model function', async () => {
|
|
262
|
+
const fields = {
|
|
263
|
+
functions: {
|
|
264
|
+
validate: {
|
|
265
|
+
id: sinon.stub().returns(undefined),
|
|
266
|
+
type: sinon.stub().returns(undefined),
|
|
267
|
+
model: sinon.stub().returns(undefined),
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
}
|
|
271
|
+
const validator = createModelValidator(fields)
|
|
272
|
+
await validator()
|
|
273
|
+
sinon.assert.notCalled(fields.functions.validate.model)
|
|
274
|
+
})
|
|
261
275
|
it('should combine results for both functions.validate for two objects that error', async () => {
|
|
262
276
|
const fields = {
|
|
263
277
|
functions: {
|