Testing

Styles are resolved by the compiler, so a runner sees them only if it runs the same transform the build does. A Vite-based runner already does; a runner that does not cannot be made to, because @plumeria/core publishes types and no runtime for it to fall back on.

Vitest

Vitest reads your Vite plugins, so the setup is the config you already have:

vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import plumeria from '@plumeria/unplugin';

export default defineConfig({
  plugins: [react(), plumeria.vite()],
  test: {
    environment: 'jsdom',
  },
});

The component is compiled exactly as it is for production, and the styling prop is gone by the time it reaches the DOM:

<h1 class="xvdv6o3r xggb8uiu xvazecna">Vite + React</h1>

What jsdom does not give you

The class names are real, but no stylesheet is loaded into the document — document.styleSheets is empty, and getComputedStyle returns initial values for every property the styles set. A test can see that the right classes were attached; it cannot see what they do.

Anything that turns on the cascade — specificity between two styles, a @media branch, a marker and its extended styles — needs a real browser. Run those under a component testing tool that builds with Vite, such as Playwright or Cypress, where the emitted stylesheet is actually applied.

Never assert a generated class name

expect(heading.className).toBe('xvdv6o3r xggb8uiu xvazecna'); // don't

An atomic class name is one property–value pair, hashed. Add a property to the style and every test that spelled the old list out fails, having found nothing wrong with the component. That assertion belongs to the compiler, which covers it already.

Assert what the component does instead — the text it renders, the state it holds, the branch it took.

Types

Because the package is types-only, the API surface can be tested with nothing but tsc. Write the calls you expect to hold, mark the ones you expect to fail, and type-check the file:

styles.test-d.ts
import * as css from '@plumeria/core';

const styles = css.create({
  text: { fontSize: '12px', color: 'red' },
  variant: (size: number) => ({ width: `${size}px` }),
});

export const atomic: string = styles.text.color;
export const fromVariant: string = styles.variant(8).width;

// @ts-expect-error atomic class names are branded per property
export const crossed: css.AtomicClassNameFor<'color', 'red'> = styles.text.fontSize;

// @ts-expect-error the key was never defined
export const missing: string = styles.nope.color;
Terminal
tsc --noEmit

An @ts-expect-error that stops being an error is itself reported, as TS2578: Unused '@ts-expect-error' directive, so the file fails when a guarantee quietly disappears.

The work is already divided by the time a test runs. TypeScript offers the property names and values as you write them, and the lint rules check what you wrote — spelling, values, pseudos, selectors, order-dependent overlap. What neither of them states is the type that comes back out, and that is what this file pins: the branding that keeps one property's class name from being passed off as another's, and keys that do not exist.

Jest

Jest cannot run these tests. Resolution fails before any style is reached:

Cannot find module '@plumeria/core' from 'style.ts'

ts-jest does not change this. The problem is not TypeScript — the types resolve — it is that the emitted require has no runtime to find, and that css.create is never rewritten into class names because Jest does not run the transform.

Use Vitest for component tests, and tsc for the types.

On this page