From harness-claude
Tests Nuxt components, composables, and pages using @nuxt/test-utils with full Nuxt context including auto-imports. Covers mounting with mountSuspended, mocking composables like useFetch and useRoute, and e2e testing with $fetch.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:nuxt-testing-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Test Nuxt components, composables, and pages using @nuxt/test-utils with full Nuxt context including auto-imports
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
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeGuides authoring and reviewing frontend unit, component, and lightweight integration tests using React Testing Library, Vue Test Utils, accessible queries, user-event interactions, and controlled mocks.
Explains Nuxt 3 auto-imports for composables, components, and utilities. Helps resolve import errors and configure custom auto-import directories.
Provides testing patterns and examples for React components, hooks, and integrations using Vitest, React Testing Library, and Jest.