@@ -321,8 +321,11 @@ function registerExitHandlers() {
321321/**
322322 * TestDriver Vitest Plugin
323323 *
324- * Use this in the `plugins` array to inject console log forwarding.
325- * For the reporter functionality, use `testDriverReporter` in the `reporters` array.
324+ * This function works in both contexts:
325+ * - As a Vite plugin (in `plugins` array) - injects console log forwarding
326+ * - As a Vitest reporter (in `reporters` array) - handles test lifecycle tracking
327+ *
328+ * The function detects which context it's being used in and returns the appropriate object.
326329 */
327330export default function testDriverPlugin ( options = { } ) {
328331 // Initialize plugin state with options
@@ -345,34 +348,38 @@ export default function testDriverPlugin(options = {}) {
345348 logger . debug ( "Global TestDriver options:" , testDriverOptions ) ;
346349 }
347350
348- // Return a Vite plugin object that injects console log forwarding
349- return {
350- name : 'testdriver-plugin' ,
351-
352- // Inject onConsoleLog into Vitest config to forward logs to sandbox
353- config ( ) {
354- return {
355- test : {
356- onConsoleLog ( log , type ) {
357- // Forward to active sandbox if available
358- const sandbox = globalThis . __testdriverActiveSandbox ;
359- if ( sandbox ?. instanceSocketConnected ) {
360- try {
361- sandbox . send ( {
362- type : "output" ,
363- output : Buffer . from ( log , "utf8" ) . toString ( "base64" ) ,
364- } ) ;
365- } catch {
366- // Ignore errors when forwarding logs
367- }
351+ // Return an object that works as BOTH a Vite plugin AND a Vitest reporter
352+ // Vite plugins need: name, config(), etc.
353+ // Vitest reporters need: onInit(), onTestCaseResult(), etc.
354+ // We return the TestDriverReporter class which has both!
355+ const reporter = new TestDriverReporter ( options ) ;
356+
357+ // Add Vite plugin properties to the reporter instance
358+ reporter . name = 'testdriver-plugin' ;
359+ reporter . config = function ( ) {
360+ return {
361+ test : {
362+ onConsoleLog ( log , type ) {
363+ // Forward to active sandbox if available
364+ const sandbox = globalThis . __testdriverActiveSandbox ;
365+ if ( sandbox ?. instanceSocketConnected ) {
366+ try {
367+ sandbox . send ( {
368+ type : "output" ,
369+ output : Buffer . from ( log , "utf8" ) . toString ( "base64" ) ,
370+ } ) ;
371+ } catch {
372+ // Ignore errors when forwarding logs
368373 }
369- // Return true to still print the log
370- return true ;
371- } ,
374+ }
375+ // Return true to still print the log
376+ return true ;
372377 } ,
373- } ;
374- } ,
378+ } ,
379+ } ;
375380 } ;
381+
382+ return reporter ;
376383}
377384
378385/**
@@ -463,6 +470,23 @@ class TestDriverReporter {
463470 process . env . TD_TEST_RUN_DB_ID = pluginState . testRun . data ?. id || "" ;
464471 process . env . TD_TEST_RUN_TOKEN = pluginState . token ;
465472
473+ // Write test run info to file for cross-process communication
474+ // Worker processes can read this to get the test run ID
475+ const resultsDir = path . join ( os . tmpdir ( ) , "testdriver-results" ) ;
476+ if ( ! fs . existsSync ( resultsDir ) ) {
477+ fs . mkdirSync ( resultsDir , { recursive : true } ) ;
478+ }
479+ const testRunInfoFile = path . join ( resultsDir , "test-run-info.json" ) ;
480+ fs . writeFileSync ( testRunInfoFile , JSON . stringify ( {
481+ testRunId : pluginState . testRunId ,
482+ testRunDbId : pluginState . testRun . data ?. id || null ,
483+ token : pluginState . token ,
484+ apiKey : pluginState . apiKey ,
485+ apiRoot : pluginState . apiRoot ,
486+ startTime : pluginState . startTime ,
487+ } , null , 2 ) ) ;
488+ logger . debug ( `Wrote test run info to ${ testRunInfoFile } ` ) ;
489+
466490 // Also store in shared state module (won't work across processes but good for main)
467491 setTestRunInfo ( {
468492 testRun : pluginState . testRun ,
0 commit comments