pict-section-recordset 1.1.0 → 1.2.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/package.json +5 -1
- package/source/views/RecordSet-Filters.js +20 -2
- package/source/views/filters/RecordSet-Filter-Base.js +86 -8
- package/source/views/read/RecordSet-Read.js +308 -2
- package/types/providers/Filter-Data-Provider.d.ts +1 -1
- package/types/providers/Filter-Data-Provider.d.ts.map +1 -1
- package/types/views/Filter-PersistenceView.d.ts +23 -2
- package/types/views/Filter-PersistenceView.d.ts.map +1 -1
- package/types/views/RecordSet-Filters.d.ts +26 -1
- package/types/views/RecordSet-Filters.d.ts.map +1 -1
- package/types/views/filters/RecordSet-Filter-Base.d.ts +14 -0
- package/types/views/filters/RecordSet-Filter-Base.d.ts.map +1 -1
- package/types/views/list/RecordSet-List.d.ts.map +1 -1
- package/types/views/read/RecordSet-Read.d.ts +51 -0
- package/types/views/read/RecordSet-Read.d.ts.map +1 -1
- package/.vscode/launch.json +0 -46
- package/CONTRIBUTING.md +0 -50
- package/debug/Harness.js +0 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +0 -76
- package/docs/_brand.json +0 -18
- package/docs/_cover.md +0 -11
- package/docs/_sidebar.md +0 -19
- package/docs/_version.json +0 -7
- package/docs/api-reference.md +0 -233
- package/docs/filters.md +0 -151
- package/docs/index.html +0 -38
- package/docs/record-providers.md +0 -155
- package/docs/retold-catalog.json +0 -87
- package/docs/retold-keyword-index.json +0 -5227
- package/docs/views/create/README.md +0 -181
- package/docs/views/dashboard/README.md +0 -308
- package/docs/views/list/README.md +0 -260
- package/docs/views/read/README.md +0 -216
- package/eslint.config.mjs +0 -10
- package/example_applications/README.md +0 -39
- package/example_applications/ServeExamples.js +0 -82
- package/example_applications/bookstore/.quackage.json +0 -9
- package/example_applications/bookstore/Bookstore-Application-Configuration.json +0 -4
- package/example_applications/bookstore/Bookstore-Application.js +0 -671
- package/example_applications/bookstore/css/bookstore.css +0 -729
- package/example_applications/bookstore/css/pure.min.css +0 -11
- package/example_applications/bookstore/html/index.html +0 -46
- package/example_applications/bookstore/package.json +0 -34
- package/example_applications/bookstore/providers/PictRouter-Bookstore.json +0 -32
- package/example_applications/bookstore/views/PictView-Bookstore-Content-About.json +0 -21
- package/example_applications/bookstore/views/PictView-Bookstore-Content-Legal.json +0 -21
- package/example_applications/bookstore/views/PictView-Bookstore-Dashboard.js +0 -147
- package/example_applications/bookstore/views/PictView-Bookstore-Layout.js +0 -85
- package/example_applications/bookstore/views/PictView-Bookstore-Login.js +0 -58
- package/example_applications/bookstore/views/PictView-Bookstore-Navigation.js +0 -228
- package/example_applications/index.html +0 -50
- package/example_applications/mocks/book-edit-view.html +0 -173
- package/example_applications/mocks/book-read-view.html +0 -166
- package/example_applications/mocks/list-view.html +0 -185
- package/example_applications/package.json +0 -16
- package/example_applications/simple_entity/.quackage.json +0 -9
- package/example_applications/simple_entity/README-Simple-RecordSet.md +0 -8
- package/example_applications/simple_entity/Simple-RecordSet-Application.js +0 -887
- package/example_applications/simple_entity/html/index.html +0 -207
- package/example_applications/simple_entity/package.json +0 -27
- package/test/PictSectionRecordSet-Basic_tests.js +0 -205
- package/test/PictSectionRecordSet-Filter-Data-Provider_tests.js +0 -263
- package/test/PictSectionRecordSet-Filter-InstanceViews-Render_tests.js +0 -328
- package/test/PictSectionRecordSet-RecordProvider-Meadow_tests.js +0 -216
- package/tsconfig.build.json +0 -16
- package/tsconfig.json +0 -16
|
@@ -1,263 +0,0 @@
|
|
|
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
|
-
);
|
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Unit tests for Pict-Template-FilterInstanceViews.renderAsync.
|
|
3
|
-
|
|
4
|
-
Exercises the parallel filter fan-out, per-filter transaction isolation,
|
|
5
|
-
deferred post-render drain, render-epoch guard, and transaction-map cleanup
|
|
6
|
-
added for the filter render performance refactor.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const libBrowserEnv = require('browser-env');
|
|
10
|
-
libBrowserEnv({ url: 'http://localhost/' });
|
|
11
|
-
|
|
12
|
-
const Chai = require('chai');
|
|
13
|
-
const Expect = Chai.expect;
|
|
14
|
-
|
|
15
|
-
const libPict = require('pict');
|
|
16
|
-
const libPictView = require('pict-view');
|
|
17
|
-
const libPictTemplateFilterInstanceViews = require('../source/templates/Pict-Template-FilterInstanceViews.js');
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Minimal stub filter view that records every interaction and lets each test
|
|
21
|
-
* control when the render and drain callbacks fire. The stub never touches
|
|
22
|
-
* the DOM and never spawns sub-renders - everything is recorded so the tests
|
|
23
|
-
* can inspect ordering.
|
|
24
|
-
*/
|
|
25
|
-
class StubFilterView extends libPictView
|
|
26
|
-
{
|
|
27
|
-
constructor(pFable, pOptions, pServiceHash)
|
|
28
|
-
{
|
|
29
|
-
super(pFable, pOptions, pServiceHash);
|
|
30
|
-
this.recordedCalls = [];
|
|
31
|
-
this.pendingRenderCallbacks = [];
|
|
32
|
-
this.pendingDrainCallbacks = [];
|
|
33
|
-
this._fakeOutput = pOptions && pOptions.fakeOutput ? pOptions.fakeOutput : '<stub/>';
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
prepareRecord(pRecord)
|
|
37
|
-
{
|
|
38
|
-
this.recordedCalls.push({ type: 'prepareRecord', hash: pRecord && pRecord.Hash });
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
renderWithScopeAsync(pScope, pRenderableHash, pDestinationAddress, pRecord, pRootRenderable, fCallback)
|
|
42
|
-
{
|
|
43
|
-
this.recordedCalls.push({
|
|
44
|
-
type: 'renderWithScopeAsync',
|
|
45
|
-
rootRenderable: pRootRenderable,
|
|
46
|
-
destinationAddress: pDestinationAddress,
|
|
47
|
-
clauseHash: pRecord && pRecord.Hash,
|
|
48
|
-
});
|
|
49
|
-
// Simulate a real render writing into the pict template output cache.
|
|
50
|
-
const tmpCacheKey = pDestinationAddress.split('.')[1];
|
|
51
|
-
const tmpIndex = this.pendingRenderCallbacks.length;
|
|
52
|
-
this.pict.__TemplateOutputCache[tmpCacheKey] = `${this._fakeOutput}-${tmpIndex}`;
|
|
53
|
-
// Defer the callback until the test fires it manually.
|
|
54
|
-
this.pendingRenderCallbacks.push(() => fCallback());
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
onAfterRenderAsync(fCallback, pRenderable)
|
|
58
|
-
{
|
|
59
|
-
this.recordedCalls.push({
|
|
60
|
-
type: 'onAfterRenderAsync',
|
|
61
|
-
rootRenderable: pRenderable,
|
|
62
|
-
});
|
|
63
|
-
// Defer the drain callback until the test fires it manually.
|
|
64
|
-
this.pendingDrainCallbacks.push(() => fCallback());
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
fireAllRenderCallbacks()
|
|
68
|
-
{
|
|
69
|
-
while (this.pendingRenderCallbacks.length > 0)
|
|
70
|
-
{
|
|
71
|
-
this.pendingRenderCallbacks.shift()();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
fireAllDrainCallbacks()
|
|
76
|
-
{
|
|
77
|
-
while (this.pendingDrainCallbacks.length > 0)
|
|
78
|
-
{
|
|
79
|
-
this.pendingDrainCallbacks.shift()();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Stand up a pict instance with just enough wiring for the FilterInstanceViews
|
|
86
|
-
* template to run. Returns the pict instance, the template instance, and the
|
|
87
|
-
* stub filter view.
|
|
88
|
-
*
|
|
89
|
-
* @param {Array<object>} pFilterClauses - Filter clauses to populate into Bundle._ActiveFilterState.
|
|
90
|
-
*/
|
|
91
|
-
function buildHarness(pFilterClauses)
|
|
92
|
-
{
|
|
93
|
-
const _Pict = new libPict();
|
|
94
|
-
_Pict.LogNoisiness = 0;
|
|
95
|
-
|
|
96
|
-
// FilterInstanceViews.renderAsync expects pict.PictSectionRecordSet.recordSetProviderConfigurations
|
|
97
|
-
// to resolve the record set. Stub the minimum shape.
|
|
98
|
-
_Pict.PictSectionRecordSet = {
|
|
99
|
-
recordSetProviderConfigurations: {
|
|
100
|
-
TestRS: { RecordSet: 'TestRS' },
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
// Register a stub filter view so _getViewForFilterClause returns it for
|
|
105
|
-
// clauses of type 'StubFilterType'. Doing this BEFORE adding the
|
|
106
|
-
// 'PRSP-Filters' fake entry below, because addView creates a real view
|
|
107
|
-
// in the pict.views map and we need the hash to match what
|
|
108
|
-
// _getViewForFilterClause looks up.
|
|
109
|
-
_Pict.addView('PRSP-FilterType-StubFilterType', {}, StubFilterView);
|
|
110
|
-
|
|
111
|
-
// FilterInstanceViews expects pict.views['PRSP-Filters'] to have a
|
|
112
|
-
// _renderEpoch field so the deferred drain can snapshot + compare it.
|
|
113
|
-
// Direct assignment on the views map is fine for a stub.
|
|
114
|
-
_Pict.views['PRSP-Filters'] = { _renderEpoch: 0 };
|
|
115
|
-
|
|
116
|
-
// Populate the active filter state that renderAsync reads.
|
|
117
|
-
_Pict.Bundle._ActiveFilterState = {
|
|
118
|
-
TestRS: { FilterClauses: pFilterClauses },
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
// Register the template instance we want to test and capture it.
|
|
122
|
-
const tmpInstruction = _Pict.addTemplate(libPictTemplateFilterInstanceViews);
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
pict: _Pict,
|
|
126
|
-
instruction: tmpInstruction,
|
|
127
|
-
stubView: _Pict.views['PRSP-FilterType-StubFilterType'],
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
suite('PictSectionRecordSet FilterInstanceViews Render', () =>
|
|
132
|
-
{
|
|
133
|
-
suite('renderAsync', () =>
|
|
134
|
-
{
|
|
135
|
-
test('is a no-op when there are zero clauses', (fDone) =>
|
|
136
|
-
{
|
|
137
|
-
const tmpHarness = buildHarness([]);
|
|
138
|
-
tmpHarness.instruction.renderAsync('', { RecordSet: 'TestRS' }, (pError, pResult) =>
|
|
139
|
-
{
|
|
140
|
-
Expect(pError).to.be.null;
|
|
141
|
-
Expect(pResult).to.equal('');
|
|
142
|
-
Expect(tmpHarness.stubView.recordedCalls).to.deep.equal([]);
|
|
143
|
-
return fDone();
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test('fans out all filter renders in parallel before any callback fires', (fDone) =>
|
|
148
|
-
{
|
|
149
|
-
const tmpClauses = [
|
|
150
|
-
{ Type: 'StubFilterType', Hash: 'clauseA', FilterByColumn: 'A' },
|
|
151
|
-
{ Type: 'StubFilterType', Hash: 'clauseB', FilterByColumn: 'B' },
|
|
152
|
-
{ Type: 'StubFilterType', Hash: 'clauseC', FilterByColumn: 'C' },
|
|
153
|
-
];
|
|
154
|
-
const tmpHarness = buildHarness(tmpClauses);
|
|
155
|
-
|
|
156
|
-
let tmpFinalCallbackFired = false;
|
|
157
|
-
tmpHarness.instruction.renderAsync('', { RecordSet: 'TestRS' }, () =>
|
|
158
|
-
{
|
|
159
|
-
tmpFinalCallbackFired = true;
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// At this point every filter should have had renderWithScopeAsync
|
|
163
|
-
// called, even though zero render callbacks have fired yet. A
|
|
164
|
-
// sequential version would have only called renderWithScopeAsync
|
|
165
|
-
// on the first filter at this point.
|
|
166
|
-
const tmpRenderStarts = tmpHarness.stubView.recordedCalls.filter((c) => c.type === 'renderWithScopeAsync');
|
|
167
|
-
Expect(tmpRenderStarts.length).to.equal(3, 'all three filter renders should have started in parallel');
|
|
168
|
-
Expect(tmpFinalCallbackFired).to.equal(false, 'final callback must not fire until every render completes');
|
|
169
|
-
|
|
170
|
-
// Fire all render callbacks, then finalize runs synchronously.
|
|
171
|
-
tmpHarness.stubView.fireAllRenderCallbacks();
|
|
172
|
-
Expect(tmpFinalCallbackFired).to.equal(true, 'final callback should fire once every render completes');
|
|
173
|
-
return fDone();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test('concatenates per-filter output in clause order', (fDone) =>
|
|
177
|
-
{
|
|
178
|
-
const tmpClauses = [
|
|
179
|
-
{ Type: 'StubFilterType', Hash: 'clauseA', FilterByColumn: 'A' },
|
|
180
|
-
{ Type: 'StubFilterType', Hash: 'clauseB', FilterByColumn: 'B' },
|
|
181
|
-
{ Type: 'StubFilterType', Hash: 'clauseC', FilterByColumn: 'C' },
|
|
182
|
-
];
|
|
183
|
-
const tmpHarness = buildHarness(tmpClauses);
|
|
184
|
-
|
|
185
|
-
tmpHarness.instruction.renderAsync('', { RecordSet: 'TestRS' }, (pError, pResult) =>
|
|
186
|
-
{
|
|
187
|
-
Expect(pError).to.be.null;
|
|
188
|
-
// Stub output is `<stub/>-N` where N is the render start index.
|
|
189
|
-
// Since all three are started in order, the outputs are 0, 1, 2.
|
|
190
|
-
Expect(pResult).to.equal('<stub/>-0<stub/>-1<stub/>-2');
|
|
191
|
-
return fDone();
|
|
192
|
-
});
|
|
193
|
-
tmpHarness.stubView.fireAllRenderCallbacks();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
test('each filter render gets its own transaction hash (not the dashboard root)', (fDone) =>
|
|
197
|
-
{
|
|
198
|
-
const tmpClauses = [
|
|
199
|
-
{ Type: 'StubFilterType', Hash: 'clauseA', FilterByColumn: 'A' },
|
|
200
|
-
{ Type: 'StubFilterType', Hash: 'clauseB', FilterByColumn: 'B' },
|
|
201
|
-
];
|
|
202
|
-
const tmpHarness = buildHarness(tmpClauses);
|
|
203
|
-
|
|
204
|
-
// Pass a fake dashboard root as pState.RootRenderable. The template
|
|
205
|
-
// must NOT propagate this transaction hash into the per-filter
|
|
206
|
-
// renders - each should have its own.
|
|
207
|
-
const tmpFakeDashboardRoot =
|
|
208
|
-
{
|
|
209
|
-
TransactionHash: 'DashboardTx-shouldNotLeak',
|
|
210
|
-
RootRenderableViewHash: 'PRSP-Dashboard-Stub',
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
tmpHarness.instruction.renderAsync('', { RecordSet: 'TestRS' }, () =>
|
|
214
|
-
{
|
|
215
|
-
const tmpRenderStarts = tmpHarness.stubView.recordedCalls.filter((c) => c.type === 'renderWithScopeAsync');
|
|
216
|
-
const tmpTransactionHashes = tmpRenderStarts.map((c) => c.rootRenderable.TransactionHash);
|
|
217
|
-
|
|
218
|
-
Expect(tmpTransactionHashes).to.have.lengthOf(2);
|
|
219
|
-
Expect(tmpTransactionHashes[0]).to.not.equal('DashboardTx-shouldNotLeak');
|
|
220
|
-
Expect(tmpTransactionHashes[1]).to.not.equal('DashboardTx-shouldNotLeak');
|
|
221
|
-
Expect(tmpTransactionHashes[0]).to.not.equal(tmpTransactionHashes[1]);
|
|
222
|
-
Expect(tmpTransactionHashes[0]).to.match(/^FilterInstance-/);
|
|
223
|
-
Expect(tmpTransactionHashes[1]).to.match(/^FilterInstance-/);
|
|
224
|
-
return fDone();
|
|
225
|
-
}, null, null, { RootRenderable: tmpFakeDashboardRoot });
|
|
226
|
-
|
|
227
|
-
tmpHarness.stubView.fireAllRenderCallbacks();
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
test('fires fCallback BEFORE draining deferred post-render work', (fDone) =>
|
|
231
|
-
{
|
|
232
|
-
const tmpClauses = [
|
|
233
|
-
{ Type: 'StubFilterType', Hash: 'clauseA', FilterByColumn: 'A' },
|
|
234
|
-
{ Type: 'StubFilterType', Hash: 'clauseB', FilterByColumn: 'B' },
|
|
235
|
-
];
|
|
236
|
-
const tmpHarness = buildHarness(tmpClauses);
|
|
237
|
-
|
|
238
|
-
let tmpFinalCallbackFired = false;
|
|
239
|
-
tmpHarness.instruction.renderAsync('', { RecordSet: 'TestRS' }, () =>
|
|
240
|
-
{
|
|
241
|
-
tmpFinalCallbackFired = true;
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// Complete all filter renders synchronously.
|
|
245
|
-
tmpHarness.stubView.fireAllRenderCallbacks();
|
|
246
|
-
Expect(tmpFinalCallbackFired).to.equal(true, 'fCallback should have fired once every render completed');
|
|
247
|
-
|
|
248
|
-
// At this exact moment no drain has run yet: the drain is queued
|
|
249
|
-
// behind a setTimeout(0) macrotask.
|
|
250
|
-
const tmpDrainsBeforeTick = tmpHarness.stubView.recordedCalls.filter((c) => c.type === 'onAfterRenderAsync').length;
|
|
251
|
-
Expect(tmpDrainsBeforeTick).to.equal(0, 'no drain should have run yet - it is scheduled on the next macrotask');
|
|
252
|
-
|
|
253
|
-
// Let the setTimeout(0) fire.
|
|
254
|
-
setTimeout(() =>
|
|
255
|
-
{
|
|
256
|
-
const tmpDrainsAfterTick = tmpHarness.stubView.recordedCalls.filter((c) => c.type === 'onAfterRenderAsync').length;
|
|
257
|
-
Expect(tmpDrainsAfterTick).to.equal(2, 'both filter drains should have run on the next macrotask');
|
|
258
|
-
return fDone();
|
|
259
|
-
}, 5);
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
test('epoch guard: a drain scheduled before a filter re-render is skipped', (fDone) =>
|
|
263
|
-
{
|
|
264
|
-
const tmpClauses = [
|
|
265
|
-
{ Type: 'StubFilterType', Hash: 'clauseA', FilterByColumn: 'A' },
|
|
266
|
-
];
|
|
267
|
-
const tmpHarness = buildHarness(tmpClauses);
|
|
268
|
-
const tmpFiltersView = tmpHarness.pict.views['PRSP-Filters'];
|
|
269
|
-
|
|
270
|
-
tmpHarness.instruction.renderAsync('', { RecordSet: 'TestRS' }, () => {});
|
|
271
|
-
tmpHarness.stubView.fireAllRenderCallbacks();
|
|
272
|
-
|
|
273
|
-
// Simulate a filter mutation that bumps the epoch between the
|
|
274
|
-
// dashboard callback firing and the setTimeout drain running.
|
|
275
|
-
tmpFiltersView._renderEpoch = tmpFiltersView._renderEpoch + 1;
|
|
276
|
-
|
|
277
|
-
setTimeout(() =>
|
|
278
|
-
{
|
|
279
|
-
const tmpDrainCalls = tmpHarness.stubView.recordedCalls.filter((c) => c.type === 'onAfterRenderAsync').length;
|
|
280
|
-
Expect(tmpDrainCalls).to.equal(0, 'drain should have been skipped because the epoch changed');
|
|
281
|
-
return fDone();
|
|
282
|
-
}, 5);
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
test('transaction map entries for filter renders are cleaned up after the drain', (fDone) =>
|
|
286
|
-
{
|
|
287
|
-
const tmpClauses = [
|
|
288
|
-
{ Type: 'StubFilterType', Hash: 'clauseA', FilterByColumn: 'A' },
|
|
289
|
-
{ Type: 'StubFilterType', Hash: 'clauseB', FilterByColumn: 'B' },
|
|
290
|
-
];
|
|
291
|
-
const tmpHarness = buildHarness(tmpClauses);
|
|
292
|
-
|
|
293
|
-
tmpHarness.instruction.renderAsync('', { RecordSet: 'TestRS' }, () => {});
|
|
294
|
-
tmpHarness.stubView.fireAllRenderCallbacks();
|
|
295
|
-
|
|
296
|
-
// Right after fCallback fires, both filter transactions are
|
|
297
|
-
// registered in the map (the drain has not run yet).
|
|
298
|
-
const tmpFilterKeysBefore = Object.keys(tmpHarness.pict.TransactionTracking.transactionMap)
|
|
299
|
-
.filter((k) => k.startsWith('FilterInstance-'));
|
|
300
|
-
Expect(tmpFilterKeysBefore.length).to.equal(2, 'both filter transactions should be registered before the drain runs');
|
|
301
|
-
|
|
302
|
-
setTimeout(() =>
|
|
303
|
-
{
|
|
304
|
-
// The drain kicks off both filters' onAfterRenderAsync calls.
|
|
305
|
-
// Fire their drain completion callbacks so the cleanup runs.
|
|
306
|
-
tmpHarness.stubView.fireAllDrainCallbacks();
|
|
307
|
-
|
|
308
|
-
const tmpFilterKeysAfter = Object.keys(tmpHarness.pict.TransactionTracking.transactionMap)
|
|
309
|
-
.filter((k) => k.startsWith('FilterInstance-'));
|
|
310
|
-
Expect(tmpFilterKeysAfter.length).to.equal(0, 'transaction map should be cleaned up after the drain completes');
|
|
311
|
-
return fDone();
|
|
312
|
-
}, 5);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
test('bails early with empty result when the record set is not configured', (fDone) =>
|
|
316
|
-
{
|
|
317
|
-
const tmpHarness = buildHarness([
|
|
318
|
-
{ Type: 'StubFilterType', Hash: 'clauseA', FilterByColumn: 'A' },
|
|
319
|
-
]);
|
|
320
|
-
tmpHarness.instruction.renderAsync('', { RecordSet: 'NotConfigured' }, (pError, pResult) =>
|
|
321
|
-
{
|
|
322
|
-
Expect(pError).to.be.null;
|
|
323
|
-
Expect(pResult).to.equal('');
|
|
324
|
-
return fDone();
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
});
|
|
328
|
-
});
|