@@ -2,6 +2,7 @@ import { exec } from '../../lib/cli/cli';
22import { CliIoHost } from '../../lib/cli/io-host' ;
33import { Configuration } from '../../lib/cli/user-configuration' ;
44import { TestIoHost } from '../_helpers/io-host' ;
5+ import { Notices } from '../../lib/api/notices' ;
56
67// Store original version module exports so we don't conflict with other tests
78const originalVersion = jest . requireActual ( '../../lib/cli/version' ) ;
@@ -30,24 +31,26 @@ const actualUserConfig = jest.requireActual('../../lib/cli/user-configuration');
3031Configuration . fromArgs = jest . fn ( ) . mockImplementation ( ( ) => actualUserConfig . Configuration . fromArgs ( ioHelper ) ) ;
3132Configuration . fromArgsAndFiles = jest . fn ( ) . mockImplementation ( ( ) => actualUserConfig . Configuration . fromArgs ( ioHelper ) ) ;
3233
33- jest . mock ( '../../lib/api/notices' , ( ) => ( {
34- Notices : {
35- create : jest . fn ( ) . mockReturnValue ( {
36- refresh : jest . fn ( ) . mockResolvedValue ( undefined ) ,
37- display : jest . fn ( ) ,
38- } ) ,
39- } ,
40- } ) ) ;
41-
4234jest . mock ( '../../lib/cli/parse-command-line-arguments' , ( ) => ( {
43- parseCommandLineArguments : jest . fn ( ) . mockImplementation ( ( args ) => Promise . resolve ( {
44- _ : [ 'version' ] ,
45- verbose : args . includes ( '-v' ) ? (
46- args . filter ( ( arg : string ) => arg === '-v' ) . length
47- ) : args . includes ( '--verbose' ) ? (
48- parseInt ( args [ args . indexOf ( '--verbose' ) + 1 ] ) || true
49- ) : undefined ,
50- } ) ) ,
35+ parseCommandLineArguments : jest . fn ( ) . mockImplementation ( ( args ) => {
36+ const result : any = {
37+ _ : [ 'version' ] ,
38+ verbose : args . includes ( '-v' ) ? (
39+ args . filter ( ( arg : string ) => arg === '-v' ) . length
40+ ) : args . includes ( '--verbose' ) ? (
41+ parseInt ( args [ args . indexOf ( '--verbose' ) + 1 ] ) || true
42+ ) : undefined ,
43+ } ;
44+
45+ // Handle notices flags
46+ if ( args . includes ( '--notices' ) ) {
47+ result . notices = true ;
48+ } else if ( args . includes ( '--no-notices' ) ) {
49+ result . notices = false ;
50+ }
51+
52+ return Promise . resolve ( result ) ;
53+ } ) ,
5154} ) ) ;
5255
5356describe ( 'exec verbose flag tests' , ( ) => {
@@ -97,3 +100,296 @@ describe('exec verbose flag tests', () => {
97100 expect ( CliIoHost . instance ( ) . logLevel ) . toBe ( 'trace' ) ;
98101 } ) ;
99102} ) ;
103+
104+ describe ( 'notices configuration tests' , ( ) => {
105+ let mockNoticesCreate : jest . SpyInstance ;
106+
107+ beforeEach ( ( ) => {
108+ jest . clearAllMocks ( ) ;
109+
110+ // Mock the Notices.create method
111+ mockNoticesCreate = jest . spyOn ( Notices , 'create' ) . mockReturnValue ( {
112+ refresh : jest . fn ( ) . mockResolvedValue ( undefined ) ,
113+ display : jest . fn ( ) ,
114+ } as any ) ;
115+
116+ // Set up version module for our tests
117+ jest . mock ( '../../lib/cli/version' , ( ) => ( {
118+ ...originalVersion ,
119+ DISPLAY_VERSION : 'test-version' ,
120+ displayVersionMessage : jest . fn ( ) . mockResolvedValue ( undefined ) ,
121+ } ) ) ;
122+ } ) ;
123+
124+ afterEach ( ( ) => {
125+ mockNoticesCreate . mockRestore ( ) ;
126+ // Restore the version module to its original state
127+ jest . resetModules ( ) ;
128+ jest . setMock ( '../../lib/cli/version' , originalVersion ) ;
129+ } ) ;
130+
131+ test ( 'should send notices to "stderr" when passing --notices flag in CLI' , async ( ) => {
132+ await exec ( [ '--notices' , 'version' ] ) ;
133+
134+ expect ( mockNoticesCreate ) . toHaveBeenCalledWith (
135+ expect . objectContaining ( {
136+ "ioHost" : expect . objectContaining ( {
137+ "noticesDestination" : "stderr" ,
138+ } ) ,
139+ } )
140+ ) ;
141+ } ) ;
142+
143+ test ( 'should send notices to "drop" when passing --no-notices in CLI' , async ( ) => {
144+ await exec ( [ '--no-notices' , 'version' ] ) ;
145+
146+ expect ( mockNoticesCreate ) . toHaveBeenCalledWith (
147+ expect . objectContaining ( {
148+ "ioHost" : expect . objectContaining ( {
149+ "noticesDestination" : "drop" ,
150+ } ) ,
151+ } )
152+ ) ;
153+ } ) ;
154+
155+ test ( 'should send notices to "drop" when notices: false in settings and no CLI flag is provided' , async ( ) => {
156+ // Mock configuration to return notices: false
157+ const mockConfig = {
158+ loadConfigFiles : jest . fn ( ) . mockResolvedValue ( undefined ) ,
159+ settings : {
160+ get : jest . fn ( ) . mockImplementation ( ( key : string [ ] ) => {
161+ if ( key [ 0 ] === 'notices' ) return false ;
162+ return undefined ;
163+ } ) ,
164+ } ,
165+ context : {
166+ get : jest . fn ( ) . mockReturnValue ( [ ] ) ,
167+ } ,
168+ } ;
169+
170+ ( Configuration as any ) . mockImplementation ( ( ) => mockConfig ) ;
171+ Configuration . fromArgsAndFiles = jest . fn ( ) . mockImplementation ( ( ) => mockConfig ) ;
172+
173+ await exec ( [ 'version' ] ) ;
174+
175+ expect ( mockNoticesCreate ) . toHaveBeenCalledWith (
176+ expect . objectContaining ( {
177+ "ioHost" : expect . objectContaining ( {
178+ "noticesDestination" : "drop" ,
179+ } ) ,
180+ } )
181+ ) ;
182+ } ) ;
183+
184+ test . each ( [
185+ {
186+ envVar : 'TEAMCITY_VERSION' ,
187+ description : 'TeamCity' ,
188+ scenarios : [
189+ {
190+ name : 'should send notices to "drop" by default when no settings or CLI flags are provided' ,
191+ configNotices : undefined ,
192+ cliArgs : [ 'version' ] ,
193+ expectedDestination : 'drop'
194+ } ,
195+ {
196+ name : 'should send notices to "stderr" when config setting notices=true' ,
197+ configNotices : true ,
198+ cliArgs : [ 'version' ] ,
199+ expectedDestination : 'stderr'
200+ } ,
201+ {
202+ name : 'should send notices to "stderr" when passing --notices CLI flag' ,
203+ configNotices : undefined ,
204+ cliArgs : [ '--notices' , 'version' ] ,
205+ expectedDestination : 'stderr'
206+ } ,
207+ {
208+ name : 'should send notices to "drop" when passing --no-notices CLI flag, even when config has notices=true' ,
209+ configNotices : true ,
210+ cliArgs : [ '--no-notices' , 'version' ] ,
211+ expectedDestination : 'drop'
212+ }
213+ ]
214+ } ,
215+ {
216+ envVar : 'TF_BUILD' ,
217+ description : 'Azure DevOps' ,
218+ scenarios : [
219+ {
220+ name : 'should send notices to "drop" when no settings or CLI flags are provided' ,
221+ configNotices : undefined ,
222+ cliArgs : [ 'version' ] ,
223+ expectedDestination : 'drop'
224+ } ,
225+ {
226+ name : 'should send notices to "stderr" config setting notices=true' ,
227+ configNotices : true ,
228+ cliArgs : [ 'version' ] ,
229+ expectedDestination : 'stderr'
230+ } ,
231+ {
232+ name : 'should send notices to "stderr" --notices CLI flag' ,
233+ configNotices : undefined ,
234+ cliArgs : [ '--notices' , 'version' ] ,
235+ expectedDestination : 'stderr'
236+ } ,
237+ {
238+ name : 'should send notices to "drop" when passing --no-notices CLI flag, even when config has notices=true' ,
239+ configNotices : true ,
240+ cliArgs : [ '--no-notices' , 'version' ] ,
241+ expectedDestination : 'drop'
242+ }
243+ ]
244+ } ,
245+ ] ) ( 'CI environment with $description' , async ( { envVar, scenarios } ) => {
246+ for ( const scenario of scenarios ) {
247+ // Store original environment variables
248+ const originalCI = process . env . CI ;
249+ const originalEnvVar = process . env [ envVar ] ;
250+
251+ // Set CI environment variables
252+ process . env . CI = '1' ;
253+ process . env [ envVar ] = '1' ;
254+
255+ try {
256+ // Mock configuration
257+ const mockConfig = {
258+ loadConfigFiles : jest . fn ( ) . mockResolvedValue ( undefined ) ,
259+ settings : {
260+ get : jest . fn ( ) . mockImplementation ( ( key : string [ ] ) => {
261+ if ( key [ 0 ] === 'notices' ) return scenario . configNotices ;
262+ return undefined ;
263+ } ) ,
264+ } ,
265+ context : {
266+ get : jest . fn ( ) . mockReturnValue ( [ ] ) ,
267+ } ,
268+ } ;
269+
270+ ( Configuration as any ) . mockImplementation ( ( ) => mockConfig ) ;
271+ Configuration . fromArgsAndFiles = jest . fn ( ) . mockImplementation ( ( ) => mockConfig ) ;
272+
273+ await exec ( scenario . cliArgs ) ;
274+
275+ expect ( mockNoticesCreate ) . toHaveBeenCalledWith (
276+ expect . objectContaining ( {
277+ "ioHost" : expect . objectContaining ( {
278+ "noticesDestination" : scenario . expectedDestination ,
279+ } ) ,
280+ } )
281+ ) ;
282+ } finally {
283+ // Restore original environment variables
284+ if ( originalCI !== undefined ) {
285+ process . env . CI = originalCI ;
286+ } else {
287+ delete process . env . CI ;
288+ }
289+ if ( originalEnvVar !== undefined ) {
290+ process . env [ envVar ] = originalEnvVar ;
291+ } else {
292+ delete process . env [ envVar ] ;
293+ }
294+ }
295+ }
296+ } ) ;
297+
298+ test ( 'should read notices=true setting from configuration' , async ( ) => {
299+ // Mock configuration to return notices: true
300+ const mockConfig = {
301+ loadConfigFiles : jest . fn ( ) . mockResolvedValue ( undefined ) ,
302+ settings : {
303+ get : jest . fn ( ) . mockImplementation ( ( key : string ) => {
304+ if ( key [ 0 ] === 'notices' ) return true ;
305+ return undefined ;
306+ } ) ,
307+ } ,
308+ context : {
309+ get : jest . fn ( ) . mockReturnValue ( [ ] ) ,
310+ } ,
311+ } ;
312+
313+ ( Configuration as any ) . mockImplementation ( ( ) => mockConfig ) ;
314+ Configuration . fromArgsAndFiles = jest . fn ( ) . mockImplementation ( ( ) => mockConfig ) ;
315+
316+ await exec ( [ 'version' ] ) ;
317+
318+ expect ( mockNoticesCreate ) . toHaveBeenCalledWith (
319+ expect . objectContaining ( {
320+ "ioHost" : expect . objectContaining ( {
321+ "noticesDestination" : "stderr" ,
322+ } ) ,
323+ } )
324+ ) ;
325+ } ) ;
326+
327+ test ( 'should send notices to "drop" when passing --no-notices in CLI and config set to notices: false' , async ( ) => {
328+ // Mock configuration to return notices: true, but CLI flag should override
329+ const mockConfig = {
330+ loadConfigFiles : jest . fn ( ) . mockResolvedValue ( undefined ) ,
331+ settings : {
332+ get : jest . fn ( ) . mockImplementation ( ( key : string ) => {
333+ if ( key [ 0 ] === 'notices' ) return true ;
334+ return undefined ;
335+ } ) ,
336+ } ,
337+ context : {
338+ get : jest . fn ( ) . mockReturnValue ( [ ] ) ,
339+ } ,
340+ } ;
341+
342+ ( Configuration as any ) . mockImplementation ( ( ) => mockConfig ) ;
343+ Configuration . fromArgsAndFiles = jest . fn ( ) . mockImplementation ( ( ) => mockConfig ) ;
344+
345+ await exec ( [ '--no-notices' , 'version' ] ) ;
346+
347+ expect ( mockNoticesCreate ) . toHaveBeenCalledWith (
348+ expect . objectContaining ( {
349+ "ioHost" : expect . objectContaining ( {
350+ "noticesDestination" : "drop" ,
351+ } ) ,
352+ } )
353+ ) ;
354+ } ) ;
355+
356+ test . each ( [
357+ { value : undefined , expected : 'stderr' , description : 'undefined (autodetection)' } ,
358+ { value : false , expected : 'drop' , description : 'boolean false' } ,
359+ { value : true , expected : 'stderr' , description : 'boolean true' } ,
360+ // support string "false" as false
361+ { value : 'false' , expected : 'drop' , description : 'string "false"' } ,
362+ { value : 'truthy' , expected : 'stderr' , description : 'string "truthy"' } ,
363+ { value : 0 , expected : 'drop' , description : 'numeric 0' } ,
364+ { value : 1 , expected : 'stderr' , description : 'numeric 1' } ,
365+ { value : '' , expected : 'drop' , description : 'empty string' } ,
366+ { value : null , expected : 'drop' , description : 'null' } ,
367+ ] ) ( 'should send notices to "$expected" config value: $description' , async ( { value, expected } ) => {
368+ // Mock configuration to return the test value
369+ const mockConfig = {
370+ loadConfigFiles : jest . fn ( ) . mockResolvedValue ( undefined ) ,
371+ settings : {
372+ get : jest . fn ( ) . mockImplementation ( ( key : string [ ] ) => {
373+ if ( key [ 0 ] === 'notices' ) return value ;
374+ return undefined ;
375+ } ) ,
376+ } ,
377+ context : {
378+ get : jest . fn ( ) . mockReturnValue ( [ ] ) ,
379+ } ,
380+ } ;
381+
382+ ( Configuration as any ) . mockImplementation ( ( ) => mockConfig ) ;
383+ Configuration . fromArgsAndFiles = jest . fn ( ) . mockImplementation ( ( ) => mockConfig ) ;
384+
385+ await exec ( [ 'version' ] ) ;
386+
387+ expect ( mockNoticesCreate ) . toHaveBeenCalledWith (
388+ expect . objectContaining ( {
389+ "ioHost" : expect . objectContaining ( {
390+ "noticesDestination" : expected ,
391+ } ) ,
392+ } )
393+ ) ;
394+ } ) ;
395+ } ) ;
0 commit comments