pict-section-recordset 1.0.57 → 1.0.59

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.
Files changed (46) hide show
  1. package/docs/.nojekyll +0 -0
  2. package/docs/README.md +76 -0
  3. package/docs/_sidebar.md +19 -0
  4. package/docs/api-reference.md +233 -0
  5. package/docs/cover.md +11 -0
  6. package/docs/filters.md +151 -0
  7. package/docs/index.html +51 -0
  8. package/docs/record-providers.md +155 -0
  9. package/docs/views/create/README.md +181 -0
  10. package/docs/views/dashboard/README.md +308 -0
  11. package/docs/views/list/README.md +260 -0
  12. package/docs/views/read/README.md +216 -0
  13. package/example_applications/README.md +25 -1
  14. package/package.json +11 -10
  15. package/source/application/Pict-Application-RecordSet.js +6 -0
  16. package/source/providers/Filter-Data-Provider.js +611 -187
  17. package/source/views/Filter-PersistenceView.js +534 -0
  18. package/source/views/RecordSet-Filters.js +141 -28
  19. package/test/PictSectionRecordSet-Basic_tests.js +39 -16
  20. package/test/PictSectionRecordSet-Filter-Data-Provider_tests.js +263 -0
  21. package/test/PictSectionRecordSet-RecordProvider-Meadow_tests.js +5 -3
  22. package/types/application/Pict-Application-RecordSet.d.ts.map +1 -1
  23. package/types/providers/Filter-Data-Provider.d.ts +225 -52
  24. package/types/providers/Filter-Data-Provider.d.ts.map +1 -1
  25. package/types/views/Filter-PersistenceView.d.ts +104 -0
  26. package/types/views/Filter-PersistenceView.d.ts.map +1 -0
  27. package/types/views/RecordSet-Filters.d.ts +43 -10
  28. package/types/views/RecordSet-Filters.d.ts.map +1 -1
  29. package/types/views/filters/RecordSet-Filter-Base-Range.d.ts.map +1 -1
  30. package/types/views/filters/RecordSet-Filter-Base.d.ts.map +1 -1
  31. package/types/views/filters/RecordSet-Filter-DateMatch.d.ts.map +1 -1
  32. package/types/views/filters/RecordSet-Filter-DateRange.d.ts.map +1 -1
  33. package/types/views/filters/RecordSet-Filter-ExternalJoinDateMatch.d.ts.map +1 -1
  34. package/types/views/filters/RecordSet-Filter-ExternalJoinDateRange.d.ts.map +1 -1
  35. package/types/views/filters/RecordSet-Filter-ExternalJoinNumericMatch.d.ts.map +1 -1
  36. package/types/views/filters/RecordSet-Filter-ExternalJoinNumericRange.d.ts.map +1 -1
  37. package/types/views/filters/RecordSet-Filter-ExternalJoinSelectedValue.d.ts.map +1 -1
  38. package/types/views/filters/RecordSet-Filter-ExternalJoinSelectedValueList.d.ts.map +1 -1
  39. package/types/views/filters/RecordSet-Filter-InternalJoinDateMatch.d.ts.map +1 -1
  40. package/types/views/filters/RecordSet-Filter-InternalJoinDateRange.d.ts.map +1 -1
  41. package/types/views/filters/RecordSet-Filter-InternalJoinNumericMatch.d.ts.map +1 -1
  42. package/types/views/filters/RecordSet-Filter-InternalJoinNumericRange.d.ts.map +1 -1
  43. package/types/views/filters/RecordSet-Filter-InternalJoinSelectedValue.d.ts.map +1 -1
  44. package/types/views/filters/RecordSet-Filter-InternalJoinSelectedValueList.d.ts.map +1 -1
  45. package/types/views/filters/RecordSet-Filter-NumericMatch.d.ts.map +1 -1
  46. package/types/views/filters/RecordSet-Filter-NumericRange.d.ts.map +1 -1
@@ -38,6 +38,7 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
38
38
  </div>
39
39
  {~T:PRSP-SUBSET-Filters-Template-Button-Fieldset~}
40
40
  {~T:PRSP-SUBSET-Filters-Template-AddFilter-Fieldset~}
41
+ {~T:PRSP-SUBSET-Filters-Template-ManageFilters-Fieldset~}
41
42
  </form>
42
43
  <!-- DefaultPackage end view template: [PRSP-SUBSET-Filters-Template] -->
43
44
  `
@@ -58,10 +59,22 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
58
59
  Template: /*html*/`
59
60
  <!-- DefaultPackage pict view template: [PRSP-SUBSET-Filters-Template-Button-Fieldset] -->
60
61
  <fieldset>
