manyfest 1.0.2 → 1.0.3

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.
@@ -3,6 +3,7 @@
3
3
  * @author <steven@velozo.com>
4
4
  */
5
5
  let libSimpleLog = require('./Manyfest-LogToConsole.js');
6
+ let libObjectAddressResolver = require('./Manyfest-ObjectAddressResolver.js');
6
7
 
7
8
  /**
8
9
  * Manyfest object address-based descriptions and manipulations.
@@ -11,15 +12,31 @@ let libSimpleLog = require('./Manyfest-LogToConsole.js');
11
12
  */
12
13
  class Manyfest
13
14
  {
14
- constructor(pManifest, pInfoLog, pErrorLog)
15
+ constructor(pManifest, pInfoLog, pErrorLog, pOptions)
15
16
  {
16
17
  // Wire in logging
17
18
  this.logInfo = (typeof(pInfoLog) === 'function') ? pInfoLog : libSimpleLog;
18
19
  this.logError = (typeof(pErrorLog) === 'function') ? pErrorLog : libSimpleLog;
19
20
 
21
+ // Create an object address resolver and map in the functions
22
+ this.objectAddressResolver = new libObjectAddressResolver(this.logInfo, this.logError);
23
+
20
24
  this.options = (
21
25
  {
22
- strict: false
26
+ strict: false,
27
+ defaultValues:
28
+ {
29
+ "String": "",
30
+ "Number": 0,
31
+ "Float": 0.0,
32
+ "Integer": 0,
33
+ "Boolean": false,
34
+ "Binary": 0,
35
+ "DateTime": 0,
36
+ "Array": [],
37
+ "Object": {},
38
+ "Null": null
39
+ }
23
40
  });
24
41
 
25
42
  this.scope = undefined;
@@ -72,12 +89,12 @@ class Manyfest
72
89
  }
73
90
  else
74
91
  {
75
- this.logError(`(${this.scope}) Error loading scope from manifest; expecting a string but property was type ${typeof(pManifest.Scope)}.`);
92
+ this.logError(`(${this.scope}) Error loading scope from manifest; expecting a string but property was type ${typeof(pManifest.Scope)}.`, pManifest);
76
93
  }
77
94
  }
78
95
  else
79
96
  {
80
- this.logError(`(${this.scope}) Error loading scope from manifest object. Property "Scope" does not exist in the root of the object.`);
97
+ this.logError(`(${this.scope}) Error loading scope from manifest object. Property "Scope" does not exist in the root of the object.`, pManifest);
81
98
  }
82
99
 
83
100
  if (pManifest.hasOwnProperty('Descriptors'))
@@ -92,12 +109,12 @@ class Manyfest
92
109
  }
93
110
  else
94
111
  {
95
- this.logError(`(${this.scope}) Error loading description object from manifest object. Expecting an object in 'Manifest.Descriptors' but the property was type ${typeof(pManifest.Description)}.`);
112
+ this.logError(`(${this.scope}) Error loading description object from manifest object. Expecting an object in 'Manifest.Descriptors' but the property was type ${typeof(pManifest.Descriptors)}.`, pManifest);
96
113
  }
97
114
  }
98
115
  else
99
116
  {
100
- this.logError(`(${this.scope}) Error loading object description from manifest object. Property "Descriptors" does not exist in the root of the object.`);
117
+ this.logError(`(${this.scope}) Error loading object description from manifest object. Property "Descriptors" does not exist in the root of the Manifest object.`, pManifest);
101
118
  }
102
119
  }
103
120
 
@@ -175,201 +192,45 @@ class Manyfest
175
192
  /*************************************************************************
176
193
  * Beginning of Object Manipulation (read & write) Functions
177
194
  */
178
- // Get the value of an element by its hash
179
- getValueByHash (pObject, pHash)
195
+ // Check if an element exists by its hash
196
+ checkAddressExistsByHash (pObject, pHash)
180
197
  {
181
198
  if (this.elementHashes.hasOwnProperty(pHash))
182
199
  {
183
- return this.getValueAtAddress(pObject, this.elementHashes[pHash]);
200
+ return this.checkAddressExists(pObject, this.elementHashes[pHash]);
184
201
  }
185
202
  else
186
203
  {
187
- this.logError(`(${this.scope}) Error in getValueByHash; the Hash ${pHash} doesn't exist in the schema.`);
204
+ this.logError(`(${this.scope}) Error in checkAddressExistsByHash; the Hash ${pHash} doesn't exist in the schema.`);
188
205
  return undefined;
189
206
  }
190
207
  }
