-
Notifications
You must be signed in to change notification settings - Fork 413
/
Copy pathuse-user.integration.test.tsx
149 lines (124 loc) · 4.41 KB
/
use-user.integration.test.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/**
* @vitest-environment jsdom
*/
import React from "react";
import { act, renderHook, waitFor } from "@testing-library/react";
import * as swrModule from "swr";
import {
afterEach,
beforeEach,
describe,
expect,
it,
vi,
type MockInstance
} from "vitest";
import type { User } from "../../types/index.js";
import { useUser } from "./use-user.js";
// New test suite for integration testing with fetch and SWR cache
describe("useUser Integration with SWR Cache", () => {
const initialUser: User = {
sub: "initial_user_123",
name: "Initial User",
email: "[email protected]"
};
const updatedUser: User = {
sub: "updated_user_456",
name: "Updated User",
email: "[email protected]"
};
// Explicitly type fetchSpy using MockInstance and the global fetch signature
let fetchSpy: MockInstance<
(
input: RequestInfo | URL,
init?: RequestInit | undefined
) => Promise<Response>
>;
beforeEach(() => {
// Mock the global fetch
fetchSpy = vi.spyOn(global, "fetch");
});
afterEach(() => {
vi.restoreAllMocks(); // Restore original fetch implementation
});
it("should fetch initial user data and update after invalidate", async () => {
// Mock fetch to return initial data first
fetchSpy.mockResolvedValueOnce(
new Response(JSON.stringify(initialUser), {
status: 200,
headers: { "Content-Type": "application/json" }
})
);
const wrapper = ({ children }: { children: React.ReactNode }) => (
<swrModule.SWRConfig value={{ provider: () => new Map() }}>
{children}
</swrModule.SWRConfig>
);
const { result } = renderHook(() => useUser(), { wrapper });
// Wait for the initial data to load
await waitFor(() => expect(result.current.isLoading).toBe(false));
// Assert initial state
expect(result.current.user).toEqual(initialUser);
expect(result.current.error).toBe(null);
// Mock fetch to return updated data for the next call
fetchSpy.mockResolvedValueOnce(
new Response(JSON.stringify(updatedUser), {
status: 200,
headers: { "Content-Type": "application/json" }
})
);
// Call invalidate to trigger re-fetch
await act(async () => {
result.current.invalidate();
});
// Wait for the hook to reflect the updated data
await waitFor(() => expect(result.current.user).toEqual(updatedUser));
// Assert updated state
expect(result.current.user).toEqual(updatedUser);
expect(result.current.error).toBe(null);
expect(result.current.isLoading).toBe(false);
// Verify fetch was called twice (initial load + invalidate)
expect(fetchSpy).toHaveBeenCalledTimes(2);
expect(fetchSpy).toHaveBeenCalledWith("/auth/profile");
});
it("should handle fetch error during invalidation", async () => {
// Mock fetch to return initial data first
fetchSpy.mockResolvedValueOnce(
new Response(JSON.stringify(initialUser), {
status: 200,
headers: { "Content-Type": "application/json" }
})
);
const wrapper = ({ children }: { children: React.ReactNode }) => (
<swrModule.SWRConfig
value={{
provider: () => new Map(),
shouldRetryOnError: false,
dedupingInterval: 0
}}
>
{children}
</swrModule.SWRConfig>
);
const { result } = renderHook(() => useUser(), { wrapper });
// Wait for the initial data to load
await waitFor(() => expect(result.current.isLoading).toBe(false));
expect(result.current.user).toEqual(initialUser);
// Mock fetch to return an error for the next call
const fetchError = new Error("Network Error");
fetchSpy.mockRejectedValueOnce(fetchError);
// Call invalidate to trigger re-fetch
await act(async () => {
result.current.invalidate();
});
// Wait for the hook to reflect the error state, user should still be the initial one before error
await waitFor(() => expect(result.current.error).not.toBeNull());
// Assert error state - SWR catches the rejection from fetch itself.
// Check for the message of the error we explicitly rejected with.
expect(result.current.user).toBeNull(); // Expect null now, not stale data
expect(result.current.error?.message).toBe(fetchError.message); // Correct assertion
expect(result.current.isLoading).toBe(false);
// Verify fetch was called twice
expect(fetchSpy).toHaveBeenCalledTimes(2);
});
});