Next.js: рдпреВрдЬрд░ рд░рд╛рдЙрдЯрд░ рдХрд╛ рдореЙрдХ рдХреИрд╕реЗ рдХрд░реЗрдВ?

рдХреЛ рдирд┐рд░реНрдорд┐рдд 1 рдЬреВрди 2019  ┬╖  21рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: vercel/next.js

Next.js рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдкреНрд░рд╢реНрди

рдореИрдВ рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдореЗрд░рд╛ рдШрдЯрдХ рдЙрдкрдпреЛрдЧ рд░рд╛рдЙрдЯрд░ рд╣реБрдХ рдХреЗ рд╕рд╛рде рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдкреНрд░рд╕реНрддреБрдд рдХрд░рддрд╛ рд╣реИ (рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдореИрдВ рдпрд╣ рд╕рдордЭрдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рдХрд┐ рдирдпрд╛ рдЧрддрд┐рд╢реАрд▓ рд░реВрдЯрд┐рдВрдЧ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ), рдЗрд╕рд▓рд┐рдП рдореЗрд░реЗ рдкрд╛рд╕ рдХреЛрдб рд╣реИ:

import React from 'react';
import { NextPage } from 'next';
import { useRouter } from 'next/router';

const UserInfo : NextPage = () => {
  const router = useRouter();
  const { query } = router;

  return <div>Hello {query.user}</div>;
};

export default UserInfo;

рдФрд░ рдореИрдВ рдЬреЛ рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рд╡рд╣ рд╣реИ:

// test
import { render, cleanup, waitForElement } from '@testing-library/react';

import UserInfo from './$user';

// somehow mock useRouter for $user component!!!

afterEach(cleanup);

it('Should render correctly on route: /users/nikita', async () => {
  const { getByText } = render(<UserInfo />);

  await waitForElement(() => getByText(/Hello nikita!/i));
});

рд▓реЗрдХрд┐рди рдореБрдЭреЗ рдПрдХ рддреНрд░реБрдЯрд┐ рдорд┐рд▓рддреА рд╣реИ TypeError: Cannot read property 'query' of null рдЬреЛ const router = useRouter(); рд▓рд╛рдЗрди рдкрд░ рдЗрдВрдЧрд┐рдд рдХрд░рддрд╛ рд╣реИред

PS рдореБрдЭреЗ рдкрддрд╛ рд╣реИ рдХрд┐ рдбрд╛рдпрдирд╛рдорд┐рдХ рд░реВрдЯрд┐рдВрдЧ рдЕрднреА рдХреЗ рд▓рд┐рдП рдХреИрдирд░реА рд╡рд░реНрдЬрди рдкрд░ рдЙрдкрд▓рдмреНрдз рд╣реИ рдФрд░ рдмрджрд▓ рд╕рдХрддреА рд╣реИ, рд▓реЗрдХрд┐рди рдореБрдЭреЗ рд░рд╛рдЙрдЯрд░ рдХреЗ рд╕рд╛рде рд╕рдорд╕реНрдпрд╛ рд╣реИ, WIP рдлреАрдЪрд░ рдХреЗ рд╕рд╛рде рдирд╣реАрдВ (рдХреНрдпрд╛ рдореИрдВ рд╣реВрдВ?)

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

рдореИрдВрдиреЗ рдЗрд╕реЗ рдЗрд╕ рддрд░рд╣ рдордЬрд╝рд╛рдХ рдЙрдбрд╝рд╛рдпрд╛, рдореБрдЭреЗ рдХреЗрд╡рд▓ useRouter рдирд┐рд░реНрдпрд╛рдд рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдпрд╣ рдореЗрд░реЗ рдЙрджреНрджреЗрд╢реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рдХрд╛рдлреА рдЕрдЪреНрдЫрд╛ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ:

jest.mock("next/router", () => ({
    useRouter() {
        return {
            route: "/",
            pathname: "",
            query: "",
            asPath: "",
        };
    },
}));

рд╕рднреА 21 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рдирдорд╕реНрддреЗ, рдпрд╣ рд╕реБрд╡рд┐рдзрд╛ рдЕрднреА рднреА рдкреНрд░рд╛рдпреЛрдЧрд┐рдХ рд╣реИ рд▓реЗрдХрд┐рди useRouter React.useContext рдХрд╛ рдЙрдкрдпреЛрдЧ next-server/dist/lib/router-context рд╕реЗ рд╕рдВрджрд░реНрдн рдХрд╛ рдЙрдкрднреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд░рддреА рд╣реИред рдЗрд╕рдХрд╛ рдордЬрд╛рдХ рдЙрдбрд╝рд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдкрдХреЛ рдЗрд╕реЗ рдЗрд╕ рд▓рд╛рдЗрди рдХреЗ рд╕рдорд╛рди рд╕рдВрджрд░реНрдн рдкреНрд░рджрд╛рддрд╛ рдореЗрдВ рд▓рдкреЗрдЯрдирд╛ рд╣реЛрдЧрд╛

@ijjk рд╣рд╛рдп, рдзрдиреНрдпрд╡рд╛рдж!
рдореБрдЭреЗ рдирд╣реАрдВ рдкрддрд╛ рдХрд┐ рдореИрдВ рдЗрд╕реЗ рд╕рд╣реА рдХрд░ рд░рд╣рд╛ рд╣реВрдВ, рд▓реЗрдХрд┐рди рдкрд░реАрдХреНрд╖рд╛ рдкрд╛рд╕