61
- <button type="button" id="PRSP_Filter_Button_Reset" onclick="_Pict.views['PRSP-Filters'].handleReset(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}')">Reset</button>
62
+ <button type="button" id="PRSP_Filter_Button_Clear" title="Clear all filters to a blank state" onclick="_Pict.views['PRSP-Filters'].handleClear(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}')">Clear</button>
63
+ <button type="button" id="PRSP_Filter_Button_Reset" title="Reset all filters to the last saved/defaulted state" onclick="_Pict.views['PRSP-Filters'].handleReset(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}')">Reset</button>
62
64
  <button type="submit" id="PRSP_Filter_Button_Apply">Apply</button>
63
65
  </fieldset>
64
66
  <!-- DefaultPackage end view template: [PRSP-SUBSET-Filters-Template-Button-Fieldset] -->
67
+ `
68
+ },
69
+ {
70
+ Hash: 'PRSP-SUBSET-Filters-Template-ManageFilters-Fieldset',
71
+ Template: /*html*/`
72
+ <!-- DefaultPackage pict view template: [PRSP-SUBSET-Filters-Template-ManageFilters-Fieldset] -->
73
+ <fieldset>
74
+ <button type="button" id="PRSP_Filter_Button_Manage" title="Manage saved filter experiences" onclick="_Pict.views['PRSP-Filters'].handleManage(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}')">Manage Filter Experience</button>
75
+ <div id="FilterPersistenceView-Container"></div>
76
+ </fieldset>
77
+ <!-- DefaultPackage end view template: [PRSP-SUBSET-Filters-Template-ManageFilters-Fieldset] -->
65
78
  `
66
79
  },
67
80
  {
@@ -69,7 +82,7 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
69
82
  Template: /*html*/`
70
83
  <!-- DefaultPackage pict view template: [PRSP-SUBSET-Filters-Template-AddFilter-Fieldset] -->
71
84
  <fieldset>
72
- <button type="button" id="PRSP_Filter_Button_Add" onclick="_Pict.views['PRSP-Filters'].selectFilterToAdd(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}')">+</button>
85
+ <button type="button" id="PRSP_Filter_Button_Add" title="Add a new filter clause" onclick="_Pict.views['PRSP-Filters'].selectFilterToAdd(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}')">+</button>
73
86
  <div id="PRSP-SUBSET-Filters-Template-AddFilter-Dropdown"></div>
74
87
  </fieldset>
75
88
  <!-- DefaultPackage end view template: [PRSP-SUBSET-Filters-Template-AddFilter-Fieldset] -->
@@ -220,6 +233,43 @@ class ViewRecordSetSUBSETFilters extends libPictView
220
233
  {
221
234
  this.lookup[this.chars.charCodeAt(i)] = i;
222
235
  }
236
+
237
+ this.addFilterCallback = null;
238
+ this.removeFilterCallback = null;
239
+ }
240
+
241
+ /**
242
+ * Sets a callback function to be executed after a filter is added.
243
+ * @param {function} pCallback - The callback function to be executed after a filter is added.
244
+ */
245
+ setAddFilterCallback(pCallback)
246
+ {
247
+ this.addFilterCallback = pCallback;
248
+ }
249
+
250
+ /**
251
+ * Sets a callback function to be executed after a filter is removed.
252
+ * @param {function} pCallback - The callback function to be executed after a filter is removed.
253
+ */
254
+ setRemoveFilterCallback(pCallback)
255
+ {
256
+ this.removeFilterCallback = pCallback;
257
+ }
258
+
259
+ /**
260
+ * Removes the callback function for adding a filter.
261
+ */
262
+ removeAddFilterCallback()
263
+ {
264
+ this.addFilterCallback = null;
265
+ }
266
+
267
+ /**
268
+ * Removes the callback function for removing a filter.
269
+ */
270
+ removeRemoveFilterCallback()
271
+ {
272
+ this.removeFilterCallback = null;
223
273
  }
224
274
 
225
275
  /**
@@ -246,7 +296,6 @@ class ViewRecordSetSUBSETFilters extends libPictView
246
296
 
247
297
  /**
248
298
  * Marshals data from the view to the model, usually AppData (or configured data store).
249
- *
250
299
  * @returns {any} The result of the superclass's onMarshalFromView method.
251
300
  */
252
301
  onMarshalFromView()
@@ -264,7 +313,6 @@ class ViewRecordSetSUBSETFilters extends libPictView
264
313
 
265
314
  /**
266
315
  * Marshals the data to the view from the model, usually AppData (or configured data store).
267
- *
268
316
  * @returns {any} The result of the super.onMarshalToView() method.
269
317
  */
270
318
  onMarshalToView()
@@ -344,33 +392,66 @@ class ViewRecordSetSUBSETFilters extends libPictView
344
392
  }
345
393
  //FIXME: this doesn't force a re-render if other filters have changes, but aren't in the URL - so we either need to put them in the URL, or force a re-render based on the filter states
