Как вы имитируете анимацию компонентов Dialog и Transition в @ headlessui / react?
В своих тестах Jest я хотел бы имитировать анимацию компонентов и @headlessui / response, чтобы ускорить мои тесты. В настоящее время я просто прибегаю к
await screen.findBy
нужно дождаться окончания анимации, но время выполнения набора тестов становится проблемой, поскольку я добавляю больше тестов.
Это макет, который я пробовал до сих пор:
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>
);
}
Но если я попробую, выдается ошибка 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
использует как
Dialog
и
Transition
компоненты:
// 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"
>
​
</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();
});
Я пробовал искать их документы и репозиторий, но, похоже, там нет никаких документов.
1 ответ
Надеюсь, это поможет кому-то, кто ищет подобную проблему:
setupFile.ts
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,
}),
};
});