next-test-api-route-handler
Version:
Confidently unit and integration test your Next.js API routes/handlers in an isolated Next.js-like environment
179 lines • 7.56 kB
TypeScript
import type { IncomingMessage, ServerResponse } from 'node:http';
import type { NextApiHandler } from 'next';
import type { NextRequest } from 'next/server';
/**
* @internal
*/
export declare const $originalGlobalFetch: unique symbol;
/**
* @internal
*/
export declare const $isPatched: unique symbol;
/**
* @internal
*/
export type Promisable<Promised> = Promised | Promise<Promised>;
/**
* @internal
*/
export type FetchReturnType<NextResponseJsonType> = Promise<Omit<Response, 'json'> & {
json: (...args: Parameters<Response['json']>) => Promise<NextResponseJsonType>;
cookies: ReturnType<typeof import('cookie').parse>[];
}>;
export interface NtarhInit<NextResponseJsonType = unknown> {
/**
* If `false`, errors thrown from within a handler are kicked up to Next.js's
* resolver to deal with, which is what would happen in production. If `true`,
* the {@link testApiHandler} function will reject immediately instead.
*
* You should use `rejectOnHandlerError` whenever you want to manually handle
* an error that bubbles up from your handler (which is especially true if
* you're using `expect` _within_ your handler) or when you notice a false
* negative despite exceptions being thrown.
*
* @default false
*/
rejectOnHandlerError?: boolean;
/**
* `test` is a function that runs your test assertions. This function receives
* one destructured parameter: `fetch`, which is equivalent to
* `globalThis.fetch` but with the first parameter omitted.
*/
test: (parameters: {
fetch: (customInit?: RequestInit) => FetchReturnType<NextResponseJsonType>;
}) => Promisable<void>;
}
type AppRouteUserlandModule = import('next/dist/server/route-modules/app-route/module').AppRouteUserlandModule;
/**
* The parameters expected by `testApiHandler` when using `appHandler`.
*/
export interface NtarhInitAppRouter<NextResponseJsonType = unknown> extends NtarhInit<NextResponseJsonType> {
/**
* The actual App Router route handler under test. It should be an object
* containing one or more async functions named for valid HTTP methods and/or
* a valid configuration option. See [the Next.js
* documentation](https://nextjs.org/docs/app/building-your-application/routing/route-handlers)
* for details.
*/
appHandler: Partial<Omit<AppRouteUserlandModule, keyof import('next/dist/server/route-modules/app-route/module').AppRouteHandlers> & { [key in keyof import('next/dist/server/route-modules/app-route/module').AppRouteHandlers]?: (req: NextRequest, segmentData?: any) => any }>;
pagesHandler?: undefined;
/**
* `params` is passed directly to the handler and represents processed dynamic
* routes. This should not be confused with query string parsing, which is
* handled by `Request` automatically.
*
* `params: { id: 'some-id' }` is shorthand for `paramsPatcher: (params) => {
* params.id = 'some-id' }`. This is useful for quickly setting many params at
* once.
*/
params?: Record<string, string | string[]>;
/**
* A function that receives `params`, an object representing "processed"
* dynamic route parameters. Modifications to `params` are passed directly to
* the handler. You can also return a custom object from this function which
* will replace `params` entirely.
*
* Parameter patching should not be confused with query string parsing, which
* is handled by `Request` automatically.
*/
paramsPatcher?: (params: Record<string, string | string[]>) => Promisable<void | Record<string, string | string[]>>;
/**
* A function that receives a `NextRequest` object and returns a `Request`
* instance. Use this function to edit the request _before_ it's injected
* into the handler.
*
* If the returned `Request` instance is not also an instance of
* `NextRequest`, it will be wrapped with `NextRequest`, e.g. `new
* NextRequest(returnedRequest, { ... })`.
*/
requestPatcher?: (request: import('next/server').NextRequest) => Promisable<void | Request>;
/**
* A function that receives the `Response` object returned from `appHandler`
* and returns a `Response` instance. Use this function to edit the response
* _after_ your handler runs but _before_ it's processed by the server.
*
* Note that `responsePatcher` is called even in the case of exceptions,
* including _unhandled exceptions_ (for which Next.js returns an HTTP 500
* response). The only time `responsePatcher` is not called is when an
* unhandled exception occurs _and_ `rejectOnHandlerError` is `true`.
*/
responsePatcher?: (res: Response) => Promisable<void | Response>;
/**
* `url: 'your-url'` is shorthand for `requestPatcher: (request) => new
* NextRequest('your-url', request)`
*/
url?: string;
}
/**
* The parameters expected by `testApiHandler` when using `pagesHandler`.
*/
export interface NtarhInitPagesRouter<NextResponseJsonType = unknown> extends NtarhInit<NextResponseJsonType> {
/**
* The actual Pages Router route handler under test. It should be an async
* function that accepts `NextApiRequest` and `NextApiResult` objects (in
* that order) as its two parameters.
*
* Note that type checking for `res.send` and similar methods was retired in
* NTARH@4. Only the `response.json` method returned by NTARH's fetch wrapper
* will have a typed result.
*/
pagesHandler: NextApiHandler | {
default: NextApiHandler;
};
appHandler?: undefined;
/**
* `params` is passed directly to the handler and represents processed dynamic
* routes. This should not be confused with query string parsing, which is
* handled automatically.
*
* `params: { id: 'some-id' }` is shorthand for `paramsPatcher: (params) => {
* params.id = 'some-id' }`. This is useful for quickly setting many params at
* once.
*/
params?: Record<string, unknown>;
/**
* A function that receives `params`, an object representing "processed"
* dynamic route parameters. Modifications to `params` are passed directly to
* the handler. You can also return a custom object from this function which
* will replace `params` entirely.
*
* Parameter patching should not be confused with query string parsing, which
* is handled automatically.
*/
paramsPatcher?: (params: Record<string, unknown>) => Promisable<void | Record<string, unknown>>;
/**
* A function that receives an `IncomingMessage` object. Use this function
* to edit the request _before_ it's injected into the handler.
*
* **Note: all replacement `IncomingMessage.header` names must be
* lowercase.**
*/
requestPatcher?: (request: IncomingMessage) => Promisable<void>;
/**
* A function that receives a `ServerResponse` object. Use this function
* to edit the response _before_ it's injected into the handler.
*/
responsePatcher?: (res: ServerResponse) => Promisable<void>;
/**
* `url: 'your-url'` is shorthand for `requestPatcher: (req) => { req.url =
* 'your-url' }`
*/
url?: string;
}
/**
* Uses Next's internal `apiResolver` (for Pages Router) or an
* `AppRouteRouteModule` instance (for App Router) to execute api route handlers
* in a Next-like testing environment.
*/
export declare function testApiHandler<NextResponseJsonType = any>({
rejectOnHandlerError,
requestPatcher,
responsePatcher,
paramsPatcher,
params,
url,
pagesHandler: pagesHandler_,
appHandler,
test
}: NtarhInitAppRouter<NextResponseJsonType> | NtarhInitPagesRouter<NextResponseJsonType>): Promise<void>;
export {};