Skip to content

Instantly share code, notes, and snippets.

@chaance
Created March 13, 2020 19:01
Show Gist options
  • Save chaance/5240530a9573c373fd95120449585940 to your computer and use it in GitHub Desktop.
Save chaance/5240530a9573c373fd95120449585940 to your computer and use it in GitHub Desktop.
Debugging the act warning in Jest + React Testing Library
import React from "react";
import { render, fireEvent, act, screen } from "@testing-library/react";
import { axe } from "jest-axe";
import Tooltip, { LEAVE_TIMEOUT, MOUSE_REST_TIMEOUT } from "@reach/tooltip";
// TODO: Whyyyyyy are we getting the `not wrapped in act(...)` warning?
// AFAICT, everything related to rendering and updating is wrapped in act.
// React testing experts, what the heck are we missing here?
describe("<Tooltip />", () => {
beforeEach(() => {
jest.useFakeTimers();
});
it("should not have basic a11y issues", async () => {
jest.useRealTimers();
act(() => {
render(
<div data-testid="tooltip">
<Tooltip label="Content">
<button>Trigger</button>
</Tooltip>
</div>
);
});
let trigger = screen.getByText("Trigger");
let results = await axe(screen.getByTestId("tooltip"));
expect(results).toHaveNoViolations();
act(() => {
jest.useFakeTimers();
fireEvent.mouseOver(trigger);
jest.advanceTimersByTime(MOUSE_REST_TIMEOUT);
jest.useRealTimers();
});
results = await axe(screen.getByTestId("tooltip"));
expect(results).toHaveNoViolations();
act(() => {
jest.useFakeTimers();
fireEvent.mouseLeave(trigger);
jest.advanceTimersByTime(LEAVE_TIMEOUT);
});
});
it("shows/hides on hover", () => {
let tooltipText = "Look at me";
act(() => {
render(
<Tooltip label={tooltipText}>
<button>Trigger</button>
</Tooltip>
);
});
let trigger = screen.getByText("Trigger");
expect(screen.queryByText(tooltipText)).toBeFalsy();
act(() => {
fireEvent.mouseOver(trigger);
jest.advanceTimersByTime(MOUSE_REST_TIMEOUT);
});
expect(screen.queryByText(tooltipText)).toBeTruthy();
act(() => {
fireEvent.mouseLeave(trigger);
jest.advanceTimersByTime(LEAVE_TIMEOUT);
});
expect(screen.queryByText(tooltipText)).toBeFalsy();
});
it("renders as any HTML element", () => {
let tooltipText = "Look at me";
act(() => {
render(
<p>
<Tooltip as="span" style={{ display: "block" }} label={tooltipText}>
<span>Trigger</span>
</Tooltip>
</p>
);
});
let trigger = screen.getByText("Trigger");
act(() => {
fireEvent.mouseOver(trigger);
jest.advanceTimersByTime(MOUSE_REST_TIMEOUT);
});
let tooltip = screen.getByText(tooltipText);
expect(tooltip.tagName).toBe("SPAN");
act(() => {
fireEvent.mouseLeave(trigger);
jest.advanceTimersByTime(LEAVE_TIMEOUT);
});
});
it("shows/hides when trigger is activeElement", () => {
let tooltipText = "Look at me";
act(() => {
render(
<Tooltip label={tooltipText}>
<button>Trigger</button>
</Tooltip>
);
});
let trigger = screen.getByText("Trigger");
expect(screen.queryByText(tooltipText)).toBeFalsy();
act(() => void fireEvent.focus(trigger));
expect(screen.queryByText(tooltipText)).toBeTruthy();
act(() => {
fireEvent.blur(trigger);
jest.advanceTimersByTime(LEAVE_TIMEOUT);
});
expect(screen.queryByText(tooltipText)).toBeFalsy();
});
it("shows without timeout when one tooltip is already visible", () => {
act(() => {
render(
<>
<Tooltip label="First">
<button>First Trigger</button>
</Tooltip>
<Tooltip label="Second">
<button>Second Trigger</button>
</Tooltip>
</>
);
});
let firstTrigger = screen.getByText("First Trigger");
let secondTrigger = screen.getByText("Second Trigger");
act(() => {
fireEvent.mouseOver(firstTrigger);
jest.advanceTimersByTime(MOUSE_REST_TIMEOUT);
});
expect(screen.queryByText("First Trigger")).toBeTruthy();
act(() => {
fireEvent.mouseLeave(firstTrigger);
fireEvent.mouseOver(secondTrigger);
});
expect(screen.queryByText("Second Trigger")).toBeTruthy();
});
it("hides on ESC", () => {
let tooltipText = "Look at me";
act(() => {
render(
<Tooltip label={tooltipText}>
<button>Trigger</button>
</Tooltip>
);
});
let trigger = screen.getByText("Trigger");
act(() => {
fireEvent.focus(trigger);
jest.advanceTimersByTime(MOUSE_REST_TIMEOUT);
});
expect(screen.queryByText(tooltipText)).toBeTruthy();
act(() => void fireEvent.keyDown(trigger, { key: "Escape" }));
expect(screen.queryByText(tooltipText)).toBeFalsy();
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment