Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cold-nights-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@easypost/easy-ui": patch
---

fix: expose test utilities as published entry point
25 changes: 13 additions & 12 deletions easy-ui-react/src/Tooltip/Tooltip.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,28 +88,29 @@ describe("<Tooltip />", () => {

// Helpers for interacting with tooltip trigger
//
// TODO: Look into why hover() and tab() has to be wrapped in act() since this
// shouldn't be necessary. Perhaps something being done in Aria
// user-event interactions wrap themselves in act(), so they must not be nested
// inside another act() call. Only the synchronous timer flush, which drives the
// tooltip's open/close delay state updates, needs to be wrapped.

export async function hoverOverTooltipTrigger(
user: UserEvent,
el: HTMLElement,
{ runTimers = true } = {},
) {
fireEvent.mouseMove(document.body);
await act(async () => {
await userHover(user, el);
if (runTimers) {
await userHover(user, el);
if (runTimers) {
act(() => {
vi.runAllTimers();
}
});
});
}
}

async function tabToTooltipTrigger(user: UserEvent, { runTimers = true } = {}) {
await act(async () => {
await userTab(user);
if (runTimers) {
await userTab(user);
if (runTimers) {
act(() => {
vi.runAllTimers();
}
});
});
}
}
47 changes: 20 additions & 27 deletions easy-ui-react/src/utilities/test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import { act, render as renderWithTestingLib } from "@testing-library/react";
import { render as renderWithTestingLib } from "@testing-library/react";
import userEvent, { UserEvent } from "@testing-library/user-event";
import { ReactElement } from "react";
import { config as reactTransitionGroupConfig } from "react-transition-group";
import { vi } from "vitest";

declare global {
// eslint-disable-next-line no-var
var jest: object;
// eslint-disable-next-line no-var
var IS_REACT_ACT_ENVIRONMENT: boolean;
}

/**
* Render a react element for testing. Passes in vitest's timers for user-event.
*/
Expand All @@ -28,16 +21,22 @@ export function render(jsx: ReactElement) {
* user-event with `vi.useFakeTimers()`
*
* See https://github.com/testing-library/react-testing-library/issues/1195
*
* Note: relies on the test runner's `beforeAll` global, so it must be called
* from a vitest/jest environment that exposes globals.
*/
export function installJestCompatibleFakeTimers() {
beforeAll(() => {
const _jest = globalThis.jest;
globalThis.jest = {
...globalThis.jest,
const globalWithJest = globalThis as typeof globalThis & {
jest?: Record<string, unknown>;
};
const _jest = globalWithJest.jest;
globalWithJest.jest = {
...globalWithJest.jest,
advanceTimersByTime: vi.advanceTimersByTime.bind(vi),
};
return () => {
globalThis.jest = _jest;
globalWithJest.jest = _jest;
};
});
}
Expand All @@ -63,7 +62,11 @@ export function mockIntersectionObserver() {
}

export function installScrollToMock() {
const originalScrollTo = Element.prototype.scrollTo;
Element.prototype.scrollTo = () => {};
return () => {
Element.prototype.scrollTo = originalScrollTo;
};
}

export function mockMatchMedia({
Expand Down Expand Up @@ -95,37 +98,27 @@ export function silenceConsoleError() {
}

export async function userClick(user: UserEvent, element: Element) {
await act(async () => {
await user.click(element);
});
await user.click(element);
}

export async function userKeyboard(user: UserEvent, text: string) {
await act(async () => {
await user.keyboard(text);
});
await user.keyboard(text);
}

export async function userHover(user: UserEvent, element: Element) {
await act(async () => {
await user.hover(element);
});
await user.hover(element);
}

export async function userTab(user: UserEvent) {
await act(async () => {
await user.tab();
});
await user.tab();
}

export async function userType(
user: UserEvent,
element: Element,
text: string,
) {
await act(async () => {
await user.type(element, text);
});
await user.type(element, text);
}

export async function selectCheckbox(user: UserEvent, el: HTMLElement) {
Expand Down
12 changes: 8 additions & 4 deletions easy-ui-react/vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,20 @@ export default defineConfig({
entry: buildEntryObject([
...glob.sync("src/**/index.ts"),
...glob.sync("src/utilities/*.ts", {
ignore: ["**/test.ts", "**/*.test.ts"],
ignore: ["**/*.test.ts"],
}),
]),
cssFileName: "style",
},
rollupOptions: {
external: (id) =>
["react", "react-dom"].some(
(pkg) => id === pkg || id.startsWith(`${pkg}/`),
),
[
"react",
"react-dom",
"vitest",
"@testing-library/react",
"@testing-library/user-event",
Comment on lines +58 to +60

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want to bundle these, just rely on the consumer having it, if wanting our test utilities.

].some((pkg) => id === pkg || id.startsWith(`${pkg}/`)),
output: [
{
format: "cjs",
Expand Down
5 changes: 5 additions & 0 deletions easy-ui-react/vitest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import {
installScrollToMock,
} from "./src/utilities/test";

declare global {
// eslint-disable-next-line no-var
var IS_REACT_ACT_ENVIRONMENT: boolean;
}

globalThis.IS_REACT_ACT_ENVIRONMENT = true;

installJestCompatibleFakeTimers();
Expand Down
Loading