346
394
  tmpPictRouter.router.navigate(tmpURL);
395
+ // always store the last used filter experience search, even if they don't save it
396
+ this.pict.providers.FilterDataProvider.setLastUsedFilterExperience(null, pRecordSet, pViewContext);
347
397
  });
398
+ // new search means this can't be out of date anymore
399
+ this.pict.providers.FilterDataProvider.filterExperienceModifiedFromURLHash = false;
348
400
  }
349
401
 
350
402
  /**
403
+ * Clear all filter clauses for the given record set and view context.
351
404
  * @param {Event} pEvent - The DOM event that triggered the search
352
405
  * @param {string} pRecordSet - The record set being filtered
353
406
  * @param {string} pViewContext - The view context for the filter (ex. List, Dashboard)
354
407
  */
355
- handleReset(pEvent, pRecordSet, pViewContext)
408
+ handleClear(pEvent, pRecordSet, pViewContext)
356
409
  {
357
- pEvent.preventDefault();
410
+ if (pEvent) pEvent.preventDefault();
358
411
  this.pict.ContentAssignment.assignContent('input[name="filter"]', '');
359
- const tmpFilterExperienceClauses = this.pict.Bundle._ActiveFilterState[pRecordSet]?.FilterClauses;
360
- if (Array.isArray(tmpFilterExperienceClauses))
412
+ this.pict.Bundle._ActiveFilterState[pRecordSet].FilterClauses = [];
413
+ this.pict.providers.FilterDataProvider.removeDefaultFilterExperience(pRecordSet, pViewContext);
414
+ this.performSearch(pRecordSet, pViewContext, '');
415
+ }
416
+
417
+ // NOTE: Reset means to default state, Clear means to no filters at all
418
+ /**
419
+ * Reset the filters to default state or fallback to to clear everything if no default exist for the given record set and view context.
420
+ * @param {Event} pEvent - The DOM event that triggered the search
421
+ * @param {string} pRecordSet - The record set being filtered
422
+ * @param {string} pViewContext - The view context for the filter (ex. List, Dashboard)
423
+ */
424
+ handleReset(pEvent, pRecordSet, pViewContext)
425
+ {
426
+ if (pEvent) pEvent.preventDefault();
427
+ this.pict.providers.FilterDataProvider.removeLastUsedFilterExperience(pRecordSet, pViewContext);
428
+ this.pict.log.info(`Clearing filters for record set: ${pRecordSet} in view context: ${pViewContext}`);
429
+ // Apply default filter experience if it exists, otherwise just clear
430
+ const tmpDefaultFilterExperience = this.pict.providers.FilterDataProvider.getDefaultFilterExperience(pRecordSet, pViewContext);
431
+ if (tmpDefaultFilterExperience)
432
+ {
433
+ this.pict.log.info(`Applying default filter experience for record set: ${pRecordSet} in view context: ${pViewContext}`);
434
+ this.pict.providers.FilterDataProvider.applyExpectedFilterExperience(pRecordSet, pViewContext, tmpDefaultFilterExperience.FilterExperienceHash, false);
435
+ this.pict.providers.FilterDataProvider.filterExperienceModifiedFromURLHash = false;
436
+ }
437
+ else
361
438
  {
362
- for (const tmpClause of tmpFilterExperienceClauses)
363
- {
364
- delete tmpClause.Value;
365
- delete tmpClause.Values;
366
- delete tmpClause.SearchInputValue;
367
- delete tmpClause.SelectedValues;
368
- delete tmpClause.SearchResults;
369
- delete tmpClause.SearchResultsOffset;
370
- delete tmpClause.LoadMoreEnabled;
371
- }
439
+ this.handleClear(pEvent, pRecordSet, pViewContext);
372
440
  }
373
- this.performSearch(pRecordSet, pViewContext);
441
+ }
442
+
443
+ /**
444
+ * @param {Event} pEvent - The DOM event that triggered the search
445
+ * @param {string} pRecordSet - The record set being filtered
446
+ * @param {string} pViewContext - The view context for the filter (ex. List, Dashboard)
447
+ * @returns {boolean} - Always returns false to prevent default action
448
+ */
449
+ handleManage(pEvent, pRecordSet, pViewContext)
450
+ {
451
+ if (pEvent) pEvent.preventDefault();
452
+ this.pict.log.info(`Managing filters for record set: ${pRecordSet} in view context: ${pViewContext}`);
453
+ this.pict.views.FilterPersistenceView.openFilterPersistenceUI(pRecordSet, pViewContext);
454
+ return false;
374
455
  }
375
456
 
376
457
  /**
@@ -380,30 +461,50 @@ class ViewRecordSetSUBSETFilters extends libPictView
380
461
  */
381
462
  selectFilterToAdd(pEvent, pRecordSet, pViewContext)
382
463
  {
383
- pEvent.preventDefault();
464
+ if (pEvent) pEvent.preventDefault();
384
465
  //const tmpRecordsetProvider = this.pict.providers['RSP-Provider-' + pRecordSet];
385
466
  //this.pict.log.info(`Selecting filter to add for record set: ${pRecordSet} in view context: ${pViewContext}`, tmpRecordsetProvider.getFilterSchema())
386
467
  this.renderWithScope(this.pict.providers[`RSP-Provider-${pRecordSet}`], 'PRSP-SUBSET-Filters-Template-AddFilter-Dropdown', undefined, { RecordSet: pRecordSet, ViewContext: pViewContext });
387
468
  }
388
469
 
470
+ /**
471
+ * @param {Event} pEvent - The DOM event that triggered the search
472
+ * @param {string} pRecordSet - The record set being filtered
473
+ * @param {string} pViewContext - The view context for the filter (ex. List, Dashboard)
474
+ * @param {string} pFilterKey - The key of the filter to add
475
+ * @param {string} pClauseKey - The key of the clause to add
476
+ */
389
477
  addFilter(pEvent, pRecordSet, pViewContext, pFilterKey, pClauseKey)
390
478
  {
391
- pEvent?.preventDefault();
479
+ if (pEvent) pEvent.preventDefault();
392
480
  this.pict.log.info(`Adding filter: ${pFilterKey} with clause: ${pClauseKey} to record set: ${pRecordSet} in view context: ${pViewContext}`);
393
481
  this.pict.providers[`RSP-Provider-${pRecordSet}`].addFilterClause(pFilterKey, pClauseKey);
394
482
  //FIXME: we need the record from the original render here but no longer have it...
395
483
  this.render(undefined, undefined, { RecordSet: pRecordSet, ViewContext: pViewContext });
484
+ if (this.addFilterCallback) this.addFilterCallback();
396
485
  }
397
486
 
487
+ /**
488
+ * @param {Event} pEvent - The DOM event that triggered the search
489
+ * @param {string} pRecordSet - The record set being filtered
490
+ * @param {string} pViewContext - The view context for the filter (ex. List, Dashboard)
491
+ * @param {string} pSpecificFilterKey - The key of the specific filter to remove
492
+ */
398
493
  removeFilter(pEvent, pRecordSet, pViewContext, pSpecificFilterKey)
399
494
  {
400
- pEvent?.preventDefault();
495
+ if (pEvent) pEvent.preventDefault();
401
496
  this.pict.log.info(`Removing filter: ${pSpecificFilterKey} from record set: ${pRecordSet} in view context: ${pViewContext}`);
402
497
  this.pict.providers[`RSP-Provider-${pRecordSet}`].removeFilterClause(pSpecificFilterKey);
403
498
  //FIXME: we need the record from the original render here but no longer have it...
404
499
  this.render(undefined, undefined, { RecordSet: pRecordSet, ViewContext: pViewContext });
500
+ if (this.removeFilterCallback) this.removeFilterCallback();
405
501
  }
406
502
 
503
+ /**
504
+ * Gets the filter schema for the given record set.
505
+ * @param {string} pRecordSet - The record set to get the filter schema for
506
+ * @return {Array<any>} - The filter schema for the given record set
507
+ */
407
508
  getFilterSchema(pRecordSet)
408
509
  {
409
510
  const tmpRecordsetProvider = this.pict.providers['RSP-Provider-' + pRecordSet];
@@ -412,7 +513,6 @@ class ViewRecordSetSUBSETFilters extends libPictView
412
513
 
413
514
  /**
414
515
  * Lifecycle hook that triggers after the view is rendered.
415
- *
416
516
  * @param {import('pict-view').Renderable} pRenderable - The renderable that was rendered.
417
517
  */
418
518
  onAfterRender(pRenderable)
@@ -426,7 +526,7 @@ class ViewRecordSetSUBSETFilters extends libPictView
426
526
  const tmpSelect = document.getElementById('PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-Select');
427
527
  if (tmpSelect)
428
528
  {
429
- const tmpActiveOption = document.getElementById('PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-Select')?.querySelector('option:checked')
529
+ const tmpActiveOption = document.getElementById('PRSP-SUBSET-Filters-Template-AddFilter-Dropdown-Select')?.querySelector('option:checked');
430
530
  const tmpRecordSet = tmpActiveOption?.getAttribute('data-i-recordset');
431
531
  const tmpFilterKey = tmpActiveOption?.getAttribute('data-i-filter-key');
432
532
  const tmpViewContext = tmpSelect?.getAttribute('data-i-view-context');
@@ -447,9 +547,24 @@ class ViewRecordSetSUBSETFilters extends libPictView
447
547
  }
448
548
  }
449
549
  this.onMarshalToView();
