@osdk/client 2.2.0-beta.8 → 2.2.0-beta.9
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 +18 -0
- package/build/browser/createMinimalClient.js +2 -2
- package/build/browser/createMinimalClient.js.map +1 -1
- package/build/browser/object/SimpleOsdkProperties.js +2 -0
- package/build/browser/object/SimpleOsdkProperties.js.map +1 -0
- package/build/browser/object/convertWireToOsdkObjects/BaseHolder.js +2 -0
- package/build/browser/object/convertWireToOsdkObjects/BaseHolder.js.map +1 -0
- package/build/browser/object/convertWireToOsdkObjects/InterfaceHolder.js.map +1 -1
- package/build/browser/object/convertWireToOsdkObjects/ObjectHolder.js.map +1 -1
- package/build/browser/object/convertWireToOsdkObjects/createOsdkInterface.js.map +1 -1
- package/build/browser/object/convertWireToOsdkObjects/createOsdkObject.js +10 -3
- package/build/browser/object/convertWireToOsdkObjects/createOsdkObject.js.map +1 -1
- package/build/browser/object/convertWireToOsdkObjects/getDollarAs.js.map +1 -1
- package/build/browser/object/convertWireToOsdkObjects/getDollarLink.js.map +1 -1
- package/build/browser/object/convertWireToOsdkObjects.js +4 -2
- package/build/browser/object/convertWireToOsdkObjects.js.map +1 -1
- package/build/browser/object/fetchPage.js +13 -1
- package/build/browser/object/fetchPage.js.map +1 -1
- package/build/browser/object/fetchPage.test.js +56 -2
- package/build/browser/object/fetchPage.test.js.map +1 -1
- package/build/browser/object/object.test.js.map +1 -1
- package/build/browser/objectSet/InterfaceObjectSet.test.js +18 -2
- package/build/browser/objectSet/InterfaceObjectSet.test.js.map +1 -1
- package/build/browser/objectSet/ObjectSet.test.js +92 -102
- package/build/browser/objectSet/ObjectSet.test.js.map +1 -1
- package/build/browser/objectSet/ObjectSetListenerWebsocket.js +18 -14
- package/build/browser/objectSet/ObjectSetListenerWebsocket.js.map +1 -1
- package/build/browser/objectSet/createObjectSet.js.map +1 -1
- package/build/browser/observable/ListPayload.js.map +1 -1
- package/build/browser/observable/ObjectPayload.js.map +1 -1
- package/build/browser/observable/ObservableClient.js.map +1 -1
- package/build/browser/observable/internal/ActionApplication.js +29 -29
- package/build/browser/observable/internal/ActionApplication.js.map +1 -1
- package/build/browser/observable/internal/CacheKey.js +1 -1
- package/build/browser/observable/internal/CacheKey.js.map +1 -1
- package/build/browser/observable/internal/CacheKeys.js +2 -2
- package/build/browser/observable/internal/CacheKeys.js.map +1 -1
- package/build/browser/observable/internal/{ChangedObjects.js → Changes.js} +20 -9
- package/build/browser/observable/internal/Changes.js.map +1 -0
- package/build/browser/observable/internal/Layer.js +3 -0
- package/build/browser/observable/internal/Layer.js.map +1 -1
- package/build/browser/observable/internal/ListQuery.js +411 -170
- package/build/browser/observable/internal/ListQuery.js.map +1 -1
- package/build/browser/observable/internal/ObjectQuery.js +32 -16
- package/build/browser/observable/internal/ObjectQuery.js.map +1 -1
- package/build/browser/observable/internal/ObservableClientImpl.js +4 -12
- package/build/browser/observable/internal/ObservableClientImpl.js.map +1 -1
- package/build/browser/observable/internal/OptimisticJob.js.map +1 -1
- package/build/browser/observable/internal/OrderByCanonicalizer.js +73 -0
- package/build/browser/observable/internal/OrderByCanonicalizer.js.map +1 -0
- package/build/browser/observable/internal/OrderByCanonicalizer.test.js +78 -0
- package/build/browser/observable/internal/OrderByCanonicalizer.test.js.map +1 -0
- package/build/browser/observable/internal/Query.js +64 -31
- package/build/browser/observable/internal/Query.js.map +1 -1
- package/build/browser/observable/internal/RefCounts.js +7 -2
- package/build/browser/observable/internal/RefCounts.js.map +1 -1
- package/build/browser/observable/internal/SimpleWhereClause.js +2 -0
- package/build/browser/observable/internal/SimpleWhereClause.js.map +1 -0
- package/build/browser/observable/internal/Store.js +84 -267
- package/build/browser/observable/internal/Store.js.map +1 -1
- package/build/browser/observable/internal/Store.test.js +264 -247
- package/build/browser/observable/internal/Store.test.js.map +1 -1
- package/build/browser/observable/internal/WhereClauseCanonicalizer.js +11 -3
- package/build/browser/observable/internal/WhereClauseCanonicalizer.js.map +1 -1
- package/build/browser/observable/internal/objectMatchesWhereClause.js.map +1 -1
- package/build/browser/observable/internal/objectMatchesWhereClause.test.js.map +1 -1
- package/build/browser/observable/internal/testUtils.js +82 -18
- package/build/browser/observable/internal/testUtils.js.map +1 -1
- package/build/browser/public/unstable-do-not-use.js.map +1 -1
- package/build/browser/util/UserAgent.js +1 -1
- package/build/cjs/{chunk-JPENHIJB.cjs → chunk-EY52J5Z4.cjs} +25 -15
- package/build/cjs/chunk-EY52J5Z4.cjs.map +1 -0
- package/build/cjs/{chunk-IU47QMYO.cjs → chunk-MCQVHD2F.cjs} +32 -28
- package/build/cjs/chunk-MCQVHD2F.cjs.map +1 -0
- package/build/cjs/chunk-T4NIFYZS.cjs +14 -0
- package/build/cjs/chunk-T4NIFYZS.cjs.map +1 -0
- package/build/cjs/index.cjs +69 -72
- package/build/cjs/index.cjs.map +1 -1
- package/build/cjs/public/internal.cjs +6 -6
- package/build/cjs/public/unstable-do-not-use.cjs +683 -539
- package/build/cjs/public/unstable-do-not-use.cjs.map +1 -1
- package/build/cjs/public/unstable-do-not-use.d.cts +27 -26
- package/build/esm/createMinimalClient.js +2 -2
- package/build/esm/createMinimalClient.js.map +1 -1
- package/build/esm/object/SimpleOsdkProperties.js +2 -0
- package/build/esm/object/SimpleOsdkProperties.js.map +1 -0
- package/build/esm/object/convertWireToOsdkObjects/BaseHolder.js +2 -0
- package/build/esm/object/convertWireToOsdkObjects/BaseHolder.js.map +1 -0
- package/build/esm/object/convertWireToOsdkObjects/InterfaceHolder.js.map +1 -1
- package/build/esm/object/convertWireToOsdkObjects/ObjectHolder.js.map +1 -1
- package/build/esm/object/convertWireToOsdkObjects/createOsdkInterface.js.map +1 -1
- package/build/esm/object/convertWireToOsdkObjects/createOsdkObject.js +10 -3
- package/build/esm/object/convertWireToOsdkObjects/createOsdkObject.js.map +1 -1
- package/build/esm/object/convertWireToOsdkObjects/getDollarAs.js.map +1 -1
- package/build/esm/object/convertWireToOsdkObjects/getDollarLink.js.map +1 -1
- package/build/esm/object/convertWireToOsdkObjects.js +4 -2
- package/build/esm/object/convertWireToOsdkObjects.js.map +1 -1
- package/build/esm/object/fetchPage.js +13 -1
- package/build/esm/object/fetchPage.js.map +1 -1
- package/build/esm/object/fetchPage.test.js +56 -2
- package/build/esm/object/fetchPage.test.js.map +1 -1
- package/build/esm/object/object.test.js.map +1 -1
- package/build/esm/objectSet/InterfaceObjectSet.test.js +18 -2
- package/build/esm/objectSet/InterfaceObjectSet.test.js.map +1 -1
- package/build/esm/objectSet/ObjectSet.test.js +92 -102
- package/build/esm/objectSet/ObjectSet.test.js.map +1 -1
- package/build/esm/objectSet/ObjectSetListenerWebsocket.js +18 -14
- package/build/esm/objectSet/ObjectSetListenerWebsocket.js.map +1 -1
- package/build/esm/objectSet/createObjectSet.js.map +1 -1
- package/build/esm/observable/ListPayload.js.map +1 -1
- package/build/esm/observable/ObjectPayload.js.map +1 -1
- package/build/esm/observable/ObservableClient.js.map +1 -1
- package/build/esm/observable/internal/ActionApplication.js +29 -29
- package/build/esm/observable/internal/ActionApplication.js.map +1 -1
- package/build/esm/observable/internal/CacheKey.js +1 -1
- package/build/esm/observable/internal/CacheKey.js.map +1 -1
- package/build/esm/observable/internal/CacheKeys.js +2 -2
- package/build/esm/observable/internal/CacheKeys.js.map +1 -1
- package/build/esm/observable/internal/{ChangedObjects.js → Changes.js} +20 -9
- package/build/esm/observable/internal/Changes.js.map +1 -0
- package/build/esm/observable/internal/Layer.js +3 -0
- package/build/esm/observable/internal/Layer.js.map +1 -1
- package/build/esm/observable/internal/ListQuery.js +411 -170
- package/build/esm/observable/internal/ListQuery.js.map +1 -1
- package/build/esm/observable/internal/ObjectQuery.js +32 -16
- package/build/esm/observable/internal/ObjectQuery.js.map +1 -1
- package/build/esm/observable/internal/ObservableClientImpl.js +4 -12
- package/build/esm/observable/internal/ObservableClientImpl.js.map +1 -1
- package/build/esm/observable/internal/OptimisticJob.js.map +1 -1
- package/build/esm/observable/internal/OrderByCanonicalizer.js +73 -0
- package/build/esm/observable/internal/OrderByCanonicalizer.js.map +1 -0
- package/build/esm/observable/internal/OrderByCanonicalizer.test.js +78 -0
- package/build/esm/observable/internal/OrderByCanonicalizer.test.js.map +1 -0
- package/build/esm/observable/internal/Query.js +64 -31
- package/build/esm/observable/internal/Query.js.map +1 -1
- package/build/esm/observable/internal/RefCounts.js +7 -2
- package/build/esm/observable/internal/RefCounts.js.map +1 -1
- package/build/esm/observable/internal/SimpleWhereClause.js +2 -0
- package/build/esm/observable/internal/SimpleWhereClause.js.map +1 -0
- package/build/esm/observable/internal/Store.js +84 -267
- package/build/esm/observable/internal/Store.js.map +1 -1
- package/build/esm/observable/internal/Store.test.js +264 -247
- package/build/esm/observable/internal/Store.test.js.map +1 -1
- package/build/esm/observable/internal/WhereClauseCanonicalizer.js +11 -3
- package/build/esm/observable/internal/WhereClauseCanonicalizer.js.map +1 -1
- package/build/esm/observable/internal/objectMatchesWhereClause.js.map +1 -1
- package/build/esm/observable/internal/objectMatchesWhereClause.test.js.map +1 -1
- package/build/esm/observable/internal/testUtils.js +82 -18
- package/build/esm/observable/internal/testUtils.js.map +1 -1
- package/build/esm/public/unstable-do-not-use.js.map +1 -1
- package/build/esm/util/UserAgent.js +1 -1
- package/build/types/object/SimpleOsdkProperties.d.ts +1 -0
- package/build/types/object/SimpleOsdkProperties.d.ts.map +1 -0
- package/build/types/object/convertWireToOsdkObjects/BaseHolder.d.ts +1 -0
- package/build/types/object/convertWireToOsdkObjects/BaseHolder.d.ts.map +1 -0
- package/build/types/object/convertWireToOsdkObjects.d.ts +8 -1
- package/build/types/object/convertWireToOsdkObjects.d.ts.map +1 -1
- package/build/types/observable/ListPayload.d.ts +5 -9
- package/build/types/observable/ListPayload.d.ts.map +1 -1
- package/build/types/observable/ObjectPayload.d.ts +4 -7
- package/build/types/observable/ObjectPayload.d.ts.map +1 -1
- package/build/types/observable/ObservableClient.d.ts +27 -11
- package/build/types/observable/ObservableClient.d.ts.map +1 -1
- package/build/types/observable/internal/ActionApplication.d.ts.map +1 -1
- package/build/types/observable/internal/CacheKeys.d.ts +1 -1
- package/build/types/observable/internal/CacheKeys.d.ts.map +1 -1
- package/build/types/observable/internal/Changes.d.ts +15 -0
- package/build/types/observable/internal/Changes.d.ts.map +1 -0
- package/build/types/observable/internal/Layer.d.ts +1 -0
- package/build/types/observable/internal/Layer.d.ts.map +1 -1
- package/build/types/observable/internal/ListQuery.d.ts +59 -14
- package/build/types/observable/internal/ListQuery.d.ts.map +1 -1
- package/build/types/observable/internal/ObjectQuery.d.ts +5 -6
- package/build/types/observable/internal/ObjectQuery.d.ts.map +1 -1
- package/build/types/observable/internal/OptimisticJob.d.ts +1 -1
- package/build/types/observable/internal/OptimisticJob.d.ts.map +1 -1
- package/build/types/observable/internal/OrderByCanonicalizer.d.ts +12 -0
- package/build/types/observable/internal/OrderByCanonicalizer.d.ts.map +1 -0
- package/build/types/observable/internal/OrderByCanonicalizer.test.d.ts +1 -0
- package/build/types/observable/internal/OrderByCanonicalizer.test.d.ts.map +1 -0
- package/build/types/observable/internal/Query.d.ts +39 -4
- package/build/types/observable/internal/Query.d.ts.map +1 -1
- package/build/types/observable/internal/RefCounts.d.ts.map +1 -1
- package/build/types/observable/internal/SimpleWhereClause.d.ts +2 -0
- package/build/types/observable/internal/SimpleWhereClause.d.ts.map +1 -0
- package/build/types/observable/internal/Store.d.ts +19 -43
- package/build/types/observable/internal/Store.d.ts.map +1 -1
- package/build/types/observable/internal/WhereClauseCanonicalizer.d.ts +2 -1
- package/build/types/observable/internal/WhereClauseCanonicalizer.d.ts.map +1 -1
- package/build/types/observable/internal/objectMatchesWhereClause.d.ts +4 -2
- package/build/types/observable/internal/objectMatchesWhereClause.d.ts.map +1 -1
- package/build/types/observable/internal/testUtils.d.ts +39 -9
- package/build/types/observable/internal/testUtils.d.ts.map +1 -1
- package/build/types/public/unstable-do-not-use.d.ts +1 -4
- package/build/types/public/unstable-do-not-use.d.ts.map +1 -1
- package/package.json +12 -10
- package/build/browser/observable/internal/ChangedObjects.js.map +0 -1
- package/build/cjs/chunk-IU47QMYO.cjs.map +0 -1
- package/build/cjs/chunk-JPENHIJB.cjs.map +0 -1
- package/build/esm/observable/internal/ChangedObjects.js.map +0 -1
- package/build/types/observable/internal/ChangedObjects.d.ts +0 -11
- package/build/types/observable/internal/ChangedObjects.d.ts.map +0 -1
|
@@ -14,16 +14,21 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { $ontologyRid, createOffice, Employee, Todo } from "@osdk/client.test.ontology";
|
|
18
|
-
import {
|
|
17
|
+
import { $ontologyRid, createOffice, Employee, FooInterface, Todo } from "@osdk/client.test.ontology";
|
|
18
|
+
import { wireObjectTypeFullMetadataToSdkObjectMetadata } from "@osdk/generator-converters";
|
|
19
|
+
import { apiServer, stubData } from "@osdk/shared.test";
|
|
19
20
|
import chalk from "chalk";
|
|
20
21
|
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi, vitest } from "vitest";
|
|
21
22
|
import { createClient } from "../../createClient.js";
|
|
23
|
+
import { createMinimalClient } from "../../createMinimalClient.js";
|
|
24
|
+
import { createObjectSet } from "../../objectSet/createObjectSet.js";
|
|
25
|
+
import { InterfaceDefinitions } from "../../ontology/OntologyProvider.js";
|
|
22
26
|
import { createOptimisticId } from "./OptimisticId.js";
|
|
23
27
|
import { runOptimisticJob } from "./OptimisticJob.js";
|
|
24
|
-
import { Store } from "./Store.js";
|
|
25
|
-
import { applyCustomMatchers, createClientMockHelper, createDefer, createTestLogger, expectSingleListCallAndClear, expectSingleObjectCallAndClear,
|
|
28
|
+
import { invalidateList, Store } from "./Store.js";
|
|
29
|
+
import { applyCustomMatchers, createClientMockHelper, createDefer, createTestLogger, expectNoMoreCalls, expectSingleListCallAndClear, expectSingleObjectCallAndClear, getObject, mockListSubCallback, mockSingleSubCallback, objectPayloadContaining, updateList, updateObject, waitForCall } from "./testUtils.js";
|
|
26
30
|
const defer = createDefer();
|
|
31
|
+
const logger = createTestLogger({});
|
|
27
32
|
beforeAll(() => {
|
|
28
33
|
vi.setConfig({
|
|
29
34
|
fakeTimers: {
|
|
@@ -63,7 +68,7 @@ describe(Store, () => {
|
|
|
63
68
|
beforeAll(async () => {
|
|
64
69
|
apiServer.listen();
|
|
65
70
|
client = createClient("https://stack.palantir.com", $ontologyRid, async () => "myAccessToken", {
|
|
66
|
-
logger
|
|
71
|
+
logger
|
|
67
72
|
});
|
|
68
73
|
employeesAsServerReturns = (await client(Employee).fetchPage()).data;
|
|
69
74
|
mutatedEmployees = [employeesAsServerReturns[0], employeesAsServerReturns[1].$clone({
|
|
@@ -81,26 +86,27 @@ describe(Store, () => {
|
|
|
81
86
|
});
|
|
82
87
|
it("basic single object works", async () => {
|
|
83
88
|
const emp = employeesAsServerReturns[0];
|
|
89
|
+
const cacheKey = cache.getCacheKey("object", "Employee", emp.$primaryKey);
|
|
84
90
|
|
|
85
91
|
// starts empty
|
|
86
|
-
expect(cache.
|
|
87
|
-
const result =
|
|
92
|
+
expect(cache.getValue(cacheKey)?.value).toBeUndefined();
|
|
93
|
+
const result = updateObject(cache, emp);
|
|
88
94
|
expect(emp).toBe(result);
|
|
89
95
|
|
|
90
96
|
// getting the object now matches the result
|
|
91
|
-
expect(cache.
|
|
92
|
-
const updatedEmpFromCache =
|
|
97
|
+
expect(cache.getValue(cacheKey)?.value).toEqual(result);
|
|
98
|
+
const updatedEmpFromCache = updateObject(cache, emp.$clone({
|
|
93
99
|
fullName: "new name"
|
|
94
100
|
}));
|
|
95
101
|
expect(updatedEmpFromCache).not.toBe(emp);
|
|
96
102
|
|
|
97
103
|
// getting it again is the updated object
|
|
98
|
-
expect(cache.
|
|
104
|
+
expect(cache.getValue(cacheKey)?.value).toEqual(updatedEmpFromCache);
|
|
99
105
|
});
|
|
100
106
|
describe("optimistic updates", () => {
|
|
101
107
|
it("rolls back objects", async () => {
|
|
102
108
|
const emp = employeesAsServerReturns[0];
|
|
103
|
-
|
|
109
|
+
updateObject(cache, emp); // pre-seed the cache with the "real" value
|
|
104
110
|
|
|
105
111
|
const subFn = mockSingleSubCallback();
|
|
106
112
|
defer(cache.observeObject(Employee, emp.$primaryKey, {
|
|
@@ -109,7 +115,7 @@ describe(Store, () => {
|
|
|
109
115
|
expectSingleObjectCallAndClear(subFn, emp, "loaded");
|
|
110
116
|
const optimisticId = createOptimisticId();
|
|
111
117
|
// update with an optimistic write
|
|
112
|
-
|
|
118
|
+
updateObject(cache, emp.$clone({
|
|
113
119
|
fullName: "new name"
|
|
114
120
|
}), {
|
|
115
121
|
optimisticId
|
|
@@ -124,8 +130,8 @@ describe(Store, () => {
|
|
|
124
130
|
});
|
|
125
131
|
it("rolls back to an updated real value", async () => {
|
|
126
132
|
// pre-seed the cache with the "real" value
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
updateList(cache, {
|
|
134
|
+
type: Employee,
|
|
129
135
|
where: {},
|
|
130
136
|
orderBy: {}
|
|
131
137
|
}, employeesAsServerReturns);
|
|
@@ -137,7 +143,7 @@ describe(Store, () => {
|
|
|
137
143
|
expectSingleObjectCallAndClear(empSubFn, emp, "loaded");
|
|
138
144
|
const listSubFn = mockListSubCallback();
|
|
139
145
|
defer(cache.observeList({
|
|
140
|
-
|
|
146
|
+
type: Employee,
|
|
141
147
|
mode: "offline"
|
|
142
148
|
}, listSubFn));
|
|
143
149
|
await waitForCall(listSubFn, 1);
|
|
@@ -147,10 +153,10 @@ describe(Store, () => {
|
|
|
147
153
|
});
|
|
148
154
|
const optimisticId = createOptimisticId();
|
|
149
155
|
testStage("optimistic update");
|
|
150
|
-
expect(listSubFn).not.toHaveBeenCalled();
|
|
156
|
+
expect(listSubFn.next).not.toHaveBeenCalled();
|
|
151
157
|
|
|
152
158
|
// update with an optimistic write
|
|
153
|
-
|
|
159
|
+
updateObject(cache, optimisticEmployee, {
|
|
154
160
|
optimisticId
|
|
155
161
|
});
|
|
156
162
|
testStage("after optimistic update");
|
|
@@ -170,8 +176,8 @@ describe(Store, () => {
|
|
|
170
176
|
fullName: "real update"
|
|
171
177
|
});
|
|
172
178
|
testStage("write real update");
|
|
173
|
-
|
|
174
|
-
|
|
179
|
+
updateList(cache, {
|
|
180
|
+
type: Employee,
|
|
175
181
|
where: {},
|
|
176
182
|
orderBy: {}
|
|
177
183
|
}, [truthUpdatedEmployee]);
|
|
@@ -188,47 +194,38 @@ describe(Store, () => {
|
|
|
188
194
|
status: "loaded",
|
|
189
195
|
isOptimistic: false
|
|
190
196
|
});
|
|
191
|
-
vi.useRealTimers();
|
|
192
197
|
});
|
|
193
198
|
it("rolls back to an updated real value via list", async () => {
|
|
194
199
|
const emp = employeesAsServerReturns[0];
|
|
195
|
-
|
|
200
|
+
updateObject(cache, emp); // pre-seed the cache with the "real" value
|
|
196
201
|
|
|
197
202
|
const subFn = mockSingleSubCallback();
|
|
198
203
|
defer(cache.observeObject(Employee, emp.$primaryKey, {
|
|
199
204
|
mode: "offline"
|
|
200
205
|
}, subFn));
|
|
201
|
-
|
|
202
|
-
object: emp,
|
|
203
|
-
status: "loaded"
|
|
204
|
-
}));
|
|
205
|
-
subFn.mockClear();
|
|
206
|
+
expectSingleObjectCallAndClear(subFn, emp, "loaded");
|
|
206
207
|
const optimisticEmployee = emp.$clone({
|
|
207
208
|
fullName: "new name"
|
|
208
209
|
});
|
|
209
210
|
|
|
210
211
|
// update with an optimistic write
|
|
211
212
|
const optimisticId = createOptimisticId();
|
|
212
|
-
|
|
213
|
+
updateObject(cache, optimisticEmployee, {
|
|
213
214
|
optimisticId
|
|
214
215
|
});
|
|
215
|
-
|
|
216
|
-
object: optimisticEmployee
|
|
217
|
-
}));
|
|
218
|
-
subFn.mockClear();
|
|
216
|
+
expectSingleObjectCallAndClear(subFn, optimisticEmployee);
|
|
219
217
|
const truthUpdatedEmployee = emp.$clone({
|
|
220
218
|
fullName: "real update"
|
|
221
219
|
});
|
|
222
|
-
|
|
220
|
+
updateObject(cache, truthUpdatedEmployee);
|
|
223
221
|
|
|
224
222
|
// we shouldn't expect an update because the top layer has a value
|
|
225
|
-
expect(subFn).not.toHaveBeenCalled();
|
|
223
|
+
expect(subFn.next).not.toHaveBeenCalled();
|
|
226
224
|
|
|
227
225
|
// remove the optimistic write
|
|
228
226
|
cache.removeLayer(optimisticId);
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}));
|
|
227
|
+
expectSingleObjectCallAndClear(subFn, truthUpdatedEmployee);
|
|
228
|
+
expectNoMoreCalls(subFn);
|
|
232
229
|
});
|
|
233
230
|
});
|
|
234
231
|
describe(".invalidateObject", () => {
|
|
@@ -237,29 +234,20 @@ describe(Store, () => {
|
|
|
237
234
|
const staleEmp = emp.$clone({
|
|
238
235
|
fullName: "stale"
|
|
239
236
|
});
|
|
240
|
-
|
|
237
|
+
updateObject(cache, staleEmp);
|
|
241
238
|
const subFn = mockSingleSubCallback();
|
|
242
239
|
defer(cache.observeObject(Employee, emp.$primaryKey, {
|
|
243
240
|
mode: "offline"
|
|
244
241
|
}, subFn));
|
|
245
|
-
|
|
246
|
-
object: staleEmp,
|
|
247
|
-
status: "loaded"
|
|
248
|
-
}));
|
|
249
|
-
subFn.mockClear();
|
|
242
|
+
expectSingleObjectCallAndClear(subFn, staleEmp, "loaded");
|
|
250
243
|
|
|
251
244
|
// invalidate
|
|
252
245
|
void cache.invalidateObject(Employee, staleEmp.$primaryKey);
|
|
253
|
-
await
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
subFn.mockClear();
|
|
259
|
-
await vi.waitFor(() => expect(subFn).toHaveBeenCalled());
|
|
260
|
-
expect(subFn).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
261
|
-
object: emp
|
|
262
|
-
}));
|
|
246
|
+
await waitForCall(subFn);
|
|
247
|
+
expectSingleObjectCallAndClear(subFn, staleEmp, "loading");
|
|
248
|
+
await waitForCall(subFn);
|
|
249
|
+
expectSingleObjectCallAndClear(subFn, emp, "loaded");
|
|
250
|
+
expectNoMoreCalls(subFn);
|
|
263
251
|
});
|
|
264
252
|
});
|
|
265
253
|
describe(".invalidateList", () => {
|
|
@@ -274,8 +262,8 @@ describe(Store, () => {
|
|
|
274
262
|
const staleEmp = emp.$clone({
|
|
275
263
|
fullName: "stale"
|
|
276
264
|
});
|
|
277
|
-
|
|
278
|
-
|
|
265
|
+
updateList(cache, {
|
|
266
|
+
type: Employee,
|
|
279
267
|
where: {},
|
|
280
268
|
orderBy: {}
|
|
281
269
|
}, [staleEmp]);
|
|
@@ -286,7 +274,7 @@ describe(Store, () => {
|
|
|
286
274
|
expectSingleObjectCallAndClear(subFn, staleEmp);
|
|
287
275
|
const subListFn = mockListSubCallback();
|
|
288
276
|
defer(cache.observeList({
|
|
289
|
-
|
|
277
|
+
type: Employee,
|
|
290
278
|
mode: "offline"
|
|
291
279
|
}, subListFn));
|
|
292
280
|
await waitForCall(subListFn, 1);
|
|
@@ -294,8 +282,8 @@ describe(Store, () => {
|
|
|
294
282
|
status: "loaded"
|
|
295
283
|
});
|
|
296
284
|
testStage("invalidate");
|
|
297
|
-
|
|
298
|
-
|
|
285
|
+
const invalidateListPromise = invalidateList(cache, {
|
|
286
|
+
type: Employee
|
|
299
287
|
});
|
|
300
288
|
testStage("check invalidate");
|
|
301
289
|
await waitForCall(subListFn, 1);
|
|
@@ -308,6 +296,9 @@ describe(Store, () => {
|
|
|
308
296
|
});
|
|
309
297
|
await waitForCall(subFn, 1);
|
|
310
298
|
expectSingleObjectCallAndClear(subFn, emp, "loaded");
|
|
299
|
+
|
|
300
|
+
// don't leave promises dangling
|
|
301
|
+
await invalidateListPromise;
|
|
311
302
|
});
|
|
312
303
|
});
|
|
313
304
|
describe(".invalidateObjectType", () => {
|
|
@@ -322,8 +313,8 @@ describe(Store, () => {
|
|
|
322
313
|
const staleEmp = emp.$clone({
|
|
323
314
|
fullName: "stale"
|
|
324
315
|
});
|
|
325
|
-
|
|
326
|
-
|
|
316
|
+
updateList(cache, {
|
|
317
|
+
type: Employee,
|
|
327
318
|
where: {},
|
|
328
319
|
orderBy: {}
|
|
329
320
|
}, [staleEmp]);
|
|
@@ -334,7 +325,7 @@ describe(Store, () => {
|
|
|
334
325
|
expectSingleObjectCallAndClear(subFn, staleEmp);
|
|
335
326
|
const subListFn = mockListSubCallback();
|
|
336
327
|
defer(cache.observeList({
|
|
337
|
-
|
|
328
|
+
type: Employee,
|
|
338
329
|
where: {},
|
|
339
330
|
orderBy: {},
|
|
340
331
|
mode: "offline"
|
|
@@ -346,21 +337,13 @@ describe(Store, () => {
|
|
|
346
337
|
testStage("invalidate");
|
|
347
338
|
const pInvalidateComplete = cache.invalidateObjectType(Employee, undefined);
|
|
348
339
|
await waitForCall(subListFn, 1);
|
|
349
|
-
|
|
350
|
-
resolvedList: [staleEmp],
|
|
340
|
+
expectSingleListCallAndClear(subListFn, [staleEmp], {
|
|
351
341
|
status: "loading"
|
|
352
|
-
})
|
|
353
|
-
subListFn
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}));
|
|
358
|
-
subListFn.mockClear();
|
|
359
|
-
await vi.waitFor(() => expect(subFn).toHaveBeenCalled());
|
|
360
|
-
expect(subFn).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
361
|
-
status: "loaded",
|
|
362
|
-
object: emp
|
|
363
|
-
}));
|
|
342
|
+
});
|
|
343
|
+
await waitForCall(subListFn, 1);
|
|
344
|
+
expectSingleListCallAndClear(subListFn, employeesAsServerReturns);
|
|
345
|
+
await waitForCall(subFn, 1);
|
|
346
|
+
expectSingleObjectCallAndClear(subFn, emp, "loaded");
|
|
364
347
|
|
|
365
348
|
// we don't need this value to control the test but we want to make sure we don't have
|
|
366
349
|
// any unhandled exceptions upon test completion
|
|
@@ -371,8 +354,12 @@ describe(Store, () => {
|
|
|
371
354
|
const subFn1 = mockSingleSubCallback();
|
|
372
355
|
const subFn2 = mockSingleSubCallback();
|
|
373
356
|
beforeEach(async () => {
|
|
374
|
-
subFn1.mockClear();
|
|
375
|
-
|
|
357
|
+
subFn1.complete.mockClear();
|
|
358
|
+
subFn1.next.mockClear();
|
|
359
|
+
subFn1.error.mockClear();
|
|
360
|
+
subFn2.complete.mockClear();
|
|
361
|
+
subFn2.next.mockClear();
|
|
362
|
+
subFn2.error.mockClear();
|
|
376
363
|
});
|
|
377
364
|
const likeEmployee50030 = expect.objectContaining({
|
|
378
365
|
$primaryKey: 50030,
|
|
@@ -382,42 +369,36 @@ describe(Store, () => {
|
|
|
382
369
|
defer(cache.observeObject(Employee, 50030, {
|
|
383
370
|
mode: "force"
|
|
384
371
|
}, subFn1));
|
|
385
|
-
expect(subFn1).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
372
|
+
expect(subFn1.next).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
386
373
|
status: "loading",
|
|
387
374
|
object: undefined,
|
|
388
375
|
isOptimistic: false
|
|
389
376
|
}));
|
|
390
|
-
subFn1.mockClear();
|
|
391
|
-
await
|
|
392
|
-
expect(subFn1).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
377
|
+
subFn1.next.mockClear();
|
|
378
|
+
await waitForCall(subFn1);
|
|
379
|
+
expect(subFn1.next).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
393
380
|
object: likeEmployee50030,
|
|
394
381
|
isOptimistic: false
|
|
395
382
|
}));
|
|
396
|
-
const firstLoad = subFn1.mock.lastCall?.[0];
|
|
397
|
-
subFn1.mockClear();
|
|
383
|
+
const firstLoad = subFn1.next.mock.lastCall?.[0];
|
|
384
|
+
subFn1.next.mockClear();
|
|
398
385
|
defer(cache.observeObject(Employee, 50030, {
|
|
399
386
|
mode: "force"
|
|
400
387
|
}, subFn2));
|
|
401
|
-
|
|
402
|
-
status: "loading"
|
|
403
|
-
}));
|
|
404
|
-
subFn1.mockClear();
|
|
388
|
+
expectSingleObjectCallAndClear(subFn1, likeEmployee50030, "loading");
|
|
405
389
|
|
|
406
390
|
// should be the earlier results
|
|
407
|
-
|
|
408
|
-
status: "loading"
|
|
409
|
-
}));
|
|
410
|
-
subFn2.mockClear();
|
|
391
|
+
expectSingleObjectCallAndClear(subFn2, likeEmployee50030, "loading");
|
|
411
392
|
|
|
412
393
|
// both will be updated
|
|
413
394
|
for (const s of [subFn1, subFn2]) {
|
|
414
395
|
// wait for the result to come in
|
|
415
|
-
await
|
|
416
|
-
expect(s).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
396
|
+
await waitForCall(s, 1);
|
|
397
|
+
expect(s.next).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
417
398
|
...firstLoad,
|
|
418
399
|
lastUpdated: expect.toBeGreaterThan(firstLoad.lastUpdated)
|
|
419
400
|
}));
|
|
420
|
-
s.mockClear();
|
|
401
|
+
s.next.mockClear();
|
|
421
402
|
}
|
|
422
403
|
});
|
|
423
404
|
});
|
|
@@ -425,68 +406,66 @@ describe(Store, () => {
|
|
|
425
406
|
const subFn = mockSingleSubCallback();
|
|
426
407
|
let sub;
|
|
427
408
|
beforeEach(() => {
|
|
428
|
-
subFn.mockClear();
|
|
409
|
+
subFn.complete.mockClear();
|
|
410
|
+
subFn.next.mockClear();
|
|
411
|
+
subFn.error.mockClear();
|
|
429
412
|
sub = defer(cache.observeObject(Employee, 50030, {
|
|
430
413
|
mode: "offline"
|
|
431
414
|
}, subFn));
|
|
432
|
-
|
|
433
|
-
status: "init",
|
|
434
|
-
object: undefined
|
|
435
|
-
}));
|
|
436
|
-
subFn.mockClear();
|
|
415
|
+
expectSingleObjectCallAndClear(subFn, undefined, "init");
|
|
437
416
|
});
|
|
438
417
|
it("does basic observation and unsubscribe", async () => {
|
|
439
418
|
const emp = employeesAsServerReturns[0];
|
|
440
419
|
|
|
441
420
|
// force an update
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
object: emp
|
|
445
|
-
}));
|
|
446
|
-
subFn.mockClear();
|
|
421
|
+
updateObject(cache, emp);
|
|
422
|
+
expectSingleObjectCallAndClear(subFn, emp);
|
|
447
423
|
|
|
448
424
|
// force again
|
|
449
|
-
|
|
425
|
+
updateObject(cache, emp.$clone({
|
|
450
426
|
fullName: "new name"
|
|
451
427
|
}));
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
fullName: "new name"
|
|
455
|
-
})
|
|
428
|
+
expectSingleObjectCallAndClear(subFn, emp.$clone({
|
|
429
|
+
fullName: "new name"
|
|
456
430
|
}));
|
|
457
|
-
subFn.mockClear();
|
|
458
431
|
sub.unsubscribe();
|
|
459
432
|
|
|
460
433
|
// force again but no subscription update
|
|
461
|
-
|
|
434
|
+
updateObject(cache, emp.$clone({
|
|
462
435
|
fullName: "new name 2"
|
|
463
436
|
}));
|
|
464
|
-
expect(subFn).not.toHaveBeenCalled();
|
|
437
|
+
expect(subFn.next).not.toHaveBeenCalled();
|
|
465
438
|
});
|
|
466
439
|
it("observes with list update", async () => {
|
|
467
440
|
const emp = employeesAsServerReturns[0];
|
|
468
441
|
|
|
469
442
|
// force an update
|
|
470
|
-
|
|
443
|
+
updateObject(cache, emp.$clone({
|
|
471
444
|
fullName: "not the name"
|
|
472
445
|
}));
|
|
473
|
-
expect(subFn).toHaveBeenCalledTimes(1);
|
|
474
|
-
|
|
475
|
-
|
|
446
|
+
expect(subFn.next).toHaveBeenCalledTimes(1);
|
|
447
|
+
updateList(cache, {
|
|
448
|
+
type: Employee,
|
|
476
449
|
where: {},
|
|
477
450
|
orderBy: {}
|
|
478
451
|
}, employeesAsServerReturns);
|
|
479
|
-
expect(subFn).toHaveBeenCalledTimes(2);
|
|
480
|
-
expect(subFn.mock.calls[1][0]).toEqual(objectPayloadContaining({
|
|
452
|
+
expect(subFn.next).toHaveBeenCalledTimes(2);
|
|
453
|
+
expect(subFn.next.mock.calls[1][0]).toEqual(objectPayloadContaining({
|
|
481
454
|
object: emp
|
|
482
455
|
}));
|
|
483
456
|
});
|
|
484
457
|
});
|
|
485
458
|
describe(".observeList", () => {
|
|
486
459
|
const listSub1 = mockListSubCallback();
|
|
460
|
+
const ifaceSub = mockListSubCallback();
|
|
487
461
|
beforeEach(() => {
|
|
488
462
|
vi.useFakeTimers({});
|
|
489
|
-
listSub1.mockReset();
|
|
463
|
+
vi.mocked(listSub1.next).mockReset();
|
|
464
|
+
vi.mocked(listSub1.error).mockReset();
|
|
465
|
+
vi.mocked(listSub1.complete).mockReset();
|
|
466
|
+
vi.mocked(ifaceSub.next).mockReset();
|
|
467
|
+
vi.mocked(ifaceSub.error).mockReset();
|
|
468
|
+
vi.mocked(ifaceSub.complete).mockReset();
|
|
490
469
|
});
|
|
491
470
|
afterEach(() => {
|
|
492
471
|
vi.useRealTimers();
|
|
@@ -494,102 +473,107 @@ describe(Store, () => {
|
|
|
494
473
|
describe("mode=force", () => {
|
|
495
474
|
it("initial load", async () => {
|
|
496
475
|
defer(cache.observeList({
|
|
497
|
-
|
|
498
|
-
where: {},
|
|
476
|
+
type: Employee,
|
|
499
477
|
orderBy: {},
|
|
500
478
|
mode: "force"
|
|
501
479
|
}, listSub1));
|
|
480
|
+
defer(cache.observeList({
|
|
481
|
+
type: FooInterface,
|
|
482
|
+
orderBy: {},
|
|
483
|
+
mode: "force"
|
|
484
|
+
}, ifaceSub));
|
|
502
485
|
vitest.runOnlyPendingTimers();
|
|
503
|
-
await
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
})
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
486
|
+
await waitForCall(listSub1);
|
|
487
|
+
await waitForCall(ifaceSub);
|
|
488
|
+
expectSingleListCallAndClear(listSub1, [], {
|
|
489
|
+
status: "loading"
|
|
490
|
+
});
|
|
491
|
+
expectSingleListCallAndClear(ifaceSub, [], {
|
|
492
|
+
status: "loading"
|
|
493
|
+
});
|
|
494
|
+
await waitForCall(listSub1);
|
|
495
|
+
expectSingleListCallAndClear(listSub1, employeesAsServerReturns, {
|
|
512
496
|
status: "loaded"
|
|
513
|
-
})
|
|
497
|
+
});
|
|
498
|
+
await waitForCall(ifaceSub);
|
|
499
|
+
expectSingleListCallAndClear(ifaceSub, employeesAsServerReturns.filter(o => o.$primaryKey === 50050), {
|
|
500
|
+
status: "loaded"
|
|
501
|
+
});
|
|
502
|
+
expectNoMoreCalls(listSub1);
|
|
503
|
+
expectNoMoreCalls(ifaceSub);
|
|
504
|
+
expect(listSub1.next).not.toHaveBeenCalled();
|
|
505
|
+
expect(listSub1.error).not.toHaveBeenCalled();
|
|
506
|
+
expect(ifaceSub.next).not.toHaveBeenCalled();
|
|
507
|
+
expect(ifaceSub.error).not.toHaveBeenCalled();
|
|
514
508
|
});
|
|
515
509
|
it("subsequent load", async () => {
|
|
516
510
|
// Pre-seed with data the server doesn't return
|
|
517
|
-
|
|
518
|
-
|
|
511
|
+
updateList(cache, {
|
|
512
|
+
type: Employee,
|
|
519
513
|
where: {},
|
|
520
514
|
orderBy: {}
|
|
521
515
|
}, mutatedEmployees);
|
|
522
516
|
defer(cache.observeList({
|
|
523
|
-
|
|
517
|
+
type: Employee,
|
|
524
518
|
mode: "force"
|
|
525
519
|
}, listSub1));
|
|
526
520
|
await waitForCall(listSub1, 1);
|
|
527
|
-
const firstLoad = listSub1.mock.calls[0][0];
|
|
521
|
+
const firstLoad = listSub1.next.mock.calls[0][0];
|
|
528
522
|
expectSingleListCallAndClear(listSub1, mutatedEmployees, {
|
|
529
523
|
status: "loading"
|
|
530
524
|
});
|
|
531
|
-
await
|
|
532
|
-
|
|
533
|
-
resolvedList: employeesAsServerReturns,
|
|
525
|
+
await waitForCall(listSub1, 1);
|
|
526
|
+
expectSingleListCallAndClear(listSub1, employeesAsServerReturns, {
|
|
534
527
|
status: "loaded",
|
|
535
528
|
lastUpdated: expect.toBeGreaterThan(firstLoad.lastUpdated)
|
|
536
|
-
})
|
|
537
|
-
listSub1.mockClear();
|
|
529
|
+
});
|
|
538
530
|
});
|
|
539
531
|
});
|
|
540
532
|
describe("mode = offline", () => {
|
|
541
533
|
it("updates with list updates", async () => {
|
|
542
534
|
defer(cache.observeList({
|
|
543
|
-
|
|
535
|
+
type: Employee,
|
|
544
536
|
where: {},
|
|
545
537
|
orderBy: {},
|
|
546
538
|
mode: "offline"
|
|
547
539
|
}, listSub1));
|
|
548
|
-
expect(listSub1).toHaveBeenCalledTimes(0);
|
|
549
|
-
|
|
550
|
-
|
|
540
|
+
expect(listSub1.next).toHaveBeenCalledTimes(0);
|
|
541
|
+
updateList(cache, {
|
|
542
|
+
type: Employee,
|
|
551
543
|
where: {},
|
|
552
544
|
orderBy: {}
|
|
553
545
|
}, employeesAsServerReturns);
|
|
554
546
|
vitest.runOnlyPendingTimers();
|
|
555
|
-
|
|
556
|
-
resolvedList: employeesAsServerReturns
|
|
557
|
-
}));
|
|
558
|
-
listSub1.mockClear();
|
|
547
|
+
expectSingleListCallAndClear(listSub1, employeesAsServerReturns);
|
|
559
548
|
|
|
560
549
|
// list is just now one object
|
|
561
|
-
|
|
562
|
-
|
|
550
|
+
updateList(cache, {
|
|
551
|
+
type: Employee,
|
|
563
552
|
where: {},
|
|
564
553
|
orderBy: {}
|
|
565
554
|
}, [employeesAsServerReturns[0]]);
|
|
566
555
|
vitest.runOnlyPendingTimers();
|
|
567
|
-
|
|
568
|
-
resolvedList: [employeesAsServerReturns[0]]
|
|
569
|
-
}));
|
|
556
|
+
expectSingleListCallAndClear(listSub1, [employeesAsServerReturns[0]]);
|
|
570
557
|
});
|
|
571
558
|
it("updates with different list updates", async () => {
|
|
572
559
|
defer(cache.observeList({
|
|
573
|
-
|
|
560
|
+
type: Employee,
|
|
574
561
|
where: {},
|
|
575
562
|
orderBy: {},
|
|
576
563
|
mode: "offline"
|
|
577
564
|
}, listSub1));
|
|
578
|
-
expect(listSub1).toHaveBeenCalledTimes(0);
|
|
579
|
-
|
|
580
|
-
|
|
565
|
+
expect(listSub1.next).toHaveBeenCalledTimes(0);
|
|
566
|
+
updateList(cache, {
|
|
567
|
+
type: Employee,
|
|
581
568
|
where: {},
|
|
582
569
|
orderBy: {}
|
|
583
570
|
}, employeesAsServerReturns);
|
|
584
571
|
vitest.runOnlyPendingTimers();
|
|
585
|
-
|
|
586
|
-
resolvedList: employeesAsServerReturns
|
|
587
|
-
}));
|
|
588
|
-
listSub1.mockClear();
|
|
572
|
+
expectSingleListCallAndClear(listSub1, employeesAsServerReturns);
|
|
589
573
|
|
|
590
574
|
// new where === different list
|
|
591
|
-
|
|
592
|
-
|
|
575
|
+
updateList(cache, {
|
|
576
|
+
type: Employee,
|
|
593
577
|
where: {
|
|
594
578
|
employeeId: {
|
|
595
579
|
$gt: 0
|
|
@@ -600,9 +584,7 @@ describe(Store, () => {
|
|
|
600
584
|
vitest.runOnlyPendingTimers();
|
|
601
585
|
|
|
602
586
|
// original list updates still
|
|
603
|
-
|
|
604
|
-
resolvedList: mutatedEmployees
|
|
605
|
-
}));
|
|
587
|
+
expectSingleListCallAndClear(listSub1, mutatedEmployees);
|
|
606
588
|
});
|
|
607
589
|
});
|
|
608
590
|
});
|
|
@@ -616,39 +598,33 @@ describe(Store, () => {
|
|
|
616
598
|
it("works in the solo case", async () => {
|
|
617
599
|
const listSub = mockListSubCallback();
|
|
618
600
|
defer(cache.observeList({
|
|
619
|
-
|
|
601
|
+
type: Employee,
|
|
620
602
|
where: {},
|
|
621
603
|
orderBy: {},
|
|
622
604
|
mode: "force",
|
|
623
605
|
pageSize: 1
|
|
624
606
|
}, listSub));
|
|
625
|
-
expect(listSub).not.toHaveBeenCalled();
|
|
626
|
-
await
|
|
627
|
-
|
|
607
|
+
expect(listSub.next).not.toHaveBeenCalled();
|
|
608
|
+
await waitForCall(listSub, 1);
|
|
609
|
+
expectSingleListCallAndClear(listSub, [], {
|
|
628
610
|
status: "loading"
|
|
629
|
-
})
|
|
630
|
-
listSub
|
|
631
|
-
await vi.waitFor(() => expect(listSub).toHaveBeenCalled());
|
|
632
|
-
expect(listSub).toHaveBeenCalledExactlyOnceWith(listPayloadContaining({
|
|
633
|
-
resolvedList: employeesAsServerReturns.slice(0, 1),
|
|
634
|
-
status: "loaded"
|
|
635
|
-
}));
|
|
611
|
+
});
|
|
612
|
+
await waitForCall(listSub, 1);
|
|
636
613
|
const {
|
|
637
614
|
fetchMore
|
|
638
|
-
} = listSub.mock.calls[0][0];
|
|
639
|
-
listSub.
|
|
615
|
+
} = listSub.next.mock.calls[0][0];
|
|
616
|
+
expectSingleListCallAndClear(listSub, employeesAsServerReturns.slice(0, 1), {
|
|
617
|
+
status: "loaded"
|
|
618
|
+
});
|
|
640
619
|
void fetchMore();
|
|
641
|
-
await
|
|
642
|
-
|
|
643
|
-
resolvedList: employeesAsServerReturns.slice(0, 1),
|
|
620
|
+
await waitForCall(listSub, 1);
|
|
621
|
+
expectSingleListCallAndClear(listSub, employeesAsServerReturns.slice(0, 1), {
|
|
644
622
|
status: "loading"
|
|
645
|
-
})
|
|
646
|
-
listSub
|
|
647
|
-
|
|
648
|
-
expect(listSub).toHaveBeenCalledWith(listPayloadContaining({
|
|
649
|
-
resolvedList: employeesAsServerReturns.slice(0, 2),
|
|
623
|
+
});
|
|
624
|
+
await waitForCall(listSub, 1);
|
|
625
|
+
expectSingleListCallAndClear(listSub, employeesAsServerReturns.slice(0, 2), {
|
|
650
626
|
status: "loaded"
|
|
651
|
-
})
|
|
627
|
+
});
|
|
652
628
|
});
|
|
653
629
|
});
|
|
654
630
|
});
|
|
@@ -661,11 +637,25 @@ describe(Store, () => {
|
|
|
661
637
|
client = mockClient.client;
|
|
662
638
|
store = new Store(client);
|
|
663
639
|
});
|
|
640
|
+
it("properly fires error handler for a list", async () => {
|
|
641
|
+
const error = new Error("A faux error");
|
|
642
|
+
mockClient.mockFetchPageOnce().reject(error);
|
|
643
|
+
const sub = mockListSubCallback();
|
|
644
|
+
store.observeList({
|
|
645
|
+
type: Employee,
|
|
646
|
+
where: {},
|
|
647
|
+
orderBy: {}
|
|
648
|
+
}, sub);
|
|
649
|
+
await waitForCall(sub.error, 1);
|
|
650
|
+
expect(sub.error).toHaveBeenCalled();
|
|
651
|
+
expect(sub.next).not.toHaveBeenCalled();
|
|
652
|
+
});
|
|
664
653
|
describe("actions", () => {
|
|
665
654
|
it("properly invalidates objects", async () => {
|
|
666
655
|
// after the below `observeObject`, the cache will need to load from the server
|
|
667
656
|
mockClient.mockFetchOneOnce().resolve({
|
|
668
|
-
$apiName: "Todo"
|
|
657
|
+
$apiName: "Todo",
|
|
658
|
+
$primaryKey: 0
|
|
669
659
|
});
|
|
670
660
|
const todoSubFn = mockSingleSubCallback();
|
|
671
661
|
defer(store.observeObject(Todo, 0, {}, todoSubFn));
|
|
@@ -690,15 +680,14 @@ describe(Store, () => {
|
|
|
690
680
|
});
|
|
691
681
|
|
|
692
682
|
// after we apply the action, the object is invalidated and gets re-requested
|
|
693
|
-
|
|
694
683
|
mockClient.mockFetchOneOnce().resolve({
|
|
684
|
+
$primaryKey: 0,
|
|
695
685
|
$apiName: "Todo",
|
|
696
686
|
text: "hello there kind sir"
|
|
697
687
|
});
|
|
698
|
-
|
|
688
|
+
await store.applyAction(createOffice, {
|
|
699
689
|
officeId: "whatever"
|
|
700
690
|
});
|
|
701
|
-
await actionPromise;
|
|
702
691
|
await todoSubFn.expectLoadingAndLoaded({
|
|
703
692
|
loading: objectPayloadContaining({
|
|
704
693
|
status: "loading"
|
|
@@ -748,7 +737,7 @@ describe(Store, () => {
|
|
|
748
737
|
}
|
|
749
738
|
});
|
|
750
739
|
await waitForCall(todoSubFn, 1);
|
|
751
|
-
expect(todoSubFn).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
740
|
+
expect(todoSubFn.next).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
752
741
|
object: {
|
|
753
742
|
...fauxObject,
|
|
754
743
|
text: "optimistic"
|
|
@@ -756,7 +745,7 @@ describe(Store, () => {
|
|
|
756
745
|
status: "loading",
|
|
757
746
|
isOptimistic: true
|
|
758
747
|
}));
|
|
759
|
-
todoSubFn.mockClear();
|
|
748
|
+
todoSubFn.next.mockClear();
|
|
760
749
|
|
|
761
750
|
// let the action error out
|
|
762
751
|
applyActionResult.reject("an error thrown");
|
|
@@ -764,43 +753,72 @@ describe(Store, () => {
|
|
|
764
753
|
|
|
765
754
|
// back to the original object
|
|
766
755
|
await waitForCall(todoSubFn, 1);
|
|
767
|
-
expect(todoSubFn).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
756
|
+
expect(todoSubFn.next).toHaveBeenCalledExactlyOnceWith(objectPayloadContaining({
|
|
768
757
|
object: fauxObject,
|
|
769
758
|
status: "loaded",
|
|
770
759
|
isOptimistic: false
|
|
771
760
|
}));
|
|
772
761
|
});
|
|
773
762
|
});
|
|
774
|
-
describe("orderBy", () => {
|
|
763
|
+
describe("orderBy", async () => {
|
|
764
|
+
const ontologyProvider = {
|
|
765
|
+
getObjectDefinition: async () => {
|
|
766
|
+
return {
|
|
767
|
+
...wireObjectTypeFullMetadataToSdkObjectMetadata(stubData.todoWithLinkTypes, true),
|
|
768
|
+
[InterfaceDefinitions]: {}
|
|
769
|
+
};
|
|
770
|
+
},
|
|
771
|
+
getActionDefinition() {
|
|
772
|
+
throw new Error("not implemented");
|
|
773
|
+
},
|
|
774
|
+
getInterfaceDefinition() {
|
|
775
|
+
throw new Error("not implemented");
|
|
776
|
+
},
|
|
777
|
+
getQueryDefinition() {
|
|
778
|
+
throw new Error("not implemented");
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
const minimalClient = createMinimalClient({
|
|
782
|
+
ontologyRid: "ri.whatever"
|
|
783
|
+
}, "https://localhost:8080", () => Promise.resolve("token"), {
|
|
784
|
+
logger
|
|
785
|
+
}, fetch, createObjectSet, () => () => ontologyProvider);
|
|
786
|
+
async function createObject(type, x) {
|
|
787
|
+
return (await minimalClient.objectFactory2(minimalClient, [{
|
|
788
|
+
...x,
|
|
789
|
+
$apiName: type.apiName,
|
|
790
|
+
$objectType: type.apiName,
|
|
791
|
+
$objectSpecifier: `${type.apiName}:${x.$primaryKey}`
|
|
792
|
+
}], undefined))[0];
|
|
793
|
+
}
|
|
775
794
|
let nextPk = 0;
|
|
776
|
-
const fauxObjectA = {
|
|
777
|
-
$
|
|
778
|
-
$objectType: "Todo",
|
|
779
|
-
$primaryKey: nextPk++,
|
|
795
|
+
const fauxObjectA = await createObject(Todo, {
|
|
796
|
+
$primaryKey: nextPk,
|
|
780
797
|
$title: "a",
|
|
798
|
+
id: nextPk,
|
|
781
799
|
text: "a"
|
|
782
|
-
};
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
$
|
|
786
|
-
$primaryKey: nextPk++,
|
|
800
|
+
});
|
|
801
|
+
nextPk++;
|
|
802
|
+
const fauxObjectB = await createObject(Todo, {
|
|
803
|
+
$primaryKey: nextPk,
|
|
787
804
|
$title: "b",
|
|
805
|
+
id: nextPk,
|
|
788
806
|
text: "b"
|
|
789
|
-
};
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
$
|
|
793
|
-
$primaryKey: nextPk++,
|
|
807
|
+
});
|
|
808
|
+
nextPk++;
|
|
809
|
+
const fauxObjectC = await createObject(Todo, {
|
|
810
|
+
$primaryKey: nextPk,
|
|
794
811
|
$title: "c",
|
|
812
|
+
id: nextPk,
|
|
795
813
|
text: "c"
|
|
796
|
-
};
|
|
814
|
+
});
|
|
797
815
|
const noWhereNoOrderBy = {
|
|
798
|
-
|
|
816
|
+
type: Todo,
|
|
799
817
|
where: {},
|
|
800
818
|
orderBy: {}
|
|
801
819
|
};
|
|
802
820
|
const noWhereOrderByText = {
|
|
803
|
-
|
|
821
|
+
type: Todo,
|
|
804
822
|
where: {},
|
|
805
823
|
orderBy: {
|
|
806
824
|
text: "asc"
|
|
@@ -813,16 +831,16 @@ describe(Store, () => {
|
|
|
813
831
|
...noWhereNoOrderBy,
|
|
814
832
|
mode: "offline"
|
|
815
833
|
}, subListUnordered));
|
|
816
|
-
expect(subListUnordered).toHaveBeenCalledTimes(0);
|
|
834
|
+
expect(subListUnordered.next).toHaveBeenCalledTimes(0);
|
|
817
835
|
defer(store.observeList({
|
|
818
836
|
...noWhereOrderByText,
|
|
819
837
|
mode: "offline"
|
|
820
838
|
}, subListOrdered));
|
|
821
|
-
expect(subListOrdered).toHaveBeenCalledTimes(0);
|
|
839
|
+
expect(subListOrdered.next).toHaveBeenCalledTimes(0);
|
|
822
840
|
});
|
|
823
841
|
it("invalidates the correct lists", async () => {
|
|
824
842
|
// for whatever reason, the first list is loaded as [B, A]
|
|
825
|
-
|
|
843
|
+
updateList(store, noWhereNoOrderBy, [fauxObjectB, fauxObjectA]);
|
|
826
844
|
await waitForCall(subListUnordered, 1);
|
|
827
845
|
expectSingleListCallAndClear(subListUnordered, [fauxObjectB, fauxObjectA]);
|
|
828
846
|
|
|
@@ -833,8 +851,8 @@ describe(Store, () => {
|
|
|
833
851
|
|
|
834
852
|
// For whatever reason, object B is no longer in the first set (use your imagination)
|
|
835
853
|
// but we have added a C before A. So the first list is [C, A]
|
|
836
|
-
|
|
837
|
-
|
|
854
|
+
updateList(store, {
|
|
855
|
+
type: Todo,
|
|
838
856
|
where: {},
|
|
839
857
|
orderBy: {}
|
|
840
858
|
}, [fauxObjectC, fauxObjectA]);
|
|
@@ -847,22 +865,20 @@ describe(Store, () => {
|
|
|
847
865
|
expectSingleListCallAndClear(subListOrdered, [fauxObjectA, fauxObjectB, fauxObjectC]);
|
|
848
866
|
});
|
|
849
867
|
it("produces proper results with optimistic updates and successful action", async () => {
|
|
850
|
-
const optimisticallyMutatedA = {
|
|
868
|
+
const optimisticallyMutatedA = await createObject(Todo, {
|
|
851
869
|
...fauxObjectA,
|
|
852
870
|
text: "optimistic"
|
|
853
|
-
};
|
|
871
|
+
});
|
|
854
872
|
const pkForOptimistic = nextPk++;
|
|
855
|
-
const optimisticallyCreatedObjectD = {
|
|
856
|
-
"$apiName": "Todo",
|
|
857
|
-
"$objectType": "Todo",
|
|
873
|
+
const optimisticallyCreatedObjectD = await createObject(Todo, {
|
|
858
874
|
"$primaryKey": pkForOptimistic,
|
|
859
875
|
"$title": "d",
|
|
860
876
|
"text": "d",
|
|
861
877
|
id: pkForOptimistic
|
|
862
|
-
};
|
|
878
|
+
});
|
|
863
879
|
|
|
864
880
|
// for whatever reason, the first list is loaded as [B, A]
|
|
865
|
-
|
|
881
|
+
updateList(store, noWhereNoOrderBy, [fauxObjectB, fauxObjectA]);
|
|
866
882
|
await waitForCall(subListUnordered, 1);
|
|
867
883
|
expectSingleListCallAndClear(subListUnordered, [fauxObjectB, fauxObjectA]);
|
|
868
884
|
|
|
@@ -917,32 +933,28 @@ describe(Store, () => {
|
|
|
917
933
|
// I think these are named backwards
|
|
918
934
|
it("produces proper results with optimistic updates and rollback", async () => {
|
|
919
935
|
const pkForOptimistic = nextPk++;
|
|
920
|
-
const optimisticallyCreatedObjectD = {
|
|
921
|
-
"$apiName": "Todo",
|
|
922
|
-
"$objectType": "Todo",
|
|
936
|
+
const optimisticallyCreatedObjectD = await createObject(Todo, {
|
|
923
937
|
"$primaryKey": pkForOptimistic,
|
|
924
938
|
"$title": "d",
|
|
925
939
|
"text": "d",
|
|
926
940
|
id: pkForOptimistic
|
|
927
|
-
};
|
|
928
|
-
const optimisticallyMutatedA = {
|
|
941
|
+
});
|
|
942
|
+
const optimisticallyMutatedA = await createObject(Todo, {
|
|
929
943
|
...fauxObjectA,
|
|
930
944
|
text: "optimistic"
|
|
931
|
-
};
|
|
945
|
+
});
|
|
932
946
|
testStage("Initial Setup");
|
|
933
947
|
|
|
934
948
|
// later we will "create" this object
|
|
935
|
-
const createdObjectD = {
|
|
936
|
-
"$apiName": "Todo",
|
|
937
|
-
"$objectType": "Todo",
|
|
949
|
+
const createdObjectD = await createObject(Todo, {
|
|
938
950
|
"$primaryKey": 9000,
|
|
939
951
|
"$title": "d prime",
|
|
940
952
|
"text": "d prime",
|
|
941
953
|
id: 9000
|
|
942
|
-
};
|
|
954
|
+
});
|
|
943
955
|
|
|
944
956
|
// for whatever reason, the first list is loaded as [B, A]
|
|
945
|
-
|
|
957
|
+
updateList(store, noWhereNoOrderBy, [fauxObjectB, fauxObjectA]);
|
|
946
958
|
await waitForCall(subListUnordered, 1);
|
|
947
959
|
expectSingleListCallAndClear(subListUnordered, [fauxObjectB, fauxObjectA]);
|
|
948
960
|
|
|
@@ -961,8 +973,8 @@ describe(Store, () => {
|
|
|
961
973
|
officeId: "5"
|
|
962
974
|
}, {
|
|
963
975
|
optimisticUpdate: b => {
|
|
964
|
-
b.createObject(Todo,
|
|
965
|
-
id:
|
|
976
|
+
b.createObject(Todo, optimisticallyCreatedObjectD.$primaryKey, {
|
|
977
|
+
id: optimisticallyCreatedObjectD.$primaryKey,
|
|
966
978
|
text: "d"
|
|
967
979
|
});
|
|
968
980
|
b.updateObject(optimisticallyMutatedA);
|
|
@@ -978,16 +990,20 @@ describe(Store, () => {
|
|
|
978
990
|
isOptimistic: true
|
|
979
991
|
});
|
|
980
992
|
|
|
981
|
-
// the second list is now [
|
|
993
|
+
// the second list is now [B, optimistic, optimistic a]
|
|
982
994
|
await waitForCall(subListOrdered, 1);
|
|
983
995
|
expectSingleListCallAndClear(subListOrdered, [fauxObjectB, optimisticallyCreatedObjectD, optimisticallyMutatedA], {
|
|
984
996
|
isOptimistic: true
|
|
985
997
|
});
|
|
986
998
|
testStage("Resolve Action");
|
|
987
|
-
const modifiedObjectA = {
|
|
999
|
+
const modifiedObjectA = await createObject(Todo, {
|
|
988
1000
|
...fauxObjectA,
|
|
989
1001
|
text: "a prime"
|
|
990
|
-
};
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
// console.log("winner?", modifiedObjectA.$as);
|
|
1005
|
+
// // throw "hi";
|
|
1006
|
+
// console.log("winner2?", modifiedObjectA.$as("Todo").$as);
|
|
991
1007
|
|
|
992
1008
|
// The action will complete and then revalidate in order...
|
|
993
1009
|
mockClient.mockFetchOneOnce(modifiedObjectA.$primaryKey).resolve(modifiedObjectA);
|
|
@@ -1023,6 +1039,7 @@ describe(Store, () => {
|
|
|
1023
1039
|
|
|
1024
1040
|
await actionPromise;
|
|
1025
1041
|
await waitForCall(subListUnordered, 1);
|
|
1042
|
+
console.log("=====", subListUnordered.next.mock.calls[0][0]);
|
|
1026
1043
|
expectSingleListCallAndClear(subListUnordered, [fauxObjectB,
|
|
1027
1044
|
// fauxObjectC,
|
|
1028
1045
|
modifiedObjectA, createdObjectD], {
|
|
@@ -1049,12 +1066,12 @@ describe(Store, () => {
|
|
|
1049
1066
|
|
|
1050
1067
|
// set the truth
|
|
1051
1068
|
for (const obj of baseObjects) {
|
|
1052
|
-
|
|
1069
|
+
updateObject(store, obj);
|
|
1053
1070
|
}
|
|
1054
1071
|
|
|
1055
1072
|
// expect the truth
|
|
1056
1073
|
for (const obj of baseObjects) {
|
|
1057
|
-
expect(
|
|
1074
|
+
expect(getObject(store, "Employee", obj.$primaryKey)).toEqual(expect.objectContaining({
|
|
1058
1075
|
$title: `truth ${obj.$primaryKey}`
|
|
1059
1076
|
}));
|
|
1060
1077
|
}
|
|
@@ -1074,7 +1091,7 @@ describe(Store, () => {
|
|
|
1074
1091
|
|
|
1075
1092
|
// expect the optimistic values
|
|
1076
1093
|
for (let i = 0; i < 2; i++) {
|
|
1077
|
-
expect(
|
|
1094
|
+
expect(getObject(store, "Employee", baseObjects[i].$primaryKey)).toEqual(expect.objectContaining({
|
|
1078
1095
|
$title: `optimistic ${baseObjects[i].$primaryKey}`
|
|
1079
1096
|
}));
|
|
1080
1097
|
}
|
|
@@ -1083,10 +1100,10 @@ describe(Store, () => {
|
|
|
1083
1100
|
store.removeLayer(layerIds[0]);
|
|
1084
1101
|
|
|
1085
1102
|
// should have truth object 1 and optimistic object 2
|
|
1086
|
-
expect(
|
|
1103
|
+
expect(getObject(store, "Employee", 1)).toEqual(expect.objectContaining({
|
|
1087
1104
|
$title: "truth 1"
|
|
1088
1105
|
}));
|
|
1089
|
-
expect(
|
|
1106
|
+
expect(getObject(store, "Employee", 2)).toEqual(expect.objectContaining({
|
|
1090
1107
|
$title: "optimistic 2"
|
|
1091
1108
|
}));
|
|
1092
1109
|
|
|
@@ -1095,7 +1112,7 @@ describe(Store, () => {
|
|
|
1095
1112
|
|
|
1096
1113
|
// should have truth objects
|
|
1097
1114
|
for (const obj of baseObjects) {
|
|
1098
|
-
expect(
|
|
1115
|
+
expect(getObject(store, "Employee", obj.$primaryKey)).toEqual(expect.objectContaining({
|
|
1099
1116
|
$title: `truth ${obj.$primaryKey}`
|
|
1100
1117
|
}));
|
|
1101
1118
|
}
|