@frak-labs/react-sdk 0.1.1 → 0.2.0
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 +25 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +28 -64
- package/dist/index.d.ts +28 -64
- package/dist/index.js +1 -1
- package/package.json +17 -16
- package/src/hook/helper/useReferralInteraction.test.ts +358 -0
- package/src/hook/helper/useReferralInteraction.ts +78 -0
- package/src/hook/index.ts +10 -0
- package/src/hook/useDisplayModal.test.ts +275 -0
- package/src/hook/useDisplayModal.ts +68 -0
- package/src/hook/useFrakClient.test.ts +119 -0
- package/src/hook/useFrakClient.ts +11 -0
- package/src/hook/useFrakConfig.test.ts +184 -0
- package/src/hook/useFrakConfig.ts +22 -0
- package/src/hook/useGetMerchantInformation.ts +56 -0
- package/src/hook/useOpenSso.test.ts +202 -0
- package/src/hook/useOpenSso.ts +51 -0
- package/src/hook/usePrepareSso.test.ts +197 -0
- package/src/hook/usePrepareSso.ts +55 -0
- package/src/hook/useSendTransaction.test.ts +218 -0
- package/src/hook/useSendTransaction.ts +62 -0
- package/src/hook/useSiweAuthenticate.test.ts +258 -0
- package/src/hook/useSiweAuthenticate.ts +66 -0
- package/src/hook/useWalletStatus.test.ts +112 -0
- package/src/hook/useWalletStatus.ts +55 -0
- package/src/hook/utils/useFrakContext.test.ts +157 -0
- package/src/hook/utils/useFrakContext.ts +42 -0
- package/src/hook/utils/useMounted.test.ts +70 -0
- package/src/hook/utils/useMounted.ts +12 -0
- package/src/hook/utils/useWindowLocation.test.ts +54 -0
- package/src/hook/utils/useWindowLocation.ts +40 -0
- package/src/index.ts +25 -0
- package/src/provider/FrakConfigProvider.test.ts +246 -0
- package/src/provider/FrakConfigProvider.ts +54 -0
- package/src/provider/FrakIFrameClientProvider.test.tsx +209 -0
- package/src/provider/FrakIFrameClientProvider.ts +86 -0
- package/src/provider/index.ts +7 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { GetMerchantInformationReturnType } from "@frak-labs/core-sdk";
|
|
2
|
+
import { getMerchantInformation } from "@frak-labs/core-sdk/actions";
|
|
3
|
+
import { ClientNotFound, type FrakRpcError } from "@frak-labs/frame-connector";
|
|
4
|
+
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
|
|
5
|
+
import { useFrakClient } from "./useFrakClient";
|
|
6
|
+
|
|
7
|
+
/** @ignore */
|
|
8
|
+
type QueryOptions = Omit<
|
|
9
|
+
UseQueryOptions<
|
|
10
|
+
GetMerchantInformationReturnType,
|
|
11
|
+
FrakRpcError,
|
|
12
|
+
GetMerchantInformationReturnType
|
|
13
|
+
>,
|
|
14
|
+
"queryKey" | "queryFn"
|
|
15
|
+
>;
|
|
16
|
+
|
|
17
|
+
/** @inline */
|
|
18
|
+
interface UseGetMerchantInformationParams {
|
|
19
|
+
/**
|
|
20
|
+
* Optional query options, see {@link @tanstack/react-query!useQuery | `useQuery()`} for more infos
|
|
21
|
+
*/
|
|
22
|
+
query?: QueryOptions;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Hook that return a query helping to get the current merchant information
|
|
27
|
+
*
|
|
28
|
+
* It's a {@link @tanstack/react-query!home | `tanstack`} wrapper around the {@link @frak-labs/core-sdk!actions.getMerchantInformation | `getMerchantInformation()`} action
|
|
29
|
+
*
|
|
30
|
+
* @param args - Optional config object with `query` for customizing the underlying {@link @tanstack/react-query!useQuery | `useQuery()`}
|
|
31
|
+
*
|
|
32
|
+
* @group hooks
|
|
33
|
+
*
|
|
34
|
+
* @returns
|
|
35
|
+
* The query hook wrapping the `getMerchantInformation()` action
|
|
36
|
+
* The `data` result is a {@link @frak-labs/core-sdk!index.GetMerchantInformationReturnType | `GetMerchantInformationReturnType`}
|
|
37
|
+
*
|
|
38
|
+
* @see {@link @frak-labs/core-sdk!actions.getMerchantInformation | `getMerchantInformation()`} for more info about the underlying action
|
|
39
|
+
* @see {@link @tanstack/react-query!useQuery | `useQuery()`} for more info about the useQuery options and response
|
|
40
|
+
*/
|
|
41
|
+
export function useGetMerchantInformation({
|
|
42
|
+
query,
|
|
43
|
+
}: UseGetMerchantInformationParams = {}) {
|
|
44
|
+
const client = useFrakClient();
|
|
45
|
+
|
|
46
|
+
return useQuery({
|
|
47
|
+
...query,
|
|
48
|
+
queryKey: ["frak-sdk", "get-merchant-information"],
|
|
49
|
+
queryFn: async () => {
|
|
50
|
+
if (!client) {
|
|
51
|
+
throw new ClientNotFound();
|
|
52
|
+
}
|
|
53
|
+
return getMerchantInformation(client);
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for useOpenSso hook
|
|
3
|
+
* Tests TanStack Mutation wrapper for opening SSO
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { vi } from "vitest";
|
|
7
|
+
|
|
8
|
+
vi.mock("@frak-labs/core-sdk/actions");
|
|
9
|
+
|
|
10
|
+
import type { OpenSsoReturnType } from "@frak-labs/core-sdk";
|
|
11
|
+
import { openSso } from "@frak-labs/core-sdk/actions";
|
|
12
|
+
import { ClientNotFound } from "@frak-labs/frame-connector";
|
|
13
|
+
import { renderHook, waitFor } from "@testing-library/react";
|
|
14
|
+
import { describe, expect, test } from "../../tests/vitest-fixtures";
|
|
15
|
+
import { useOpenSso } from "./useOpenSso";
|
|
16
|
+
|
|
17
|
+
describe("useOpenSso", () => {
|
|
18
|
+
test("should throw ClientNotFound when client is not available", async ({
|
|
19
|
+
queryWrapper,
|
|
20
|
+
}) => {
|
|
21
|
+
const { result } = renderHook(() => useOpenSso(), {
|
|
22
|
+
wrapper: queryWrapper.wrapper,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await waitFor(() => {
|
|
26
|
+
expect(result.current.mutate).toBeDefined();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
result.current.mutate({});
|
|
30
|
+
|
|
31
|
+
await waitFor(() => {
|
|
32
|
+
expect(result.current.isError).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(result.current.error).toBeInstanceOf(ClientNotFound);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("should open SSO successfully", async ({ mockFrakProviders }) => {
|
|
39
|
+
const mockResult = undefined as unknown as OpenSsoReturnType;
|
|
40
|
+
|
|
41
|
+
vi.mocked(openSso).mockResolvedValue(mockResult);
|
|
42
|
+
|
|
43
|
+
const { result } = renderHook(() => useOpenSso(), {
|
|
44
|
+
wrapper: mockFrakProviders,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
result.current.mutate({});
|
|
48
|
+
|
|
49
|
+
await waitFor(() => {
|
|
50
|
+
expect(result.current.isSuccess).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(openSso).toHaveBeenCalledTimes(1);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("should open SSO with redirectUrl", async ({ mockFrakProviders }) => {
|
|
57
|
+
const mockResult = undefined as unknown as OpenSsoReturnType;
|
|
58
|
+
|
|
59
|
+
vi.mocked(openSso).mockResolvedValue(mockResult);
|
|
60
|
+
|
|
61
|
+
const { result } = renderHook(() => useOpenSso(), {
|
|
62
|
+
wrapper: mockFrakProviders,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const redirectUrl = "https://example.com/callback";
|
|
66
|
+
|
|
67
|
+
result.current.mutate({
|
|
68
|
+
redirectUrl,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await waitFor(() => {
|
|
72
|
+
expect(result.current.isSuccess).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(openSso).toHaveBeenCalledWith(
|
|
76
|
+
expect.anything(),
|
|
77
|
+
expect.objectContaining({
|
|
78
|
+
redirectUrl,
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("should open SSO with metadata", async ({ mockFrakProviders }) => {
|
|
84
|
+
const mockResult = undefined as unknown as OpenSsoReturnType;
|
|
85
|
+
|
|
86
|
+
vi.mocked(openSso).mockResolvedValue(mockResult);
|
|
87
|
+
|
|
88
|
+
const { result } = renderHook(() => useOpenSso(), {
|
|
89
|
+
wrapper: mockFrakProviders,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const metadata = {
|
|
93
|
+
logoUrl: "https://example.com/logo.png",
|
|
94
|
+
homepageLink: "https://example.com",
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
result.current.mutate({
|
|
98
|
+
metadata,
|
|
99
|
+
directExit: true,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await waitFor(() => {
|
|
103
|
+
expect(result.current.isSuccess).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(openSso).toHaveBeenCalledWith(
|
|
107
|
+
expect.anything(),
|
|
108
|
+
expect.objectContaining({
|
|
109
|
+
metadata,
|
|
110
|
+
directExit: true,
|
|
111
|
+
})
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("should handle mutateAsync", async ({ mockFrakProviders }) => {
|
|
116
|
+
const mockResult = undefined as unknown as OpenSsoReturnType;
|
|
117
|
+
|
|
118
|
+
vi.mocked(openSso).mockResolvedValue(mockResult);
|
|
119
|
+
|
|
120
|
+
const { result } = renderHook(() => useOpenSso(), {
|
|
121
|
+
wrapper: mockFrakProviders,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
await result.current.mutateAsync({
|
|
125
|
+
directExit: false,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
await waitFor(() => {
|
|
129
|
+
expect(result.current.isSuccess).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("should handle RPC errors", async ({ mockFrakProviders }) => {
|
|
134
|
+
const error = new Error("SSO open failed");
|
|
135
|
+
vi.mocked(openSso).mockRejectedValue(error);
|
|
136
|
+
|
|
137
|
+
const { result } = renderHook(() => useOpenSso(), {
|
|
138
|
+
wrapper: mockFrakProviders,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
result.current.mutate({});
|
|
142
|
+
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
expect(result.current.isError).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(result.current.error).toEqual(error);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("should handle mutation options", async ({ mockFrakProviders }) => {
|
|
151
|
+
const mockResult = undefined as unknown as OpenSsoReturnType;
|
|
152
|
+
|
|
153
|
+
vi.mocked(openSso).mockResolvedValue(mockResult);
|
|
154
|
+
|
|
155
|
+
const onSuccess = vi.fn();
|
|
156
|
+
const onError = vi.fn();
|
|
157
|
+
|
|
158
|
+
const { result } = renderHook(
|
|
159
|
+
() =>
|
|
160
|
+
useOpenSso({
|
|
161
|
+
mutations: {
|
|
162
|
+
onSuccess,
|
|
163
|
+
onError,
|
|
164
|
+
},
|
|
165
|
+
}),
|
|
166
|
+
{
|
|
167
|
+
wrapper: mockFrakProviders,
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
result.current.mutate({});
|
|
172
|
+
|
|
173
|
+
await waitFor(() => {
|
|
174
|
+
expect(result.current.isSuccess).toBe(true);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
expect(onSuccess).toHaveBeenCalled();
|
|
178
|
+
expect(onError).not.toHaveBeenCalled();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("should reset mutation state", async ({ mockFrakProviders }) => {
|
|
182
|
+
const mockResult = undefined as unknown as OpenSsoReturnType;
|
|
183
|
+
|
|
184
|
+
vi.mocked(openSso).mockResolvedValue(mockResult);
|
|
185
|
+
|
|
186
|
+
const { result } = renderHook(() => useOpenSso(), {
|
|
187
|
+
wrapper: mockFrakProviders,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
result.current.mutate({});
|
|
191
|
+
|
|
192
|
+
await waitFor(() => {
|
|
193
|
+
expect(result.current.isSuccess).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
result.current.reset();
|
|
197
|
+
|
|
198
|
+
await waitFor(() => {
|
|
199
|
+
expect(result.current.isIdle).toBe(true);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { OpenSsoParamsType, OpenSsoReturnType } from "@frak-labs/core-sdk";
|
|
2
|
+
import { openSso } from "@frak-labs/core-sdk/actions";
|
|
3
|
+
import { ClientNotFound, type FrakRpcError } from "@frak-labs/frame-connector";
|
|
4
|
+
import { type UseMutationOptions, useMutation } from "@tanstack/react-query";
|
|
5
|
+
import { useFrakClient } from "./useFrakClient";
|
|
6
|
+
|
|
7
|
+
/** @ignore */
|
|
8
|
+
type MutationOptions = Omit<
|
|
9
|
+
UseMutationOptions<OpenSsoReturnType, FrakRpcError, OpenSsoParamsType>,
|
|
10
|
+
"mutationFn" | "mutationKey"
|
|
11
|
+
>;
|
|
12
|
+
|
|
13
|
+
/** @inline */
|
|
14
|
+
interface UseOpenSsoParams {
|
|
15
|
+
/**
|
|
16
|
+
* Optional mutation options, see {@link @tanstack/react-query!useMutation | `useMutation()`} for more infos
|
|
17
|
+
*/
|
|
18
|
+
mutations?: MutationOptions;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Hook that return a mutation helping to open the SSO page
|
|
23
|
+
*
|
|
24
|
+
* It's a {@link @tanstack/react-query!home | `tanstack`} wrapper around the {@link @frak-labs/core-sdk!actions.openSso | `openSso()`} action
|
|
25
|
+
*
|
|
26
|
+
* @param args - Optional config object with `mutations` for customizing the underlying {@link @tanstack/react-query!useMutation | `useMutation()`}
|
|
27
|
+
*
|
|
28
|
+
* @group hooks
|
|
29
|
+
*
|
|
30
|
+
* @returns
|
|
31
|
+
* The mutation hook wrapping the `openSso()` action
|
|
32
|
+
* The `mutate` and `mutateAsync` argument is of type {@link @frak-labs/core-sdk!index.OpenSsoParamsType | `OpenSsoParamsType`}
|
|
33
|
+
* The mutation doesn't output any value
|
|
34
|
+
*
|
|
35
|
+
* @see {@link @frak-labs/core-sdk!actions.openSso | `openSso()`} for more info about the underlying action
|
|
36
|
+
* @see {@link @tanstack/react-query!useMutation | `useMutation()`} for more info about the mutation options and response
|
|
37
|
+
*/
|
|
38
|
+
export function useOpenSso({ mutations }: UseOpenSsoParams = {}) {
|
|
39
|
+
const client = useFrakClient();
|
|
40
|
+
|
|
41
|
+
return useMutation({
|
|
42
|
+
...mutations,
|
|
43
|
+
mutationKey: ["frak-sdk", "open-sso"],
|
|
44
|
+
mutationFn: async (params: OpenSsoParamsType) => {
|
|
45
|
+
if (!client) {
|
|
46
|
+
throw new ClientNotFound();
|
|
47
|
+
}
|
|
48
|
+
return openSso(client, params);
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for usePrepareSso hook
|
|
3
|
+
* Tests TanStack Query wrapper for preparing SSO URLs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { vi } from "vitest";
|
|
7
|
+
|
|
8
|
+
vi.mock("@frak-labs/core-sdk/actions");
|
|
9
|
+
|
|
10
|
+
import type { PrepareSsoReturnType } from "@frak-labs/core-sdk";
|
|
11
|
+
import { prepareSso } from "@frak-labs/core-sdk/actions";
|
|
12
|
+
import { ClientNotFound } from "@frak-labs/frame-connector";
|
|
13
|
+
import { renderHook, waitFor } from "@testing-library/react";
|
|
14
|
+
import { describe, expect, test } from "../../tests/vitest-fixtures";
|
|
15
|
+
import { usePrepareSso } from "./usePrepareSso";
|
|
16
|
+
|
|
17
|
+
describe("usePrepareSso", () => {
|
|
18
|
+
test("should throw ClientNotFound when client is not available", async ({
|
|
19
|
+
queryWrapper,
|
|
20
|
+
}) => {
|
|
21
|
+
const { result } = renderHook(
|
|
22
|
+
() => usePrepareSso({ directExit: true }),
|
|
23
|
+
{
|
|
24
|
+
wrapper: queryWrapper.wrapper,
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
await waitFor(() => {
|
|
29
|
+
expect(result.current.isError).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(result.current.error).toBeInstanceOf(ClientNotFound);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("should prepare SSO URL successfully", async ({
|
|
36
|
+
mockFrakProviders,
|
|
37
|
+
}) => {
|
|
38
|
+
const mockSsoResult: PrepareSsoReturnType = {
|
|
39
|
+
ssoUrl: "https://wallet-test.frak.id/sso?params=xyz",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
vi.mocked(prepareSso).mockResolvedValue(mockSsoResult);
|
|
43
|
+
|
|
44
|
+
const { result } = renderHook(
|
|
45
|
+
() => usePrepareSso({ directExit: true }),
|
|
46
|
+
{
|
|
47
|
+
wrapper: mockFrakProviders,
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
await waitFor(() => {
|
|
52
|
+
expect(result.current.isSuccess).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(result.current.data).toEqual(mockSsoResult);
|
|
56
|
+
expect(result.current.data?.ssoUrl).toContain("sso");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("should handle SSO params with redirectUrl", async ({
|
|
60
|
+
mockFrakProviders,
|
|
61
|
+
}) => {
|
|
62
|
+
const mockSsoResult: PrepareSsoReturnType = {
|
|
63
|
+
ssoUrl: "https://wallet-test.frak.id/sso?redirect=example.com",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
vi.mocked(prepareSso).mockResolvedValue(mockSsoResult);
|
|
67
|
+
|
|
68
|
+
const { result } = renderHook(
|
|
69
|
+
() =>
|
|
70
|
+
usePrepareSso({
|
|
71
|
+
redirectUrl: "https://example.com/callback",
|
|
72
|
+
directExit: false,
|
|
73
|
+
}),
|
|
74
|
+
{
|
|
75
|
+
wrapper: mockFrakProviders,
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
await waitFor(() => {
|
|
80
|
+
expect(result.current.isSuccess).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(result.current.data).toEqual(mockSsoResult);
|
|
84
|
+
expect(prepareSso).toHaveBeenCalledWith(
|
|
85
|
+
expect.anything(),
|
|
86
|
+
expect.objectContaining({
|
|
87
|
+
redirectUrl: "https://example.com/callback",
|
|
88
|
+
directExit: false,
|
|
89
|
+
})
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("should handle SSO params with metadata", async ({
|
|
94
|
+
mockFrakProviders,
|
|
95
|
+
}) => {
|
|
96
|
+
const mockSsoResult: PrepareSsoReturnType = {
|
|
97
|
+
ssoUrl: "https://wallet-test.frak.id/sso?metadata=xyz",
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
vi.mocked(prepareSso).mockResolvedValue(mockSsoResult);
|
|
101
|
+
|
|
102
|
+
const { result } = renderHook(
|
|
103
|
+
() =>
|
|
104
|
+
usePrepareSso({
|
|
105
|
+
metadata: {
|
|
106
|
+
logoUrl: "https://example.com/logo.png",
|
|
107
|
+
homepageLink: "https://example.com",
|
|
108
|
+
},
|
|
109
|
+
directExit: true,
|
|
110
|
+
}),
|
|
111
|
+
{
|
|
112
|
+
wrapper: mockFrakProviders,
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
expect(result.current.isSuccess).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
expect(result.current.data).toEqual(mockSsoResult);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("should handle RPC errors", async ({ mockFrakProviders }) => {
|
|
124
|
+
const error = new Error("SSO preparation failed");
|
|
125
|
+
vi.mocked(prepareSso).mockRejectedValue(error);
|
|
126
|
+
|
|
127
|
+
const { result } = renderHook(
|
|
128
|
+
() => usePrepareSso({ directExit: true }),
|
|
129
|
+
{
|
|
130
|
+
wrapper: mockFrakProviders,
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
await waitFor(() => {
|
|
135
|
+
expect(result.current.isError).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
expect(result.current.error).toEqual(error);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("should update query key with params", async ({
|
|
142
|
+
mockFrakProviders,
|
|
143
|
+
}) => {
|
|
144
|
+
const mockSsoResult: PrepareSsoReturnType = {
|
|
145
|
+
ssoUrl: "https://wallet-test.frak.id/sso?params=abc",
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
vi.mocked(prepareSso).mockResolvedValue(mockSsoResult);
|
|
149
|
+
|
|
150
|
+
const params = {
|
|
151
|
+
directExit: false,
|
|
152
|
+
redirectUrl: "https://example.com",
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const { result, rerender } = renderHook(() => usePrepareSso(params), {
|
|
156
|
+
wrapper: mockFrakProviders,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await waitFor(() => {
|
|
160
|
+
expect(result.current.isSuccess).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
expect(result.current.data).toEqual(mockSsoResult);
|
|
164
|
+
|
|
165
|
+
// Query key includes params, so changes should trigger re-fetch
|
|
166
|
+
rerender();
|
|
167
|
+
expect(result.current.data).toEqual(mockSsoResult);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("should call prepareSso with client and params", async ({
|
|
171
|
+
mockFrakProviders,
|
|
172
|
+
mockFrakClient,
|
|
173
|
+
}) => {
|
|
174
|
+
const mockSsoResult: PrepareSsoReturnType = {
|
|
175
|
+
ssoUrl: "https://wallet-test.frak.id/sso?test=123",
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
vi.mocked(prepareSso).mockResolvedValue(mockSsoResult);
|
|
179
|
+
|
|
180
|
+
const params = {
|
|
181
|
+
directExit: true,
|
|
182
|
+
metadata: {
|
|
183
|
+
logoUrl: "https://example.com/logo.png",
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const { result } = renderHook(() => usePrepareSso(params), {
|
|
188
|
+
wrapper: mockFrakProviders,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await waitFor(() => {
|
|
192
|
+
expect(result.current.isSuccess).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(prepareSso).toHaveBeenCalledWith(mockFrakClient, params);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { PrepareSsoParamsType } from "@frak-labs/core-sdk";
|
|
2
|
+
import { prepareSso } from "@frak-labs/core-sdk/actions";
|
|
3
|
+
import { ClientNotFound } from "@frak-labs/frame-connector";
|
|
4
|
+
import { useQuery } from "@tanstack/react-query";
|
|
5
|
+
import { useFrakClient } from "./useFrakClient";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hook that generates SSO URL for popup flow
|
|
9
|
+
*
|
|
10
|
+
* This is a **synchronous** hook (no async calls) that generates the SSO URL
|
|
11
|
+
* client-side without communicating with the wallet iframe.
|
|
12
|
+
*
|
|
13
|
+
* @param params - SSO parameters for URL generation
|
|
14
|
+
*
|
|
15
|
+
* @group hooks
|
|
16
|
+
*
|
|
17
|
+
* @returns
|
|
18
|
+
* Object containing:
|
|
19
|
+
* - `ssoUrl`: Generated SSO URL (or undefined if client not ready)
|
|
20
|
+
* - `isReady`: Boolean indicating if URL is available
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* function MyComponent() {
|
|
25
|
+
* const { data } = usePrepareSso({
|
|
26
|
+
* metadata: { logoUrl: "..." },
|
|
27
|
+
* directExit: true
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* const handleClick = () => {
|
|
31
|
+
* if (ssoUrl) {
|
|
32
|
+
* window.open(data?.ssoUrl, "_blank");
|
|
33
|
+
* }
|
|
34
|
+
* };
|
|
35
|
+
*
|
|
36
|
+
* return <button onClick={handleClick} disabled={!isReady}>Login</button>;
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @see {@link @frak-labs/core-sdk!actions.prepareSso | `prepareSso()`} for the underlying action
|
|
41
|
+
* @see {@link @frak-labs/core-sdk!actions.openSso | `openSso()`} for the recommended high-level API
|
|
42
|
+
*/
|
|
43
|
+
export function usePrepareSso(params: PrepareSsoParamsType) {
|
|
44
|
+
const client = useFrakClient();
|
|
45
|
+
|
|
46
|
+
return useQuery({
|
|
47
|
+
queryKey: ["frak-sdk", "prepare-sso", params],
|
|
48
|
+
queryFn: async () => {
|
|
49
|
+
if (!client) {
|
|
50
|
+
throw new ClientNotFound();
|
|
51
|
+
}
|
|
52
|
+
return prepareSso(client, params);
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|