Repository: electron/electron (language: cpp)

Issue:
Tests fail on macOS with reduced motion and reduced transparency
### Preflight Checklist

- [x] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [x] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [x] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a bug report that matches the one I want to file, without success.

### Electron Version

37.0.0-nightly.20250402, 36, 35, 34, 33

### What operating system(s) are you using?

macOS

### Operating System Version

15.4, 13.7.6

### What arch are you using?

arm64 (including Apple Silicon) and x64

### Last Known Working Electron version

_No response_

### Expected Behavior

Tests run fine despite

```bash
defaults write com.apple.universalaccess reduceMotion -bool true
defaults write com.apple.universalaccess reduceTransparency -bool true
```

### Actual Behavior

16 tests fail with those settings.

**Repro:**

```bash
defaults write com.apple.universalaccess reduceMotion -bool true
defaults write com.apple.universalaccess reduceTransparency -bool true
e test --electronVersion 37.0.0-nightly.20250402 -g 'BrowserWindow module sizing'
```

<details><summary>Test output</summary>
<p>

```
% e test --electronVersion 37.0.0-nightly.20250402 -g 'BrowserWindow module sizing'
Running "/usr/local/bin/node ./script/spec-runner.js -g BrowserWindow module sizing --electronVersion=37.0.0-nightly.20250402" in /Volumes/Electron/electron/src/electron
Only running: [ 'main' ]
Running against Electron undefined

Running: Main process specs
ok 1 BrowserWindow module sizing BrowserWindow.setBounds(bounds[, animate]) sets the window bounds with full bounds
ok 2 BrowserWindow module sizing BrowserWindow.setBounds(bounds[, animate]) rounds non-integer bounds
ok 3 BrowserWindow module sizing BrowserWindow.setBounds(bounds[, animate]) sets the window bounds with partial bounds
ok 4 BrowserWindow module sizing BrowserWindow.setBounds(bounds[, animate]) on macOS
ok 5 BrowserWindow module sizing BrowserWindow.setBounds(bounds[, animate]) does not emits the resize event for move-only changes
ok 6 BrowserWindow module sizing BrowserWindow.setSize(width, height) sets the window size
ok 7 BrowserWindow module sizing BrowserWindow.setSize(width, height) emits the resize event for single-pixel size changes
ok 8 BrowserWindow module sizing BrowserWindow.setSize(width, height) on macOS
ok 9 BrowserWindow module sizing BrowserWindow.setMinimum/MaximumSize(width, height) sets the maximum and minimum size of the window
ok 10 BrowserWindow module sizing BrowserWindow.setAspectRatio(ratio) resets the behaviour when passing in 0
ok 11 BrowserWindow module sizing BrowserWindow.setAspectRatio(ratio) doesn't change bounds when maximum size is set
ok 12 BrowserWindow module sizing BrowserWindow.setPosition(x, y) sets the window position
ok 13 BrowserWindow module sizing BrowserWindow.setContentSize(width, height) sets the content size
ok 14 BrowserWindow module sizing BrowserWindow.setContentSize(width, height) works for a frameless window
ok 15 BrowserWindow module sizing BrowserWindow.setContentBounds(bounds) sets the content size and position
ok 16 BrowserWindow module sizing BrowserWindow.setContentBounds(bounds) works for a frameless window
ok 17 BrowserWindow module sizing BrowserWindow.getBackgroundColor() returns default value if no backgroundColor is set
ok 18 BrowserWindow module sizing BrowserWindow.getBackgroundColor() returns correct value if backgroundColor is set
ok 19 BrowserWindow module sizing BrowserWindow.getBackgroundColor() returns correct value from setBackgroundColor()
ok 20 BrowserWindow module sizing BrowserWindow.getBackgroundColor() returns correct color with multiple passed formats
ok 21 BrowserWindow module sizing BrowserWindow.getBackgroundColor() can set the background color with transparency
ok 22 BrowserWindow module sizing BrowserWindow.getNormalBounds() Normal state checks normal bounds after resize
ok 23 BrowserWindow module sizing BrowserWindow.getNormalBounds() Normal state checks normal bounds after move
not ok 24 BrowserWindow module sizing BrowserWindow.getNormalBounds() Maximized state checks normal bounds when maximized
  Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
  Error: Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
      at listOnTimeout (node:internal/timers:594:17)
      at processTimers (node:internal/timers:529:7)
ok 25 BrowserWindow module sizing BrowserWindow.getNormalBounds() Maximized state updates normal bounds after resize and maximize
ok 26 BrowserWindow module sizing BrowserWindow.getNormalBounds() Maximized state updates normal bounds after move and maximize
not ok 27 BrowserWindow module sizing BrowserWindow.getNormalBounds() Maximized state checks normal bounds when unmaximized
  Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
  Error: Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
      at listOnTimeout (node:internal/timers:594:17)
      at processTimers (node:internal/timers:529:7)
not ok 28 BrowserWindow module sizing BrowserWindow.getNormalBounds() Maximized state correctly reports maximized state after maximizing then minimizing
  Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
  Error: Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
      at listOnTimeout (node:internal/timers:594:17)
      at processTimers (node:internal/timers:529:7)
not ok 29 BrowserWindow module sizing BrowserWindow.getNormalBounds() Maximized state correctly reports maximized state after maximizing then fullscreening
  Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
  Error: Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
      at listOnTimeout (node:internal/timers:594:17)
      at processTimers (node:internal/timers:529:7)
not ok 30 BrowserWindow module sizing BrowserWindow.getNormalBounds() Maximized state does not crash if maximized, minimized, then restored to maximized state
  Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
  Error: Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
      at listOnTimeout (node:internal/timers:594:17)
      at processTimers (node:internal/timers:529:7)
not ok 31 BrowserWindow module sizing BrowserWindow.getNormalBounds() Maximized state checks normal bounds for maximized transparent window
  Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
  Error: Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
      at listOnTimeout (node:internal/timers:594:17)
      at processTimers (node:internal/timers:529:7)
not ok 32 BrowserWindow module sizing BrowserWindow.getNormalBounds() Maximized state does not change size for a frameless window with min size
  Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
  Error: Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
      at listOnTimeout (node:internal/timers:594:17)
      at processTimers (node:internal/timers:529:7)
not ok 33 BrowserWindow module sizing BrowserWindow.getNormalBounds() Maximized state correctly checks transparent window maximization state
  Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
  Error: Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
      at listOnTimeout (node:internal/timers:594:17)
      at processTimers (node:internal/timers:529:7)
not ok 34 BrowserWindow module sizing BrowserWindow.getNormalBounds() Maximized state returns the correct value for windows with an aspect ratio
  Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
  Error: Timeout of 30000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Volumes/Electron/electron/src/electron/spec/api-browser-window-spec.ts)
      at listOnTimeout (node:internal/timers:594:17)
      at processTimers (node:internal/timers:529:7)
ok 35 BrowserWindow module sizing BrowserWindow.getNormalBounds() Minimized state checks normal bounds when minimized
ok 36 BrowserWindow module sizing BrowserWindow.getNormalBounds() Minimized state updates normal bounds after move and minimize
ok 37 BrowserWindow module sizing BrowserWindow.getNormalBounds() Minimized state updates normal bounds after resize and minimize
ok 38 BrowserWindow module sizing BrowserWindow.getNormalBounds() Minimized state checks normal bounds when restored
ok 39 BrowserWindow module sizing BrowserWindow.getNormalBounds() Minimized state does not change size for a frameless window with min size
ok 40 BrowserWindow module sizing BrowserWindow.getNormalBounds() Fullscreen state with properties # SKIP -
ok 41 BrowserWindow module sizing BrowserWindow.getNormalBounds() Fullscreen state with functions # SKIP -
# tests 39
# pass 30
# fail 9
1..41
✗ Electron tests failed with code 9.
```

