Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ jobs:
run: pnpm install

- name: 🏗️ Build project
run: pnpm build
run: NODE_ENV=test pnpm build

- name: ♿ Accessibility audit (Lighthouse - ${{ matrix.mode }} mode)
run: ./scripts/lighthouse-a11y.sh
Expand Down
64 changes: 64 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ We want to create 'a fast, modern browser for the npm registry.' This means, amo
- [Unit tests](#unit-tests)
- [Component accessibility tests](#component-accessibility-tests)
- [End to end tests](#end-to-end-tests)
- [Test fixtures (mocking external APIs)](#test-fixtures-mocking-external-apis)
- [Submitting changes](#submitting-changes)
- [Before submitting](#before-submitting)
- [Pull request process](#pull-request-process)
Expand Down Expand Up @@ -482,6 +483,69 @@ pnpm test:browser:ui # Run with Playwright UI

Make sure to read about [Playwright best practices](https://playwright.dev/docs/best-practices) and don't rely on classes/IDs but try to follow user-replicable behaviour (like selecting an element based on text content instead).

### Test fixtures (mocking external APIs)

E2E tests use a fixture system to mock external API requests, ensuring tests are deterministic and don't hit real APIs. This is handled at two levels:

**Server-side mocking** (`modules/fixtures.ts` + `modules/runtime/server/cache.ts`):

- Intercepts all `$fetch` calls during SSR
- Serves pre-recorded fixture data from `test/fixtures/`
- Enabled via `NUXT_TEST_FIXTURES=true` or Nuxt test mode

**Client-side mocking** (`test/e2e/test-utils.ts`):

- Uses Playwright's route interception to mock browser requests
- All test files import from `./test-utils` instead of `@nuxt/test-utils/playwright`
- Throws a clear error if an unmocked external request is detected

#### Fixture files

Fixtures are stored in `test/fixtures/` with this structure:

```
test/fixtures/
├── npm-registry/
│ ├── packuments/ # Package metadata (vue.json, @nuxt/kit.json)
│ ├── search/ # Search results (vue.json, nuxt.json)
│ └── orgs/ # Org package lists (nuxt.json)
├── npm-api/
│ └── downloads/ # Download stats
└── users/ # User package lists
```

#### Adding new fixtures

1. **Generate fixtures** using the script:

```bash
pnpm generate:fixtures vue lodash @nuxt/kit
```

2. **Or manually create** a JSON file in the appropriate directory

#### Environment variables

| Variable | Purpose |
| --------------------------------- | ---------------------------------- |
| `NUXT_TEST_FIXTURES=true` | Enable server-side fixture mocking |
| `NUXT_TEST_FIXTURES_VERBOSE=true` | Enable detailed fixture logging |

#### When tests fail due to missing fixtures

If a test fails with an error like:

```
UNMOCKED EXTERNAL API REQUEST DETECTED
API: npm registry
URL: https://registry.npmjs.org/some-package
```

You need to either:

1. Add a fixture file for that package/endpoint
2. Update the mock handlers in `test/e2e/test-utils.ts` (client) or `modules/runtime/server/cache.ts` (server)

## Submitting changes

### Before submitting
Expand Down
1 change: 1 addition & 0 deletions knip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const config: KnipConfig = {
'scripts/**/*.ts',
],
project: ['**/*.{ts,vue,cjs,mjs}'],
ignore: ['test/fixtures/**'],
ignoreDependencies: [
'@iconify-json/*',
'@vercel/kv',
Expand Down
40 changes: 40 additions & 0 deletions modules/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import process from 'node:process'
import { addServerPlugin, createResolver, defineNuxtModule, useNuxt } from 'nuxt/kit'

/**
* Test fixtures module for mocking external API requests.
*
* This module intercepts server-side requests to external APIs (npm registry, etc.)
* and serves pre-recorded fixture data instead. This ensures tests are deterministic
* and don't depend on external API availability.
*
* Enabled when:
* - `nuxt.options.test` is true (Nuxt test mode), OR
* - `NUXT_TEST_FIXTURES=true` environment variable is set
*
* Set `NUXT_TEST_FIXTURES_VERBOSE=true` for detailed logging.
*
* Note: This only mocks server-side requests. For client-side mocking in
* Playwright tests, see test/e2e/test-utils.ts.
*/
export default defineNuxtModule({
meta: {
name: 'fixtures',
},
setup() {
const nuxt = useNuxt()
const resolver = createResolver(import.meta.url)

if (nuxt.options.test || process.env.NUXT_TEST_FIXTURES === 'true') {
addServerPlugin(resolver.resolve('./runtime/server/cache.ts'))

nuxt.hook('nitro:config', nitroConfig => {
nitroConfig.storage ||= {}
nitroConfig.storage['fixtures'] = {
driver: 'fsLite',
base: resolver.resolve('../test/fixtures'),
}
})
}
},
})
Loading
Loading