jazz-tools 0.19.7 → 0.19.10
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/.turbo/turbo-build.log +65 -59
- package/CHANGELOG.md +34 -3
- package/dist/{chunk-CUS6O5NE.js → chunk-FFEEPZEG.js} +454 -122
- package/dist/chunk-FFEEPZEG.js.map +1 -0
- package/dist/expo/polyfills.js +22 -0
- package/dist/expo/polyfills.js.map +1 -0
- package/dist/index.js +26 -6
- package/dist/index.js.map +1 -1
- package/dist/react/hooks.d.ts +1 -1
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +5 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react-core/hooks.d.ts +59 -0
- package/dist/react-core/hooks.d.ts.map +1 -1
- package/dist/react-core/index.js +133 -34
- package/dist/react-core/index.js.map +1 -1
- package/dist/react-core/tests/testUtils.d.ts +1 -0
- package/dist/react-core/tests/testUtils.d.ts.map +1 -1
- package/dist/react-core/tests/useSuspenseAccount.test.d.ts +2 -0
- package/dist/react-core/tests/useSuspenseAccount.test.d.ts.map +1 -0
- package/dist/react-core/tests/useSuspenseCoState.test.d.ts +2 -0
- package/dist/react-core/tests/useSuspenseCoState.test.d.ts.map +1 -0
- package/dist/react-core/use.d.ts +3 -0
- package/dist/react-core/use.d.ts.map +1 -0
- package/dist/react-native/index.d.ts +1 -1
- package/dist/react-native/index.d.ts.map +1 -1
- package/dist/react-native/index.js +717 -9
- package/dist/react-native/index.js.map +1 -1
- package/dist/react-native/polyfills.js +22 -0
- package/dist/react-native/polyfills.js.map +1 -0
- package/dist/react-native-core/crypto/RNCrypto.d.ts +2 -0
- package/dist/react-native-core/crypto/RNCrypto.d.ts.map +1 -0
- package/dist/react-native-core/crypto/RNCrypto.js +3 -0
- package/dist/react-native-core/crypto/RNCrypto.js.map +1 -0
- package/dist/react-native-core/hooks.d.ts +1 -1
- package/dist/react-native-core/hooks.d.ts.map +1 -1
- package/dist/react-native-core/index.d.ts.map +1 -1
- package/dist/react-native-core/index.js +5 -1
- package/dist/react-native-core/index.js.map +1 -1
- package/dist/react-native-core/platform.d.ts +2 -1
- package/dist/react-native-core/platform.d.ts.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/tools/coValues/account.d.ts +3 -3
- package/dist/tools/coValues/account.d.ts.map +1 -1
- package/dist/tools/coValues/coFeed.d.ts +3 -3
- package/dist/tools/coValues/coFeed.d.ts.map +1 -1
- package/dist/tools/coValues/coList.d.ts +4 -4
- package/dist/tools/coValues/coList.d.ts.map +1 -1
- package/dist/tools/coValues/coMap.d.ts +7 -7
- package/dist/tools/coValues/coMap.d.ts.map +1 -1
- package/dist/tools/coValues/coPlainText.d.ts +2 -2
- package/dist/tools/coValues/coPlainText.d.ts.map +1 -1
- package/dist/tools/coValues/coVector.d.ts +2 -2
- package/dist/tools/coValues/coVector.d.ts.map +1 -1
- package/dist/tools/coValues/deepLoading.d.ts +24 -0
- package/dist/tools/coValues/deepLoading.d.ts.map +1 -1
- package/dist/tools/coValues/group.d.ts +2 -2
- package/dist/tools/coValues/group.d.ts.map +1 -1
- package/dist/tools/coValues/interfaces.d.ts +7 -7
- package/dist/tools/coValues/interfaces.d.ts.map +1 -1
- package/dist/tools/coValues/schemaUnion.d.ts +2 -2
- package/dist/tools/coValues/schemaUnion.d.ts.map +1 -1
- package/dist/tools/config.d.ts +3 -0
- package/dist/tools/config.d.ts.map +1 -0
- package/dist/tools/exports.d.ts +2 -0
- package/dist/tools/exports.d.ts.map +1 -1
- package/dist/tools/implementation/ContextManager.d.ts +3 -0
- package/dist/tools/implementation/ContextManager.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +2 -2
- package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.d.ts +2 -2
- package/dist/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts +2 -2
- package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts +4 -4
- package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +4 -4
- package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts +4 -4
- package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +2 -2
- package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/GroupSchema.d.ts +2 -2
- package/dist/tools/implementation/zodSchema/schemaTypes/GroupSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts +2 -2
- package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts +2 -2
- package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
- package/dist/tools/subscribe/CoValueCoreSubscription.d.ts +8 -22
- package/dist/tools/subscribe/CoValueCoreSubscription.d.ts.map +1 -1
- package/dist/tools/subscribe/JazzError.d.ts.map +1 -1
- package/dist/tools/subscribe/SubscriptionCache.d.ts +51 -0
- package/dist/tools/subscribe/SubscriptionCache.d.ts.map +1 -0
- package/dist/tools/subscribe/SubscriptionScope.d.ts +27 -2
- package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
- package/dist/tools/subscribe/errorReporting.d.ts +31 -0
- package/dist/tools/subscribe/errorReporting.d.ts.map +1 -0
- package/dist/tools/subscribe/utils.d.ts +9 -1
- package/dist/tools/subscribe/utils.d.ts.map +1 -1
- package/dist/tools/testing.d.ts +2 -2
- package/dist/tools/testing.d.ts.map +1 -1
- package/dist/tools/tests/SubscriptionCache.test.d.ts +2 -0
- package/dist/tools/tests/SubscriptionCache.test.d.ts.map +1 -0
- package/dist/tools/tests/errorReporting.test.d.ts +2 -0
- package/dist/tools/tests/errorReporting.test.d.ts.map +1 -0
- package/package.json +22 -7
- package/src/react/hooks.tsx +2 -0
- package/src/react/index.ts +1 -14
- package/src/react-core/hooks.ts +181 -16
- package/src/react-core/tests/createCoValueSubscriptionContext.test.tsx +18 -8
- package/src/react-core/tests/testUtils.tsx +67 -5
- package/src/react-core/tests/useCoState.test.ts +3 -7
- package/src/react-core/tests/useSubscriptionSelector.test.ts +3 -7
- package/src/react-core/tests/useSuspenseAccount.test.tsx +343 -0
- package/src/react-core/tests/useSuspenseCoState.test.tsx +1182 -0
- package/src/react-core/use.ts +46 -0
- package/src/react-native/index.ts +1 -1
- package/src/react-native-core/crypto/RNCrypto.ts +1 -0
- package/src/react-native-core/hooks.tsx +2 -0
- package/src/react-native-core/index.ts +2 -0
- package/src/react-native-core/platform.ts +2 -1
- package/src/react-native-core/polyfills/index.js +28 -0
- package/src/tools/coValues/account.ts +3 -4
- package/src/tools/coValues/coFeed.ts +3 -2
- package/src/tools/coValues/coList.ts +4 -4
- package/src/tools/coValues/coMap.ts +4 -4
- package/src/tools/coValues/coPlainText.ts +2 -2
- package/src/tools/coValues/coVector.ts +2 -2
- package/src/tools/coValues/deepLoading.ts +31 -0
- package/src/tools/coValues/group.ts +2 -2
- package/src/tools/coValues/interfaces.ts +21 -26
- package/src/tools/coValues/schemaUnion.ts +2 -2
- package/src/tools/config.ts +9 -0
- package/src/tools/exports.ts +4 -0
- package/src/tools/implementation/ContextManager.ts +13 -0
- package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +2 -2
- package/src/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.ts +2 -2
- package/src/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.ts +2 -2
- package/src/tools/implementation/zodSchema/schemaTypes/CoListSchema.ts +4 -4
- package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +4 -4
- package/src/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.ts +4 -10
- package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +2 -2
- package/src/tools/implementation/zodSchema/schemaTypes/GroupSchema.ts +2 -2
- package/src/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.ts +2 -2
- package/src/tools/implementation/zodSchema/schemaTypes/RichTextSchema.ts +2 -2
- package/src/tools/subscribe/CoValueCoreSubscription.ts +71 -100
- package/src/tools/subscribe/JazzError.ts +9 -6
- package/src/tools/subscribe/SubscriptionCache.ts +272 -0
- package/src/tools/subscribe/SubscriptionScope.ts +218 -29
- package/src/tools/subscribe/errorReporting.ts +67 -0
- package/src/tools/subscribe/utils.ts +77 -0
- package/src/tools/testing.ts +0 -3
- package/src/tools/tests/CoValueCoreSubscription.test.ts +46 -12
- package/src/tools/tests/ContextManager.test.ts +85 -0
- package/src/tools/tests/SubscriptionCache.test.ts +237 -0
- package/src/tools/tests/coMap.test.ts +5 -7
- package/src/tools/tests/deepLoading.test.ts +47 -47
- package/src/tools/tests/errorReporting.test.ts +103 -0
- package/src/tools/tests/load.test.ts +21 -1
- package/src/tools/tests/request.test.ts +2 -1
- package/src/tools/tests/subscribe.test.ts +44 -0
- package/tsup.config.ts +17 -0
- package/dist/chunk-CUS6O5NE.js.map +0 -1
|
@@ -164,15 +164,31 @@ describe("createCoValueSubscriptionContext", () => {
|
|
|
164
164
|
});
|
|
165
165
|
|
|
166
166
|
it("the provider shows a loading fallback when loading the coValue", async () => {
|
|
167
|
+
await setupJazzTestSync({
|
|
168
|
+
asyncPeers: true,
|
|
169
|
+
});
|
|
170
|
+
|
|
167
171
|
const TestMap = co.map({
|
|
168
172
|
value: z.string(),
|
|
169
173
|
});
|
|
170
174
|
|
|
175
|
+
const map = TestMap.create({
|
|
176
|
+
value: "123",
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
await createJazzTestAccount({
|
|
180
|
+
isCurrentActiveAccount: true,
|
|
181
|
+
});
|
|
182
|
+
|
|
171
183
|
const { Provider } = createCoValueSubscriptionContext(TestMap);
|
|
172
184
|
|
|
173
185
|
const { container } = render(
|
|
174
186
|
<JazzTestProvider>
|
|
175
|
-
<Provider
|
|
187
|
+
<Provider
|
|
188
|
+
id={map.$jazz.id}
|
|
189
|
+
loadingFallback={<div>Loading...</div>}
|
|
190
|
+
unavailableFallback={<div>Unavailable</div>}
|
|
191
|
+
>
|
|
176
192
|
<div>Children should not render</div>
|
|
177
193
|
</Provider>
|
|
178
194
|
</JazzTestProvider>,
|
|
@@ -203,13 +219,7 @@ describe("createCoValueSubscriptionContext", () => {
|
|
|
203
219
|
</JazzTestProvider>,
|
|
204
220
|
);
|
|
205
221
|
|
|
206
|
-
|
|
207
|
-
expect(container.textContent).toContain("Loading...");
|
|
208
|
-
|
|
209
|
-
// Should show unavailable fallback after CoValue load timeout
|
|
210
|
-
await waitFor(() => {
|
|
211
|
-
expect(container.textContent).toContain("Unavailable");
|
|
212
|
-
});
|
|
222
|
+
expect(container.textContent).toContain("Unavailable");
|
|
213
223
|
|
|
214
224
|
// Children should never be rendered
|
|
215
225
|
expect(container.textContent).not.toContain("Children should not render");
|
|
@@ -4,9 +4,12 @@ import {
|
|
|
4
4
|
render,
|
|
5
5
|
renderHook,
|
|
6
6
|
} from "@testing-library/react";
|
|
7
|
-
import { Account, AnonymousJazzAgent
|
|
7
|
+
import { Account, AnonymousJazzAgent } from "jazz-tools";
|
|
8
8
|
import React from "react";
|
|
9
9
|
import { JazzTestProvider } from "../testing.js";
|
|
10
|
+
import { getSqliteStorageAsync, SQLiteDatabaseDriverAsync } from "cojson";
|
|
11
|
+
import Database, { type Database as DatabaseT } from "libsql";
|
|
12
|
+
import { onTestFinished } from "vitest";
|
|
10
13
|
|
|
11
14
|
type JazzExtendedOptions = {
|
|
12
15
|
account?: Account | { guest: AnonymousJazzAgent };
|
|
@@ -23,12 +26,16 @@ const customRender = (
|
|
|
23
26
|
account={options.account}
|
|
24
27
|
isAuthenticated={options.isAuthenticated}
|
|
25
28
|
>
|
|
26
|
-
{
|
|
29
|
+
{options.wrapper ? (
|
|
30
|
+
<options.wrapper>{children}</options.wrapper>
|
|
31
|
+
) : (
|
|
32
|
+
children
|
|
33
|
+
)}
|
|
27
34
|
</JazzTestProvider>
|
|
28
35
|
);
|
|
29
36
|
};
|
|
30
37
|
|
|
31
|
-
return render(ui, { wrapper: AllTheProviders
|
|
38
|
+
return render(ui, { ...options, wrapper: AllTheProviders });
|
|
32
39
|
};
|
|
33
40
|
|
|
34
41
|
const customRenderHook = <TProps, TResult>(
|
|
@@ -41,12 +48,16 @@ const customRenderHook = <TProps, TResult>(
|
|
|
41
48
|
account={options.account}
|
|
42
49
|
isAuthenticated={options.isAuthenticated}
|
|
43
50
|
>
|
|
44
|
-
{
|
|
51
|
+
{options.wrapper ? (
|
|
52
|
+
<options.wrapper>{children}</options.wrapper>
|
|
53
|
+
) : (
|
|
54
|
+
children
|
|
55
|
+
)}
|
|
45
56
|
</JazzTestProvider>
|
|
46
57
|
);
|
|
47
58
|
};
|
|
48
59
|
|
|
49
|
-
return renderHook(callback, { wrapper: AllTheProviders
|
|
60
|
+
return renderHook(callback, { ...options, wrapper: AllTheProviders });
|
|
50
61
|
};
|
|
51
62
|
|
|
52
63
|
// re-export everything
|
|
@@ -55,3 +66,54 @@ export * from "@testing-library/react";
|
|
|
55
66
|
// override render method
|
|
56
67
|
export { customRender as render };
|
|
57
68
|
export { customRenderHook as renderHook };
|
|
69
|
+
|
|
70
|
+
class LibSQLSqliteAsyncDriver implements SQLiteDatabaseDriverAsync {
|
|
71
|
+
private readonly db: DatabaseT;
|
|
72
|
+
|
|
73
|
+
constructor(filename: string) {
|
|
74
|
+
this.db = new Database(filename, {});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async initialize() {
|
|
78
|
+
await this.db.pragma("journal_mode = WAL");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async run(sql: string, params: unknown[]) {
|
|
82
|
+
this.db.prepare(sql).run(params);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async query<T>(sql: string, params: unknown[]): Promise<T[]> {
|
|
86
|
+
return this.db.prepare(sql).all(params) as T[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async get<T>(sql: string, params: unknown[]): Promise<T | undefined> {
|
|
90
|
+
return this.db.prepare(sql).get(params) as T | undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async transaction(callback: () => unknown) {
|
|
94
|
+
await this.run("BEGIN TRANSACTION", []);
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
await callback();
|
|
98
|
+
await this.run("COMMIT", []);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
await this.run("ROLLBACK", []);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async closeDb() {
|
|
105
|
+
this.db.close();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function createAsyncStorage() {
|
|
110
|
+
const storage = await getSqliteStorageAsync(
|
|
111
|
+
new LibSQLSqliteAsyncDriver(":memory:"),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
onTestFinished(() => {
|
|
115
|
+
storage.close();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return storage;
|
|
119
|
+
}
|
|
@@ -64,13 +64,9 @@ describe("useCoState", () => {
|
|
|
64
64
|
account,
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
expect(result.current.$jazz.loadingState).toBe(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
expect(result.current.$jazz.loadingState).toBe(
|
|
71
|
-
CoValueLoadingState.UNAVAILABLE,
|
|
72
|
-
);
|
|
73
|
-
});
|
|
67
|
+
expect(result.current.$jazz.loadingState).toBe(
|
|
68
|
+
CoValueLoadingState.UNAVAILABLE,
|
|
69
|
+
);
|
|
74
70
|
});
|
|
75
71
|
|
|
76
72
|
it("should update the value when the coValue changes", async () => {
|
|
@@ -89,13 +89,9 @@ describe("useSubscriptionSelector", () => {
|
|
|
89
89
|
return useSubscriptionSelector(subscription);
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
-
expect(result.current.$jazz.loadingState).toBe(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
expect(result.current.$jazz.loadingState).toBe(
|
|
96
|
-
CoValueLoadingState.UNAVAILABLE,
|
|
97
|
-
);
|
|
98
|
-
});
|
|
92
|
+
expect(result.current.$jazz.loadingState).toBe(
|
|
93
|
+
CoValueLoadingState.UNAVAILABLE,
|
|
94
|
+
);
|
|
99
95
|
});
|
|
100
96
|
|
|
101
97
|
it("should return coAccount", async () => {
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import { cojsonInternals } from "cojson";
|
|
4
|
+
import { Account, Group, Loaded, co, z } from "jazz-tools";
|
|
5
|
+
import { assertLoaded } from "jazz-tools/testing";
|
|
6
|
+
import { beforeEach, describe, expect, expectTypeOf, it } from "vitest";
|
|
7
|
+
import React, { Suspense } from "react";
|
|
8
|
+
import { useSuspenseAccount, useLogOut } from "../hooks.js";
|
|
9
|
+
import {
|
|
10
|
+
createJazzTestAccount,
|
|
11
|
+
createJazzTestGuest,
|
|
12
|
+
setActiveAccount,
|
|
13
|
+
setupJazzTestSync,
|
|
14
|
+
} from "../testing.js";
|
|
15
|
+
import { act, render, renderHook, waitFor } from "./testUtils.js";
|
|
16
|
+
import { ErrorBoundary } from "react-error-boundary";
|
|
17
|
+
|
|
18
|
+
// Silence unhandled rejection errors coming from Suspense
|
|
19
|
+
process.on("unhandledRejection", () => {});
|
|
20
|
+
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
await setupJazzTestSync({
|
|
23
|
+
asyncPeers: true,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await createJazzTestAccount({
|
|
27
|
+
isCurrentActiveAccount: true,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
cojsonInternals.setCoValueLoadingRetryDelay(10);
|
|
32
|
+
|
|
33
|
+
describe("useSuspenseAccount", () => {
|
|
34
|
+
it("should return loaded account without suspending when data is available", async () => {
|
|
35
|
+
const AccountRoot = co.map({
|
|
36
|
+
projects: co.list(
|
|
37
|
+
co.map({
|
|
38
|
+
name: z.string(),
|
|
39
|
+
description: z.string(),
|
|
40
|
+
}),
|
|
41
|
+
),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const MyAppAccount = co
|
|
45
|
+
.account({
|
|
46
|
+
profile: co.profile({
|
|
47
|
+
name: z.string(),
|
|
48
|
+
}),
|
|
49
|
+
root: AccountRoot,
|
|
50
|
+
})
|
|
51
|
+
.withMigration((account, creationProps) => {
|
|
52
|
+
if (!account.$jazz.refs.profile) {
|
|
53
|
+
account.$jazz.set("profile", {
|
|
54
|
+
name: creationProps?.name || "John Doe",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (!account.$jazz.refs.root) {
|
|
58
|
+
account.$jazz.set("root", {
|
|
59
|
+
projects: [],
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const account = await createJazzTestAccount({
|
|
65
|
+
AccountSchema: MyAppAccount,
|
|
66
|
+
isCurrentActiveAccount: true,
|
|
67
|
+
creationProps: {
|
|
68
|
+
name: "John Doe",
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
let suspenseTriggered = false;
|
|
73
|
+
|
|
74
|
+
const SuspenseFallback = () => {
|
|
75
|
+
suspenseTriggered = true;
|
|
76
|
+
return <div>Loading...</div>;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
80
|
+
<Suspense fallback={<SuspenseFallback />}>{children}</Suspense>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const { result } = renderHook(
|
|
84
|
+
() =>
|
|
85
|
+
useSuspenseAccount(MyAppAccount, {
|
|
86
|
+
resolve: {
|
|
87
|
+
profile: true,
|
|
88
|
+
root: {
|
|
89
|
+
projects: true,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
}),
|
|
93
|
+
{
|
|
94
|
+
account,
|
|
95
|
+
wrapper,
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Wait for any async operations to complete
|
|
100
|
+
await waitFor(() => {
|
|
101
|
+
expect(result.current).toBeDefined();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Verify Suspense was not triggered since data was immediately available
|
|
105
|
+
expect(suspenseTriggered).toBe(false);
|
|
106
|
+
|
|
107
|
+
// Verify the hook returns loaded data
|
|
108
|
+
assertLoaded(result.current);
|
|
109
|
+
expect(result.current.profile.name).toBe("John Doe");
|
|
110
|
+
expect(result.current.root.projects).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should have Loaded<A> return type", async () => {
|
|
114
|
+
const AccountRoot = co.map({
|
|
115
|
+
value: z.string(),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const MyAppAccount = co
|
|
119
|
+
.account({
|
|
120
|
+
profile: co.profile({
|
|
121
|
+
name: z.string(),
|
|
122
|
+
}),
|
|
123
|
+
root: AccountRoot,
|
|
124
|
+
})
|
|
125
|
+
.withMigration((account, creationProps) => {
|
|
126
|
+
if (!account.$jazz.refs.profile) {
|
|
127
|
+
account.$jazz.set("profile", {
|
|
128
|
+
name: creationProps?.name || "Test User",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (!account.$jazz.refs.root) {
|
|
132
|
+
account.$jazz.set("root", {
|
|
133
|
+
value: "test",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const account = await createJazzTestAccount({
|
|
139
|
+
AccountSchema: MyAppAccount,
|
|
140
|
+
isCurrentActiveAccount: true,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
144
|
+
<Suspense fallback={<div>Loading...</div>}>{children}</Suspense>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const { result } = renderHook(() => useSuspenseAccount(MyAppAccount), {
|
|
148
|
+
account,
|
|
149
|
+
wrapper,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
await waitFor(() => {
|
|
153
|
+
expect(result.current).toBeDefined();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Verify the return type is Loaded<typeof MyAppAccount>
|
|
157
|
+
expectTypeOf(result.current).toEqualTypeOf<Loaded<typeof MyAppAccount>>();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should suspend when account data is not immediately available", async () => {
|
|
161
|
+
const Project = co.map({
|
|
162
|
+
name: z.string(),
|
|
163
|
+
description: z.string(),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const AccountRoot = co.map({
|
|
167
|
+
projects: co.list(Project),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const root = AccountRoot.create(
|
|
171
|
+
{
|
|
172
|
+
projects: [
|
|
173
|
+
{
|
|
174
|
+
name: "My Project",
|
|
175
|
+
description: "A test project",
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
Group.create().makePublic(),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const MyAppAccount = co.account({
|
|
183
|
+
profile: co.profile({
|
|
184
|
+
name: z.string(),
|
|
185
|
+
}),
|
|
186
|
+
root: AccountRoot,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const account = await createJazzTestAccount({
|
|
190
|
+
AccountSchema: MyAppAccount,
|
|
191
|
+
isCurrentActiveAccount: true,
|
|
192
|
+
creationProps: {
|
|
193
|
+
name: "John Doe",
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
account.$jazz.set("root", root);
|
|
198
|
+
|
|
199
|
+
let suspenseTriggered = false;
|
|
200
|
+
|
|
201
|
+
const SuspenseFallback = () => {
|
|
202
|
+
suspenseTriggered = true;
|
|
203
|
+
return <div>Loading...</div>;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const TestComponent = () => {
|
|
207
|
+
const account = useSuspenseAccount(MyAppAccount, {
|
|
208
|
+
resolve: {
|
|
209
|
+
root: {
|
|
210
|
+
projects: {
|
|
211
|
+
$each: true,
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
return <div>{account.root.projects[0]?.name || "No project"}</div>;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const { container } = await act(async () => {
|
|
220
|
+
return render(
|
|
221
|
+
<Suspense fallback={<SuspenseFallback />}>
|
|
222
|
+
<TestComponent />
|
|
223
|
+
</Suspense>,
|
|
224
|
+
{
|
|
225
|
+
account,
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
expect(suspenseTriggered).toBe(true);
|
|
231
|
+
|
|
232
|
+
// Wait for data to load - the subscription should update and resolve
|
|
233
|
+
await waitFor(() => {
|
|
234
|
+
expect(container.textContent).toContain("My Project");
|
|
235
|
+
expect(container.textContent).not.toContain("Loading...");
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("should throw error for anonymous agent", async () => {
|
|
240
|
+
const MyAppAccount = co.account({
|
|
241
|
+
profile: co.profile({
|
|
242
|
+
name: z.string(),
|
|
243
|
+
}),
|
|
244
|
+
root: co.map({
|
|
245
|
+
value: z.string(),
|
|
246
|
+
}),
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const guestAccount = await createJazzTestGuest();
|
|
250
|
+
|
|
251
|
+
const TestComponent = () => {
|
|
252
|
+
useSuspenseAccount(MyAppAccount);
|
|
253
|
+
return <div>Should not render</div>;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const { container } = await act(async () => {
|
|
257
|
+
return render(
|
|
258
|
+
<ErrorBoundary fallback={<div>Error!</div>}>
|
|
259
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
260
|
+
<TestComponent />
|
|
261
|
+
</Suspense>
|
|
262
|
+
</ErrorBoundary>,
|
|
263
|
+
{
|
|
264
|
+
account: guestAccount,
|
|
265
|
+
},
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Verify error is displayed in error boundary
|
|
270
|
+
await waitFor(
|
|
271
|
+
() => {
|
|
272
|
+
expect(container.textContent).toContain("Error!");
|
|
273
|
+
},
|
|
274
|
+
{ timeout: 1000 },
|
|
275
|
+
);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("should handle account logout", async () => {
|
|
279
|
+
const MyAppAccount = co
|
|
280
|
+
.account({
|
|
281
|
+
profile: co.profile({
|
|
282
|
+
name: z.string(),
|
|
283
|
+
}),
|
|
284
|
+
root: co.map({
|
|
285
|
+
value: z.string(),
|
|
286
|
+
}),
|
|
287
|
+
})
|
|
288
|
+
.withMigration((account, creationProps) => {
|
|
289
|
+
if (!account.$jazz.refs.profile) {
|
|
290
|
+
account.$jazz.set("profile", {
|
|
291
|
+
name: creationProps?.name || "John Doe",
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
if (!account.$jazz.refs.root) {
|
|
295
|
+
account.$jazz.set("root", {
|
|
296
|
+
value: "test",
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const account = await createJazzTestAccount({
|
|
302
|
+
AccountSchema: MyAppAccount,
|
|
303
|
+
isCurrentActiveAccount: true,
|
|
304
|
+
creationProps: {
|
|
305
|
+
name: "John Doe",
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
310
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
311
|
+
<ErrorBoundary fallback={<div>Error!</div>}>{children}</ErrorBoundary>
|
|
312
|
+
</Suspense>
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
const { result } = renderHook(
|
|
316
|
+
() => {
|
|
317
|
+
const account = useSuspenseAccount(MyAppAccount);
|
|
318
|
+
const logOut = useLogOut();
|
|
319
|
+
return { account, logOut };
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
account,
|
|
323
|
+
wrapper,
|
|
324
|
+
},
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
// Wait for account to load
|
|
328
|
+
await waitFor(() => {
|
|
329
|
+
expect(result.current.account).toBeDefined();
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Verify initial account data
|
|
333
|
+
assertLoaded(result.current.account);
|
|
334
|
+
const initialAccountId = result.current.account.$jazz.id;
|
|
335
|
+
|
|
336
|
+
// Logout should cause an error since useSuspenseAccount requires authentication
|
|
337
|
+
await act(async () => {
|
|
338
|
+
result.current.logOut();
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
expect(result.current.account.$jazz.id).not.toBe(initialAccountId);
|
|
342
|
+
});
|
|
343
|
+
});
|