550
+
551
+ // NOTE: This is where we ensure the filter experience is applied after a render.
552
+ const tmpRouteUrl = this.pict.providers.PictRouter?.router?.current[0]?.url || this.pict.providers.PictRouter?.router?.current[0]?.hashString;
553
+ const tmpRecordSet = tmpRouteUrl?.split?.('PSRS/')?.[1]?.split?.('/')?.[0];
554
+ const tmpViewContext = tmpRouteUrl?.split?.('PSRS/')?.[1]?.split?.('/')?.[1];
555
+ if (tmpRecordSet && tmpViewContext)
556
+ {
557
+ this.pict.providers.FilterDataProvider.applyExpectedFilterExperience(tmpRecordSet, tmpViewContext);
558
+ }
559
+
450
560
  return res;
451
561
  }
452
562
 
563
+ /**
564
+ * Encodes the filter experience to a string.
565
+ * @param {Record<string, any>} pExperience - The filter experience to serialize.
566
+ * @return {Promise<string>} - The serialized filter experience as a string.
567
+ */
453
568
  async serializeFilterExperience(pExperience)
454
569
  {
455
570
  if (!pExperience || typeof pExperience !== 'object')
@@ -468,8 +583,8 @@ class ViewRecordSetSUBSETFilters extends libPictView
468
583
  }
469
584
 
470
585
  /**
586
+ * Decodes the filter experience from a string.
471
587
  * @param {string} pExperience - The serialized filter experience as a string.
472
- *
473
588
  * @return {Promise<Record<string, any>>} - The serialized filter experience as a string.
474
589
  */
475
590
  async deserializeFilterExperience(pExperience)
@@ -485,7 +600,6 @@ class ViewRecordSetSUBSETFilters extends libPictView
485
600
  /**
486
601
  * @param {string} string - The string to compress.
487
602
  * @param {CompressionFormat} [encoding='gzip'] - The encoding to use for compression, defaults to 'gzip'.
488
- *
489
603
  * @return {Promise<ArrayBuffer>} - The compressed byte array.
490
604
  */
491
605
  async compress(string, encoding = 'gzip')