import { render, cleanup, waitForElement } from '@testing-library/react';
import { createRouter } from 'next/router';
import { RouterContext } from 'next-server/dist/lib/router-context';

const router = createRouter('', { user: 'nikita' }, '', {
  initialProps: {},
  pageLoader: jest.fn(),
  App: jest.fn(),
  Component: jest.fn(),
});

import UserInfo from './$user';

afterEach(cleanup);

it('Should render correctly on route: /users/nikita', async () => {
  const { getByText } = render(
    <RouterContext.Provider value={router}>
      <UserInfo />
    </RouterContext.Provider>,
  );

  await waitForElement(() => getByText(/Hello nikita!/i));
});

рдпрджрд┐ рдХреНрд╡реЗрд░реА рдкреИрд░рд╛рдо рдХрд╛ рдирдХрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдФрд░ рдЕрдзрд┐рдХ рд╕рд╛рд░рдЧрд░реНрднрд┐рдд рддрд░реАрдХрд╛ рд╣реИ, рддреЛ рдореИрдВ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдорд╛рд░реНрдЧ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП /users/nikita ) рдкрд╛рд╕ рдХрд░ рдкрд╛рдКрдВрдЧрд╛ рдФрд░ рдлрд╝рд╛рдЗрд▓ рдХреЗ рд▓рд┐рдП рдкрде рдкрд╛рд╕ рдХрд░ рдкрд╛рдКрдВрдЧрд╛? рддреБрдо рдХреНрдпрд╛ рд╕реЛрдЪрддреЗ рд╣реЛ?

createRouter рдкрд░ рдХреЙрд▓ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп рд╕реАрдзреЗ рд░рд╛рдЙрдЯрд░ рдХрд╛ рдордЬрд╛рдХ рдЙрдбрд╝рд╛рдирд╛ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рд╡рд╣ рдПрдкреАрдЖрдИ рдЖрдВрддрд░рд┐рдХ рд╣реИ рдФрд░ рдХрд┐рд╕реА рднреА рд╕рдордп рдмрджрд▓ рд╕рдХрддрд╛ рд╣реИред рдпрд╣рд╛рдБ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ:

import React from 'react'
import { render } from '@testing-library/react'
import { RouterContext } from 'next-server/dist/lib/router-context'

describe('Basic test', () => {
  it('Renders current user value', async () => {
    const router = {
      pathname: '/users/$user',
      route: '/users/$user',
      query: { user: 'nikita' },
      asPath: '/users/nikita',
    }
    const User = require('../pages/users/$user').default
    const tree = render(
      <RouterContext.Provider value={router}>
         <User />
      </RouterContext.Provider>
    )
    expect(tree.getByText('User: nikita')).toBeTruthy()
  })
})

@ijjk рдЬреЛ рд╕рдордЭ рдореЗрдВ рдЖрддрд╛ рд╣реИред рдЖрдкрдХрд╛ рдмрд╣реБрдд рдмрд╣реБрдд рдзрдиреНрдпрд╡рд╛рдж!

рдХреНрдпрд╛ рдПрдВрдЬрд╛рдЗрдо + рдЬреЗрд╕реНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд░рд╛рдЙрдЯрд░ рдХрд╛ рдордЬрд╛рдХ рдЙрдбрд╝рд╛рдиреЗ рдХрд╛ рдХреЛрдИ рддрд░реАрдХрд╛ рд╣реИ? рдореИрдВ рдереЛрдбрд╝реА рджреЗрд░ рдХреЗ рд▓рд┐рдП рдСрдирд▓рд╛рдЗрди рдЦреЛрдЬ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рдФрд░ рдХреЗрд╡рд▓ рдкреНрд░рд╛рд╕рдВрдЧрд┐рдХ рдкрд░рд┐рдгрд╛рдо рдЬреЛ рдЖрддреЗ рд╣реИрдВ рд╡рд╣ рд╣реИ рдпрд╣ рдореБрджреНрджрд╛ред

рдореИрдВ рдЗрд╕ рддрд░рд╣ рдЗрд╕рдХрд╛ рдордЬрд╛рдХ рдЙрдбрд╝рд╛рдиреЗ рдореЗрдВ рдХрд╛рдордпрд╛рдм рд░рд╣рд╛ред

import * as nextRouter from 'next/router';

nextRouter.useRouter = jest.fn();
nextRouter.useRouter.mockImplementation(() => ({ route: '/' }));

jest.spyOn рдореЗрд░реЗ рд▓рд┐рдП рднреА рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ -

import React from 'react'
import { render } from '@testing-library/react'
import ResultsProductPage from 'pages/results/[product]'

const useRouter = jest.spyOn(require('next/router'), 'useRouter')

describe('ResultsProductPage', () => {
  it('renders - display mode list', () => {
    useRouter.mockImplementationOnce(() => ({
      query: { product: 'coffee' },
    }))
    const { container } = render(
      <ResultsProductPage items={[{ name: 'mocha' }]} />
    )
    expect(container).toMatchSnapshot()
  })
})

рдореИрдВрдиреЗ рдЗрд╕реЗ рдЗрд╕ рддрд░рд╣ рдордЬрд╝рд╛рдХ рдЙрдбрд╝рд╛рдпрд╛, рдореБрдЭреЗ рдХреЗрд╡рд▓ useRouter рдирд┐рд░реНрдпрд╛рдд рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдпрд╣ рдореЗрд░реЗ рдЙрджреНрджреЗрд╢реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рдХрд╛рдлреА рдЕрдЪреНрдЫрд╛ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ:

