### Setup Test Environment for Apollo GraphQL Example Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Provides bash commands to set up a testing environment for a Next.js Apollo GraphQL example. It clones the Next.js repository, navigates to the example directory, and installs necessary dependencies, including `next-test-api-route-handler` and testing tools. ```bash git clone --depth=1 https://github.com/vercel/next.js /tmp/ntarh-test cd /tmp/ntarh-test/examples/api-routes-apollo-server-and-client npm install --force npm install --force next-test-api-route-handler jest babel-jest @babel/core @babel/preset-env # You could test with an older version of Next.js if you want, e.g.: # npm install --force next@9.0.6 # Or even older: ``` -------------------------------- ### Install Dependencies and Setup for Apollo Integration (Bash) Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md This bash script installs necessary dependencies, configures Babel, and downloads route and test files for integrating Apollo with Next.js App Router using next-test-api-route-handler. It requires a nix-like environment, curl, node, and git. ```bash mkdir -p /tmp/ntarh-test/test cd /tmp/ntarh-test npm install --force next @apollo/server @as-integrations/next graphql-tag next-test-api-route-handler jest babel-jest @babel/core @babel/preset-env echo 'module.exports={presets:["@babel/preset-env"]};' > babel.config.js mkdir -p app/api/graphql curl -o app/api/graphql/route.js https://raw.githubusercontent.com/Xunnamius/next-test-api-route-handler/main/apollo_test_raw_app_route curl -o test/integration.test.js https://raw.githubusercontent.com/Xunnamius/next-test-api-route-handler/main/apollo_test_raw_app_test npx jest ``` -------------------------------- ### Quick Start: App Router Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md A guide to testing API routes within the Next.js App Router using `testApiHandler`, including optional request and response patching. ```APIDOC ## Quick Start: App Router ### Description This example demonstrates how to test an API route handler in the Next.js App Router. It shows the basic setup, including importing the handler, using `requestPatcher` to modify outgoing requests, and `responsePatcher` to transform responses before assertion. ### Method `testApiHandler` function call ### Endpoint N/A (Testing local handlers) ### Parameters `options` object for `testApiHandler`: - **appHandler** (object) - The API route handler exported from the app directory. - **requestPatcher** (function, optional) - A function to modify the request before it's sent. - **responsePatcher** (function, optional) - An async function to modify the response after it's received. - **test** (function) - An async function containing the test logic, receiving a `fetch` function. ### Request Example ```typescript /* File: test/unit.test.ts */ import { testApiHandler } from 'next-test-api-route-handler'; // ◀️ Must be first import // Import the handler under test from the app directory import * as appHandler from '../app/your-endpoint/route'; it('does what I want', async () => { await testApiHandler({ appHandler, // requestPatcher is optional requestPatcher(request) { request.headers.set('key', process.env.SPECIAL_TOKEN); }, // responsePatcher is optional async responsePatcher(response) { const json = await response.json(); return Response.json( json.apiSuccess ? { hello: 'world!' } : { goodbye: 'cruel world' } ); }, async test({ fetch }) { const res = await fetch({ method: 'POST', body: 'dummy-data' }); await expect(res.json()).resolves.toStrictEqual({ hello: 'world!' }); // ◀️ Passes! } }); }); ``` ### Response N/A (Assertions are made within the `test` function) ### Response Example N/A ``` -------------------------------- ### Install Dependencies and Configure Environment Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/MAINTAINING.md Installs project dependencies and sets up the local environment for manual releases. It copies the default environment file and installs packages using npm ci. Ensure you do not commit the .env file. ```bash # 1. Install dependencies and add your auth tokens to the .env file. # # ! DO NOT COMMIT THE .env FILE ! cp .env.default .env npm ci ``` -------------------------------- ### Install Dependencies and Configure Babel Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Installs specific versions of Next.js and next-test-api-route-handler, forces the installation to resolve potential peer dependency issues, and creates a Babel configuration file for the project. ```bash npm install --force next@9.0.0 next-server echo 'module.exports={presets:["@babel/preset-env"]};' > babel.config.js ``` -------------------------------- ### Quick Start: Edge Runtime Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Instructions for testing API routes designed for the Edge Runtime using `testApiHandler`, including generic type support for responses. ```APIDOC ## Quick Start: Edge Runtime ### Description This example shows how to test an API route handler intended for the Next.js Edge Runtime. It highlights the use of TypeScript generics for typed response data and demonstrates how to handle request bodies like readable streams. ### Method `testApiHandler` function call ### Endpoint N/A (Testing local handlers) ### Parameters `options` object for `testApiHandler`: - **appHandler** (object) - The API route handler exported from the app directory (required for Edge). - **requestPatcher** (function, optional) - A function to modify the request, potentially creating a new `Request` object. - **test** (function) - An async function containing the test logic, receiving a `fetch` function. ### Request Example ```typescript /* File: test/unit.test.ts */ import { testApiHandler } from 'next-test-api-route-handler'; // ◀️ Must be first import // Import the handler under test from the app directory import * as edgeHandler from '../app/your-edge-endpoint/route'; it('does what I want', async function () { // NTARH supports optionally typed response data via TypeScript generics: await testApiHandler<{ success: boolean }>({ // Only appHandler supports edge functions. The pagesHandler prop does not! appHandler: edgeHandler, // requestPatcher is optional requestPatcher(request) { return new Request(request, { body: dummyReadableStream, duplex: 'half' }); }, async test({ fetch }) { // The next line would cause TypeScript to complain: // const { luck: success } = await (await fetch()).json(); await expect((await fetch()).json()).resolves.toStrictEqual({ success: true // ◀️ Passes! }); } }); }); ``` ### Response N/A (Assertions are made within the `test` function) ### Response Example N/A ``` -------------------------------- ### Quick Start: Pages Router Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md A guide for testing API routes in the Next.js Pages Router using `testApiHandler`, including request header manipulation and typed response data. ```APIDOC ## Quick Start: Pages Router ### Description This example illustrates how to test an API route handler within the Next.js Pages Router. It covers importing the handler and its configuration, modifying request headers, and using TypeScript generics for typed response data. ### Method `testApiHandler` function call ### Endpoint N/A (Testing local handlers) ### Parameters `options` object for `testApiHandler`: - **pagesHandler** (object) - The API route handler exported from the pages/api directory. - **requestPatcher** (function, optional) - A function to modify the request object. - **test** (function) - An async function containing the test logic, receiving a `fetch` function. ### Request Example ```typescript /* File: test/unit.test.ts */ import { testApiHandler } from 'next-test-api-route-handler'; // ◀️ Must be first import // Import the handler under test and its config from the pages/api directory import * as pagesHandler from '../pages/api/your-endpoint'; it('does what I want', async () => { // NTARH supports optionally typed response data via TypeScript generics: await testApiHandler<{ hello: string }>({ pagesHandler, requestPatcher: (req) => { req.headers = { key: process.env.SPECIAL_TOKEN }; }, test: async ({ fetch }) => { const res = await fetch({ method: 'POST', body: 'data' }); // The next line would cause TypeScript to complain: // const { goodbye: hello } = await res.json(); const { hello } = await res.json(); expect(hello).toBe('world'); // ◀️ Passes! } }); }); ``` ### Response N/A (Assertions are made within the `test` function) ### Response Example N/A ``` -------------------------------- ### Manually Install Next.js and Peer Dependencies (Shell) Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md This command installs the 'next' package and 'react' as development dependencies. It is necessary when using npm version < 7 or node version < 15, as these versions do not install peer dependencies by default. Ensure you install the specific peer dependencies required by your Next.js version. ```shell npm install --save-dev next@latest react ``` -------------------------------- ### Install Next Server for Older Next.js Versions (Shell) Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md This command installs the 'next-server' package as a development dependency. It is required for older versions of Next.js (specifically < 9.0.6) when using the Pages Router, as 'next-server' was not merged into 'next' at that time. ```shell npm install --save-dev next-server ``` -------------------------------- ### Manual Package Release using npm Scripts Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/MAINTAINING.md A step-by-step guide to manually releasing a package by executing a sequence of npm scripts. This includes cleaning, formatting, linting, building, documenting, testing, and finally releasing the package. Each step should be verified before proceeding. ```bash # 2. OPTIONAL: Reset the working tree to a clean state. The build command # usually does this for you, making this step unnecessary. #npm run clean -- --force # 3. Format this package's files. npm run format # 4. Lint every file in the package and any files imported by those files. npm run lint:package # 5. Build this package's distributables. npm run build # 6. Build this package's documentation (AFTER format for correct line numbers). npm run build:docs # 7. Run all of this package's tests and the tests of any package imported by # source files in this package, then generate coverage data. npm run test:package:all # 8. Trigger xrelease locally to publish a new release of this package. This # requires having valid tokens for NPM, GitHub, and Codecov each with the # appropriate permissions. # # Do a dry run first: npm run release -- --skip-tasks manual --dry-run # Then review CHANGELOG.md and, after making sure the next release includes the # commits you're expecting, do the actual release: npm run release -- --skip-tasks manual ``` -------------------------------- ### App Router Handler Testing Example Source: https://context7.com/xunnamius/next-test-api-route-handler/llms.txt Demonstrates how to test Next.js App Router route handlers using `testApiHandler`. It shows how to define GET and POST handlers and then test them with various request scenarios. ```APIDOC ## App Router Handler Testing ### Description Test Next.js App Router route handlers by passing an `appHandler` object containing HTTP method functions (GET, POST, PUT, DELETE, etc.). The handlers receive actual `NextRequest` objects and can use Next.js helper functions. ### Method `testApiHandler({ appHandler, test })` ### Endpoint (Emulated within the testing environment) ### Parameters #### Request Body - **appHandler** (object) - Required - An object where keys are HTTP methods (e.g., `GET`, `POST`) and values are async functions that accept a `NextRequest` object and return a `Response` object. - **GET(request: NextRequest): Promise** - **POST(request: NextRequest): Promise** - ... other HTTP methods ... - **test** (function) - Required - An async function that receives a `fetch` utility to send requests to the `appHandler` and perform assertions. ### Request Example ```typescript import { testApiHandler } from 'next-test-api-route-handler'; import { cookies, headers } from 'next/headers'; await testApiHandler({ appHandler: { async GET(request) { const authHeader = request.headers.get('authorization'); const cookieStore = await cookies(); const sessionId = cookieStore.get('session_id')?.value; if (!authHeader || !sessionId) { return Response.json({ error: 'Unauthorized' }, { status: 401 }); } return Response.json({ user: 'authenticated', sessionId }, { status: 200 }); }, async POST(request) { const body = await request.json(); return Response.json({ received: body }, { status: 201 }); } }, test: async ({ fetch }) => { // Test unauthorized request const unauthorized = await fetch(); expect(unauthorized.status).toBe(401); // Test authorized request const authorized = await fetch({ method: 'GET', headers: { authorization: 'Bearer token123', cookie: 'session_id=abc123' } }); expect(authorized.status).toBe(200); await expect(authorized.json()).resolves.toEqual({ user: 'authenticated', sessionId: 'abc123' }); // Test POST with body const postResponse = await fetch({ method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ name: 'Test User' }) }); expect(postResponse.status).toBe(201); await expect(postResponse.json()).resolves.toEqual({ received: { name: 'Test User' } }); } }); ``` ### Response #### Success Response (200, 201, etc.) - **Response** (object) - The `Response` object returned by the handler. #### Response Example (Depends on the handler's implementation, e.g., `Response.json({ user: 'authenticated', sessionId: 'abc123' }, { status: 200 })`) ``` -------------------------------- ### Install Next Test API Route Handler Source: https://context7.com/xunnamius/next-test-api-route-handler/llms.txt Installs the next-test-api-route-handler package as a development dependency using npm. ```bash npm install --save-dev next-test-api-route-handler ``` -------------------------------- ### Test Edge Runtime API Handler Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Demonstrates testing an API route handler configured for the Edge Runtime. This example shows how to use TypeScript generics for response typing and how to modify the request body using requestPatcher. ```typescript /* File: test/unit.test.ts */ import { testApiHandler } from 'next-test-api-route-handler'; // ◄ Must be first import // Import the handler under test from the app directory import * as edgeHandler from '../app/your-edge-endpoint/route'; it('does what I want', async function () { // NTARH supports optionally typed response data via TypeScript generics: await testApiHandler<{ success: boolean }>({ // Only appHandler supports edge functions. The pagesHandler prop does not! appHandler: edgeHandler, // requestPatcher is optional requestPatcher(request) { return new Request(request, { body: dummyReadableStream, duplex: 'half' }); }, async test({ fetch }) { // The next line would cause TypeScript to complain: // const { luck: success } = await (await fetch()).json(); await expect((await fetch()).json()).resolves.toStrictEqual({ success: true // ◄ Passes! }); } }); }); ``` -------------------------------- ### Test Pages Router API Handler Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Shows how to test an API route handler in the Pages Router. This example includes using TypeScript generics for response typing and providing a custom requestPatcher to modify request headers. ```typescript /* File: test/unit.test.ts */ import { testApiHandler } from 'next-test-api-route-handler'; // ◄ Must be first import // Import the handler under test and its config from the pages/api directory import * as pagesHandler from '../pages/api/your-endpoint'; it('does what I want', async () => { // NTARH supports optionally typed response data via TypeScript generics: await testApiHandler<{ hello: string }>({ pagesHandler, requestPatcher: (req) => { req.headers = { key: process.env.SPECIAL_TOKEN }; }, test: async ({ fetch }) => { const res = await fetch({ method: 'POST', body: 'data' }); // The next line would cause TypeScript to complain: // const { goodbye: hello } = await res.json(); const { hello } = await res.json(); expect(hello).toBe('world'); // ◄ Passes! } }); }); ``` -------------------------------- ### Set Request URL Shorthand for App Handler Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md When only the request URL needs to be set for the app handler, the 'url' shorthand can be used instead of requestPatcher. This simplifies the test setup for URL-based requests. ```typescript await testApiHandler({ // requestPatcher: (request) => new Request('ntarh:///my-url?some=query', request), url: '/my-url?some=query' }); ``` -------------------------------- ### Download and Run Jest Integration Tests Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Creates a test directory, downloads an integration test file from a GitHub repository, and executes the tests using Jest to ensure proper integration with Apollo. ```bash mkdir test curl -o test/integration.test.js https://raw.githubusercontent.com/Xunnamius/next-test-api-route-handler/main/apollo_test_raw npx jest ``` -------------------------------- ### Importing testApiHandler Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Demonstrates the correct way to import testApiHandler, emphasizing that it must be the first import to avoid conflicts with Next.js internals. ```APIDOC ## Importing testApiHandler ### Description `testApiHandler` must always be the first import in your test file due to the way Next.js is written and distributed. This section provides examples for both ESM and CJS module systems, as well as a workaround for import sorting tools. ### Method Import statement ### Endpoint N/A ### Parameters N/A ### Request Example ```typescript // ESM import { testApiHandler } from 'next-test-api-route-handler'; // ◀️ Must be first // ... all other imports ... ``` ```javascript // CJS const { testApiHandler } = require('next-test-api-route-handler'); // ◀️ Must be first // ... all other imports ... ``` For import sorting tools: ```javascript import 'next-test-api-route-handler'; // ... all other imports ordered before NTARH ... import { testApiHandler } from 'next-test-api-route-handler'; // ... all other imports ordered after NTARH ... ``` ### Response N/A ### Response Example N/A ``` -------------------------------- ### App Router Handler Testing with NextRequest and Helpers Source: https://context7.com/xunnamius/next-test-api-route-handler/llms.txt Illustrates testing an App Router API route handler using testApiHandler. The handler receives actual NextRequest objects and can utilize Next.js helper functions like cookies() and headers(). Includes tests for unauthorized GET requests, authorized GET requests with cookies and headers, and a POST request with a JSON body. ```typescript import { testApiHandler } from 'next-test-api-route-handler'; import { cookies, headers } from 'next/headers'; await testApiHandler({ appHandler: { async GET(request) { const authHeader = request.headers.get('authorization'); const cookieStore = await cookies(); const sessionId = cookieStore.get('session_id')?.value; if (!authHeader || !sessionId) { return Response.json({ error: 'Unauthorized' }, { status: 401 }); } return Response.json({ user: 'authenticated', sessionId }, { status: 200 }); }, async POST(request) { const body = await request.json(); return Response.json({ received: body }, { status: 201 }); } }, test: async ({ fetch }) => { // Test unauthorized request const unauthorized = await fetch(); expect(unauthorized.status).toBe(401); // Test authorized request const authorized = await fetch({ method: 'GET', headers: { authorization: 'Bearer token123', cookie: 'session_id=abc123' } }); expect(authorized.status).toBe(200); await expect(authorized.json()).resolves.toEqual({ user: 'authenticated', sessionId: 'abc123' }); // Test POST with body const postResponse = await fetch({ method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ name: 'Test User' }) }); expect(postResponse.status).toBe(201); await expect(postResponse.json()).resolves.toEqual({ received: { name: 'Test User' } }); } }); ``` -------------------------------- ### Parameter Shorthand vs. paramsPatcher Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Illustrates the use of the `params` shorthand as a simpler alternative to `paramsPatcher` for setting dynamic route parameters. `paramsPatcher` is executed after `params` if both are provided. ```APIDOC ## GET /api/categories/{categoryName}/products ### Description Retrieves products belonging to a specific category. ### Method GET ### Endpoint /api/categories/{categoryName}/products ### Parameters #### Path Parameters - **categoryName** (string) - Required - The name of the category. #### Query Parameters - **sortOrder** (string) - Optional - The order to sort products ('asc' or 'desc'). ### Request Example (No request body for GET requests) ### Response #### Success Response (200) - **products** (array) - An array of product objects within the specified category. - **productId** (string) - The ID of the product. - **name** (string) - The name of the product. - **price** (number) - The price of the product. #### Response Example ```json { "products": [ { "productId": "prod-a", "name": "Gadget", "price": 25.50 }, { "productId": "prod-b", "name": "Widget", "price": 15.00 } ] } ``` ``` -------------------------------- ### test Function and Fetch Wrapper Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md The `test` function is where assertions are made. It provides a `fetch` wrapper for making HTTP requests to the handler under test. Redirection and MSW compatibility are also detailed. ```APIDOC ## Testing API Routes with `test` and `fetch` ### Description This section details how to use the `test` function and its provided `fetch` wrapper to test API routes. It covers handling redirections and integrating with Mock Service Worker (MSW). ### Method N/A (This is a testing utility) ### Endpoint N/A ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```typescript import { testApiHandler } from 'next-test-api-route-handler'; import { http, passthrough, HttpResponse } from 'msw'; import { setupServer } from 'msw/node'; const server = setupServer(/* ... */); beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); afterEach(() => { server.resetHandlers(); }); afterAll(() => server.close()); it('redirects a shortened URL to the real URL', async () => { expect.hasAssertions(); const { shortId, realLink } = getUriEntry(); const realUrl = new URL(realLink); await testApiHandler({ appHandler, // or pagesHandler params: { shortId }, test: async ({ fetch }) => { server.use( http.get('*', async ({ request }) => { return request.url === realUrl.href ? HttpResponse.json({ it: 'worked' }, { status: 200 }) : passthrough(); }) ); const res = await fetch({ headers: { 'x-msw-intention': 'none' } // Override MSW headers }); await expect(res.json()).resolves.toMatchObject({ it: 'worked' }); expect(res.status).toBe(200); } }); }); ``` ### Response #### Success Response (200) N/A (This is a testing utility, responses are asserted within the `test` function) #### Response Example ```json { "example": "response body" } ``` ``` -------------------------------- ### Constructing Error Specifications with ExpectExceptionsWithMatchingErrorsSpec (TypeScript) Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/docs/test/util/type-aliases/ExpectExceptionsWithMatchingErrorsSpec.md This example demonstrates how to use the ExpectExceptionsWithMatchingErrorsSpec type to define an array of error specifications. Each specification includes a parameter object and an expected error message. This is then used with the expectExceptionsWithMatchingErrors function for testing. ```typescript const errors = [ [{ something: 1 }, 'expected error #1'], [{ something: 2 }, 'expected error #2'], [{ something: 3 }, 'expected error #3'], ] as Spec<[{ something: number }], 'single-parameter'>; await expectExceptionsWithMatchingErrors( errors, (params) => fn(...params), { singleParameter: true } ); ``` -------------------------------- ### withMockedFixtures Function Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/docs/test/util/functions/withMockedFixtures.md The withMockedFixtures function creates a mock or 'dummy' filesystem structure used to simulate one or more runtime environments for the package under test. It accepts a test function, fixtures, and optional options. ```APIDOC ## Function: withMockedFixtures() ### Description Creates a mock or "dummy" filesystem structure used to simulate one or more runtime environments for the package under test. When passing one-off custom fixture functions via the `fixtures` parameter, use the `AdditionalOptions` and `AdditionalContext` type parameters to supply any additional options and context where necessary. ### Method N/A (This is a function, not an HTTP endpoint) ### Endpoint N/A ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```typescript // Example usage within a test file: import { withMockedFixtures } from '@-xun/test-mock-fixture'; import { test } from '@playwright/test'; // Define your custom fixtures const myFixtures = { // ... your fixture definitions }; test('should run with mocked fixtures', withMockedFixtures(test, myFixtures, { // ... additional options if needed })); ``` ### Response #### Success Response N/A (This function returns a Promise and does not return a value directly upon success, but rather executes the provided test function with the mocked environment.) #### Response Example N/A ``` -------------------------------- ### RunTestFixture Function Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/docs/test/util/functions/runTestFixture.md The runTestFixture function executes a binary with the specified arguments and returns a RunTestFixture type. ```APIDOC ## Function: runTestFixture() ### Description This fixture executes a binary with the specified arguments. ### Method N/A (This is a function, not an HTTP endpoint) ### Endpoint N/A ### Parameters This function does not appear to take any parameters directly in its signature. ### Request Example N/A ### Response #### Returns - **RunTestFixture** (object) - An object representing the result of running the test fixture. #### Response Example ```json { "example": "RunTestFixture object structure" } ``` ``` -------------------------------- ### Manual Package Release with Symbiote Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/MAINTAINING.md Execute a manual release of a package using Symbiote. It's recommended to perform a dry run first to preview changes before the actual release. This process leverages npm scripts. ```bash # Do a symbiote dry run first: npm run release -- --dry-run # Then do the actual symbiote-based release: npm run release ``` -------------------------------- ### RootFixture Type Alias Definition Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/docs/test/util/type-aliases/RootFixture.md Defines the RootFixture type alias as a generic MockFixture. It specifies the type for the fixture name and the generic fixture context. This alias is used to create mock fixtures for the root level of the application or testing setup. ```typescript type RootFixture = MockFixture ``` -------------------------------- ### Test Unreliable API Handler on Edge Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Tests an unreliable API handler designed to simulate errors. It configures the handler to return a specific status code (e.g., 555) once every N requests and verifies the error rate. This example uses `next-test-api-route-handler` and TypeScript. ```typescript /* File: test/unit.test.ts */ import { testApiHandler } from 'next-test-api-route-handler'; // Import the handler under test from the app/api directory import * as edgeHandler from '../app/api/unreliable'; const expectedReqPerError = 10; it('injects contrived errors at the required rate', async () => { expect.hasAssertions(); // Signal to the edge endpoint (which is configurable) that there should be 1 // error among every 10 requests process.env.REQUESTS_PER_CONTRIVED_ERROR = expectedReqPerError.toString(); await testApiHandler({ appHandler: edgeHandler, requestPatcher(request) { // Our edge handler expects Next.js to provide geo and ip data with the // request, so let's add some. This is also where you'd mock/emulate the // effects of any Next.js middleware // // IMPORTANT: starting with next@15, geo/ip have been removed from // NextRequest and were functionally replaced by @vercel/function // https://github.com/vercel/next.js/pull/68379 // https://nextjs.org/docs/app/building-your-application/upgrading/version-15#nextrequest-geolocation return new NextRequest(request, { geo: { city: 'Chicago', country: 'United States' }, ip: '110.10.77.7' }); }, test: async ({ fetch }) => { // Run 20 requests with REQUESTS_PER_CONTRIVED_ERROR = '10' and // record the results const results1 = await Promise.all( [ ...Array.from({ length: expectedReqPerError - 1 }).map(() => fetch({ method: 'GET' }) ), fetch({ method: 'POST' }), ...Array.from({ length: expectedReqPerError - 1 }).map(() => fetch({ method: 'PUT' }) ), fetch({ method: 'DELETE' }) ].map((p) => p.then((r) => r.status)) ); process.env.REQUESTS_PER_CONTRIVED_ERROR = '0'; // Run 10 requests with REQUESTS_PER_CONTRIVED_ERROR = '0' and record the // results const results2 = await Promise.all( Array.from({ length: expectedReqPerError }).map(() => fetch().then((r) => r.status) ) ); // We expect results1 to be an array with eighteen `200`s and two // `555`s in any order // // https://github.com/jest-community/jest-extended#toincludesamemembersmembers // because responses could be received out of order expect(results1).toIncludeSameMembers( [ ...Array.from({ length: expectedReqPerError - 1 }).map(() => 200), 555, ...Array.from({ length: expectedReqPerError - 1 }).map(() => 200), 555 ] ); // We expect results2 to be an array with ten `200`s expect(results2).toStrictEqual( [ ...Array.from({ length: expectedReqPerError }).map(() => 200) ] ); } }); }); ``` -------------------------------- ### Perform Semi-Automated Release of All Packages Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/MAINTAINING.md Executes a semi-automated release for all packages in the repository using npm run release:topological. It is recommended to perform a dry run first to verify the release process before executing the actual release. ```bash # Do a dry run first if you're not absolutely sure all is as it should be: npm run release:topological -- --options --dry-run # Then do the actual topological release: npm run release:topological ``` -------------------------------- ### Test GraphQL Endpoints with Apollo Server Source: https://context7.com/xunnamius/next-test-api-route-handler/llms.txt Provides an example of how to test GraphQL API endpoints using `testApiHandler`. It demonstrates sending POST requests with JSON bodies containing GraphQL queries and mutations, along with variables and headers. ```typescript import { testApiHandler } from 'next-test-api-route-handler'; import * as appHandler from '../app/api/graphql/route'; await testApiHandler({ appHandler, test: async ({ fetch }) => { // GraphQL query const queryResponse = await fetch({ method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ query: ` query GetUser($id: ID!) { user(id: $id) { id name email } } `, variables: { id: 'user-123' } }) }); await expect(queryResponse.json()).resolves.toEqual({ data: { user: { id: 'user-123', name: 'John Doe', email: 'john@example.com' } } }); // GraphQL mutation const mutationResponse = await fetch({ method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ query: ` mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name } } `, variables: { input: { name: 'Jane', email: 'jane@example.com' } } }) }); const { data } = await mutationResponse.json(); expect(data.createUser.name).toBe('Jane'); } }); ``` -------------------------------- ### Test API Handler with App Router Source: https://context7.com/xunnamius/next-test-api-route-handler/llms.txt Demonstrates how to use the testApiHandler function to test an App Router API route. It sets up a test environment and sends a GET request to fetch user data, asserting the response status and JSON content. ```typescript import { testApiHandler } from 'next-test-api-route-handler'; // Must be first import import * as appHandler from '../app/api/user/route'; describe('User API', () => { it('returns user data', async () => { await testApiHandler({ appHandler, test: async ({ fetch }) => { const response = await fetch({ method: 'GET' }); expect(response.status).toBe(200); const data = await response.json(); expect(data).toEqual({ id: 1, name: 'John' }); } }); }); }); ``` -------------------------------- ### API: testApiHandler Options Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Details the core `testApiHandler` function and its required and optional configuration options for testing Next.js API routes. ```APIDOC ## API: testApiHandler Options ### Description NTARH exports a single function, `testApiHandler(options)`, that accepts an `options` object as its only parameter. This section details the required and optional properties of this `options` object. ### Method `testApiHandler(options)` ### Endpoint N/A ### Parameters `options` object: At minimum, `options` must contain: - At least one of the `appHandler` or `pagesHandler` options, but not both. - The `test` option. **Required Properties:** - **`appHandler`** (object | undefined): The API route handler exported from the app directory. Use this for App Router or Edge Runtime handlers. Cannot be used with `pagesHandler`. - **`pagesHandler`** (object | undefined): The API route handler exported from the pages/api directory. Use this for Pages Router handlers. Cannot be used with `appHandler`. - **`test`** (function): An async function containing the test logic. It receives an object with a `fetch` function to make requests to the API route. **Optional Properties:** - **`requestPatcher`** (function): A function that receives the request object and can modify it before it's sent to the handler. - **`responsePatcher`** (function): An async function that receives the response object and can modify or replace it. ### Request Example ```typescript import { testApiHandler } from 'next-test-api-route-handler'; import { headers } from 'next/headers'; await testApiHandler({ appHandler: { dynamic: 'force-dynamic', async GET(_request) { return Response.json( { // Yep, those fancy helper functions work too! hello: (await headers()).get('x-hello') }, { status: 200 } ); } }, async test({ fetch }) { await expect( (await fetch({ headers: { 'x-hello': 'world' } })).json() ).resolves.toStrictEqual({ hello: 'world' }); } }); ``` ### Response N/A (The `test` function performs assertions) ### Response Example N/A ``` -------------------------------- ### Modify Request Object Before Handler Execution Source: https://context7.com/xunnamius/next-test-api-route-handler/llms.txt Utilizes the `requestPatcher` option to modify the request object before it's processed by the API handler. This is applicable to both App Router (modifying `NextRequest`) and Pages Router (mutating `IncomingMessage`). Examples include setting custom headers and modifying the request URL. ```typescript import { testApiHandler } from 'next-test-api-route-handler'; // App Router: Modify NextRequest await testApiHandler({ requestPatcher(request) { request.headers.set('x-api-key', 'secret-key-123'); request.headers.set('x-user-id', 'user-456'); }, appHandler: { async GET(request) { const apiKey = request.headers.get('x-api-key'); const userId = request.headers.get('x-user-id'); return Response.json({ apiKey, userId }); } }, test: async ({ fetch }) => { const response = await fetch(); await expect(response.json()).resolves.toEqual({ apiKey: 'secret-key-123', userId: 'user-456' }); } }); // App Router: Return new Request with modified URL await testApiHandler({ requestPatcher(request) { return new Request('ntarh://custom-url/api/data?filter=active', request); }, appHandler: { async GET(request) { const url = new URL(request.url); return Response.json({ pathname: url.pathname, filter: url.searchParams.get('filter') }); } }, test: async ({ fetch }) => { const response = await fetch(); await expect(response.json()).resolves.toEqual({ pathname: '/api/data', filter: 'active' }); } }); // Pages Router: Modify IncomingMessage await testApiHandler({ requestPatcher(req) { req.headers['x-custom-header'] = 'custom-value'; req.headers.authorization = 'Bearer token'; }, pagesHandler: async (req, res) => { res.json({ customHeader: req.headers['x-custom-header'], auth: req.headers.authorization }); }, test: async ({ fetch }) => { const response = await fetch(); await expect(response.json()).resolves.toEqual({ customHeader: 'custom-value', auth: 'Bearer token' }); } }); ``` -------------------------------- ### App Router Handler Test with testApiHandler Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Demonstrates how to test an App Router API route handler using `testApiHandler`. It configures the `appHandler` with an async POST method and uses the `test` function to make a POST request and assert the response. ```typescript await testApiHandler({ params: { id: 5 }, appHandler: { async POST(request, { params: { id } }) { return Response.json( { special: request.headers.get('x-special-header'), id }, { status: 200 } ); } }, async test({ fetch }) { expect((await fetch({ method: 'POST' })).status).toBe(200); const result2 = await fetch({ method: 'POST', headers: { 'x-special-header': 'x' } }); expect(result2.json()).toStrictEqual({ special: 'x', id: 5 }); } }); ``` -------------------------------- ### Test Next.js Pages Router API Handlers Source: https://context7.com/xunnamius/next-test-api-route-handler/llms.txt Tests a Next.js Pages Router API handler by providing a `pagesHandler` function. This allows for the testing of legacy API routes by simulating `NextApiRequest` and `NextApiResponse` objects. It supports testing different HTTP methods like GET and POST. ```typescript import { testApiHandler } from 'next-test-api-route-handler'; import type { NextApiRequest, NextApiResponse } from 'next'; const handler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method === 'GET') { const { id } = req.query; return res.status(200).json({ userId: id, data: 'user data' }); } if (req.method === 'POST') { const { name, email } = req.body; return res.status(201).json({ created: { name, email } }); } return res.status(405).json({ error: 'Method not allowed' }); }; await testApiHandler({ pagesHandler: handler, test: async ({ fetch }) => { // Test GET request const getResponse = await fetch({ method: 'GET' }); expect(getResponse.status).toBe(200); // Test POST request const postResponse = await fetch({ method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ name: 'Jane', email: 'jane@example.com' }) }); expect(postResponse.status).toBe(201); await expect(postResponse.json()).resolves.toEqual({ created: { name: 'Jane', email: 'jane@example.com' } }); } }); ``` -------------------------------- ### Run Test Assertions with Fetch (TypeScript) Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/docs/src/interfaces/NtarhInitPagesRouter.md The test() function executes your test assertions. It accepts a destructured parameter 'fetch', which is a modified version of globalThis.fetch. This function returns a Promisable. ```typescript /** * Runs your test assertions. * @param {object} parameters - The parameters for the test function. * @param {function} parameters.fetch - A modified fetch function. * @returns {Promise} A promise that resolves when the tests are complete. */ const test = async ({ fetch }: { fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise }): Promise => { // Your test assertions here console.log('Running tests...'); const response = await fetch('/api/data'); const data = await response.json(); console.log('Received data:', data); }; ``` -------------------------------- ### App Handler Configuration for Next.js App Router Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/docs/src/interfaces/NtarhInitAppRouter.md The appHandler property accepts a partial AppRouteUserlandModule, allowing you to define HTTP method handlers (e.g., GET, POST) or configuration options for your App Router route handler. This enables testing of individual route handler functions within the Next.js App Router framework. ```typescript appHandler: Partial & { [key in keyof AppRouteHandlers]?: (req: NextRequest, segmentData?: any) => any }> ``` -------------------------------- ### Distinction Between Route and Query Parameters Source: https://github.com/xunnamius/next-test-api-route-handler/blob/main/README.md Clarifies that route parameters (dynamic segments) are handled via `paramsPatcher` or `params`, while query string parameters are automatically parsed from the URL and available in the `NextRequest` object. ```APIDOC ## GET /api/search ### Description Performs a search based on query parameters. ### Method GET ### Endpoint /api/search ### Parameters #### Query Parameters - **q** (string) - Required - The search query string. - **page** (number) - Optional - The page number for search results. - **resultsPerPage** (number) - Optional - The number of results to display per page. ### Request Example (No request body for GET requests) ### Response #### Success Response (200) - **query** (string) - The original search query. - **results** (array) - An array of search result objects. - **id** (string) - The ID of the result item. - **title** (string) - The title of the result item. - **url** (string) - The URL of the result item. - **pagination** (object) - Information about the pagination. - **currentPage** (number) - The current page number. - **totalPages** (number) - The total number of pages available. #### Response Example ```json { "query": "next.js api testing", "results": [ { "id": "doc-1", "title": "Testing API Routes in Next.js", "url": "/docs/testing-api" }, { "id": "blog-1", "title": "Advanced API Route Techniques", "url": "/blog/advanced-api" } ], "pagination": { "currentPage": 1, "totalPages": 5 } } ``` ```