Agent skill
solid-testing
SolidJS testing patterns: use Vitest with @solidjs/testing-library, createRoot for cleanup, render components with function syntax, test user interactions and async behavior.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/solid-testing
Metadata
Additional technical details for this skill
- globs
-
[ "**/*.test.*", "**/*.spec.*", "**/__tests__/**/*" ]
SKILL.md
SolidJS Testing Patterns
Setup
Packages
Install testing dependencies:
npm install -D vitest jsdom @solidjs/testing-library @testing-library/user-event @testing-library/jest-dom
Configuration
package.json:
{
"scripts": {
"test": "vitest"
}
}
vitest.config.ts (SolidStart):
import solid from "vite-plugin-solid";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [solid()],
resolve: {
conditions: ["development", "browser"],
},
});
tsconfig.json:
{
"compilerOptions": {
"types": ["vite/client", "@testing-library/jest-dom"]
}
}
Basic Component Testing
Render Component
render from @solidjs/testing-library requires a function that returns JSX:
import { render } from "@solidjs/testing-library";
// ✅ Correct - function syntax
const { getByRole } = render(() => <Counter />);
// ❌ Wrong - direct JSX
const { getByRole } = render(<Counter />);
Test User Interactions
import { test, expect } from "vitest";
import { render } from "@solidjs/testing-library";
import userEvent from "@testing-library/user-event";
import { Counter } from "./Counter";
const user = userEvent.setup();
test("increments value", async () => {
const { getByRole } = render(() => <Counter />);
const button = getByRole("button");
expect(button).toHaveTextContent("1");
await user.click(button);
expect(button).toHaveTextContent("2");
});
Queries
Query Types
Prefixes:
getBy*: Synchronous, throws if not found or multiple matchesgetAllBy*: Synchronous, throws if not found, returns arrayqueryBy*: Synchronous, returns null if not foundqueryAllBy*: Synchronous, returns array (may be empty)findBy*: Asynchronous, waits up to 1000ms, throws if not foundfindAllBy*: Asynchronous, waits up to 1000ms, returns array
When to use:
- Default:
getBy* - Multiple elements:
getAllBy* - Lazy-loaded routes/resources:
findBy*(first query) - Testing absence:
queryAllBy*(check for empty array)
Query Suffixes (Priority Order)
- Role - ARIA roles (semantic elements like
<button>) - LabelText - Labels, aria-label, aria-labelledby
- PlaceholderText - Input placeholders
- Text - Text content (even if split)
- DisplayValue - Form element values
- AltText - Image alt text
- Title - HTML title attribute
- TestId -
data-testid(last resort, not accessible)
Testing Patterns
Testing with Context
Use wrapper option to provide context:
import { render } from "@solidjs/testing-library";
const wrapper = (props) => <DataContext value="test" {...props} />;
test("receives data from context", () => {
const { getByText } = render(() => <DataConsumer />, { wrapper });
expect(getByText("test")).toBeInTheDocument();
});
Testing Routes
Use location option for routing tests. First query must be findBy*:
const { findByText } = render(
() => <Route path="/article/:id" component={Article} />,
{ location: "/article/12345" }
);
const heading = await findByText("Article Title");
Testing Portals
Use screen export to query portal content:
import { render, screen } from "@solidjs/testing-library";
test("toast renders in portal", () => {
render(() => <Toast><p>Message</p></Toast>);
const toast = screen.getByRole("log");
expect(toast).toHaveTextContent("Message");
});
Testing Async Behavior
For Suspense/async data, use findBy*:
test("loads async data", async () => {
const { findByText } = render(() => <DataComponent />);
const content = await findByText("Loaded data");
expect(content).toBeInTheDocument();
});
createRoot for Testing
Always use createRoot for proper cleanup in tests:
import { createRoot } from "solid-js";
test("counter works", () => {
createRoot((dispose) => {
const { count, increment } = createCounter();
expect(count()).toBe(0);
increment();
expect(count()).toBe(1);
dispose(); // Clean up
});
});
Testing Primitives
Test primitives outside components with createRoot:
import { createRoot, createSignal } from "solid-js";
test("signal updates", () => {
createRoot((dispose) => {
const [count, setCount] = createSignal(0);
expect(count()).toBe(0);
setCount(5);
expect(count()).toBe(5);
dispose();
});
});
Best Practices
- Always use function syntax with render:
render(() => <Component />) - Use createRoot for cleanup: Ensures proper disposal in tests
- Prioritize accessible queries: Role > LabelText > Text > TestId
- Use findBy for async*: When testing Suspense, routes, or resources
- Use screen for portals: Query portal content with
screenexport - Test user interactions: Use
userEventfor realistic interactions - Use wrapper for context: Provide context via
wrapperoption - Test accessibility: Use role-based queries to ensure accessibility
Didn't find tool you were looking for?