launchdarkly-js-sdk-common 4.3.1 → 5.0.0-alpha.1
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/CHANGELOG.md +4 -0
- package/package.json +3 -1
- package/src/AnonymousContextProcessor.js +95 -0
- package/src/ContextFilter.js +147 -0
- package/src/EventProcessor.js +30 -13
- package/src/EventSender.js +1 -1
- package/src/Identity.js +10 -11
- package/src/PersistentFlagStore.js +3 -3
- package/src/Requestor.js +4 -4
- package/src/Stream.js +50 -8
- package/src/__tests__/ContextFilter-test.js +470 -0
- package/src/__tests__/EventProcessor-test.js +50 -121
- package/src/__tests__/LDClient-events-test.js +9 -152
- package/src/__tests__/LDClient-inspectors-test.js +1 -1
- package/src/__tests__/LDClient-test.js +18 -15
- package/src/__tests__/Stream-test.js +40 -2
- package/src/__tests__/TransientContextProcessor-test.js +115 -0
- package/src/__tests__/attributeReference-test.js +400 -0
- package/src/__tests__/configuration-test.js +20 -21
- package/src/__tests__/context-test.js +93 -0
- package/src/__tests__/diagnosticEvents-test.js +0 -4
- package/src/__tests__/utils-test.js +3 -9
- package/src/attributeReference.js +143 -0
- package/src/configuration.js +4 -8
- package/src/context.js +96 -0
- package/src/diagnosticEvents.js +0 -2
- package/src/headers.js +2 -1
- package/src/index.js +76 -89
- package/src/messages.js +12 -9
- package/src/utils.js +18 -15
- package/test-types.ts +3 -7
- package/typings.d.ts +140 -76
- package/src/UserFilter.js +0 -75
- package/src/UserValidator.js +0 -56
- package/src/__tests__/UserFilter-test.js +0 -93
- package/src/__tests__/UserValidator-test.js +0 -57
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
const ContextFilter = require('../ContextFilter');
|
|
2
|
+
|
|
3
|
+
describe('when handling legacy user contexts', () => {
|
|
4
|
+
// users to serialize
|
|
5
|
+
const user = {
|
|
6
|
+
key: 'abc',
|
|
7
|
+
firstName: 'Sue',
|
|
8
|
+
custom: { bizzle: 'def', dizzle: 'ghi' },
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const userSpecifyingOwnPrivateAttr = {
|
|
12
|
+
key: 'abc',
|
|
13
|
+
firstName: 'Sue',
|
|
14
|
+
custom: { bizzle: 'def', dizzle: 'ghi' },
|
|
15
|
+
privateAttributeNames: ['dizzle', 'unused'],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const userWithUnknownTopLevelAttrs = {
|
|
19
|
+
key: 'abc',
|
|
20
|
+
firstName: 'Sue',
|
|
21
|
+
species: 'human',
|
|
22
|
+
hatSize: 6,
|
|
23
|
+
custom: { bizzle: 'def', dizzle: 'ghi' },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const anonUser = {
|
|
27
|
+
key: 'abc',
|
|
28
|
+
anonymous: true,
|
|
29
|
+
custom: { bizzle: 'def', dizzle: 'ghi' },
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const userWithNonStringsInStringRequiredFields = {
|
|
33
|
+
key: -1,
|
|
34
|
+
name: 0,
|
|
35
|
+
ip: 1,
|
|
36
|
+
firstName: 2,
|
|
37
|
+
lastName: ['a', 99, null],
|
|
38
|
+
email: 4,
|
|
39
|
+
avatar: 5,
|
|
40
|
+
country: 6,
|
|
41
|
+
custom: {
|
|
42
|
+
validNumericField: 7,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// expected results from serializing user
|
|
47
|
+
const userWithNothingHidden = {
|
|
48
|
+
bizzle: 'def',
|
|
49
|
+
dizzle: 'ghi',
|
|
50
|
+
firstName: 'Sue',
|
|
51
|
+
key: 'abc',
|
|
52
|
+
kind: 'user',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const userWithAllAttrsHidden = {
|
|
56
|
+
kind: 'user',
|
|
57
|
+
key: 'abc',
|
|
58
|
+
_meta: {
|
|
59
|
+
redactedAttributes: ['/bizzle', '/dizzle', '/firstName'],
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const userWithSomeAttrsHidden = {
|
|
64
|
+
kind: 'user',
|
|
65
|
+
key: 'abc',
|
|
66
|
+
dizzle: 'ghi',
|
|
67
|
+
_meta: {
|
|
68
|
+
redactedAttributes: ['/bizzle', '/firstName'],
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const userWithOwnSpecifiedAttrHidden = {
|
|
73
|
+
kind: 'user',
|
|
74
|
+
key: 'abc',
|
|
75
|
+
firstName: 'Sue',
|
|
76
|
+
bizzle: 'def',
|
|
77
|
+
_meta: {
|
|
78
|
+
redactedAttributes: ['/dizzle'],
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const anonUserWithAllAttrsHidden = {
|
|
83
|
+
kind: 'user',
|
|
84
|
+
key: 'abc',
|
|
85
|
+
anonymous: true,
|
|
86
|
+
_meta: {
|
|
87
|
+
redactedAttributes: ['/bizzle', '/dizzle'],
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const userWithStringFieldsConverted = {
|
|
92
|
+
key: '-1',
|
|
93
|
+
kind: 'user',
|
|
94
|
+
name: '0',
|
|
95
|
+
ip: '1',
|
|
96
|
+
firstName: '2',
|
|
97
|
+
lastName: 'a,99,',
|
|
98
|
+
email: '4',
|
|
99
|
+
avatar: '5',
|
|
100
|
+
country: '6',
|
|
101
|
+
validNumericField: 7,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const userWithPrivateFieldsWithAPrecedingSlash = {
|
|
105
|
+
key: 'annoying',
|
|
106
|
+
custom: {
|
|
107
|
+
'/why': 'not',
|
|
108
|
+
why: 'because',
|
|
109
|
+
},
|
|
110
|
+
privateAttributeNames: ['/why'],
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const userWithPrivateFieldsWithAPrecedingSlashFiltered = {
|
|
114
|
+
kind: 'user',
|
|
115
|
+
key: 'annoying',
|
|
116
|
+
why: 'because',
|
|
117
|
+
_meta: {
|
|
118
|
+
redactedAttributes: ['/~1why'],
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
it('includes all user attributes by default', () => {
|
|
123
|
+
const uf = ContextFilter({});
|
|
124
|
+
expect(uf.filter(user)).toEqual(userWithNothingHidden);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('hides all except key if allAttributesPrivate is true', () => {
|
|
128
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
129
|
+
expect(uf.filter(user)).toEqual(userWithAllAttrsHidden);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('hides some attributes if privateAttributes is set', () => {
|
|
133
|
+
const uf = ContextFilter({ privateAttributes: ['firstName', 'bizzle'] });
|
|
134
|
+
expect(uf.filter(user)).toEqual(userWithSomeAttrsHidden);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('hides attributes specified in per-user redactedAttributes', () => {
|
|
138
|
+
const uf = ContextFilter({});
|
|
139
|
+
expect(uf.filter(userSpecifyingOwnPrivateAttr)).toEqual(userWithOwnSpecifiedAttrHidden);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('looks at both per-user redactedAttributes and global config', () => {
|
|
143
|
+
const uf = ContextFilter({ privateAttributes: ['firstName', 'bizzle'] });
|
|
144
|
+
expect(uf.filter(userSpecifyingOwnPrivateAttr)).toEqual(userWithAllAttrsHidden);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('strips unknown top-level attributes', () => {
|
|
148
|
+
const uf = ContextFilter({});
|
|
149
|
+
expect(uf.filter(userWithUnknownTopLevelAttrs)).toEqual(userWithNothingHidden);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('maintains anonymous in conversion to a single kind context', () => {
|
|
153
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
154
|
+
expect(uf.filter(anonUser)).toEqual(anonUserWithAllAttrsHidden);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('converts non-boolean "anonymous" to boolean "anonymous"', () => {
|
|
158
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
159
|
+
expect(uf.filter({ key: 'user', anonymous: 'yes' })).toEqual({ key: 'user', kind: 'user', anonymous: true });
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('converts fields to string types when needed', () => {
|
|
163
|
+
const uf = ContextFilter({});
|
|
164
|
+
expect(uf.filter(userWithNonStringsInStringRequiredFields)).toEqual(userWithStringFieldsConverted);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('it handles legacy names which had a preceding slash', () => {
|
|
168
|
+
const uf = ContextFilter({});
|
|
169
|
+
expect(uf.filter(userWithPrivateFieldsWithAPrecedingSlash)).toEqual(
|
|
170
|
+
userWithPrivateFieldsWithAPrecedingSlashFiltered
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it.each([null, undefined])('handles null and undefined the same for built-in attributes', value => {
|
|
175
|
+
const cf = ContextFilter({});
|
|
176
|
+
const user = {
|
|
177
|
+
key: 'userKey',
|
|
178
|
+
name: value,
|
|
179
|
+
ip: value,
|
|
180
|
+
firstName: value,
|
|
181
|
+
lastName: value,
|
|
182
|
+
email: value,
|
|
183
|
+
avatar: value,
|
|
184
|
+
country: value,
|
|
185
|
+
};
|
|
186
|
+
expect(cf.filter(user)).toEqual({ key: 'userKey', kind: 'user' });
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('when handling single kind contexts', () => {
|
|
191
|
+
// users to serialize
|
|
192
|
+
const context = {
|
|
193
|
+
kind: 'organization',
|
|
194
|
+
key: 'abc',
|
|
195
|
+
firstName: 'Sue',
|
|
196
|
+
bizzle: 'def',
|
|
197
|
+
dizzle: 'ghi',
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const contextSpecifyingOwnPrivateAttr = {
|
|
201
|
+
kind: 'organization',
|
|
202
|
+
key: 'abc',
|
|
203
|
+
firstName: 'Sue',
|
|
204
|
+
bizzle: 'def',
|
|
205
|
+
dizzle: 'ghi',
|
|
206
|
+
_meta: {
|
|
207
|
+
privateAttributes: ['dizzle', 'unused'],
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const anonymousContext = {
|
|
212
|
+
kind: 'organization',
|
|
213
|
+
key: 'abc',
|
|
214
|
+
anonymous: true,
|
|
215
|
+
bizzle: 'def',
|
|
216
|
+
dizzle: 'ghi',
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const contextWithNonStringKeyKeyAndSecondary = {
|
|
220
|
+
kind: 'rebel',
|
|
221
|
+
key: 42,
|
|
222
|
+
_meta: {
|
|
223
|
+
secondary: 0,
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const contextWithNullSecondary = {
|
|
228
|
+
kind: 'rebel',
|
|
229
|
+
key: 42,
|
|
230
|
+
_meta: {
|
|
231
|
+
secondary: null,
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// expected results from serializing context
|
|
236
|
+
const userWithAllAttrsHidden = {
|
|
237
|
+
kind: 'organization',
|
|
238
|
+
key: 'abc',
|
|
239
|
+
_meta: {
|
|
240
|
+
redactedAttributes: ['/bizzle', '/dizzle', '/firstName'],
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const contextWithSomeAttrsHidden = {
|
|
245
|
+
kind: 'organization',
|
|
246
|
+
key: 'abc',
|
|
247
|
+
dizzle: 'ghi',
|
|
248
|
+
_meta: {
|
|
249
|
+
redactedAttributes: ['/bizzle', '/firstName'],
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const contextWithOwnSpecifiedAttrHidden = {
|
|
254
|
+
kind: 'organization',
|
|
255
|
+
key: 'abc',
|
|
256
|
+
firstName: 'Sue',
|
|
257
|
+
bizzle: 'def',
|
|
258
|
+
_meta: {
|
|
259
|
+
redactedAttributes: ['/dizzle'],
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const contextWithAllAttrsHidden = {
|
|
264
|
+
kind: 'organization',
|
|
265
|
+
key: 'abc',
|
|
266
|
+
anonymous: true,
|
|
267
|
+
_meta: {
|
|
268
|
+
redactedAttributes: ['/bizzle', '/dizzle'],
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const contextWithStringifiedKeyKeyAndSecondary = {
|
|
273
|
+
kind: 'rebel',
|
|
274
|
+
key: '42',
|
|
275
|
+
_meta: {
|
|
276
|
+
secondary: '0',
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const contextWithNullSecondaryFiltered = {
|
|
281
|
+
kind: 'rebel',
|
|
282
|
+
key: '42',
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
it('includes all attributes by default', () => {
|
|
286
|
+
const uf = ContextFilter({});
|
|
287
|
+
expect(uf.filter(context)).toEqual(context);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('hides all except key if allAttributesPrivate is true', () => {
|
|
291
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
292
|
+
expect(uf.filter(context)).toEqual(userWithAllAttrsHidden);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('hides some attributes if privateAttributes is set', () => {
|
|
296
|
+
const uf = ContextFilter({ privateAttributes: ['firstName', 'bizzle'] });
|
|
297
|
+
expect(uf.filter(context)).toEqual(contextWithSomeAttrsHidden);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('hides attributes specified in per-context redactedAttributes', () => {
|
|
301
|
+
const uf = ContextFilter({});
|
|
302
|
+
expect(uf.filter(contextSpecifyingOwnPrivateAttr)).toEqual(contextWithOwnSpecifiedAttrHidden);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('looks at both per-context redactedAttributes and global config', () => {
|
|
306
|
+
const uf = ContextFilter({ privateAttributes: ['firstName', 'bizzle'] });
|
|
307
|
+
expect(uf.filter(contextSpecifyingOwnPrivateAttr)).toEqual(userWithAllAttrsHidden);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('context remains anonymous even when all attributes are hidden', () => {
|
|
311
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
312
|
+
expect(uf.filter(anonymousContext)).toEqual(contextWithAllAttrsHidden);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('handles non-string key and secondary', () => {
|
|
316
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
317
|
+
expect(uf.filter(contextWithNonStringKeyKeyAndSecondary)).toEqual(contextWithStringifiedKeyKeyAndSecondary);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('does not produce "null" as a secondary attribute', () => {
|
|
321
|
+
const cf = ContextFilter({});
|
|
322
|
+
expect(cf.filter(contextWithNullSecondary)).toEqual(contextWithNullSecondaryFiltered);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('converts non-boolean anonymous to boolean.', () => {
|
|
326
|
+
const uf = ContextFilter({});
|
|
327
|
+
expect(uf.filter({ kind: 'user', key: 'user', anonymous: 'string' })).toEqual({
|
|
328
|
+
kind: 'user',
|
|
329
|
+
key: 'user',
|
|
330
|
+
anonymous: true,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
expect(uf.filter({ kind: 'user', key: 'user', anonymous: null })).toEqual({
|
|
334
|
+
kind: 'user',
|
|
335
|
+
key: 'user',
|
|
336
|
+
anonymous: false,
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
describe('when handling mult-kind contexts', () => {
|
|
342
|
+
const contextWithBadContexts = {
|
|
343
|
+
kind: 'multi',
|
|
344
|
+
string: 'string',
|
|
345
|
+
null: null,
|
|
346
|
+
number: 0,
|
|
347
|
+
real: {
|
|
348
|
+
key: 'real',
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const contextWithBadContextsRemoved = {
|
|
353
|
+
kind: 'multi',
|
|
354
|
+
real: {
|
|
355
|
+
key: 'real',
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const orgAndUserContext = {
|
|
360
|
+
kind: 'multi',
|
|
361
|
+
organization: {
|
|
362
|
+
key: 'LD',
|
|
363
|
+
rocks: true,
|
|
364
|
+
name: 'name',
|
|
365
|
+
department: {
|
|
366
|
+
name: 'sdk',
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
user: {
|
|
370
|
+
key: 'abc',
|
|
371
|
+
name: 'alphabet',
|
|
372
|
+
letters: ['a', 'b', 'c'],
|
|
373
|
+
order: 3,
|
|
374
|
+
object: {
|
|
375
|
+
a: 'a',
|
|
376
|
+
b: 'b',
|
|
377
|
+
},
|
|
378
|
+
_meta: {
|
|
379
|
+
secondary: 'order',
|
|
380
|
+
privateAttributes: ['letters', '/object/b'],
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
const orgAndUserContextAllPrivate = {
|
|
386
|
+
kind: 'multi',
|
|
387
|
+
organization: {
|
|
388
|
+
key: 'LD',
|
|
389
|
+
_meta: {
|
|
390
|
+
redactedAttributes: ['/department', '/name', '/rocks'],
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
user: {
|
|
394
|
+
key: 'abc',
|
|
395
|
+
_meta: {
|
|
396
|
+
secondary: 'order',
|
|
397
|
+
redactedAttributes: ['/letters', '/name', '/object', '/order'],
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
const orgAndUserGlobalNamePrivate = {
|
|
403
|
+
kind: 'multi',
|
|
404
|
+
organization: {
|
|
405
|
+
key: 'LD',
|
|
406
|
+
rocks: true,
|
|
407
|
+
department: {
|
|
408
|
+
name: 'sdk',
|
|
409
|
+
},
|
|
410
|
+
_meta: {
|
|
411
|
+
redactedAttributes: ['/name'],
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
user: {
|
|
415
|
+
key: 'abc',
|
|
416
|
+
order: 3,
|
|
417
|
+
object: {
|
|
418
|
+
a: 'a',
|
|
419
|
+
},
|
|
420
|
+
_meta: {
|
|
421
|
+
secondary: 'order',
|
|
422
|
+
redactedAttributes: ['/letters', '/name', '/object/b'],
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const orgAndUserContextIncludedPrivate = {
|
|
428
|
+
kind: 'multi',
|
|
429
|
+
organization: {
|
|
430
|
+
key: 'LD',
|
|
431
|
+
rocks: true,
|
|
432
|
+
name: 'name',
|
|
433
|
+
department: {
|
|
434
|
+
name: 'sdk',
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
user: {
|
|
438
|
+
key: 'abc',
|
|
439
|
+
name: 'alphabet',
|
|
440
|
+
order: 3,
|
|
441
|
+
object: {
|
|
442
|
+
a: 'a',
|
|
443
|
+
},
|
|
444
|
+
_meta: {
|
|
445
|
+
secondary: 'order',
|
|
446
|
+
redactedAttributes: ['/letters', '/object/b'],
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
it('it should not include invalid contexts', () => {
|
|
452
|
+
const uf = ContextFilter({});
|
|
453
|
+
expect(uf.filter(contextWithBadContexts)).toEqual(contextWithBadContextsRemoved);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('it should remove attributes from all contexts when all attributes are private.', () => {
|
|
457
|
+
const uf = ContextFilter({ allAttributesPrivate: true });
|
|
458
|
+
expect(uf.filter(orgAndUserContext)).toEqual(orgAndUserContextAllPrivate);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('it should apply private attributes from the context to the context.', () => {
|
|
462
|
+
const uf = ContextFilter({});
|
|
463
|
+
expect(uf.filter(orgAndUserContext)).toEqual(orgAndUserContextIncludedPrivate);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('it should apply global private attributes to all contexts.', () => {
|
|
467
|
+
const uf = ContextFilter({ privateAttributes: ['name'] });
|
|
468
|
+
expect(uf.filter(orgAndUserContext)).toEqual(orgAndUserGlobalNamePrivate);
|
|
469
|
+
});
|
|
470
|
+
});
|