44 * Runs the script via `node` as a subprocess to verify its output,
55 * matching how npm would execute it after `npm install`.
66 */
7- import { execFileSync } from "node:child_process" ;
7+ import { execFileSync , spawnSync } from "node:child_process" ;
88import { dirname , resolve } from "node:path" ;
99import { describe , expect , it } from "vitest" ;
1010
@@ -25,29 +25,33 @@ function pathWithoutBun(): string {
2525
2626/**
2727 * Helper to run the postinstall script and capture output.
28+ *
29+ * The script writes to /dev/tty (bypasses npm's stdio suppression) with
30+ * a fallback to stderr. In tests we force the fallback by setting
31+ * FORCE_STDERR=1, which doesn't exist in the script -- instead we
32+ * redirect /dev/tty writes by running without a controlling terminal.
33+ * The simplest reliable approach: pipe the child's stdio so /dev/tty
34+ * open fails, triggering the stderr fallback that we CAN capture.
2835 */
2936function runPostinstall ( env ?: Record < string , string > ) : {
30- stdout : string ;
31- stderr : string ;
37+ output : string ;
3238 exitCode : number ;
3339} {
34- try {
35- const stdout = execFileSync ( "node" , [ SCRIPT_PATH ] , {
36- env : {
37- ...process . env ,
38- ...env ,
39- } ,
40- encoding : "utf-8" ,
41- timeout : 10000 ,
42- } ) ;
43- return { stdout, stderr : "" , exitCode : 0 } ;
44- } catch ( error : any ) {
45- return {
46- stdout : error . stdout ?? "" ,
47- stderr : error . stderr ?? "" ,
48- exitCode : error . status ?? 1 ,
49- } ;
50- }
40+ const result = spawnSync ( "node" , [ SCRIPT_PATH ] , {
41+ env : {
42+ ...process . env ,
43+ ...env ,
44+ } ,
45+ encoding : "utf-8" ,
46+ timeout : 10000 ,
47+ // Piping stdio detaches the child from the controlling terminal,
48+ // so openSync("/dev/tty") falls back to stderr which we capture.
49+ stdio : [ "pipe" , "pipe" , "pipe" ] ,
50+ } ) ;
51+ return {
52+ output : ( result . stdout ?? "" ) + ( result . stderr ?? "" ) ,
53+ exitCode : result . status ?? 1 ,
54+ } ;
5155}
5256
5357describe ( "scripts/postinstall.js" , ( ) => {
@@ -58,31 +62,31 @@ describe("scripts/postinstall.js", () => {
5862 } ) ;
5963
6064 it ( "prints the success message" , ( ) => {
61- const { stdout } = runPostinstall ( ) ;
65+ const { output } = runPostinstall ( ) ;
6266
63- expect ( stdout ) . toContain ( "talent-agent installed successfully!" ) ;
67+ expect ( output ) . toContain ( "talent-agent installed successfully!" ) ;
6468 } ) ;
6569
6670 it ( "prints getting-started instructions" , ( ) => {
67- const { stdout } = runPostinstall ( ) ;
71+ const { output } = runPostinstall ( ) ;
6872
69- expect ( stdout ) . toContain ( "talent-agent login" ) ;
70- expect ( stdout ) . toContain ( "talent-agent --help" ) ;
71- expect ( stdout ) . toContain ( "Get started:" ) ;
73+ expect ( output ) . toContain ( "talent-agent login" ) ;
74+ expect ( output ) . toContain ( "talent-agent --help" ) ;
75+ expect ( output ) . toContain ( "Get started:" ) ;
7276 } ) ;
7377
7478 it ( "does not show the bun warning when bun is available" , ( ) => {
75- const { stdout } = runPostinstall ( ) ;
79+ const { output } = runPostinstall ( ) ;
7680
7781 // bun is available in this dev environment
78- expect ( stdout ) . not . toContain ( "Bun runtime is required but was not found" ) ;
82+ expect ( output ) . not . toContain ( "Bun runtime is required but was not found" ) ;
7983 } ) ;
8084
8185 it ( "shows the bun warning when bun is not on PATH" , ( ) => {
82- const { stdout } = runPostinstall ( { PATH : pathWithoutBun ( ) } ) ;
86+ const { output } = runPostinstall ( { PATH : pathWithoutBun ( ) } ) ;
8387
84- expect ( stdout ) . toContain ( "Bun runtime is required but was not found" ) ;
85- expect ( stdout ) . toContain ( "curl -fsSL https://bun.sh/install | bash" ) ;
86- expect ( stdout ) . toContain ( "restart your terminal" ) ;
88+ expect ( output ) . toContain ( "Bun runtime is required but was not found" ) ;
89+ expect ( output ) . toContain ( "curl -fsSL https://bun.sh/install | bash" ) ;
90+ expect ( output ) . toContain ( "restart your terminal" ) ;
8791 } ) ;
8892} ) ;
0 commit comments