jest.mock("next/router", () => ({
    useRouter() {
        return {
            route: "/",
            pathname: "",
            query: "",
            asPath: "",
        };
    },
}));

рдЕрдЧрд░ рдХреЛрдИ рдпрд╣рд╛рдВ useRouter рдХрд╛ рдордЬрд╛рдХ рдЙрдбрд╝рд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣рд╛ рд╣реИ рддреЛ рдмрд╕ рдПрдХ рдЕрдирд┐рд╡рд╛рд░реНрдп prefetch рд╕реЗ рд╣рд╕реНрддрдХреНрд╖реЗрдк рд╕реЗ рдмрдЪрдиреЗ рдХреЗ рд▓рд┐рдП, рддреЛ рдпрд╣ рдореГрдд рд╕рд░рд▓ рдирдХрд▓реА рдХрд╛рдо рдХрд░реЗрдЧрд╛

jest.mock("next/router", () => ({
  useRouter() {
    return {
      prefetch: () => null
    };
  }
}));

рдПрдХ рдЙрджрд╛рд╣рд░рдг рдЙрдкрдпреЛрдЧ рдХреЗрд╕ рдПрдХ рдлреЙрд░реНрдо рдШрдЯрдХ рд╣реЛрдЧрд╛ рдЬрд┐рд╕рдореЗрдВ рдХреБрдЫ рдРрд╕рд╛ рд╢рд╛рдорд┐рд▓ рд╣реИ:

  const router = useRouter();
  useEffect(() => {
    router.prefetch("/success");
    if (confirmSuccess) {
      doStuff();
      router.push( {pathname: "/success" } )
    }
  }, [data]);

@ijjk рдХреНрдпрд╛ рд╡рд╣ рд╡реНрдпрд╡рд╣рд╛рд░ рдирд╡реАрдирддрдо рд╕рдВрд╕реНрдХрд░рдг рдореЗрдВ рдмрджрд▓ рдЧрдпрд╛ рд╣реИ? рдореБрдЭреЗ next/dist/next-server/lib/router-context рд╕реЗ рдЖрдпрд╛рдд рдХрд░рдирд╛ рдкрдбрд╝рд╛ред рдЕрдЧрд░ рдореИрдВ рдЕрд▓рдЧ рд╕реЗ next-server рд╕реНрдерд╛рдкрд┐рдд рдХрд░рддрд╛ рд╣реВрдВ рддреЛ рдпрд╣ рд╕рдВрджрд░реНрдн рдХреЛ рдирд╣реАрдВ рдкрд╣рдЪрд╛рди рдкрд╛рдПрдЧрд╛ред

рдореБрдЭреЗ рднреА рдмрд┐рд▓рдХреБрд▓ рдпрд╣реА рд╕рдорд╕реНрдпрд╛ рд╣реИред
рд╣рдо рдЕрдЧрд▓реЗ 9 рдХреЗ рддрд╣рдд рд╣реИрдВред RouterContext.Provider рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдХреЛрдИ рднреА рд╕рдорд╛рдзрд╛рди рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВред
рдореЗрд░реЗ рдкрд░реАрдХреНрд╖рдг рдкрд╛рд╕ рдХрд╛ рдПрдХрдорд╛рддреНрд░ рддрд░реАрдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХреЗ рдКрдкрд░ рдПрдХ рд╡реИрд╢реНрд╡рд┐рдХ рд╡рд╕реНрддреБ рдХреЗ рд░реВрдк рдореЗрдВ @aeksco рд╕рдорд╛рдзрд╛рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реИред рдЕрдиреНрдпрдерд╛ useRouter рд╣рдореЗрд╢рд╛ рдЕрдкрд░рд┐рднрд╛рд╖рд┐рдд рд╣реЛрддрд╛ рд╣реИред
рдпрд╣ рдЖрджрд░реНрд╢ рдирд╣реАрдВ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдореИрдВ рдЕрдкрдиреЗ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рдЕрд▓рдЧ-рдЕрд▓рдЧ рдкреИрд░рд╛рдореАрдЯрд░ рд╕реЗрдЯ рдирд╣реАрдВ рдХрд░ рд╕рдХрддрд╛ред
рдЗрд╕ рдкрд░ рдХреЛрдИ рд╡рд┐рдЪрд╛рд░?

