functional-models 1.0.25 → 1.1.0
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/.eslintignore +1 -0
- package/.eslintrc +9 -15
- package/cucumber.js +10 -0
- package/features/arrayFields.feature +7 -7
- package/features/basic-ts.feature +13 -0
- package/features/functions.feature +2 -3
- package/package.json +34 -10
- package/src/constants.ts +15 -0
- package/src/errors.ts +18 -0
- package/src/index.ts +11 -0
- package/src/interfaces.ts +323 -0
- package/src/lazy.ts +24 -0
- package/src/methods.ts +30 -0
- package/src/models.ts +183 -0
- package/src/properties.ts +375 -0
- package/src/serialization.ts +39 -0
- package/src/utils.ts +42 -0
- package/src/validation.ts +390 -0
- package/{features/stepDefinitions/steps.js → stepDefinitions/oldSteps.ts} +76 -53
- package/stepDefinitions/tssteps.ts +107 -0
- package/test/src/errors.test.ts +31 -0
- package/test/src/{lazy.test.js → lazy.test.ts} +4 -4
- package/test/src/methods.test.ts +45 -0
- package/test/src/models.test.ts +417 -0
- package/test/src/{properties.test.js → properties.test.ts} +257 -64
- package/test/src/serialization.test.ts +80 -0
- package/test/src/{utils.test.js → utils.test.ts} +29 -7
- package/test/src/{validation.test.js → validation.test.ts} +278 -210
- package/tsconfig.json +100 -0
- package/src/functions.js +0 -7
- package/src/index.js +0 -7
- package/src/lazy.js +0 -19
- package/src/models.js +0 -150
- package/src/properties.js +0 -261
- package/src/serialization.js +0 -47
- package/src/utils.js +0 -42
- package/src/validation.js +0 -274
- package/test/base/index.test.js +0 -5
- package/test/src/functions.test.js +0 -45
- package/test/src/index.test.js +0 -5
- package/test/src/models.test.js +0 -380
- package/test/src/serialization.test.js +0 -127
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import isEmpty from 'lodash/isEmpty'
|
|
2
|
+
import merge from 'lodash/merge'
|
|
3
|
+
import flatMap from 'lodash/flatMap'
|
|
4
|
+
import {
|
|
5
|
+
FunctionalModel,
|
|
6
|
+
ModelInstance,
|
|
7
|
+
Model,
|
|
8
|
+
ModelComponentValidator,
|
|
9
|
+
PropertyValidatorComponent,
|
|
10
|
+
PropertyValidatorComponentType,
|
|
11
|
+
PropertyValidatorComponentSync,
|
|
12
|
+
PropertyValidatorComponentTypeAdvanced,
|
|
13
|
+
PropertyValidator,
|
|
14
|
+
PropertyConfig,
|
|
15
|
+
MaybeFunction,
|
|
16
|
+
PropertyValidators,
|
|
17
|
+
ValueGetter,
|
|
18
|
+
Arrayable,
|
|
19
|
+
FunctionalType,
|
|
20
|
+
ModelErrors,
|
|
21
|
+
} from './interfaces'
|
|
22
|
+
|
|
23
|
+
const TYPE_PRIMITIVES = {
|
|
24
|
+
boolean: 'boolean',
|
|
25
|
+
string: 'string',
|
|
26
|
+
object: 'object',
|
|
27
|
+
number: 'number',
|
|
28
|
+
integer: 'integer',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const filterEmpty = <T>(
|
|
32
|
+
array: readonly (T | undefined | null)[]
|
|
33
|
+
): readonly T[] => {
|
|
34
|
+
return array.filter(x => x) as readonly T[]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const _trueOrError =
|
|
38
|
+
(method: Function, error: string): PropertyValidatorComponentSync =>
|
|
39
|
+
(value: any) => {
|
|
40
|
+
if (method(value) === false) {
|
|
41
|
+
return error
|
|
42
|
+
}
|
|
43
|
+
return undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const _typeOrError =
|
|
47
|
+
(type: string, errorMessage: string): PropertyValidatorComponentSync =>
|
|
48
|
+
(value: any) => {
|
|
49
|
+
if (typeof value !== type) {
|
|
50
|
+
return errorMessage
|
|
51
|
+
}
|
|
52
|
+
return undefined
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const isType =
|
|
56
|
+
(type: string): PropertyValidatorComponentSync =>
|
|
57
|
+
(value: any) => {
|
|
58
|
+
// @ts-ignore
|
|
59
|
+
return _typeOrError(type, `Must be a ${type}`)(value)
|
|
60
|
+
}
|
|
61
|
+
const isNumber = isType('number')
|
|
62
|
+
const isInteger = _trueOrError(Number.isInteger, 'Must be an integer')
|
|
63
|
+
|
|
64
|
+
const isBoolean = isType('boolean')
|
|
65
|
+
const isString = isType('string')
|
|
66
|
+
const isArray = _trueOrError(
|
|
67
|
+
(v: any) => Array.isArray(v),
|
|
68
|
+
'Value is not an array'
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const PRIMITIVE_TO_SPECIAL_TYPE_VALIDATOR = {
|
|
72
|
+
[TYPE_PRIMITIVES.boolean]: isBoolean,
|
|
73
|
+
[TYPE_PRIMITIVES.string]: isString,
|
|
74
|
+
[TYPE_PRIMITIVES.integer]: isInteger,
|
|
75
|
+
[TYPE_PRIMITIVES.number]: isNumber,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const arrayType =
|
|
79
|
+
(type: string): PropertyValidatorComponentSync =>
|
|
80
|
+
(value: Arrayable<FunctionalType>) => {
|
|
81
|
+
// @ts-ignore
|
|
82
|
+
const arrayError = isArray(value)
|
|
83
|
+
if (arrayError) {
|
|
84
|
+
return arrayError
|
|
85
|
+
}
|
|
86
|
+
const validator = PRIMITIVE_TO_SPECIAL_TYPE_VALIDATOR[type] || isType(type)
|
|
87
|
+
return (value as readonly []).reduce(
|
|
88
|
+
(acc: string | undefined, v: FunctionalType) => {
|
|
89
|
+
if (acc) {
|
|
90
|
+
return acc
|
|
91
|
+
}
|
|
92
|
+
// @ts-ignore
|
|
93
|
+
return validator(v)
|
|
94
|
+
},
|
|
95
|
+
undefined
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const meetsRegex =
|
|
100
|
+
(
|
|
101
|
+
regex: string | RegExp,
|
|
102
|
+
flags?: string,
|
|
103
|
+
errorMessage: string = 'Format was invalid'
|
|
104
|
+
): PropertyValidatorComponentSync =>
|
|
105
|
+
(value: FunctionalType) => {
|
|
106
|
+
const reg = new RegExp(regex, flags)
|
|
107
|
+
// @ts-ignore
|
|
108
|
+
return _trueOrError((v: string) => reg.test(v), errorMessage)(value)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const choices =
|
|
112
|
+
(choiceArray: readonly FunctionalType[]): PropertyValidatorComponentSync =>
|
|
113
|
+
(value: Arrayable<FunctionalType>) => {
|
|
114
|
+
if (Array.isArray(value)) {
|
|
115
|
+
const bad = value.find(v => !choiceArray.includes(v))
|
|
116
|
+
if (bad) {
|
|
117
|
+
return `${bad} is not a valid choice`
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
if (!choiceArray.includes(value as FunctionalType)) {
|
|
121
|
+
return `${value} is not a valid choice`
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return undefined
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const isDate: PropertyValidatorComponentType<Date> = (value: Date) => {
|
|
128
|
+
if (!value) {
|
|
129
|
+
return 'Date value is empty'
|
|
130
|
+
}
|
|
131
|
+
if (!value.toISOString) {
|
|
132
|
+
return 'Value is not a date'
|
|
133
|
+
}
|
|
134
|
+
return undefined
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const isRequired: PropertyValidatorComponentSync = (value?: any) => {
|
|
138
|
+
if (value === true || value === false) {
|
|
139
|
+
return undefined
|
|
140
|
+
}
|
|
141
|
+
// @ts-ignore
|
|
142
|
+
if (isNumber(value) === undefined) {
|
|
143
|
+
return undefined
|
|
144
|
+
}
|
|
145
|
+
const empty = isEmpty(value)
|
|
146
|
+
if (empty) {
|
|
147
|
+
// @ts-ignore
|
|
148
|
+
if (isDate(value)) {
|
|
149
|
+
return 'A value is required'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return undefined
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const maxNumber =
|
|
156
|
+
(max: Number): PropertyValidatorComponentType<number> =>
|
|
157
|
+
(value: number) => {
|
|
158
|
+
// @ts-ignore
|
|
159
|
+
const numberError = isNumber(value)
|
|
160
|
+
if (numberError) {
|
|
161
|
+
return numberError
|
|
162
|
+
}
|
|
163
|
+
if (value && value > max) {
|
|
164
|
+
return `The maximum is ${max}`
|
|
165
|
+
}
|
|
166
|
+
return undefined
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const minNumber =
|
|
170
|
+
(min: Number): PropertyValidatorComponentType<number> =>
|
|
171
|
+
(value: Number) => {
|
|
172
|
+
// @ts-ignore
|
|
173
|
+
const numberError = isNumber(value)
|
|
174
|
+
if (numberError) {
|
|
175
|
+
return numberError
|
|
176
|
+
}
|
|
177
|
+
if (value && value < min) {
|
|
178
|
+
return `The minimum is ${min}`
|
|
179
|
+
}
|
|
180
|
+
return undefined
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const maxTextLength =
|
|
184
|
+
(max: Number): PropertyValidatorComponentType<string> =>
|
|
185
|
+
(value: string) => {
|
|
186
|
+
// @ts-ignore
|
|
187
|
+
const stringError = isString(value)
|
|
188
|
+
if (stringError) {
|
|
189
|
+
return stringError
|
|
190
|
+
}
|
|
191
|
+
if (value && value.length > max) {
|
|
192
|
+
return `The maximum length is ${max}`
|
|
193
|
+
}
|
|
194
|
+
return undefined
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const minTextLength =
|
|
198
|
+
(min: Number): PropertyValidatorComponentType<string> =>
|
|
199
|
+
(value: string) => {
|
|
200
|
+
// @ts-ignore
|
|
201
|
+
const stringError = isString(value)
|
|
202
|
+
if (stringError) {
|
|
203
|
+
return stringError
|
|
204
|
+
}
|
|
205
|
+
if (value && value.length < min) {
|
|
206
|
+
return `The minimum length is ${min}`
|
|
207
|
+
}
|
|
208
|
+
return undefined
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const referenceTypeMatch = <TModel extends FunctionalModel>(
|
|
212
|
+
referencedModel: MaybeFunction<Model<TModel>>
|
|
213
|
+
): PropertyValidatorComponentTypeAdvanced<ModelInstance<TModel>, TModel> => {
|
|
214
|
+
return (value?: ModelInstance<TModel>) => {
|
|
215
|
+
if (!value) {
|
|
216
|
+
return 'Must include a value'
|
|
217
|
+
}
|
|
218
|
+
// This needs to stay here, as it delays the creation long enough for
|
|
219
|
+
// self referencing types.
|
|
220
|
+
const model =
|
|
221
|
+
typeof referencedModel === 'function'
|
|
222
|
+
? referencedModel()
|
|
223
|
+
: referencedModel
|
|
224
|
+
// Assumption: By the time this is received, value === a model instance.
|
|
225
|
+
const eModel = model.getName()
|
|
226
|
+
const aModel = value.getModel().getName()
|
|
227
|
+
if (eModel !== aModel) {
|
|
228
|
+
return `Model should be ${eModel} instead, received ${aModel}`
|
|
229
|
+
}
|
|
230
|
+
return undefined
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const aggregateValidator = (
|
|
235
|
+
value: any,
|
|
236
|
+
methodOrMethods:
|
|
237
|
+
| PropertyValidatorComponent
|
|
238
|
+
| readonly PropertyValidatorComponent[]
|
|
239
|
+
) => {
|
|
240
|
+
const toDo = Array.isArray(methodOrMethods)
|
|
241
|
+
? methodOrMethods
|
|
242
|
+
: [methodOrMethods]
|
|
243
|
+
|
|
244
|
+
const _aggregativeValidator: PropertyValidator = async (
|
|
245
|
+
instance: ModelInstance<any>,
|
|
246
|
+
instanceData: FunctionalModel
|
|
247
|
+
) => {
|
|
248
|
+
const values = await Promise.all(
|
|
249
|
+
toDo.map(method => {
|
|
250
|
+
return method(value, instance, instanceData)
|
|
251
|
+
})
|
|
252
|
+
)
|
|
253
|
+
return filterEmpty(values)
|
|
254
|
+
}
|
|
255
|
+
return _aggregativeValidator
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const emptyValidator: PropertyValidatorComponentSync = () => undefined
|
|
259
|
+
|
|
260
|
+
const _boolChoice =
|
|
261
|
+
(method: (configValue: any) => PropertyValidatorComponentSync) =>
|
|
262
|
+
(configValue: any) => {
|
|
263
|
+
const func = method(configValue)
|
|
264
|
+
const validatorWrapper: PropertyValidatorComponentSync = (
|
|
265
|
+
value: any,
|
|
266
|
+
modelInstance: ModelInstance<any>,
|
|
267
|
+
modelData: FunctionalModel
|
|
268
|
+
) => {
|
|
269
|
+
return func(value, modelInstance, modelData)
|
|
270
|
+
}
|
|
271
|
+
return validatorWrapper
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
type MethodConfigDict = {
|
|
275
|
+
readonly [s: string]: (config: any) => PropertyValidatorComponentSync
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const simpleFuncWrap = (validator: PropertyValidatorComponentSync) => () => {
|
|
279
|
+
return validator
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const CONFIG_TO_VALIDATE_METHOD: MethodConfigDict = {
|
|
283
|
+
required: _boolChoice(simpleFuncWrap(isRequired)),
|
|
284
|
+
isInteger: _boolChoice(simpleFuncWrap(isInteger)),
|
|
285
|
+
isNumber: _boolChoice(simpleFuncWrap(isNumber)),
|
|
286
|
+
isString: _boolChoice(simpleFuncWrap(isString)),
|
|
287
|
+
isArray: _boolChoice(simpleFuncWrap(isArray)),
|
|
288
|
+
isBoolean: _boolChoice(simpleFuncWrap(isBoolean)),
|
|
289
|
+
choices: _boolChoice(choices),
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const createPropertyValidator = (
|
|
293
|
+
valueGetter: ValueGetter,
|
|
294
|
+
config: PropertyConfig
|
|
295
|
+
): PropertyValidator => {
|
|
296
|
+
const _propertyValidator: PropertyValidator = async (
|
|
297
|
+
instance,
|
|
298
|
+
instanceData: FunctionalModel
|
|
299
|
+
) => {
|
|
300
|
+
if (!config) {
|
|
301
|
+
config = {}
|
|
302
|
+
}
|
|
303
|
+
const validators: readonly PropertyValidatorComponent[] = [
|
|
304
|
+
...Object.entries(config).map(([key, value]) => {
|
|
305
|
+
const method = CONFIG_TO_VALIDATE_METHOD[key]
|
|
306
|
+
if (method) {
|
|
307
|
+
return method(value)
|
|
308
|
+
}
|
|
309
|
+
return emptyValidator
|
|
310
|
+
}),
|
|
311
|
+
...(config.validators ? config.validators : []),
|
|
312
|
+
].filter(x => x)
|
|
313
|
+
const value = await valueGetter()
|
|
314
|
+
const isRequiredValue = config.required
|
|
315
|
+
? true
|
|
316
|
+
: validators.includes(isRequired)
|
|
317
|
+
if (!value && !isRequiredValue) {
|
|
318
|
+
return []
|
|
319
|
+
}
|
|
320
|
+
const validator = aggregateValidator(value, validators)
|
|
321
|
+
const errors = await validator(instance, instanceData)
|
|
322
|
+
return [...new Set(flatMap(errors))]
|
|
323
|
+
}
|
|
324
|
+
return _propertyValidator
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const createModelValidator = (
|
|
328
|
+
validators: PropertyValidators,
|
|
329
|
+
modelValidators?: readonly ModelComponentValidator[]
|
|
330
|
+
) => {
|
|
331
|
+
const _modelValidator = async (
|
|
332
|
+
instance: ModelInstance<any>,
|
|
333
|
+
options: object
|
|
334
|
+
): Promise<ModelErrors> => {
|
|
335
|
+
return Promise.resolve().then(async () => {
|
|
336
|
+
if (!instance) {
|
|
337
|
+
throw new Error(`Instance cannot be empty`)
|
|
338
|
+
}
|
|
339
|
+
const keysAndFunctions = Object.entries(validators)
|
|
340
|
+
const instanceData = await instance.toObj()
|
|
341
|
+
const propertyValidationErrors = await Promise.all(
|
|
342
|
+
keysAndFunctions.map(async ([key, validator]) => {
|
|
343
|
+
return [key, await validator(instance, instanceData)]
|
|
344
|
+
})
|
|
345
|
+
)
|
|
346
|
+
const modelValidationErrors = (
|
|
347
|
+
await Promise.all(
|
|
348
|
+
modelValidators
|
|
349
|
+
? modelValidators.map(validator =>
|
|
350
|
+
validator(instance, instanceData, options)
|
|
351
|
+
)
|
|
352
|
+
: []
|
|
353
|
+
)
|
|
354
|
+
).filter(x => x)
|
|
355
|
+
const propertyErrors = propertyValidationErrors
|
|
356
|
+
.filter(([, errors]) => Boolean(errors) && errors.length > 0)
|
|
357
|
+
.reduce((acc, [key, errors]) => {
|
|
358
|
+
return merge(acc, { [String(key)]: errors })
|
|
359
|
+
}, {})
|
|
360
|
+
return modelValidationErrors.length > 0
|
|
361
|
+
? merge(propertyErrors, { overall: modelValidationErrors })
|
|
362
|
+
: propertyErrors
|
|
363
|
+
})
|
|
364
|
+
}
|
|
365
|
+
return _modelValidator
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export {
|
|
369
|
+
isNumber,
|
|
370
|
+
isBoolean,
|
|
371
|
+
isString,
|
|
372
|
+
isInteger,
|
|
373
|
+
isType,
|
|
374
|
+
isDate,
|
|
375
|
+
isArray,
|
|
376
|
+
isRequired,
|
|
377
|
+
maxNumber,
|
|
378
|
+
minNumber,
|
|
379
|
+
choices,
|
|
380
|
+
maxTextLength,
|
|
381
|
+
minTextLength,
|
|
382
|
+
meetsRegex,
|
|
383
|
+
aggregateValidator,
|
|
384
|
+
emptyValidator,
|
|
385
|
+
createPropertyValidator,
|
|
386
|
+
createModelValidator,
|
|
387
|
+
arrayType,
|
|
388
|
+
referenceTypeMatch,
|
|
389
|
+
TYPE_PRIMITIVES,
|
|
390
|
+
}
|
|
@@ -1,72 +1,90 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { assert } from 'chai'
|
|
2
|
+
import flatMap from 'lodash/flatMap'
|
|
3
|
+
import { Given, When, Then } from '@cucumber/cucumber'
|
|
4
|
+
import {
|
|
5
|
+
BaseModel,
|
|
6
6
|
UniqueId,
|
|
7
7
|
TextProperty,
|
|
8
|
-
Function,
|
|
9
8
|
Property,
|
|
9
|
+
WrapperModelMethod,
|
|
10
|
+
WrapperInstanceMethod,
|
|
10
11
|
ArrayProperty,
|
|
11
12
|
validation,
|
|
12
|
-
}
|
|
13
|
+
} from '../src'
|
|
14
|
+
import { ModelInstanceMethod, ModelMethod } from '../src/interfaces'
|
|
13
15
|
|
|
14
|
-
const instanceToString =
|
|
16
|
+
const instanceToString = WrapperInstanceMethod(modelInstance => {
|
|
15
17
|
return `${modelInstance.getModel().getName()}-Instance`
|
|
16
18
|
})
|
|
17
19
|
|
|
18
|
-
const instanceToJson =
|
|
19
|
-
return JSON.stringify(await modelInstance.
|
|
20
|
+
const instanceToJson = WrapperInstanceMethod(async modelInstance => {
|
|
21
|
+
return JSON.stringify(await modelInstance.toObj())
|
|
20
22
|
})
|
|
21
23
|
|
|
22
|
-
const modelToString =
|
|
23
|
-
return `${model.getName()}-[${Object.keys(
|
|
24
|
+
const modelToString = WrapperModelMethod(model => {
|
|
25
|
+
return `${model.getName()}-[${Object.keys(
|
|
26
|
+
model.getModelDefinition().properties
|
|
27
|
+
).join(',')}]`
|
|
24
28
|
})
|
|
25
29
|
|
|
26
|
-
const modelWrapper =
|
|
30
|
+
const modelWrapper = WrapperModelMethod(model => {
|
|
27
31
|
return model
|
|
28
32
|
})
|
|
29
33
|
|
|
30
34
|
const MODEL_DEFINITIONS = {
|
|
31
|
-
FunctionModel1:
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
FunctionModel1: BaseModel<{
|
|
36
|
+
name: string
|
|
37
|
+
modelWrapper: ModelMethod
|
|
38
|
+
toString: ModelInstanceMethod
|
|
39
|
+
toJson: ModelInstanceMethod
|
|
40
|
+
}>('FunctionModel1', {
|
|
41
|
+
properties: {
|
|
34
42
|
id: UniqueId({ required: true }),
|
|
35
43
|
name: TextProperty({ required: true }),
|
|
36
44
|
},
|
|
45
|
+
modelMethods: {
|
|
46
|
+
modelWrapper,
|
|
47
|
+
},
|
|
48
|
+
instanceMethods: {
|
|
49
|
+
toString: instanceToString,
|
|
50
|
+
toJson: instanceToJson,
|
|
51
|
+
},
|
|
52
|
+
}),
|
|
53
|
+
TestModel1: BaseModel<{ name: string; type: string; flag: number }>(
|
|
54
|
+
'TestModel1',
|
|
37
55
|
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
instanceFunctions: {
|
|
43
|
-
toString: instanceToString,
|
|
44
|
-
toJson: instanceToJson,
|
|
56
|
+
properties: {
|
|
57
|
+
name: Property('Text', { required: true }),
|
|
58
|
+
type: Property('Type', { required: true, isString: true }),
|
|
59
|
+
flag: Property('Flag', { required: true, isNumber: true }),
|
|
45
60
|
},
|
|
46
61
|
}
|
|
47
62
|
),
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
isArray: true,
|
|
56
|
-
validators: [validation.arrayType(validation.TYPE_PRIMATIVES.integer)],
|
|
57
|
-
}),
|
|
63
|
+
ArrayModel1: BaseModel<{ ArrayProperty: readonly number[] }>('ArrayModel1', {
|
|
64
|
+
properties: {
|
|
65
|
+
ArrayProperty: Property('Array', {
|
|
66
|
+
isArray: true,
|
|
67
|
+
validators: [validation.arrayType(validation.TYPE_PRIMITIVES.integer)],
|
|
68
|
+
}),
|
|
69
|
+
},
|
|
58
70
|
}),
|
|
59
|
-
ArrayModel2:
|
|
60
|
-
|
|
71
|
+
ArrayModel2: BaseModel('ArrayModel2', {
|
|
72
|
+
properties: {
|
|
73
|
+
ArrayProperty: Property('Array', { isArray: true }),
|
|
74
|
+
},
|
|
61
75
|
}),
|
|
62
|
-
ArrayModel3:
|
|
63
|
-
|
|
76
|
+
ArrayModel3: BaseModel('ArrayModel3', {
|
|
77
|
+
properties: {
|
|
78
|
+
ArrayProperty: ArrayProperty({}),
|
|
79
|
+
},
|
|
64
80
|
}),
|
|
65
|
-
ArrayModel4:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
81
|
+
ArrayModel4: BaseModel('ArrayModel4', {
|
|
82
|
+
properties: {
|
|
83
|
+
ArrayProperty: ArrayProperty({
|
|
84
|
+
choices: [4, 5, 6],
|
|
85
|
+
validators: [validation.arrayType(validation.TYPE_PRIMITIVES.integer)],
|
|
86
|
+
}),
|
|
87
|
+
},
|
|
70
88
|
}),
|
|
71
89
|
}
|
|
72
90
|
|
|
@@ -106,15 +124,17 @@ const MODEL_INPUT_VALUES = {
|
|
|
106
124
|
}
|
|
107
125
|
|
|
108
126
|
const EXPECTED_FIELDS = {
|
|
109
|
-
TestModel1b: ['
|
|
127
|
+
TestModel1b: ['name', 'type', 'flag'],
|
|
110
128
|
}
|
|
111
129
|
|
|
112
130
|
Given(
|
|
113
131
|
'the {word} has been created, with {word} inputs provided',
|
|
114
132
|
function (modelDefinition, modelInputValues) {
|
|
133
|
+
// @ts-ignore
|
|
115
134
|
const def = MODEL_DEFINITIONS[modelDefinition]
|
|
116
135
|
this.model = def
|
|
117
136
|
|
|
137
|
+
// @ts-ignore
|
|
118
138
|
const input = MODEL_INPUT_VALUES[modelInputValues]
|
|
119
139
|
if (!def) {
|
|
120
140
|
throw new Error(`${modelDefinition} did not result in a definition`)
|
|
@@ -127,7 +147,8 @@ Given(
|
|
|
127
147
|
)
|
|
128
148
|
|
|
129
149
|
When('functions.validate is called', function () {
|
|
130
|
-
|
|
150
|
+
// @ts-ignore
|
|
151
|
+
return this.instance.validate().then(x => {
|
|
131
152
|
this.errors = x
|
|
132
153
|
})
|
|
133
154
|
})
|
|
@@ -141,6 +162,7 @@ Then('an array of {int} errors is shown', function (errorCount) {
|
|
|
141
162
|
})
|
|
142
163
|
|
|
143
164
|
Given('{word} model is used', function (modelDefinition) {
|
|
165
|
+
// @ts-ignore
|
|
144
166
|
const def = MODEL_DEFINITIONS[modelDefinition]
|
|
145
167
|
if (!def) {
|
|
146
168
|
throw new Error(`${modelDefinition} did not result in a definition`)
|
|
@@ -150,6 +172,7 @@ Given('{word} model is used', function (modelDefinition) {
|
|
|
150
172
|
})
|
|
151
173
|
|
|
152
174
|
When('{word} data is inserted', function (modelInputValues) {
|
|
175
|
+
// @ts-ignore
|
|
153
176
|
const input = MODEL_INPUT_VALUES[modelInputValues]
|
|
154
177
|
if (!input) {
|
|
155
178
|
throw new Error(`${modelInputValues} did not result in an input`)
|
|
@@ -158,36 +181,36 @@ When('{word} data is inserted', function (modelInputValues) {
|
|
|
158
181
|
})
|
|
159
182
|
|
|
160
183
|
Then('{word} expected property is found', function (properties) {
|
|
184
|
+
// @ts-ignore
|
|
161
185
|
const propertyArray = EXPECTED_FIELDS[properties]
|
|
162
186
|
if (!propertyArray) {
|
|
163
187
|
throw new Error(`${properties} did not result in properties`)
|
|
164
188
|
}
|
|
189
|
+
// @ts-ignore
|
|
165
190
|
propertyArray.forEach(key => {
|
|
166
|
-
if (!(key in this.instance)) {
|
|
191
|
+
if (!(key in this.instance.get)) {
|
|
167
192
|
throw new Error(`Did not find ${key} in model`)
|
|
168
193
|
}
|
|
169
194
|
})
|
|
170
195
|
})
|
|
171
196
|
|
|
172
197
|
Then('the {word} property is called on the model', function (property) {
|
|
173
|
-
|
|
174
|
-
this.results = result
|
|
175
|
-
})
|
|
198
|
+
this.results = this.instance.get[property]()
|
|
176
199
|
})
|
|
177
200
|
|
|
178
|
-
Then('the array values match', function (table) {
|
|
201
|
+
Then('the array values match', async function (table) {
|
|
179
202
|
const expected = JSON.parse(table.rowsHash().array)
|
|
180
|
-
assert.deepEqual(this.results, expected)
|
|
203
|
+
assert.deepEqual(await this.results, expected)
|
|
181
204
|
})
|
|
182
205
|
|
|
183
206
|
Then('{word} property is found', function (propertyKey) {
|
|
184
|
-
assert.isFunction(this.instance[propertyKey])
|
|
207
|
+
assert.isFunction(this.instance.get[propertyKey])
|
|
185
208
|
})
|
|
186
209
|
|
|
187
210
|
Then('{word} instance function is found', function (instanceFunctionKey) {
|
|
188
|
-
assert.isFunction(this.instance.
|
|
211
|
+
assert.isFunction(this.instance.methods[instanceFunctionKey])
|
|
189
212
|
})
|
|
190
213
|
|
|
191
214
|
Then('{word} model function is found', function (modelFunctionKey) {
|
|
192
|
-
assert.isFunction(this.model[modelFunctionKey])
|
|
215
|
+
assert.isFunction(this.model.methods[modelFunctionKey])
|
|
193
216
|
})
|