@@ -133,6 +133,77 @@ describe("runQaDockerUp", () => {
133133 }
134134 } ) ;
135135
136+ it ( "falls back to Corepack for the QA UI build when pnpm is unavailable" , async ( ) => {
137+ const calls : string [ ] = [ ] ;
138+ const outputDir = await mkdtemp ( path . join ( os . tmpdir ( ) , "qa-docker-up-" ) ) ;
139+ const repoRoot = path . resolve ( "/repo/openclaw" ) ;
140+ const composeFile = path . join ( outputDir , "docker-compose.qa.yml" ) ;
141+
142+ try {
143+ await runQaDockerUp (
144+ {
145+ repoRoot,
146+ outputDir,
147+ usePrebuiltImage : true ,
148+ } ,
149+ {
150+ async runCommand ( command , args , cwd ) {
151+ calls . push ( [ command , ...args , `@${ cwd } ` ] . join ( " " ) ) ;
152+ if ( command === "pnpm" ) {
153+ throw Object . assign ( new Error ( "spawn pnpm ENOENT" ) , { code : "ENOENT" } ) ;
154+ }
155+ if ( args . join ( " " ) . includes ( "ps --format json openclaw-qa-gateway" ) ) {
156+ return { stdout : '{"Health":"healthy","State":"running"}\n' , stderr : "" } ;
157+ }
158+ return { stdout : "" , stderr : "" } ;
159+ } ,
160+ fetchImpl : vi . fn ( async ( ) => ( { ok : true } ) ) ,
161+ sleepImpl : vi . fn ( async ( ) => { } ) ,
162+ } ,
163+ ) ;
164+
165+ expect ( calls ) . toEqual ( [
166+ `pnpm qa:lab:build @${ repoRoot } ` ,
167+ `corepack pnpm qa:lab:build @${ repoRoot } ` ,
168+ `docker compose -f ${ composeFile } down --remove-orphans @${ repoRoot } ` ,
169+ `docker compose -f ${ composeFile } up -d @${ repoRoot } ` ,
170+ `docker compose -f ${ composeFile } ps --format json openclaw-qa-gateway @${ repoRoot } ` ,
171+ ] ) ;
172+ } finally {
173+ await rm ( outputDir , { recursive : true , force : true } ) ;
174+ }
175+ } ) ;
176+
177+ it ( "does not hide real QA UI build failures behind the Corepack fallback" , async ( ) => {
178+ const calls : string [ ] = [ ] ;
179+ const outputDir = await mkdtemp ( path . join ( os . tmpdir ( ) , "qa-docker-up-" ) ) ;
180+ const repoRoot = path . resolve ( "/repo/openclaw" ) ;
181+
182+ try {
183+ await expect (
184+ runQaDockerUp (
185+ {
186+ repoRoot,
187+ outputDir,
188+ usePrebuiltImage : true ,
189+ } ,
190+ {
191+ async runCommand ( command , args , cwd ) {
192+ calls . push ( [ command , ...args , `@${ cwd } ` ] . join ( " " ) ) ;
193+ throw Object . assign ( new Error ( "qa lab build failed" ) , { code : 1 } ) ;
194+ } ,
195+ fetchImpl : vi . fn ( async ( ) => ( { ok : true } ) ) ,
196+ sleepImpl : vi . fn ( async ( ) => { } ) ,
197+ } ,
198+ ) ,
199+ ) . rejects . toThrow ( "qa lab build failed" ) ;
200+
201+ expect ( calls ) . toEqual ( [ `pnpm qa:lab:build @${ repoRoot } ` ] ) ;
202+ } finally {
203+ await rm ( outputDir , { recursive : true , force : true } ) ;
204+ }
205+ } ) ;
206+
136207 it ( "uses a repo-root-relative default output dir when none is provided" , async ( ) => {
137208 const calls : string [ ] = [ ] ;
138209 const repoRoot = await mkdtemp ( path . join ( os . tmpdir ( ) , "qa-docker-root-" ) ) ;
0 commit comments