Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/test/src/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ export async function setup(project: TestProject) {

// Kill a process group (for detached processes) or process tree
async function killProcessTree(pid: number, name: string): Promise<void> {
if (process.platform === 'win32') {
// negative-pid process groups don't exist on Windows — the POSIX path below
// would degrade to killing only the direct child and orphan its workers.
// taskkill /T terminates the whole tree (mirrors setupTest.ts).
try {
const { execSync } = await import('node:child_process')
execSync(`taskkill /F /T /PID ${pid}`, { stdio: 'ignore' })
console.info(`${name} process (PID: ${pid}) killed successfully.`)
} catch {
// process may already be gone, which is fine
}
return
}

try {
// for detached processes, kill the entire process group using negative pid
try {
Expand Down
39 changes: 24 additions & 15 deletions tests/test/tests/metro-compiler.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import { spawn, type ChildProcess } from 'node:child_process'
import { createRequire } from 'node:module'
import { dirname, join } from 'node:path'
import getPort from 'get-port'
import { describe, expect, it, afterEach } from 'vitest'
import { killProcessTree } from './process-tree'

// Resolve one's JS entry (run.mjs) instead of node_modules/.bin/one: on Windows
// the .bin shim is a .cmd/.ps1/.exe wrapper that `node <path>` can't load
// (MODULE_NOT_FOUND), so the dev server never starts. Resolving via package.json
// gives the real JS file on every platform (mirrors packages/test/src/setupTest.ts).
const oneRunEntry = join(
dirname(createRequire(import.meta.url).resolve('one/package.json')),
'run.mjs'
)

/**
* Test that the React compiler works through the Metro bundling path.
Expand All @@ -20,10 +32,9 @@ import { describe, expect, it, afterEach } from 'vitest'
describe('Metro React compiler', { retry: 1 }, () => {
let devServer: ChildProcess | null = null

afterEach(() => {
afterEach(async () => {
if (devServer) {
devServer.kill('SIGTERM')
setTimeout(() => devServer?.kill('SIGKILL'), 1000)
await killProcessTree(devServer.pid)
devServer = null
}
})
Expand All @@ -35,18 +46,16 @@ describe('Metro React compiler', { retry: 1 }, () => {
let metroReady = false
let processExited = false

devServer = spawn(
'node',
['../../node_modules/.bin/one', 'dev', '--port', port.toString()],
{
cwd: process.cwd(),
env: {
...process.env,
DEBUG: 'vxrn:*,vite-plugin-metro:*',
},
stdio: ['ignore', 'pipe', 'pipe'],
}
)
devServer = spawn('node', [oneRunEntry, 'dev', '--port', port.toString()], {
cwd: process.cwd(),
env: {
...process.env,
DEBUG: 'vxrn:*,vite-plugin-metro:*',
},
stdio: ['ignore', 'pipe', 'pipe'],
// group leader on POSIX so killProcessTree can signal the whole group
detached: process.platform !== 'win32',
})

devServer.on('exit', (code) => {
processExited = true
Expand Down
45 changes: 27 additions & 18 deletions tests/test/tests/metro-startup-order.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import { spawn } from 'node:child_process'
import { createRequire } from 'node:module'
import { dirname, join } from 'node:path'
import getPort from 'get-port'
import { describe, expect, it } from 'vitest'
import { killProcessTree } from './process-tree'

// Resolve one's JS entry (run.mjs) instead of node_modules/.bin/one: on Windows
// the .bin shim is a .cmd/.ps1/.exe wrapper that `node <path>` can't load
// (MODULE_NOT_FOUND), so the dev server never starts. Resolving via package.json
// gives the real JS file on every platform (mirrors packages/test/src/setupTest.ts).
const oneRunEntry = join(
dirname(createRequire(import.meta.url).resolve('one/package.json')),
'run.mjs'
)

/**
* Test that Metro initialization happens AFTER Vite server is fully started.
Expand All @@ -23,21 +35,19 @@ describe('Metro startup order', () => {
let viteReadyPos = -1
let metroReadyPos = -1

const devServer = spawn(
'node',
['../../node_modules/.bin/one', 'dev', '--port', port.toString()],
{
cwd: process.cwd(),
env: {
...process.env,
// Enable Metro mode
ONE_METRO_MODE: '1',
// Enable debug logging to see Metro ready message
DEBUG: 'vxrn:*',
},
stdio: ['ignore', 'pipe', 'pipe'],
}
)
const devServer = spawn('node', [oneRunEntry, 'dev', '--port', port.toString()], {
cwd: process.cwd(),
env: {
...process.env,
// Enable Metro mode
ONE_METRO_MODE: '1',
// Enable debug logging to see Metro ready message
DEBUG: 'vxrn:*',
},
stdio: ['ignore', 'pipe', 'pipe'],
// group leader on POSIX so killProcessTree can signal the whole group
detached: process.platform !== 'win32',
})

const collectLogs = (data: Buffer) => {
const text = data.toString()
Expand Down Expand Up @@ -80,10 +90,9 @@ describe('Metro startup order', () => {
}, 100)
})

// Kill the server
devServer.kill('SIGTERM')
// Kill the server (tree-kill: taskkill /T on Windows, process-group on POSIX)
await killProcessTree(devServer.pid)
await new Promise((r) => setTimeout(r, 500))
devServer.kill('SIGKILL')

// Verify the order - Vite should be ready BEFORE Metro
console.info('Output captured:\n', allOutput)
Expand Down
Loading