From harness-claude
Tests Nuxt components, composables, and pages using @nuxt/test-utils with full context and auto-imports. Guides mocking useFetch/useRoute, mountSuspended for async, and Playwright E2E.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Test Nuxt components, composables, and pages using @nuxt/test-utils with full Nuxt context including auto-imports
Provides Vue.js testing best practices using Vitest, Vue Test Utils, and Playwright for E2E. Covers component testing, mocking, async setups, Pinia stores, and common pitfalls.
Provides Vue.js testing best practices using Vitest and Vue Test Utils for unit/component tests, Playwright for E2E. Covers mocking, async setup, Pinia, snapshots, and common gotchas.
Tests Svelte 5 components with Vitest and @testing-library/svelte: rendering, user events, store mocking, async tick flushing.
Share bugs, ideas, or general feedback.
Test Nuxt components, composables, and pages using @nuxt/test-utils with full Nuxt context including auto-imports
useFetch, useRoute, or other Nuxt auto-imports in tests<Suspense> or useAsyncDataSetup:
@nuxt/test-utils and configure Vitest with the Nuxt environment:// vitest.config.ts
import { defineVitestConfig } from '@nuxt/test-utils/config';
export default defineVitestConfig({
test: {
environment: 'nuxt',
environmentOptions: {
nuxt: {
rootDir: '.',
},
},
},
});
Mounting components:
mountSuspended instead of mount for components that use useAsyncData, useFetch, or other async composables. It wraps the component in <Suspense> and waits for resolution:import { mountSuspended } from '@nuxt/test-utils/runtime';
import MyComponent from '~/components/MyComponent.vue';
it('renders user data', async () => {
const wrapper = await mountSuspended(MyComponent, {
props: { userId: '123' },
});
expect(wrapper.text()).toContain('John Doe');
});
renderSuspended for testing with @testing-library/vue patterns:import { renderSuspended } from '@nuxt/test-utils/runtime';
import { screen } from '@testing-library/vue';
it('shows the title', async () => {
await renderSuspended(MyPage);
expect(screen.getByRole('heading')).toHaveTextContent('Dashboard');
});
Mocking Nuxt composables:
mockNuxtImport to mock any auto-imported composable. Call it once at the top of the test file — it persists across the file:import { mockNuxtImport } from '@nuxt/test-utils/runtime';
mockNuxtImport('useFetch', () => {
return () => ({
data: ref({ users: [{ id: 1, name: 'Alice' }] }),
error: ref(null),
pending: ref(false),
});
});
useRoute to simulate navigation params:mockNuxtImport('useRoute', () => {
return () => ({
params: { id: '42' },
query: { tab: 'settings' },
path: '/users/42',
});
});
vi.mocked with mockNuxtImport for per-test overrides:const { useFetch } = vi.hoisted(() => ({ useFetch: vi.fn() }));
mockNuxtImport('useFetch', () => useFetch);
beforeEach(() => {
useFetch.mockReturnValue({
data: ref(null),
error: ref('Network error'),
pending: ref(false),
});
});
Testing server routes:
$fetch from @nuxt/test-utils to make real HTTP requests against a test Nuxt server:import { setup, $fetch } from '@nuxt/test-utils/e2e';
describe('Server routes', async () => {
await setup({ rootDir: '.' });
it('returns users', async () => {
const users = await $fetch('/api/users');
expect(users).toHaveLength(3);
});
});
End-to-end testing with Playwright:
// tests/e2e/home.spec.ts
import { setup, createPage, url } from '@nuxt/test-utils/e2e';
import { test, expect } from '@playwright/test';
await setup({ rootDir: '.', browser: true });
test('home page loads', async () => {
const page = await createPage('/');
await expect(page.locator('h1')).toHaveText('Welcome');
});
Why not use plain Vitest + @vue/test-utils?
Standard Vue Test Utils doesn't know about Nuxt's auto-import layer. Composables like useNuxtApp, useFetch, useRoute will throw "not in Nuxt context" errors. @nuxt/test-utils sets up a minimal Nuxt environment that makes auto-imports available in tests.
mountSuspended vs. mount:
mount — synchronous, works for components with no async composablesmountSuspended — wraps in <Suspense>, awaits all useAsyncData/useFetch calls, returns after data is resolvedAlways prefer mountSuspended for page components or any component using useAsyncData.
Mocking modules vs. mocking composables:
mockNuxtImport mocks at the auto-import resolution layer. It's different from vi.mock('~/composables/useMyThing') — the latter only works for explicit imports. Use mockNuxtImport for anything that's auto-imported.
Test file organization:
tests/
unit/
components/ ← mountSuspended tests
composables/ ← plain Vitest + vue's renderHook
integration/
server/ ← $fetch API tests
e2e/ ← Playwright tests
Nuxt test environment options:
// vitest.config.ts
environment: 'nuxt',
environmentOptions: {
nuxt: {
rootDir: '.',
overrides: {
// Override nuxt.config.ts for tests
runtimeConfig: { public: { apiBase: 'http://localhost:3000' } }
}
}
}
https://nuxt.com/docs/getting-started/testing