@@ -121,54 +121,75 @@ public async Task MenuBar_auto_hide_and_visibility()
121121 public async Task ReadyToShow_event_fires_after_content_ready ( )
122122 {
123123 var window = await Electron . WindowManager . CreateWindowAsync ( new BrowserWindowOptions { Show = false } , "about:blank" ) ;
124- var tcs = new TaskCompletionSource ( ) ;
125- window . OnReadyToShow += ( ) => tcs . TrySetResult ( ) ;
124+ try
125+ {
126+ var tcs = new TaskCompletionSource ( ) ;
127+ window . OnReadyToShow += ( ) => tcs . TrySetResult ( ) ;
126128
127- // Trigger a navigation and wait for DOM ready so the renderer paints, which emits ready-to-show
128- var domReadyTcs = new TaskCompletionSource ( ) ;
129- window . WebContents . OnDomReady += ( ) => domReadyTcs . TrySetResult ( ) ;
130- await Task . Delay ( 500 ) ;
131- await window . WebContents . LoadURLAsync ( "about:blank" ) ;
132- await domReadyTcs . Task ;
129+ // Trigger a navigation and wait for DOM ready so the renderer paints, which emits ready-to-show
130+ var domReadyTcs = new TaskCompletionSource ( ) ;
131+ window . WebContents . OnDomReady += ( ) => domReadyTcs . TrySetResult ( ) ;
132+ await Task . Delay ( 500 ) ;
133+ await window . WebContents . LoadURLAsync ( "about:blank" ) ;
134+ await domReadyTcs . Task ;
133135
134- var completed = await Task . WhenAny ( tcs . Task , Task . Delay ( 3000 ) ) ;
135- completed . Should ( ) . Be ( tcs . Task ) ;
136+ var completed = await Task . WhenAny ( tcs . Task , Task . Delay ( 3000 ) ) ;
137+ completed . Should ( ) . Be ( tcs . Task ) ;
136138
137- // Typical usage is to show once ready
138- window . Show ( ) ;
139+ // Typical usage is to show once ready
140+ window . Show ( ) ;
141+ }
142+ finally
143+ {
144+ window . Destroy ( ) ;
145+ }
139146 }
140147
141148 [ Fact ( Timeout = 20000 ) ]
142149 public async Task PageTitleUpdated_event_fires_on_title_change ( )
143150 {
144151 var window = await Electron . WindowManager . CreateWindowAsync ( new BrowserWindowOptions { Show = true } , "about:blank" ) ;
145- var tcs = new TaskCompletionSource < string > ( ) ;
146- window . OnPageTitleUpdated += title => tcs . TrySetResult ( title ) ;
152+ try
153+ {
154+ var tcs = new TaskCompletionSource < string > ( ) ;
155+ window . OnPageTitleUpdated += title => tcs . TrySetResult ( title ) ;
147156
148- // Navigate and wait for DOM ready, then change the document.title to trigger the event
149- var domReadyTcs = new TaskCompletionSource ( ) ;
150- window . WebContents . OnDomReady += ( ) => domReadyTcs . TrySetResult ( ) ;
151- await Task . Delay ( 500 ) ;
152- await window . WebContents . LoadURLAsync ( "about:blank" ) ;
153- await domReadyTcs . Task ;
154- await window . WebContents . ExecuteJavaScriptAsync < string > ( "document.title='NewTitle';" ) ;
157+ // Navigate and wait for DOM ready, then change the document.title to trigger the event
158+ var domReadyTcs = new TaskCompletionSource ( ) ;
159+ window . WebContents . OnDomReady += ( ) => domReadyTcs . TrySetResult ( ) ;
160+ await Task . Delay ( 500 ) ;
161+ await window . WebContents . LoadURLAsync ( "about:blank" ) ;
162+ await domReadyTcs . Task ;
163+ await window . WebContents . ExecuteJavaScriptAsync < string > ( "document.title='NewTitle';" ) ;
155164
156- // Wait for event up to a short timeout
157- var completed2 = await Task . WhenAny ( tcs . Task , Task . Delay ( 3000 ) ) ;
158- completed2 . Should ( ) . Be ( tcs . Task ) ;
159- ( await tcs . Task ) . Should ( ) . Be ( "NewTitle" ) ;
165+ // Wait for event up to a short timeout
166+ var completed2 = await Task . WhenAny ( tcs . Task , Task . Delay ( 3000 ) ) ;
167+ completed2 . Should ( ) . Be ( tcs . Task ) ;
168+ ( await tcs . Task ) . Should ( ) . Be ( "NewTitle" ) ;
169+ }
170+ finally
171+ {
172+ window . Destroy ( ) ;
173+ }
160174 }
161175
162176 [ Fact ( Timeout = 20000 ) ]
163177 public async Task Resize_event_fires_on_size_change ( )
164178 {
165179 var window = await Electron . WindowManager . CreateWindowAsync ( new BrowserWindowOptions { Show = false } , "about:blank" ) ;
166- var resized = false ;
167- window . OnResize += ( ) => resized = true ;
168- await Task . Delay ( 500 ) ;
169- window . SetSize ( 500 , 400 ) ;
170- await Task . Delay ( 300 ) ;
171- resized . Should ( ) . BeTrue ( ) ;
180+ try
181+ {
182+ var resized = false ;
183+ window . OnResize += ( ) => resized = true ;
184+ await Task . Delay ( 500 ) ;
185+ window . SetSize ( 500 , 400 ) ;
186+ await Task . Delay ( 300 ) ;
187+ resized . Should ( ) . BeTrue ( ) ;
188+ }
189+ finally
190+ {
191+ window . Destroy ( ) ;
192+ }
172193 }
173194
174195 [ Fact ( Timeout = 20000 ) ]
@@ -205,14 +226,20 @@ public async Task Menu_bar_visibility_and_auto_hide()
205226 public async Task Parent_child_relationship_roundtrip ( )
206227 {
207228 var child = await Electron . WindowManager . CreateWindowAsync ( new BrowserWindowOptions { Show = false , Width = 300 , Height = 200 } , "about:blank" ) ;
208- this . fx . MainWindow . SetParentWindow ( null ) ; // ensure top-level
209- child . SetParentWindow ( this . fx . MainWindow ) ;
210- await Task . Delay ( 500 ) ;
211- var parent = await child . GetParentWindowAsync ( ) ;
212- parent . Id . Should ( ) . Be ( this . fx . MainWindow . Id ) ;
213- var kids = await this . fx . MainWindow . GetChildWindowsAsync ( ) ;
214- kids . Select ( k => k . Id ) . Should ( ) . Contain ( child . Id ) ;
215- child . Destroy ( ) ;
229+ try
230+ {
231+ this . fx . MainWindow . SetParentWindow ( null ) ; // ensure top-level
232+ child . SetParentWindow ( this . fx . MainWindow ) ;
233+ await Task . Delay ( 500 ) ;
234+ var parent = await child . GetParentWindowAsync ( ) ;
235+ parent . Id . Should ( ) . Be ( this . fx . MainWindow . Id ) ;
236+ var kids = await this . fx . MainWindow . GetChildWindowsAsync ( ) ;
237+ kids . Select ( k => k . Id ) . Should ( ) . Contain ( child . Id ) ;
238+ }
239+ finally
240+ {
241+ child . Destroy ( ) ;
242+ }
216243 }
217244
218245 [ SkippableFact ( Timeout = 20000 ) ]
@@ -238,5 +265,144 @@ public async Task Represented_filename_and_edited_flags()
238265
239266 win . SetDocumentEdited ( false ) ;
240267 }
268+
269+ // --- Added window state tests ---
270+
271+ [ Fact ( Timeout = 20000 ) ]
272+ public async Task Fullscreen_toggle_and_query ( )
273+ {
274+ var win = this . fx . MainWindow ;
275+ win . Show ( ) ;
276+ await Task . Delay ( 300 ) ;
277+ ( await win . IsVisibleAsync ( ) ) . Should ( ) . BeTrue ( ) ;
278+
279+ // Ensure window is fullscreenable
280+ win . SetFullScreenable ( true ) ;
281+ await Task . Delay ( 200 ) ;
282+
283+ // Poll for fullscreen state instead of fixed waits (Electron can be slow)
284+ win . SetFullScreen ( true ) ;
285+ var entered = await WaitUntil ( async ( ) => await win . IsFullScreenAsync ( ) , 3000 ) ;
286+ entered . Should ( ) . BeTrue ( "Window should enter fullscreen within timeout" ) ;
287+
288+ win . SetFullScreen ( false ) ;
289+ var left = await WaitUntil ( async ( ) => ! ( await win . IsFullScreenAsync ( ) ) , 3000 ) ;
290+ left . Should ( ) . BeTrue ( "Window should exit fullscreen within timeout" ) ;
291+ }
292+
293+ [ Fact ( Timeout = 20000 ) ]
294+ public async Task Maximize_unmaximize_roundtrip ( )
295+ {
296+ var win = this . fx . MainWindow ;
297+ win . Unmaximize ( ) ; // ensure not maximized
298+ await Task . Delay ( 500 ) ;
299+ ( await win . IsMaximizedAsync ( ) ) . Should ( ) . BeFalse ( ) ;
300+ win . Maximize ( ) ;
301+ await Task . Delay ( 800 ) ;
302+ ( await win . IsMaximizedAsync ( ) ) . Should ( ) . BeTrue ( ) ;
303+ win . Unmaximize ( ) ;
304+ await Task . Delay ( 500 ) ;
305+ ( await win . IsMaximizedAsync ( ) ) . Should ( ) . BeFalse ( ) ;
306+ }
307+
308+ [ Fact ( Timeout = 20000 ) ]
309+ public async Task Minimize_restore_roundtrip ( )
310+ {
311+ var win = this . fx . MainWindow ;
312+ win . Restore ( ) ; // ensure not minimized
313+ await Task . Delay ( 500 ) ;
314+ ( await win . IsMinimizedAsync ( ) ) . Should ( ) . BeFalse ( ) ;
315+ win . Minimize ( ) ;
316+ await Task . Delay ( 800 ) ;
317+ ( await win . IsMinimizedAsync ( ) ) . Should ( ) . BeTrue ( ) ;
318+ win . Restore ( ) ;
319+ await Task . Delay ( 500 ) ;
320+ ( await win . IsMinimizedAsync ( ) ) . Should ( ) . BeFalse ( ) ;
321+ }
322+
323+ [ Fact ( Timeout = 20000 ) ]
324+ public async Task Fullscreen_events_fire_on_toggle ( )
325+ {
326+ var window = await Electron . WindowManager . CreateWindowAsync ( new BrowserWindowOptions { Show = true , Fullscreenable = true } , "about:blank" ) ;
327+ try
328+ {
329+ bool entered = false , left = false ;
330+ window . OnEnterFullScreen += ( ) => entered = true ;
331+ window . OnLeaveFullScreen += ( ) => left = true ;
332+ await Task . Delay ( 400 ) ;
333+
334+ window . SetFullScreen ( true ) ;
335+ await WaitUntil ( ( ) => Task . FromResult ( entered ) , 4000 ) ;
336+ entered . Should ( ) . BeTrue ( ) ;
337+
338+ window . SetFullScreen ( false ) ;
339+ await WaitUntil ( ( ) => Task . FromResult ( left ) , 4000 ) ;
340+ left . Should ( ) . BeTrue ( ) ;
341+ }
342+ finally
343+ {
344+ window . Destroy ( ) ;
345+ }
346+ }
347+
348+ [ Fact ( Timeout = 20000 ) ]
349+ public async Task Maximize_and_unmaximize_events_fire ( )
350+ {
351+ var window = await Electron . WindowManager . CreateWindowAsync ( new BrowserWindowOptions { Show = true } , "about:blank" ) ;
352+ try
353+ {
354+ bool maximized = false , unmaximized = false ;
355+ window . OnMaximize += ( ) => maximized = true ;
356+ window . OnUnmaximize += ( ) => unmaximized = true ;
357+ await Task . Delay ( 500 ) ;
358+ window . Maximize ( ) ;
359+ await Task . Delay ( 800 ) ;
360+ maximized . Should ( ) . BeTrue ( ) ;
361+ window . Unmaximize ( ) ;
362+ await Task . Delay ( 500 ) ;
363+ unmaximized . Should ( ) . BeTrue ( ) ;
364+ }
365+ finally
366+ {
367+ window . Destroy ( ) ;
368+ }
369+ }
370+
371+ [ Fact ( Timeout = 20000 ) ]
372+ public async Task Minimize_and_restore_events_fire ( )
373+ {
374+ var window = await Electron . WindowManager . CreateWindowAsync ( new BrowserWindowOptions { Show = true } , "about:blank" ) ;
375+ try
376+ {
377+ bool minimized = false , restored = false ;
378+ window . OnMinimize += ( ) => minimized = true ;
379+ window . OnRestore += ( ) => restored = true ;
380+ await Task . Delay ( 500 ) ;
381+ window . Minimize ( ) ;
382+ await Task . Delay ( 1000 ) ; // minimize may take longer
383+ minimized . Should ( ) . BeTrue ( ) ;
384+ window . Restore ( ) ;
385+ await Task . Delay ( 800 ) ;
386+ restored . Should ( ) . BeTrue ( ) ;
387+ }
388+ finally
389+ {
390+ window . Destroy ( ) ;
391+ }
392+ }
393+
394+ private static async Task < bool > WaitUntil ( Func < Task < bool > > predicate , int timeoutMs , int pollIntervalMs = 100 )
395+ {
396+ var start = DateTime . UtcNow ;
397+ while ( ( DateTime . UtcNow - start ) . TotalMilliseconds < timeoutMs )
398+ {
399+ if ( await predicate ( ) . ConfigureAwait ( false ) )
400+ {
401+ return true ;
402+ }
403+ await Task . Delay ( pollIntervalMs ) . ConfigureAwait ( false ) ;
404+ }
405+ return await predicate ( ) . ConfigureAwait ( false ) ;
406+ }
241407 }
242408}
0 commit comments