Utilities for testing applications in WebContainers
Installation | Configuration | API
Test your applications and packages inside WebContainers.
Add @webcontainer/test
to your development dependencies.
$ npm install --save-dev @webcontainer/test
Vitest is also required as peer dependency.
$ npm install --save-dev vitest @vitest/browser
Add vitestWebcontainers
plugin in your Vitest config and enable browser mode:
import { defineConfig } from "vitest/config";
import { vitestWebcontainers } from "@webcontainer/test/plugin";
export default defineConfig({
plugins: [vitestWebcontainers()],
test: {
browser: {
enabled: true,
},
},
});
Webcontainer utilities are exposed as test fixtures:
import { test } from "@webcontainer/test";
test("run development server inside webcontainer", async ({
webcontainer,
preview,
}) => {
await webcontainer.mount("path/to/project");
await webcontainer.runCommand("npm", ["install"]);
const { exit } = webcontainer.runCommand("npm", ["run", "dev"]);
await preview.getByRole("heading", { level: 1, name: "Hello Vite!" });
await exit();
});
To use test hooks, you can import fixture typings:
import { test, type TestContext } from "@webcontainer/test";
import { beforeEach } from "vitest";
// Mount project before each test
beforeEach<TestContext>(({ webcontainer }) => {
await webcontainer.mount("projects/example");
});
Vitest's getByRole
that's scoped to the preview window.
await preview.getByRole("heading", { level: 1, name: "Hello Vite!" });
Vitest's getByText
that's scoped to the preview window.
await preview.getByText("Hello Vite!");
Vitest's locator
of the preview window.
await preview.locator.hover();
Mount file system inside webcontainer.
Accepts a path that is relative to the project root, or inlined FileSystemTree
.
await webcontainer.mount("/path/to/project");
await webcontainer.mount({
"package.json": { file: { contents: '{ "name": "example-project" }' } },
src: {
directory: {
"index.ts": { file: { contents: "export default 'Hello!';" } },
},
},
});
Run command inside webcontainer.
await webcontainer.runCommand("npm", ["install"]);
Calling await
on the result resolves into the command output:
const files = await webcontainer.runCommand("ls", ["-l"]);
To write into the output stream, use write
method of the non-awaited output.
To verify output of continuous stream, use waitForText()
:
const { write, waitForText, exit } = webcontainer.runCommand("npm", [
"create",
"vite",
]);
await waitForText("What would you like to call your project?");
await write("Example Project\n");
await waitForText("Where should the project be created?");
await write("./example-project\n");
await exit();
To capture each output chunk one-by-one, you can use onData
callback.
This can be useful when debugging output of the stream.
const { isDone, onData } = webcontainer.runCommand("npm", ["run", "build"]);
onData((chunk) => console.log(chunk));
await isDone;
WebContainer's readFile
method.
const content = await webcontainer.readFile("/package.json");
WebContainer's writeFile
method.
await webcontainer.writeFile("/main.ts", "console.log('Hello world!')");
WebContainer's rename
method.
await webcontainer.rename("/before.ts", "/after.ts");
WebContainer's mkdir
method.
await webcontainer.mkdir("/src/components");
WebContainer's readdir
method.
const contents = await webcontainer.readdir("/src");
WebContainer's rm
method.
await webcontainer.rm("/node_modules");
If you have repetitive steps that are needed by multiple test cases, you can improve test performance by using setup
.
It calls the given function once, saves WebContainer state in a snapshot, and restores that snapshot before each test.
import { test, type TestContext } from "@webcontainer/test";
import { beforeEach, expect, onTestFinished } from "vitest";
beforeEach<TestContext>(async ({ webcontainer, setup }) => {
// This is run once and cached for each next run
await setup(async () => {
await webcontainer.mount("./svelte-project");
await webcontainer.runCommand("npm", ["install"]);
});
});
// No need to re-mount file system or re-run install in test cases
test("user can build project", async ({ webcontainer }) => {
await webcontainer.runCommand("npm", ["run", "build"]);
});
test("user can start project", async ({ webcontainer, preview }) => {
void webcontainer.runCommand("npm", ["run", "dev"]);
await preview.getByRole("heading", { name: "Welcome to SvelteKit" });
});