Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/client/test/ObjectObserver.basic.test.ts
1028 views
1
/**
2
ISC License (ISC)
3
4
Copyright 2015 Yuri Guller ([email protected])
5
Modifications 2021 Data Liberation Foundation
6
7
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
8
provided that the above copyright notice and this permission notice appear in all copies.
9
10
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
11
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
12
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
13
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
14
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
*/
16
import IObservableChange from '@secret-agent/interfaces/IObservableChange';
17
import ObjectObserver from '../lib/ObjectObserver';
18
19
// object
20
//
21
test('verify object - root - insert', () => {
22
let c;
23
const o: any = ObjectObserver.create({}, cs => {
24
c = cs[0];
25
});
26
o.some = 'new';
27
28
expect(c).toEqual({
29
type: 'insert',
30
path: ['some'],
31
value: 'new',
32
});
33
});
34
35
test('verify object - deep - insert', () => {
36
let c;
37
const o: any = ObjectObserver.create({ a: {} }, cs => {
38
c = cs[0];
39
});
40
o.a.some = 'new';
41
expect(c).toEqual({
42
type: 'insert',
43
path: ['a', 'some'],
44
value: 'new',
45
});
46
});
47
48
test('verify object - root - update', () => {
49
let c: IObservableChange;
50
const o = ObjectObserver.create({ p: 'old' }, cs => {
51
c = cs[0];
52
});
53
o.p = 'new';
54
expect(c).toEqual({
55
type: 'update',
56
path: ['p'],
57
value: 'new',
58
});
59
});
60
61
test('verify object - deep - update', () => {
62
let c;
63
const o = ObjectObserver.create({ a: { p: 'old' } }, cs => {
64
c = cs[0];
65
});
66
o.a.p = 'new';
67
expect(c).toEqual({
68
type: 'update',
69
path: ['a', 'p'],
70
value: 'new',
71
});
72
});
73
74
test('verify object - root - delete', () => {
75
let c;
76
const o = ObjectObserver.create({ p: 'old' }, cs => {
77
c = cs[0];
78
});
79
delete o.p;
80
expect(c).toEqual({
81
type: 'delete',
82
path: ['p'],
83
});
84
});
85
86
test('verify object - deep - delete', () => {
87
let c;
88
const o = ObjectObserver.create({ a: { p: 'old' } }, cs => {
89
c = cs[0];
90
});
91
delete o.a.p;
92
expect(c).toEqual({
93
type: 'delete',
94
path: ['a', 'p'],
95
});
96
});
97
98
// array
99
//
100
test('verify array - root - insert', () => {
101
let c;
102
const o = ObjectObserver.create([], cs => {
103
c = cs[0];
104
});
105
o.push('new');
106
expect(c).toEqual({
107
type: 'insert',
108
path: [0],
109
value: 'new',
110
});
111
});
112
113
test('verify array - deep - insert', () => {
114
let c;
115
const o = ObjectObserver.create([[]], cs => {
116
c = cs[0];
117
});
118
o[0].push('new');
119
expect(c).toEqual({
120
type: 'insert',
121
path: [0, 0],
122
value: 'new',
123
});
124
});
125
126
test('verify array - root - update', () => {
127
let c;
128
const o = ObjectObserver.create(['old'], cs => {
129
c = cs[0];
130
});
131
o[0] = 'new';
132
expect(c).toEqual({
133
type: 'update',
134
path: [0],
135
value: 'new',
136
});
137
});
138
139
test('verify array - deep - update', () => {
140
let c;
141
const o = ObjectObserver.create([['old']], cs => {
142
c = cs[0];
143
});
144
o[0][0] = 'new';
145
expect(c).toEqual({
146
type: 'update',
147
path: [0, 0],
148
value: 'new',
149
});
150
});
151
152
test('verify array - root - delete', () => {
153
let c;
154
const o = ObjectObserver.create(['old'], cs => {
155
c = cs[0];
156
});
157
o.pop();
158
expect(c).toEqual({
159
type: 'delete',
160
path: [0],
161
});
162
});
163
164
test('verify array - deep - delete', () => {
165
let c;
166
const o = ObjectObserver.create([['old']], cs => {
167
c = cs[0];
168
});
169
o[0].pop();
170
expect(c).toEqual({
171
type: 'delete',
172
path: [0, 0],
173
});
174
});
175
176
// advanced types
177
//
178
test('verify date - changes', () => {
179
const changes = [];
180
const o = ObjectObserver.create([{ name: 'test', date: new Date() }], cs => {
181
changes.push(...cs);
182
});
183
expect(o[0].date).toEqual(expect.any(String));
184
const date2 = new Date();
185
date2.setTime(new Date().getTime() - 100000);
186
187
o[0].date = date2 as any;
188
189
expect(changes[0]).toEqual({
190
type: 'update',
191
path: [0, 'date'],
192
value: date2.toISOString(),
193
});
194
});
195
196
test('buffer - records changes', () => {
197
const events = [];
198
const buffer = Buffer.from('This is a test');
199
const po = ObjectObserver.create({ buffer }, changes => events.push(...changes));
200
201
expect(po.buffer).toBe(buffer.toString('base64'));
202
expect(po.buffer).not.toEqual(buffer);
203
204
buffer.write('More');
205
expect(po.buffer).not.toBe(buffer.toString('base64'));
206
207
po.buffer = buffer;
208
expect(events).toHaveLength(1);
209
expect(events[0]).toEqual({
210
type: 'update',
211
path: ['buffer'],
212
value: Buffer.from('More is a test').toString('base64'),
213
});
214
});
215
216
// object tests
217
//
218
test('creating observable preserves original object keys order', () => {
219
const person = {
220
name: 'name',
221
age: 7,
222
street: 'street',
223
block: 9,
224
apt: 1,
225
};
226
const oPerson = ObjectObserver.create(person);
227
const sKeys = Object.keys(person);
228
const oKeys = Object.keys(oPerson);
229
230
expect(sKeys).toStrictEqual(oKeys);
231
});
232
233
test('plain object operations', () => {
234
const o: any = {
235
name: 'name',
236
age: 7,
237
address: null,
238
};
239
const events = [];
240
const tmpAddress = { street: 'some' };
241
242
const po = ObjectObserver.create(o, changes => events.push(...changes));
243
244
const v1 = (po.name = 'new name'); // eslint-disable-line no-multi-assign
245
const v2 = (po.age = 9); // eslint-disable-line no-multi-assign
246
const v3 = (po.address = tmpAddress); // eslint-disable-line no-multi-assign
247
expect(v1).toBe('new name');
248
expect(v2).toBe(9);
249
expect(v3).toBe(tmpAddress);
250
251
expect(events).toHaveLength(3);
252
expect(events[0]).toEqual({
253
type: 'update',
254
path: ['name'],
255
value: 'new name',
256
});
257
expect(events[1]).toEqual({
258
type: 'update',
259
path: ['age'],
260
value: 9,
261
});
262
expect(events[2]).toEqual({
263
type: 'update',
264
path: ['address'],
265
value: po.address,
266
});
267
268
const v4 = (po.address = null); // eslint-disable-line no-multi-assign
269
const v5 = (po.sex = 'male'); // eslint-disable-line no-multi-assign
270
delete po.sex;
271
expect(v4).toBe(null);
272
expect(v5).toBe('male');
273
274
expect(events).toHaveLength(6);
275
276
expect(events[3]).toEqual({
277
type: 'update',
278
path: ['address'],
279
value: null,
280
});
281
expect(events[4]).toEqual({
282
type: 'insert',
283
path: ['sex'],
284
value: 'male',
285
});
286
expect(events[5]).toEqual({
287
type: 'delete',
288
path: ['sex'],
289
});
290
});
291
292
test('sub tree object operations', () => {
293
const person = {
294
name: 'name',
295
age: 7,
296
address: null,
297
addressB: {
298
street: {
299
name: 'street name',
300
apt: 123,
301
},
302
},
303
};
304
const events = [];
305
const newAddress = {};
306
307
const po = ObjectObserver.create(person, changes => {
308
[].push.apply(events, changes);
309
});
310
311
po.address = newAddress;
312
expect(events).toHaveLength(1);
313
expect(events[0]).toEqual({
314
type: 'update',
315
path: ['address'],
316
value: po.address,
317
});
318
319
po.address.street = 'street';
320
po.addressB.street.name = 'new street name';
321
322
expect(events).toHaveLength(3);
323
expect(events[1]).toEqual({
324
type: 'insert',
325
path: ['address', 'street'],
326
value: 'street',
327
});
328
expect(events[2]).toEqual({
329
type: 'update',
330
path: ['addressB', 'street', 'name'],
331
value: 'new street name',
332
});
333
});
334
335
test('Object.assign with multiple properties yields many callbacks', () => {
336
const events = [];
337
let callbacks = 0;
338
const observable: any = ObjectObserver.create({}, changes => {
339
callbacks += 1;
340
events.push(...changes);
341
});
342
const newData = { a: 1, b: 2, c: 3 };
343
344
Object.assign(observable, newData);
345
observable.a = 4;
346
expect(callbacks).toBe(4);
347
expect(events).toHaveLength(4);
348
});
349
350