import type { IncomingMessage, ServerResponse } from 'node:http' import type { Connect } from 'vite' import type { ProjectBrowser } from './project' import type { ParentBrowserProject } from './projectParent' import crypto from 'node:crypto' import { join } from 'pathe' import { replacer } from './utils' export async function resolveTester( globalServer: ParentBrowserProject, url: URL, res: ServerResponse, next: Connect.NextFunction, ): Promise { const csp = res.getHeader('Content-Security-Policy') if (typeof csp === 'string') { // add frame-ancestors to allow the iframe to be loaded by Vitest, // but keep the rest of the CSP res.setHeader( 'Content-Security-Policy', csp.replace(/frame-ancestors [^;]+/, 'frame-ancestors *'), ) } const sessionId = url.searchParams.get('sessionId') const session = sessionId && globalServer.vitest._browserSessions.getSession(sessionId) if (!session) { res.statusCode = 400 res.end('Invalid session ID') return } const project = globalServer.vitest.getProjectByName(session.project.name || '') const browserProject = (project.browser as ProjectBrowser | undefined) || [...globalServer.children][0] if (!browserProject) { res.statusCode = 400 res.end('Invalid session ID') return } const injectorJs: string = typeof globalServer.injectorJs === 'string' ? globalServer.injectorJs : await globalServer.injectorJs const injector = replacer(injectorJs, { __VITEST_PROVIDER__: JSON.stringify(project.browser!.provider.name), __VITEST_CONFIG__: JSON.stringify(browserProject.wrapSerializedConfig()), __VITEST_VITE_CONFIG__: JSON.stringify({ root: browserProject.vite.config.root, }), __VITEST_TYPE__: '"tester"', __VITEST_METHOD__: JSON.stringify('none'), __VITEST_SESSION_ID__: JSON.stringify(sessionId), __VITEST_OTEL_CARRIER__: JSON.stringify(null), __VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()), __VITEST_PROVIDED_CONTEXT__: '{}', __VITEST_API_TOKEN__: JSON.stringify(globalServer.vitest.config.api.token), }) try { browserProject.testerHtmlTransformed ??= (async () => { const testerHtml = typeof browserProject.testerHtml === 'string' ? browserProject.testerHtml : await browserProject.testerHtml const url = join('/@fs/', browserProject.testerFilepath) return await browserProject.vite.transformIndexHtml(url, testerHtml) })() const indexhtml = await browserProject.testerHtmlTransformed const html = replacer(indexhtml, { __VITEST_FAVICON__: globalServer.faviconUrl, __VITEST_INJECTOR__: injector, }) return html } catch (err: any) { // don't cache a rejection — the next request should retry the transform browserProject.testerHtmlTransformed = undefined session.fail(err) next(err) } }