191
208
 
209
+ // Check if an element exists at an address
210
+ checkAddressExists (pObject, pAddress)
211
+ {
212
+ return this.objectAddressResolver.checkAddressExists(pObject, pAddress);
213
+ }
214
+
192
215
 
193
- cleanWrapCharacters (pCharacter, pString)
216
+ // Get the value of an element by its hash
217
+ getValueByHash (pObject, pHash)
194
218
  {
195
- if (pString.startsWith(pCharacter) && pString.endsWith(pCharacter))
219
+ if (this.elementHashes.hasOwnProperty(pHash))
196
220
  {
197
- return pString.substring(1, pString.length - 1);
221
+ return this.getValueAtAddress(pObject, this.elementHashes[pHash]);
198
222
  }
199
223
  else
200
224
  {
201
- return pString;
225
+ this.logError(`(${this.scope}) Error in getValueByHash; the Hash ${pHash} doesn't exist in the schema.`);
226
+ return undefined;
202
227
  }
203
228
  }
204
229
 
205
230
  // Get the value of an element at an address
206
231
  getValueAtAddress (pObject, pAddress)
207
232
  {
208
- // Make sure pObject is an object
209
- if (!typeof(pObject) === 'object') return undefined;
210
- // Make sure pAddress is a string
211
- if (!typeof(pAddress) === 'string') return undefined;
212
-
213
- // TODO: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
214
- let tmpSeparatorIndex = pAddress.indexOf('.');
215
-
216
- // This is the terminal address string (no more dots so the RECUSION ENDS IN HERE somehow)
217
- if (tmpSeparatorIndex === -1)
218
- {
219
- // Check if it's a boxed property
220
- let tmpBracketStartIndex = pAddress.indexOf('[');
221
- let tmpBracketStopIndex = pAddress.indexOf(']');
222
- // Boxed elements look like this:
223
- // MyValues[10]
224
- // MyValues['Name']
225
- // MyValues["Age"]
226
- // MyValues[`Cost`]
227
- //
228
- // When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
229
- // The requirements to detect a boxed element are:
230
- // 1) The start bracket is after character 0
231
- if ((tmpBracketStartIndex > 0)
232
- // 2) The end bracket has something between them
233
- && (tmpBracketStopIndex > tmpBracketStartIndex)
234
- // 3) There is data
235
- && (tmpBracketStopIndex - tmpBracketStartIndex > 0))
236
- {
237
- // The "Name" of the Object contained too the left of the bracket
238
- let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
239
-
240
- // If the subproperty doesn't test as a proper Object, none of the rest of this is possible.
241
- // This is a rare case where Arrays testing as Objects is useful
242
- if (typeof(pObject[tmpBoxedPropertyName]) !== 'object')
243
- {
244
- return undefined;
245
- }
246
-
247
- // The "Reference" to the property within it, either an array element or object property
248
- let tmpBoxedPropertyReference = pAddress.substring(tmpBracketStartIndex+1, tmpBracketStopIndex).trim();
249
- // Attempt to parse the reference as a number, which will be used as an array element
250
- let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
251
-
252
- // Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
253
- // This seems confusing to me at first read, so explaination:
254
- // Is the Boxed Object an Array? TRUE
255
- // And is the Reference inside the boxed Object not a number? TRUE
256
- // --> So when these are in agreement, it's an impossible access state
257
- if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber))
258
- {
259
- return undefined;
260
- }
261
-
262
- // 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
263
- // otherwise we will try to treat it as a dynamic object property.
264
- if (isNaN(tmpBoxedPropertyNumber))
265
- {
266
- // This isn't a number ... let's treat it as a dynamic object property.
267
- // We would expect the property to be wrapped in some kind of quotes so strip them
268
- tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
269
- tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
270
- tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
271
-
272
- // Return the value in the property
273
- return pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference];
274
- }
275
- else
276
- {
277
- return pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber];
278
- }
279
- }
280
- else
281
- {
282
- // Now is the point in recursion to return the value in the address
283
- return pObject[pAddress];
284
- }
285
- }
286
- else
287
- {
288
- let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
289
- let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
290
-
291
- // Test if the tmpNewAddress is an array or object
292
- // Check if it's a boxed property
293
- let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
294
- let tmpBracketStopIndex = tmpSubObjectName.indexOf(']');
295
- // Boxed elements look like this:
296
- // MyValues[42]
297
- // MyValues['Color']
298
- // MyValues["Weight"]
299
- // MyValues[`Diameter`]
300
- //
301
- // When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
302
- // The requirements to detect a boxed element are:
303
- // 1) The start bracket is after character 0
304
- if ((tmpBracketStartIndex > 0)
305
- // 2) The end bracket has something between them
306
- && (tmpBracketStopIndex > tmpBracketStartIndex)
307
- // 3) There is data
308
- && (tmpBracketStopIndex - tmpBracketStartIndex > 0))
309
- {
310
- let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
311
-
312
- let tmpBoxedPropertyReference = tmpSubObjectName.substring(tmpBracketStartIndex+1, tmpBracketStopIndex).trim();
313
-
314
- let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
315
-
316
- // Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
317
- // This seems confusing to me at first read, so explaination:
318
- // Is the Boxed Object an Array? TRUE
319
- // And is the Reference inside the boxed Object not a number? TRUE
320
- // --> So when these are in agreement, it's an impossible access state
321
- // This could be a failure in the recursion chain because they passed something like this in:
322
- // StudentData.Sections.Algebra.Students[1].Tardy
323
- // BUT
324
- // StudentData.Sections.Algebra.Students[1] is an object, so the .Tardy is not possible to access
325
- // This could be a failure in the recursion chain because they passed something like this in:
326
- // StudentData.Sections.Algebra.Students["JaneDoe"].Grade
327
- // BUT
328
- // StudentData.Sections.Algebra.Students["JaneDoe"] is an array, so the .Grade is not possible to access
329
- // TODO: Should this be an error or something? Should we keep a log of failures like this?
330
- if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber))
331
- {
332
- return undefined;
333
- }
334
-
335
- //This is a bracketed value
336
- // 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
337
- // otherwise we will try to reat it as a dynamic object property.
338
- if (isNaN(tmpBoxedPropertyNumber))
339
- {
340
- // This isn't a number ... let's treat it as a dynanmic object property.
341
- tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
342
- tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
343
- tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
344
-
345
- // Recurse directly into the subobject
346
- return this.getValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress);
347
- }
348
- else
349
- {
350
- // We parsed a valid number out of the boxed property name, so recurse into the array
351
- return this.getValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress);
352
- }
353
- }
354
-
355
- // If there is an object property already named for the sub object, but it isn't an object
356
- // then the system can't set the value in there. Error and abort!
357
- if (pObject.hasOwnProperty(tmpSubObjectName) && typeof(pObject[tmpSubObjectName]) !== 'object')
358
- {
359
- return undefined;
360
- }
361
- else if (pObject.hasOwnProperty(tmpSubObjectName))
362
- {
363
- // If there is already a subobject pass that to the recursive thingy
364
- return this.getValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress);
365
- }
366
- else
367
- {
368
- // Create a subobject and then pass that
369
- pObject[tmpSubObjectName] = {};
370
- return this.getValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress);
371
- }
372
- }
233
+ return this.objectAddressResolver.getValueAtAddress(pObject, pAddress);
373
234
  }
