Capture daemon startup logs before window ready in e2e test (#10040)

The daemon health-check guard logs during main-process startup, which can
complete before the renderer window resolves. Moved stderr listening to the
launch options so early logs aren't missed. Also made the assertion regex
pattern-based instead of exact-string matching to tolerate benign log
rewording, and added a check that the replace path stayed off.
This commit is contained in:
Jinjing 2026-07-22 19:34:10 -07:00 committed by GitHub
parent 3708c4f6ce
commit 5b7fdd7ef5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 31 additions and 9 deletions

View File

@ -83,11 +83,13 @@ test('preserves a live daemon PTY when the daemon is too slow for the startup he
}
}, RESUME_DAEMON_AFTER_MS)
try {
const secondLaunch = await session.launch()
secondApp = secondLaunch.app
secondApp.process().stderr?.on('data', (chunk: Buffer) => {
stderrLines.push(chunk.toString())
// Why: capture stderr from process start — the daemon guard logs its
// preservation decision during main-process startup, which can complete
// before firstWindow resolves, so a post-launch listener would miss it.
const secondLaunch = await session.launch({
onStderr: (chunk) => stderrLines.push(chunk)
})
secondApp = secondLaunch.app
await waitForSessionReady(secondLaunch.page)
await expect
@ -101,11 +103,15 @@ test('preserves a live daemon PTY when the daemon is too slow for the startup he
await waitForPaneCount(secondLaunch.page, 1, 30_000)
await waitForTerminalOutput(secondLaunch.page, marker, 20_000)
// The guard path must actually have run: the daemon failed the health
// check and was preserved because its live session was verified.
// The guard path must actually have run and chosen preserve over replace:
// the daemon failed the health check yet was kept because its live session
// was verified. Match the stable "preserve…daemon…health check" concepts
// (not the exact wording) so a benign log reword doesn't flake, and
// confirm the replace path stayed off.
await expect
.poll(() => stderrLines.join(''), { timeout: 10_000 })
.toContain('Preserving daemon that failed the health check')
.toMatch(/preserv\w*\s+daemon[^\n]*health check/i)
expect(stderrLines.join('')).not.toMatch(/\breplacing daemon\b/i)
expect(readDaemonPid(session.userDataDir)).toBe(daemonPid)
// Why: a killed daemon cold-restores scrollback from history, so the
// marker text alone cannot distinguish a live session from a dead one.

View File

@ -34,9 +34,19 @@ type LaunchedOrca = {
page: Page
}
type LaunchOptions = {
/**
* Called for each chunk the relaunched main process writes to stderr. The
* listener is attached before `firstWindow()` resolves so main-process
* startup logs (e.g. the daemon health-check guard) can't be emitted before
* the test starts capturing.
*/
onStderr?: (chunk: string) => void
}
type RestartSession = {
userDataDir: string
launch: () => Promise<LaunchedOrca>
launch: (options?: LaunchOptions) => Promise<LaunchedOrca>
/** Gracefully close a launch, letting beforeunload flush session state. */
close: (app: ElectronApplication) => Promise<void>
/** Remove the shared userDataDir after the test is done. */
@ -137,7 +147,7 @@ export function createRestartSession(
`${JSON.stringify(getE2ECompletedOnboardingProfile(), null, 2)}\n`
)
const launch = async (): Promise<LaunchedOrca> => {
const launch = async (options?: LaunchOptions): Promise<LaunchedOrca> => {
runtimeWsPort ??= await reserveRestartRuntimeWsPort()
const app = await electron.launch({
args: getOrcaElectronLaunchArgs(mainPath, headful),
@ -146,6 +156,12 @@ export function createRestartSession(
ORCA_E2E_RUNTIME_WS_PORT: String(runtimeWsPort)
}
})
// Why: attach before firstWindow — the main-process daemon guard can emit
// its decision line during startup, before the renderer window is ready.
if (options?.onStderr) {
const onStderr = options.onStderr
app.process().stderr?.on('data', (chunk: Buffer) => onStderr(chunk.toString()))
}
try {
const resolvedHome = await app.evaluate(({ app }) => app.getPath('home'))
assertElectronResolvedIsolatedHome(resolvedHome, homeIsolation)