Skip to main content
Version: v5 (Preview)

Overview

@stencil/vitest provides testing utilities for Stencil components, powered by Vitest: a render() function, matchers for custom elements, and spies for events, methods, props, and lifecycle hooks.

Paired with @stencil/unplugin, which compiles your components as each test imports them, there's no Stencil build to run before your tests. The same test code runs in:

  • A real browser, through Vitest's browser mode. Components get real layout, styling, and events, so this is the recommended setup.
  • Node, with a simulated DOM (Stencil's mock-doc, jsdom, or happy-dom). Tests start faster and need no browser installed.

Quick Start​

The quickest way to set up is the wizard. Run it from your Stencil project:

stencil add @stencil/vitest

It asks whether your tests should run in a browser or in Node, and how components should be loaded, then installs the dependencies, writes vitest.config.ts, and adds test scripts to your package.json. Choose Plugin (source) when it asks how to load components, for the setup described on this page. The stencil init wizard offers the same steps when you create a project.

The rest of this section sets up browser tests by hand.

1. Install​

npm install --save-dev @stencil/vitest @stencil/unplugin vitest @vitest/browser-playwright playwright

Then download the browser the tests run in:

npx playwright install chromium

2. Create vitest.config.ts​

vitest.config.ts
import { stencilVite } from '@stencil/unplugin';
import { playwright } from '@vitest/browser-playwright';
import { defineConfig } from 'vitest/config';

export default defineConfig({
plugins: [stencilVite()],
test: {
include: ['src/**/*.test.{ts,tsx}'],
browser: {
enabled: true,
provider: playwright(),
headless: true,
instances: [{ browser: 'chromium' }],
},
},
});

stencilVite() reads your stencil.config.ts and compiles each component when a test imports it.

3. Write Tests​

Import the component's source file, then render it. Importing the file registers the custom element, so also import any other components that yours renders:

src/components/my-button/my-button.test.tsx
import { render, describe, it, expect } from '@stencil/vitest';

import './my-button';

describe('my-button', () => {
it('renders its label', async () => {
const { root } = await render(<my-button label="Save" />);
await expect(root).toEqualHtml(`
<my-button class="hydrated">
<mock:shadow-root>
<button>SAVE</button>
</mock:shadow-root>
</my-button>
`);
});

it('emits myPress when clicked', async () => {
const { root, spyOnEvent, waitForChanges } = await render(<my-button label="Save" />);
const spy = spyOnEvent('myPress');

root.shadowRoot?.querySelector('button')?.click();
await waitForChanges();

expect(spy).toHaveReceivedEventTimes(1);
});
});

4. Run Tests​

Add scripts to your package.json:

package.json
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest"
}
}

Next Steps​

Migration from Stencil Test Runner​

If you're coming from the test runner built into Stencil v4, see the Migration Guide. Existing newSpecPage() tests can keep running under Vitest.