1
1
import type { Draft } from 'immer' ;
2
2
import * as sinon from 'sinon' ;
3
3
4
- import { BaseController } from './BaseControllerV2' ;
4
+ import { BaseController , getAnonymizedState , getPersistentState } from './BaseControllerV2' ;
5
5
6
6
type MockControllerState = {
7
7
count : number ;
8
8
} ;
9
9
10
+ const mockControllerStateMetadata = {
11
+ count : {
12
+ persist : true ,
13
+ anonymous : true ,
14
+ } ,
15
+ } ;
16
+
10
17
class MockController extends BaseController < MockControllerState > {
11
18
update ( callback : ( state : Draft < MockControllerState > ) => void | MockControllerState ) {
12
19
super . update ( callback ) ;
@@ -19,21 +26,27 @@ class MockController extends BaseController<MockControllerState> {
19
26
20
27
describe ( 'BaseController' , ( ) => {
21
28
it ( 'should set initial state' , ( ) => {
22
- const controller = new MockController ( { count : 0 } ) ;
29
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
23
30
24
31
expect ( controller . state ) . toEqual ( { count : 0 } ) ;
25
32
} ) ;
26
33
34
+ it ( 'should set initial schema' , ( ) => {
35
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
36
+
37
+ expect ( controller . metadata ) . toEqual ( mockControllerStateMetadata ) ;
38
+ } ) ;
39
+
27
40
it ( 'should not allow mutating state directly' , ( ) => {
28
- const controller = new MockController ( { count : 0 } ) ;
41
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
29
42
30
43
expect ( ( ) => {
31
44
controller . state = { count : 1 } ;
32
45
} ) . toThrow ( ) ;
33
46
} ) ;
34
47
35
48
it ( 'should allow updating state by modifying draft' , ( ) => {
36
- const controller = new MockController ( { count : 0 } ) ;
49
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
37
50
38
51
controller . update ( ( draft ) => {
39
52
draft . count += 1 ;
@@ -43,7 +56,7 @@ describe('BaseController', () => {
43
56
} ) ;
44
57
45
58
it ( 'should allow updating state by return a value' , ( ) => {
46
- const controller = new MockController ( { count : 0 } ) ;
59
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
47
60
48
61
controller . update ( ( ) => {
49
62
return { count : 1 } ;
@@ -53,7 +66,7 @@ describe('BaseController', () => {
53
66
} ) ;
54
67
55
68
it ( 'should throw an error if update callback modifies draft and returns value' , ( ) => {
56
- const controller = new MockController ( { count : 0 } ) ;
69
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
57
70
58
71
expect ( ( ) => {
59
72
controller . update ( ( draft ) => {
@@ -64,7 +77,7 @@ describe('BaseController', () => {
64
77
} ) ;
65
78
66
79
it ( 'should inform subscribers of state changes' , ( ) => {
67
- const controller = new MockController ( { count : 0 } ) ;
80
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
68
81
const listener1 = sinon . stub ( ) ;
69
82
const listener2 = sinon . stub ( ) ;
70
83
@@ -81,7 +94,7 @@ describe('BaseController', () => {
81
94
} ) ;
82
95
83
96
it ( 'should inform a subscriber of each state change once even after multiple subscriptions' , ( ) => {
84
- const controller = new MockController ( { count : 0 } ) ;
97
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
85
98
const listener1 = sinon . stub ( ) ;
86
99
87
100
controller . subscribe ( listener1 ) ;
@@ -95,7 +108,7 @@ describe('BaseController', () => {
95
108
} ) ;
96
109
97
110
it ( 'should no longer inform a subscriber about state changes after unsubscribing' , ( ) => {
98
- const controller = new MockController ( { count : 0 } ) ;
111
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
99
112
const listener1 = sinon . stub ( ) ;
100
113
101
114
controller . subscribe ( listener1 ) ;
@@ -108,7 +121,7 @@ describe('BaseController', () => {
108
121
} ) ;
109
122
110
123
it ( 'should no longer inform a subscriber about state changes after unsubscribing once, even if they subscribed many times' , ( ) => {
111
- const controller = new MockController ( { count : 0 } ) ;
124
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
112
125
const listener1 = sinon . stub ( ) ;
113
126
114
127
controller . subscribe ( listener1 ) ;
@@ -122,7 +135,7 @@ describe('BaseController', () => {
122
135
} ) ;
123
136
124
137
it ( 'should allow unsubscribing listeners who were never subscribed' , ( ) => {
125
- const controller = new MockController ( { count : 0 } ) ;
138
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
126
139
const listener1 = sinon . stub ( ) ;
127
140
128
141
expect ( ( ) => {
@@ -131,7 +144,7 @@ describe('BaseController', () => {
131
144
} ) ;
132
145
133
146
it ( 'should no longer update subscribers after being destroyed' , ( ) => {
134
- const controller = new MockController ( { count : 0 } ) ;
147
+ const controller = new MockController ( { count : 0 } , mockControllerStateMetadata ) ;
135
148
const listener1 = sinon . stub ( ) ;
136
149
const listener2 = sinon . stub ( ) ;
137
150
@@ -146,3 +159,275 @@ describe('BaseController', () => {
146
159
expect ( listener2 . callCount ) . toEqual ( 0 ) ;
147
160
} ) ;
148
161
} ) ;
162
+
163
+ describe ( 'getAnonymizedState' , ( ) => {
164
+ it ( 'should return empty state' , ( ) => {
165
+ expect ( getAnonymizedState ( { } , { } ) ) . toEqual ( { } ) ;
166
+ } ) ;
167
+
168
+ it ( 'should return empty state when no properties are anonymized' , ( ) => {
169
+ const anonymizedState = getAnonymizedState ( { count : 1 } , { count : { anonymous : false , persist : false } } ) ;
170
+ expect ( anonymizedState ) . toEqual ( { } ) ;
171
+ } ) ;
172
+
173
+ it ( 'should return state that is already anonymized' , ( ) => {
174
+ const anonymizedState = getAnonymizedState (
175
+ {
176
+ password : 'secret password' ,
177
+ privateKey : '123' ,
178
+ network : 'mainnet' ,
179
+ tokens : [ 'DAI' , 'USDC' ] ,
180
+ } ,
181
+ {
182
+ password : {
183
+ anonymous : false ,
184
+ persist : false ,
185
+ } ,
186
+ privateKey : {
187
+ anonymous : false ,
188
+ persist : false ,
189
+ } ,
190
+ network : {
191
+ anonymous : true ,
192
+ persist : false ,
193
+ } ,
194
+ tokens : {
195
+ anonymous : true ,
196
+ persist : false ,
197
+ } ,
198
+ } ,
199
+ ) ;
200
+ expect ( anonymizedState ) . toEqual ( { network : 'mainnet' , tokens : [ 'DAI' , 'USDC' ] } ) ;
201
+ } ) ;
202
+
203
+ it ( 'should use anonymizing function to anonymize state' , ( ) => {
204
+ const anonymizeTransactionHash = ( hash : string ) => {
205
+ return hash . split ( '' ) . reverse ( ) . join ( '' ) ;
206
+ } ;
207
+
208
+ const anonymizedState = getAnonymizedState (
209
+ {
210
+ transactionHash : '0x1234' ,
211
+ } ,
212
+ {
213
+ transactionHash : {
214
+ anonymous : anonymizeTransactionHash ,
215
+ persist : false ,
216
+ } ,
217
+ } ,
218
+ ) ;
219
+
220
+ expect ( anonymizedState ) . toEqual ( { transactionHash : '4321x0' } ) ;
221
+ } ) ;
222
+
223
+ it ( 'should allow returning a partial object from an anonymizing function' , ( ) => {
224
+ const anonymizeTxMeta = ( txMeta : { hash : string ; value : number } ) => {
225
+ return { value : txMeta . value } ;
226
+ } ;
227
+
228
+ const anonymizedState = getAnonymizedState (
229
+ {
230
+ txMeta : {
231
+ hash : '0x123' ,
232
+ value : 10 ,
233
+ } ,
234
+ } ,
235
+ {
236
+ txMeta : {
237
+ anonymous : anonymizeTxMeta ,
238
+ persist : false ,
239
+ } ,
240
+ } ,
241
+ ) ;
242
+
243
+ expect ( anonymizedState ) . toEqual ( { txMeta : { value : 10 } } ) ;
244
+ } ) ;
245
+
246
+ it ( 'should allow returning a nested partial object from an anonymizing function' , ( ) => {
247
+ const anonymizeTxMeta = ( txMeta : { hash : string ; value : number ; history : { hash : string ; value : number } [ ] } ) => {
248
+ return {
249
+ history : txMeta . history . map ( ( entry ) => {
250
+ return { value : entry . value } ;
251
+ } ) ,
252
+ value : txMeta . value ,
253
+ } ;
254
+ } ;
255
+
256
+ const anonymizedState = getAnonymizedState (
257
+ {
258
+ txMeta : {
259
+ hash : '0x123' ,
260
+ history : [
261
+ {
262
+ hash : '0x123' ,
263
+ value : 9 ,
264
+ } ,
265
+ ] ,
266
+ value : 10 ,
267
+ } ,
268
+ } ,
269
+ {
270
+ txMeta : {
271
+ anonymous : anonymizeTxMeta ,
272
+ persist : false ,
273
+ } ,
274
+ } ,
275
+ ) ;
276
+
277
+ expect ( anonymizedState ) . toEqual ( { txMeta : { history : [ { value : 9 } ] , value : 10 } } ) ;
278
+ } ) ;
279
+
280
+ it ( 'should allow transforming types in an anonymizing function' , ( ) => {
281
+ const anonymizedState = getAnonymizedState (
282
+ {
283
+ count : '1' ,
284
+ } ,
285
+ {
286
+ count : {
287
+ anonymous : ( count ) => Number ( count ) ,
288
+ persist : false ,
289
+ } ,
290
+ } ,
291
+ ) ;
292
+
293
+ expect ( anonymizedState ) . toEqual ( { count : 1 } ) ;
294
+ } ) ;
295
+ } ) ;
296
+
297
+ describe ( 'getPersistentState' , ( ) => {
298
+ it ( 'should return empty state' , ( ) => {
299
+ expect ( getPersistentState ( { } , { } ) ) . toEqual ( { } ) ;
300
+ } ) ;
301
+
302
+ it ( 'should return empty state when no properties are persistent' , ( ) => {
303
+ const persistentState = getPersistentState ( { count : 1 } , { count : { anonymous : false , persist : false } } ) ;
304
+ expect ( persistentState ) . toEqual ( { } ) ;
305
+ } ) ;
306
+
307
+ it ( 'should return persistent state' , ( ) => {
308
+ const persistentState = getPersistentState (
309
+ {
310
+ password : 'secret password' ,
311
+ privateKey : '123' ,
312
+ network : 'mainnet' ,
313
+ tokens : [ 'DAI' , 'USDC' ] ,
314
+ } ,
315
+ {
316
+ password : {
317
+ anonymous : false ,
318
+ persist : true ,
319
+ } ,
320
+ privateKey : {
321
+ anonymous : false ,
322
+ persist : true ,
323
+ } ,
324
+ network : {
325
+ anonymous : false ,
326
+ persist : false ,
327
+ } ,
328
+ tokens : {
329
+ anonymous : false ,
330
+ persist : false ,
331
+ } ,
332
+ } ,
333
+ ) ;
334
+ expect ( persistentState ) . toEqual ( { password : 'secret password' , privateKey : '123' } ) ;
335
+ } ) ;
336
+
337
+ it ( 'should use function to derive persistent state' , ( ) => {
338
+ const normalizeTransacitonHash = ( hash : string ) => {
339
+ return hash . toLowerCase ( ) ;
340
+ } ;
341
+
342
+ const persistentState = getPersistentState (
343
+ {
344
+ transactionHash : '0X1234' ,
345
+ } ,
346
+ {
347
+ transactionHash : {
348
+ anonymous : false ,
349
+ persist : normalizeTransacitonHash ,
350
+ } ,
351
+ } ,
352
+ ) ;
353
+
354
+ expect ( persistentState ) . toEqual ( { transactionHash : '0x1234' } ) ;
355
+ } ) ;
356
+
357
+ it ( 'should allow returning a partial object from a persist function' , ( ) => {
358
+ const getPersistentTxMeta = ( txMeta : { hash : string ; value : number } ) => {
359
+ return { value : txMeta . value } ;
360
+ } ;
361
+
362
+ const persistentState = getPersistentState (
363
+ {
364
+ txMeta : {
365
+ hash : '0x123' ,
366
+ value : 10 ,
367
+ } ,
368
+ } ,
369
+ {
370
+ txMeta : {
371
+ anonymous : false ,
372
+ persist : getPersistentTxMeta ,
373
+ } ,
374
+ } ,
375
+ ) ;
376
+
377
+ expect ( persistentState ) . toEqual ( { txMeta : { value : 10 } } ) ;
378
+ } ) ;
379
+
380
+ it ( 'should allow returning a nested partial object from a persist function' , ( ) => {
381
+ const getPersistentTxMeta = ( txMeta : {
382
+ hash : string ;
383
+ value : number ;
384
+ history : { hash : string ; value : number } [ ] ;
385
+ } ) => {
386
+ return {
387
+ history : txMeta . history . map ( ( entry ) => {
388
+ return { value : entry . value } ;
389
+ } ) ,
390
+ value : txMeta . value ,
391
+ } ;
392
+ } ;
393
+
394
+ const persistentState = getPersistentState (
395
+ {
396
+ txMeta : {
397
+ hash : '0x123' ,
398
+ history : [
399
+ {
400
+ hash : '0x123' ,
401
+ value : 9 ,
402
+ } ,
403
+ ] ,
404
+ value : 10 ,
405
+ } ,
406
+ } ,
407
+ {
408
+ txMeta : {
409
+ anonymous : false ,
410
+ persist : getPersistentTxMeta ,
411
+ } ,
412
+ } ,
413
+ ) ;
414
+
415
+ expect ( persistentState ) . toEqual ( { txMeta : { history : [ { value : 9 } ] , value : 10 } } ) ;
416
+ } ) ;
417
+
418
+ it ( 'should allow transforming types in a persist function' , ( ) => {
419
+ const persistentState = getPersistentState (
420
+ {
421
+ count : '1' ,
422
+ } ,
423
+ {
424
+ count : {
425
+ anonymous : false ,
426
+ persist : ( count ) => Number ( count ) ,
427
+ } ,
428
+ } ,
429
+ ) ;
430
+
431
+ expect ( persistentState ) . toEqual ( { count : 1 } ) ;
432
+ } ) ;
433
+ } ) ;
0 commit comments