рд╕рдВрдкрд╛рджрд┐рдд рдХрд░реЗрдВ
рдореИрдВ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдПрдХ рд╡реИрд╢реНрд╡рд┐рдХ рдирдХрд▓реА рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ next/router рдЖрдпрд╛рдд рдФрд░ рдПрдХ spyOn рдирдХрд▓реА рд╣реИ, рдЬреЛ рдореБрдЭреЗ рдлреЛрди рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ рдкрд░ mockImplementation(() => ({// whatever you want}) рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рд╛ рдореЗрдВред
рдРрд╕рд╛ рдХреБрдЫ рджрд┐рдЦрддрд╛ рд╣реИ:

jest.mock("next/router", () => ({
  useRouter() {
    return {
      route: "",
      pathname: "",
      query: "",
      asPath: "",
    };
  },
}));

const useRouter = jest.spyOn(require("next/router"), "useRouter");

рдлрд┐рд░ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдореЗрдВ:

useRouter.mockImplementation(() => ({
      route: "/yourRoute",
      pathname: "/yourRoute",
      query: "",
      asPath: "",
    }));

рдпрд╣ рдЖрджрд░реНрд╢ рдирд╣реАрдВ рд╣реИ рд▓реЗрдХрд┐рди рдХрдо рд╕реЗ рдХрдо рдпрд╣ рдореЗрд░реЗ рд▓рд┐рдП рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ

рдПрдлрдбрдмреНрд▓реНрдпреВрдЖрдИрдбрдмреНрд▓реНрдпреВ рдпрд╣реА рд╡рд╣ рд╣реИ рдЬрд┐рд╕ рдкрд░ рдореИрдВрдиреЗ рд╕рдордЭреМрддрд╛ рдХрд┐рдпрд╛ рд╣реИ:

import { RouterContext } from 'next/dist/next-server/lib/router-context'
import { action } from '@storybook/addon-actions'
import PropTypes from 'prop-types'
import { useState } from 'react'
import Router from 'next/router'

function RouterMock({ children }) {
  const [pathname, setPathname] = useState('/')

  const mockRouter = {
    pathname,
    prefetch: () => {},
    push: async newPathname => {
      action('Clicked link')(newPathname)
      setPathname(newPathname)
    }
  }

  Router.router = mockRouter

  return (
    <RouterContext.Provider value={mockRouter}>
      {children}
    </RouterContext.Provider>
  )
}

RouterMock.propTypes = {
  children: PropTypes.node.isRequired
}

export default RouterMock

рдореБрдЭреЗ рдХреБрдЫ рдРрд╕рд╛ рдЪрд╛рд╣рд┐рдП рдерд╛ рдЬреЛ Storybook рдФрд░ Jest рджреЛрдиреЛрдВ рдореЗрдВ рдХрд╛рдо рдХрд░реЗред рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдЪрд╛рд▓ рдЪрд▓ рд░рд╣реА рд╣реИ, рдЖрдкрдиреЗ рдШрдЯрдХ рдкреЗрдбрд╝ рдХреЗ рдКрдкрд░ рдХрд╣реАрдВ рднреА <Routermock> рд╕реЗрдЯ рдХрд┐рдпрд╛ рд╣реИред рдпрд╣ рдЖрджрд░реНрд╢ рдирд╣реАрдВ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдореБрдЭреЗ Router.router рд▓рдЧрд╛рддрд╛рд░ рдУрд╡рд░рд░рд╛рдЗрдб рдХрд░рдирд╛ рдкрд╕рдВрдж рдирд╣реАрдВ рд╣реИред

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдПрдХ рдЖрдзрд┐рдХрд╛рд░рд┐рдХ рдореЙрдХрд┐рдВрдЧ рд╕рдорд╛рдзрд╛рди рдкреНрдпрд╛рд░рд╛ рд╣реЛрдЧрд╛ :)

@smasontst рдХреА рд╡рд┐рдзрд┐ рдиреЗ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдХрд╛рдо рдХрд┐рдпрд╛, рд▓реЗрдХрд┐рди mockImplementationOnce() рд╕рд╛рд╡рдзрд╛рди рд░рд╣реЗрдВ ... рдпрджрд┐ рдЖрдкрдХреЗ рдШрдЯрдХ рдХреЛ рдЖрдкрдХреЗ рдкрд░реАрдХреНрд╖рдг рдХреЗ рджреМрд░рд╛рди рдПрдХ рд╕реЗ рдЕрдзрд┐рдХ рдмрд╛рд░ рдкреНрд░рд╕реНрддреБрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рддреЛ рдЖрдк рдкрд╛рдПрдВрдЧреЗ рдХрд┐ рдпрд╣ рджреВрд╕рд░реЗ рд░реЗрдВрдбрд░ рдкрд░ рдЖрдкрдХреЗ рдирдХрд▓реА рд░рд╛рдЙрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░ рд░рд╣рд╛ рд╣реИ рдФрд░ рдЖрдкрдХрд╛ рдкрд░реАрдХреНрд╖рдг рд╡рд┐рдлрд▓ рд╣реЛ рдЬрд╛рдПрдЧрд╛ред рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рд╣рдореЗрд╢рд╛ mockImplementation() рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рд╣реИ, рдЬрдм рддрдХ рдХрд┐ рдЖрдкрдХреЗ рдкрд╛рд╕ mockImplementationOnce() рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдХреЛрдИ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХрд╛рд░рдг рди рд╣реЛред

рдореБрдЭреЗ рдЕрдкрдиреЗ рдкреНрд░рд╛рд░рдВрднрд┐рдХ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рдирд╛ рдкрдбрд╝рд╛ рдХреНрдпреЛрдВрдХрд┐ рдореБрдЭреЗ рдкрд░реАрдХреНрд╖рдг-рджрд░-рдкрд░реАрдХреНрд╖рдг рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЕрджреНрд╡рд┐рддреАрдп useRouter рд╕реНрдерд┐рддрд┐ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдереАред @ nterol24s рджреНрд╡рд╛рд░рд╛ рдкреНрд░рджрд╛рди рдХрд┐рдП рдЧрдП рдЙрджрд╛рд╣рд░рдг рд╕реЗ рдПрдХ рдкреГрд╖реНрда рд▓рд┐рдпрд╛ рдФрд░ рдЗрд╕реЗ рдЙрдкрдпреЛрдЧрд┐рддрд╛ рдлрд╝рдВрдХреНрд╢рди рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рд░реНрдп рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрджреНрдпрддрди рдХрд┐рдпрд╛ рдЬрд┐рд╕реЗ рдореИрдВ рдЕрдкрдиреЗ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдореЗрдВ рдХреЙрд▓ рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ:

// Mocks useRouter
const useRouter = jest.spyOn(require("next/router"), "useRouter");

/**
 * mockNextUseRouter
 * Mocks the useRouter React hook from Next.js on a test-case by test-case basis
 */
export function mockNextUseRouter(props: {
    route: string;
    pathname: string;
    query: string;
    asPath: string;
}) {
    useRouter.mockImplementationOnce(() => ({
        route: props.route,
        pathname: props.pathname,
        query: props.query,
        asPath: props.asPath,
    }));
}

рдореИрдВ рдЕрдм рдЗрд╕ рддрд░рд╣ рдХреА рдЪреАрдЬреЗрдВ рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ:

import { mockNextUseRouter } from "@src/test_util";

describe("Pricing Page", () => {

    // Mocks Next.js route
    mockNextUseRouter({
        route: "/pricing",
        pathname: "/pricing",
        query: "",
        asPath: `/pricing?error=${encodeURIComponent("Uh oh - something went wrong")}`,
    });

    test("render with error param", () => {
        const tree: ReactTestRendererJSON = Renderer.create(
            <ComponentThatDependsOnUseRouter />
        ).toJSON();
        expect(tree).toMatchSnapshot();
    });
});

@mbrowne рджреНрд╡рд╛рд░рд╛ рдЯрд┐рдкреНрдкрдгреА рдкрд░ рдзреНрдпрд╛рди рджреЗрдВ - рдЖрдк рдЗрд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреЗ рд╕рд╛рде рдПрдХ рд╣реА рд╕рдорд╕реНрдпрд╛ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░реЗрдВрдЧреЗ, рд▓реЗрдХрд┐рди рдЖрдк рдКрдкрд░ рджрд┐рдП рдЧрдП рдЙрджрд╛рд╣рд░рдг рдХреЛ mockNextUseRouter рдФрд░ mockNextUseRouterOnce рдлрд╝рдВрдХреНрд╢рдВрд╕ рдореЗрдВ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдпрджрд┐ рдЖрдкрдХреЛ рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛред

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛ рдПрдХ рдмрдбрд╝рд╛: +1: рдЖрдзрд┐рдХрд╛рд░рд┐рдХ рдореЙрдХрд┐рдВрдЧ рд╕рдорд╛рдзрд╛рди

рдХрд┐рд╕реА рднреА рд╡реНрдпрдХреНрддрд┐ рдХреЗ рд▓рд┐рдП рдЬреЛ рд╡рд┐рд╢реНрд╡ рд╕реНрддрд░ рдкрд░ рдирдХрд▓реА Router рдЙрджрд╛рд╣рд░рдг рдЪрд╛рд╣рддрд╛ рд╣реИ, рдЖрдк рдХрд╣реАрдВ рднреА __mocks__ рдлрд╝реЛрд▓реНрдбрд░ рд░рдЦ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ next/router рдкреИрдХреЗрдЬ рдХреЛ рдЗрд╕ рддрд░рд╣ рд▓рдХреНрд╖рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:

__mocks__/next/router/index.js (рдЗрд╕ рдлрд╝реЛрд▓реНрдбрд░ рд╕рдВрд░рдЪрдирд╛ рдкреИрдЯрд░реНрди рдХрд╛ рдкрд╛рд▓рди рдХрд░рдирд╛ рд╣реИ!)

рдиреАрдЪреЗ рджрд┐рдпрд╛ рдЧрдпрд╛ рдпрд╣ рдЙрджрд╛рд╣рд░рдг Router.push рдФрд░ Router.replace рд▓рдХреНрд╖рд┐рдд рдХрд░рддрд╛ рд╣реИ:

jest.mock("next/router", () => ({
  // spread out all "Router" exports
  ...require.requireActual("next/router"),

  // shallow merge the "default" exports with...
  default: {
    // all actual "default" exports...
    ...require.requireActual("next/router").default,

    // and overwrite push and replace to be jest functions
    push: jest.fn(),
    replace: jest.fn(),
   },
}));

// export the mocked instance above
module.exports = require.requireMock("next/router");

рдЕрдм, рдХрд╣реАрдВ рднреА import Router from "next/router"; рдпрд╣ рдирдХрд▓реА рдЙрджрд╛рд╣рд░рдг рд╣реЛрдЧрд╛ред рдЖрдк рдЙрди рдкрд░ mockImplementation рдлрд╝рдВрдХреНрд╢рдВрд╕ рднреА рдЬреЛрдбрд╝ рдкрд╛рдПрдВрдЧреЗ рдХреНрдпреЛрдВрдХрд┐ рдЙрдирдХрд╛ рд╡рд┐рд╢реНрд╡ рд╕реНрддрд░ рдкрд░ рдордЬрд╝рд╛рдХ рдЙрдбрд╝рд╛рдпрд╛ рдЬрд╛рдПрдЧрд╛ред
рдпрджрд┐ рдЖрдк рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдХрд┐ рдЗрд╕ рдЙрджрд╛рд╣рд░рдг рдХреЛ рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рд░реАрд╕реЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рдП, рддреЛ рдЕрдкрдиреЗ jest.json рдореЗрдВ рдПрдХ clearMocks рдЧреБрдг рдЬреЛрдбрд╝реЗрдВред

рд╕рдВрджрд░реНрдн рдХреЗ рд▓рд┐рдП, рдпрджрд┐ рдЖрдк рдХрд┐рд╕реА рд╡рд┐рд╢рд┐рд╖реНрдЯ рдирд┐рд░реНрдпрд╛рдд рдХреЛ рд▓рдХреНрд╖рд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рдпрд╣рд╛рдВ Router рд╕рдВрд░рдЪрдирд╛ рд╣реИ:

{
  __esModule: true,
  useRouter: [Function: useRouter],
  makePublicRouterInstance: [Function: makePublicRouterInstance],
  default: { 
    router: null,
    readyCallbacks: [ 
      [Function],
      [Function],
      [Function],
      [Function],
      [Function],
      [Function] 
    ],
    ready: [Function: ready],
    push: [Function],
    replace: [Function],
    reload: [Function],
    back: [Function],
    prefetch: [Function],
    beforePopState: [Function] },
    withRouter: [Function: withRouter],
    createRouter: [Function: createRouter],
    Router: { 
      [Function: Router]
      events: { 
        on: [Function: on],
        off: [Function: off],
        emit: [Function: emit] 
       } 
    },
    NextRouter: undefined 
  }
}

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдпрджрд┐ рдЖрдкрдХреЗ рдкрд╛рд╕ mount рдШрдЯрдХ рд╣реИрдВ рдЬреЛ withRouter рдпрд╛ useRouter рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЖрдк

import { createElement } from "react";
import { mount } from "enzyme";
import { RouterContext } from "next/dist/next-server/lib/router-context";
// Important note: The RouterContext import will vary based upon the next version you're using;
// in some versions, it's a part of the next package, in others, it's a separate package

/**
 * Factory function to create a mounted RouterContext wrapper for a React component
 *
 * <strong i="33">@function</strong> withRouterContext
 * <strong i="34">@param</strong> {node} Component - Component to be mounted
 * <strong i="35">@param</strong> {object} initialProps - Component initial props for setup.
 * <strong i="36">@param</strong> {object} state - Component initial state for setup.
 * <strong i="37">@param</strong> {object} router - Initial route options for RouterContext.
 * <strong i="38">@param</strong> {object} options - Optional options for enzyme's mount function.
 * <strong i="39">@function</strong> createElement - Creates a wrapper around passed in component (now we can use wrapper.setProps on root)
 * <strong i="40">@returns</strong> {wrapper} - a mounted React component with Router context.
*/
export const withRouterContext = (
  Component,
  initialProps = {},
  state = null,
  router = {
    pathname: "/",
    route: "/",
    query: {},
    asPath: "/",
  },
  options = {},
) => {
  const wrapper = mount(
    createElement(
      props => ( 
        <RouterContext.Provider value={router}>
          <Component { ...props } /> 
        </RouterContext.Provider>
      ),
      initialProps,
    ),
    options,
  );
  if (state) wrapper.find(Component).setState(state);
  return wrapper;
};

рдЙрджрд╛рд╣рд░рдг рдЙрдкрдпреЛрдЧ:

import React from "react";
import withRouterContext from "./path/to/reusable/test/utils"; // alternatively you can make this global
import ExampleComponent from "./index";

const initialProps = {
  id: "0123456789",
  firstName: "John",
  lastName: "Smith"
};

const router = {
  pathname: "/users/$user",
  route: "/users/$user",
  query: { user: "john" },
  asPath: "/users/john",
};

const wrapper = withRouterContext(ExampleComponent, initialProps, null, router);

...etc

рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреНрдпреЛрдВ рдХрд░реЗрдВ? рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЖрдкрдХреЛ рд░рд╛рдЙрдЯрд░ рдХреЗ рд╕рдВрджрд░реНрдн рдореЗрдВ рд▓рд┐рдкрдЯреЗ рдПрдХ рдкреБрди: рдкреНрд░рдпреЛрдЬреНрдп рдорд╛рдЙрдВрдЯреЗрдб рд░рд┐рдПрдХреНрдЯ рдШрдЯрдХ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ; рдФрд░ рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ рдпрд╣ рдЖрдкрдХреЛ рд░реВрдЯ рдШрдЯрдХ рдкрд░ wrapper.setProps(..) рдкрд░ рдХреЙрд▓ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ!

import { useRouter } from 'next/router'

jest.mock('next/router', () => ({
  __esModule: true,
  useRouter: jest.fn()
}))

describe('XXX', () => {
  it('XXX', () => {
    const mockRouter = {
      push: jest.fn() // the component uses `router.push` only
    }
    ;(useRouter as jest.Mock).mockReturnValue(mockRouter)
    // ...
    expect(mockRouter.push).toHaveBeenCalledWith('/hello/world')
  })
})

рдЗрдирдореЗрдВ рд╕реЗ рдХрд┐рд╕реА рднреА рд╕рдорд╛рдзрд╛рди рдиреЗ рдореЗрд░реЗ рд▓рд┐рдП рдХрд╛рдо рдирд╣реАрдВ рдХрд┐рдпрд╛ред рдЬреЗрд╕реНрдЯ рдбреЙрдХреНрд╕ рдореЗрдВ "рд╕рд╣реА" рд╡рд░реНрдХрдлрд╝реНрд▓реЛ рдХрд╛ рд╡рд░реНрдгрди рдпрд╣рд╛рдБ рднреА рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ: https://jestjs.io/docs/en/es6-class-mocks#spying -on-methods-of-our-class

рд╣рд╛рд▓рд╛рдВрдХрд┐, рдореИрдВ рдирдХрд▓реА рджреЗрдЦ рд╕рдХрддрд╛ рд╣реВрдВ, рд▓реЗрдХрд┐рди рдпрд╣ рдХреЙрд▓ рд░рд┐рдХреЙрд░реНрдб рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ ...

рдпрд╣ рдореЗрд░рд╛ рд╡рд░реНрддрдорд╛рди test-utils.tsx ред рдореБрдЭреЗ рдпрд╣ рд╡реИрд╢реНрд╡рд┐рдХ рдирдХрд▓реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╕реЗ рдмрд╣реБрдд рдмреЗрд╣рддрд░ рд▓рдЧрддрд╛ рд╣реИред

import React from 'react';
import { render as defaultRender } from '@testing-library/react';
import { RouterContext } from 'next/dist/next-server/lib/router-context';
import { NextRouter } from 'next/router';

export * from '@testing-library/react';

// --------------------------------------------------
// Override the default test render with our own
//
// You can override the router mock like this:
//
// const { baseElement } = render(<MyComponent />, {
//   router: { pathname: '/my-custom-pathname' },
// });
// --------------------------------------------------
type DefaultParams = Parameters<typeof defaultRender>;
type RenderUI = DefaultParams[0];
type RenderOptions = DefaultParams[1] & { router?: Partial<NextRouter> };

export function render(
  ui: RenderUI,
  { wrapper, router, ...options }: RenderOptions = {},
) {
  if (!wrapper) {
    wrapper = ({ children }) => (
      <RouterContext.Provider value={{ ...mockRouter, ...router }}>
        {children}
      </RouterContext.Provider>
    );
  }

  return defaultRender(ui, { wrapper, ...options });
}

const mockRouter: NextRouter = {
  basePath: '',
  pathname: '/',
  route: '/',
  asPath: '/',
  query: {},
  push: jest.fn(),
  replace: jest.fn(),
  reload: jest.fn(),
  back: jest.fn(),
  prefetch: jest.fn(),
  beforePopState: jest.fn(),
  events: {
    on: jest.fn(),
    off: jest.fn(),
    emit: jest.fn(),
  },
  isFallback: false,
};

@flybayer рдзрдиреНрдпрд╡рд╛рдж! рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ!

@flybayer рдХрд╛ рд╕рдорд╛рдзрд╛рди рдореЗрд░реЗ рд▓рд┐рдП рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдореБрдЭреЗ рд░реЗрдВрдбрд░ рдлрд╝рдВрдХреНрд╢рди рдкрд░ рд░рд┐рдЯрд░реНрди рдкреНрд░рдХрд╛рд░ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛

import { render as defaultRender, RenderResult } from '@testing-library/react'

...

export function render(
  ui: RenderUI,
  { wrapper, router, ...options }: RenderOptions = {}
): RenderResult { ... }

рдХрд┐рд╕реА рднреА рд╡реНрдпрдХреНрддрд┐ рдХреЗ рд▓рд┐рдП рдЬреЛ рд╡рд┐рд╢реНрд╡ рд╕реНрддрд░ рдкрд░ рдирдХрд▓реА Router рдЙрджрд╛рд╣рд░рдг рдЪрд╛рд╣рддрд╛ рд╣реИ, рдЖрдк рдХрд╣реАрдВ рднреА __mocks__ рдлрд╝реЛрд▓реНрдбрд░ рд░рдЦ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ next/router рдкреИрдХреЗрдЬ рдХреЛ рдЗрд╕ рддрд░рд╣ рд▓рдХреНрд╖рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:

__mocks__/next/router/index.js (рдЗрд╕ рдлрд╝реЛрд▓реНрдбрд░ рд╕рдВрд░рдЪрдирд╛ рдкреИрдЯрд░реНрди рдХрд╛ рдкрд╛рд▓рди рдХрд░рдирд╛ рд╣реИ!)

рдиреАрдЪреЗ рджрд┐рдпрд╛ рдЧрдпрд╛ рдпрд╣ рдЙрджрд╛рд╣рд░рдг Router.push рдФрд░ Router.replace рд▓рдХреНрд╖рд┐рдд рдХрд░рддрд╛ рд╣реИ:

jest.mock("next/router", () => ({
  // spread out all "Router" exports
  ...require.requireActual("next/router"),

  // shallow merge the "default" exports with...
  default: {
    // all actual "default" exports...
    ...require.requireActual("next/router").default,

    // and overwrite push and replace to be jest functions
    push: jest.fn(),
    replace: jest.fn(),
   },
}));

// export the mocked instance above
module.exports = require.requireMock("next/router");

рдЕрдм, рдХрд╣реАрдВ рднреА import Router from "next/router"; рдпрд╣ рдирдХрд▓реА рдЙрджрд╛рд╣рд░рдг рд╣реЛрдЧрд╛ред рдЖрдк рдЙрди рдкрд░ mockImplementation рдлрд╝рдВрдХреНрд╢рдВрд╕ рднреА рдЬреЛрдбрд╝ рдкрд╛рдПрдВрдЧреЗ рдХреНрдпреЛрдВрдХрд┐ рдЙрдирдХрд╛ рд╡рд┐рд╢реНрд╡ рд╕реНрддрд░ рдкрд░ рдордЬрд╝рд╛рдХ рдЙрдбрд╝рд╛рдпрд╛ рдЬрд╛рдПрдЧрд╛ред
рдпрджрд┐ рдЖрдк рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдХрд┐ рдЗрд╕ рдЙрджрд╛рд╣рд░рдг рдХреЛ рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рд░реАрд╕реЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рдП, рддреЛ рдЕрдкрдиреЗ jest.json рдореЗрдВ рдПрдХ clearMocks рдЧреБрдг рдЬреЛрдбрд╝реЗрдВред

рд╕рдВрджрд░реНрдн рдХреЗ рд▓рд┐рдП, рдпрджрд┐ рдЖрдк рдХрд┐рд╕реА рд╡рд┐рд╢рд┐рд╖реНрдЯ рдирд┐рд░реНрдпрд╛рдд рдХреЛ рд▓рдХреНрд╖рд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рдпрд╣рд╛рдВ Router рд╕рдВрд░рдЪрдирд╛ рд╣реИ:

{
  __esModule: true,
  useRouter: [Function: useRouter],
  makePublicRouterInstance: [Function: makePublicRouterInstance],
  default: { 
    router: null,
    readyCallbacks: [ 
      [Function],
      [Function],
      [Function],
      [Function],
      [Function],
      [Function] 
    ],
    ready: [Function: ready],
    push: [Function],
    replace: [Function],
    reload: [Function],
    back: [Function],
    prefetch: [Function],
    beforePopState: [Function] },
    withRouter: [Function: withRouter],
    createRouter: [Function: createRouter],
    Router: { 
      [Function: Router]
      events: { 
        on: [Function: on],
        off: [Function: off],
        emit: [Function: emit] 
       } 
    },
    NextRouter: undefined 
  }
}

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдпрджрд┐ рдЖрдкрдХреЗ рдкрд╛рд╕ mount рдШрдЯрдХ рд╣реИрдВ рдЬреЛ withRouter рдпрд╛ useRouter рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЖрдк

