test: fix retry-reliant macOS fullscreen BrowserWindow specs (#55230)

"should be able to load a URL while transitioning to fullscreen" and
"transparent option > correctly returns isMaximized() when the window is
maximized then minimized" time out on their first attempt on macOS and
pass on retry. Each runs right after a test that ends with two windows
sharing fullscreen (a borderless child of a fullscreen parent, and
"multiple windows inherit correct fullscreen state"). Those tests'
reported durations, which include the afterEach hook, are too short to
contain a fullscreen exit in the failing runs, so closeAllWindows()
destroyed a window that was still animating out of fullscreen, and AppKit
ignored the next test's first window request.

closeWindow() only waited when isFullScreen() was true, which it no
longer is while a window is being taken out of fullscreen along with
another one. Track which BrowserWindows have entered fullscreen and not
yet left it, wait for their 'leave-full-screen' too, and warn when that
does not arrive within the existing 5s bound.

"does not exit simpleFullScreen when requestFullscreen is called" still
spent its first attempt's whole 10s focus wait without the page getting
focus on macOS arm64, and passed on the retry, which requested app
activation again. Request activation on every poll instead of once.


Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
claude[bot]andClaude authored and GitHub committed 2026-10-26 08:01:43 +00:00
1 parent 0240841b24
commit 6408a0c6bb
2 files changed
+41 -11

No files matched your search

+14 -4
View File
@@ -48,7 +48,6 @@ import {
ifit,
ifdescribe,
defer,
focusWebContents,
listen,
waitUntil,
isWayland,
@@ -8025,9 +8024,20 @@ describe('BrowserWindow module', () => {
// the page is focused after entering simple fullscreen.
// webContents.focus() only activates the app when no other app is
// active, and the earlier fullscreen specs can leave another app
// frontmost, so take activation explicitly first.
app.focus({ steal: true });
await focusWebContents(w.webContents);
// frontmost, so take activation explicitly. One request is not
// always enough: on macOS arm64 CI the first attempt sometimes
// spent the whole wait without the page getting focus, while the
// retry, which requested activation again, passed at once. So ask
// again on every poll until the page has focus.
await waitUntil(
async () => {
if (await w.webContents.executeJavaScript('document.hasFocus()')) return true;
app.focus({ steal: true });
w.webContents.focus();
return false;
},
{ rate: 100 }
);
const enterHtmlFS = once(w.webContents, 'enter-html-full-screen');
await w.webContents.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
+27 -7
View File
@@ -1,17 +1,37 @@
import { BaseWindow, BrowserWindow, webContents } from 'electron/main';
import { app, BaseWindow, BrowserWindow, webContents } from 'electron/main';
import { expect } from 'vitest';
import { once } from 'node:events';
// BrowserWindows that have emitted 'enter-full-screen' but not yet
// 'leave-full-screen'.
const inFullScreen = new WeakSet<BaseWindow>();
if (process.platform === 'darwin') {
app.on('browser-window-created', (_event, window) => {
window.on('enter-full-screen', () => inFullScreen.add(window));
window.on('leave-full-screen', () => inFullScreen.delete(window));
});
}
// On macOS, destroying a fullscreen window animates it out of its Space, and
// AppKit ignores fullscreen requests from other windows until that finishes.
// Leave fullscreen first so the next test doesn't start inside the animation.
// AppKit ignores fullscreen and other window state requests from other
// windows until that finishes. Leave fullscreen first so the next test
// doesn't start inside the animation.
//
// isFullScreen() is not enough to tell: a window can already be on its way
// out, for example when another window in the same fullscreen Space left
// fullscreen and took it along. isFullScreen() is then false, but the window
// still emits 'leave-full-screen' once the animation ends, so wait for that.
async function leaveFullScreen(window: BaseWindow) {
if (process.platform !== 'darwin' || !window.isFullScreen()) return;
const left = once(window, 'leave-full-screen');
window.setFullScreen(false);
await Promise.race([left, new Promise((resolve) => setTimeout(resolve, 5000))]);
if (process.platform !== 'darwin') return;
if (!window.isFullScreen() && !inFullScreen.has(window)) return;
const left = once(window, 'leave-full-screen').then(() => true);
if (window.isFullScreen()) window.setFullScreen(false);
const timeout = new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 5000));
if (!(await Promise.race([left, timeout]))) {
console.warn(`closeWindow(): window ${window.id} did not leave fullscreen within 5s, destroying it anyway`);
}
}
async function ensureWindowIsClosed(window: BaseWindow | null) {