@uploadista/react 0.0.20 → 0.1.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.
@@ -0,0 +1,316 @@
1
+ import { render, renderHook, screen, waitFor } from "@testing-library/react";
2
+ import type { ReactNode } from "react";
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import {
5
+ UploadistaProvider,
6
+ useUploadistaContext,
7
+ } from "../components/uploadista-provider";
8
+
9
+ // Mock dependencies
10
+ vi.mock("@uploadista/client-browser", () => ({
11
+ createUploadistaClient: vi.fn(() => ({
12
+ upload: vi.fn(),
13
+ executeFlow: vi.fn(),
14
+ discoverFlowInputs: vi.fn(),
15
+ uploadWithFlow: vi.fn(),
16
+ multiInputFlowUpload: vi.fn(),
17
+ getChunkingInsights: vi.fn(() => ({
18
+ currentChunkSize: 1024 * 1024,
19
+ recommendedChunkSize: 1024 * 1024,
20
+ networkCondition: "good",
21
+ })),
22
+ exportMetrics: vi.fn(() => ({})),
23
+ getNetworkMetrics: vi.fn(() => ({
24
+ averageSpeed: 1024 * 1024,
25
+ currentSpeed: 1024 * 1024,
26
+ estimatedTimeRemaining: 0,
27
+ })),
28
+ getNetworkCondition: vi.fn(() => "good"),
29
+ resetMetrics: vi.fn(),
30
+ })),
31
+ }));
32
+
33
+ vi.mock("@uploadista/client-core", async (importOriginal) => {
34
+ const actual =
35
+ await importOriginal<typeof import("@uploadista/client-core")>();
36
+ return {
37
+ ...actual,
38
+ FlowManager: vi.fn().mockImplementation(() => ({
39
+ handleFlowEvent: vi.fn(),
40
+ handleUploadProgress: vi.fn(),
41
+ cleanup: vi.fn(),
42
+ })),
43
+ };
44
+ });
45
+
46
+ describe("UploadistaProvider", () => {
47
+ describe("context provision", () => {
48
+ it("should render children", () => {
49
+ render(
50
+ <UploadistaProvider
51
+ baseUrl="https://api.example.com"
52
+ storageId="test"
53
+ chunkSize={1024 * 1024}
54
+ storeFingerprintForResuming={true}
55
+ >
56
+ <div data-testid="child">Child content</div>
57
+ </UploadistaProvider>,
58
+ );
59
+
60
+ expect(screen.getByTestId("child")).toHaveTextContent("Child content");
61
+ });
62
+
63
+ it("should provide context to children", () => {
64
+ const TestComponent = () => {
65
+ const context = useUploadistaContext();
66
+ return (
67
+ <div data-testid="result">
68
+ {context.client ? "has-client" : "no-client"}
69
+ </div>
70
+ );
71
+ };
72
+
73
+ render(
74
+ <UploadistaProvider
75
+ baseUrl="https://api.example.com"
76
+ storageId="test"
77
+ chunkSize={1024 * 1024}
78
+ storeFingerprintForResuming={true}
79
+ >
80
+ <TestComponent />
81
+ </UploadistaProvider>,
82
+ );
83
+
84
+ expect(screen.getByTestId("result")).toHaveTextContent("has-client");
85
+ });
86
+
87
+ it("should provide client and config through context", () => {
88
+ const TestComponent = () => {
89
+ const { client, config } = useUploadistaContext();
90
+ return (
91
+ <div>
92
+ <div data-testid="client">{client ? "present" : "missing"}</div>
93
+ <div data-testid="baseUrl">{config.baseUrl}</div>
94
+ <div data-testid="storageId">{config.storageId}</div>
95
+ </div>
96
+ );
97
+ };
98
+
99
+ render(
100
+ <UploadistaProvider
101
+ baseUrl="https://api.example.com"
102
+ storageId="my-storage"
103
+ chunkSize={1024 * 1024}
104
+ storeFingerprintForResuming={true}
105
+ >
106
+ <TestComponent />
107
+ </UploadistaProvider>,
108
+ );
109
+
110
+ expect(screen.getByTestId("client")).toHaveTextContent("present");
111
+ expect(screen.getByTestId("baseUrl")).toHaveTextContent(
112
+ "https://api.example.com",
113
+ );
114
+ expect(screen.getByTestId("storageId")).toHaveTextContent("my-storage");
115
+ });
116
+ });
117
+
118
+ describe("useUploadistaContext", () => {
119
+ it("should throw error when used outside provider", () => {
120
+ const TestComponent = () => {
121
+ useUploadistaContext();
122
+ return null;
123
+ };
124
+
125
+ expect(() => {
126
+ render(<TestComponent />);
127
+ }).toThrow(
128
+ "useUploadistaContext must be used within an UploadistaProvider",
129
+ );
130
+ });
131
+
132
+ it("should return context value when used within provider", () => {
133
+ const wrapper = ({ children }: { children: ReactNode }) => (
134
+ <UploadistaProvider
135
+ baseUrl="https://api.example.com"
136
+ storageId="test"
137
+ chunkSize={1024 * 1024}
138
+ storeFingerprintForResuming={true}
139
+ >
140
+ {children}
141
+ </UploadistaProvider>
142
+ );
143
+
144
+ const { result } = renderHook(() => useUploadistaContext(), { wrapper });
145
+
146
+ expect(result.current.client).toBeDefined();
147
+ expect(result.current.config).toBeDefined();
148
+ expect(result.current.subscribeToEvents).toBeDefined();
149
+ expect(typeof result.current.subscribeToEvents).toBe("function");
150
+ });
151
+ });
152
+
153
+ describe("event subscription", () => {
154
+ it("should provide subscribeToEvents function", () => {
155
+ const wrapper = ({ children }: { children: ReactNode }) => (
156
+ <UploadistaProvider
157
+ baseUrl="https://api.example.com"
158
+ storageId="test"
159
+ chunkSize={1024 * 1024}
160
+ storeFingerprintForResuming={true}
161
+ >
162
+ {children}
163
+ </UploadistaProvider>
164
+ );
165
+
166
+ const { result } = renderHook(() => useUploadistaContext(), { wrapper });
167
+
168
+ expect(typeof result.current.subscribeToEvents).toBe("function");
169
+ });
170
+
171
+ it("should allow subscribing to events", () => {
172
+ const wrapper = ({ children }: { children: ReactNode }) => (
173
+ <UploadistaProvider
174
+ baseUrl="https://api.example.com"
175
+ storageId="test"
176
+ chunkSize={1024 * 1024}
177
+ storeFingerprintForResuming={true}
178
+ >
179
+ {children}
180
+ </UploadistaProvider>
181
+ );
182
+
183
+ const { result } = renderHook(() => useUploadistaContext(), { wrapper });
184
+
185
+ const handler = vi.fn();
186
+ const unsubscribe = result.current.subscribeToEvents(handler);
187
+
188
+ expect(typeof unsubscribe).toBe("function");
189
+ });
190
+
191
+ it("should return unsubscribe function from subscribeToEvents", () => {
192
+ const wrapper = ({ children }: { children: ReactNode }) => (
193
+ <UploadistaProvider
194
+ baseUrl="https://api.example.com"
195
+ storageId="test"
196
+ chunkSize={1024 * 1024}
197
+ storeFingerprintForResuming={true}
198
+ >
199
+ {children}
200
+ </UploadistaProvider>
201
+ );
202
+
203
+ const { result } = renderHook(() => useUploadistaContext(), { wrapper });
204
+
205
+ const handler = vi.fn();
206
+ const unsubscribe = result.current.subscribeToEvents(handler);
207
+
208
+ // Should not throw when called
209
+ expect(() => unsubscribe()).not.toThrow();
210
+ });
211
+ });
212
+
213
+ describe("options passthrough", () => {
214
+ it("should pass all options to the client", () => {
215
+ const TestComponent = () => {
216
+ const { config } = useUploadistaContext();
217
+ return (
218
+ <div>
219
+ <div data-testid="chunkSize">{config.chunkSize}</div>
220
+ <div data-testid="parallelUploads">{config.parallelUploads}</div>
221
+ </div>
222
+ );
223
+ };
224
+
225
+ render(
226
+ <UploadistaProvider
227
+ baseUrl="https://api.example.com"
228
+ storageId="test"
229
+ chunkSize={2 * 1024 * 1024}
230
+ parallelUploads={4}
231
+ storeFingerprintForResuming={true}
232
+ >
233
+ <TestComponent />
234
+ </UploadistaProvider>,
235
+ );
236
+
237
+ expect(screen.getByTestId("chunkSize")).toHaveTextContent(
238
+ String(2 * 1024 * 1024),
239
+ );
240
+ expect(screen.getByTestId("parallelUploads")).toHaveTextContent("4");
241
+ });
242
+ });
243
+
244
+ describe("nested providers", () => {
245
+ it("should allow nested providers with different configs", () => {
246
+ const TestComponent = ({ testId }: { testId: string }) => {
247
+ const { config } = useUploadistaContext();
248
+ return <div data-testid={testId}>{config.storageId}</div>;
249
+ };
250
+
251
+ render(
252
+ <UploadistaProvider
253
+ baseUrl="https://api.example.com"
254
+ storageId="outer-storage"
255
+ chunkSize={1024 * 1024}
256
+ storeFingerprintForResuming={true}
257
+ >
258
+ <TestComponent testId="outer" />
259
+ <UploadistaProvider
260
+ baseUrl="https://api.example.com"
261
+ storageId="inner-storage"
262
+ chunkSize={1024 * 1024}
263
+ storeFingerprintForResuming={true}
264
+ >
265
+ <TestComponent testId="inner" />
266
+ </UploadistaProvider>
267
+ </UploadistaProvider>,
268
+ );
269
+
270
+ expect(screen.getByTestId("outer")).toHaveTextContent("outer-storage");
271
+ expect(screen.getByTestId("inner")).toHaveTextContent("inner-storage");
272
+ });
273
+ });
274
+
275
+ describe("context stability", () => {
276
+ it("should maintain stable context value reference", async () => {
277
+ const contextValues: any[] = [];
278
+
279
+ const TestComponent = () => {
280
+ const context = useUploadistaContext();
281
+ contextValues.push(context);
282
+ return null;
283
+ };
284
+
285
+ const { rerender } = render(
286
+ <UploadistaProvider
287
+ baseUrl="https://api.example.com"
288
+ storageId="test"
289
+ chunkSize={1024 * 1024}
290
+ storeFingerprintForResuming={true}
291
+ >
292
+ <TestComponent />
293
+ </UploadistaProvider>,
294
+ );
295
+
296
+ // Re-render with same props
297
+ rerender(
298
+ <UploadistaProvider
299
+ baseUrl="https://api.example.com"
300
+ storageId="test"
301
+ chunkSize={1024 * 1024}
302
+ storeFingerprintForResuming={true}
303
+ >
304
+ <TestComponent />
305
+ </UploadistaProvider>,
306
+ );
307
+
308
+ await waitFor(() => {
309
+ expect(contextValues.length).toBe(2);
310
+ });
311
+
312
+ // Both context values should have the same client reference
313
+ expect(contextValues[0].client).toBe(contextValues[1].client);
314
+ });
315
+ });
316
+ });