@@ -7,22 +7,29 @@ import {
77 afterEach ,
88 beforeEach ,
99} from "vitest" ;
10- import { execSync , execFileSync } from "node:child_process" ;
10+ import { exec , execFile } from "node:child_process" ;
11+ import { promisify } from "node:util" ;
1112import path from "node:path" ;
1213import * as url from "node:url" ;
1314import fs from "node:fs" ;
1415
15- export const runCLI = ( args : string [ ] ) : string | undefined => {
16+ const execAsync = promisify ( exec ) ;
17+ const execFileAsync = promisify ( execFile ) ;
18+
19+ // Spawn the CLI asynchronously: a synchronous spawn blocks the vitest worker
20+ // thread, which under vitest 3.2 trips the "onTaskUpdate" RPC timeout on slower
21+ // machines (e.g. CI) even though the test itself passes.
22+ export const runCLI = async ( args : string [ ] ) : Promise < string | undefined > => {
1623 const cliPath = path . resolve (
1724 path . dirname ( url . fileURLToPath ( import . meta. url ) ) ,
1825 "../src/cli.ts" ,
1926 ) ;
2027
2128 try {
22- const cmd = "npx" ;
23- const allArgs = [ "tsx" , cliPath , ... args ] ;
24-
25- return execFileSync ( cmd , allArgs , { encoding : "utf-8" } ) ;
29+ const { stdout } = await execFileAsync ( "npx" , [ "tsx" , cliPath , ... args ] , {
30+ encoding : "utf-8" ,
31+ } ) ;
32+ return stdout ;
2633 } catch ( error ) {
2734 if ( error instanceof Error ) {
2835 return JSON . stringify ( error ) ;
@@ -32,9 +39,9 @@ export const runCLI = (args: string[]): string | undefined => {
3239} ;
3340
3441describe ( "CLI script" , ( ) => {
35- beforeAll ( ( ) => {
42+ beforeAll ( async ( ) => {
3643 // Ensure the build completes before tests
37- execSync ( "pnpm run build" , { stdio : "inherit" } ) ;
44+ await execAsync ( "pnpm run build" ) ;
3845
3946 // Verify that the build directory exists
4047 const thisBuildPath = path . resolve (
@@ -53,18 +60,18 @@ describe("CLI script", () => {
5360 vi . clearAllMocks ( ) ;
5461 } ) ;
5562
56- it ( "should exit with an error if build directory paths are missing" , ( ) => {
57- const output = runCLI ( [ ] ) ;
63+ it ( "should exit with an error if build directory paths are missing" , async ( ) => {
64+ const output = await runCLI ( [ ] ) ;
5865 expect ( output ) . toContain (
5966 "Not enough non-option arguments: got 0, need at least 1" ,
6067 ) ;
6168 } ) ;
6269
63- it ( "should exit with success if upload token is in an env var" , ( ) => {
70+ it ( "should exit with success if upload token is in an env var" , async ( ) => {
6471 const originalToken = process . env . CODECOV_UPLOAD_TOKEN ;
6572 process . env . CODECOV_UPLOAD_TOKEN = "token123" ;
6673
67- const output = runCLI ( [
74+ const output = await runCLI ( [
6875 "./src" ,
6976 "../bundle-analyzer" ,
7077 "--bundle-name=someName" ,
@@ -80,8 +87,8 @@ describe("CLI script", () => {
8087 ) ;
8188 } ) ;
8289
83- it ( "should exit with success when valid inputs are provided" , ( ) => {
84- const output = runCLI ( [
90+ it ( "should exit with success when valid inputs are provided" , async ( ) => {
91+ const output = await runCLI ( [
8592 "./src" ,
8693 "../bundle-analyzer" ,
8794 "--bundle-name=someName" ,
@@ -96,8 +103,8 @@ describe("CLI script", () => {
96103 ) ;
97104 } ) ;
98105
99- it ( "should log an error message if the directory doesn't exist" , ( ) => {
100- const output = runCLI ( [
106+ it ( "should log an error message if the directory doesn't exist" , async ( ) => {
107+ const output = await runCLI ( [
101108 "./doesnt-exist" ,
102109 "--bundle-name=someName" ,
103110 "--upload-token=token123" ,
@@ -106,8 +113,8 @@ describe("CLI script", () => {
106113 expect ( output ) . toContain ( "An error occurred:" ) ;
107114 } ) ;
108115
109- it ( "should handle multiple ignore patterns correctly" , ( ) => {
110- const output = runCLI ( [
116+ it ( "should handle multiple ignore patterns correctly" , async ( ) => {
117+ const output = await runCLI ( [
111118 "./src" ,
112119 "../bundle-analyzer" ,
113120 "--bundle-name=someName" ,
@@ -125,8 +132,8 @@ describe("CLI script", () => {
125132 expect ( output ) . not . toContain ( ".test.js" ) ;
126133 } ) ;
127134
128- it ( "should log an error for invalid CLI arguments" , ( ) => {
129- const output = runCLI ( [
135+ it ( "should log an error for invalid CLI arguments" , async ( ) => {
136+ const output = await runCLI ( [
130137 "./src" ,
131138 "../bundle-analyzer" ,
132139 "--bundle-name=someName" ,
@@ -223,10 +230,20 @@ describe("test CLI functions directly", () => {
223230 fs . unlinkSync ( configFilePath ) ; // Clean up after test
224231
225232 expect ( consoleSpy ) . toHaveBeenCalled ( ) ;
226- // the CLI argument should override anything supplied in the config file
227- expect ( consoleSpy . mock . calls [ 0 ] ?. [ 0 ] ) . toContain (
228- `bundleName":"this-is-the-name"` ,
229- ) ;
233+ // the CLI argument should override anything supplied in the config file.
234+ // Importing ./cli triggers a stray top-level runCli invocation, so match
235+ // the report this test produced rather than assuming a call index.
236+ const loggedReports = consoleSpy . mock . calls . map ( ( call ) => String ( call [ 0 ] ) ) ;
237+ expect (
238+ loggedReports . some ( ( report ) =>
239+ report . includes ( `bundleName":"this-is-the-name"` ) ,
240+ ) ,
241+ ) . toBe ( true ) ;
242+ expect (
243+ loggedReports . some ( ( report ) =>
244+ report . includes ( `bundleName":"this-name-should-be-ignored"` ) ,
245+ ) ,
246+ ) . toBe ( false ) ;
230247 } ) ;
231248
232249 it ( "should load options from a configuration file with error if file does not exist" , async ( ) => {
0 commit comments