@auth0/nextjs-auth0
Version:
Auth0 Next.js SDK
108 lines (107 loc) • 5.15 kB
JavaScript
/**
* @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 } from "vitest";
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 = {
sub: "initial_user_123",
name: "Initial User",
email: "initial@example.com"
};
const updatedUser = {
sub: "updated_user_456",
name: "Updated User",
email: "updated@example.com"
};
// Explicitly type fetchSpy using MockInstance and the global fetch signature
let fetchSpy;
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 }) => (React.createElement(swrModule.SWRConfig, { value: { provider: () => new Map() } }, children));
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 }) => (React.createElement(swrModule.SWRConfig, { value: {
provider: () => new Map(),
shouldRetryOnError: false,
dedupingInterval: 0
} }, children));
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);
});
it("should handle unauthenticated requests to the profile endpoint", async () => {
fetchSpy.mockResolvedValueOnce(new Response(null, {
status: 204
}));
const wrapper = ({ children }) => (React.createElement(swrModule.SWRConfig, { value: { provider: () => new Map() } }, children));
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(null);
expect(result.current.error).toBe(undefined);
expect(fetchSpy).toHaveBeenCalledOnce();
expect(fetchSpy).toHaveBeenCalledWith("/auth/profile");
});
});