Skip to content

Commit

Permalink
feat: expose helper to perform action and wait for update (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanPiercey authored Jul 23, 2021
1 parent e906526 commit 7cd9d12
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 25 deletions.
10 changes: 9 additions & 1 deletion src/__tests__/render.browser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render, fireEvent, screen, cleanup } from "..";
import { render, fireEvent, screen, cleanup, act } from "..";
import Counter from "./fixtures/counter.marko";
import LegacyCounter from "./fixtures/legacy-counter";
import UpdateCounter from "./fixtures/update-counter.marko";
Expand Down Expand Up @@ -122,6 +122,14 @@ test("can render into a different container", async () => {
expect(getByText("Hello World")).toHaveProperty("parentNode", container);
});

test("act waits for pending updates", async () => {
const { getByText } = await render(Counter);
expect(getByText(/Value: 0/)).toBeInTheDocument();

await act(() => getByText("Increment").click());
expect(getByText("Value: 1")).toBeInTheDocument();
});

test("fireEvent waits for pending updates", async () => {
const { getByText } = await render(Counter);
expect(getByText(/Value: 0/)).toBeInTheDocument();
Expand Down
13 changes: 11 additions & 2 deletions src/__tests__/render.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render, screen, fireEvent, cleanup } from "..";
import { render, screen, fireEvent, cleanup, act } from "..";
import Counter from "./fixtures/counter.marko";
import LegacyCounter from "./fixtures/legacy-counter";
import Clickable from "./fixtures/clickable.marko";
Expand Down Expand Up @@ -57,11 +57,20 @@ test("fails when checking emitted events", async () => {
);
});

test("fails when calling act", async () => {
const { getByText } = await render(Counter);
await expect(
act(() => getByText("Increment").click())
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Cannot perform client side interaction tests on the server side. Please use @marko/testing-library in a browser environment."`
);
});

test("fails when emitting events", async () => {
const { getByText } = await render(Counter);
await expect(
fireEvent.click(getByText("Increment"))
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Cannot fire events when testing on the server side. Please use @marko/testing-library in a browser environment."`
`"Cannot perform client side interaction tests on the server side. Please use @marko/testing-library in a browser environment."`
);
});
2 changes: 1 addition & 1 deletion src/index-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface MountedComponent {
}
const mountedComponents = new Set<MountedComponent>();

export { FireFunction, FireObject, fireEvent } from "./shared";
export { FireFunction, FireObject, fireEvent, act } from "./shared";

export type RenderResult = AsyncReturnValue<typeof render>;

Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from "@testing-library/dom";
import { autoCleanupEnabled } from "./shared";

export { FireFunction, FireObject, fireEvent } from "./shared";
export { FireFunction, FireObject, fireEvent, act } from "./shared";

export type RenderResult = AsyncReturnValue<typeof render>;

Expand Down
37 changes: 17 additions & 20 deletions src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,28 @@ export type FireObject = {
) => Promise<ReturnType<originalFireFunction>>;
};

export const fireEvent = (async (...params) => {
failIfNoWindow();
const result = originalFireEvent(...params);
export async function act<T extends (...args: unknown[]) => unknown>(fn: T) {
type Return = ReturnType<T>;
if (typeof window === "undefined") {
throw new Error(
"Cannot perform client side interaction tests on the server side. Please use @marko/testing-library in a browser environment."
);
}

const result = await fn();
await waitForBatchedUpdates();
return result;
}) as FireFunction & FireObject;
return result as T extends (...args: unknown[]) => Promise<unknown>
? AsyncReturnValue<T>
: Return;
}

export const fireEvent = ((...params) =>
act(() => originalFireEvent(...params))) as FireFunction & FireObject;

(Object.keys(originalFireEvent) as EventType[]).forEach(
(eventName: EventType) => {
const fire = originalFireEvent[eventName];
fireEvent[eventName] = async (...params) => {
failIfNoWindow();
const result = fire(...params);

await waitForBatchedUpdates();
return result;
};
fireEvent[eventName] = (...params) => act(() => fire(...params));
}
);

Expand All @@ -74,14 +79,6 @@ export type AsyncReturnValue<
NonNullable<Parameters<ReturnType<AsyncFunction>["then"]>[0]>
>[0];

function failIfNoWindow() {
if (typeof window === "undefined") {
throw new Error(
"Cannot fire events when testing on the server side. Please use @marko/testing-library in a browser environment."
);
}
}

type Callback = (...args: unknown[]) => void;
const tick =
// Re-implements the same scheduler Marko 4/5 is using internally.
Expand Down

0 comments on commit 7cd9d12

Please sign in to comment.