Jest.js 在@headlessui/react中,如何模拟Dialog和Transition组件的动画?

qrjkbowd  于 2023-08-01  发布在  Jest
关注(0)|答案(1)|浏览(111)

在我的Jest测试中,我想模拟@headlessui/react的TransitionDialog组件的动画,以加快测试速度。我目前只是求助于await screen.findBy的来等待动画结束,但是随着我添加更多的测试,测试套件运行时正在成为一个问题。
这是我迄今为止尝试过的mock:

jest.mock("@headlessui/react", () => ({
  Transition: ({ children, show }) => <>{show ? children : ""}</>,
}));

字符串
这适用于更简单的组件,例如:

// SimpleTransition.js
function SimpleTransition() {
  const [isShowing, setIsShowing] = useState(false);

  return (
    <div>
      <button onClick={() => setIsShowing((isShowing) => !isShowing)}>
        Toggle
      </button>
      <Transition
        show={isShowing}
        enter="transition-opacity duration-75"
        enterFrom="opacity-0"
        enterTo="opacity-100"
        leave="transition-opacity duration-150"
        leaveFrom="opacity-100"
        leaveTo="opacity-0"
      >
        I will fade in and out
      </Transition>
    </div>
  );
}


但是如果我在Modal.js中尝试它,它会抛出ff错误:

TypeError: Cannot read properties of undefined (reading 'Overlay') 
      ​​​​​at MyModal ​​​​​​src/Modal.js:15:2​
      ​​​​​at Object.<anonymous> ​​​​​​src/Modal.test.js:35:2


Modal.js同时使用DialogTransition组件:

// Modal.js
import { Dialog, Transition } from "@headlessui/react";
import { Fragment, useState } from "react";

export function MyModal() {
  let [isOpen, setIsOpen] = useState(false);

  function closeModal() {
    setIsOpen(false);
  }

  function openModal() {
    setIsOpen(true);
  }

  return (
    <>
      <div className="fixed inset-0 flex items-center justify-center">
        <button
          type="button"
          onClick={openModal}
          className="px-4 py-2 text-sm font-medium text-white bg-black rounded-md bg-opacity-20 hover:bg-opacity-30 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
        >
          Open dialog
        </button>
      </div>

      <Transition appear show={isOpen} as={Fragment}>
        <Dialog
          as="div"
          className="fixed inset-0 z-10 overflow-y-auto"
          onClose={closeModal}
        >
          <div className="min-h-screen px-4 text-center">
            <Transition.Child
              as={Fragment}
              enter="ease-out duration-300"
              enterFrom="opacity-0"
              enterTo="opacity-100"
              leave="ease-in duration-200"
              leaveFrom="opacity-100"
              leaveTo="opacity-0"
            >
              <Dialog.Overlay className="fixed inset-0" />
            </Transition.Child>

            {/* This element is to trick the browser into centering the modal contents. */}
            <span
              className="inline-block h-screen align-middle"
              aria-hidden="true"
            >
              &#8203;
            </span>
            <Transition.Child
              as={Fragment}
              enter="ease-out duration-300"
              enterFrom="opacity-0 scale-95"
              enterTo="opacity-100 scale-100"
              leave="ease-in duration-200"
              leaveFrom="opacity-100 scale-100"
              leaveTo="opacity-0 scale-95"
            >
              <div className="inline-block w-full max-w-md p-6 my-8 overflow-hidden text-left align-middle transition-all transform bg-white shadow-xl rounded-2xl">
                <Dialog.Title
                  as="h3"
                  className="text-lg font-medium leading-6 text-gray-900"
                >
                  Payment successful
                </Dialog.Title>
                <div className="mt-2">
                  <p className="text-sm text-gray-500">
                    Your payment has been successfully submitted. We’ve sent you
                    an email with all of the details of your order.
                  </p>
                </div>

                <div className="mt-4">
                  <button
                    type="button"
                    className="inline-flex justify-center px-4 py-2 text-sm font-medium text-blue-900 bg-blue-100 border border-transparent rounded-md hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-blue-500"
                    onClick={closeModal}
                  >
                    cancel
                  </button>
                </div>
              </div>
            </Transition.Child>
          </div>
        </Dialog>
      </Transition>
    </>
  );
}


测试如下:

//Modal.test.js
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom/extend-expect";
import { MyModal } from "./Modal";

// Mock IntersectionObserver
class IntersectionObserver {
  observe = jest.fn();
  disconnect = jest.fn();
  unobserve = jest.fn();
}

Object.defineProperty(window, "IntersectionObserver", {
  writable: true,
  configurable: true,
  value: IntersectionObserver,
});

Object.defineProperty(global, "IntersectionObserver", {
  writable: true,
  configurable: true,
  value: IntersectionObserver,
});

// This is what's causing the issue
jest.mock("@headlessui/react", () => ({
  Transition: ({ children, show }) => <>{show ? children : ""}</>,
}));

test("Modal Test", () => {
  render(<MyModal />);
  userEvent.click(screen.getByRole("button", { name: /open dialog/i }));
  expect(screen.getByRole("dialog")).toBeInTheDocument();
  userEvent.click(screen.getByRole("button", { name: /cancel/i }));
  expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});


我试着搜索他们的文档和存储库,但似乎没有任何文档。

bihw5rsg

bihw5rsg1#

希望这可以帮助那些正在寻找类似问题的人:
第一个月

export const TransitionRoot = ({
  children,
  className,
  show = true,
}: {
  children: React.ReactNode;
  className: string;
  show?: boolean;
}) => (show ? <div className={className}>{children}</div> : null);

export const createReturnChildren = () => ({
  className,
  children,
}: {
  className: string;
  children: React.ReactNode;
}) => <div className={className}>{children}</div>;

字符串
setupTests.tsx

import { createReturnChildren, TransitionRoot } from './setupFile';

vi.mock('@headlessui/react', async () => {
  const original = (await vi.importActual('@headlessui/react')) as object;

  return {
    ...original,
    Dialog: Object.assign(createReturnChildren(), {
      Panel: createReturnChildren(),
    }),
    Transition: Object.assign(TransitionRoot, {
      Child: createReturnChildren(),
      Root: TransitionRoot,
    }),
  };
});

相关问题