374
235
 
375
236
  // Set the value of an element by its hash
@@ -386,79 +247,13 @@ class Manyfest
386
247
  }
387
248
  }
388
249
 
250
+
389
251
  // Set the value of an element at an address
390
252
  setValueAtAddress (pObject, pAddress, pValue)
391
253
  {
392
- // Make sure pObject is an object
393
- if (!typeof(pObject) === 'object') return false;
394
- // Make sure pAddress is a string
395
- if (!typeof(pAddress) === 'string') return false;
396
-
397
- let tmpSeparatorIndex = pAddress.indexOf('.');
398
-
399
- if (tmpSeparatorIndex === -1)
400
- {
401
- // Now is the time to set the value in the object
402
- pObject[pAddress] = pValue;
403
- return true;
404
- }
405
- else
406
- {
407
- let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
408
- let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
409
-
410
- // If there is an object property already named for the sub object, but it isn't an object
411
- // then the system can't set the value in there. Error and abort!
412
- if (pObject.hasOwnProperty(tmpSubObjectName) && typeof(pObject[tmpSubObjectName]) !== 'object')
413
- {
414
- if (!pObject.hasOwnProperty('__ERROR'))
415
- pObject['__ERROR'] = {};
416
- // Put it in an error object so data isn't lost
417
- pObject['__ERROR'][pAddress] = pValue;
418
- return false;
419
- }
420
- else if (pObject.hasOwnProperty(tmpSubObjectName))
421
- {
422
- // If there is already a subobject pass that to the recursive thingy
423
- return this.setValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, pValue);
424
- }
425
- else
426
- {
427
- // Create a subobject and then pass that
428
- pObject[tmpSubObjectName] = {};
429
- return this.setValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, pValue);
430
- }
431
- }
432
- }
433
-
434
- setValueAtAddressInContainer(pRecordObject, pFormContainerAddress, pFormContainerIndex, pFormValueAddress, pFormValue)
435
- {
436
- // First see if there *is* a container object
437
- let tmpContainerObject = this.getValueAtAddress(pRecordObject, pFormContainerAddress);
438
-
439
- if (typeof(pFormContainerAddress) !== 'string') return false;
440
-
441
- let tmpFormContainerIndex = parseInt(pFormContainerIndex, 10);
442
- if (isNaN(tmpFormContainerIndex)) return false;
443
-
444
- if ((typeof(tmpContainerObject) !== 'object') || (!Array.isArray(tmpContainerObject)))
445
- {
446
- // Check if there is a value here and we want to store it in the "__OverwrittenData" thing
447
- tmpContainerObject = [];
448
- this.setValueAtAddress(pRecordObject, pFormContainerAddress, tmpContainerObject);
449
- }
450
-
451
- for (let i = 0; (tmpContainerObject.length + i) <= (tmpFormContainerIndex+1); i++)
452
- {
453
- // Add objects to this container until it has enough
454
- tmpContainerObject.push({});
455
- }
456
-
457
- // Now set the value *in* the container object
458
- return this.setValueAtAddress(tmpContainerObject[tmpFormContainerIndex], pFormValueAddress, pFormValue);
254
+ return this.objectAddressResolver.setValueAtAddress(pObject, pAddress, pValue);
459
255
  }
