@@ -26,6 +26,7 @@ import { deepClone, deepMerge, nanoid, noop, toArray } from '@vitest/utils/helpe
2626import { serializeValue } from '@vitest/utils/serialize'
2727import { join , normalize , relative } from 'pathe'
2828import { version } from '../../package.json' with { type : 'json' }
29+ import { setup as wsApiSetup } from '../api/setup'
2930import { defaultBrowserPort } from '../constants'
3031import { distDir } from '../paths'
3132import { createTagsFilter } from '../runtime/runner/utils/tags'
@@ -447,8 +448,22 @@ export class Vitest {
447448 // root-level browser server the API lives on the same shared httpServer and
448449 // is only needed when a UI (Vitest dashboard or browser orchestrator) is served.
449450 const apiNeeded = ! this . _rootBrowserParent || resolved . ui || resolved . browser . ui
450- if ( resolved . api && resolved . watch && apiNeeded ) {
451- ( await import ( '../api/setup' ) ) . setup ( this )
451+ const rootApi = resolved . api && resolved . watch && apiNeeded
452+ if ( rootApi ) {
453+ wsApiSetup ( this )
454+ }
455+
456+ const attachedApiServers = new Set ( [ rootApi ? this . vite . httpServer : null ] )
457+ for ( const project of this . projects ) {
458+ const browserServer = project . vite
459+ if (
460+ project . config . browser . ui
461+ && browserServer ?. httpServer
462+ && ! attachedApiServers . has ( browserServer . httpServer )
463+ ) {
464+ attachedApiServers . add ( browserServer . httpServer )
465+ wsApiSetup ( this , browserServer )
466+ }
452467 }
453468
454469 await this . _fsCache . ensureCacheIntegrity ( )
0 commit comments