1

我对多页应用程序进行了测试。现在我想在测试中初始化一个路由。但我无法使用 react-router initialEntries 使其工作。我尝试了使用 createMemoryHistory 和 MemoryRouter 的路由器。但测试总是以“/”路由开始。

如何使测试从“/page-1”开始?

这是代码沙盒的链接 https://codesandbox.io/s/testing-react-89nfe?file=/src/index.js 由于沙盒还不支持玩笑模拟,因此测试在代码沙盒上失败。

app.test.js

import 'mutationobserver-shim'
import React from 'react'
import {render, fireEvent, waitFor} from '@testing-library/react'
import { MemoryRouter, Router } from 'react-router-dom'
import { createMemoryHistory } from 'history'
import App from './app'
import { submitForm } from './api';

function renderWithRouter(
  ui,
  {
    route = '/',
    history = createMemoryHistory({ initialEntries: [route] }),
  } = {}
) {
  const Wrapper = ({ children }) => (
    <Router history={history}>{children}</Router>
  )
  return {
    ...render(ui, { wrapper: Wrapper }),
    // adding `history` to the returned utilities to allow us
    // to reference it in our tests (just try to avoid using
    // this to test implementation details).
    history,
  }
}

jest.mock('./api');
test('multi-step form', async () => {
  submitForm.mockResolvedValue({ success: true })
  const input = { food: 'pizza', drink: 'beer' }

  const foodLabelRegex = /food/i;
  const drinkLabelRegex = /drink/i;
  const nextButtonRegex = /next/i;
  const reviewButtonRegex = /review/i;
  const confirmRegex = /confirm/i;
  const page2Regex = /page 2/i;
  const successRegex = /success/i;
  // const name = container.querySelector('input[name="name"]')

  const {debug, getByLabelText, getByText, getByRole, container } = renderWithRouter(<App />, {
    route: '/page-1'
  });
  // const {debug, getByLabelText, getByText, getByRole, container } = render(
  //   <MemoryRouter initialEntries={['/page-1']}>
  //     <App />
  //   </MemoryRouter>
  // );

  fireEvent.click(getByRole('link'))

  fireEvent.change(getByLabelText(foodLabelRegex), {target: {value: input.food}})
  fireEvent.click(getByText(nextButtonRegex))

  await waitFor(() => expect(getByRole('heading')).toHaveTextContent(page2Regex))

  fireEvent.change(getByLabelText(drinkLabelRegex), {target: {value: input.drink}})
  fireEvent.click(getByText(reviewButtonRegex))

  await waitFor(() => expect(getByRole('heading')).toHaveTextContent(confirmRegex))

  expect(getByLabelText(foodLabelRegex)).toHaveTextContent(input.food)
  expect(getByLabelText(drinkLabelRegex)).toHaveTextContent(input.drink)

  fireEvent.click(getByText(confirmRegex, { selector: 'button' }))

  await waitFor(() => {
    expect(submitForm).toHaveBeenCalledTimes(1)
    expect(getByRole('heading')).toHaveTextContent(successRegex)
    expect(container.querySelector('a[id="go-home"]')).toBeTruthy()
    debug()
  });
})

应用程序.js

import React from 'react'
import {
  Switch,
  Route,
  Link,
  //HashRouter as Router,
  BrowserRouter as Router,
} from 'react-router-dom'
import {submitForm} from './api'

function Main() {
  return (
    <>
      <h1>Welcome to the app</h1>
      <Link to="/page-1">Fill out the form</Link>
    </>
  )
}

function Page1({state, setState, history}) {
  return (
    <>
      <h2>Page 1</h2>
      <form
        onSubmit={(e) => {
          e.preventDefault()
          history.push('/page-2')
        }}
      >
        <label htmlFor="food">Favorite Food</label>
        <input
          id="food"
          value={state.food}
          onChange={(e) => setState({food: e.target.value})}
        />
      </form>
      <Link to="/">Go Home</Link> | <Link to="/page-2">Next</Link>
    </>
  )
}

function Page2({state, setState, history}) {
  return (
    <>
      <h2>Page 2</h2>
      <form
        onSubmit={(e) => {
          e.preventDefault()
          history.push('/confirm')
        }}
      >
        <label htmlFor="drink">Favorite Drink</label>
        <input
          id="drink"
          value={state.drink}
          onChange={(e) => setState({drink: e.target.value})}
        />
      </form>
      <Link to="/page-1">Go Back</Link> | <Link to="/confirm">Review</Link>
    </>
  )
}

function Confirm({state, onConfirmClick}) {
  return (
    <>
      <h2>Confirm</h2>
      <div>
        <strong>Please confirm your choices</strong>
      </div>
      <div>
        <strong id="food-label">Favorite Food</strong>:{' '}
        <span aria-labelledby="food-label">{state.food}</span>
      </div>
      <div>
        <strong id="drink-label">Favorite Drink</strong>:{' '}
        <span aria-labelledby="drink-label">{state.drink}</span>
      </div>
      <Link to="/page-2">Go Back</Link> |{' '}
      <button onClick={onConfirmClick}>Confirm</button>
    </>
  )
}

function Success() {
  return (
    <>
      <h2>Success</h2>
      <div>
        <Link to="/" id="go-home">
          Go home
        </Link>
      </div>
    </>
  )
}

function Error({
  location: {
    state: {error},
  },
}) {
  return (
    <>
      <div>Oh no. There was an error.</div>
      <pre>{error.message}</pre>
      <Link to="/">Go Home</Link>
      <Link to="/confirm">Try again</Link>
    </>
  )
}

export default function App() {
  const [state, setState] = React.useReducer((s, a) => ({...s, ...a}), {
    food: '',
    drink: '',
  })

  function handleConfirmClick(history) {
    submitForm(state).then(
      () => {
        setState({food: '', drink: ''})
        history.push('/success')
      },
      (error) => {
        history.push('/error', {state: {error}})
      },
    )
  }

  return (
    <Router>
      <Switch>
        <Route exact path="/" component={Main} />
        <Route
          path="/page-1"
          render={(props) => (
            <Page1 {...props} state={state} setState={setState} />
          )}
        />
        <Route
          path="/page-2"
          render={(props) => (
            <Page2 {...props} state={state} setState={setState} />
          )}
        />
        <Route
          path="/confirm"
          render={(props) => (
            <Confirm
              {...props}
              state={state}
              onConfirmClick={() => handleConfirmClick(props.history)}
            />
          )}
        />
        <Route path="/success" render={(props) => <Success {...props} />} />
        <Route path="/error" render={(props) => <Error {...props} />} />
      </Switch>
    </Router>
  )
}
4

0 回答 0