460
256
 
461
-
462
257
  // Validate the consistency of an object against the schema
463
258
  validate(pObject)
464
259
  {
@@ -475,7 +270,13 @@ class Manyfest
475
270
  tmpValidationData.Errors.push(`Expected passed in object to be type object but was passed in ${typeof(pObject)}`);
476
271
  }
477
272
 
478
- // Now enumerate through the values and check for anomalies
273
+ let addValidationError = (pAddress, pErrorMessage) =>
274
+ {
275
+ tmpValidationData.Error = true;
276
+ tmpValidationData.Errors.push(`Element at address "${pAddress}" ${pErrorMessage}.`);
277
+ };
278
+
279
+ // Now enumerate through the values and check for anomalies based on the schema
479
280
  for (let i = 0; i < this.elementAddresses.length; i++)
480
281
  {
481
282
  let tmpDescriptor = this.getDescriptor(this.elementAddresses[i]);
@@ -483,18 +284,144 @@ class Manyfest
483
284
 
484
285
  if (typeof(tmpValue) == 'undefined')
485
286
  {
287
+ // This will technically mean that `Object.Some.Value = undefined` will end up showing as "missing"
288
+ // TODO: Do we want to do a different message based on if the property exists but is undefined?
486
289
  tmpValidationData.MissingElements.push(tmpDescriptor.Address);
487
-
488
290
  if (tmpDescriptor.Required || this.options.strict)
489
291
  {
490
- tmpValidationData.Error = true;
491
- tmpValidationData.Errors.push(`Element at address '${tmpDescriptor.Address}' is flagged Required but is not present.`);
292
+ addValidationError(tmpDescriptor.Address, 'is flagged REQUIRED but is not set in the object');
293
+ }
294
+ }
295
+
296
+ // Now see if there is a data type specified for this element
297
+ if (tmpDescriptor.DataType)
298
+ {
299
+ let tmpElementType = typeof(tmpValue);
300
+ switch(tmpDescriptor.DataType.toString().trim().toLowerCase())
301
+ {
302
+ case 'string':
303
+ if (tmpElementType != 'string')
304
+ {
305
+ addValidationError(tmpDescriptor.Address, `has a DataType ${tmpDescriptor.DataType} but is of the type ${tmpElementType}`);
306
+ }
307
+ break;
308
+
309
+ case 'number':
310
+ if (tmpElementType != 'number')
311
+ {
312
+ addValidationError(tmpDescriptor.Address, `has a DataType ${tmpDescriptor.DataType} but is of the type ${tmpElementType}`);
313
+ }
314
+ break;
315
+
316
+ case 'integer':
317
+ if (tmpElementType != 'number')
318
+ {
319
+ addValidationError(tmpDescriptor.Address, `has a DataType ${tmpDescriptor.DataType} but is of the type ${tmpElementType}`);
320
+ }
321
+ else
322
+ {
323
+ let tmpValueString = tmpValue.toString();
324
+ if (tmpValueString.indexOf('.') > -1)
325
+ {
326
+ // TODO: Is this an error?
327
+ addValidationError(tmpDescriptor.Address, `has a DataType ${tmpDescriptor.DataType} but has a decimal point in the number.`);
328
+ }
329
+ }
330
+ break;
331
+
332
+ case 'float':
333
+ if (tmpElementType != 'number')
334
+ {
335
+ addValidationError(tmpDescriptor.Address, `has a DataType ${tmpDescriptor.DataType} but is of the type ${tmpElementType}`);
336
+ }
337
+ break;
338
+
339
+ case 'DateTime':
340
+ let tmpValueDate = new Date(tmpValue);
341
+ if (tmpValueDate.toString() == 'Invalid Date')
342
+ {
343
+ addValidationError(tmpDescriptor.Address, `has a DataType ${tmpDescriptor.DataType} but is not parsable as a Date by Javascript`);
344
+ }
345
+
346
+ default:
347
+ // Check if this is a string, in the default case
348
+ // Note this is only when a DataType is specified and it is an unrecognized data type.
349
+ if (tmpElementType != 'string')
350
+ {
351
+ addValidationError(tmpDescriptor.Address, `has a DataType ${tmpDescriptor.DataType} (which auto-converted to String because it was unrecognized) but is of the type ${tmpElementType}`);
352
+ }
353
+ break;
492
354
  }
493
355
  }
494
356
  }
