| Compatible Runners |
Not tied to any runner |
|---|---|
| Linters |
N/A |
| Commands |
|
| Related presets |
Crafty provides a preset that will run Vitest once
crafty test is executed.
It adds safe defaults to be able to run your tests with your configuration, owns the generated Vitest config used by Crafty and your IDE, and provides an extension hook that allows you and other presets to extend its configuration.
Vitest is a Node-based runner. Unless you set another environment, the tests run in a Node environment and not in a real browser.
If you need another environment, you can override it in the vitest(...)
hook.
We recommend that you use a separate tool for browser end-to-end tests if you need them.
Table of Contents
File name Conventions
Vitest will look for test files with any of the following popular naming conventions:
- Files with supported extensions in
__tests__folders. - Files with
.test.<extension>suffix. - Files with
.spec.<extension>suffix.
By default, Crafty supports js, mjs, and cjs test files. json stays
resolvable, but does not take part in test discovery.
The .test.<extension> / .spec.<extension> files (or the __tests__
folders) can be located at any depth under the project root.
We recommend to put the test files (or __tests__ folders) next to the code
they are testing so that relative imports appear shorter. For example, if
App.test.js and App.js are in the same folder, the test needs to
import App from "./App" instead of a long relative path.
Using
crafty-preset-babelorcrafty-preset-swcwill addjsxas a supported test file extension. Usingcrafty-preset-typescriptwill add the TypeScript extensionsts,tsx,mts, andcts.
crafty test
Running crafty test will run all tests and exit. But you can use any option
provided by Vitest itself.
For example, crafty test --watch will run your tests in watch mode. This mode
will run all your tests once and then wait for code or test changes to rerun
the concerned tests.
Crafty supports one active test runner. If both Jest and Vitest are
configured, crafty test fails with a clear error.
Crafty-specific CLI options
The Vitest preset also provides extra options:
--moduleDirectories <dir[,dir...]>: adds directories to module lookup beyondnode_modules.--moduleFileExtensions <ext[,ext...]>: adds file extensions to module resolution and test discovery.--reporters <name[,name...]>: configures Vitest reporters. The special valuessonarandvitest-sonar-reporterare normalized to Crafty’s Sonar reporter defaults. Crafty resolves the reporter from the preset, writescoverage/sonar-report.xmlby default, and keeps relative file paths unless you setreportedFilePath: "absolute".
You can pass a comma-separated list or repeat the option.
Writing Tests
To create tests, add it() (or test()) blocks with the name of the test and
its code. You may optionally wrap them in describe() blocks for logical
grouping but this is neither required nor recommended.
Crafty enables Vitest’s test() and expect() globals for you. A basic test
could look like this:
import sum from "./sum";
test("sums numbers", () => {
expect(sum(1, 2)).toEqual(3);
expect(sum(2, 2)).toEqual(4);
});
All expect() matchers supported by Vitest are documented on the
official website.
Focusing and Excluding Tests
Use test.skip() or it.skip() to temporarily exclude a test from being
executed. Vitest also provides focused test helpers and CLI filters when you
want to run a single test in isolation.
Coverage Reporting
crafty-preset-vitest ships with V8 coverage support out of the box.
Tests run slower with coverage enabled, so we recommend running it separately from your normal watch workflow.
Run crafty test --coverage to include a coverage
report. Crafty keeps Vitest coverage output under coverage/ and includes
coverage/lcov.info by default so SonarQube and similar tools can ingest it.
Unless you override them, Crafty normalizes coverage to use provider v8 and
reporter lcov.
If you need to customize Crafty’s coverage settings, assign
options.test.coverage through the vitest(crafty, options, context) hook.
module.exports = {
vitest(crafty, options) {
options.test.coverage = {
reportsDirectory: "./reports/coverage",
reporter: ["lcov"],
};
},
};
Snapshot Testing
Vitest supports snapshot testing for values, rendered components, and more. Read more about snapshot testing.
Vitest configuration ownership
Crafty owns the Vitest configuration it runs.
crafty testcomputes the Vitest config from Crafty state and ignores officialvitest.config.*files.crafty idewrites the IDE-facingvitest.config.mjsfile and removes alternative filenames such asvitest.config.js,vitest.config.cjs,vitest.config.mts,vitest.config.ts, andvitest.config.cts.
If you need to change Vitest behavior, use the vitest(crafty, options, context) hook rather than maintaining a separate vitest.config.* file.
Extending the configuration
Each preset and crafty.config.js can define the
vitest(crafty, options, context) function to override Vitest’s configuration.
const path = require("node:path");
const MODULES = path.join(__dirname, "..", "node_modules");
module.exports = {
/**
* Represents the extension point for Vitest configuration
* @param {Crafty} crafty - The instance of Crafty.
* @param {Object} options - The Vitest configuration object
* @param {Object} context - Crafty helpers for resolution and runtime plugins
*/
vitest(crafty, options, context) {
context.moduleDirectories.push(MODULES);
context.moduleFileExtensions.push("ts");
options.test.setupFiles = options.test.setupFiles || [];
options.test.setupFiles.push(require.resolve("./testSetup"));
options.test.environment = "jsdom";
},
};
Because Crafty computes the configuration before starting Vitest, options
must stay serializable.
Reporter callbacks such as vitest-sonar-reporter’s onWritePath option are
not supported through this hook. For Sonar path control, use
reportedFilePath: "absolute" or reportedFilePath: "relative" on the Sonar
reporter config.
Use context.moduleDirectories and context.moduleFileExtensions to extend
module resolution and test discovery.
Migrating an existing vitest.config.*
When you migrate from a raw Vitest configuration, move repo-specific Vitest
settings into the vitest(crafty, options, context) hook.
Common fields that still need to be re-expressed manually are:
resolve.aliastest.includetest.excludetest.testTimeouttest.hookTimeouttest.pooltest.reporters
Crafty always adds its own default discovery globs for supported extensions.
If your repository contains fixtures, sample apps, or embedded workspaces, use
options.test.exclude to narrow discovery as needed. A common pattern is to
exclude paths such as fixtures/**, examples/**, or samples/**.
If you replace options.test.reporters, you own the full reporter list for
that package. Add sonar explicitly when you still want the default Sonar XML
report alongside a custom reporter such as verbose.
The full list of available configuration options is available on the official website.
Runtime Vite plugins
If you need a Vite plugin at runtime, register it through
context.runtimePlugins instead of attaching a plugin object directly to
options.plugins.
module.exports = {
vitest(crafty, options, context) {
context.runtimePlugins.push({
pluginPath: require.resolve("./vitest-plugin"),
options: {
mode: "test",
},
});
},
};
The module at pluginPath must export a function that receives options and
returns a Vite plugin. If you attach non-serializable values directly to
options, Crafty rejects them with a clear error.
crafty ide
Running crafty ide generates a package-local vitest.config.mjs file for
IDE discovery.
In a monorepo, run crafty ide from each package directory that has its own
crafty.config.js and uses @swissquote/crafty-preset-vitest.
Crafty does not modify .vscode/settings.json for Vitest. The generated
vitest.config.mjs is intended to be discovered by the official Vitest IDE
integration.
Read more about Vitest IDE integration.
SonarQube Integration
Most of the time, at Swissquote, we use SonarQube to check our code quality. More often than not, we add a reporter to create a SonarQube test report.
crafty-preset-vitest comes out of the box with a sonar report that is written
to coverage/sonar-report.xml.
Consumers do not need to add vitest-sonar-reporter directly unless they
import it themselves. Crafty resolves the reporter from the preset.
This report is automatically added when no reporters are configured after the
Crafty hooks have run. The sonar alias, and the raw
vitest-sonar-reporter name, are normalized to that default output path with
relative file paths by default. This keeps the XML portable across local runs
and multi-platform CI.
When coverage is enabled through crafty test --coverage, Crafty also keeps
Vitest coverage artifacts under coverage/ and includes coverage/lcov.info
by default.
If your Sonar setup requires absolute file paths, set
reportedFilePath: "absolute" on the Sonar reporter config.
You can decide to change this configuration by overriding
options.test.reporters
module.exports = {
/**
* Represents the extension point for Vitest configuration
* @param {Crafty} crafty - The instance of Crafty.
* @param {Object} options - The Vitest configuration object
*/
vitest(crafty, options) {
options.test.reporters = [
"verbose",
["sonar", { reportedFilePath: "absolute" }],
];
},
};