diff --git a/src/terminal.integration.test.ts b/src/terminal.integration.test.ts index f2ad04f..911a720 100644 --- a/src/terminal.integration.test.ts +++ b/src/terminal.integration.test.ts @@ -483,4 +483,52 @@ describe.skipIf(!runIntegrationTests)("Integration Tests", () => { expect(dataReceived).toContain("HelloFromEnv"); }); + + test("Terminal onData listener receives data when set immediately after construction", async () => { + const start = Date.now(); + const maxRuntime = 4000; + let runnings = 1; + while (Date.now() - start < maxRuntime) { + console.log(`[TEST] Iteration ${runnings++}`); + const { success, stdout, stderr } = Bun.spawnSync({ + cmd: ["bun", "test", "terminal.integration.test.ts", "--test-name-pattern", "Terminal sync tests"], + stdout: "pipe", + stderr: "pipe", + env: { ...process.env, SYNC_TESTS: "1" }, + }); + expect(success, `stderr: ${stderr}`).toBe(true); + } + }); + + test.skipIf(!process.env.SYNC_TESTS)("Terminal sync tests", async () => { + let dataReceived = ""; + let hasExited = false; + + const { cmd, args } = echoCommand("sync test"); + console.log("[TEST] Starting terminal with command:", cmd, args); + const terminal = new Terminal(cmd, args); + terminals.push(terminal); + + const exitPromise = new Promise((resolve) => { + terminal.onExit(() => { + console.log("[TEST] Process exited"); + hasExited = true; + resolve(); + }); + }); + const dataPromise = new Promise((resolve) => { + terminal.onData((data) => { + console.log("[TEST] Received data:", data); + dataReceived += data; + if (dataReceived.includes("sync test")) + resolve(); + }); + const timeout = isWindows ? 5000 : 2000; + setTimeout(() => { resolve(); }, timeout); // Timeout to avoid hanging test + }); + + await Promise.race([exitPromise, dataPromise]); + + expect(dataReceived).toContain("sync test"); + }); }); diff --git a/src/terminal.ts b/src/terminal.ts index ec248d4..6c1efec 100644 --- a/src/terminal.ts +++ b/src/terminal.ts @@ -172,7 +172,8 @@ export class Terminal implements IPty { if (this.handle < 0) throw new Error("PTY spawn failed"); this._pid = lib.symbols.bun_pty_get_pid(this.handle); - this._startReadLoop(); + // allow constructor to finish and caller to set up event listeners + queueMicrotask(() => this._startReadLoop()); } /* ------------- accessors ------------- */