495
357
 
496
358
  return tmpValidationData;
497
359
  }
360
+
361
+ // Returns a default value, or, the default value for the data type (which is overridable with configuration)
362
+ getDefaultValue(pDescriptor)
363
+ {
364
+ if (pDescriptor.hasOwnProperty('Default'))
365
+ {
366
+ return pDescriptor.Default;
367
+ }
368
+ else
369
+ {
370
+ // Default to a null if it doesn't have a type specified.
371
+ // This will ensure a placeholder is created but isn't misinterpreted.
372
+ let tmpDataType = (pDescriptor.hasOwnProperty('DataType')) ? pDescriptor.DataType : 'String';
373
+ if (this.options.defaultValues.hasOwnProperty(tmpDataType))
374
+ {
375
+ return this.options.defaultValues[tmpDataType];
376
+ }
377
+ else
378
+ {
379
+ // give up and return null
380
+ return null;
381
+ }
382
+ }
383
+ }
384
+
385
+ // Enumerate through the schema and populate default values if they don't exist.
386
+ populateDefaults(pObject, pOverwriteProperties)
387
+ {
388
+ return this.populateObject(pObject, pOverwriteProperties,
389
+ // This just sets up a simple filter to see if there is a default set.
390
+ (pDescriptor) =>
391
+ {
392
+ return pDescriptor.hasOwnProperty('Default');
393
+ });
394
+ }
395
+
396
+ // Forcefully populate all values even if they don't have defaults.
397
+ // Based on type, this can do unexpected things.
398
+ populateObject(pObject, pOverwriteProperties, fFilter)
399
+ {
400
+ // Automatically create an object if one isn't passed in.
401
+ let tmpObject = (typeof(pObject) === 'object') ? pObject : {};
402
+ // Default to *NOT OVERWRITING* properties
403
+ let tmpOverwriteProperties = (typeof(pOverwriteProperties) == 'undefined') ? false : pOverwriteProperties;
404
+ // This is a filter function, which is passed the schema and allows complex filtering of population
405
+ // The default filter function just returns true, populating everything.
406
+ let tmpFilterFunction = (typeof(fFilter) == 'function') ? fFilter : (pDescriptor) => { return true; };
407
+
408
+ this.elementAddresses.forEach(
409
+ (pAddress) =>
410
+ {
411
+ let tmpDescriptor = this.getDescriptor(pAddress);
412
+ // Check the filter function to see if this is an address we want to set the value for.
413
+ if (tmpFilterFunction(tmpDescriptor))
414
+ {
415
+ // If we are overwriting properties OR the property does not exist
416
+ if (tmpOverwriteProperties || !this.checkAddressExists(tmpObject, pAddress))
417
+ {
418
+ this.setValueAtAddress(tmpObject, pAddress, this.getDefaultValue(tmpDescriptor));
419
+ }
420
+ }
421
+ });
422
+
423
+ return tmpObject;
424
+ }
498
425
  };
