From 430400882b6f0fe582a704f4748864487345a583 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Thu, 8 Jan 2026 18:57:44 -0800 Subject: [PATCH 1/2] fix(desktop): use dynamic import for electron in bounds-validation test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test was failing with "Export named 'screen' not found" because static imports are resolved before mock.module can intercept them. Changed to use mock.module for electron before dynamic import, following the same pattern used in terminal manager tests. This ensures the mock is in place before the bounds-validation module (which imports electron) is loaded. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../window-state/bounds-validation.test.ts | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/apps/desktop/src/main/lib/window-state/bounds-validation.test.ts b/apps/desktop/src/main/lib/window-state/bounds-validation.test.ts index 8b89325ac49..2a8dcf24135 100644 --- a/apps/desktop/src/main/lib/window-state/bounds-validation.test.ts +++ b/apps/desktop/src/main/lib/window-state/bounds-validation.test.ts @@ -1,9 +1,28 @@ -import { beforeEach, describe, expect, it, type mock } from "bun:test"; -import { screen } from "electron"; -import { - getInitialWindowBounds, - isVisibleOnAnyDisplay, -} from "./bounds-validation"; +import { beforeEach, describe, expect, it, mock, type mock as MockType } from "bun:test"; + +// Mock electron with screen API before importing anything that uses it +const mockScreen = { + getPrimaryDisplay: mock(() => ({ + workAreaSize: { width: 1920, height: 1080 }, + bounds: { x: 0, y: 0, width: 1920, height: 1080 }, + })), + getAllDisplays: mock(() => [ + { + bounds: { x: 0, y: 0, width: 1920, height: 1080 }, + workAreaSize: { width: 1920, height: 1080 }, + }, + ]), +}; + +mock.module("electron", () => ({ + screen: mockScreen, +})); + +// Import module after mocks are set up +const { getInitialWindowBounds, isVisibleOnAnyDisplay } = await import( + "./bounds-validation" +); +const screen = mockScreen; const MIN_VISIBLE_OVERLAP = 50; const MIN_WINDOW_SIZE = 400; @@ -11,7 +30,7 @@ const MIN_WINDOW_SIZE = 400; describe("isVisibleOnAnyDisplay", () => { describe("single display setup", () => { beforeEach(() => { - (screen.getAllDisplays as ReturnType).mockReturnValue([ + (screen.getAllDisplays as ReturnType).mockReturnValue([ { bounds: { x: 0, y: 0, width: 1920, height: 1080 } }, ]); }); @@ -99,7 +118,7 @@ describe("isVisibleOnAnyDisplay", () => { describe("multi-display setup", () => { beforeEach(() => { - (screen.getAllDisplays as ReturnType).mockReturnValue([ + (screen.getAllDisplays as ReturnType).mockReturnValue([ { bounds: { x: 0, y: 0, width: 1920, height: 1080 } }, { bounds: { x: 1920, y: 0, width: 1920, height: 1080 } }, ]); @@ -126,7 +145,7 @@ describe("isVisibleOnAnyDisplay", () => { describe("secondary display with offset", () => { beforeEach(() => { - (screen.getAllDisplays as ReturnType).mockReturnValue([ + (screen.getAllDisplays as ReturnType).mockReturnValue([ { bounds: { x: 0, y: 0, width: 1920, height: 1080 } }, { bounds: { x: 960, y: 1080, width: 1920, height: 1080 } }, ]); @@ -147,7 +166,7 @@ describe("isVisibleOnAnyDisplay", () => { describe("display to the left (negative coordinates)", () => { beforeEach(() => { - (screen.getAllDisplays as ReturnType).mockReturnValue([ + (screen.getAllDisplays as ReturnType).mockReturnValue([ { bounds: { x: 0, y: 0, width: 1920, height: 1080 } }, { bounds: { x: -1920, y: 0, width: 1920, height: 1080 } }, ]); @@ -162,14 +181,14 @@ describe("isVisibleOnAnyDisplay", () => { describe("edge cases", () => { it("should return false when no displays connected", () => { - (screen.getAllDisplays as ReturnType).mockReturnValue([]); + (screen.getAllDisplays as ReturnType).mockReturnValue([]); expect( isVisibleOnAnyDisplay({ x: 100, y: 100, width: 800, height: 600 }), ).toBe(false); }); it("should return true for zero-size window if position is valid (size validation is separate)", () => { - (screen.getAllDisplays as ReturnType).mockReturnValue([ + (screen.getAllDisplays as ReturnType).mockReturnValue([ { bounds: { x: 0, y: 0, width: 1920, height: 1080 } }, ]); expect( @@ -181,10 +200,10 @@ describe("isVisibleOnAnyDisplay", () => { describe("getInitialWindowBounds", () => { beforeEach(() => { - (screen.getPrimaryDisplay as ReturnType).mockReturnValue({ + (screen.getPrimaryDisplay as ReturnType).mockReturnValue({ workAreaSize: { width: 1920, height: 1080 }, }); - (screen.getAllDisplays as ReturnType).mockReturnValue([ + (screen.getAllDisplays as ReturnType).mockReturnValue([ { bounds: { x: 0, y: 0, width: 1920, height: 1080 } }, ]); }); @@ -319,7 +338,7 @@ describe("getInitialWindowBounds", () => { describe("DPI/resolution changes", () => { it("should handle resolution decrease gracefully", () => { - (screen.getPrimaryDisplay as ReturnType).mockReturnValue({ + (screen.getPrimaryDisplay as ReturnType).mockReturnValue({ workAreaSize: { width: 1280, height: 720 }, }); @@ -336,7 +355,7 @@ describe("getInitialWindowBounds", () => { }); it("should clamp to work area even if smaller than MIN_WINDOW_SIZE", () => { - (screen.getPrimaryDisplay as ReturnType).mockReturnValue({ + (screen.getPrimaryDisplay as ReturnType).mockReturnValue({ workAreaSize: { width: 300, height: 200 }, }); @@ -355,7 +374,7 @@ describe("getInitialWindowBounds", () => { describe("multi-monitor scenarios", () => { beforeEach(() => { - (screen.getAllDisplays as ReturnType).mockReturnValue([ + (screen.getAllDisplays as ReturnType).mockReturnValue([ { bounds: { x: 0, y: 0, width: 1920, height: 1080 } }, { bounds: { x: 1920, y: 0, width: 1920, height: 1080 } }, ]); From 27bd67a3e5cd9adf2accea3cb59bfcdba7aedf76 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Thu, 8 Jan 2026 20:05:52 -0800 Subject: [PATCH 2/2] Fix lint --- .../window-state/bounds-validation.test.ts | 29 ++++++++++++++----- bun.lock | 4 +-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/apps/desktop/src/main/lib/window-state/bounds-validation.test.ts b/apps/desktop/src/main/lib/window-state/bounds-validation.test.ts index 2a8dcf24135..febf5141b10 100644 --- a/apps/desktop/src/main/lib/window-state/bounds-validation.test.ts +++ b/apps/desktop/src/main/lib/window-state/bounds-validation.test.ts @@ -1,4 +1,11 @@ -import { beforeEach, describe, expect, it, mock, type mock as MockType } from "bun:test"; +import { + beforeEach, + describe, + expect, + it, + type mock as MockType, + mock, +} from "bun:test"; // Mock electron with screen API before importing anything that uses it const mockScreen = { @@ -181,7 +188,9 @@ describe("isVisibleOnAnyDisplay", () => { describe("edge cases", () => { it("should return false when no displays connected", () => { - (screen.getAllDisplays as ReturnType).mockReturnValue([]); + (screen.getAllDisplays as ReturnType).mockReturnValue( + [], + ); expect( isVisibleOnAnyDisplay({ x: 100, y: 100, width: 800, height: 600 }), ).toBe(false); @@ -338,9 +347,11 @@ describe("getInitialWindowBounds", () => { describe("DPI/resolution changes", () => { it("should handle resolution decrease gracefully", () => { - (screen.getPrimaryDisplay as ReturnType).mockReturnValue({ - workAreaSize: { width: 1280, height: 720 }, - }); + (screen.getPrimaryDisplay as ReturnType).mockReturnValue( + { + workAreaSize: { width: 1280, height: 720 }, + }, + ); const result = getInitialWindowBounds({ x: 0, @@ -355,9 +366,11 @@ describe("getInitialWindowBounds", () => { }); it("should clamp to work area even if smaller than MIN_WINDOW_SIZE", () => { - (screen.getPrimaryDisplay as ReturnType).mockReturnValue({ - workAreaSize: { width: 300, height: 200 }, - }); + (screen.getPrimaryDisplay as ReturnType).mockReturnValue( + { + workAreaSize: { width: 300, height: 200 }, + }, + ); const result = getInitialWindowBounds({ x: 0, diff --git a/bun.lock b/bun.lock index c259823837a..5e423dced2d 100644 --- a/bun.lock +++ b/bun.lock @@ -121,7 +121,7 @@ }, "apps/desktop": { "name": "@superset/desktop", - "version": "0.0.50", + "version": "0.0.51", "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", @@ -156,7 +156,7 @@ "@xterm/addon-unicode11": "^0.8.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/addon-webgl": "^0.18.0", - "@xterm/headless": "5.5.0", + "@xterm/headless": "^5.5.0", "@xterm/xterm": "^5.5.0", "better-sqlite3": "12.5.0", "bindings": "^1.5.0",