@@ -4,11 +4,12 @@ import * as path from 'path';
44import type { FeatureFlag , Toolkit } from '@aws-cdk/toolkit-lib' ;
55// @ts -ignore
66import { Select } from 'enquirer' ;
7+ import type { IoHelper } from '../../lib/api-private' ;
78import { asIoHelper } from '../../lib/api-private' ;
89import { CliIoHost } from '../../lib/cli/io-host' ;
910import type { FlagsOptions } from '../../lib/cli/user-input' ;
10- import { displayFlags , handleFlags } from '../../lib/commands/flag-operations' ;
1111import { FlagCommandHandler } from '../../lib/commands/flags/flags' ;
12+ import { FlagOperations } from '../../lib/commands/flags/operations' ;
1213
1314jest . mock ( 'enquirer' , ( ) => ( {
1415 Select : jest . fn ( ) ,
@@ -29,6 +30,13 @@ const mockFlagsData: FeatureFlag[] = [
2930 userValue : false ,
3031 explanation : 'Test flag for unit tests' ,
3132 } ,
33+ {
34+ module : 'aws-cdk-lib' ,
35+ name : '@aws-cdk/core:needsAttention' ,
36+ recommendedValue : true ,
37+ userValue : undefined ,
38+ explanation : 'Test flag for unit tests' ,
39+ } ,
3240 {
3341 module : 'aws-cdk-lib' ,
3442 name : '@aws-cdk/s3:anotherFlag' ,
@@ -310,8 +318,7 @@ describe('displayFlags', () => {
310318
311319 const plainTextOutput = output ( ) ;
312320 // Should still show the table headers for API consistency
313- expect ( plainTextOutput ) . toContain ( 'Feature Flag Name' ) ;
314- expect ( plainTextOutput ) . toContain ( 'Recommended Value' ) ;
321+ expect ( plainTextOutput ) . toContain ( 'Recommended' ) ;
315322 // Should show helpful message after the empty table
316323 expect ( plainTextOutput ) . toContain ( '✅ All feature flags are already set to their recommended values.' ) ;
317324 expect ( plainTextOutput ) . toContain ( 'Use \'cdk flags --all --unstable=flags\' to see all flags and their current values.' ) ;
@@ -334,10 +341,9 @@ describe('displayFlags', () => {
334341
335342 const plainTextOutput = output ( ) ;
336343 // Should show the table with flags that need attention
337- expect ( plainTextOutput ) . toContain ( 'Feature Flag Name' ) ;
338- expect ( plainTextOutput ) . toContain ( 'Recommended Value' ) ;
339- expect ( plainTextOutput ) . toContain ( ' @aws-cdk/core:testFlag' ) ;
340- expect ( plainTextOutput ) . toContain ( ' @aws-cdk/s3:anotherFlag' ) ;
344+ expect ( plainTextOutput ) . not . toContain ( '@aws-cdk/core:testFlag' ) ;
345+ expect ( plainTextOutput ) . not . toContain ( '@aws-cdk/s3:anotherFlag' ) ;
346+ expect ( plainTextOutput ) . toContain ( '@aws-cdk/core:needsAttention' ) ;
341347 // Should NOT show the helpful message since there are flags to display
342348 expect ( plainTextOutput ) . not . toContain ( '✅ All feature flags are already set to their recommended values.' ) ;
343349 expect ( plainTextOutput ) . not . toContain ( 'Use \'cdk flags --all --unstable=flags\' to see all flags and their current values.' ) ;
@@ -372,17 +378,18 @@ describe('processFlagsCommand', () => {
372378 expect ( plainTextOutput ) . toContain ( ' @aws-cdk/s3:anotherFlag' ) ;
373379 } ) ;
374380
375- test ( 'displays only differing flags when no specific options are provided' , async ( ) => {
381+ test ( 'displays only unset flags when no specific options are provided' , async ( ) => {
376382 const options : FlagsOptions = {
377383 } ;
378384
379385 const flagOperations = new FlagCommandHandler ( mockFlagsData , ioHelper , options , mockToolkit ) ;
380386 await flagOperations . processFlagsCommand ( ) ;
381387
382388 const plainTextOutput = output ( ) ;
383- expect ( plainTextOutput ) . toContain ( ' @aws-cdk/core:testFlag' ) ;
384- expect ( plainTextOutput ) . toContain ( ' @aws-cdk/s3:anotherFlag' ) ;
385- expect ( plainTextOutput ) . not . toContain ( ' @aws-cdk/core:matchingFlag' ) ;
389+ expect ( plainTextOutput ) . not . toContain ( '@aws-cdk/core:testFlag' ) ;
390+ expect ( plainTextOutput ) . not . toContain ( '@aws-cdk/s3:anotherFlag' ) ; // Recommended: false, effective: false
391+ expect ( plainTextOutput ) . toContain ( '@aws-cdk/core:needsAttention' ) ;
392+ expect ( plainTextOutput ) . not . toContain ( '@aws-cdk/core:matchingFlag' ) ;
386393 } ) ;
387394
388395 test ( 'handles flag not found for specific flag query' , async ( ) => {
@@ -821,51 +828,6 @@ describe('processFlagsCommand', () => {
821828 await cleanupCdkJsonFile ( cdkJsonPath ) ;
822829 requestResponseSpy . mockRestore ( ) ;
823830 } ) ;
824-
825- test ( 'shows error when flag is not found during prototypeChanges' , async ( ) => {
826- // This test targets the validation in prototypeChanges function:
827- // if (!flag) { await ioHelper.defaults.error(`Flag ${flagName} not found.`); return false; }
828-
829- const cdkJsonPath = await createCdkJsonFile ( { } ) ;
830-
831- setupMockToolkitForPrototyping ( mockToolkit ) ;
832-
833- // Create a scenario where we try to set multiple flags but one doesn't exist
834- // We'll mock the internal flag lookup to simulate a missing flag during the prototyping process
835- const originalFind = Array . prototype . find ;
836- let findCallCount = 0 ;
837-
838- // Mock Array.find to return undefined for the second call (simulating missing flag in prototypeChanges)
839- Array . prototype . find = function ( this : any [ ] , callback : any ) {
840- findCallCount ++ ;
841- // First call is in handleFlags validation (should find the flag)
842- // Second call is in prototypeChanges (should not find the flag to trigger our test case)
843- if ( findCallCount === 2 ) {
844- return undefined ; // Simulate flag not found in prototypeChanges
845- }
846- return originalFind . call ( this , callback ) ;
847- } ;
848-
849- const options : FlagsOptions = {
850- set : true ,
851- all : true ,
852- recommended : true ,
853- } ;
854-
855- await handleFlags ( mockFlagsData , ioHelper , options , mockToolkit ) ;
856-
857- const plainTextOutput = output ( ) ;
858- expect ( plainTextOutput ) . toContain ( 'Flag @aws-cdk/s3:anotherFlag not found.' ) ;
859-
860- // Verify that prototyping was attempted but failed due to missing flag
861- expect ( mockToolkit . fromCdkApp ) . toHaveBeenCalledTimes ( 1 ) ; // Only the initial call, not the modified one
862- expect ( mockToolkit . diff ) . not . toHaveBeenCalled ( ) ; // Diff should not be called due to early return
863-
864- // Restore original Array.find
865- Array . prototype . find = originalFind ;
866-
867- await cleanupCdkJsonFile ( cdkJsonPath ) ;
868- } ) ;
869831} ) ;
870832
871833describe ( 'modifyValues' , ( ) => {
@@ -1398,3 +1360,37 @@ describe('setSafeFlags', () => {
13981360 requestResponseSpy . mockRestore ( ) ;
13991361 } ) ;
14001362} ) ;
1363+
1364+ interface FlagOperationsParams {
1365+ flagData : FeatureFlag [ ] ;
1366+ toolkit : Toolkit ;
1367+ ioHelper : IoHelper ;
1368+
1369+ /** User ran --recommended option */
1370+ recommended ?: boolean ;
1371+
1372+ /** User ran --all option */
1373+ all ?: boolean ;
1374+
1375+ /** User provided --value field */
1376+ value ?: string ;
1377+
1378+ /** User provided FLAGNAME field */
1379+ flagName ?: string [ ] ;
1380+
1381+ /** User ran --default option */
1382+ default ?: boolean ;
1383+
1384+ /** User ran --unconfigured option */
1385+ unconfigured ?: boolean ;
1386+ }
1387+
1388+ async function displayFlags ( params : FlagOperationsParams ) : Promise < void > {
1389+ const f = new FlagOperations ( params . flagData , params . toolkit , params . ioHelper ) ;
1390+ await f . displayFlags ( params ) ;
1391+ }
1392+
1393+ async function handleFlags ( flagData : FeatureFlag [ ] , _ioHelper : IoHelper , options : FlagsOptions , toolkit : Toolkit ) {
1394+ const f = new FlagCommandHandler ( flagData , _ioHelper , options , toolkit ) ;
1395+ await f . processFlagsCommand ( ) ;
1396+ }
0 commit comments