【问题标题】:can't build test to useOnClickOutside custom hook无法构建测试以使用 OnClickOutside 自定义挂钩
【发布时间】:2020-08-09 22:50:02
【问题描述】:

我构建了一个自定义钩子,我想从中构建测试,但我不知道从哪里开始我希望你能帮我解释几件事:

1.我如何在测试中捕获 mousedown 2. 我如何使用 useRef 并将其分配给当前值 如果您能帮助我并向我展示代码,那将非常有帮助,因为我有时会坐在上面

下面是自定义钩子和我实现自定义钩子的代码 先谢谢了???

 import { useEffect } from 'react';

function useOnClickOutside(ref, callback) {
  useEffect(
    () => {
      const listener = (event) => {
        // Do nothing if clicking ref's element or descendent elements
        if (!ref.current || ref.current.contains(event.target)) {
          return;
        }

        callback(event);
      };

      document.addEventListener('mousedown', listener);
      document.addEventListener('touchstart', listener);

      return () => {
        document.removeEventListener('mousedown', listener);
        document.removeEventListener('touchstart', listener);
      };
    },
    [ref, callback],
  );
}

export default useOnClickOutside;

这是使用它的组件:

import React, { useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
import styles from './OverlayDialog.module.css';
import  useOnClickOutside from '../../CustomeHooks/useOnClickOutside/useOnClickOutside';
const OverlayDialog = (props) => {
    const wrapperRef = useRef(null);
    useEffect(() => {
        window.addEventListener("mousedown", handleClickOutside);
        return () => {
            window.removeEventListener("mousedown", handleClickOutside);
        };
    });
    const handleClickOutside = event => {
        const { current: wrap } = wrapperRef;
        if (wrap && !wrap.contains(event.target)) {
            props.onClose(false);
        }
    };

   useOnClickOutside( wrapperRef,()=>props.onClose(false))

    return ReactDOM.createPortal(
        <div className={styles.Dialog}>
            <div className={styles.InnerDialog} tabIndex={1}>
                <div className={styles.DialogCard} tabIndex={-1}>
                    <div className={props.className ? props.className : styles.DialogContent} ref={wrapperRef}>

                        {props.children}
                    </div>
                </div>
            </div>
        </div>,
        document.getElementById('OverlayDialog')
    )
}

export default OverlayDialog;

【问题讨论】:

    标签: reactjs jestjs enzyme react-testing-library react-hooks-testing-library


    【解决方案1】:

    单独测试 useOnClickOutside 的示例

    import { createRef } from 'react';
    import { renderHook } from '@testing-library/react-hooks';
    import { fireEvent, render, screen } from '@testing-library/react';
    
    import useOnClickOutside from './useOnClickOutside';
    
    describe('useOnClickOutside', () => {
      it('calls handler when click is outside element', () => {
        // Arrange
        const handler = jest.fn();
        const ref = createRef<HTMLDivElement>();
        render(<div ref={ref}></div>);
    
        // Act
        renderHook(() => useOnClickOutside(ref, handler));
        fireEvent.click(document);
    
        // Assert
        expect(handler).toBeCalledTimes(1);
      });
    
      it(`doesn't calls handler when click is within element`, () => {
        // Arrange
        const handler = jest.fn();
        const ref = createRef<HTMLDivElement>();
        render(<div ref={ref} data-testid="element-testid"></div>);
    
        // Act
        renderHook(() => useOnClickOutside(ref, handler));
        fireEvent.click(screen.getByTestId('element-testid'));
    
        //  Assert
        expect(handler).not.toBeCalled();
      });
    });
    
    

    【讨论】:

      【解决方案2】:

      我为您的案例找到了不错的博文。有信息如何测试 onClickOutside https://webman.pro/blog/how-to-detect-and-test-click-outside-in-react/ 我查看了您的组件并且很好奇它的实际工作原理)因此,如果您添加代码的实时示例,我可以为您提供更多帮助)

      更新 我为您的组件中的内容添加了测试 ID) 组件:

      import React, { useRef } from "react";
      import ReactDOM from "react-dom";
      import styles from "./OverlayDialog.module.css";
      import useOnClickOutside from "./useOnClickOutside";
      const OverlayDialog = (props) => {
        const wrapperRef = useRef(null);
      
        useOnClickOutside(wrapperRef, () => props.onClose());
      
        return ReactDOM.createPortal(
          <div className={styles.Dialog}>
            <div className={styles.InnerDialog} tabIndex={1}>
              <div className={styles.DialogCard} tabIndex={-1}>
                <div
                  className={props.className ? props.className : styles.DialogContent}
                  ref={wrapperRef}
                  data-testid="content"
                >
                  {props.children}
                </div>
              </div>
            </div>
          </div>,
          document.getElementById("OverlayDialog")
        );
      };
      
      export default OverlayDialog;
      

      测试:

      import React from 'react';
      import { render, screen, fireEvent } from '@testing-library/react';
      import OverlayDialog from "./OverlayDialog";
      
      test('renders learn react link', () => {
        const onClose = jest.fn();
        const modalRoot = document.createElement('div');
        modalRoot.setAttribute('id', 'OverlayDialog');
        const body = document.querySelector('body');
        body.appendChild(modalRoot);
        render(
            <OverlayDialog onClose={onClose}>
              content
            </OverlayDialog>
        );
        /*checking that if we click on content nothing happens*/
        expect(screen.queryByTestId('content')).toBeInTheDocument();
        fireEvent(screen.queryByTestId('content'), new MouseEvent('mousedown', {
          bubbles: true,
          cancelable: true,
        }));
        expect(onClose).not.toBeCalled();
      
        /*checking that if we click outside we'll fire onClose*/
        fireEvent(body, new MouseEvent('mousedown', {
          bubbles: true,
          cancelable: true,
        }));
        expect(onClose).toBeCalled();
      });
      
      

      【讨论】:

      • 我更新了我的答案。我也不熟悉@testing-library。通常我使用 jest/enzyme 没有这样的包装。所以我认为没有import { screen } from '@testing-library/react'; 也可以做到,但我不知道怎么做)
      猜你喜欢
      • 1970-01-01
      • 2021-12-07
      • 2023-01-19
      • 2022-07-15
      • 1970-01-01
      • 2016-04-14
      • 2021-12-26
      • 2021-07-08
      • 2020-04-07
      相关资源
      最近更新 更多