@wopr-network/platform-ui-core 1.5.0 → 1.5.1
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/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import { render, screen } from "@testing-library/react";
|
|
1
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
2
2
|
import userEvent from "@testing-library/user-event";
|
|
3
3
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
4
|
|
|
5
|
-
const { mockCreateCryptoCheckout, mockIsAllowedRedirectUrl } =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const { mockCreateCryptoCheckout, mockIsAllowedRedirectUrl, mockGetSupportedPaymentMethods } =
|
|
6
|
+
vi.hoisted(() => ({
|
|
7
|
+
mockCreateCryptoCheckout: vi.fn(),
|
|
8
|
+
mockIsAllowedRedirectUrl: vi.fn(),
|
|
9
|
+
mockGetSupportedPaymentMethods: vi.fn(),
|
|
10
|
+
}));
|
|
9
11
|
|
|
10
12
|
vi.mock("@/lib/api", () => ({
|
|
11
13
|
createCryptoCheckout: (...args: unknown[]) => mockCreateCryptoCheckout(...args),
|
|
14
|
+
createEthCheckout: vi.fn(),
|
|
15
|
+
createStablecoinCheckout: vi.fn(),
|
|
16
|
+
getSupportedPaymentMethods: (...args: unknown[]) => mockGetSupportedPaymentMethods(...args),
|
|
12
17
|
}));
|
|
13
18
|
|
|
14
19
|
vi.mock("@/lib/validate-redirect-url", () => ({
|
|
@@ -44,6 +49,9 @@ vi.mock("next/navigation", () => ({
|
|
|
44
49
|
vi.mock("better-auth/react", () => ({
|
|
45
50
|
createAuthClient: () => ({
|
|
46
51
|
useSession: () => ({ data: null, isPending: false, error: null }),
|
|
52
|
+
signIn: { email: vi.fn(), social: vi.fn() },
|
|
53
|
+
signUp: { email: vi.fn() },
|
|
54
|
+
signOut: vi.fn(),
|
|
47
55
|
}),
|
|
48
56
|
}));
|
|
49
57
|
|
|
@@ -55,14 +63,31 @@ vi.mock("lucide-react", async (importOriginal) => {
|
|
|
55
63
|
};
|
|
56
64
|
});
|
|
57
65
|
|
|
66
|
+
const MOCK_USDC_METHOD = {
|
|
67
|
+
id: "usdc:base",
|
|
68
|
+
type: "stablecoin",
|
|
69
|
+
label: "USDC on Base",
|
|
70
|
+
token: "USDC",
|
|
71
|
+
chain: "base",
|
|
72
|
+
};
|
|
73
|
+
const MOCK_BTC_METHOD = {
|
|
74
|
+
id: "btc:btcpay",
|
|
75
|
+
type: "btc",
|
|
76
|
+
label: "BTC",
|
|
77
|
+
token: "BTC",
|
|
78
|
+
chain: "bitcoin",
|
|
79
|
+
};
|
|
80
|
+
|
|
58
81
|
afterEach(() => {
|
|
59
82
|
vi.restoreAllMocks();
|
|
60
83
|
mockIsAllowedRedirectUrl.mockReset();
|
|
61
84
|
mockCreateCryptoCheckout.mockReset();
|
|
85
|
+
mockGetSupportedPaymentMethods.mockReset();
|
|
62
86
|
});
|
|
63
87
|
|
|
64
88
|
describe("BuyCryptoCreditPanel", () => {
|
|
65
89
|
it("renders crypto amount buttons ($10, $25, $50, $100)", async () => {
|
|
90
|
+
mockGetSupportedPaymentMethods.mockResolvedValue([MOCK_USDC_METHOD, MOCK_BTC_METHOD]);
|
|
66
91
|
const { BuyCryptoCreditPanel } = await import("../components/billing/buy-crypto-credits-panel");
|
|
67
92
|
render(<BuyCryptoCreditPanel />);
|
|
68
93
|
|
|
@@ -74,23 +99,35 @@ describe("BuyCryptoCreditPanel", () => {
|
|
|
74
99
|
});
|
|
75
100
|
|
|
76
101
|
it("Pay button is disabled when no amount selected", async () => {
|
|
102
|
+
mockGetSupportedPaymentMethods.mockResolvedValue([MOCK_USDC_METHOD, MOCK_BTC_METHOD]);
|
|
77
103
|
const { BuyCryptoCreditPanel } = await import("../components/billing/buy-crypto-credits-panel");
|
|
78
104
|
render(<BuyCryptoCreditPanel />);
|
|
79
105
|
|
|
80
|
-
|
|
106
|
+
// Wait for methods to load
|
|
107
|
+
await waitFor(() => {
|
|
108
|
+
expect(screen.getByText("Stablecoin")).toBeInTheDocument();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const payBtn = screen.getByRole("button", { name: "Pay with USDC" });
|
|
81
112
|
expect(payBtn).toBeDisabled();
|
|
82
113
|
});
|
|
83
114
|
|
|
84
115
|
it("Pay button is enabled after selecting an amount", async () => {
|
|
116
|
+
mockGetSupportedPaymentMethods.mockResolvedValue([MOCK_USDC_METHOD, MOCK_BTC_METHOD]);
|
|
85
117
|
const user = userEvent.setup();
|
|
86
118
|
const { BuyCryptoCreditPanel } = await import("../components/billing/buy-crypto-credits-panel");
|
|
87
119
|
render(<BuyCryptoCreditPanel />);
|
|
88
120
|
|
|
121
|
+
await waitFor(() => {
|
|
122
|
+
expect(screen.getByText("Stablecoin")).toBeInTheDocument();
|
|
123
|
+
});
|
|
124
|
+
|
|
89
125
|
await user.click(screen.getByText("$25"));
|
|
90
|
-
expect(screen.getByRole("button", { name: "Pay with
|
|
126
|
+
expect(screen.getByRole("button", { name: "Pay with USDC" })).toBeEnabled();
|
|
91
127
|
});
|
|
92
128
|
|
|
93
129
|
it("calls createCryptoCheckout with selected amount and redirects", async () => {
|
|
130
|
+
mockGetSupportedPaymentMethods.mockResolvedValue([MOCK_USDC_METHOD, MOCK_BTC_METHOD]);
|
|
94
131
|
const hrefSetter = vi.fn();
|
|
95
132
|
Object.defineProperty(window, "location", {
|
|
96
133
|
value: { ...window.location, href: "" },
|
|
@@ -112,8 +149,14 @@ describe("BuyCryptoCreditPanel", () => {
|
|
|
112
149
|
const { BuyCryptoCreditPanel } = await import("../components/billing/buy-crypto-credits-panel");
|
|
113
150
|
render(<BuyCryptoCreditPanel />);
|
|
114
151
|
|
|
152
|
+
await waitFor(() => {
|
|
153
|
+
expect(screen.getByText("BTC")).toBeInTheDocument();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Switch to BTC tab
|
|
157
|
+
await user.click(screen.getByText("BTC"));
|
|
115
158
|
await user.click(screen.getByText("$50"));
|
|
116
|
-
await user.click(screen.getByRole("button", { name: "Pay with
|
|
159
|
+
await user.click(screen.getByRole("button", { name: "Pay with BTC" }));
|
|
117
160
|
|
|
118
161
|
expect(mockCreateCryptoCheckout).toHaveBeenCalledWith(50);
|
|
119
162
|
expect(mockIsAllowedRedirectUrl).toHaveBeenCalledWith("https://btcpay.example.com/i/abc123");
|
|
@@ -121,6 +164,7 @@ describe("BuyCryptoCreditPanel", () => {
|
|
|
121
164
|
});
|
|
122
165
|
|
|
123
166
|
it("shows error when redirect URL is not allowed", async () => {
|
|
167
|
+
mockGetSupportedPaymentMethods.mockResolvedValue([MOCK_USDC_METHOD, MOCK_BTC_METHOD]);
|
|
124
168
|
mockCreateCryptoCheckout.mockResolvedValue({
|
|
125
169
|
url: "https://evil.com/steal",
|
|
126
170
|
referenceId: "ref-evil",
|
|
@@ -131,15 +175,21 @@ describe("BuyCryptoCreditPanel", () => {
|
|
|
131
175
|
const { BuyCryptoCreditPanel } = await import("../components/billing/buy-crypto-credits-panel");
|
|
132
176
|
render(<BuyCryptoCreditPanel />);
|
|
133
177
|
|
|
178
|
+
await waitFor(() => {
|
|
179
|
+
expect(screen.getByText("BTC")).toBeInTheDocument();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
await user.click(screen.getByText("BTC"));
|
|
134
183
|
await user.click(screen.getByText("$10"));
|
|
135
|
-
await user.click(screen.getByRole("button", { name: "Pay with
|
|
184
|
+
await user.click(screen.getByRole("button", { name: "Pay with BTC" }));
|
|
136
185
|
|
|
137
186
|
expect(
|
|
138
187
|
await screen.findByText("Unexpected checkout URL. Please contact support."),
|
|
139
188
|
).toBeInTheDocument();
|
|
140
189
|
});
|
|
141
190
|
|
|
142
|
-
it("shows
|
|
191
|
+
it("shows Creating checkout... while checkout is in progress", async () => {
|
|
192
|
+
mockGetSupportedPaymentMethods.mockResolvedValue([MOCK_USDC_METHOD, MOCK_BTC_METHOD]);
|
|
143
193
|
mockCreateCryptoCheckout.mockReturnValue(
|
|
144
194
|
new Promise(() => {
|
|
145
195
|
/* intentionally pending */
|
|
@@ -150,29 +200,33 @@ describe("BuyCryptoCreditPanel", () => {
|
|
|
150
200
|
const { BuyCryptoCreditPanel } = await import("../components/billing/buy-crypto-credits-panel");
|
|
151
201
|
render(<BuyCryptoCreditPanel />);
|
|
152
202
|
|
|
203
|
+
await waitFor(() => {
|
|
204
|
+
expect(screen.getByText("BTC")).toBeInTheDocument();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
await user.click(screen.getByText("BTC"));
|
|
153
208
|
await user.click(screen.getByText("$100"));
|
|
154
|
-
await user.click(screen.getByRole("button", { name: "Pay with
|
|
209
|
+
await user.click(screen.getByRole("button", { name: "Pay with BTC" }));
|
|
155
210
|
|
|
156
|
-
expect(await screen.findByText("
|
|
211
|
+
expect(await screen.findByText("Creating checkout...")).toBeInTheDocument();
|
|
157
212
|
});
|
|
158
213
|
|
|
159
214
|
it("shows error when crypto checkout API call fails", async () => {
|
|
215
|
+
mockGetSupportedPaymentMethods.mockResolvedValue([MOCK_USDC_METHOD, MOCK_BTC_METHOD]);
|
|
160
216
|
mockCreateCryptoCheckout.mockRejectedValue(new Error("API down"));
|
|
161
217
|
|
|
162
218
|
const user = userEvent.setup();
|
|
163
219
|
const { BuyCryptoCreditPanel } = await import("../components/billing/buy-crypto-credits-panel");
|
|
164
220
|
render(<BuyCryptoCreditPanel />);
|
|
165
221
|
|
|
222
|
+
await waitFor(() => {
|
|
223
|
+
expect(screen.getByText("BTC")).toBeInTheDocument();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
await user.click(screen.getByText("BTC"));
|
|
166
227
|
await user.click(screen.getByText("$25"));
|
|
167
|
-
await user.click(screen.getByRole("button", { name: "Pay with
|
|
228
|
+
await user.click(screen.getByRole("button", { name: "Pay with BTC" }));
|
|
168
229
|
|
|
169
230
|
expect(await screen.findByText("Checkout failed. Please try again.")).toBeInTheDocument();
|
|
170
231
|
});
|
|
171
|
-
|
|
172
|
-
it("displays minimum amount note", async () => {
|
|
173
|
-
const { BuyCryptoCreditPanel } = await import("../components/billing/buy-crypto-credits-panel");
|
|
174
|
-
render(<BuyCryptoCreditPanel />);
|
|
175
|
-
|
|
176
|
-
expect(screen.getByText(/Minimum \$10/)).toBeInTheDocument();
|
|
177
|
-
});
|
|
178
232
|
});
|
|
@@ -19,10 +19,17 @@ vi.mock("next/navigation", () => ({
|
|
|
19
19
|
usePathname: () => "/settings/profile",
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
-
// Mock better-auth/react
|
|
22
|
+
// Mock better-auth/react — session with user-001 (Alice) so role-gated org UI renders admin view
|
|
23
23
|
vi.mock("better-auth/react", () => ({
|
|
24
24
|
createAuthClient: () => ({
|
|
25
|
-
useSession: () => ({
|
|
25
|
+
useSession: () => ({
|
|
26
|
+
data: {
|
|
27
|
+
user: { id: "user-001", name: "Alice Johnson", email: "alice@example.com" },
|
|
28
|
+
session: { id: "sess-1", userId: "user-001", expiresAt: new Date(Date.now() + 86400000) },
|
|
29
|
+
},
|
|
30
|
+
isPending: false,
|
|
31
|
+
error: null,
|
|
32
|
+
}),
|
|
26
33
|
signIn: { email: vi.fn(), social: vi.fn() },
|
|
27
34
|
signUp: { email: vi.fn() },
|
|
28
35
|
signOut: vi.fn(),
|