diff --git a/AGENTS.md b/AGENTS.md index b09cc371f..d499ca234 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -90,7 +90,7 @@ All work should be driven by items on the project board. - Run unit tests with `npm run test` (or `npm run test:watch` during development) from `clients/web/` - Run `npm run test:coverage` to verify the per-file gate: lines ≥ 90, statements ≥ 85, functions ≥ 80, branches ≥ 50 (CI enforces this gate). Branches is intentionally relaxed because Mantine portal/media-query branches are not exercisable under happy-dom; new business-logic branches should still be covered. - Run `npm run test:integration` (also from `clients/web/`) for the v1.5-ported InspectorClient + transport + auth integration suite. It runs under a separate `integration` vitest project in node env (no happy-dom) with 30s timeouts. The script builds `test-servers/` first via `tsc -p ../../test-servers --noCheck` so the stdio MCP test server can be spawned as a real subprocess. CI runs it as its own step after unit tests. -- Test files live alongside the source as `.test.tsx` (or `.test.ts` for non-React modules). v1.5-ported integration tests live under `clients/web/src/test/core/` and are wired into the `integration` project via the `integrationTests` list in `vite.config.ts`. +- Test files live alongside the source as `.test.tsx` (or `.test.ts` for non-React modules). v1.5-ported integration tests live under `clients/web/src/test/integration/`, mirroring the `core/` source layout (`mcp/`, `mcp/node/`, `mcp/remote/`, `auth/`, `auth/node/`, `storage/`). Any test file under that folder is automatically picked up by the `integration` vitest project (node env, 30s timeouts) via the folder glob in `vite.config.ts` — placement is the manifest, there is no enumeration to keep in sync. Tests outside the folder run in the `unit` project (happy-dom). When adding a new test for, e.g., `core/mcp/remote/foo.ts`, put it at `src/test/integration/mcp/remote/foo.test.ts`. - Use `renderWithMantine` from `src/test/renderWithMantine.tsx` to render components — it wraps in `MantineProvider` with the project theme ### Responding to Code Reviews diff --git a/clients/web/src/test/core/auth/discovery.test.ts b/clients/web/src/test/integration/auth/discovery.test.ts similarity index 100% rename from clients/web/src/test/core/auth/discovery.test.ts rename to clients/web/src/test/integration/auth/discovery.test.ts diff --git a/clients/web/src/test/core/auth/oauth-callback-server.test.ts b/clients/web/src/test/integration/auth/node/oauth-callback-server.test.ts similarity index 100% rename from clients/web/src/test/core/auth/oauth-callback-server.test.ts rename to clients/web/src/test/integration/auth/node/oauth-callback-server.test.ts diff --git a/clients/web/src/test/core/auth/storage-node.test.ts b/clients/web/src/test/integration/auth/node/storage.test.ts similarity index 100% rename from clients/web/src/test/core/auth/storage-node.test.ts rename to clients/web/src/test/integration/auth/node/storage.test.ts diff --git a/clients/web/src/test/core/auth/state-machine.test.ts b/clients/web/src/test/integration/auth/state-machine.test.ts similarity index 100% rename from clients/web/src/test/core/auth/state-machine.test.ts rename to clients/web/src/test/integration/auth/state-machine.test.ts diff --git a/clients/web/src/test/core/helpers/oauth-client-fixtures.ts b/clients/web/src/test/integration/helpers/oauth-client-fixtures.ts similarity index 100% rename from clients/web/src/test/core/helpers/oauth-client-fixtures.ts rename to clients/web/src/test/integration/helpers/oauth-client-fixtures.ts diff --git a/clients/web/src/test/core/inspectorClient-oauth-e2e.test.ts b/clients/web/src/test/integration/mcp/inspectorClient-oauth-e2e.test.ts similarity index 99% rename from clients/web/src/test/core/inspectorClient-oauth-e2e.test.ts rename to clients/web/src/test/integration/mcp/inspectorClient-oauth-e2e.test.ts index 1cbeb17ed..e87e23819 100644 --- a/clients/web/src/test/core/inspectorClient-oauth-e2e.test.ts +++ b/clients/web/src/test/integration/mcp/inspectorClient-oauth-e2e.test.ts @@ -34,7 +34,7 @@ import { completeOAuthAuthorization, createClientMetadataServer, type ClientMetadataDocument, -} from "./helpers/oauth-client-fixtures.js"; +} from "../helpers/oauth-client-fixtures.js"; import { clearAllOAuthClientState, NodeOAuthStorage, diff --git a/clients/web/src/test/core/inspectorClient-oauth-fetchFn.test.ts b/clients/web/src/test/integration/mcp/inspectorClient-oauth-fetchFn.test.ts similarity index 98% rename from clients/web/src/test/core/inspectorClient-oauth-fetchFn.test.ts rename to clients/web/src/test/integration/mcp/inspectorClient-oauth-fetchFn.test.ts index cae05d8c5..5ed25eeef 100644 --- a/clients/web/src/test/core/inspectorClient-oauth-fetchFn.test.ts +++ b/clients/web/src/test/integration/mcp/inspectorClient-oauth-fetchFn.test.ts @@ -14,7 +14,7 @@ import { InspectorClient } from "@inspector/core/mcp/inspectorClient.js"; import { createTransportNode } from "@inspector/core/mcp/node/transport.js"; import type { MCPServerConfig } from "@inspector/core/mcp/types.js"; import { NodeOAuthStorage } from "@inspector/core/auth/node/storage-node.js"; -import { createOAuthClientConfig } from "./helpers/oauth-client-fixtures.js"; +import { createOAuthClientConfig } from "../helpers/oauth-client-fixtures.js"; import type { InspectorClientOptions } from "@inspector/core/mcp/inspectorClient.js"; const oauthTestStatePath = path.join( diff --git a/clients/web/src/test/core/inspectorClient-oauth-remote-storage-e2e.test.ts b/clients/web/src/test/integration/mcp/inspectorClient-oauth-remote-storage-e2e.test.ts similarity index 99% rename from clients/web/src/test/core/inspectorClient-oauth-remote-storage-e2e.test.ts rename to clients/web/src/test/integration/mcp/inspectorClient-oauth-remote-storage-e2e.test.ts index e44ab7e85..34e21aa58 100644 --- a/clients/web/src/test/core/inspectorClient-oauth-remote-storage-e2e.test.ts +++ b/clients/web/src/test/integration/mcp/inspectorClient-oauth-remote-storage-e2e.test.ts @@ -35,7 +35,7 @@ import { import { createOAuthClientConfig, completeOAuthAuthorization, -} from "./helpers/oauth-client-fixtures.js"; +} from "../helpers/oauth-client-fixtures.js"; import { ConsoleNavigation } from "@inspector/core/auth/providers.js"; import type { InspectorClientOptions } from "@inspector/core/mcp/inspectorClient.js"; import type { MCPServerConfig } from "@inspector/core/mcp/types.js"; diff --git a/clients/web/src/test/core/inspectorClient-oauth.test.ts b/clients/web/src/test/integration/mcp/inspectorClient-oauth.test.ts similarity index 99% rename from clients/web/src/test/core/inspectorClient-oauth.test.ts rename to clients/web/src/test/integration/mcp/inspectorClient-oauth.test.ts index 8189f6c54..776674412 100644 --- a/clients/web/src/test/core/inspectorClient-oauth.test.ts +++ b/clients/web/src/test/integration/mcp/inspectorClient-oauth.test.ts @@ -25,7 +25,7 @@ import { import { createOAuthClientConfig, completeOAuthAuthorization, -} from "./helpers/oauth-client-fixtures.js"; +} from "../helpers/oauth-client-fixtures.js"; import type { InspectorClientOptions } from "@inspector/core/mcp/inspectorClient.js"; const oauthTestStatePath = path.join( diff --git a/clients/web/src/test/core/inspectorClient.test.ts b/clients/web/src/test/integration/mcp/inspectorClient.test.ts similarity index 100% rename from clients/web/src/test/core/inspectorClient.test.ts rename to clients/web/src/test/integration/mcp/inspectorClient.test.ts diff --git a/clients/web/src/test/core/transport.test.ts b/clients/web/src/test/integration/mcp/node/transport.test.ts similarity index 100% rename from clients/web/src/test/core/transport.test.ts rename to clients/web/src/test/integration/mcp/node/transport.test.ts diff --git a/clients/web/src/test/core/remote-server-config.test.ts b/clients/web/src/test/integration/mcp/remote/server-config.test.ts similarity index 100% rename from clients/web/src/test/core/remote-server-config.test.ts rename to clients/web/src/test/integration/mcp/remote/server-config.test.ts diff --git a/clients/web/src/test/core/remote-transport.test.ts b/clients/web/src/test/integration/mcp/remote/transport.test.ts similarity index 100% rename from clients/web/src/test/core/remote-transport.test.ts rename to clients/web/src/test/integration/mcp/remote/transport.test.ts diff --git a/clients/web/src/test/core/storage-adapters.test.ts b/clients/web/src/test/integration/storage/adapters.test.ts similarity index 100% rename from clients/web/src/test/core/storage-adapters.test.ts rename to clients/web/src/test/integration/storage/adapters.test.ts diff --git a/clients/web/vite.config.ts b/clients/web/vite.config.ts index f03ae22f4..da20dcc5a 100644 --- a/clients/web/vite.config.ts +++ b/clients/web/vite.config.ts @@ -70,26 +70,17 @@ const projectResolve = { dedupe: sharedDedupe, }; -// v1.5-ported integration tests that need a node-env vitest project — they -// spawn real HTTP/stdio servers via test-servers/, run end-to-end OAuth flows, -// talk to fs/network, or mock `@modelcontextprotocol/sdk/client/auth.js` (the -// SDK auth mock identity is lost under happy-dom + Vitest 4, but works under -// node env). Tracked in #1307. -const integrationTests = [ - 'clients/web/src/test/core/inspectorClient.test.ts', - 'clients/web/src/test/core/inspectorClient-oauth.test.ts', - 'clients/web/src/test/core/inspectorClient-oauth-e2e.test.ts', - 'clients/web/src/test/core/inspectorClient-oauth-fetchFn.test.ts', - 'clients/web/src/test/core/inspectorClient-oauth-remote-storage-e2e.test.ts', - 'clients/web/src/test/core/transport.test.ts', - 'clients/web/src/test/core/remote-transport.test.ts', - 'clients/web/src/test/core/remote-server-config.test.ts', - 'clients/web/src/test/core/storage-adapters.test.ts', - 'clients/web/src/test/core/auth/storage-node.test.ts', - 'clients/web/src/test/core/auth/oauth-callback-server.test.ts', - 'clients/web/src/test/core/auth/discovery.test.ts', - 'clients/web/src/test/core/auth/state-machine.test.ts', -]; +// Integration tests live under clients/web/src/test/integration/ and run in +// the node-env vitest project below. The folder is the manifest: anything +// inside it is integration (node env, 30s timeout, real servers); anything +// outside is a unit test (happy-dom). This prevents the silent +// misclassification trap where a file's environment depended on whether +// someone remembered to add it to an enumeration (#1314). +// +// Match `{ts,tsx}` to mirror the unit project's include below — otherwise a +// stray `.test.tsx` placed inside this folder would slip past the integration +// include AND fail to be excluded from unit, silently landing under happy-dom. +const integrationGlob = 'clients/web/src/test/integration/**/*.test.{ts,tsx}'; // More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon export default defineConfig({ @@ -182,7 +173,7 @@ export default defineConfig({ // global lifecycle hooks; cleanup is invoked manually in setup.ts. include: ['clients/web/src/**/*.test.{ts,tsx}'], // Integration tests run in the integration project below (node env). - exclude: integrationTests, + exclude: [integrationGlob], setupFiles: [path.join(dirname, 'src/test/setup.ts')], }, }, @@ -199,7 +190,7 @@ export default defineConfig({ // Same reason as the unit project: rooted at repoRoot so vitest // can transform core/ modules and run tests against the source. root: repoRoot, - include: integrationTests, + include: [integrationGlob], // Integration tests spawn real HTTP/stdio servers via test-servers/, // bind sockets, run e2e OAuth flows, and exercise filesystem-backed // storage. 30s matches the v1.5 core/vitest.config.ts.