499
426
 
500
427
  module.exports = Manyfest;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Unit tests for Meadow
3
+ *
4
+ * @license MIT
5
+ *
6
+ * @author Steven Velozo <steven@velozo.com>
7
+ */
8
+
9
+ var Chai = require("chai");
10
+ var Expect = Chai.expect;
11
+
12
+ let libManyfest = require('../source/Manyfest.js');
13
+
14
+ let _SampleDataArchiveOrgFrankenberry = require('./Data-Archive-org-Frankenberry.json');
15
+
16
+ suite
17
+ (
18
+ 'Manyfest Object Check Element Existence',
19
+ function()
20
+ {
21
+ setup (()=> {} );
22
+
23
+ suite
24
+ (
25
+ 'Basic Check Existence',
26
+ ()=>
27
+ {
28
+ test
29
+ (
30
+ 'It should be easy to check if an element exists by address.',
31
+ (fTestComplete)=>
32
+ {
33
+ let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
34
+ // Property not in schema:
35
+ let tmpTitleExists = _Manyfest.checkAddressExists(_SampleDataArchiveOrgFrankenberry, 'metadata.title');
36
+ Expect(tmpTitleExists)
37
+ .to.equal(true);
38
+ fTestComplete();
39
+ }
40
+ );
41
+ test
42
+ (
43
+ 'If an element does not exist, checkAddressExists should return false.',
44
+ (fTestComplete)=>
45
+ {
46
+ let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
47
+ // Property not in schema:
48
+ let tmpTitleExists = _Manyfest.checkAddressExists(_SampleDataArchiveOrgFrankenberry, 'metadata.someStrangeProperty');
49
+ Expect(tmpTitleExists)
50
+ .to.equal(false);
51
+ fTestComplete();
52
+ }
53
+ );
54
+ test
55
+ (
56
+ 'It should be trivial to access subproperties with a schema by hash.',
57
+ (fTestComplete)=>
58
+ {
59
+ let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
60
+ // Property not schema, accessed by hash:
61
+ let tmpCreator = _Manyfest.checkAddressExistsByHash(_SampleDataArchiveOrgFrankenberry, 'Creator');
62
+ Expect(tmpCreator)
63
+ .to.equal(true);
64
+ fTestComplete();
65
+ }
66
+ );
67
+ }
68
+ );
69
+ }
70
+ );