UNPKG

next-auth

Version:

Authentication for Next.js

189 lines (143 loc) 5.23 kB
import { rest } from "msw" import { render, screen, waitFor } from "@testing-library/react" import { server, mockSession } from "./helpers/mocks" import { printFetchCalls } from "./helpers/utils" import { SessionProvider, useSession, signOut, getSession } from "../../react" const origDocumentVisibility = document.visibilityState const fetchSpy = jest.spyOn(global, "fetch") beforeAll(() => { server.listen() }) afterEach(() => { server.resetHandlers() changeTabVisibility(origDocumentVisibility) fetchSpy.mockClear() }) afterAll(() => { server.close() }) test("fetches the session once and re-uses it for different consumers", async () => { render(<ProviderFlow />) expect(screen.getByTestId("session-1")).toHaveTextContent("loading") expect(screen.getByTestId("session-2")).toHaveTextContent("loading") return waitFor(() => { expect(fetchSpy).toHaveBeenCalledTimes(1) expect(fetchSpy).toHaveBeenCalledWith( "/api/auth/session", expect.anything() ) const session1 = screen.getByTestId("session-1").textContent const session2 = screen.getByTestId("session-2").textContent expect(session1).toEqual(session2) }) }) test("when there's an existing session, it won't try to fetch a new one straightaway", async () => { render(<ProviderFlow session={mockSession} />) expect(fetchSpy).not.toHaveBeenCalled() }) test("will refetch the session when the browser tab becomes active again", async () => { render(<ProviderFlow session={mockSession} />) expect(fetchSpy).not.toHaveBeenCalled() // Hide the current tab changeTabVisibility("hidden") // Given the current tab got hidden, it should not attempt to re-fetch the session expect(fetchSpy).not.toHaveBeenCalled() // Make the tab again visible changeTabVisibility("visible") // Given the user made the tab visible again, now attempts to sync and re-fetch the session return waitFor(() => { expect(fetchSpy).toHaveBeenCalledTimes(1) expect(fetchSpy).toHaveBeenCalledWith( "/api/auth/session", expect.anything() ) }) }) test("will refetch the session if told to do so programmatically from another window", async () => { render(<ProviderFlow session={mockSession} />) expect(fetchSpy).not.toHaveBeenCalled() // Hide the current tab changeTabVisibility("hidden") // Given the current tab got hidden, it should not attempt to re-fetch the session expect(fetchSpy).not.toHaveBeenCalled() // simulate sign-out triggered by another tab signOut({ redirect: false }) // Given signed out in another tab, it attempts to sync and re-fetch the session return waitFor(() => { expect(fetchSpy).toHaveBeenCalledWith( "/api/auth/session", expect.anything() ) // We should have a call to sign-out and a call to refetch the session accordingly expect(printFetchCalls(fetchSpy.mock.calls)).toMatchInlineSnapshot(` Array [ "GET /api/auth/csrf", "POST /api/auth/signout", "GET /api/auth/session", ] `) }) }) test("allows to customize how often the session will be re-fetched through polling", () => { jest.useFakeTimers() render(<ProviderFlow session={mockSession} refetchInterval={1} />) // we provided a mock session so it shouldn't try to fetch a new one expect(fetchSpy).not.toHaveBeenCalled() jest.advanceTimersByTime(1000) expect(fetchSpy).toHaveBeenCalledTimes(1) expect(fetchSpy).toHaveBeenCalledWith("/api/auth/session", expect.anything()) jest.advanceTimersByTime(1000) // it should have tried to refetch the session, hence counting 2 calls to the session endpoint expect(fetchSpy).toHaveBeenCalledTimes(2) expect(printFetchCalls(fetchSpy.mock.calls)).toMatchInlineSnapshot(` Array [ "GET /api/auth/session", "GET /api/auth/session", ] `) }) test("allows to customize the URL for session fetching", async () => { const myPath = "/api/v1/auth" server.use( rest.get(`${myPath}/session`, (req, res, ctx) => res(ctx.status(200), ctx.json(mockSession)) ) ) render(<ProviderFlow session={mockSession} basePath={myPath} />) // there's an existing session so it should not try to fetch a new one expect(fetchSpy).not.toHaveBeenCalled() // force a session refetch across all clients... getSession() return waitFor(() => { expect(fetchSpy).toHaveBeenCalledTimes(1) expect(fetchSpy).toHaveBeenCalledWith( `${myPath}/session`, expect.anything() ) }) }) function ProviderFlow(props) { return ( <SessionProvider {...props}> <SessionConsumer /> <SessionConsumer testId="2" /> </SessionProvider> ) } function SessionConsumer({ testId = 1, ...rest }) { const { data: session, status } = useSession(rest) return ( <div data-testid={`session-${testId}`}> {status === "loading" ? "loading" : JSON.stringify(session)} </div> ) } function changeTabVisibility(status) { const visibleStates = ["visible", "hidden"] if (!visibleStates.includes(status)) return Object.defineProperty(document, "visibilityState", { configurable: true, value: status, }) document.dispatchEvent(new Event("visibilitychange")) }