88 lines
2.7 KiB
TypeScript
88 lines
2.7 KiB
TypeScript
import { fireEvent, render, waitFor } from "@testing-library/react";
|
|
import { afterEach, expect, it, vi } from "vitest";
|
|
import { bindIconAnimation } from "./bind-icon-animation";
|
|
|
|
afterEach(() => vi.restoreAllMocks());
|
|
|
|
it("finishes every delayed part after leaving and does not restart on quick re-entry", async () => {
|
|
vi.spyOn(window, "matchMedia").mockReturnValue({
|
|
matches: true,
|
|
} as MediaQueryList);
|
|
const { container, getByRole } = render(
|
|
<button type="button">
|
|
<svg ref={bindIconAnimation} aria-hidden="true" />
|
|
Category
|
|
</button>,
|
|
);
|
|
const icon = container.querySelector("svg");
|
|
if (!icon) throw new Error("Missing icon");
|
|
const target = getByRole("button");
|
|
let finishFirst!: () => void;
|
|
let finishLast!: () => void;
|
|
const getAnimations = vi.fn(() => [
|
|
{
|
|
finished: new Promise<void>((resolve) => {
|
|
finishFirst = resolve;
|
|
}),
|
|
},
|
|
{
|
|
finished: new Promise<void>((resolve) => {
|
|
finishLast = resolve;
|
|
}),
|
|
},
|
|
]);
|
|
Object.defineProperty(icon, "getAnimations", { value: getAnimations });
|
|
fireEvent.mouseEnter(target);
|
|
fireEvent.mouseLeave(target);
|
|
expect(icon).toHaveAttribute("data-icon-animating");
|
|
finishFirst();
|
|
await Promise.resolve();
|
|
expect(icon).toHaveAttribute("data-icon-animating");
|
|
fireEvent.mouseEnter(target);
|
|
expect(getAnimations).toHaveBeenCalledTimes(1);
|
|
finishLast();
|
|
await Promise.resolve();
|
|
expect(icon).toHaveAttribute("data-icon-animating");
|
|
fireEvent.mouseLeave(target);
|
|
await waitFor(() => expect(icon).not.toHaveAttribute("data-icon-animating"));
|
|
fireEvent.mouseEnter(target);
|
|
expect(getAnimations).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("keeps playing after keyboard blur and releases cancelled animations", async () => {
|
|
const { container, getByRole, unmount } = render(
|
|
<button type="button">
|
|
<svg ref={bindIconAnimation} aria-hidden="true" />
|
|
Category
|
|
</button>,
|
|
);
|
|
const icon = container.querySelector("svg");
|
|
if (!icon) throw new Error("Missing icon");
|
|
const target = getByRole("button");
|
|
// JSDOM does not model the browser keyboard-focus modality.
|
|
vi.spyOn(target, "matches").mockImplementation(
|
|
(selector) => selector === ":focus-visible",
|
|
);
|
|
let cancel!: () => void;
|
|
Object.defineProperty(icon, "getAnimations", {
|
|
value: () => [
|
|
{
|
|
finished: new Promise<void>((_resolve, reject) => {
|
|
cancel = () => reject(new Error("cancelled"));
|
|
}),
|
|
},
|
|
],
|
|
});
|
|
target.focus();
|
|
expect(icon).toHaveAttribute("data-icon-animating");
|
|
target.blur();
|
|
expect(icon).toHaveAttribute("data-icon-animating");
|
|
cancel();
|
|
await waitFor(() => expect(icon).not.toHaveAttribute("data-icon-animating"));
|
|
target.focus();
|
|
unmount();
|
|
cancel();
|
|
await Promise.resolve();
|
|
expect(icon).not.toHaveAttribute("data-icon-animating");
|
|
});
|