@@ -506,6 +620,7 @@ class ViewRecordSetSUBSETFilters extends libPictView
506
620
  {
507
621
  const cs = new DecompressionStream(encoding);
508
622
  const writer = cs.writable.getWriter();
623
+ // @ts-ignore
509
624
  writer.write(byteArray);
510
625
  writer.close();
511
626
  return new Response(cs.readable).arrayBuffer().then((arrayBuffer) =>
@@ -516,7 +631,6 @@ class ViewRecordSetSUBSETFilters extends libPictView
516
631
 
517
632
  /**
518
633
  * @param {ArrayBuffer} arraybuffer - The ArrayBuffer to encode to Base64.
519
- *
520
634
  * @return {string} - The Base64 encoded string.
521
635
  */
522
636
  encode(arraybuffer)
@@ -548,7 +662,6 @@ class ViewRecordSetSUBSETFilters extends libPictView
548
662
 
549
663
  /**
550
664
  * @param {string} base64 - The Base64 encoded string to decode to an ArrayBuffer.
551
- *
552
665
  * @return {ArrayBuffer} - The decoded ArrayBuffer.
553
666
  */
554
667
  decode(base64)
@@ -1,14 +1,13 @@
1
1
  /*
2
2
  Unit tests for PictSectionRecordSet Basic
3
-
3
+
4
4
  */
5
5
 
6
- // This is temporary, but enables unit tests
7
6
  const libBrowserEnv = require('browser-env');
8
- libBrowserEnv();
9
7
 
10
8
  const libPictView = require('pict-view');
11
9
 
10
+ const sinon = require('sinon');
12
11
  const Chai = require('chai');
13
12
  const Expect = Chai.expect;
14
13
 
@@ -54,7 +53,29 @@ suite
54
53
  'PictSectionRecordSet Basic',
55
54
  () =>
56
55
  {
57
- setup(() => { });
56
+ let originalLocalStorage;
57
+
58
+ setup(() =>
59
+ {
60
+ libBrowserEnv({
61
+ url: "http://localhost/",
62
+ });
63
+ originalLocalStorage = localStorage;
64
+ // @ts-ignore
65
+ localStorage = {
66
+ getItem: sinon.stub(),
67
+ setItem: sinon.stub(),
68
+ removeItem: sinon.stub(),
69
+ };
70
+ });
71
+
72
+ teardown(() =>
73
+ {
74
+ sinon.restore();
75
+ // @ts-ignore
76
+ delete localStorage;
77
+ localStorage = originalLocalStorage;
78
+ });
58
79
 
59
80
  suite
60
81
  (
@@ -67,10 +88,13 @@ suite
67
88
  {
68
89
  let _Pict = new libPict();
69
90
  _Pict.LogNoisiness = 1;
70
- let _PictEnvironment = new libPict.EnvironmentObject(_Pict);
91
+ //let _PictEnvironment = new libPict.EnvironmentObject(_Pict);
92
+ localStorage = originalLocalStorage;
71
93
 
94
+ // Define view configuration
72
95
  let _Application = new DoNothingApplication(_Pict, {});
73
96
 
97
+
74
98
  Expect(_Application).to.be.an('object', 'Application should be an object.');
75
99
  Expect(_Application).to.be.an.instanceof(libPictSectionRecordSet.PictRecordSetApplication, 'Application should be an instance of PictRecordSetApplication.');
76
100
 
@@ -78,20 +102,19 @@ suite
78
102
 
79
103
  _Application.initialize();
80
104
 
105
+ let _PictSectionRecordSet = _Pict.addView(tmpViewHash, tmpViewConfiguration, libPictSectionRecordSet);
81
106
 
82
- // let _PictSectionRecordSet = _Pict.addView(tmpViewHash, tmpViewConfiguration, libPictSectionRecordSet);
83
-
84
- // Expect(_PictSectionRecordSet).to.be.an('object');
107
+ Expect(_PictSectionRecordSet).to.be.an('object');
85
108
 
86
- // // Test package anthropology
87
- // Expect(_PictSectionRecordSet._PackageFableServiceProvider).to.be.an('object', 'Fable should have a _PackageFableServiceProvider object.');
88
- // Expect(_PictSectionRecordSet._PackageFableServiceProvider.name).equal('fable-serviceproviderbase', 'Fable _PackageFableServiceProvider.package.name should be set.');
89
- // Expect(_PictSectionRecordSet._PackagePictView).to.be.an('object', 'Should have a _PackagePictView object.');
90
- // Expect(_PictSectionRecordSet._PackagePictView.name).to.equal('pict-view', '_PackagePictView.package.name should be set.');
91
- // Expect(_PictSectionRecordSet._Package).to.be.an('object', 'Should have a _Package object.');
92
- // Expect(_PictSectionRecordSet._Package.name).to.equal('pict-section-recordset', '_Package.package.name should be set.');
109
+ // Test package anthropology
110
+ Expect(_PictSectionRecordSet._PackageFableServiceProvider).to.be.an('object', 'Fable should have a _PackageFableServiceProvider object.');
111
+ Expect(_PictSectionRecordSet._PackageFableServiceProvider.name).equal('fable-serviceproviderbase', 'Fable _PackageFableServiceProvider.package.name should be set.');
112
+ Expect(_PictSectionRecordSet._PackagePictView).to.be.an('object', 'Should have a _PackagePictView object.');
113
+ Expect(_PictSectionRecordSet._PackagePictView.name).to.equal('pict-view', '_PackagePictView.package.name should be set.');
114
+ Expect(_PictSectionRecordSet._Package).to.be.an('object', 'Should have a _Package object.');
115
+ Expect(_PictSectionRecordSet._Package.name).to.equal('pict-section-recordset', '_Package.package.name should be set.');
93
116
 
94
- // return fDone();
117
+ return fDone();
95
118
  }
96
119
  );
97
120
  });
@@ -0,0 +1,263 @@
1
+ /*
2
+ Unit tests for PictSectionRecordSet Basic
3
+
4
+ */
5
+
6
+ const libBrowserEnv = require('browser-env');
7
+
8
+ const libPictView = require('pict-view');
9
+
10
+ const sinon = require('sinon');
11
+ const Chai = require('chai');
12
+ const Expect = Chai.expect;
13
+
14
+ const libPict = require('pict');
15
+
16
+ const libPictSectionRecordSet = require('../source/Pict-Section-RecordSet.js');
17
+ const libPictSectionRecordSetFilterDataProvider = require('../source/providers/Filter-Data-Provider.js');
18
+
19
+ class DoNothingApplication extends libPictSectionRecordSet.PictRecordSetApplication
20
+ {
21
+ constructor(pFable, pOptions, pServiceHash)
22
+ {
23
+ super(pFable, pOptions, pServiceHash);
24
+
25
+ this.pict.addView('DoNothingView', {}, DoNothingView);
26
+ this.pict.addProvider('FilterDataProvider', libPictSectionRecordSetFilterDataProvider);
27
+ }
28
+
29
+ /**
30
+ * @param {function} fDone - Callback that finishes the test
31
+ */
32
+ set testDone(fDone)
33
+ {
34
+ this._testDone = fDone;
35
+ }
36
+
37
+ onAfterInitialize()
38
+ {
39
+ this.solve();
40
+ this._testDone();
41
+ return super.onAfterInitialize();
42
+ }
43
+ }
44
+
45
+ class DoNothingView extends libPictView
46
+ {
47
+ constructor(pPict, pOptions)
48
+ {
49
+ super(pPict, pOptions);
50
+ }
51
+ }
52
+
53
+ suite
54
+ (
55
+ 'PictSectionRecordSet Filter Data Provider Basic Tests',
56
+ () =>
57
+ {
58
+ let originalLocalStorage;
59
+ let _Pict = new libPict();
60
+ _Pict.LogNoisiness = 1;
61
+ //let _PictEnvironment = new libPict.EnvironmentObject(_Pict);
62
+ localStorage = originalLocalStorage;
63
+
64
+ setup(() =>
65
+ {
66
+ libBrowserEnv({
67
+ url: "http://localhost/",
68
+ });
69
+ originalLocalStorage = localStorage;
70
+ // @ts-ignore
71
+ localStorage = {
72
+ getItem: sinon.stub(),
73
+ setItem: sinon.stub(),
74
+ removeItem: sinon.stub(),
75
+ };
76
+ });
77
+
78
+ teardown(() =>
79
+ {
80
+ sinon.restore();
81
+ // @ts-ignore
82
+ delete localStorage;
83
+ localStorage = originalLocalStorage;
84
+ });
85
+
86
+ suite
87
+ (
88
+ 'Filter Data Provider Basic Tests',
89
+ () =>
90
+ {
91
+ test(
92
+ 'Basic Initialization',
93
+ (fDone) =>
94
+ {
95
+ // Define view configuration
96
+ let _Application = new DoNothingApplication(_Pict, {});
97
+ let _DataFilterProvider = _Application.pict.providers.FilterDataProvider;
98
+
99
+ _DataFilterProvider.storageProvider = localStorage
100
+ let _StorageProvider = _DataFilterProvider.storageProvider;
101
+
102
+ Expect(_DataFilterProvider).to.be.an('object', 'Filter Data Provider should be an object.');
103
+ Expect(_StorageProvider).to.be.an.instanceof(originalLocalStorage.constructor, 'Storage Provider should be an instance of localStorage.');
104
+
105
+ Expect(_Application).to.be.an('object', 'Application should be an object.');
106
+ Expect(_Application).to.be.an.instanceof(libPictSectionRecordSet.PictRecordSetApplication, 'Application should be an instance of PictRecordSetApplication.');
107
+
108
+ _Application.testDone = fDone;
109
+
110
+ _Application.initialize();
111
+
112
+ }
113
+ );
114
+ });
115
+
116
+ suite
117
+ (
118
+ 'Filter Data Provider Apply Expected Filter Experience Tests',
119
+ () =>
120
+ {
121
+ test(
122
+ 'Apply Expected Filter Experience with no parameters',
123
+ (fDone) =>
124
+ {
125
+ // Define view configuration
126
+ let _Application = new DoNothingApplication(_Pict, {});
127
+ let _DataFilterProvider = _Application.pict.providers.FilterDataProvider;
128
+
129
+ _DataFilterProvider.storageProvider = localStorage
130
+ let _StorageProvider = _DataFilterProvider.storageProvider;
131
+
132
+ Expect(_DataFilterProvider).to.be.an('object', 'Filter Data Provider should be an object.');
133
+ Expect(_StorageProvider).to.be.an.instanceof(originalLocalStorage.constructor, 'Storage Provider should be an instance of localStorage.');
134
+
135
+ Expect(_Application).to.be.an('object', 'Application should be an object.');
136
+ Expect(_Application).to.be.an.instanceof(libPictSectionRecordSet.PictRecordSetApplication, 'Application should be an instance of PictRecordSetApplication.');
137
+
138
+ _Application.testDone = fDone;
139
+
140
+ _Application.initialize();
141
+
142
+ // Call applyExpectedFilterExperience with no parameters
143
+ let result = _DataFilterProvider.applyExpectedFilterExperience();
144
+
145
+ // TODO: Need better way to test this... Last used and default filter experiences are not accounted for in this test context.
146
+ // For now, just check that the method runs and returns true (should be false if nothing is set in storage).
147
+ Expect(result).to.be.true;
148
+
149
+ }
150
+ );
151
+
152
+ test(
153
+ 'Apply Expected Filter Experience with parameters',
154
+ (fDone) =>
155
+ {
156
+ // Define view configuration
157
+ let _Application = new DoNothingApplication(_Pict, {});
158
+ let _DataFilterProvider = _Application.pict.providers.FilterDataProvider;
159
+
160
+ _DataFilterProvider.storageProvider = localStorage
161
+ let _StorageProvider = _DataFilterProvider.storageProvider;
162
+
163
+ Expect(_DataFilterProvider).to.be.an('object', 'Filter Data Provider should be an object.');
164
+ Expect(_StorageProvider).to.be.an.instanceof(originalLocalStorage.constructor, 'Storage Provider should be an instance of localStorage.');
165
+
166
+ Expect(_Application).to.be.an('object', 'Application should be an object.');
167
+ Expect(_Application).to.be.an.instanceof(libPictSectionRecordSet.PictRecordSetApplication, 'Application should be an instance of PictRecordSetApplication.');
168
+
169
+ _Application.testDone = fDone;
170
+
171
+ _Application.initialize();
172
+
173
+ // Call applyExpectedFilterExperience with parameters
174
+ let result = _DataFilterProvider.applyExpectedFilterExperience('TestRecordSet', 'TestViewContext');
175
+
176
+ Expect(result).to.be.true;
177
+
178
+ }
179
+ );
180
+
181
+ test(
182
+ 'Modify localStorage to simulate last used filter experience and test Apply Expected Filter Experience',
183
+ (fDone) =>
184
+ {
185
+ // Define view configuration
186
+ let _Application = new DoNothingApplication(_Pict, {});
187
+ let _DataFilterProvider = _Application.pict.providers.FilterDataProvider;
188
+
189
+ _DataFilterProvider.storageProvider = localStorage
190
+ let _StorageProvider = _DataFilterProvider.storageProvider;
191
+
192
+ Expect(_DataFilterProvider).to.be.an('object', 'Filter Data Provider should be an object.');
193
+ Expect(_StorageProvider).to.be.an.instanceof(originalLocalStorage.constructor, 'Storage Provider should be an instance of localStorage.');
194
+
195
+ Expect(_Application).to.be.an('object', 'Application should be an object.');
196
+ Expect(_Application).to.be.an.instanceof(libPictSectionRecordSet.PictRecordSetApplication, 'Application should be an instance of PictRecordSetApplication.');
197
+
198
+ _Application.testDone = fDone;
199
+
200
+ _Application.initialize();
201
+
202
+ // Simulate last used filter experience in localStorage
203
+ const testRecordSet = 'TestRecordSet';
204
+ const testViewContext = 'TestViewContext';
205
+ const testFilterExperienceHash = 'LastUsedHash123';
206
+
207
+ const lastUsedKey = `Filter_MetaTest_${testRecordSet}_${testViewContext}_${test}`;
208
+ localStorage.getItem.withArgs(lastUsedKey).returns(JSON.stringify({
209
+ FilterExperienceHash: testFilterExperienceHash
210
+ }));
211
+
212
+ // Call applyExpectedFilterExperience with parameters
213
+ let result = _DataFilterProvider.applyExpectedFilterExperience(testRecordSet, testViewContext, testFilterExperienceHash);
214
+
215
+ Expect(result).to.be.true;
216
+ Expect(localStorage.setItem.calledWith(lastUsedKey, sinon.match.string)).to.be.true;
217
+
218
+ }
219
+ );
220
+
221
+ test(
222
+ 'Modify localStorage to simuate creating settings for the default filter experience and test Apply Expected Filter Experience',
223
+ (fDone) =>
224
+ {
225
+ // Define view configuration
226
+ let _Application = new DoNothingApplication(_Pict, {});
227
+ let _DataFilterProvider = _Application.pict.providers.FilterDataProvider;
228
+
229
+ _DataFilterProvider.storageProvider = localStorage
230
+ let _StorageProvider = _DataFilterProvider.storageProvider;
231
+
232
+ Expect(_DataFilterProvider).to.be.an('object', 'Filter Data Provider should be an object.');
233
+ Expect(_StorageProvider).to.be.an.instanceof(originalLocalStorage.constructor, 'Storage Provider should be an instance of localStorage.');
234
+
235
+ Expect(_Application).to.be.an('object', 'Application should be an object.');
236
+ Expect(_Application).to.be.an.instanceof(libPictSectionRecordSet.PictRecordSetApplication, 'Application should be an instance of PictRecordSetApplication.');
237
+
238
+ _Application.testDone = fDone;
239
+
240
+ _Application.initialize();
241
+
242
+ // Simulate default filter experience settings in localStorage
243
+ const testRecordSet = 'TestRecordSet';
244
+ const testViewContext = 'TestViewContext';
245
+ const testFilterExperienceHash = 'DefaultHash123';
246
+
247
+ const defaultKey = `Filter_MetaTest_${testRecordSet}_${testViewContext}_SETTINGS`;
248
+ localStorage.getItem.withArgs(defaultKey).returns(JSON.stringify({
249
+ LastUsedFilterExperienceHash: testFilterExperienceHash
250
+ }));
251
+ // Call applyExpectedFilterExperience with parameters
252
+ let result = _DataFilterProvider.applyExpectedFilterExperience(testRecordSet, testViewContext, testFilterExperienceHash);
253
+
254
+ Expect(result).to.be.true;
255
+ Expect(localStorage.setItem.calledWith(defaultKey, sinon.match.string)).to.be.true;
256
+
257
+ }
258
+ );
259
+ }
260
+
261
+ );
262
+ }
263
+ );