import { createElement } from "react";
import { mount } from "enzyme";
import { RouterContext } from "next/dist/next-server/lib/router-context";
// Important note: The RouterContext import will vary based upon the next version you're using;
// in some versions, it's a part of the next package, in others, it's a separate package

/**
 * Factory function to create a mounted RouterContext wrapper for a React component
 *
 * <strong i="33">@function</strong> withRouterContext
 * <strong i="34">@param</strong> {node} Component - Component to be mounted
 * <strong i="35">@param</strong> {object} initialProps - Component initial props for setup.
 * <strong i="36">@param</strong> {object} state - Component initial state for setup.
 * <strong i="37">@param</strong> {object} router - Initial route options for RouterContext.
 * <strong i="38">@param</strong> {object} options - Optional options for enzyme's mount function.
 * <strong i="39">@function</strong> createElement - Creates a wrapper around passed in component (now we can use wrapper.setProps on root)
 * <strong i="40">@returns</strong> {wrapper} - a mounted React component with Router context.
*/
export const withRouterContext = (
  Component,
  initialProps = {},
  state = null,
  router = {
    pathname: "/",
    route: "/",
    query: {},
    asPath: "/",
  },
  options = {},
) => {
  const wrapper = mount(
    createElement(
      props => ( 
        <RouterContext.Provider value={router}>
          <Component { ...props } /> 
        </RouterContext.Provider>
      ),
      initialProps,
    ),
    options,
  );
  if (state) wrapper.find(Component).setState(state);
  return wrapper;
};

