* refactor(web): sharpen src/lib vs src/utils rule and move misfiled modules (#1776) Codify the empirical rule the web client already followed loosely — utils = pure functions that compute; lib = things that instantiate, adapt, or touch the environment — in AGENTS.md and clients/web/README.md, and move the modules that sat on the wrong side of it. Moves (source + co-located test): - lib/sandbox-csp -> utils (pure CSP-string construction; type-only import) - utils/browserTabVisibility -> lib (registers visibilitychange listeners) - utils/oauthResume -> lib (reads/writes sessionStorage) - utils/clearServerOAuthState -> lib (drives live InspectorClient / OAuthStorage) Borderline audits (kept in place): pendingReauth (pure type declarations), deepLink / correlateTransportErrors / mcpNetworkHeaders / oauthUx / oauthFlow / oauthNetworkPhase (pure transforms / type-only core imports) stay in utils; downloadFile stays whole in lib (dominant side is DOM side effects). Also relocate the lone src/test/lib/downloadFile.test.ts straggler beside its source at src/lib/downloadFile.test.ts, and document the test-placement rule (side-by-side by default; src/test/ only for core/ tests, the integration project, and shared scaffolding). Pure move — no behavior change; no build/coverage config keys off the boundary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * refactor(web): address review — fix stale paths, move customHeaders, one-way arrows (#1776) Responds to the @claude review on PR #1784: 1. Stale path references (fixed): update the four in-repo pointers left at pre-move paths — specification/v2_auth_mid_session.md (oauthResume, browserTabVisibility → lib/), and the two load-bearing sandbox security comments (sandbox_proxy.html, sandbox-controller.ts → src/utils/sandbox-csp). 2. lib/types/customHeaders (moved): it's pure (types + pure transforms), the rule's clearest counterexample. Moved lib/types/customHeaders.{ts,test.ts} → utils/ (no external importers; only its own test) and removed the undocumented lib/types/ subdir. Documented that utils/ is the home for pure shared domain types + their constructors — no separate types/ bucket. 3. One-way arrows (item 3): declare OAuthResumeAuthKind in utils/pendingReauth and re-export it from lib/oauthResume, so the only cross-directory arrow now points lib → utils. Codified that invariant in AGENTS.md. Wording nit: AGENTS.md + README now state diagnostic logging (console.warn/error) doesn't count as a side effect for the utils rule. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * docs: address review round 2 — stale customHeaders refs, rule wording (#1776) Doc-only follow-up to the round-2 @claude review on PR #1784 (no code changed): 1. Stale paths (same class as round-1 item 1): the customHeaders move left three references to the deleted lib/types/customHeaders.ts path in specification/ (v2_ux_interfaces_phase_0_4_inventory.md, v2_ux_interfaces_plan.md ×2) — updated to utils/customHeaders.ts. 2. customHeaders anchor: note in AGENTS.md that its shape is staged for ServerSettingsForm (per v2_ux_interfaces_plan.md), so its zero-importer state reads as intentional rather than rot; kept as the no-types/-bucket illustration and led the anchor list with load-bearing modules. 3. Type-only-import clause: split the conflated clause — a type-only import (pendingReauth) and re-exporting core's pure functions/constants (oauthUx/oauthFlow) are distinct reasons to stay in utils; what makes a module lib is wrapping core's stateful runtime. Fixed in both AGENTS.md and the web README. Cosmetic: re-padded the v2_auth_mid_session.md table row to column width. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * docs: address review round 3 — scope the "no types/ bucket" claim (#1776) Doc-only follow-up to the round-3 @claude review on PR #1784 (no code changed): 1. "no separate types/ bucket" overreached: clients/web/src/types/ does exist (navigation.ts + react-syntax-highlighter .d.ts ambient stubs wired via tsconfig.app.json paths). Scoped the claim in both AGENTS.md and the web README to "no types/ sub-bucket INSIDE lib/utils", and noted src/types/ is for ambient .d.ts stubs, not domain types. 2. README example list drifted from AGENTS.md's round-2 load-bearing anchors — realigned it (inspectorTabs/mcpNetworkHeaders, dropped the zero-importer customHeaders from the headline list). 3. Clarified v2_ux_interfaces_plan.md:185 so the v2 path reads as the copy's destination, not as the v1.5 source location. Follow-up filed as #1785 (board Todo): src/types/navigation.ts InspectorTab is dead and duplicates utils/inspectorTabs.ts — kept out of this pure-move PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * docs: address review round 4 — reconcile src/types/ claim with #1785 (#1776) Doc-only follow-up to the round-4 @claude review on PR #1784 (no code changed): The round-3 scope fix overshot: it asserted src/types/ holds "not domain types at all", but the dead navigation.ts InspectorTab is a plain-.ts domain type still living there, and specification/v2_ux_interfaces_plan.md:181-182 actively prescribes placing InspectorTab there — so AGENTS.md and that spec doc gave opposite guidance about the same directory. - AGENTS.md + web README: softened to "not the place for new domain types" and added a parenthetical that the one lingering domain type (dead navigation.ts) is tracked for removal in #1785; until then a pure domain type belongs in utils/, not src/types/. - v2_ux_interfaces_plan.md:181: marked the tab-enum placement guidance as superseded by #1785 (live type is InspectorTabId in utils/inspectorTabs.ts). Self-heals on merge of #1785. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * docs: address review round 5 — fix garbled #1785 clause, restructure utils rule (#1776) Doc-only follow-up to the round-5 @claude review on PR #1784 (no code changed): 1. Grammar (item 1): the round-4 #1785 clause read "superseding [#1785] will remove it" — a participle with no subject. Reworded to "the dead navigation.ts InspectorTab, which #1785 will remove". 2. Readability (item 2): the src/utils/ bullet had accreted into a single ~1,400-char blob across five review rounds. Promoted the carve-outs to scannable sub-bullets (Domain types / Diagnostic logging / Importing from core) and lifted the src/types/ note out to its own line, since it's a statement about a sibling directory, not about utils. AGENTS.md only; the README's parallel sentence was already clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * docs+config: address review round 6 — correct the coverage-glob claim (#1776) Responds to the round-6 @claude review on PR #1784: 1. "no build/coverage glob" was factually wrong (should-fix): the web coverage include in clients/web/vite.config.ts DOES enumerate src/lib/** and src/utils/**. The conclusion (a move between them is coverage-neutral) survives, but the reason was wrong. Reworded both AGENTS.md and the web README to say nothing *enforces* the boundary — no path alias, and the include lists both dirs so a move is coverage-neutral — plus the important whitelist caveat: a module outside components/lib/utils/server falls out of the ≥90 gate entirely. 2. Removed a pre-existing duplicate glob in vite.config.ts (src/lib/**/*.{ts,tsx} appeared twice, lines 161 and 163). Inert (vitest de-dupes) but it sat three lines from the config the doc now describes. Nits: gave the README bullet the same round-5 sub-bullet restructure as AGENTS.md, and made its #1785 reference a real markdown link (bare #N doesn't autolink in rendered repo markdown). npm run ci green (config change re-verified through coverage + smokes + Storybook). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
330 lines
16 KiB
TypeScript
330 lines
16 KiB
TypeScript
/// <reference types="vitest/config" />
|
|
import { defineConfig, type Plugin } from 'vite';
|
|
import react from '@vitejs/plugin-react';
|
|
|
|
// https://vite.dev/config/
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
|
|
import { playwright } from '@vitest/browser-playwright';
|
|
import { honoMiddlewarePlugin } from './server/vite-hono-plugin';
|
|
import { getViteBaseConfig, getViteDevOptimizeDeps } from './server/vite-base-config';
|
|
import { buildWebServerConfigFromEnv } from './server/web-server-config';
|
|
import { createBrowserExternalizedBuiltinGate } from './server/browser-externalized-builtin-gate';
|
|
import { vitestSharedPaths } from '../../vitest.shared.mts';
|
|
const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
|
|
const { repoRoot, sharedDedupe, nodeModulesAliases, projectResolve, sharedAliases } =
|
|
vitestSharedPaths(dirname);
|
|
|
|
// 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}';
|
|
|
|
// Fail `vite build` when a Node built-in lands in the browser bundle (#1769).
|
|
// Vite 8 otherwise only *warns* and ships a `module.exports = {}` stub, so a
|
|
// broken bundle builds green; the runtime browser smoke (`smoke:web:browser`,
|
|
// #1615) only catches the subset where that stub is *called* at module init.
|
|
// The detection + error logic lives in `browser-externalized-builtin-gate.ts`
|
|
// (Vite-agnostic, unit-tested); this is only the Vite wiring.
|
|
//
|
|
// Throwing directly in `onLog` does NOT abort a rolldown build (it's the one
|
|
// hook where a thrown error is swallowed — verified against vite@8.0.0), so the
|
|
// gate *records* the warning in `onLog` and re-throws in `buildEnd`, which runs
|
|
// after module resolution (by when the warning has fired) and where a throw
|
|
// aborts the build with a non-zero exit.
|
|
//
|
|
// `apply: 'build'` scopes it to `vite build` (never `vite dev` or the vitest
|
|
// projects), and `applyToEnvironment` narrows it to the **browser** (`client`)
|
|
// environment so a future SSR/node environment built from this config isn't
|
|
// failed for a legitimate `node:*` import — the browser-only intent is
|
|
// structural, not incidental. (The Node runner build is a separate tsup config.)
|
|
//
|
|
// `buildStart` resets the gate so a `vite build --watch` rebuild doesn't inherit
|
|
// a prior build's recorded warnings (the plugin instance is reused across
|
|
// rebuilds). `buildEnd` only asserts on the *success* path: rolldown also calls
|
|
// `buildEnd(error)` when the build already failed for another reason, and
|
|
// throwing then would mask that real error with the #1769 message.
|
|
function browserExternalizedBuiltinGate(): Plugin {
|
|
const gate = createBrowserExternalizedBuiltinGate();
|
|
return {
|
|
name: 'inspector:fail-on-browser-externalized-builtin',
|
|
apply: 'build',
|
|
applyToEnvironment: (environment) => environment.name === 'client',
|
|
// `enforce: 'pre'` runs this ahead of the normal-plugin group (and Vite's
|
|
// core plugins) in the `onLog` chain: a plugin whose `onLog` returns `false`
|
|
// filters that log for every later plugin, so trailing the array would let a
|
|
// future log-filtering plugin silently blind the gate. Ordering the gate
|
|
// first makes "it sees every log" structural rather than dependent on array
|
|
// position. Harmless to the emitting `rolldown:vite-resolve` plugin — the gate
|
|
// defines no resolve/load/transform, and `onLog` delivery doesn't depend on
|
|
// the emitter's position.
|
|
enforce: 'pre',
|
|
buildStart() {
|
|
gate.reset();
|
|
},
|
|
onLog(_level, log) {
|
|
gate.recordLog(log.message);
|
|
},
|
|
buildEnd(error) {
|
|
if (!error) gate.assertClean();
|
|
},
|
|
};
|
|
}
|
|
|
|
// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
|
|
export default defineConfig(({ command }) => {
|
|
const isDevServer = command === "serve" && !process.env.VITEST;
|
|
return {
|
|
// `honoMiddlewarePlugin` is gated by `apply: 'serve'` so it only attaches
|
|
// during `vite dev` / `vite preview` — vitest projects share this config
|
|
// but never invoke `configureServer`, so the plugin stays inert there.
|
|
//
|
|
// The plugin statically imports the node-only dev backend
|
|
// (`core/mcp/remote/node/server.ts`), so Vite's config bundler (Rolldown)
|
|
// walks that chain when it loads this file and reaches node-only deps
|
|
// (`chokidar`, `atomically`, `@napi-rs/keyring`). Those resolve cleanly at
|
|
// config-bundle time because they're declared in the repo-root
|
|
// `package.json` and installed into the repo-root `node_modules`, which sits
|
|
// on `core/`'s module-resolution chain (core/ has no node_modules of its
|
|
// own). Keep them in the root manifest: drop one and Rolldown can no longer
|
|
// resolve it from core/, reviving the benign `UNRESOLVED_IMPORT` warnings
|
|
// that #1491 eliminated at the source (by removing the old stream filter and
|
|
// build-time onwarn suppressions rather than re-hiding the symptom).
|
|
// `browserExternalizedBuiltinGate` fails `vite build` if a Node built-in
|
|
// reaches the browser bundle (#1769) — see its definition above.
|
|
plugins: [
|
|
react(),
|
|
honoMiddlewarePlugin(buildWebServerConfigFromEnv()),
|
|
browserExternalizedBuiltinGate(),
|
|
],
|
|
// Shared optimizeDeps exclusions so node-only packages
|
|
// (`@modelcontextprotocol/client/stdio`, `cross-spawn`, `which`)
|
|
// consumed by the dev backend aren't scanned for browser pre-bundling.
|
|
// Browser code reaches the node-side stack via the Hono plugin only.
|
|
// Dev server: force a full dep pre-bundle each launch (no stale cache).
|
|
optimizeDeps: isDevServer
|
|
? getViteDevOptimizeDeps()
|
|
: getViteBaseConfig().optimizeDeps,
|
|
resolve: {
|
|
// NOTE: the unit vitest project (below) overrides this — see comment there.
|
|
//
|
|
// Once App.tsx started consuming the full hook + state-manager surface
|
|
// (#1244), the browser dep graph reached bare-module subpaths in core/
|
|
// that Rolldown couldn't resolve against `core/`'s parent (it has no
|
|
// node_modules of its own). Promote the same bare-module aliases the
|
|
// vitest projects use so `vite dev` / `vite build` can resolve them
|
|
// from `clients/web/node_modules`.
|
|
alias: [
|
|
...Object.entries(sharedAliases).map(([find, replacement]) => ({
|
|
find,
|
|
replacement,
|
|
})),
|
|
...nodeModulesAliases,
|
|
],
|
|
// Source files in core/ import bare modules (react, @testing-library/react,
|
|
// etc.) that only exist in clients/web/node_modules. Dedupe ensures Vite
|
|
// resolves them from this package rather than walking up from core/'s
|
|
// location (which has no node_modules of its own yet).
|
|
dedupe: sharedDedupe,
|
|
},
|
|
// Pin the Vite dev server to the same port (and host) the Hono plugin
|
|
// configures from env, so `allowedOrigins` actually matches the browser
|
|
// origin. Without this, `vite dev` falls back to Vite's default 5173
|
|
// while the dev backend's `buildWebServerConfigFromEnv()` defaults to
|
|
// CLIENT_PORT=6274 — origin check rejects every `/api/*` request from
|
|
// the browser. CLIENT_PORT / HOST overrides flow through here too.
|
|
// `strictPort: true` so a port collision fails loudly instead of
|
|
// silently picking a different port (which would leave `allowedOrigins`
|
|
// pointing at the wrong host and break browser fetches).
|
|
server: {
|
|
port: parseInt(process.env.CLIENT_PORT ?? '6274', 10),
|
|
host: process.env.HOST ?? 'localhost',
|
|
strictPort: true,
|
|
fs: {
|
|
allow: [path.resolve(dirname, '../..')],
|
|
},
|
|
},
|
|
test: {
|
|
coverage: {
|
|
provider: 'v8',
|
|
reporter: ['text', 'html', 'json-summary'],
|
|
include: [
|
|
'src/components/**/*.{ts,tsx}',
|
|
'src/lib/**/*.{ts,tsx}',
|
|
'src/utils/**/*.{ts,tsx}',
|
|
'clients/web/server/**/*.{ts,tsx}',
|
|
path.join(repoRoot, 'core/mcp/**/*.{ts,tsx}'),
|
|
path.join(repoRoot, 'core/json/**/*.{ts,tsx}'),
|
|
path.join(repoRoot, 'core/client/**/*.{ts,tsx}'),
|
|
path.join(repoRoot, 'core/react/**/*.{ts,tsx}'),
|
|
path.join(repoRoot, 'core/auth/**/*.{ts,tsx}'),
|
|
path.join(repoRoot, 'core/storage/**/*.{ts,tsx}'),
|
|
path.join(repoRoot, 'core/logging/**/*.{ts,tsx}'),
|
|
path.join(repoRoot, 'core/node/**/*.{ts,tsx}'),
|
|
],
|
|
exclude: [
|
|
'**/*.stories.{ts,tsx}',
|
|
'**/*.test.{ts,tsx}',
|
|
'**/*.fixtures.{ts,tsx}',
|
|
'**/index.{ts,tsx}',
|
|
'src/components/**/types.ts',
|
|
// Dev-backend runtime glue: each file is exercised end-to-end via
|
|
// `npm run dev` (Hono plugin attaches, banner prints, /api/* serves).
|
|
// `vite-hono-plugin.ts` requires standing up a real Vite server with
|
|
// an HTTP listener to drive `configureServer`; `server.ts` is the
|
|
// production Hono entry that the v2 launcher (not yet ported, #1246)
|
|
// will invoke; `start-vite-dev-server.ts` is its dev counterpart.
|
|
// The non-glue parts are extracted to `web-server-config.ts` (fully
|
|
// tested) and `sandbox-controller.ts` (HTTP behavior tested).
|
|
'clients/web/server/vite-hono-plugin.ts',
|
|
'clients/web/server/server.ts',
|
|
'clients/web/server/start-vite-dev-server.ts',
|
|
// Pure-type modules: `interface`/`type` declarations only, no runtime
|
|
// statements. Excluding them keeps the report clean (would otherwise
|
|
// surface as misleading 0/0 rows).
|
|
path.join(repoRoot, 'core/mcp/types.ts'),
|
|
path.join(repoRoot, 'core/mcp/elicitationCreateMessage.ts'),
|
|
path.join(repoRoot, 'core/mcp/samplingCreateMessage.ts'),
|
|
path.join(repoRoot, 'core/mcp/sessionStorage.ts'),
|
|
path.join(repoRoot, 'core/mcp/inspectorClientProtocol.ts'),
|
|
path.join(repoRoot, 'core/mcp/remote/types.ts'),
|
|
path.join(repoRoot, 'core/mcp/import/types.ts'),
|
|
'clients/web/server/types.ts',
|
|
// .d.ts files are declaration-only.
|
|
path.join(repoRoot, '**/*.d.ts'),
|
|
// inspectorClientEventTarget.ts is types + a single empty-body class
|
|
// (extends TypedEventTarget). v8/istanbul records 0 statements for it
|
|
// today. TODO(#1243): drop this exclusion once the class gains real
|
|
// behavior as the v1.5 InspectorClient port progresses.
|
|
path.join(repoRoot, 'core/mcp/inspectorClientEventTarget.ts'),
|
|
path.join(repoRoot, 'core/mcp/__tests__/**'),
|
|
// test-servers/ is test infrastructure (composable MCP servers and
|
|
// fixtures), not application code — its build output also lives at
|
|
// test-servers/build/, which we don't want to measure either.
|
|
path.join(repoRoot, 'test-servers/**'),
|
|
],
|
|
thresholds: {
|
|
perFile: true,
|
|
// Uniform 90 per-file gate across every dimension. The branch floor was
|
|
// lifted 50 → 70 (#1271) and then the whole gate raised to 90 after a
|
|
// codebase-wide coverage audit added real tests for every outlier.
|
|
// Genuinely-unreachable branches (Mantine portal / `useMediaQuery` /
|
|
// `typeof window` SSR guards, React StrictMode effect replay, and
|
|
// provably-dead defensive guards) are annotated with justified
|
|
// `/* v8 ignore … */` comments at the source rather than relaxing the
|
|
// gate. New code must clear 90 on all four dimensions.
|
|
lines: 90,
|
|
statements: 90,
|
|
functions: 90,
|
|
branches: 90,
|
|
},
|
|
},
|
|
projects: [
|
|
{
|
|
extends: true,
|
|
// Vitest projects don't inherit `resolve` from the parent. The unit
|
|
// project runs from repoRoot (so vitest's coverage transformer can
|
|
// reach core/), but repoRoot has no node_modules of its own — the
|
|
// shared regex aliases redirect bare `react`/`pino`/etc. imports
|
|
// from core/ back into clients/web/node_modules.
|
|
resolve: projectResolve,
|
|
test: {
|
|
name: 'unit',
|
|
environment: 'happy-dom',
|
|
// Don't let happy-dom actually navigate child frames. Components like
|
|
// the MCP Apps sandbox render an <iframe src="/sandbox.html">; with
|
|
// navigation enabled happy-dom fetches that URL (and unloads it on
|
|
// teardown), which fails under the test server and floods the run with
|
|
// alarming-but-expected `DOMException [NetworkError/AbortError]` and
|
|
// `AsyncTaskManager destroyed` output. The component tests only assert
|
|
// on the iframe element/attributes, not its loaded document, so
|
|
// disabling frame navigation removes the noise without losing coverage.
|
|
environmentOptions: {
|
|
happyDOM: {
|
|
settings: { navigation: { disableChildFrameNavigation: true } },
|
|
},
|
|
},
|
|
// Root the unit project at the repo root so vitest's coverage
|
|
// transformer (which only processes files inside a project root)
|
|
// can reach core/ modules. Without this, untested core/ files fall
|
|
// back to raw-TS parsing in rolldown, which can't handle TS-only
|
|
// syntax (e.g. `import type`) — silently dropping them and bypassing
|
|
// the per-file gate.
|
|
root: repoRoot,
|
|
// No `globals: true` — every test file imports `describe`, `it`,
|
|
// `expect`, `vi` explicitly from "vitest". This keeps the pattern
|
|
// consistent and avoids relying on auto-cleanup tied to Vitest's
|
|
// 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: [integrationGlob],
|
|
setupFiles: [path.join(dirname, 'src/test/setup.ts')],
|
|
},
|
|
},
|
|
{
|
|
extends: true,
|
|
// See note on the unit project: integration tests also run from
|
|
// repoRoot and import core/ modules, so they need the same alias
|
|
// setup. The shared bare-module aliases keep `pino`, `hono`, etc.
|
|
// resolving against clients/web/node_modules.
|
|
resolve: projectResolve,
|
|
test: {
|
|
name: 'integration',
|
|
environment: 'node',
|
|
// Same reason as the unit project: rooted at repoRoot so vitest
|
|
// can transform core/ modules and run tests against the source.
|
|
root: repoRoot,
|
|
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.
|
|
testTimeout: 30000,
|
|
hookTimeout: 30000,
|
|
// Inline the MCP SDK so vi.mock("@modelcontextprotocol/client")
|
|
// hooks the same transformed copy that source files import.
|
|
// Externalized node_modules are loaded via Node's loader and bypass
|
|
// Vitest's mock system. Inlining ensures the transformer pipeline
|
|
// owns the module rather than the Node loader.
|
|
server: {
|
|
deps: {
|
|
inline: [/@modelcontextprotocol\/(client|core)/],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
extends: true,
|
|
plugins: [
|
|
// The plugin will run tests for the stories defined in your Storybook config
|
|
// See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
|
|
storybookTest({
|
|
configDir: path.join(dirname, '.storybook'),
|
|
}),
|
|
],
|
|
test: {
|
|
name: 'storybook',
|
|
browser: {
|
|
enabled: true,
|
|
headless: true,
|
|
provider: playwright({}),
|
|
instances: [
|
|
{
|
|
browser: 'chromium',
|
|
},
|
|
],
|
|
},
|
|
setupFiles: ['.storybook/vitest.setup.ts'],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
};
|
|
});
|