-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest.js
More file actions
223 lines (199 loc) · 10.3 KB
/
test.js
File metadata and controls
223 lines (199 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
const assert = require('assert');
const statecoreLib = require('./statecore.js');
// Test the module factory
const statecoreInstance = statecoreLib.createStatecore({ test: 'test' });
assert.deepEqual(statecoreInstance.statecoreGetState(), { test: 'test' });
// Test the observer
let observerCalled = false;
let observerCalledWithArgs = null;
const observer = function(...args) {
observerCalled = true;
observerCalledWithArgs = args;
}
const removeObserver = statecoreInstance.statecoreAddObserver(observer);
assert.equal(typeof removeObserver, 'function');
statecoreInstance.statecoreSetState({ test: 'test2' });
assert.deepEqual(statecoreInstance.statecoreGetState(), { test: 'test2' });
// The observer should have been called
assert.equal(observerCalled, true);
assert.deepEqual(observerCalledWithArgs, [statecoreLib.STATECORE_EVENT__STATE_CHANGE, { test: 'test2' }, { test: 'test' }]);
// Test the observer throwing an error
observerCalled = false;
observerCalledWithArgs = null;
const observerError = new Error('Test error');
const observerThrowingError = function() {
throw observerError;
}
statecoreInstance.statecoreAddObserver(observerThrowingError);
statecoreInstance.statecoreSetState({ test: 'test3' });
assert.deepEqual(statecoreInstance.statecoreGetState(), { test: 'test3' });
// The 1st observer should have been called
assert.ok(observerCalled);
// The observer throwing an error should not prevent other observers from being called
assert.deepEqual(
observerCalledWithArgs,
[statecoreLib.STATECORE_EVENT__OBSERVER_ERROR, observerError, [statecoreLib.STATECORE_EVENT__STATE_CHANGE, { test: 'test3' }, { test: 'test2' }]]
);
// Test remove the observer that throws an error
const removeObserverThrowingError = statecoreInstance.statecoreAddObserver(observerThrowingError);
removeObserverThrowingError();
statecoreInstance.statecoreSetState({ test: 'test4' });
assert.deepEqual(observerCalledWithArgs, [statecoreLib.STATECORE_EVENT__STATE_CHANGE, { test: 'test4' }, { test: 'test3' }]);
// Test the removeObserver function
observerCalled = false;
observerCalledWithArgs = null;
removeObserver();
// The observer should not have been called
statecoreInstance.statecoreSetState({ test: 'test5' });
assert.equal(observerCalled, false);
assert.deepEqual(observerCalledWithArgs, null);
assert.deepEqual(statecoreInstance.statecoreGetState(), { test: 'test5' });
// Test the destroy function
let destroyEventCalled = false;
statecoreInstance.statecoreAddObserver((eventName) => {
if (eventName === statecoreLib.STATECORE_EVENT__DESTROY) {
destroyEventCalled = true;
}
});
statecoreInstance.statecoreDestroy();
assert.ok(destroyEventCalled);
assert.ok(statecoreInstance.statecoreIsDestroyed());
assert.deepEqual(statecoreInstance.statecoreGetState(), undefined); // state is undefined after destroy
assert.throws(() => statecoreInstance.statecoreSetState({ test: 'test5' }), /The statecore instance has been destroyed!/);
// Test statecoreAddObserver with leading filter args
const filterInstance = statecoreLib.createStatecore();
let filterObserverCalled = false;
let filterObserverArgs = null;
// Observer registered with a leading filter arg - only matched when callerArgs.length >= filter count
const removeFilterObserver = filterInstance.statecoreAddObserver(statecoreLib.STATECORE_EVENT__STATE_CHANGE, function(...args) {
filterObserverCalled = true;
filterObserverArgs = args;
});
assert.equal(typeof removeFilterObserver, 'function');
// STATE_CHANGE has 3 args — satisfies the 1-filter-arg count requirement
filterInstance.statecoreSetState('newValue');
assert.ok(filterObserverCalled, 'Filter observer should be called when arg count is satisfied');
assert.equal(filterObserverArgs[0], statecoreLib.STATECORE_EVENT__STATE_CHANGE);
// Observer registered with more filter args than the notification provides — should be skipped
const filterInstance2 = statecoreLib.createStatecore();
let tooManyFiltersCalled = false;
// DESTROY emits only 1 arg; registering with 2 filter args means it requires 2 args → skipped for DESTROY
filterInstance2.statecoreAddObserver(statecoreLib.STATECORE_EVENT__DESTROY, 'extraFilter', function() {
tooManyFiltersCalled = true;
});
filterInstance2.statecoreDestroy();
assert.equal(tooManyFiltersCalled, false, 'Observer with more filter args than notification args should be skipped');
// removeObserver returned by filter-arg form works correctly
const filterInstance3 = statecoreLib.createStatecore();
let filterRemoveCalled = false;
const removeIt = filterInstance3.statecoreAddObserver(statecoreLib.STATECORE_EVENT__STATE_CHANGE, function() {
filterRemoveCalled = true;
});
removeIt();
filterInstance3.statecoreSetState('x');
assert.equal(filterRemoveCalled, false, 'Observer should not be called after removeObserver()');
// statecoreRemoveObserver still works with filter-arg observers
const filterInstance4 = statecoreLib.createStatecore();
let filterRemoveCalled2 = false;
const filterObs = function() { filterRemoveCalled2 = true; };
filterInstance4.statecoreAddObserver(statecoreLib.STATECORE_EVENT__STATE_CHANGE, filterObs);
filterInstance4.statecoreRemoveObserver(filterObs);
filterInstance4.statecoreSetState('x');
assert.equal(filterRemoveCalled2, false, 'statecoreRemoveObserver should work for filter-arg observers');
// Test GrabInstance() static method
const StatecoreClass = statecoreLib.StatecoreClass;
const instanceA1 = StatecoreClass.statecoreClassStaticGrabInstance('instanceA', { value: 1 });
assert.deepEqual(instanceA1.statecoreGetState(), { value: 1 });
const instanceA2 = StatecoreClass.statecoreClassStaticGrabInstance(false, 'instanceA');
assert.strictEqual(instanceA1, instanceA2); // should be the same instance
const instanceB1 = StatecoreClass.statecoreClassStaticGrabInstance('instanceB', { value: 2 });
assert.notStrictEqual(instanceA1, instanceB1); // should be different instances
assert.deepEqual(instanceB1.statecoreGetState(), { value: 2 });
// Destroy instanceA and grab again
instanceA1.statecoreDestroy();
assert.ok(instanceA1.statecoreIsDestroyed());
const instanceA3 = StatecoreClass.statecoreClassStaticGrabInstance('instanceA', { value: 3 });
assert.notStrictEqual(instanceA1, instanceA3); // should be a new instance
assert.deepEqual(instanceA3.statecoreGetState(), { value: 3 });
const instanceA4 = StatecoreClass.statecoreClassStaticGrabInstance(false, 'instanceA');
assert.strictEqual(instanceA3, instanceA4); // should be the same instance
const instanceC1 = StatecoreClass.statecoreClassStaticGrabInstance(false, 'instanceC');
assert.strictEqual(instanceC1, null); // should be null since it doesn't exist and opt is null
// test for subclass
class SubStatecore extends StatecoreClass {
constructor(initialState) {
super(initialState);
}
}
const subInstance1 = SubStatecore.statecoreClassStaticGrabInstance('subInstance', { subValue: 1 });
assert.deepEqual(subInstance1.statecoreGetState(), { subValue: 1 });
let subObserverCalled = false;
subInstance1.statecoreAddObserver(() => {
subObserverCalled = true;
});
subInstance1.statecoreSetState({ subValue: 2 });
assert.ok(subObserverCalled);
assert.deepEqual(subInstance1.statecoreGetState(), { subValue: 2 });
// test the instance from the super class vs subclass
const superInstance = StatecoreClass.statecoreClassStaticGrabInstance('subInstance', { subValue: 2 });
assert.deepEqual(superInstance.statecoreGetState(), subInstance1.statecoreGetState()); // should be the same value
assert.notStrictEqual(superInstance, subInstance1); // but should be different instances
// changing the state of one should not affect the other
superInstance.statecoreSetState({ subValue: 3 });
assert.deepEqual(superInstance.statecoreGetState(), { subValue: 3 });
assert.deepEqual(subInstance1.statecoreGetState(), { subValue: 2 }); // should not be affected
// Test return value of statecoreNotifyAllObservers
const retValInstance = statecoreLib.createStatecore();
// No observers → empty array
assert.deepEqual(retValInstance.statecoreNotifyAllObservers('CUSTOM_EVENT'), []);
// Single observer returning a value
retValInstance.statecoreAddObserver(function(eventName) {
if (eventName === 'CUSTOM_EVENT') return 42;
});
assert.deepEqual(retValInstance.statecoreNotifyAllObservers('CUSTOM_EVENT'), [{ value: 42 }]);
// Observer returning undefined is still captured
retValInstance.statecoreAddObserver(function() { /* returns nothing */ });
const twoResults = retValInstance.statecoreNotifyAllObservers('CUSTOM_EVENT');
assert.equal(twoResults.length, 2);
assert.deepEqual(twoResults[0], { value: 42 });
assert.deepEqual(twoResults[1], { value: undefined });
// Throwing observer produces { error } entry and does not prevent subsequent observers from running
const retValInstance2 = statecoreLib.createStatecore();
const boom = new Error('observer boom');
retValInstance2.statecoreAddObserver(function() { return 'first'; });
retValInstance2.statecoreAddObserver(function() { throw boom; });
retValInstance2.statecoreAddObserver(function() { return 'third'; });
const mixedResults = retValInstance2.statecoreNotifyAllObservers('CUSTOM_EVENT');
assert.equal(mixedResults.length, 3);
assert.deepEqual(mixedResults[0], { value: 'first' });
assert.deepEqual(mixedResults[1], { error: boom });
assert.deepEqual(mixedResults[2], { value: 'third' });
// Observers with filter args should receive the callerArgs after the filter args
const instanceForObserver = statecoreLib.createStatecore();
let observerArgs = null;
let observerCalledCount = 0;
instanceForObserver.statecoreAddObserver(1, 2, 3, function(...args) {
// triggered
observerCalledCount++;
observerArgs = args;
});
instanceForObserver.statecoreAddObserver(1, 2, function(...args) {
// triggered
observerCalledCount++;
observerArgs = args;
});
instanceForObserver.statecoreAddObserver(1, 3, function(...args) {
// not triggered
observerCalledCount++;
observerArgs = args;
});
instanceForObserver.statecoreAddObserver(1, 2, 4, function(...args) {
// not triggered
observerCalledCount++;
observerArgs = args;
});
instanceForObserver.statecoreNotifyAllObservers(1, 2, 3, 'newState', 'oldState');
assert.deepEqual(observerArgs, [1, 2, 3, 'newState', 'oldState'], 'Observer should receive the callerArgs after the filter args');
assert.equal(observerCalledCount, 2, 'Only the observer with matching filter args should be called');
// ALL TESTS PASSED
console.log('✅ All tests passed!');