Created
March 13, 2020 19:01
-
-
Save chaance/5240530a9573c373fd95120449585940 to your computer and use it in GitHub Desktop.
Debugging the act warning in Jest + React Testing Library
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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