在测试中为BrowserRouter设置路由

问题描述 投票:0回答:2

我有一个测试用例,我需要从注册页面 (

/signup
) 开始,成功注册后,应用程序应导航到主页 (
/
)。我目前正在使用 MemoryRouter 将初始路由设置为
/signup
,一切正常。见下图:

import React from 'react';
import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router';
import { Route, Routes } from 'react-router-dom';
import { render, screen } from '../../test/test-utils';
import { SignUpPage } from './SignUpPage';

const MockHomePage = () => <div>MockHomePage</div>;

describe('<SignUp />', () => {
  test('navigates to Home page on successful signup', async () => {
    render(
      <MemoryRouter initialEntries={['/signup']}>
        <Routes>
          <Route path="/" element={<MockHomePage />} />
          <Route path="/signup" element={<SignUpPage />} />
        </Routes>
      </MemoryRouter>
    );

    // Enter valid user info and submit form
    userEvent.type(screen.getByLabelText('Full Name'), 'John Smith');
    userEvent.type(screen.getByLabelText('Email'), '[email protected]');
    userEvent.type(screen.getByLabelText('Password'), 'let-me-in');
    userEvent.type(screen.getByLabelText('Confirm Password'), 'let-me-in');
    userEvent.click(screen.getByText('Sign up'));

    // Expect to see the Home page
    expect(await screen.findByText('MockHomePage')).toBeInTheDocument();
  });
});

但是,我想在测试中使用

BrowserRouter
而不是
MemoryRouter
(我知道这是推荐的方式,因为它更接近实际应用程序使用的方式)。不幸的是,
BrowserRouter
没有
initialEntries
属性(我正在使用 React Router v6 beta.7)。有没有办法强制
BrowserRouter
/signup
开始?

我尝试了这样的位置API:

window.location.href = 'http://localhost:3000/signup';

但是我收到 jsdom 错误,指出此 API 未实现:

Error: Not implemented: navigation (except hash changes)
  at module.exports (/Users/naresh/projects/accelerated-news/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)

还有其他办法吗?

使用

react-router-dom
v6.

reactjs react-router react-router-dom
2个回答
6
投票

我相信你需要使用

window.history.pushState()
所以它看起来像这样:

test('navigates to Home page on successful signup', async () => {
  window.history.pushState({}, '', '/signup')

  render(
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<MockHomePage />} />
        <Route path="/signup" element={<SignUpPage />} />
      </Routes>
    </BrowserRouter>
  );

  // etc..
});

0
投票

这是react 18(react router v6+)上的真实代码示例 使用 window.history.pushState(state, title, url) 让我们解决使用错误的问题。

render(<BrowserRouter><App/><BrowserRouter/>)

or

render(<MemoryRouter><App/><MemoryRouter/>)

在App.test.tsx文件中


// App.tsx
import React from 'react';
import { BrowserRouter , Route, Routes } from 'react-router-dom';
import ProductPage from './app/pages/ProductPage';
import LoginPage from './app/pages/LoginPage';

const App: React.FC = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<LoginPage />} />
        <Route path="/products" element={<ProductPage />} />
        <Route path="*" element={<LoginPage />} />
      </Routes>
    </BrowserRouter>
  );
};

export default App;

// App.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import App from '../../App';

describe('Integration Tests for the App', () => {
  beforeEach(() => {
    // ล้าง sessionStorage ก่อนแต่ละการทดสอบ
    sessionStorage.clear();

    // กําหนด Path Url เริ่มต้น คือ /login
    window.history.pushState({}, 'Login Page', '/login');
  });

  // ทดสอบการ login สำเร็จ ควร redirect ไปหน้า products และ sessionStorage isLoggedIn เป็น true
  test('Login success redirects to product page and sets sessionStorage', async () => {
    // ทำการ render หน้า App ขึ้นมา
    render(<App />);

    // เลือก Input ที่มี Placeholder 'Username' , จากนั้นทำการกรอกค่า 'user' ลงไปในช่อง Input
    fireEvent.change(screen.getByPlaceholderText('Username'), { target: { value: 'user' } });

    // เลือก Input ที่มี Placeholder 'Password' , จากนั้นทำการกรอกค่า 'password' ลงไปในช่อง Input
    fireEvent.change(screen.getByPlaceholderText('Password'), { target: { value: 'password' } });

    // คลิกปุ่มที่มีชื่อว่า 'Login'
    fireEvent.click(screen.getByText('Login'));

    // ตรวจสอบผลลัพธ์หลัง login success => sessionStorage.isLoggedIn เป็น true
    expect(sessionStorage.getItem('isLoggedIn')).toBe('true');

    // ตรวจสอบผลลัพธ์หลัง login success => redirect ไปยังหน้า products (ตรวจสอบจากการค้นหา Text 'Product Page' , ถ้าเจอแสดงว่าอยู่หน้า products)
    expect(screen.getByText('Product Page')).toBeInTheDocument();
  });

  // ทดสอบกรณี login ไม่สำเร็จ ควรแสดง alert 'Invalid credentials'
  test('Login fail shows Invalid credentials alert', async () => {
    // ทำการ spyOn 'window.alert'
    const alertMock = jest.spyOn(window, 'alert').mockImplementation();

    // ทำการ render หน้า App ขึ้นมา
    render(<App />);

    // เลือก Input ที่มี Placeholder 'Username' , จากนั้นทำการกรอกค่า 'user' ลงไปในช่อง Input
    fireEvent.change(screen.getByPlaceholderText('Username'), { target: { value: 'wrong' } });

    // เลือก Input ที่มี Placeholder 'Password' , จากนั้นทำการกรอกค่า 'password' ลงไปในช่อง Input
    fireEvent.change(screen.getByPlaceholderText('Password'), { target: { value: 'credentials' } });

    // คลิกปุ่มที่มีชื่อว่า 'Login'
    fireEvent.click(screen.getByText('Login'));

    // ตรวจสอบผลลัพธ์หลัง login fail => alert แสดง Invalid credentials
    expect(alertMock).toHaveBeenCalledWith('Invalid credentials');
  });

  // ทดสอบว่าหลังจาก login สำเร็จแล้ว refresh หน้าจะต้องยังคงอยู่ที่หน้า products
  test('Stays on product page on refresh after login success', async () => {
    // ทำการ render หน้า App ขึ้นมา
    render(<App />);

    // เลือก Input ที่มี Placeholder 'Username' , จากนั้นทำการกรอกค่า 'user' ลงไปในช่อง Input
    fireEvent.change(screen.getByPlaceholderText('Username'), { target: { value: 'user' } });

    // เลือก Input ที่มี Placeholder 'Password' , จากนั้นทำการกรอกค่า 'password' ลงไปในช่อง Input
    fireEvent.change(screen.getByPlaceholderText('Password'), { target: { value: 'password' } });

    // คลิกปุ่มที่มีชื่อว่า 'Login'
    fireEvent.click(screen.getByText('Login'));

    // ตรวจสอบผลลัพธ์หลัง login success => sessionStorage.isLoggedIn เป็น true
    expect(sessionStorage.getItem('isLoggedIn')).toBe('true');

    // ตรวจสอบผลลัพธ์หลัง login success => redirect ไปยังหน้า products (ตรวจสอบจากการค้นหา Text 'Product Page' , ถ้าเจอแสดงว่าอยู่หน้า products)
    expect(screen.getByText('Product Page')).toBeInTheDocument();

    // คลิกปุ่มที่มีชื่อว่า 'Refresh Page' เพื่อทำการ Refresh หน้าจอ
    fireEvent.click(screen.getByText('Refresh Page'));

    // ตรวจสอบผลลัพธ์หลัง login success => refresh หน้าจอ => sessionStorage.isLoggedIn เป็น true
    expect(sessionStorage.getItem('isLoggedIn')).toBe('true');

    // ตรวจสอบผลลัพธ์หลัง login success => refresh หน้าจอ => redirect ไปยังหน้า products (ตรวจสอบจากการค้นหา Text 'Product Page' , ถ้าเจอแสดงว่าอยู่หน้า products)
    expect(screen.getByText('Product Page')).toBeInTheDocument();
  });

  // ทดสอบกรณี logout จะต้อง redirect ไปหน้า login
  test('Logout redirects to login page', async () => {
    // จำลองการ login สำเร็จ (แบบวิธีลัด) , ด้วยการกำหนด sessionStorage 'isLoggedIn' เป็นค่า true
    sessionStorage.setItem('isLoggedIn', 'true');

    // ทำการ render หน้า App ขึ้นมา
    render(<App />);

    // คลิกปุ่มที่มีชื่อว่า 'Logout'
    fireEvent.click(screen.getByText('Logout'));

    // ตรวจสอบผลลัพธ์หลัง logout => sessionStorage.isLoggedIn ว่าเป็น null
    expect(sessionStorage.getItem('isLoggedIn')).toBeNull();

    // ตรวจสอบผลลัพธ์หลัง logout => redirect ไปยังหน้า login (ตรวจสอบจากการค้นหา Text 'Login Page' , ถ้าเจอแสดงว่าอยู่หน้า login)
    expect(screen.getByText('Login Page')).toBeInTheDocument();
  });

  // ทดสอบฟังก์ชันการค้นหา ควรแสดงผลิตภัณฑ์ที่ตรงกับคำค้นหา
  test('Search filters products correctly', async () => {
    // จำลองการ login สำเร็จ (แบบวิธีลัด) , ด้วยการกำหนด sessionStorage 'isLoggedIn' เป็นค่า true
    sessionStorage.setItem('isLoggedIn', 'true');

    // ทำการ render หน้า App ขึ้นมา
    render(<App />);

    // เลือก Input ที่มี Placeholder 'Search products...' , จากนั้นทำการกรอกค่า 'Laptop' ลงไปในช่อง Input
    fireEvent.change(screen.getByPlaceholderText('Search products...'), { target: { value: 'Laptop' } });

    // ตรวจสอบผลลัพธ์หลัง search => จะต้องเจอข้อมูล 'Laptop - Electronics'
    expect(screen.getByText('Laptop - Electronics')).toBeInTheDocument();

    // ตรวจสอบผลลัพธ์หลัง search => จะต้องไม่เจอข้อมูล 'Shirt - Apparel'
    expect(screen.queryByText('Shirt - Apparel')).not.toBeInTheDocument();
  });

  // ทดสอบฟังก์ชันการ 'filter' ควรแสดงผลิตภัณฑ์ที่ตรงกับ 'filter' ที่เลือก
  test('Filter shows correct products', async () => {
    // จำลองการ login สำเร็จ (แบบวิธีลัด) , ด้วยการกำหนด sessionStorage 'isLoggedIn' เป็นค่า true
    sessionStorage.setItem('isLoggedIn', 'true');

    // ทำการ render หน้า App ขึ้นมา
    render(<App />);

    // คลิกปุ่มที่มีชื่อว่า 'Electronics' เพื่อ filter ข้อมูล
    fireEvent.click(screen.getByText('Electronics'));

    // ตรวจสอบผลลัพธ์หลัง filter => จะต้องเจอข้อมูล 'Laptop - Electronics'
    expect(screen.getByText('Laptop - Electronics')).toBeInTheDocument();

    // ตรวจสอบผลลัพธ์หลัง filter => จะต้องไม่เจอข้อมูล 'Shirt - Apparel'
    expect(screen.queryByText('Shirt - Apparel')).not.toBeInTheDocument();
  });

  // ทดสอบการนำทางไปยังหน้า login สำหรับ route ที่ไม่ได้กำหนด (ไม่มีอยู่จริง) หากยังไม่ได้ login
  test('Navigates to login page for undefined route when not logged in', async () => {
    // ทำการ render หน้า App ขึ้นมา
    render(<App />);

    // จำลองการไปยัง route ที่ไม่ได้กำหนด (ไม่มีอยู่จริง)
    window.history.pushState({}, 'Unknown Page', '/unknown');

    // ตรวจสอบผลลัพธ์หลัง redirect ไปยังหน้า login
    expect(screen.getByText('Login Page')).toBeInTheDocument();
  });

  // ทดสอบการ redirect ไปหน้า login เมื่อพยายามเข้าถึงหน้า products โดยตรงโดยไม่มีการ login
  test('Redirects to login page when trying to access product page directly without login', async () => {
    // ทำการ render หน้า App ขึ้นมา
    render(<App />);

    // จำลองการนำทางไปยัง '/products' โดยตรงโดยไม่ได้ login
    window.history.pushState({}, 'Product Page', '/products');

    // ตรวจสอบผลลัพธ์หลัง redirect ไปหน้า login , เมื่อพยายามเข้าถึงหน้า products โดยตรงโดยไม่มีการ login
    expect(screen.getByText('Login Page')).toBeInTheDocument();
  });
});
© www.soinside.com 2019 - 2024. All rights reserved.