Code Notes

🧪Jest global mocks

Some useful mocks

window.matchMedia

jsdom still doesn’t implement matchMedia. The mock needs the modern addEventListener/removeEventListener/dispatchEvent shape — I keep the deprecated addListener/removeListener in there too for older libs that still call them.

/**
 * Contract + integration tests will use node env, not jsdom;
 * window doesn't exist in node and will make jest fail.
 * Put any mocks referencing 'window' in this 'if' block.
 */
if (typeof window !== 'undefined') {
  window.matchMedia = jest.fn().mockImplementation((query) => ({
    matches: false,
    media: query,
    onchange: null,
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
    // deprecated, but some libs still use them
    addListener: jest.fn(),
    removeListener: jest.fn(),
  }))
}
jest.mock('next/link', () => {
  return ({ children }) => {
    return children
  }
})

Next.js router

For the App Router, mock next/navigation:

jest.mock('next/navigation', () => ({
  useRouter: () => ({
    push: jest.fn(),
    replace: jest.fn(),
    back: jest.fn(),
    prefetch: jest.fn(),
    refresh: jest.fn(),
  }),
  usePathname: () => '/',
  useSearchParams: () => new URLSearchParams(),
}))

Legacy (Pages Router only):

jest.mock('next/router', () => {
  return {
    push: () => {},
    replace: () => {},
    prefetch: () => {},
  }
})

next/config (legacy)

publicRuntimeConfig only exists in the Pages Router — it’s unavailable in the App Router, where plain env vars are preferred. Keeping this for old codebases:

import config from 'config'

const mockPublicConfigData = {
  env: config.get('env'),
  base: config.get('base'),
  client: config.get('client'),
}

jest.mock('next/config', () => () => ({
  publicRuntimeConfig: mockPublicConfigData,
}))

I used to stub MutationObserver here too, but jsdom has shipped a real implementation since 2020, so that’s no longer needed.