{"id":13623756,"url":"https://github.com/scottrippey/next-router-mock","last_synced_at":"2025-05-14T09:08:51.398Z","repository":{"id":37984772,"uuid":"328237625","full_name":"scottrippey/next-router-mock","owner":"scottrippey","description":"Mock implementation of the Next.js Router","archived":false,"fork":false,"pushed_at":"2025-02-10T21:43:41.000Z","size":1536,"stargazers_count":408,"open_issues_count":18,"forks_count":41,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-03T19:47:55.064Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scottrippey.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-09T20:10:58.000Z","updated_at":"2025-03-28T13:28:22.000Z","dependencies_parsed_at":"2024-01-14T08:14:26.833Z","dependency_job_id":"a0617e53-380a-4ce1-af7b-efc5ce9f12ab","html_url":"https://github.com/scottrippey/next-router-mock","commit_stats":{"total_commits":214,"total_committers":8,"mean_commits":26.75,"dds":"0.19626168224299068","last_synced_commit":"ff5baf4330c8d76ec14ae5069016795f411d0fd5"},"previous_names":[],"tags_count":53,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottrippey%2Fnext-router-mock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottrippey%2Fnext-router-mock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottrippey%2Fnext-router-mock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottrippey%2Fnext-router-mock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scottrippey","download_url":"https://codeload.github.com/scottrippey/next-router-mock/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248055275,"owners_count":21040151,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T21:01:35.279Z","updated_at":"2025-04-11T03:27:29.162Z","avatar_url":"https://github.com/scottrippey.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\n# `next-router-mock`\n\nAn implementation of the Next.js Router that keeps the state of the \"URL\" in memory (does not read or write to the\naddress bar).  Useful in **tests** and **Storybook**. \nInspired by [`react-router \u003e MemoryRouter`](https://github.com/remix-run/react-router/blob/main/docs/router-components/memory-router.md).\n\nTested with NextJS v13, v12, v11, and v10.\n\nInstall via NPM: `npm install --save-dev next-router-mock`\n\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*\n\n- [Usage with Jest](#usage-with-jest)\n    - [Jest Configuration](#jest-configuration)\n    - [Jest Example](#jest-example)\n- [Usage with Storybook](#usage-with-storybook)\n    - [Storybook Configuration](#storybook-configuration)\n    - [Storybook Example](#storybook-example)\n- [Compatibility with `next/link`](#compatibility-with-nextlink)\n    - [Example: `next/link` with React Testing Library](#example-nextlink-with-react-testing-library)\n    - [Example: `next/link` with Enzyme](#example-nextlink-with-enzyme)\n    - [Example: `next/link` with Storybook](#example-nextlink-with-storybook)\n- [Dynamic Routes](#dynamic-routes)\n- [Sync vs Async](#sync-vs-async)\n- [Supported Features](#supported-features)\n  - [Not yet supported](#not-yet-supported)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n# Usage with Jest\n\n### Jest Configuration\nFor unit tests, the `next-router-mock` module can be used as a drop-in replacement for `next/router`:\n\n```js\njest.mock('next/router', () =\u003e require('next-router-mock'));\n```\n\nYou can do this once per spec file, or you can [do this globally using `setupFilesAfterEnv`](https://jestjs.io/docs/configuration/#setupfilesafterenv-array).\n\n### Jest Example\n\nIn your tests, use the router from `next-router-mock` to set the current URL and to make assertions.\n\n```jsx\nimport { useRouter } from 'next/router';\nimport { render, screen, fireEvent } from '@testing-library/react';\nimport mockRouter from 'next-router-mock';\n\njest.mock('next/router', () =\u003e jest.requireActual('next-router-mock'))\n\nconst ExampleComponent = ({ href = '' }) =\u003e {\n  const router = useRouter();\n  return (\n    \u003cbutton onClick={() =\u003e router.push(href)}\u003e\n      The current route is: \"{router.asPath}\"\n    \u003c/button\u003e\n  );\n}\n\ndescribe('next-router-mock', () =\u003e {\n  it('mocks the useRouter hook', () =\u003e {\n    // Set the initial url:\n    mockRouter.push(\"/initial-path\");\n    \n    // Render the component:\n    render(\u003cExampleComponent href=\"/foo?bar=baz\" /\u003e);\n    expect(screen.getByRole('button')).toHaveTextContent(\n      'The current route is: \"/initial-path\"'\n    );\n\n    // Click the button:\n    fireEvent.click(screen.getByRole('button'));\n    \n    // Ensure the router was updated:\n    expect(mockRouter).toMatchObject({ \n      asPath: \"/foo?bar=baz\",\n      pathname: \"/foo\",\n      query: { bar: \"baz\" },\n    });\n  });\n});\n```\n\n\n# Usage with Storybook\n\n### Storybook Configuration\nGlobally enable `next-router-mock` by adding the following webpack alias to your Storybook configuration.\n\nIn `.storybook/main.js` add:\n```js\nmodule.exports = {\n  webpackFinal: async (config, { configType }) =\u003e {\n    config.resolve.alias = {\n      ...config.resolve.alias,\n      \"next/router\": \"next-router-mock\",\n    };\n    return config;\n  },\n};\n```\n\nThis ensures that all your components that use `useRouter` will work in Storybook.  If you also need to test `next/link`, please see the section [Example: **`next/link` with Storybook**](#example-nextlink-with-storybook).\n\n### Storybook Example\n\nIn your individual stories, you might want to mock the current URL (eg. for testing an \"ActiveLink\" component), or you might want to log `push/replace` actions.  You can do this by wrapping your stories with the `\u003cMemoryRouterProvider\u003e` component.  \n\n```jsx\n// ActiveLink.story.jsx\nimport { action } from '@storybook/addon-actions';\nimport { MemoryRouterProvider } \n  from 'next-router-mock/MemoryRouterProvider/next-13';\nimport { ActiveLink } from './active-link';\n\nexport const ExampleStory = () =\u003e (\n  \u003cMemoryRouterProvider url=\"/active\" onPush={action(\"router.push\")}\u003e\n    \u003cActiveLink href=\"/example\"\u003eNot Active\u003c/ActiveLink\u003e\n    \u003cActiveLink href=\"/active\"\u003eActive\u003c/ActiveLink\u003e\n  \u003c/MemoryRouterProvider\u003e\n);\n```\n\n\u003e Be sure to import from **a matching Next.js version**: \n\u003e ```\n\u003e import { MemoryRouterProvider } \n\u003e   from 'next-router-mock/MemoryRouterProvider/next-13.5';\n\u003e ```\n\u003e Choose from `next-13.5`, `next-13`, `next-12`, or `next-11`.\n\nThe `MemoryRouterProvider` has the following optional properties:\n\n- `url` (`string` or `object`) sets the current route's URL\n- `async` enables async mode, if necessary (see \"Sync vs Async\" for details)\n- Events:\n  - `onPush(url, { shallow })`\n  - `onReplace(url, { shallow })`\n  - `onRouteChangeStart(url, { shallow })`\n  - `onRouteChangeComplete(url, { shallow })`\n\n\n# Compatibility with `next/link`\n\nTo use `next-router-mock` with `next/link`, you must use a `\u003cMemoryRouterProvider\u003e` to wrap the test component.\n\n### Example: `next/link` with React Testing Library\n\nWhen rendering, simply supply the option `{ wrapper: MemoryRouterProvider }`\n\n```jsx\nimport { render } from '@testing-library/react';\nimport NextLink from 'next/link';\n\nimport mockRouter from 'next-router-mock';\nimport { MemoryRouterProvider } from 'next-router-mock/MemoryRouterProvider';\n\nit('NextLink can be rendered', () =\u003e {\n  render(\n    \u003cNextLink href=\"/example\"\u003eExample Link\u003c/NextLink\u003e, \n    { wrapper: MemoryRouterProvider }\n  );\n  fireEvent.click(screen.getByText('Example Link'));\n  expect(mockRouter.asPath).toEqual('/example')\n});\n```\n\n### Example: `next/link` with Enzyme\n\nWhen rendering, simply supply the option `{ wrapperComponent: MemoryRouterProvider }`\n\n```jsx\nimport { shallow } from 'enzyme';\nimport NextLink from 'next/link';\n\nimport mockRouter from 'next-router-mock';\nimport { MemoryRouterProvider } from 'next-router-mock/MemoryRouterProvider';\n\nit('NextLink can be rendered', () =\u003e {\n  const wrapper = shallow(\n    \u003cNextLink href=\"/example\"\u003eExample Link\u003c/NextLink\u003e, \n    { wrapperComponent: MemoryRouterProvider }\n  );\n  \n  wrapper.find('a').simulate('click');\n  \n  expect(mockRouter.asPath).to.equal('/example')\n});\n```\n\n### Example: `next/link` with Storybook\n\nIn Storybook, you must wrap your component with the `\u003cMemoryRouterProvider\u003e` component (with optional `url` set).\n\n```jsx\n// example.story.jsx\nimport NextLink from 'next/link';\nimport { action } from '@storybook/addon-actions';\n\nimport { MemoryRouterProvider } from 'next-router-mock/MemoryRouterProvider/next-13.5';\n\nexport const ExampleStory = () =\u003e (\n  \u003cMemoryRouterProvider url=\"/initial\"\u003e\n    \u003cNextLink href=\"/example\"\u003eExample Link\u003c/NextLink\u003e\n  \u003c/MemoryRouterProvider\u003e\n);\n```\n\nThis can be done inline (as above).  \nIt can also be implemented as a `decorator`, which can be per-Story, per-Component, or Global (see [Storybook Decorators Documentation](https://storybook.js.org/docs/react/writing-stories/decorators) for details).  \nGlobal example:\n\n```\n// .storybook/preview.js\nimport { MemoryRouterProvider } from 'next-router-mock/MemoryRouterProvider';\n\nexport const decorators = [\n  (Story) =\u003e \u003cMemoryRouterProvider\u003e\u003cStory /\u003e\u003c/MemoryRouterProvider\u003e\n]; \n```\n\n\n# Dynamic Routes\n\nBy default, `next-router-mock` does not know about your dynamic routes (eg. files like `/pages/[id].js`).\nTo test code that uses dynamic routes, you must add the routes manually, like so:\n\n```typescript\nimport mockRouter from \"next-router-mock\";\nimport { createDynamicRouteParser } from \"next-router-mock/dynamic-routes\";\n\nmockRouter.useParser(createDynamicRouteParser([\n  // These paths should match those found in the `/pages` folder:\n  \"/[id]\",\n  \"/static/path\",\n  \"/[dynamic]/path\",\n  \"/[...catchAll]/path\"\n]));\n\n// Example test:\nit('should parse dynamic routes', () =\u003e {\n  mockRouter.push('/FOO');\n  expect(mockRouter).toMatchObject({\n    pathname: '/[id]',\n    query: { id: 'FOO' }\n  });\n})\n```\n\n# Sync vs Async\n\nBy default, `next-router-mock` handles route changes synchronously. This is convenient for testing, and works for most\nuse-cases.  \nHowever, Next normally handles route changes asynchronously, and in certain cases you might actually rely on that\nbehavior. If that's the case, you can use `next-router-mock/async`. Tests will need to account for the async behavior\ntoo; for example:\n\n```jsx\nit('next/link can be tested too', async () =\u003e {\n  render(\u003cNextLink href=\"/example?foo=bar\"\u003e\u003ca\u003eExample Link\u003c/a\u003e\u003c/NextLink\u003e);\n  fireEvent.click(screen.getByText('Example Link'));\n  await waitFor(() =\u003e {\n    expect(singletonRouter).toMatchObject({\n      asPath: '/example?foo=bar',\n      pathname: '/example',\n      query: { foo: 'bar' },\n    });\n  });\n});\n```\n\n# Supported Features\n\n- `useRouter()`\n- `withRouter(Component)`\n- `router.push(url, as?, options?)`\n- `router.replace(url, as?, options?)`\n- `router.route`\n- `router.pathname`\n- `router.asPath`\n- `router.query`\n- Works with `next/link` (see Jest notes)\n- `router.events` supports:\n  - `routeChangeStart(url, { shallow })`\n  - `routeChangeComplete(url, { shallow })`\n  - `hashChangeStart(url, { shallow })`\n  - `hashChangeComplete(url, { shallow })`\n\n## Not yet supported\n\nPRs welcome!  \nThese fields just have default values; these methods do nothing.\n\n- `router.isReady`\n- `router.basePath`\n- `router.isFallback`\n- `router.isLocaleDomain`\n- `router.locale`\n- `router.locales`\n- `router.defaultLocale`\n- `router.domainLocales`\n- `router.prefetch()`\n- `router.back()`\n- `router.beforePopState(cb)`\n- `router.reload()`\n- `router.events` not implemented:\n  - `routeChangeError`\n  - `beforeHistoryChange`\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscottrippey%2Fnext-router-mock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscottrippey%2Fnext-router-mock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscottrippey%2Fnext-router-mock/lists"}