@@ -365,6 +365,64 @@ describe('runList', () => {
365365 expect ( seen [ 0 ] ) . toContain ( 'createdFrom=portal' ) ;
366366 } ) ;
367367
368+ it ( 'uses TESTSPRITE_PROJECT_ID when --project is omitted' , async ( ) => {
369+ const { credentialsPath } = makeCreds ( ) ;
370+ const seen : string [ ] = [ ] ;
371+ const fetchImpl = makeFetch ( url => {
372+ seen . push ( url ) ;
373+ return { body : { items : [ FE_TEST ] , nextToken : null } } ;
374+ } ) ;
375+ await runList (
376+ { profile : 'default' , output : 'json' , debug : false } ,
377+ {
378+ credentialsPath,
379+ env : { TESTSPRITE_PROJECT_ID : 'project_env' } as NodeJS . ProcessEnv ,
380+ fetchImpl,
381+ stdout : ( ) => undefined ,
382+ } ,
383+ ) ;
384+ expect ( seen [ 0 ] ) . toContain ( 'projectId=project_env' ) ;
385+ } ) ;
386+
387+ it ( 'uses TESTSPRITE_PROJECT_ID when --project is blank' , async ( ) => {
388+ const { credentialsPath } = makeCreds ( ) ;
389+ const seen : string [ ] = [ ] ;
390+ const fetchImpl = makeFetch ( url => {
391+ seen . push ( url ) ;
392+ return { body : { items : [ FE_TEST ] , nextToken : null } } ;
393+ } ) ;
394+ await runList (
395+ { profile : 'default' , output : 'json' , debug : false , projectId : ' ' } ,
396+ {
397+ credentialsPath,
398+ env : { TESTSPRITE_PROJECT_ID : 'project_env' } as NodeJS . ProcessEnv ,
399+ fetchImpl,
400+ stdout : ( ) => undefined ,
401+ } ,
402+ ) ;
403+ expect ( seen [ 0 ] ) . toContain ( 'projectId=project_env' ) ;
404+ } ) ;
405+
406+ it ( 'prefers explicit --project over TESTSPRITE_PROJECT_ID' , async ( ) => {
407+ const { credentialsPath } = makeCreds ( ) ;
408+ const seen : string [ ] = [ ] ;
409+ const fetchImpl = makeFetch ( url => {
410+ seen . push ( url ) ;
411+ return { body : { items : [ FE_TEST ] , nextToken : null } } ;
412+ } ) ;
413+ await runList (
414+ { profile : 'default' , output : 'json' , debug : false , projectId : 'project_flag' } ,
415+ {
416+ credentialsPath,
417+ env : { TESTSPRITE_PROJECT_ID : 'project_env' } as NodeJS . ProcessEnv ,
418+ fetchImpl,
419+ stdout : ( ) => undefined ,
420+ } ,
421+ ) ;
422+ expect ( seen [ 0 ] ) . toContain ( 'projectId=project_flag' ) ;
423+ expect ( seen [ 0 ] ) . not . toContain ( 'projectId=project_env' ) ;
424+ } ) ;
425+
368426 it ( 'accepts --created-from cli and passes createdFrom=cli to the wire (dogfood 2026-06-04)' , async ( ) => {
369427 // End-to-end through parseEnumFlag: backend now stamps createFrom='cli'
370428 // on `testsprite test create` rows, so the filter must accept 'cli'.
@@ -783,10 +841,15 @@ describe('createTestCommand list — required flag', () => {
783841 await test . parseAsync ( [ 'list' ] , { from : 'user' } ) ;
784842 expect . unreachable ( 'expected ApiError' ) ;
785843 } catch ( err ) {
786- const apiErr = err as { code ?: string ; exitCode ?: number ; details ?: { field ?: string } } ;
844+ const apiErr = err as {
845+ code ?: string ;
846+ exitCode ?: number ;
847+ details ?: { field ?: string ; reason ?: string } ;
848+ } ;
787849 expect ( apiErr . code ) . toBe ( 'VALIDATION_ERROR' ) ;
788850 expect ( apiErr . exitCode ) . toBe ( 5 ) ;
789851 expect ( apiErr . details ?. field ) . toBe ( 'project' ) ;
852+ expect ( apiErr . details ?. reason ) . toContain ( 'TESTSPRITE_PROJECT_ID' ) ;
790853 }
791854 } ) ;
792855
@@ -4847,7 +4910,7 @@ describe('runCreate', () => {
48474910 ...SAMPLE_RESPONSE ,
48484911 type : 'backend' ,
48494912 warnings : [
4850- 'This test appears to hardcode an auth credential — read auth from __AUTH_HEADERS__.' ,
4913+ 'This test appears to hardcode an auth credential - read auth from __AUTH_HEADERS__.' ,
48514914 ] ,
48524915 } ,
48534916 } ;
@@ -4873,10 +4936,43 @@ describe('runCreate', () => {
48734936 ) ;
48744937 // Warning lands on stderr, prefixed `[warn]`.
48754938 expect ( err . some ( l => l . includes ( '[warn]' ) && l . includes ( '__AUTH_HEADERS__' ) ) ) . toBe ( true ) ;
4876- // stdout stays the parseable wire object — no warning noise.
4939+ // stdout stays the parseable wire object - no warning noise.
48774940 expect ( out . join ( '\n' ) ) . not . toContain ( '[warn]' ) ;
48784941 } ) ;
48794942
4943+ it ( 'uses TESTSPRITE_PROJECT_ID for create when --project is omitted' , async ( ) => {
4944+ const { credentialsPath } = makeCreds ( ) ;
4945+ const codeFile = writeCodeFile ( 'code body' ) ;
4946+ type Captured = { method : string ; body : unknown ; url : string } ;
4947+ const captured : Captured [ ] = [ ] ;
4948+ const fetchImpl = makeFetch ( ( url , init ) => {
4949+ const method = init . method ?? 'GET' ;
4950+ captured . push ( { url, method, body : init . body ? JSON . parse ( init . body as string ) : undefined } ) ;
4951+ if ( method === 'GET' ) return { status : 200 , body : { items : [ ] } } ;
4952+ return { status : 200 , body : SAMPLE_RESPONSE } ;
4953+ } ) ;
4954+ await runCreate (
4955+ {
4956+ profile : 'default' ,
4957+ output : 'json' ,
4958+ debug : false ,
4959+ type : 'frontend' ,
4960+ name : 'n' ,
4961+ codeFile,
4962+ } ,
4963+ {
4964+ credentialsPath,
4965+ env : { TESTSPRITE_PROJECT_ID : 'project_env' } as NodeJS . ProcessEnv ,
4966+ fetchImpl,
4967+ stdout : ( ) => undefined ,
4968+ } ,
4969+ ) ;
4970+ expect ( captured . some ( c => c . method === 'GET' && c . url . includes ( 'projectId=project_env' ) ) ) . toBe (
4971+ true ,
4972+ ) ;
4973+ const post = captured . find ( c => c . method === 'POST' ) ! ;
4974+ expect ( post . body ) . toMatchObject ( { projectId : 'project_env' } ) ;
4975+ } ) ;
48804976 it ( 'respects a caller-supplied --idempotency-key (for safe retries)' , async ( ) => {
48814977 const { credentialsPath } = makeCreds ( ) ;
48824978 const codeFile = writeCodeFile ( 'code body' ) ;
@@ -5030,7 +5126,6 @@ describe('runCreate', () => {
50305126 profile : 'default' ,
50315127 output : 'json' ,
50325128 debug : false ,
5033- // @ts -expect-error — exercising the runtime gate
50345129 projectId : undefined ,
50355130 type : 'frontend' ,
50365131 name : 'n' ,
0 commit comments