skills/rstest/SKILL.md
Rstest patterns for Rspack-native unit testing with Preact. Trigger: When writing tests with @rstest/core, testing-library/preact, or configuring rstest.config.ts.
npx skillsauth add Hyperxq/modular-frontend-architecture rstestInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
@rstest/core — NEVER from vitest or jest@testing-library/preact — NEVER @testing-library/react"jsxImportSource": "preact" in tsconfig — NEVER Reactcleanup() is called automatically via setup file — but know it must be in afterEachvi is the mock API (same as vitest) — imported from @rstest/coreglobals: true in config means describe/it/expect are available globally, but prefer explicit imports// rstest.config.ts
import { pluginPreact } from "@rsbuild/plugin-preact";
import { pluginSass } from "@rsbuild/plugin-sass";
import { defineConfig } from "@rstest/core";
export default defineConfig({
globals: true,
testEnvironment: "jsdom",
setupFiles: ["./rstest.setup.ts"],
plugins: [pluginPreact(), pluginSass()],
exclude: ["node_modules", "automation_test/**"],
});
// rstest.setup.ts
import { afterEach, expect } from "@rstest/core";
import * as jestDomMatchers from "@testing-library/jest-dom/matchers";
import { cleanup } from "@testing-library/preact";
afterEach(() => cleanup());
expect.extend(jestDomMatchers);
import { describe, it, expect, beforeEach } from "@rstest/core";
import { render, screen, fireEvent } from "@testing-library/preact";
import { Counter } from "./Counter";
describe("Counter", () => {
it("renders initial count", () => {
render(<Counter initialCount={0} />);
expect(screen.getByText("Count: 0")).toBeInTheDocument();
});
it("increments on button click", () => {
render(<Counter initialCount={0} />);
fireEvent.click(screen.getByRole("button", { name: /increment/i }));
expect(screen.getByText("Count: 1")).toBeInTheDocument();
});
});
import { describe, it, expect } from "@rstest/core";
import { render, screen, waitFor } from "@testing-library/preact";
import { UserProfile } from "./UserProfile";
describe("UserProfile", () => {
it("loads user data asynchronously", async () => {
render(<UserProfile userId="1" />);
// findBy* = getBy* + waitFor (auto-retries)
const name = await screen.findByText("John Doe");
expect(name).toBeInTheDocument();
});
it("shows error state", async () => {
render(<UserProfile userId="invalid" />);
await waitFor(() => {
expect(screen.getByRole("alert")).toBeInTheDocument();
});
});
});
import { describe, it, expect, vi, beforeEach } from "@rstest/core";
// Module mock
vi.mock("../services/api", () => ({
fetchUser: vi.fn().mockResolvedValue({ id: "1", name: "John" }),
}));
// Spy on method
import * as api from "../services/api";
describe("mocking", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("calls fetchUser with correct id", async () => {
const spy = vi.spyOn(api, "fetchUser").mockResolvedValue({ id: "1", name: "Jane" });
await api.fetchUser("1");
expect(spy).toHaveBeenCalledWith("1");
expect(spy).toHaveBeenCalledTimes(1);
});
it("uses vi.fn for callbacks", () => {
const onChange = vi.fn();
render(<Input onChange={onChange} />);
fireEvent.input(screen.getByRole("textbox"), { target: { value: "hello" } });
expect(onChange).toHaveBeenCalledWith("hello");
});
});
bun run test # run all tests
bun run test --watch # watch mode
bun run test --coverage # coverage report
rstest, testing, preact, unit test, jsdom, testing-library, @rstest/core, vitest-compatible, component test, mocking, vi.mock, vi.fn, vi.spyOn, waitFor, findBy, fireEvent, screen
tools
Rspack bundler patterns for Rsbuild/Rslib config customization. Trigger: When customizing rspack config via tools.rspack, adding plugins, aliases, or Module Federation setup.
tools
Rslib library build tool patterns for Rspack-based component libraries. Trigger: When configuring rslib.config.ts, library builds, Module Federation remotes, or dynamic entry discovery.
development
Preact 10 patterns with React-compat and Module Federation singleton setup. Trigger: When writing Preact components, hooks, types, or configuring Preact in Rsbuild/Rslib/Rstest.
tools
# Skill: playwright (project-local) Extends the global Playwright skill with project-specific setup, browser install, and MF dev server orchestration for this monorepo. --- ## Browser Installation in AI Agents (OpenCode / Claude) The MCP Playwright server looks for `chrome` at `/opt/google/chrome/chrome` by default. That binary is **not available** in this environment. ### Fix 1 — Configure MCP to use chromium (preferred, one-time) In `~/.config/opencode/opencode.json`, add `--browser chro