@dxos/echo-atom 0.8.4-main.ef1bc66f44 → 0.8.4-main.fcc0d83b33
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/README.md +28 -18
- package/dist/lib/neutral/index.mjs +52 -11
- package/dist/lib/neutral/index.mjs.map +3 -3
- package/dist/lib/neutral/meta.json +1 -1
- package/dist/types/src/atom.d.ts +19 -6
- package/dist/types/src/atom.d.ts.map +1 -1
- package/dist/types/src/query-atom.d.ts +2 -2
- package/dist/types/src/query-atom.d.ts.map +1 -1
- package/dist/types/src/ref-utils.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/src/atom.test.ts +106 -16
- package/src/atom.ts +87 -18
- package/src/batching.test.ts +24 -24
- package/src/query-atom.test.ts +138 -12
- package/src/query-atom.ts +5 -10
- package/src/reactivity.test.ts +51 -24
- package/src/ref-atom.test.ts +45 -11
- package/src/ref-utils.ts +17 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-atom",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.fcc0d83b33",
|
|
4
4
|
"description": "Effect Atom wrappers for ECHO objects with explicit subscriptions.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -20,25 +20,25 @@
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"types": "dist/types/src/index.d.ts",
|
|
23
|
-
"typesVersions": {
|
|
24
|
-
"*": {}
|
|
25
|
-
},
|
|
26
23
|
"files": [
|
|
27
24
|
"dist",
|
|
28
25
|
"src"
|
|
29
26
|
],
|
|
30
27
|
"dependencies": {
|
|
31
28
|
"@effect-atom/atom": "^0.5.1",
|
|
32
|
-
"
|
|
33
|
-
"@dxos/echo": "0.8.4-main.
|
|
34
|
-
"@dxos/invariant": "0.8.4-main.
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/util": "0.8.4-main.ef1bc66f44"
|
|
29
|
+
"@dxos/echo": "0.8.4-main.fcc0d83b33",
|
|
30
|
+
"@dxos/echo-db": "0.8.4-main.fcc0d83b33",
|
|
31
|
+
"@dxos/invariant": "0.8.4-main.fcc0d83b33",
|
|
32
|
+
"@dxos/util": "0.8.4-main.fcc0d83b33"
|
|
37
33
|
},
|
|
38
34
|
"devDependencies": {
|
|
39
|
-
"
|
|
40
|
-
"@dxos/
|
|
41
|
-
"@dxos/
|
|
35
|
+
"effect": "3.20.0",
|
|
36
|
+
"@dxos/context": "0.8.4-main.fcc0d83b33",
|
|
37
|
+
"@dxos/random": "0.8.4-main.fcc0d83b33",
|
|
38
|
+
"@dxos/test-utils": "0.8.4-main.fcc0d83b33"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"effect": "3.20.0"
|
|
42
42
|
},
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public"
|
package/src/atom.test.ts
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import * as Registry from '@effect-atom/atom/Registry';
|
|
6
|
-
import { describe, expect, test } from 'vitest';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
7
7
|
|
|
8
8
|
import { Obj, Ref } from '@dxos/echo';
|
|
9
|
-
import { TestSchema } from '@dxos/echo/testing';
|
|
10
9
|
import { createObject } from '@dxos/echo-db';
|
|
10
|
+
import { EchoTestBuilder } from '@dxos/echo-db/testing';
|
|
11
|
+
import { TestSchema } from '@dxos/echo/testing';
|
|
11
12
|
|
|
12
13
|
import * as AtomObj from './atom';
|
|
13
14
|
|
|
@@ -54,7 +55,7 @@ describe('Echo Atom - Basic Functionality', () => {
|
|
|
54
55
|
expect(registry.get(emailAtom)).toBe('test@example.com');
|
|
55
56
|
});
|
|
56
57
|
|
|
57
|
-
test('atom updates when object is mutated via Obj.
|
|
58
|
+
test('atom updates when object is mutated via Obj.update', () => {
|
|
58
59
|
const obj = createObject(
|
|
59
60
|
Obj.make(TestSchema.Person, { name: 'Test', username: 'test', email: 'test@example.com' }),
|
|
60
61
|
);
|
|
@@ -71,9 +72,9 @@ describe('Echo Atom - Basic Functionality', () => {
|
|
|
71
72
|
{ immediate: true },
|
|
72
73
|
);
|
|
73
74
|
|
|
74
|
-
// Mutate object via Obj.
|
|
75
|
-
Obj.
|
|
76
|
-
|
|
75
|
+
// Mutate object via Obj.update.
|
|
76
|
+
Obj.update(obj, (obj) => {
|
|
77
|
+
obj.name = 'Updated';
|
|
77
78
|
});
|
|
78
79
|
|
|
79
80
|
// Subscription should have fired: immediate + update.
|
|
@@ -84,7 +85,7 @@ describe('Echo Atom - Basic Functionality', () => {
|
|
|
84
85
|
expect(obj.name).toBe('Updated');
|
|
85
86
|
});
|
|
86
87
|
|
|
87
|
-
test('property atom supports updater pattern via Obj.
|
|
88
|
+
test('property atom supports updater pattern via Obj.update', () => {
|
|
88
89
|
const obj = createObject(
|
|
89
90
|
Obj.make(TestSchema.Task, {
|
|
90
91
|
title: 'Task',
|
|
@@ -103,9 +104,9 @@ describe('Echo Atom - Basic Functionality', () => {
|
|
|
103
104
|
{ immediate: true },
|
|
104
105
|
);
|
|
105
106
|
|
|
106
|
-
// Update through Obj.
|
|
107
|
-
Obj.
|
|
108
|
-
|
|
107
|
+
// Update through Obj.update.
|
|
108
|
+
Obj.update(obj, (obj) => {
|
|
109
|
+
obj.title = (obj.title ?? '') + ' Updated';
|
|
109
110
|
});
|
|
110
111
|
|
|
111
112
|
// Subscription should have fired: immediate + update.
|
|
@@ -139,8 +140,8 @@ describe('Echo Atom - Basic Functionality', () => {
|
|
|
139
140
|
expect(propertyUpdateCount).toBe(1);
|
|
140
141
|
|
|
141
142
|
// Mutate the standalone object.
|
|
142
|
-
Obj.
|
|
143
|
-
|
|
143
|
+
Obj.update(obj, (obj) => {
|
|
144
|
+
obj.name = 'Updated Standalone';
|
|
144
145
|
});
|
|
145
146
|
|
|
146
147
|
// Both atoms should have received updates.
|
|
@@ -243,8 +244,8 @@ describe('Echo Atom - Referential Equality', () => {
|
|
|
243
244
|
expect(updateCount).toBe(1);
|
|
244
245
|
|
|
245
246
|
// Mutate the object.
|
|
246
|
-
Obj.
|
|
247
|
-
|
|
247
|
+
Obj.update(obj, (obj) => {
|
|
248
|
+
obj.name = 'Updated';
|
|
248
249
|
});
|
|
249
250
|
|
|
250
251
|
// The subscription should still work.
|
|
@@ -275,8 +276,8 @@ describe('Echo Atom - Referential Equality', () => {
|
|
|
275
276
|
expect(updateCount).toBe(1);
|
|
276
277
|
|
|
277
278
|
// Mutate the specific property.
|
|
278
|
-
Obj.
|
|
279
|
-
|
|
279
|
+
Obj.update(obj, (obj) => {
|
|
280
|
+
obj.name = 'Updated';
|
|
280
281
|
});
|
|
281
282
|
|
|
282
283
|
// The subscription should still work.
|
|
@@ -306,3 +307,92 @@ describe('Echo Atom - Referential Equality', () => {
|
|
|
306
307
|
expect(atom1).toBe(atom2);
|
|
307
308
|
});
|
|
308
309
|
});
|
|
310
|
+
|
|
311
|
+
describe('AtomObj.makeWithReactive', () => {
|
|
312
|
+
test('returns object for direct obj input', () => {
|
|
313
|
+
const obj = createObject(
|
|
314
|
+
Obj.make(TestSchema.Person, { name: 'Test', username: 'test', email: 'test@example.com' }),
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
const registry = Registry.make();
|
|
318
|
+
const atom = AtomObj.makeWithReactive(obj);
|
|
319
|
+
const result = registry.get(atom);
|
|
320
|
+
|
|
321
|
+
expect(result).toBe(obj);
|
|
322
|
+
expect(result?.name).toBe('Test');
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test('returns same atom for same object', () => {
|
|
326
|
+
const obj = createObject(
|
|
327
|
+
Obj.make(TestSchema.Person, { name: 'Test', username: 'test', email: 'test@example.com' }),
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
const atom1 = AtomObj.makeWithReactive(obj);
|
|
331
|
+
const atom2 = AtomObj.makeWithReactive(obj);
|
|
332
|
+
|
|
333
|
+
expect(atom1).toBe(atom2);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
describe('with ref input', () => {
|
|
337
|
+
let testBuilder: InstanceType<typeof EchoTestBuilder>;
|
|
338
|
+
|
|
339
|
+
beforeEach(async () => {
|
|
340
|
+
testBuilder = await new EchoTestBuilder().open();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
afterEach(async () => {
|
|
344
|
+
await testBuilder.close();
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test('returns resolved object for ref', async ({ expect }) => {
|
|
348
|
+
const { db } = await testBuilder.createDatabase({
|
|
349
|
+
types: [TestSchema.Person, TestSchema.Task],
|
|
350
|
+
});
|
|
351
|
+
const task = db.add(Obj.make(TestSchema.Task, { title: 'Task 1' }));
|
|
352
|
+
const person = db.add(
|
|
353
|
+
Obj.make(TestSchema.Person, {
|
|
354
|
+
name: 'Test',
|
|
355
|
+
username: 'test',
|
|
356
|
+
email: 'test@example.com',
|
|
357
|
+
tasks: [Ref.make(task)],
|
|
358
|
+
}),
|
|
359
|
+
);
|
|
360
|
+
await db.flush();
|
|
361
|
+
|
|
362
|
+
const ref = person.tasks![0];
|
|
363
|
+
const registry = Registry.make();
|
|
364
|
+
const atom = AtomObj.makeWithReactive(ref);
|
|
365
|
+
const result = registry.get(atom);
|
|
366
|
+
|
|
367
|
+
expect(result).toBe(task);
|
|
368
|
+
expect(result?.title).toBe('Task 1');
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
test('returns undefined when ref target was removed', async ({ expect }) => {
|
|
372
|
+
const { db } = await testBuilder.createDatabase({
|
|
373
|
+
types: [TestSchema.Person, TestSchema.Task],
|
|
374
|
+
});
|
|
375
|
+
const task = db.add(Obj.make(TestSchema.Task, { title: 'Task 1' }));
|
|
376
|
+
const person = db.add(
|
|
377
|
+
Obj.make(TestSchema.Person, {
|
|
378
|
+
name: 'Test',
|
|
379
|
+
username: 'test',
|
|
380
|
+
email: 'test@example.com',
|
|
381
|
+
tasks: [Ref.make(task)],
|
|
382
|
+
}),
|
|
383
|
+
);
|
|
384
|
+
await db.flush();
|
|
385
|
+
|
|
386
|
+
const ref = person.tasks![0];
|
|
387
|
+
const registry = Registry.make();
|
|
388
|
+
const atom = AtomObj.makeWithReactive(ref);
|
|
389
|
+
|
|
390
|
+
expect(registry.get(atom)).toBe(task);
|
|
391
|
+
|
|
392
|
+
db.remove(task);
|
|
393
|
+
await db.flush();
|
|
394
|
+
|
|
395
|
+
expect(registry.get(atom)).toBeUndefined();
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
});
|
package/src/atom.ts
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import * as Atom from '@effect-atom/atom/Atom';
|
|
6
|
-
import
|
|
6
|
+
import * as Result from '@effect-atom/atom/Result';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import * as Function from 'effect/Function';
|
|
9
|
+
import * as Option from 'effect/Option';
|
|
7
10
|
|
|
8
|
-
import { Obj, Ref } from '@dxos/echo';
|
|
11
|
+
import { type Entity, Obj, Ref, Relation } from '@dxos/echo';
|
|
9
12
|
import { assertArgument } from '@dxos/invariant';
|
|
10
13
|
|
|
11
14
|
import { loadRefTarget } from './ref-utils';
|
|
@@ -23,7 +26,7 @@ const objectFamily = Atom.family(<T extends Obj.Unknown>(obj: T): Atom.Atom<Obj.
|
|
|
23
26
|
get.addFinalizer(() => unsubscribe());
|
|
24
27
|
|
|
25
28
|
return Obj.getSnapshot(obj);
|
|
26
|
-
});
|
|
29
|
+
}).pipe(Atom.keepAlive);
|
|
27
30
|
});
|
|
28
31
|
|
|
29
32
|
/**
|
|
@@ -48,7 +51,7 @@ const refFamily = Atom.family(<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Ato
|
|
|
48
51
|
});
|
|
49
52
|
|
|
50
53
|
return loadRefTarget(ref, get, setupTargetSubscription);
|
|
51
|
-
});
|
|
54
|
+
}).pipe(Atom.keepAlive);
|
|
52
55
|
});
|
|
53
56
|
|
|
54
57
|
/**
|
|
@@ -80,7 +83,7 @@ const propertyFamily = Atom.family(<T extends Obj.Unknown>(obj: T) =>
|
|
|
80
83
|
|
|
81
84
|
const unsubscribe = Obj.subscribe(obj, () => {
|
|
82
85
|
const newValue = obj[key];
|
|
83
|
-
if (
|
|
86
|
+
if (previousSnapshot !== newValue) {
|
|
84
87
|
previousSnapshot = snapshotForComparison(newValue);
|
|
85
88
|
// Return a snapshot copy so React sees a new reference.
|
|
86
89
|
get.setSelf(snapshotForComparison(newValue));
|
|
@@ -91,43 +94,46 @@ const propertyFamily = Atom.family(<T extends Obj.Unknown>(obj: T) =>
|
|
|
91
94
|
|
|
92
95
|
// Return a snapshot copy so React sees a new reference.
|
|
93
96
|
return snapshotForComparison(obj[key]);
|
|
94
|
-
});
|
|
97
|
+
}).pipe(Atom.keepAlive);
|
|
95
98
|
}),
|
|
96
99
|
);
|
|
97
100
|
|
|
98
101
|
/**
|
|
99
|
-
* Create a read-only atom for a reactive object or ref.
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
+
* Create a read-only atom for a single reactive object or ref.
|
|
103
|
+
* Returns {@link Obj.Snapshot} (immutable plain data), not the live reactive object.
|
|
104
|
+
* Use this when you need one object's data for display or React dependency tracking.
|
|
102
105
|
* The atom updates automatically when the object is mutated.
|
|
103
106
|
* For refs, automatically handles async loading.
|
|
104
|
-
* Uses Atom.family internally - same object/ref
|
|
107
|
+
* Uses Atom.family internally - same object/ref returns same atom instance.
|
|
105
108
|
*
|
|
106
109
|
* @param objOrRef - The reactive object or ref to create an atom for, or undefined.
|
|
107
|
-
* @returns An atom that returns the object snapshot. Returns undefined only for refs (async loading) or undefined input.
|
|
110
|
+
* @returns An atom that returns the object snapshot (plain data). Returns undefined only for refs (async loading) or undefined input.
|
|
108
111
|
*/
|
|
109
112
|
export function make<T extends Obj.Unknown>(obj: T): Atom.Atom<Obj.Snapshot<T>>;
|
|
113
|
+
export function make<T extends Relation.Unknown>(relation: T): Atom.Atom<Relation.Snapshot<T>>;
|
|
114
|
+
export function make<T extends Entity.Unknown>(entity: T): Atom.Atom<Entity.Snapshot>;
|
|
110
115
|
export function make<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Atom<Obj.Snapshot<T> | undefined>;
|
|
111
116
|
export function make<T extends Obj.Unknown>(
|
|
112
117
|
objOrRef: T | Ref.Ref<T> | undefined,
|
|
113
118
|
): Atom.Atom<Obj.Snapshot<T> | undefined>;
|
|
114
|
-
export function make<T extends
|
|
119
|
+
export function make<T extends Entity.Unknown>(
|
|
115
120
|
objOrRef: T | Ref.Ref<T> | undefined,
|
|
116
|
-
): Atom.Atom<
|
|
121
|
+
): Atom.Atom<Entity.Snapshot | undefined> {
|
|
117
122
|
if (objOrRef === undefined) {
|
|
118
|
-
return Atom.make<
|
|
123
|
+
return Atom.make<Entity.Snapshot | undefined>(() => undefined);
|
|
119
124
|
}
|
|
120
125
|
|
|
121
126
|
// Handle Ref inputs.
|
|
122
127
|
if (Ref.isRef(objOrRef)) {
|
|
123
|
-
return refFamily(objOrRef as
|
|
128
|
+
return refFamily(objOrRef as any);
|
|
124
129
|
}
|
|
125
130
|
|
|
126
131
|
// At this point, objOrRef is definitely T (not a Ref).
|
|
127
132
|
const obj = objOrRef as T;
|
|
128
|
-
assertArgument(Obj.isObject(obj), 'obj', 'Object must be a reactive object');
|
|
133
|
+
assertArgument(Obj.isObject(obj) || Relation.isRelation(obj), 'obj', 'Object must be a reactive object');
|
|
129
134
|
|
|
130
|
-
|
|
135
|
+
// TODO(dmaretskyi): Fix echo types during review.
|
|
136
|
+
return objectFamily(obj as any);
|
|
131
137
|
}
|
|
132
138
|
|
|
133
139
|
/**
|
|
@@ -155,6 +161,69 @@ export function makeProperty<T extends Obj.Unknown, K extends keyof T>(
|
|
|
155
161
|
}
|
|
156
162
|
|
|
157
163
|
assertArgument(Obj.isObject(obj), 'obj', 'Object must be a reactive object');
|
|
158
|
-
assertArgument(key in obj, 'key', 'Property must exist on object');
|
|
159
164
|
return propertyFamily(obj)(key);
|
|
160
165
|
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Atom family for ECHO objects - returns the live object, not a snapshot.
|
|
169
|
+
* Same as objectFamily but returns T instead of Obj.Snapshot<T>.
|
|
170
|
+
*/
|
|
171
|
+
const objectWithReactiveFamily = Atom.family(<T extends Obj.Unknown>(obj: T): Atom.Atom<T> => {
|
|
172
|
+
return Atom.make<T>((get) => {
|
|
173
|
+
const unsubscribe = Obj.subscribe(obj, () => {
|
|
174
|
+
get.setSelf(obj);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
get.addFinalizer(() => unsubscribe());
|
|
178
|
+
|
|
179
|
+
return obj;
|
|
180
|
+
}).pipe(Atom.keepAlive);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Atom family for ECHO refs - returns the live reactive object, not a snapshot.
|
|
185
|
+
* Resolves the ref via the database; returns undefined while loading or if unresolved.
|
|
186
|
+
*/
|
|
187
|
+
const refWithReactiveFamily = Atom.family(<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Atom<T | undefined> => {
|
|
188
|
+
const effect = (get: Atom.Context) =>
|
|
189
|
+
Effect.gen(function* () {
|
|
190
|
+
const snapshot = get(make(ref));
|
|
191
|
+
if (snapshot == null) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
const option = yield* Obj.getReactiveOption(snapshot);
|
|
195
|
+
return Option.getOrElse(option, () => undefined);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return Function.pipe(
|
|
199
|
+
Atom.make(effect),
|
|
200
|
+
Atom.map((result) => Result.getOrElse(result, () => undefined)),
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Like {@link make} but returns the live reactive object instead of a snapshot.
|
|
206
|
+
* Same input: Obj or Ref.Ref. Same output shape: Atom that updates when the object mutates.
|
|
207
|
+
* Prefer {@link make} (snapshot) unless you need the live Obj.Obj for generic mutations (e.g. Obj.update).
|
|
208
|
+
*
|
|
209
|
+
* @param objOrRef - The reactive object or ref.
|
|
210
|
+
* @returns An atom that returns the live object. Returns undefined for refs (async loading) or undefined input.
|
|
211
|
+
*/
|
|
212
|
+
export function makeWithReactive<T extends Obj.Unknown>(obj: T): Atom.Atom<T>;
|
|
213
|
+
export function makeWithReactive<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Atom<T | undefined>;
|
|
214
|
+
export function makeWithReactive<T extends Obj.Unknown>(objOrRef: T | Ref.Ref<T> | undefined): Atom.Atom<T | undefined>;
|
|
215
|
+
export function makeWithReactive<T extends Obj.Unknown>(
|
|
216
|
+
objOrRef: T | Ref.Ref<T> | undefined,
|
|
217
|
+
): Atom.Atom<T | undefined> {
|
|
218
|
+
if (objOrRef === undefined) {
|
|
219
|
+
return Atom.make<T | undefined>(() => undefined);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (Ref.isRef(objOrRef)) {
|
|
223
|
+
return refWithReactiveFamily(objOrRef as Ref.Ref<T>);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const obj = objOrRef as T;
|
|
227
|
+
assertArgument(Obj.isObject(obj), 'obj', 'Object must be a reactive object');
|
|
228
|
+
return objectWithReactiveFamily(obj);
|
|
229
|
+
}
|
package/src/batching.test.ts
CHANGED
|
@@ -6,13 +6,13 @@ import * as Registry from '@effect-atom/atom/Registry';
|
|
|
6
6
|
import { describe, expect, test } from 'vitest';
|
|
7
7
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
|
-
import { TestSchema } from '@dxos/echo/testing';
|
|
10
9
|
import { createObject } from '@dxos/echo-db';
|
|
10
|
+
import { TestSchema } from '@dxos/echo/testing';
|
|
11
11
|
|
|
12
12
|
import * as AtomObj from './atom';
|
|
13
13
|
|
|
14
14
|
describe('Echo Atom - Batch Updates', () => {
|
|
15
|
-
test('multiple updates to same object atom in single Obj.
|
|
15
|
+
test('multiple updates to same object atom in single Obj.update fire single update', () => {
|
|
16
16
|
const obj = createObject(
|
|
17
17
|
Obj.make(TestSchema.Person, { name: 'Test', username: 'test', email: 'test@example.com' }),
|
|
18
18
|
);
|
|
@@ -33,14 +33,14 @@ describe('Echo Atom - Batch Updates', () => {
|
|
|
33
33
|
const initialCount = updateCount;
|
|
34
34
|
expect(initialCount).toBe(1); // Verify immediate update fired.
|
|
35
35
|
|
|
36
|
-
// Make multiple updates to the same object in a single Obj.
|
|
37
|
-
Obj.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
// Make multiple updates to the same object in a single Obj.update call.
|
|
37
|
+
Obj.update(obj, (obj) => {
|
|
38
|
+
obj.name = 'Updated1';
|
|
39
|
+
obj.email = 'updated@example.com';
|
|
40
|
+
obj.username = 'updated';
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
// Should have fired once for initial + once for the Obj.
|
|
43
|
+
// Should have fired once for initial + once for the Obj.update (not once per property update).
|
|
44
44
|
expect(updateCount).toBe(2);
|
|
45
45
|
|
|
46
46
|
// Verify final state.
|
|
@@ -50,7 +50,7 @@ describe('Echo Atom - Batch Updates', () => {
|
|
|
50
50
|
expect(finalValue.username).toBe('updated');
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
test('multiple separate Obj.
|
|
53
|
+
test('multiple separate Obj.update calls fire separate updates', () => {
|
|
54
54
|
const obj = createObject(
|
|
55
55
|
Obj.make(TestSchema.Person, { name: 'Test', username: 'test', email: 'test@example.com' }),
|
|
56
56
|
);
|
|
@@ -71,18 +71,18 @@ describe('Echo Atom - Batch Updates', () => {
|
|
|
71
71
|
const initialCount = updateCount;
|
|
72
72
|
expect(initialCount).toBe(1);
|
|
73
73
|
|
|
74
|
-
// Make multiple separate Obj.
|
|
75
|
-
Obj.
|
|
76
|
-
|
|
74
|
+
// Make multiple separate Obj.update calls.
|
|
75
|
+
Obj.update(obj, (obj) => {
|
|
76
|
+
obj.name = 'Updated1';
|
|
77
77
|
});
|
|
78
|
-
Obj.
|
|
79
|
-
|
|
78
|
+
Obj.update(obj, (obj) => {
|
|
79
|
+
obj.email = 'updated@example.com';
|
|
80
80
|
});
|
|
81
|
-
Obj.
|
|
82
|
-
|
|
81
|
+
Obj.update(obj, (obj) => {
|
|
82
|
+
obj.username = 'updated';
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
-
// Should have fired once for initial + once per Obj.
|
|
85
|
+
// Should have fired once for initial + once per Obj.update call.
|
|
86
86
|
expect(updateCount).toBe(4);
|
|
87
87
|
|
|
88
88
|
// Verify final state.
|
|
@@ -92,7 +92,7 @@ describe('Echo Atom - Batch Updates', () => {
|
|
|
92
92
|
expect(finalValue.username).toBe('updated');
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
-
test('multiple updates to same property atom in single Obj.
|
|
95
|
+
test('multiple updates to same property atom in single Obj.update fire single update', () => {
|
|
96
96
|
const obj = createObject(
|
|
97
97
|
Obj.make(TestSchema.Person, { name: 'Test', username: 'test', email: 'test@example.com' }),
|
|
98
98
|
);
|
|
@@ -113,14 +113,14 @@ describe('Echo Atom - Batch Updates', () => {
|
|
|
113
113
|
const initialCount = updateCount;
|
|
114
114
|
expect(initialCount).toBe(1);
|
|
115
115
|
|
|
116
|
-
// Make multiple updates to the same property in a single Obj.
|
|
117
|
-
Obj.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
// Make multiple updates to the same property in a single Obj.update call.
|
|
117
|
+
Obj.update(obj, (obj) => {
|
|
118
|
+
obj.name = 'Updated1';
|
|
119
|
+
obj.name = 'Updated2';
|
|
120
|
+
obj.name = 'Updated3';
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
-
// Should have fired once for initial + once for the Obj.
|
|
123
|
+
// Should have fired once for initial + once for the Obj.update (not once per assignment).
|
|
124
124
|
expect(updateCount).toBe(2);
|
|
125
125
|
|
|
126
126
|
// Verify final state.
|