nuxt-spec
v0.2.0-alpha.4
Published
Test-pack layer for Nuxt Applications
Readme
Nuxt Spec

Nuxt Spec (aka nuxt-spec) is a base layer for Nuxt applications incorporating together a couple of testing libraries and packages and providing some utility functions. I created this project in early 2025 because I was unable to find a convenient "one-dependency" way to start testing my Nuxt apps and I didn't want to repeat the same steps and maintain the same set of dependencies over and over.
While Nuxt itself does have a dedicated module for testing, to remain as versatile as possible, it has to be combined with other packages (which can be different based on your choice). I am trying to overcome this by defining "The Way". This is both the strength and the weakness of this project. You were warned.
The most important client of nuxt-spec is my Nuxt Ignis template starter that adds up even more ready-to-use cool stuff for your future awesome Nuxt websites.
How to use
Aside from being "forked" and used as you seem fit, nuxt-spec is also available as an NPM package that can be referenced as a single-import with all the features incoming.
The nuxt-spec package comes with a built-in CLI tool that can help you:
- setup the dependency in your project
- scaffold the default
vitest.config.ts(see configuration section) - add a few test-related script shorthands into your
package.json(see running tests section) - create demo test files in proposed file structure
To use it, just run the CLI script in your terminal:
| Manager | Command |
|-----------------|---------|
| npm | npx nuxt-spec setup |
| yarn | yarn dlx nuxt-spec setup |
| pnpm | pnpx nuxt-spec setup |
| Bun | bunx nuxt-spec setup |
| Deno | deno run --allow-run npm:npx nuxt-spec setup |
First, the CLI tool will ask you whether you want to do the setup automatically. If you choose yes, it will perform all the steps for you. If you choose no, it will guide you through the manual setup step-by-step (see manual setup section).
Manual setup
If you don't want to use the CLI tool, or you want to understand its flow better, here are the detailed steps:
- Add following dependency into your
package.json:
"nuxt-spec": "0.2.0-alpha.4"- Add following section into your
nuxt.config.ts:
extends: [
'nuxt-spec'
]- Add
.npmrcfile with following content (if you don't have it yet):
shamefully-hoist=true- Add
vitest.config.tsfile with following content (if you don't have it yet):
import { loadVitestConfig } from 'nuxt-spec/config'
export default loadVitestConfig({
// your custom config here
})- (Optional) Add following scripts into your
package.json:
"scripts": {
"test": "vitest run",
"test-u": "vitest run -u",
"test-i": "vitest"
}- (Optional) Setup file structures for tests as follows:
test/
├── browser/
│ └── vitest-browser.test.ts
├── e2e/
│ └── nuxt-e2e.test.ts
│ └── nuxt-visual.test.ts
├── nuxt/
│ └── nuxt-unit.test.ts
└── unit/
└── vitest-unit.test.tsYou can use sample files from the project repository.
Install and execute
Whether you used the CLI tool or did the manual setup, you are ready to install and run the tests.
- Install the dependencies:
npm
npm installyarn
yarn installpnpm
pnpm installbun
bun install- If you're prompted (for the first time when installing to a new machine), install headless browser runtimes:
npm
npx playwright-core installyarn
yarn dlx playwright-core installpnpm
pnpm exec playwright-core installbun
bunx playwright-core install- Start the development server of your awesome Nuxt project:
npm
npm run devyarn
yarn devpnpm
pnpm devbun
bun run devRunning tests
Once installed, Vitest automatically discovers all *.test.ts and *.spec.ts files in project and becomes capable of running them.
You can use those three optional commands package.json file in "scripts" section in order to run tests easilly:
test: vitest run- runs once and endstest-u: vitest run -u- runs once and updates snapshotstest-i: vitest- runs and waits in HMR mode for test file changes
Then you can call in terminal in root of your project:
npm
npm run test # runs once and ends
npm run test-u # runs once and updates snapshots
npm run test-i # runs and waits in HMR modeyarn
yarn test # runs once and ends
yarn test-u # runs once and updates snapshots
yarn test-i # runs and waits in HMR modepnpm
pnpm test # runs once and ends
pnpm test-u # runs once and updates snapshots
pnpm test-i # runs and waits in HMR modebun
bun run test # runs once and ends
bun run test-u # runs once and updates snapshots
bun run test-i # runs and waits in HMR modeOr you can use the vitest command directly with all its parameters. See Vitest CLI documentation for more info.
Overview
Nuxt Spec currently contains:
- vitest v4 as the fundamental testing framework
- @vitest/browser as more advanced browser-native testing runner
- @vitest/ui as graphic UI above the Vitest test runner
- happy-dom as the headless browser runtime
- playwright-core as the headless browser testing framework
- @vue/test-utils for testing Vue stuff
- @nuxt/test-utils for testing Nuxt stuff
Planned future development:
- reason about (not) using Vitest browser mode (or make it optional)
- solution for visual regression testing - (currently there is experimental custom solution)
See CHANGELOG.md for the latest updates and features.
Configuration
By default, nuxt-spec uses Vitest configuration defined in /config/index.mjs. The configuration is based on Nuxt team recommendations and our best judgement.
To add/override your custom config, you can create (or scaffold via CLI tool) a file named vitest.config.ts in the root of your project with the following content:
import { loadVitestConfig } from 'nuxt-spec/config'
export default loadVitestConfig({
// your custom config here
})And pass whatever you want as a parameter object. It will be defu-merged with the defaults (custom config takes precedence). The object is typed to be compatible with both Vite and Vitest configuration options. Used type is derived from the respective .d.ts files of those packages.
NOTE: Based on the Vitest documentation, it is possible to pass in any configuration option valid for Vite. Configuration related directly to Vitest must be passed under the test key, e.g.:
import { loadVitestConfig } from 'nuxt-spec/config'
export default loadVitestConfig({
test: {
// your custom config specific to Vitest here
}
// by the nature of the Vitest config resolution,
// you may also pass ANY OTHER valid Vite configuration options here
})By default, Nuxt Spec built-in configuration establishes 4 projects + one fallback:
unit- for unit tests intest/unit/**- env is set tonodenuxt- for Nuxt-related tests intest/nuxt/**- env is set tonuxte2e- for end-to-end tests intest/e2e/**- env is set tonodebrowser- for browser-mode tests intest/browser/**- env is set tonode(this is effectively an alternative tonuxtrelying on@vitest/browserinstead of@nuxt/test-utils)default- fallback for all other tests intest/**and/ortests/**directories - env is set tonode
Vitest will then expects at least one test defined in either of those directories. Any parts of the test.projects confing may be altered and user-defined values will be logically merged with the defaults. Also you may add new custom projects' definitions to fit your needs. If your project uses significantly different configuration (i.e. your tests reside in completely different path), you can pass false as a second parameter to loadVitestConfig() function to exclude default test.projects values from being injected completely:
import { loadVitestConfig } from 'nuxt-spec/config'
export default loadVitestConfig({
// your custom config here
}, false)Alternatively, if you don't want to use any part of the nuxt-spec default configuration at all, you can override vitest.config.ts file completely and define your own Vitest configuration from scratch.
Utilities
Nuxt Spec offers couple of utility functions that are exported via nuxt-spec/utils subpackage.
You can use them in your test files as follows:
import { compareScreenshot, gotoPage, getDataHtml, getAPIResultHtml, } from 'nuxt-spec/utils'
// accepts instance of NuxtPage (from @nuxt/test-utils)
// takes a screenshot of current viewport and compares it with stored baseline
// if screenshot doesn't exist, it will be created as baseline
// if screenshots don't match, the method will cause Vitest test to fail
// accepts optional object with extra options:
// - `fileName` - name of the screenshot file (default is based on current route)
// - `selector` - CSS selector of the element to capture (default is full page)
// - `targetDir` - directory where the screenshots should be stored (default is `./test/e2e/`)
await compareScreenshot(page) // will produce "index.png" file in `./test/e2e/` directory
await compareScreenshot(page, { fileName: 'homepage.png' }) // will produce "homepage.png"
await compareScreenshot(page, { fileName: 'component.png', selector: '#test' }) // will produce "component.png" only with id="test" element
await compareScreenshot(page, { fileName: 'homepage.png', targetDir: '/screenshots' }) // will produce "homepage.png" in `/screenshots` directory
// navigates to given URL and returns the instance of NuxtPage (from @nuxt/test-utils)
const page: NuxtPage = await gotoPage('url')
// accepts either a URL string or instance of NuxtPage (from @nuxt/test-utils) and a CSS selector
// returns `innerHTML` of the element matching the selector
const html: string = await getDataHtml('/', '#test')
const html: string = await getDataHtml(page, '#test')
// accepts either a URL string or instance of NuxtPage (from @nuxt/test-utils)
// css selector for element that triggers API call when clicked (i.e. button)
// fragment of API endpoint URL that should be called (to test the response)
// css selector for element where the API response should be rendered (i.e. div)
// returns `innerHTML` of the element matching the result selector after the API call
// is made by Playwright runner
const html: string = await getAPIResultHtml('/', '#api-fetch', '/your-api', '#api-result')
const html: string = await getAPIResultHtml(page, '#api-fetch', '/your-api', '#api-result')For detailed description, see utils.d.ts.
Contact
Use GitHub issues to report bugs or suggest improvements. I will be more than happy to address them.