рдЙрджрд╛рд╣рд░рдг рдЙрдкрдпреЛрдЧ:

import React from "react";
import withRouterContext from "./path/to/reusable/test/utils"; // alternatively you can make this global
import ExampleComponent from "./index";

const initialProps = {
  id: "0123456789",
  firstName: "John",
  lastName: "Smith"
};

const router = {
  pathname: "/users/$user",
  route: "/users/$user",
  query: { user: "john" },
  asPath: "/users/john",
};

const wrapper = withRouterContext(ExampleComponent, initialProps, null, router);

...etc

рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреНрдпреЛрдВ рдХрд░реЗрдВ? рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЖрдкрдХреЛ рд░рд╛рдЙрдЯрд░ рдХреЗ рд╕рдВрджрд░реНрдн рдореЗрдВ рд▓рд┐рдкрдЯреЗ рдПрдХ рдкреБрди: рдкреНрд░рдпреЛрдЬреНрдп рдорд╛рдЙрдВрдЯреЗрдб рд░рд┐рдПрдХреНрдЯ рдШрдЯрдХ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ; рдФрд░ рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ рдпрд╣ рдЖрдкрдХреЛ рд░реВрдЯ рдШрдЯрдХ рдкрд░ wrapper.setProps(..) рдкрд░ рдХреЙрд▓ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ!

рд╣рд╛рдп, рдореБрдЭреЗ рдпрд╣ рддреНрд░реБрдЯрд┐ рдорд┐рд▓ рд░рд╣реА рд╣реИ:

рд▓реЗрдЦрди рддреНрд░реБрдЯрд┐: requ.requireMock рдХреЛрдИ рдлрд╝рдВрдХреНрд╢рди рдирд╣реАрдВ рд╣реИ

рдЗрд╕ рд╕рдорд╛рдзрд╛рди рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛:

jest.mock("next/router", () => ({
  // spread out all "Router" exports
  ...jest.requireActual("next/router"),

  // shallow merge the "default" exports with...
  default: {
    // all actual "default" exports...
    ...jest.requireActual("next/router").default,

    // and overwrite push and replace to be jest functions
    push: jest.fn(),
    replace: jest.fn(),
  },
}));

// export the mocked instance above
module.exports = jest.requireMock("next/router");
рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

pie6k picture pie6k  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

YarivGilad picture YarivGilad  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

sospedra picture sospedra  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

DvirSh picture DvirSh  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

kenji4569 picture kenji4569  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