import { type RenderHookOptions, type RenderHookResult } from "@testing-library/react"; import type { AxiosInstance, AxiosError, AxiosResponse, InternalAxiosRequestConfig } from "axios"; import { type SublayContextValues } from "../context/sublay-context"; import type { AuthUser } from "../interfaces/models/User"; export type AxiosMethod = "get" | "post" | "put" | "patch" | "delete"; export interface AxiosCallRecord { url: string; body?: unknown; config?: Record; } export interface AxiosMockHandle { instance: AxiosInstance; mockResponse: (method: AxiosMethod, data: T, status?: number) => void; mockError: (method: AxiosMethod, status: number, data?: unknown) => void; mockNetworkError: (method: AxiosMethod, message?: string) => void; calls: (method: AxiosMethod) => AxiosCallRecord[]; } declare function toAxiosResponse(data: T, status: number, config?: InternalAxiosRequestConfig): AxiosResponse; declare function toAxiosError(status: number, data?: unknown, config?: InternalAxiosRequestConfig): AxiosError; /** * Spies on every HTTP verb of a real axios instance so hook code that calls * `axios.get/post/...` hits a controllable mock instead of the network. This * bypasses the instance's interceptors entirely (auth-header injection, * 403-refresh) — that layer gets its own dedicated coverage in Task 1.3. */ export declare function mockAxiosInstance(instance: AxiosInstance): AxiosMockHandle; export declare const mockAxiosPrivate: () => AxiosMockHandle; export declare const mockAxiosPublic: () => AxiosMockHandle; export type AxiosAdapter = (config: InternalAxiosRequestConfig) => Promise; /** * Installs a custom adapter on a real axios instance so its interceptors run * for real while the actual network call is replaced. Use this (instead of * `mockAxiosInstance`) when the test needs the interceptor behavior itself — * e.g. `useAxiosPrivate`'s auth-header injection and 403-refresh retry. Build * responses/errors with `okAxiosResponse`/`axiosErrorWithStatus` so the * resolved/rejected value carries the real request `config` through (the * refresh-retry interceptor reuses it to re-issue the original request). */ export declare function stubAxiosAdapter(instance: AxiosInstance, adapter: AxiosAdapter): void; export declare const okAxiosResponse: typeof toAxiosResponse; export declare const axiosErrorWithStatus: typeof toAxiosError; /** * Restores every axios spy created via `mockAxiosPrivate`/`mockAxiosPublic` * (and any other `vi.spyOn`/`vi.fn` mock), clears any interceptors a hook * test attached via `renderHook(() => useAxiosPrivate())`, and restores the * default adapter after a `stubAxiosAdapter` call. Call from `afterEach` — * the axios instances in `config/axios.ts` are module-level singletons * shared by every hook in the package, so this state leaks across test * cases/files if not reset. */ export declare function resetAxiosMocks(): void; /** A real store with just the `sublay` slice mounted — no RTK-Query `baseApi`. */ export declare function makeSublayStore(): import("@reduxjs/toolkit").EnhancedStore<{ sublay: { auth: import("../store/slices/authSlice").AuthState; appNotifications: import("../store/slices/appNotificationsSlice").AppNotificationsState; collections: import("../store/slices/collectionsSlice").CollectionsState; user: import("../store/slices/userSlice").UserState; entityLists: import("../store/slices/entityListsSlice").EntityListsState; spaceLists: import("../store/slices/spaceListsSlice").SpaceListsState; accounts: import("../store/slices/accountsSlice").AccountsState; chat: import("..").ChatState; tables: import("../store/slices/tablesSlice").TablesState; }; }, import("@reduxjs/toolkit").UnknownAction, import("@reduxjs/toolkit").Tuple<[import("@reduxjs/toolkit").StoreEnhancer<{ dispatch: import("@reduxjs/toolkit").ThunkDispatch<{ sublay: { auth: import("../store/slices/authSlice").AuthState; appNotifications: import("../store/slices/appNotificationsSlice").AppNotificationsState; collections: import("../store/slices/collectionsSlice").CollectionsState; user: import("../store/slices/userSlice").UserState; entityLists: import("../store/slices/entityListsSlice").EntityListsState; spaceLists: import("../store/slices/spaceListsSlice").SpaceListsState; accounts: import("../store/slices/accountsSlice").AccountsState; chat: import("..").ChatState; tables: import("../store/slices/tablesSlice").TablesState; }; }, undefined, import("@reduxjs/toolkit").UnknownAction>; }>, import("@reduxjs/toolkit").StoreEnhancer]>>; export type SublayStore = ReturnType; export interface RenderHookWithAxiosOptions extends Omit, "wrapper"> { projectId?: string; project?: SublayContextValues["project"]; user?: AuthUser | null; accessToken?: string | null; refreshToken?: string | null; /** * Called with the (already-installed) axios mock handles right before the * hook is mounted. Use this to queue responses for hooks that fire a * request from a mount-time `useEffect` — without it, the effect's request * would race ahead of any `mockResponse`/`mockError` call made after this * function returns and fall through to the real, un-mocked axios method. */ beforeRender?: (handles: { axiosPrivate: AxiosMockHandle; axiosPublic: AxiosMockHandle; }) => void; } export interface RenderHookWithAxiosResult extends RenderHookResult { store: SublayStore; axiosPrivate: AxiosMockHandle; axiosPublic: AxiosMockHandle; } /** * Renders a core hook wrapped in `SublayContext` plus a real (sublay-slice-only) * Redux store, with both axios instances pre-mocked. Covers the common * direct-axios hook shape: `useProject()` + `useUser()`/`useAuth()` + * `useAxiosPrivate()`/the public `axios` instance. */ export declare function renderHookWithAxios(callback: (props: Props) => Result, options?: RenderHookWithAxiosOptions): RenderHookWithAxiosResult; export declare function makeAuthUser(overrides?: Partial): AuthUser; export {};