Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
@gradio/tootils
Unit testing utilities for Gradio Svelte components. Built on top of @testing-library/dom and vitest.
render
Mounts a Gradio component into the DOM with all required shared props (dispatcher, i18n, theme, etc.) pre-configured. Returns query helpers, event utilities, and lifecycle controls.
import { render } from "@self/tootils";
import MyComponent from "./Index.svelte";
const result = await render(MyComponent, {
value: "hello",
label: "My input"
});
Signature
function render(
Component,
props?,
options?: { container?: HTMLElement }
): Promise<RenderResult>
Component
The Svelte component to mount. Accepts either a component constructor directly or a module with a default export.
props
Component props, excluding gradio and loading_status (which are provided automatically). You can override loading_status if needed:
await render(MyComponent, {
value: "hello",
loading_status: { status: "pending", /* ... */ }
});
Props listed in allowed_shared_props (from @gradio/utils) are separated out and passed via shared_props. All other props are passed as component-level props.
options.container
The parent DOM element to mount into. Defaults to document.body.
Return value
render returns a promise that resolves to an object combining @testing-library/dom query helpers with Gradio-specific utilities:
DOM queries
All @testing-library/dom query functions are bound to the container and available directly on the result:
const { getByText, getByLabelText, queryByRole } = await render(MyComponent, {
label: "Name"
});
const input = getByLabelText("Name");
See the @testing-library/dom docs for the full list.
container
The root DOM element the component was mounted into.
component
The mounted Svelte component instance.
listen
Creates a vi.fn() mock that records all dispatched events for a given event name.
const { listen } = await render(MyComponent, { value: "" });
const change = listen("change");
// interact with the component...
expect(change).toHaveBeenCalledTimes(1);
expect(change).toHaveBeenCalledWith("new value");
Retrospective mode
By default, listen only captures events dispatched after it is called. If a component emits events during mount (before listen can be called), pass { retrospective: true } to replay all buffered events onto the mock:
const { listen } = await render(MyComponent, { value: "hi" });
// "change" may have fired during mount — retrospective replays it
const change = listen("change", { retrospective: true });
expect(change).toHaveBeenCalledWith("hi");
All dispatched events are buffered from the moment the dispatcher is created (before mount), so retrospective mode has access to the complete event history.
set_data
Simulates the Gradio server sending new data to the component (as if via a backend update). Waits for two Svelte ticks to allow all reactive updates and side-effect events to settle before returning:
const { set_data, listen } = await render(MyComponent, { value: "" });
const change = listen("change");
await set_data({ value: "updated" });
expect(change).toHaveBeenCalledWith("updated");
get_data
Retrieves the component's current data as reported by its internal get_data handler:
const { get_data } = await render(MyComponent, { value: "hello" });
const data = await get_data();
expect(data.value).toBe("hello");
debug
Prints a pretty-formatted DOM tree of the container (or a specific element) to the console. Useful for debugging test failures:
const result = await render(MyComponent, { value: "hello" });
result.debug(); // prints full container
result.debug(someElement); // prints specific element
unmount
Removes the component from the DOM:
const { unmount } = await render(MyComponent, { value: "hello" });
// ...assertions...
unmount();
cleanup
Unmounts all components mounted via render and removes their DOM nodes. Call this in an afterEach hook to prevent test pollution:
import { cleanup } from "@self/tootils";
afterEach(() => {
cleanup();
});
fireEvent
An async wrapper around @testing-library/dom's fireEvent. Each event method waits for two Svelte ticks after firing, ensuring reactive state updates and any resulting event emissions have settled before your assertions run:
import { render, fireEvent } from "@self/tootils";
const { getByRole } = await render(MyComponent, { value: "" });
const input = getByRole("textbox");
await fireEvent.input(input, { target: { value: "hello" } });
await fireEvent.blur(input);
// state has settled — safe to assert
All standard DOM event methods are available (click, input, change, focus, blur, keyDown, etc.).
download_file
Clicks an element and captures the resulting file download. This triggers a real browser download via Playwright's download event API — the file is actually downloaded and its content is readable.
Works with both download patterns used in Gradio components:
- Static
<a download href="...">links (DownloadLink, FilePreview, etc.) - Programmatic downloads that create an anchor, set
.href/.download, and call.click()(DownloadButton, Gallery, Code, etc.)
import { render, download_file } from "@self/tootils/render";
const { container } = await render(FileComponent, {
value: { url: "/files/data.csv", orig_name: "data.csv" }
});
const { suggested_filename, content } = await download_file("a[download]");
expect(suggested_filename).toBe("data.csv");
expect(content).toContain("col1,col2");
Signature
function download_file(
selector: string,
options?: { timeout?: number }
): Promise<{ suggested_filename: string; content: string | null }>
selector
A CSS selector for the element to click. The click triggers the download.
options.timeout
How long to wait for the download event (default 5000ms). If no download is triggered within this window, the promise rejects.
Return value
suggested_filename— the filename the browser would save the file as (from thedownloadattribute orContent-Dispositionheader)content— the text content of the downloaded file, ornullif the file couldn't be read
How it works
This utility uses a Vitest browser command that runs server-side with access to the Playwright Page object. It sets up page.waitForEvent("download") before clicking the element, so the download event is never missed regardless of timing. The downloaded file is saved to a temp path by Playwright and its content is read back.
upload_file
Sets files on an <input type="file"> element using real file fixtures. Triggers the browser's native change event.
import { render, upload_file, mock_client, TEST_JPG } from "@self/tootils/render";
const { listen } = await render(ImageUpload, { interactive: true, client: mock_client() });
const upload = listen("upload");
await upload_file(TEST_JPG);
await vi.waitFor(() => expect(upload).toHaveBeenCalled());
Signature
function upload_file(
files: FileData | FileData[],
selector?: string // default: 'input[type="file"]'
): Promise<void>
drop_file
Simulates dragging and dropping files onto an element. Reads fixture files from disk, constructs a real DataTransfer with File objects, and dispatches dragenter, dragover, and drop events on the target.
import { render, drop_file, mock_client, TEST_JPG } from "@self/tootils/render";
const { listen } = await render(ImageUpload, { interactive: true, client: mock_client() });
const upload = listen("upload");
await drop_file(TEST_JPG, "[aria-label='Click to upload or drop files']");
await vi.waitFor(() => expect(upload).toHaveBeenCalled());
Signature
function drop_file(
files: FileData | FileData[],
selector: string
): Promise<void>
mock_client
Creates a mock client suitable for components that use file uploads. The upload mock echoes back the input FileData unchanged, and the stream mock returns a no-op event source.
import { render, mock_client } from "@self/tootils/render";
await render(FileComponent, {
interactive: true,
root: "http://localhost:7860",
client: mock_client()
});
Test fixtures
Pre-built FileData instances pointing to existing test files in test/test_files/. Use these as value props or with upload_file/drop_file:
| Export | File | MIME type |
|---|---|---|
TEST_TXT |
alphabet.txt |
text/plain |
TEST_JPG |
cheetah1.jpg |
image/jpeg |
TEST_PNG |
bus.png |
image/png |
TEST_MP4 |
video_sample.mp4 |
video/mp4 |
TEST_WAV |
audio_sample.wav |
audio/wav |
TEST_PDF |
sample_file.pdf |
application/pdf |
import { render, TEST_PNG } from "@self/tootils/render";
// As a component value
await render(ImageComponent, { value: TEST_PNG });
// As an upload fixture
await upload_file(TEST_PNG);
Each fixture has path, url, orig_name, size, and mime_type set. The URLs are served by the Vite dev server during tests.
Re-exports
Everything from @testing-library/dom is re-exported, so you can import query utilities, screen, within, etc. directly:
import { screen, within } from "@self/tootils";