</p>
</details>

**IMPORTANT:** There are more tests that fail than just the ones triggered with `-g 'BrowserWindow module sizing'`. To see all failing tests, open one of the CI logs from below and search for "not ok".

### Testcase Gist URL

_No response_

### Additional Information

**Related CI failures:**

- https://github.com/electron/electron/actions/runs/14226269149/job/39868911885?pr=46447 (v37)
- https://github.com/electron/electron/actions/runs/14225207342/job/39869424817?pr=46443 (v33)

CI will be fixed through https://github.com/electron/electron/pull/46454, but we should fix the underlying problem around maximizing windows with reduced motion.


Discussion / hints:

Fix patch:
diff --git a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm
index 3f7c689bfc8b0..04fa6781dd1fe 100644
--- a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm
+++ b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm
@@ -221,6 +221,12 @@ - (void)windowDidResize:(NSNotification*)notification {
   [super windowDidResize:notification];
   shell_->NotifyWindowResize();
   shell_->RedrawTrafficLights();
+  // When reduce motion is enabled windowDidResize is only called once after
+  // a resize and windowDidEndLiveResize is not called. So we need to call
+  // handleZoomEnd here as well.
+  if (NSWorkspace.sharedWorkspace.accessibilityDisplayShouldReduceMotion) {
+    [self handleZoomEnd];
+  }
 }
 
 - (void)windowWillMove:(NSNotification*)notification {
@@ -276,9 +282,7 @@ - (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame {
   return YES;
 }
 
-- (void)windowDidEndLiveResize:(NSNotification*)notification {
-  resizingHorizontally_.reset();
-  shell_->NotifyWindowResized();
+- (void)handleZoomEnd {
   if (is_zooming_) {
     if (shell_->IsMaximized())
       shell_->NotifyWindowMaximize();
@@ -288,6 +292,12 @@ - (void)windowDidEndLiveResize:(NSNotification*)notification {
   }
 }
 
+- (void)windowDidEndLiveResize:(NSNotification*)notification {
+  resizingHorizontally_.reset();
+  shell_->NotifyWindowResized();
+  [self handleZoomEnd];
+}
+
 - (void)windowWillEnterFullScreen:(NSNotification*)notification {
   // Store resizable mask so it can be restored after exiting fullscreen.
   is_resizable_ = shell_->HasStyleMask(NSWindowStyleMaskResizable);


Test patch:
diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml
index a094ce1171c25..cddf2fe918406 100644
--- a/.github/workflows/pipeline-segment-electron-test.yml
+++ b/.github/workflows/pipeline-segment-electron-test.yml
@@ -118,12 +118,6 @@ jobs:
     - name: Turn off the unexpectedly quit dialog on macOS
       if: ${{ inputs.target-platform == 'macos' }}
       run: defaults write com.apple.CrashReporter DialogType server
-    - name: Reenable graphics effects on macOS
-      if: ${{ inputs.target-platform == 'macos' }}
-      run: |
-        # These options to reduce graphics effects were enabled upstream in https://github.com/actions/runner-images/pull/11877
-        defaults write com.apple.universalaccess reduceMotion -bool false
-        defaults write com.apple.universalaccess reduceTransparency -bool false
     - name: Checkout Electron
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
       with: